From 20a22ddc6be4ad63226a15b93bf7bc42646fa04d Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Thu, 5 Oct 2017 15:28:53 -0500 Subject: [PATCH] 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 --- kmod/src/dir.c | 2 + kmod/src/file.c | 7 +- kmod/src/format.h | 3 + kmod/src/inode.c | 156 +++++++++++++++++++++++++++++++++++++++ kmod/src/inode.h | 7 +- kmod/src/scoutfs_trace.h | 57 ++++++++++++++ 6 files changed, 229 insertions(+), 3 deletions(-) diff --git a/kmod/src/dir.c b/kmod/src/dir.c index 5c8a7d13..b869fb7b 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -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, diff --git a/kmod/src/file.c b/kmod/src/file.c index b59df5e0..9382d96a 100644 --- a/kmod/src/file.c +++ b/kmod/src/file.c @@ -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) { diff --git a/kmod/src/format.h b/kmod/src/format.h index ffe5ce44..e30a1320 100644 --- a/kmod/src/format.h +++ b/kmod/src/format.h @@ -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 */ diff --git a/kmod/src/inode.c b/kmod/src/inode.c index 095c478e..cdb7275b 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -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); diff --git a/kmod/src/inode.h b/kmod/src/inode.h index fe884f13..ca35e4ba 100644 --- a/kmod/src/inode.h +++ b/kmod/src/inode.h @@ -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); diff --git a/kmod/src/scoutfs_trace.h b/kmod/src/scoutfs_trace.h index 8e3d4eba..b920b038 100644 --- a/kmod/src/scoutfs_trace.h +++ b/kmod/src/scoutfs_trace.h @@ -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),