scoutfs: provide ->setattr

Simple attr changes are mostly handled by the VFS, we just have to mirror
them into our inode. Truncates are done in a seperate set of transactions.
We use a flag to indicate an in-progress truncate. This allows us to
detect and continue the truncate should the node crash.

Index locking is a bit complicated, so we add a helper function to grab
index locks and start a transaction.

With this patch we now pass the following xfstests:

generic/014
generic/101
generic/313

Signed-off-by: Mark Fasheh <mfasheh@versity.com>
This commit is contained in:
Mark Fasheh
2017-10-05 15:28:53 -05:00
committed by Zach Brown
parent dd99a0127e
commit 20a22ddc6b
6 changed files with 229 additions and 3 deletions

View File

@@ -987,6 +987,7 @@ const struct inode_operations scoutfs_symlink_iops = {
.follow_link = scoutfs_follow_link,
.put_link = scoutfs_put_link,
.getattr = scoutfs_getattr,
.setattr = scoutfs_setattr,
.setxattr = scoutfs_setxattr,
.getxattr = scoutfs_getxattr,
.listxattr = scoutfs_listxattr,
@@ -1642,6 +1643,7 @@ const struct inode_operations scoutfs_dir_iops = {
.rmdir = scoutfs_unlink,
.rename = scoutfs_rename,
.getattr = scoutfs_getattr,
.setattr = scoutfs_setattr,
.setxattr = scoutfs_setxattr,
.getxattr = scoutfs_getxattr,
.listxattr = scoutfs_listxattr,

View File

@@ -73,15 +73,18 @@ ssize_t scoutfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (ret)
goto out;
ret = scoutfs_complete_truncate(inode, inode_lock);
if (ret)
goto out;
scoutfs_per_task_add(&si->pt_data_lock, &pt_ent, inode_lock);
/* XXX: remove SUID bit */
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
out:
scoutfs_per_task_del(&si->pt_data_lock, &pt_ent);
scoutfs_unlock(sb, inode_lock, DLM_LOCK_EX);
out:
mutex_unlock(&inode->i_mutex);
if (ret > 0 || ret == -EIOCBQUEUED) {

View File

@@ -483,11 +483,14 @@ struct scoutfs_inode {
__le32 gid;
__le32 mode;
__le32 rdev;
__le32 flags;
struct scoutfs_timespec atime;
struct scoutfs_timespec ctime;
struct scoutfs_timespec mtime;
} __packed;
#define SCOUTFS_INO_FLAG_TRUNCATE 0x1
#define SCOUTFS_ROOT_INO 1
/* like the block size, a reasonable min PATH_MAX across platforms */

View File

@@ -152,6 +152,7 @@ void scoutfs_destroy_inode(struct inode *inode)
static const struct inode_operations scoutfs_file_iops = {
.getattr = scoutfs_getattr,
.setattr = scoutfs_setattr,
.setxattr = scoutfs_setxattr,
.getxattr = scoutfs_getxattr,
.listxattr = scoutfs_listxattr,
@@ -161,6 +162,7 @@ static const struct inode_operations scoutfs_file_iops = {
static const struct inode_operations scoutfs_special_iops = {
.getattr = scoutfs_getattr,
.setattr = scoutfs_setattr,
.setxattr = scoutfs_setxattr,
.getxattr = scoutfs_getxattr,
.listxattr = scoutfs_listxattr,
@@ -242,6 +244,8 @@ static void load_inode(struct inode *inode, struct scoutfs_inode *cinode)
ci->data_version = le64_to_cpu(cinode->data_version);
ci->next_readdir_pos = le64_to_cpu(cinode->next_readdir_pos);
ci->flags = le32_to_cpu(cinode->flags);
set_item_info(ci, cinode);
}
@@ -325,6 +329,138 @@ int scoutfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
return ret;
}
static int set_inode_size(struct inode *inode, struct scoutfs_lock *lock,
u64 new_size, bool truncate)
{
struct scoutfs_inode_info *ci = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
LIST_HEAD(ind_locks);
int ret;
if (!S_ISREG(inode->i_mode))
return 0;
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, new_size, true,
SIC_DIRTY_INODE());
if (ret)
return ret;
truncate_setsize(inode, new_size);
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
if (truncate)
ci->flags |= SCOUTFS_INO_FLAG_TRUNCATE;
scoutfs_inode_set_data_seq(inode);
scoutfs_update_inode_item(inode, lock, &ind_locks);
scoutfs_release_trans(sb);
scoutfs_inode_index_unlock(sb, &ind_locks);
return ret;
}
static int clear_truncate_flag(struct inode *inode, struct scoutfs_lock *lock)
{
struct scoutfs_inode_info *ci = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
LIST_HEAD(ind_locks);
int ret;
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks,
i_size_read(inode), false,
SIC_DIRTY_INODE());
if (ret)
return ret;
ci->flags &= ~SCOUTFS_INO_FLAG_TRUNCATE;
scoutfs_update_inode_item(inode, lock, &ind_locks);
scoutfs_release_trans(sb);
scoutfs_inode_index_unlock(sb, &ind_locks);
return ret;
}
int scoutfs_complete_truncate(struct inode *inode, struct scoutfs_lock *lock)
{
struct scoutfs_inode_info *ci = SCOUTFS_I(inode);
u64 start;
int ret, err;
trace_scoutfs_complete_truncate(inode, ci->flags);
if (!(ci->flags & SCOUTFS_INO_FLAG_TRUNCATE))
return 0;
start = (i_size_read(inode) + SCOUTFS_BLOCK_SIZE - 1) >> SCOUTFS_BLOCK_SHIFT;
ret = scoutfs_data_truncate_items(inode->i_sb, scoutfs_ino(inode),
start, ~0ULL, false, lock);
err = clear_truncate_flag(inode, lock);
return ret ? ret : err;
}
int scoutfs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
struct super_block *sb = inode->i_sb;
struct scoutfs_lock *lock = NULL;
LIST_HEAD(ind_locks);
bool truncate = false;
u64 attr_size;
int ret;
trace_scoutfs_setattr(dentry, attr);
ret = scoutfs_lock_inode(sb, DLM_LOCK_EX, SCOUTFS_LKF_REFRESH_INODE,
inode, &lock);
if (ret)
return ret;
ret = inode_change_ok(inode, attr);
if (ret)
goto out;
attr_size = (attr->ia_valid & ATTR_SIZE) ? attr->ia_size :
i_size_read(inode);
if (S_ISREG(inode->i_mode) && attr->ia_valid & ATTR_SIZE) {
/*
* Complete any truncates that may have failed while
* in progress
*/
ret = scoutfs_complete_truncate(inode, lock);
if (ret)
goto out;
truncate = i_size_read(inode) > attr_size;
ret = set_inode_size(inode, lock, attr_size, truncate);
if (ret)
goto out;
if (truncate) {
ret = scoutfs_complete_truncate(inode, lock);
if (ret)
goto out;
}
}
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks,
i_size_read(inode), false,
SIC_DIRTY_INODE());
if (ret)
goto out;
setattr_copy(inode, attr);
scoutfs_update_inode_item(inode, lock, &ind_locks);
scoutfs_release_trans(sb);
scoutfs_inode_index_unlock(sb, &ind_locks);
out:
scoutfs_unlock(sb, lock, DLM_LOCK_EX);
return ret;
}
/*
* Set a given seq to the current trans seq if it differs. The caller
* holds locks and a transaction which prevents the transaction from
@@ -486,6 +622,7 @@ static void store_inode(struct scoutfs_inode *cinode, struct inode *inode)
cinode->data_seq = cpu_to_le64(scoutfs_inode_data_seq(inode));
cinode->data_version = cpu_to_le64(scoutfs_inode_data_version(inode));
cinode->next_readdir_pos = cpu_to_le64(ci->next_readdir_pos);
cinode->flags = cpu_to_le32(ci->flags);
}
/*
@@ -970,6 +1107,24 @@ out:
return ret;
}
int scoutfs_inode_index_lock_hold(struct inode *inode, struct list_head *list,
u64 size, bool set_data_seq,
const struct scoutfs_item_count cnt)
{
struct super_block *sb = inode->i_sb;
int ret;
u64 seq;
do {
ret = scoutfs_inode_index_start(sb, &seq) ?:
scoutfs_inode_index_prepare(sb, list, inode, size,
set_data_seq) ?:
scoutfs_inode_index_try_lock_hold(sb, list, seq, cnt);
} while (ret > 0);
return ret;
}
/*
* Unlocks and frees all the locks on the list.
*/
@@ -1172,6 +1327,7 @@ struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
ci->next_readdir_pos = SCOUTFS_DIRENT_FIRST_POS;
ci->have_item = false;
atomic64_set(&ci->last_refreshed, scoutfs_lock_refresh_gen(lock));
ci->flags = 0;
scoutfs_inode_set_meta_seq(inode);
scoutfs_inode_set_data_seq(inode);

