mirror of
https://github.com/versity/scoutfs.git
synced 2026-06-09 21:22:36 +00:00
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:
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
156
kmod/src/inode.c
156
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user