View File

@@ -15,6 +15,7 @@ struct scoutfs_inode_info {
u64 meta_seq;
u64 data_seq;
u64 data_version;
u32 flags;
/*
* The in-memory item info caches the current index item values
@@ -36,7 +37,6 @@ struct scoutfs_inode_info {
struct scoutfs_per_task pt_data_lock;
struct rw_semaphore xattr_rwsem;
struct rb_node writeback_node;
struct inode inode;
};
@@ -72,6 +72,9 @@ int scoutfs_inode_index_prepare_ino(struct super_block *sb,
int scoutfs_inode_index_try_lock_hold(struct super_block *sb,
struct list_head *list, u64 seq,
const struct scoutfs_item_count cnt);
int scoutfs_inode_index_lock_hold(struct inode *inode, struct list_head *list,
u64 size, bool set_data_seq,
const struct scoutfs_item_count cnt);
void scoutfs_inode_index_unlock(struct super_block *sb, struct list_head *list);
int scoutfs_dirty_inode_item(struct inode *inode, struct scoutfs_lock *lock);
@@ -90,11 +93,13 @@ void scoutfs_inode_inc_data_version(struct inode *inode);
u64 scoutfs_inode_meta_seq(struct inode *inode);
u64 scoutfs_inode_data_seq(struct inode *inode);
u64 scoutfs_inode_data_version(struct inode *inode);
int scoutfs_complete_truncate(struct inode *inode, struct scoutfs_lock *lock);
int scoutfs_inode_refresh(struct inode *inode, struct scoutfs_lock *lock,
int flags);
int scoutfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
int scoutfs_setattr(struct dentry *dentry, struct iattr *attr);
int scoutfs_scan_orphans(struct super_block *sb);

View File

@@ -41,6 +41,63 @@ struct lock_info;
#define FSID_ARG(sb) le64_to_cpu(SCOUTFS_SB(sb)->super.hdr.fsid)
#define FSID_FMT "%llx"
TRACE_EVENT(scoutfs_setattr,
TP_PROTO(struct dentry *dentry, struct iattr *attr),
TP_ARGS(dentry, attr),
TP_STRUCT__entry(
__field(__u64, fsid)
__field(__u64, ino)
__field(unsigned int, d_len)
__string(d_name, dentry->d_name.name)
__field(__u64, i_size)
__field(__u64, ia_size)
__field(unsigned int, ia_valid)
__field(int, size_change)
),
TP_fast_assign(
__entry->fsid = FSID_ARG(dentry->d_inode->i_sb);
__entry->ino = scoutfs_ino(dentry->d_inode);
__entry->d_len = dentry->d_name.len;
__assign_str(d_name, dentry->d_name.name);
__entry->ia_valid = attr->ia_valid;
__entry->size_change = !!(attr->ia_valid & ATTR_SIZE);
__entry->ia_size = attr->ia_size;
__entry->i_size = i_size_read(dentry->d_inode);
),
TP_printk(FSID_FMT" %s ino %llu ia_valid 0x%x size change %d ia_size "
"%llu i_size %llu", __entry->fsid, __get_str(d_name),
__entry->ino, __entry->ia_valid, __entry->size_change,
__entry->ia_size, __entry->i_size)
);
TRACE_EVENT(scoutfs_complete_truncate,
TP_PROTO(struct inode *inode, __u32 flags),
TP_ARGS(inode, flags),
TP_STRUCT__entry(
__field(__u64, fsid)
__field(__u64, ino)
__field(__u64, i_size)
__field(__u32, flags)
),
TP_fast_assign(
__entry->fsid = FSID_ARG(inode->i_sb);
__entry->ino = scoutfs_ino(inode);
__entry->i_size = i_size_read(inode);
__entry->flags = flags;
),
TP_printk(FSID_FMT" ino %llu i_size %llu flags 0x%x",
__entry->fsid, __entry->ino, __entry->i_size,
__entry->flags)
);
DECLARE_EVENT_CLASS(scoutfs_comp_class,
TP_PROTO(struct super_block *sb, struct scoutfs_bio_completion *comp),