fs: implement multigrain timestamps
Subsystems (34)
Jeff Layton
maintainerv4 cover fs: implement multigrain timestamps
v4:
- add request_mask argument to generic_fillattr
- Drop current_ctime helper and just code functionality into current_time
- rework i_ctime accessor functions
A few weeks ago, during one of the discussions around i_version, Dave Chinner wrote this:
"You've missed the part where I suggested lifting the "nfsd sampled i_version" state into an inode state flag rather than hiding it in the i_version field. At that point, we could optimise away the secondary ctime updates just like you are proposing we do with the i_version updates. Further, we could also use that state it to decide whether we need to use high resolution timestamps when recording ctime updates - if the nfsd has not sampled the ctime/i_version, we don't need high res timestamps to be recorded for ctime...."
While I don't think we can practically optimize away ctime updates like we do with i_version, I do like the idea of using this scheme to indicate when we need to use a high-res timestamp.
The basic idea here is to use an unused bit in the timespec64.tv_nsec field to act as a flag to indicate that the value was queried since the last time we updated it. If that flag is set when we go to update the timestamp, we'll clear it and grab a fine-grained ktime value for the update.
The first couple of patches add the necessary infrastructure, and the last several patches update various filesystems to use it. For now, I'm focusing on widely-used, exportable filesystems, but this scheme is probably suitable for most filesystems in the kernel.
Note that this does cause at least one test failure with LTP's statx06 test. I have submitted a patch to fix the issue (by changing how it fetches the "after" timestamp in that test).
Jeff Layton (9):
fs: pass the request_mask to generic_fillattr
fs: add infrastructure for multigrain inode i_m/ctime
overlayfs: allow it to handle multigrain timestamps
nfsd: ensure we use ctime_peek to grab the inode->i_ctime
ksmbd: use ctime_peek to grab the ctime out of the inode
tmpfs: add support for multigrain timestamps
xfs: switch to multigrain timestamps
ext4: convert to multigrain timestamps
btrfs: convert to multigrain timestamps
fs/9p/vfs_inode.c | 4 +-- fs/9p/vfs_inode_dotl.c | 4 +-- fs/afs/inode.c | 2 +- fs/btrfs/delayed-inode.c | 2 +- fs/btrfs/inode.c | 4 +-- fs/btrfs/super.c | 5 +-- fs/btrfs/tree-log.c | 2 +- fs/ceph/inode.c | 2 +- fs/cifs/inode.c | 2 +- fs/coda/inode.c | 3 +- fs/ecryptfs/inode.c | 5 +-- fs/erofs/inode.c | 2 +- fs/exfat/file.c | 2 +- fs/ext2/inode.c | 2 +- fs/ext4/inode.c | 19 ++++++++-- fs/ext4/super.c | 2 +- fs/f2fs/file.c | 2 +- fs/fat/file.c | 2 +- fs/fuse/dir.c | 2 +- fs/gfs2/inode.c | 2 +- fs/hfsplus/inode.c | 2 +- fs/inode.c | 48 +++++++++++++++++++++---- fs/kernfs/inode.c | 2 +- fs/ksmbd/smb2pdu.c | 28 +++++++-------- fs/ksmbd/vfs.c | 3 +- fs/libfs.c | 4 +-- fs/minix/inode.c | 2 +- fs/nfs/inode.c | 2 +- fs/nfs/namespace.c | 3 +- fs/nfsd/nfsfh.c | 11 ++++-- fs/ntfs3/file.c | 2 +- fs/ocfs2/file.c | 2 +- fs/orangefs/inode.c | 2 +- fs/overlayfs/file.c | 7 ++-- fs/overlayfs/util.c | 2 +- fs/proc/base.c | 4 +-- fs/proc/fd.c | 2 +- fs/proc/generic.c | 2 +- fs/proc/proc_net.c | 2 +- fs/proc/proc_sysctl.c | 2 +- fs/proc/root.c | 3 +- fs/stat.c | 59 ++++++++++++++++++++++++------ fs/sysv/itree.c | 3 +- fs/ubifs/dir.c | 2 +- fs/udf/symlink.c | 2 +- fs/vboxsf/utils.c | 2 +- fs/xfs/libxfs/xfs_inode_buf.c | 2 +- fs/xfs/xfs_inode_item.c | 2 +- fs/xfs/xfs_iops.c | 4 +-- fs/xfs/xfs_super.c | 2 +- include/linux/fs.h | 68 +++++++++++++++++++++++++++++++++-- mm/shmem.c | 4 +-- 52 files changed, 260 insertions(+), 95 deletions(-)
Signature · 1 line
2.40.1
Jeff Layton
maintainerPATCH v4 1/9 fs: pass the request_mask to generic_fillattr
generic_fillattr just fills in the entire stat struct indiscriminately today, copying data from the inode. There is at least one attribute (STATX_CHANGE_COOKIE) that can have side effects when it is reported, and we're looking at adding more with the addition of multigrain timestamps.
Add a request_mask argument to generic_fillattr and have most callers just pass in the value that is passed to getattr. Have other callers (e.g. ksmbd) just pass in STATX_BASIC_STATS. Also move the setting of STATX_CHANGE_COOKIE into generic_fillattr.
--- fs/9p/vfs_inode.c | 4 ++-- fs/9p/vfs_inode_dotl.c | 4 ++-- fs/afs/inode.c | 2 +- fs/btrfs/inode.c | 2 +- fs/ceph/inode.c | 2 +- fs/cifs/inode.c | 2 +- fs/coda/inode.c | 3 ++- fs/ecryptfs/inode.c | 5 +++-- fs/erofs/inode.c | 2 +- fs/exfat/file.c | 2 +- fs/ext2/inode.c | 2 +- fs/ext4/inode.c | 2 +- fs/f2fs/file.c | 2 +- fs/fat/file.c | 2 +- fs/fuse/dir.c | 2 +- fs/gfs2/inode.c | 2 +- fs/hfsplus/inode.c | 2 +- fs/kernfs/inode.c | 2 +- fs/ksmbd/smb2pdu.c | 22 +++++++++++----------- fs/ksmbd/vfs.c | 3 ++- fs/libfs.c | 4 ++-- fs/minix/inode.c | 2 +- fs/nfs/inode.c | 2 +- fs/nfs/namespace.c | 3 ++- fs/ntfs3/file.c | 2 +- fs/ocfs2/file.c | 2 +- fs/orangefs/inode.c | 2 +- fs/proc/base.c | 4 ++-- fs/proc/fd.c | 2 +- fs/proc/generic.c | 2 +- fs/proc/proc_net.c | 2 +- fs/proc/proc_sysctl.c | 2 +- fs/proc/root.c | 3 ++- fs/stat.c | 18 ++++++++++-------- fs/sysv/itree.c | 3 ++- fs/ubifs/dir.c | 2 +- fs/udf/symlink.c | 2 +- fs/vboxsf/utils.c | 2 +- include/linux/fs.h | 2 +- mm/shmem.c | 2 +- 40 files changed, 70 insertions(+), 62 deletions(-)
fs/9p/vfs_inode.c +2 -2
@@ -1016,7 +1016,7 @@ v9fs_vfs_getattr(struct mnt_idmap *idmap, const struct path *path,p9_debug (P9_DEBUG_VFS ,"dentry: %p\n" , dentry); v9ses =v9fs_dentry2v9ses (dentry);if (v9ses->cache & (CACHE_META |CACHE_LOOSE )) { -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }else if (v9ses->cache &CACHE_WRITEBACK ) {if (S_ISREG (inode->i_mode)) {
@@ -1037,7 +1037,7 @@ v9fs_vfs_getattr(struct mnt_idmap *idmap, const struct path *path,return PTR_ERR (st);v9fs_stat2inode (st,d_inode (dentry), dentry->d_sb,0 ); -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (dentry), stat);p9stat_free (st);kfree (st);
fs/9p/vfs_inode_dotl.c +2 -2
@@ -451,7 +451,7 @@ v9fs_vfs_getattr_dotl(struct mnt_idmap *idmap,p9_debug (P9_DEBUG_VFS ,"dentry: %p\n" , dentry); v9ses =v9fs_dentry2v9ses (dentry);if (v9ses->cache & (CACHE_META |CACHE_LOOSE )) { -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }else if (v9ses->cache) {if (S_ISREG (inode->i_mode)) {
@@ -476,7 +476,7 @@ v9fs_vfs_getattr_dotl(struct mnt_idmap *idmap,return PTR_ERR (st);v9fs_stat2inode_dotl (st,d_inode (dentry),0 ); -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (dentry), stat);/* Change block size to what the server returned */ stat->blksize = st->st_blksize;
fs/afs/inode.c +1 -1
@@ -773,7 +773,7 @@ int afs_getattr(struct mnt_idmap *idmap, const struct path *path, do { read_seqbegin_or_lock(&vnode->cb_lock, &seq); - generic_fillattr(&nop_mnt_idmap, inode, stat); + generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); if (test_bit(AFS_VNODE_SILLY_DELETED, &vnode->flags) && stat->nlink > 0) stat->nlink -= 1;
fs/btrfs/inode.c +1 -1
@@ -8625,7 +8625,7 @@ static int btrfs_getattr(struct mnt_idmap *idmap,STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat); stat->dev =BTRFS_I (inode)->root->anon_dev;spin_lock (&BTRFS_I (inode)->lock);
fs/ceph/inode.c +1 -1
@@ -2465,7 +2465,7 @@ int ceph_getattr(struct mnt_idmap *idmap, const struct path *path,return err; } -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->ino =ceph_present_inode (inode);/*
fs/cifs/inode.c +1 -1
@@ -2540,7 +2540,7 @@ int cifs_getattr(struct mnt_idmap *idmap, const struct path *path,return rc; } -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->blksize = cifs_sb->ctx->bsize; stat->ino =CIFS_I (inode)->uniqueid;
fs/coda/inode.c +2 -1
@@ -256,7 +256,8 @@ int coda_getattr(struct mnt_idmap *idmap, const struct path *path, {int err =coda_revalidate_inode (d_inode (path->dentry));if (!err) -generic_fillattr (&nop_mnt_idmap,d_inode (path->dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask, +d_inode (path->dentry), stat);return err; }
fs/ecryptfs/inode.c +3 -2
@@ -982,7 +982,7 @@ static int ecryptfs_getattr_link(struct mnt_idmap *idmap, mount_crypt_stat = &ecryptfs_superblock_to_private ( dentry->d_sb)->mount_crypt_stat; -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (dentry), stat);if (mount_crypt_stat->flags &ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES ) {char *target;size_t targetsiz;
@@ -1011,7 +1011,8 @@ static int ecryptfs_getattr(struct mnt_idmap *idmap,if (!rc) {fsstack_copy_attr_all (d_inode (dentry),ecryptfs_inode_to_lower (d_inode (dentry))); -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask, +d_inode (dentry), stat); stat->blocks = lower_stat.blocks; }return rc;
fs/erofs/inode.c +1 -1
@@ -372,7 +372,7 @@ int erofs_getattr(struct mnt_idmap *idmap, const struct path *path, stat->attributes_mask |= (STATX_ATTR_COMPRESSED |STATX_ATTR_IMMUTABLE ); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);return 0 ; }
fs/exfat/file.c +1 -1
@@ -232,7 +232,7 @@ int exfat_getattr(struct mnt_idmap *idmap, const struct path *path,struct inode *inode =d_backing_inode (path->dentry);struct exfat_inode_info *ei =EXFAT_I (inode); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);exfat_truncate_atime (&stat->atime); stat->result_mask |=STATX_BTIME ; stat->btime.tv_sec = ei->i_crtime.tv_sec;
fs/ext2/inode.c +1 -1
@@ -1614,7 +1614,7 @@ int ext2_getattr(struct mnt_idmap *idmap, const struct path *path,STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }
fs/ext4/inode.c +1 -1
@@ -5539,7 +5539,7 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path,STATX_ATTR_NODUMP | STATX_ATTR_VERITY); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);return 0 ; }
fs/f2fs/file.c +1 -1
@@ -888,7 +888,7 @@ int f2fs_getattr(struct mnt_idmap *idmap, const struct path *path,STATX_ATTR_NODUMP | STATX_ATTR_VERITY); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);/* we need to show initial sectors used for inline_data/dentries */ if ((S_ISREG(inode->i_mode) && f2fs_has_inline_data(inode)) ||
fs/fat/file.c +1 -1
@@ -401,7 +401,7 @@ int fat_getattr(struct mnt_idmap *idmap, const struct path *path,struct inode *inode =d_inode (path->dentry);struct msdos_sb_info *sbi =MSDOS_SB (inode->i_sb); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat); stat->blksize = sbi->cluster_size;if (sbi->options.nfs ==FAT_NFS_NOSTALE_RO ) {
fs/fuse/dir.c +1 -1
@@ -1224,7 +1224,7 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,forget_all_cached_acls (inode); err =fuse_do_getattr (inode, stat, file); }else if (stat) { -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->mode = fi->orig_i_mode; stat->ino = fi->orig_ino; }
fs/gfs2/inode.c +1 -1
@@ -2071,7 +2071,7 @@ static int gfs2_getattr(struct mnt_idmap *idmap,STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (gfs2_holder_initialized (&gh))gfs2_glock_dq_uninit (&gh);
fs/hfsplus/inode.c +1 -1
@@ -298,7 +298,7 @@ int hfsplus_getattr(struct mnt_idmap *idmap, const struct path *path, stat->attributes_mask |=STATX_ATTR_APPEND |STATX_ATTR_IMMUTABLE |STATX_ATTR_NODUMP ; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }
fs/kernfs/inode.c +1 -1
@@ -191,7 +191,7 @@ int kernfs_iop_getattr(struct mnt_idmap *idmap,down_read (&root->kernfs_iattr_rwsem);kernfs_refresh_inode (kn, inode); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);up_read (&root->kernfs_iattr_rwsem);return 0 ;
fs/ksmbd/smb2pdu.c +11 -11
@@ -4342,8 +4342,8 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, } basic_info = (struct smb2_file_basic_info *)rsp->Buffer; -generic_fillattr (file_mnt_idmap (fp->filp),file_inode (fp->filp), - &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , +file_inode (fp->filp), &stat); basic_info->CreationTime =cpu_to_le64 (fp->create_time); time =ksmbd_UnixTimeToNT (stat.atime); basic_info->LastAccessTime =cpu_to_le64 (time);
@@ -4383,7 +4383,7 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp,struct kstat stat; inode =file_inode (fp->filp); -generic_fillattr (file_mnt_idmap (fp->filp), inode, &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , inode, &stat); sinfo = (struct smb2_file_standard_info *)rsp->Buffer; delete_pending =ksmbd_inode_pending_delete (fp);
@@ -4437,7 +4437,7 @@ static int get_file_all_info(struct ksmbd_work *work,return PTR_ERR (filename); inode =file_inode (fp->filp); -generic_fillattr (file_mnt_idmap (fp->filp), inode, &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , inode, &stat);ksmbd_debug (SMB ,"filename = %s\n" , filename); delete_pending =ksmbd_inode_pending_delete (fp);
@@ -4514,8 +4514,8 @@ static void get_file_stream_info(struct ksmbd_work *work,int buf_free_len;struct smb2_query_info_req *req =ksmbd_req_buf_next (work); -generic_fillattr (file_mnt_idmap (fp->filp),file_inode (fp->filp), - &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , +file_inode (fp->filp), &stat); file_info = (struct smb2_file_stream_info *)rsp->Buffer; buf_free_len =
@@ -4605,8 +4605,8 @@ static void get_file_internal_info(struct smb2_query_info_rsp *rsp,struct smb2_file_internal_info *file_info;struct kstat stat; -generic_fillattr (file_mnt_idmap (fp->filp),file_inode (fp->filp), - &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , +file_inode (fp->filp), &stat); file_info = (struct smb2_file_internal_info *)rsp->Buffer; file_info->IndexNumber =cpu_to_le64 (stat.ino); rsp->OutputBufferLength =
@@ -4631,7 +4631,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; inode =file_inode (fp->filp); -generic_fillattr (file_mnt_idmap (fp->filp), inode, &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , inode, &stat); file_info->CreationTime =cpu_to_le64 (fp->create_time); time =ksmbd_UnixTimeToNT (stat.atime);
@@ -4692,8 +4692,8 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp,struct smb2_file_comp_info *file_info;struct kstat stat; -generic_fillattr (file_mnt_idmap (fp->filp),file_inode (fp->filp), - &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , +file_inode (fp->filp), &stat); file_info = (struct smb2_file_comp_info *)rsp->Buffer; file_info->CompressedFileSize =cpu_to_le64 (stat.blocks <<9 );
fs/ksmbd/vfs.c +2 -1
@@ -1597,7 +1597,8 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,u64 time;int rc; -generic_fillattr (idmap,d_inode (dentry), ksmbd_kstat->kstat); +generic_fillattr (idmap,STATX_BASIC_STATS ,d_inode (dentry), + ksmbd_kstat->kstat); time =ksmbd_UnixTimeToNT (ksmbd_kstat->kstat->ctime); ksmbd_kstat->create_time = time;
fs/libfs.c +2 -2
@@ -33,7 +33,7 @@ int simple_getattr(struct mnt_idmap *idmap, const struct path *path, unsigned int query_flags) {struct inode *inode =d_inode (path->dentry); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->blocks = inode->i_mapping->nrpages << (PAGE_SHIFT -9 );return 0 ; }
@@ -1315,7 +1315,7 @@ static int empty_dir_getattr(struct mnt_idmap *idmap, u32 request_mask, unsigned int query_flags) { struct inode *inode = d_inode(path->dentry); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }
fs/minix/inode.c +1 -1
@@ -660,7 +660,7 @@ int minix_getattr(struct mnt_idmap *idmap, const struct path *path,struct super_block *sb = path->dentry->d_sb;struct inode *inode =d_inode (path->dentry); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (INODE_VERSION (inode) ==MINIX_V1 ) stat->blocks = (BLOCK_SIZE /512 ) *V1_minix_blocks (stat->size, sb);else
fs/nfs/inode.c +1 -1
@@ -912,7 +912,7 @@ int nfs_getattr(struct mnt_idmap *idmap, const struct path *path,/* Only return attributes that were revalidated. */ stat->result_mask =nfs_get_valid_attrmask (inode) | request_mask; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->ino =nfs_compat_user_ino64 (NFS_FILEID (inode)); stat->change_cookie =inode_peek_iversion_raw (inode); stat->attributes_mask |=STATX_ATTR_CHANGE_MONOTONIC ;
fs/nfs/namespace.c +2 -1
@@ -215,7 +215,8 @@ nfs_namespace_getattr(struct mnt_idmap *idmap,if (NFS_FH (d_inode (path->dentry))->size !=0 )return nfs_getattr (idmap, path, stat, request_mask, query_flags); -generic_fillattr (&nop_mnt_idmap,d_inode (path->dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (path->dentry), + stat);return 0 ; }
fs/ntfs3/file.c +1 -1
@@ -85,7 +85,7 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, stat->attributes_mask |=STATX_ATTR_COMPRESSED |STATX_ATTR_ENCRYPTED ; -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat); stat->result_mask |=STATX_BTIME ; stat->btime = ni->i_crtime;
fs/ocfs2/file.c +1 -1
@@ -1317,7 +1317,7 @@ int ocfs2_getattr(struct mnt_idmap *idmap, const struct path *path, goto bail; } -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);/* * If there is inline data in the inode, the inode will normally not * have data blocks allocated (it may have an external xattr block).
fs/orangefs/inode.c +1 -1
@@ -871,7 +871,7 @@ int orangefs_getattr(struct mnt_idmap *idmap, const struct path *path, ret =orangefs_inode_getattr (inode, request_mask &STATX_SIZE ?ORANGEFS_GETATTR_SIZE :0 );if (ret ==0 ) { -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);/* override block size reported to stat */ if (!(request_mask &STATX_SIZE ))
fs/proc/base.c +2 -2
@@ -1966,7 +1966,7 @@ int pid_getattr(struct mnt_idmap *idmap, const struct path *path,struct proc_fs_info *fs_info =proc_sb_info (inode->i_sb);struct task_struct *task; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->uid =GLOBAL_ROOT_UID ; stat->gid =GLOBAL_ROOT_GID ;
@@ -3899,7 +3899,7 @@ static int proc_task_getattr(struct mnt_idmap *idmap, {struct inode *inode =d_inode (path->dentry);struct task_struct *p =get_proc_task (inode); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (p) { stat->nlink +=get_nr_threads (p);
fs/proc/fd.c +1 -1
@@ -352,7 +352,7 @@ static int proc_fd_getattr(struct mnt_idmap *idmap,struct inode *inode =d_inode (path->dentry);int rv =0 ; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);/* If it's a directory, put the number of open fds there */ if (S_ISDIR (inode->i_mode)) {
fs/proc/generic.c +1 -1
@@ -146,7 +146,7 @@ static int proc_getattr(struct mnt_idmap *idmap, } } -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }
fs/proc/proc_net.c +1 -1
@@ -308,7 +308,7 @@ static int proc_tgid_net_getattr(struct mnt_idmap *idmap, net =get_proc_task_net (inode); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (net !=NULL ) { stat->nlink = net->proc_net->nlink;
fs/proc/proc_sysctl.c +1 -1
@@ -855,7 +855,7 @@ static int proc_sys_getattr(struct mnt_idmap *idmap,if (IS_ERR (head))return PTR_ERR (head); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (table) stat->mode = (stat->mode &S_IFMT ) | table->mode;
fs/proc/root.c +2 -1
@@ -314,7 +314,8 @@ static int proc_root_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int query_flags) { - generic_fillattr(&nop_mnt_idmap, d_inode(path->dentry), stat); + generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(path->dentry), +stat ); stat->nlink = proc_root.nlink +nr_processes ();return 0 ; }
fs/stat.c +10 -8
@@ -29,6 +29,7 @@/** * generic_fillattr - Fill in the basic attributes from the inode struct * @idmap: idmap of the mount the inode was found from +* @req_mask statx request_mask * @inode: Inode to use as the source * @stat: Where to fill in the attributes *
@@ -42,8 +43,8 @@* uid and gid filds. On non-idmapped mounts or if permission checking is to be * performed on the raw inode simply passs @nop_mnt_idmap. */ -void generic_fillattr (struct mnt_idmap *idmap,struct inode *inode, -struct kstat *stat) +void generic_fillattr (struct mnt_idmap *idmap,u32 request_mask, +struct inode *inode,struct kstat *stat) {vfsuid_t vfsuid =i_uid_into_vfsuid (idmap, inode);vfsgid_t vfsgid =i_gid_into_vfsgid (idmap, inode);
@@ -61,6 +62,12 @@ void generic_fillattr(struct mnt_idmap *idmap, struct inode *inode, stat->ctime = inode->i_ctime; stat->blksize =i_blocksize (inode); stat->blocks = inode->i_blocks; + +if ((request_mask &STATX_CHANGE_COOKIE ) &&IS_I_VERSION (inode)) { + stat->result_mask |=STATX_CHANGE_COOKIE ; + stat->change_cookie =inode_query_iversion (inode); + } + }EXPORT_SYMBOL (generic_fillattr);
@@ -123,17 +130,12 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT |STATX_ATTR_DAX ); -if ((request_mask &STATX_CHANGE_COOKIE ) &&IS_I_VERSION (inode)) { - stat->result_mask |=STATX_CHANGE_COOKIE ; - stat->change_cookie =inode_query_iversion (inode); - } - idmap =mnt_idmap (path->mnt);if (inode->i_op->getattr)return inode->i_op->getattr (idmap, path, stat, request_mask, query_flags); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);return 0 ; }EXPORT_SYMBOL (vfs_getattr_nosec);
fs/sysv/itree.c +2 -1
@@ -445,7 +445,8 @@ int sysv_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) { struct super_block *s = path->dentry->d_sb; -generic_fillattr (&nop_mnt_idmap,d_inode (path->dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (path->dentry), + stat); stat->blocks = (s->s_blocksize /512 ) *sysv_nblocks (s, stat->size); stat->blksize = s->s_blocksize;return 0 ;
fs/ubifs/dir.c +1 -1
@@ -1665,7 +1665,7 @@ int ubifs_getattr(struct mnt_idmap *idmap, const struct path *path,STATX_ATTR_ENCRYPTED | STATX_ATTR_IMMUTABLE); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->blksize =UBIFS_BLOCK_SIZE ; stat->size = ui->ui_size;
fs/udf/symlink.c +1 -1
@@ -153,7 +153,7 @@ static int udf_symlink_getattr(struct mnt_idmap *idmap,struct inode *inode =d_backing_inode (dentry);struct page *page; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); page =read_mapping_page (inode->i_mapping,0 ,NULL );if (IS_ERR (page))return PTR_ERR (page);
fs/vboxsf/utils.c +1 -1
@@ -252,7 +252,7 @@ int vboxsf_getattr(struct mnt_idmap *idmap, const struct path *path,if (err)return err; -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), kstat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (dentry), kstat);return 0 ; }
include/linux/fs.h +1 -1
@@ -2857,7 +2857,7 @@ extern void page_put_link(void *);extern int page_symlink (struct inode *inode,const char *symname,int len);extern const struct inode_operations page_symlink_inode_operations;extern void kfree_link (void *); -void generic_fillattr (struct mnt_idmap *,struct inode *,struct kstat *); +void generic_fillattr (struct mnt_idmap *,u32 ,struct inode *,struct kstat *);void generic_fill_statx_attr (struct inode *inode,struct kstat *stat);extern int vfs_getattr_nosec (const struct path *,struct kstat *,u32 ,unsigned int );extern int vfs_getattr (const struct path *,struct kstat *,u32 ,unsigned int );
mm/shmem.c +1 -1
@@ -1073,7 +1073,7 @@ static int shmem_getattr(struct mnt_idmap *idmap, stat->attributes_mask |= (STATX_ATTR_APPEND |STATX_ATTR_IMMUTABLE |STATX_ATTR_NODUMP ); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);if (shmem_is_huge (inode,0 , false,NULL ,0 )) stat->blksize =HPAGE_PMD_SIZE ;
Signature · 1 line
2.40.1
Re: [PATCH v4 1/9] fs: pass the request_mask to generic_fillattr
Quoted 6 lines · Jeff Layton
generic_fillattrjust fills in the entire stat struct indiscriminately today, copying data from the inode. There is at least one attribute (STATX_CHANGE_COOKIE) that can have side effects when it is reported, and we're looking at adding more with the addition of multigrain timestamps.
Quoted 6 lines
Add a
request_maskargument togeneric_fillattrand have most callers just pass in the value that is passed to getattr. Have other callers (e.g. ksmbd) just pass inSTATX_BASIC_STATS. Also move the setting ofSTATX_CHANGE_COOKIEintogeneric_fillattr.Signed-off-by Jeff Layton <[email protected]>
Looks good to me. Feel free to add:
Honza
Quoted 721 lines
--- fs/9p/vfs_inode.c | 4 ++-- fs/9p/vfs_inode_dotl.c | 4 ++-- fs/afs/inode.c | 2 +- fs/btrfs/inode.c | 2 +- fs/ceph/inode.c | 2 +- fs/cifs/inode.c | 2 +- fs/coda/inode.c | 3 ++- fs/ecryptfs/inode.c | 5 +++-- fs/erofs/inode.c | 2 +- fs/exfat/file.c | 2 +- fs/ext2/inode.c | 2 +- fs/ext4/inode.c | 2 +- fs/f2fs/file.c | 2 +- fs/fat/file.c | 2 +- fs/fuse/dir.c | 2 +- fs/gfs2/inode.c | 2 +- fs/hfsplus/inode.c | 2 +- fs/kernfs/inode.c | 2 +- fs/ksmbd/smb2pdu.c | 22 +++++++++++----------- fs/ksmbd/vfs.c | 3 ++- fs/libfs.c | 4 ++-- fs/minix/inode.c | 2 +- fs/nfs/inode.c | 2 +- fs/nfs/namespace.c | 3 ++- fs/ntfs3/file.c | 2 +- fs/ocfs2/file.c | 2 +- fs/orangefs/inode.c | 2 +- fs/proc/base.c | 4 ++-- fs/proc/fd.c | 2 +- fs/proc/generic.c | 2 +- fs/proc/proc_net.c | 2 +- fs/proc/proc_sysctl.c | 2 +- fs/proc/root.c | 3 ++- fs/stat.c | 18 ++++++++++-------- fs/sysv/itree.c | 3 ++- fs/ubifs/dir.c | 2 +- fs/udf/symlink.c | 2 +- fs/vboxsf/utils.c | 2 +- include/linux/fs.h | 2 +- mm/shmem.c | 2 +- 40 files changed, 70 insertions(+), 62 deletions(-)fs/9p/vfs_inode.c +2
-2@@ -1016,7 +1016,7 @@ v9fs_vfs_getattr(struct mnt_idmap *idmap, const struct path *path,p9_debug (P9_DEBUG_VFS ,"dentry: %p\n" , dentry); v9ses =v9fs_dentry2v9ses (dentry);if (v9ses->cache & (CACHE_META |CACHE_LOOSE )) { -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }else if (v9ses->cache &CACHE_WRITEBACK ) {if (S_ISREG (inode->i_mode)) {@@ -1037,7 +1037,7 @@ v9fs_vfs_getattr(struct mnt_idmap *idmap, const struct path *path,return PTR_ERR (st);v9fs_stat2inode (st,d_inode (dentry), dentry->d_sb,0 ); -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (dentry), stat);p9stat_free (st);kfree (st);fs/9p/vfs_inode_dotl.c +2
-2@@ -451,7 +451,7 @@ v9fs_vfs_getattr_dotl(struct mnt_idmap *idmap,p9_debug (P9_DEBUG_VFS ,"dentry: %p\n" , dentry); v9ses =v9fs_dentry2v9ses (dentry);if (v9ses->cache & (CACHE_META |CACHE_LOOSE )) { -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }else if (v9ses->cache) {if (S_ISREG (inode->i_mode)) {@@ -476,7 +476,7 @@ v9fs_vfs_getattr_dotl(struct mnt_idmap *idmap,return PTR_ERR (st);v9fs_stat2inode_dotl (st,d_inode (dentry),0 ); -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (dentry), stat);/* Change block size to what the server returned */ stat->blksize = st->st_blksize;fs/afs/inode.c +1
-1@@ -773,7 +773,7 @@ int afs_getattr(struct mnt_idmap *idmap, const struct path *path, do { read_seqbegin_or_lock(&vnode->cb_lock, &seq); - generic_fillattr(&nop_mnt_idmap, inode, stat); + generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); if (test_bit(AFS_VNODE_SILLY_DELETED, &vnode->flags) && stat->nlink > 0) stat->nlink -= 1;fs/btrfs/inode.c +1
-1@@ -8625,7 +8625,7 @@ static int btrfs_getattr(struct mnt_idmap *idmap,STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat); stat->dev =BTRFS_I (inode)->root->anon_dev;spin_lock (&BTRFS_I (inode)->lock);fs/ceph/inode.c +1
-1@@ -2465,7 +2465,7 @@ int ceph_getattr(struct mnt_idmap *idmap, const struct path *path,return err; } -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->ino =ceph_present_inode (inode);/* fs/cifs/inode.c +1
-1@@ -2540,7 +2540,7 @@ int cifs_getattr(struct mnt_idmap *idmap, const struct path *path,return rc; } -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->blksize = cifs_sb->ctx->bsize; stat->ino =CIFS_I (inode)->uniqueid;fs/coda/inode.c +2
-1@@ -256,7 +256,8 @@ int coda_getattr(struct mnt_idmap *idmap, const struct path *path, {int err =coda_revalidate_inode (d_inode (path->dentry));if (!err) -generic_fillattr (&nop_mnt_idmap,d_inode (path->dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask, +d_inode (path->dentry), stat);return err; }fs/ecryptfs/inode.c +3
-2@@ -982,7 +982,7 @@ static int ecryptfs_getattr_link(struct mnt_idmap *idmap, mount_crypt_stat = &ecryptfs_superblock_to_private ( dentry->d_sb)->mount_crypt_stat; -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (dentry), stat);if (mount_crypt_stat->flags &ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES ) {char *target;size_t targetsiz;@@ -1011,7 +1011,8 @@ static int ecryptfs_getattr(struct mnt_idmap *idmap,if (!rc) {fsstack_copy_attr_all (d_inode (dentry),ecryptfs_inode_to_lower (d_inode (dentry))); -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask, +d_inode (dentry), stat); stat->blocks = lower_stat.blocks; }return rc;fs/erofs/inode.c +1
-1@@ -372,7 +372,7 @@ int erofs_getattr(struct mnt_idmap *idmap, const struct path *path, stat->attributes_mask |= (STATX_ATTR_COMPRESSED |STATX_ATTR_IMMUTABLE ); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);return 0 ; }fs/exfat/file.c +1
-1@@ -232,7 +232,7 @@ int exfat_getattr(struct mnt_idmap *idmap, const struct path *path,struct inode *inode =d_backing_inode (path->dentry);struct exfat_inode_info *ei =EXFAT_I (inode); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);exfat_truncate_atime (&stat->atime); stat->result_mask |=STATX_BTIME ; stat->btime.tv_sec = ei->i_crtime.tv_sec;fs/ext2/inode.c +1
-1@@ -1614,7 +1614,7 @@ int ext2_getattr(struct mnt_idmap *idmap, const struct path *path,STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }fs/ext4/inode.c +1
-1@@ -5539,7 +5539,7 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path,STATX_ATTR_NODUMP | STATX_ATTR_VERITY); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);return 0 ; }fs/f2fs/file.c +1
-1@@ -888,7 +888,7 @@ int f2fs_getattr(struct mnt_idmap *idmap, const struct path *path,STATX_ATTR_NODUMP | STATX_ATTR_VERITY); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);/* we need to show initial sectors used for inline_data/dentries */ if ((S_ISREG(inode->i_mode) && f2fs_has_inline_data(inode)) ||fs/fat/file.c +1
-1@@ -401,7 +401,7 @@ int fat_getattr(struct mnt_idmap *idmap, const struct path *path,struct inode *inode =d_inode (path->dentry);struct msdos_sb_info *sbi =MSDOS_SB (inode->i_sb); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat); stat->blksize = sbi->cluster_size;if (sbi->options.nfs ==FAT_NFS_NOSTALE_RO ) {fs/fuse/dir.c +1
-1@@ -1224,7 +1224,7 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,forget_all_cached_acls (inode); err =fuse_do_getattr (inode, stat, file); }else if (stat) { -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->mode = fi->orig_i_mode; stat->ino = fi->orig_ino; }fs/gfs2/inode.c +1
-1@@ -2071,7 +2071,7 @@ static int gfs2_getattr(struct mnt_idmap *idmap,STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (gfs2_holder_initialized (&gh))gfs2_glock_dq_uninit (&gh);fs/hfsplus/inode.c +1
-1@@ -298,7 +298,7 @@ int hfsplus_getattr(struct mnt_idmap *idmap, const struct path *path, stat->attributes_mask |=STATX_ATTR_APPEND |STATX_ATTR_IMMUTABLE |STATX_ATTR_NODUMP ; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }fs/kernfs/inode.c +1
-1@@ -191,7 +191,7 @@ int kernfs_iop_getattr(struct mnt_idmap *idmap,down_read (&root->kernfs_iattr_rwsem);kernfs_refresh_inode (kn, inode); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);up_read (&root->kernfs_iattr_rwsem);return 0 ;fs/ksmbd/smb2pdu.c +11
-11@@ -4342,8 +4342,8 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, } basic_info = (struct smb2_file_basic_info *)rsp->Buffer; -generic_fillattr (file_mnt_idmap (fp->filp),file_inode (fp->filp), - &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , +file_inode (fp->filp), &stat); basic_info->CreationTime =cpu_to_le64 (fp->create_time); time =ksmbd_UnixTimeToNT (stat.atime); basic_info->LastAccessTime =cpu_to_le64 (time);@@ -4383,7 +4383,7 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp,struct kstat stat; inode =file_inode (fp->filp); -generic_fillattr (file_mnt_idmap (fp->filp), inode, &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , inode, &stat); sinfo = (struct smb2_file_standard_info *)rsp->Buffer; delete_pending =ksmbd_inode_pending_delete (fp);@@ -4437,7 +4437,7 @@ static int get_file_all_info(struct ksmbd_work *work,return PTR_ERR (filename); inode =file_inode (fp->filp); -generic_fillattr (file_mnt_idmap (fp->filp), inode, &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , inode, &stat);ksmbd_debug (SMB ,"filename = %s\n" , filename); delete_pending =ksmbd_inode_pending_delete (fp);@@ -4514,8 +4514,8 @@ static void get_file_stream_info(struct ksmbd_work *work,int buf_free_len;struct smb2_query_info_req *req =ksmbd_req_buf_next (work); -generic_fillattr (file_mnt_idmap (fp->filp),file_inode (fp->filp), - &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , +file_inode (fp->filp), &stat); file_info = (struct smb2_file_stream_info *)rsp->Buffer; buf_free_len =@@ -4605,8 +4605,8 @@ static void get_file_internal_info(struct smb2_query_info_rsp *rsp,struct smb2_file_internal_info *file_info;struct kstat stat; -generic_fillattr (file_mnt_idmap (fp->filp),file_inode (fp->filp), - &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , +file_inode (fp->filp), &stat); file_info = (struct smb2_file_internal_info *)rsp->Buffer; file_info->IndexNumber =cpu_to_le64 (stat.ino); rsp->OutputBufferLength =@@ -4631,7 +4631,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; inode =file_inode (fp->filp); -generic_fillattr (file_mnt_idmap (fp->filp), inode, &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , inode, &stat); file_info->CreationTime =cpu_to_le64 (fp->create_time); time =ksmbd_UnixTimeToNT (stat.atime);@@ -4692,8 +4692,8 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp,struct smb2_file_comp_info *file_info;struct kstat stat; -generic_fillattr (file_mnt_idmap (fp->filp),file_inode (fp->filp), - &stat); +generic_fillattr (file_mnt_idmap (fp->filp),STATX_BASIC_STATS , +file_inode (fp->filp), &stat); file_info = (struct smb2_file_comp_info *)rsp->Buffer; file_info->CompressedFileSize =cpu_to_le64 (stat.blocks <<9 );fs/ksmbd/vfs.c +2
-1@@ -1597,7 +1597,8 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,u64 time;int rc; -generic_fillattr (idmap,d_inode (dentry), ksmbd_kstat->kstat); +generic_fillattr (idmap,STATX_BASIC_STATS ,d_inode (dentry), + ksmbd_kstat->kstat); time =ksmbd_UnixTimeToNT (ksmbd_kstat->kstat->ctime); ksmbd_kstat->create_time = time;fs/libfs.c +2
-2@@ -33,7 +33,7 @@ int simple_getattr(struct mnt_idmap *idmap, const struct path *path, unsigned int query_flags) {struct inode *inode =d_inode (path->dentry); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->blocks = inode->i_mapping->nrpages << (PAGE_SHIFT -9 );return 0 ; }@@ -1315,7 +1315,7 @@ static int empty_dir_getattr(struct mnt_idmap *idmap, u32 request_mask, unsigned int query_flags) { struct inode *inode = d_inode(path->dentry); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }fs/minix/inode.c +1
-1@@ -660,7 +660,7 @@ int minix_getattr(struct mnt_idmap *idmap, const struct path *path,struct super_block *sb = path->dentry->d_sb;struct inode *inode =d_inode (path->dentry); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (INODE_VERSION (inode) ==MINIX_V1 ) stat->blocks = (BLOCK_SIZE /512 ) *V1_minix_blocks (stat->size, sb);else fs/nfs/inode.c +1
-1@@ -912,7 +912,7 @@ int nfs_getattr(struct mnt_idmap *idmap, const struct path *path,/* Only return attributes that were revalidated. */ stat->result_mask =nfs_get_valid_attrmask (inode) | request_mask; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->ino =nfs_compat_user_ino64 (NFS_FILEID (inode)); stat->change_cookie =inode_peek_iversion_raw (inode); stat->attributes_mask |=STATX_ATTR_CHANGE_MONOTONIC ;fs/nfs/namespace.c +2
-1@@ -215,7 +215,8 @@ nfs_namespace_getattr(struct mnt_idmap *idmap,if (NFS_FH (d_inode (path->dentry))->size !=0 )return nfs_getattr (idmap, path, stat, request_mask, query_flags); -generic_fillattr (&nop_mnt_idmap,d_inode (path->dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (path->dentry), + stat);return 0 ; }fs/ntfs3/file.c +1
-1@@ -85,7 +85,7 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, stat->attributes_mask |=STATX_ATTR_COMPRESSED |STATX_ATTR_ENCRYPTED ; -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat); stat->result_mask |=STATX_BTIME ; stat->btime = ni->i_crtime;fs/ocfs2/file.c +1
-1@@ -1317,7 +1317,7 @@ int ocfs2_getattr(struct mnt_idmap *idmap, const struct path *path, goto bail; } -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);/* * If there is inline data in the inode, the inode will normally not * have data blocks allocated (it may have an external xattr block). fs/orangefs/inode.c +1
-1@@ -871,7 +871,7 @@ int orangefs_getattr(struct mnt_idmap *idmap, const struct path *path, ret =orangefs_inode_getattr (inode, request_mask &STATX_SIZE ?ORANGEFS_GETATTR_SIZE :0 );if (ret ==0 ) { -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);/* override block size reported to stat */ if (!(request_mask &STATX_SIZE ))fs/proc/base.c +2
-2@@ -1966,7 +1966,7 @@ int pid_getattr(struct mnt_idmap *idmap, const struct path *path,struct proc_fs_info *fs_info =proc_sb_info (inode->i_sb);struct task_struct *task; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->uid =GLOBAL_ROOT_UID ; stat->gid =GLOBAL_ROOT_GID ;@@ -3899,7 +3899,7 @@ static int proc_task_getattr(struct mnt_idmap *idmap, {struct inode *inode =d_inode (path->dentry);struct task_struct *p =get_proc_task (inode); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (p) { stat->nlink +=get_nr_threads (p);fs/proc/fd.c +1
-1@@ -352,7 +352,7 @@ static int proc_fd_getattr(struct mnt_idmap *idmap,struct inode *inode =d_inode (path->dentry);int rv =0 ; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);/* If it's a directory, put the number of open fds there */ if (S_ISDIR (inode->i_mode)) {fs/proc/generic.c +1
-1@@ -146,7 +146,7 @@ static int proc_getattr(struct mnt_idmap *idmap, } } -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);return 0 ; }fs/proc/proc_net.c +1
-1@@ -308,7 +308,7 @@ static int proc_tgid_net_getattr(struct mnt_idmap *idmap, net =get_proc_task_net (inode); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (net !=NULL ) { stat->nlink = net->proc_net->nlink;fs/proc/proc_sysctl.c +1
-1@@ -855,7 +855,7 @@ static int proc_sys_getattr(struct mnt_idmap *idmap,if (IS_ERR (head))return PTR_ERR (head); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat);if (table) stat->mode = (stat->mode &S_IFMT ) | table->mode;fs/proc/root.c +2
-1@@ -314,7 +314,8 @@ static int proc_root_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int query_flags) { - generic_fillattr(&nop_mnt_idmap, d_inode(path->dentry), stat); + generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(path->dentry), +stat ); stat->nlink = proc_root.nlink +nr_processes ();return 0 ; }fs/stat.c +10
-8@@ -29,6 +29,7 @@/** * generic_fillattr - Fill in the basic attributes from the inode struct * @idmap: idmap of the mount the inode was found from +* @req_mask statx request_mask * @inode: Inode to use as the source * @stat: Where to fill in the attributes * @@ -42,8 +43,8 @@* uid and gid filds. On non-idmapped mounts or if permission checking is to be * performed on the raw inode simply passs @nop_mnt_idmap. */ -void generic_fillattr (struct mnt_idmap *idmap,struct inode *inode, -struct kstat *stat) +void generic_fillattr (struct mnt_idmap *idmap,u32 request_mask, +struct inode *inode,struct kstat *stat) {vfsuid_t vfsuid =i_uid_into_vfsuid (idmap, inode);vfsgid_t vfsgid =i_gid_into_vfsgid (idmap, inode);@@ -61,6 +62,12 @@ void generic_fillattr(struct mnt_idmap *idmap, struct inode *inode, stat->ctime = inode->i_ctime; stat->blksize =i_blocksize (inode); stat->blocks = inode->i_blocks; + +if ((request_mask &STATX_CHANGE_COOKIE ) &&IS_I_VERSION (inode)) { + stat->result_mask |=STATX_CHANGE_COOKIE ; + stat->change_cookie =inode_query_iversion (inode); + } + }EXPORT_SYMBOL (generic_fillattr);@@ -123,17 +130,12 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT |STATX_ATTR_DAX ); -if ((request_mask &STATX_CHANGE_COOKIE ) &&IS_I_VERSION (inode)) { - stat->result_mask |=STATX_CHANGE_COOKIE ; - stat->change_cookie =inode_query_iversion (inode); - } - idmap =mnt_idmap (path->mnt);if (inode->i_op->getattr)return inode->i_op->getattr (idmap, path, stat, request_mask, query_flags); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);return 0 ; }EXPORT_SYMBOL (vfs_getattr_nosec);fs/sysv/itree.c +2
-1@@ -445,7 +445,8 @@ int sysv_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) { struct super_block *s = path->dentry->d_sb; -generic_fillattr (&nop_mnt_idmap,d_inode (path->dentry), stat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (path->dentry), + stat); stat->blocks = (s->s_blocksize /512 ) *sysv_nblocks (s, stat->size); stat->blksize = s->s_blocksize;return 0 ;fs/ubifs/dir.c +1
-1@@ -1665,7 +1665,7 @@ int ubifs_getattr(struct mnt_idmap *idmap, const struct path *path,STATX_ATTR_ENCRYPTED | STATX_ATTR_IMMUTABLE); -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); stat->blksize =UBIFS_BLOCK_SIZE ; stat->size = ui->ui_size;fs/udf/symlink.c +1
-1@@ -153,7 +153,7 @@ static int udf_symlink_getattr(struct mnt_idmap *idmap,struct inode *inode =d_backing_inode (dentry);struct page *page; -generic_fillattr (&nop_mnt_idmap, inode, stat); +generic_fillattr (&nop_mnt_idmap, request_mask, inode, stat); page =read_mapping_page (inode->i_mapping,0 ,NULL );if (IS_ERR (page))return PTR_ERR (page);fs/vboxsf/utils.c +1
-1@@ -252,7 +252,7 @@ int vboxsf_getattr(struct mnt_idmap *idmap, const struct path *path,if (err)return err; -generic_fillattr (&nop_mnt_idmap,d_inode (dentry), kstat); +generic_fillattr (&nop_mnt_idmap, request_mask,d_inode (dentry), kstat);return 0 ; }include/linux/fs.h +1
-1@@ -2857,7 +2857,7 @@ extern void page_put_link(void *);extern int page_symlink (struct inode *inode,const char *symname,int len);extern const struct inode_operations page_symlink_inode_operations;extern void kfree_link (void *); -void generic_fillattr (struct mnt_idmap *,struct inode *,struct kstat *); +void generic_fillattr (struct mnt_idmap *,u32 ,struct inode *,struct kstat *);void generic_fill_statx_attr (struct inode *inode,struct kstat *stat);extern int vfs_getattr_nosec (const struct path *,struct kstat *,u32 ,unsigned int );extern int vfs_getattr (const struct path *,struct kstat *,u32 ,unsigned int );mm/shmem.c +1
-1@@ -1073,7 +1073,7 @@ static int shmem_getattr(struct mnt_idmap *idmap, stat->attributes_mask |= (STATX_ATTR_APPEND |STATX_ATTR_IMMUTABLE |STATX_ATTR_NODUMP ); -generic_fillattr (idmap, inode, stat); +generic_fillattr (idmap, request_mask, inode, stat);if (shmem_is_huge (inode,0 , false,NULL ,0 )) stat->blksize =HPAGE_PMD_SIZE ;Signature · 1 line
2.40.1
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jeff Layton
maintainerPATCH v4 2/9 fs: add infrastructure for multigrain inode i_m/ctime
In reply to Jeff LaytonThe VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the tv_nsec field are ever used. Whenever the mtime changes, the ctime must also change.
Use the 31st bit of the ctime tv_nsec field to indicate that something has queried the inode for the i_mtime or i_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the FS_MULTIGRAIN_TS flag in the fstype.
Later patches will convert individual filesystems over to use it.
--- fs/inode.c | 48 ++++++++++++++++++++++++++++----- fs/stat.c | 41 ++++++++++++++++++++++++++-- include/linux/fs.h | 66 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 145 insertions(+), 10 deletions(-)
fs/inode.c +41 -7
@@ -2029,6 +2029,7 @@ EXPORT_SYMBOL(file_remove_privs);static int inode_needs_update_time (struct inode *inode,struct timespec64 *now) {int sync_it =0 ; +struct timespec64 ctime;/* First try to exhaust all avenues to not sync */ if (IS_NOCMTIME (inode))
@@ -2037,7 +2038,8 @@ static int inode_needs_update_time(struct inode *inode, struct timespec64 *now)if (!timespec64_equal (&inode->i_mtime, now)) sync_it =S_MTIME ; -if (!timespec64_equal (&inode->i_ctime, now)) + ctime =ctime_peek (inode); +if (!timespec64_equal (&ctime, now)) sync_it |=S_CTIME ;if (IS_I_VERSION (inode) &&inode_iversion_need_inc (inode))
@@ -2431,6 +2433,40 @@ struct timespec64 timestamp_truncate(struct timespec64 t, struct inode *inode) }EXPORT_SYMBOL (timestamp_truncate); +/** +* current_mg_time - Return FS time (possibly fine-grained) +* @inode: inode. +* +* Return the current time truncated to the time granularity supported by +* the fs, as suitable for a ctime/mtime change. If the ctime is flagged +* as having been QUERIED, get a fine-grained timestamp. +*/ +static struct timespec64 current_mg_time (struct inode *inode) +{ +struct timespec64 now; +atomic_long_t *pnsec = (atomic_long_t *)&inode->i_ctime.tv_nsec; +long nsec =atomic_long_fetch_andnot (I_CTIME_QUERIED , pnsec); + +if (nsec &I_CTIME_QUERIED ) { +ktime_get_real_ts64 (&now); + }else { +struct timespec64 ctime; + +ktime_get_coarse_real_ts64 (&now); + +/* +* If we've recently fetched a fine-grained timestamp +* then the coarse-grained one may still be earlier than the +* existing one. Just keep the existing ctime if so. +*/ + ctime =ctime_peek (inode); +if (timespec64_compare (&ctime, &now) >0 ) + now = ctime; + } + +return now; +} +/** * current_time - Return FS time * @inode: inode.
@@ -2445,12 +2481,10 @@ struct timespec64 current_time(struct inode *inode) {struct timespec64 now; -ktime_get_coarse_real_ts64 (&now); - -if (unlikely (!inode->i_sb)) { -WARN (1 ,"current_time() called with uninitialized super_block in the inode" ); -return now; - } +if (is_multigrain_ts (inode)) + now =current_mg_time (inode); +else +ktime_get_coarse_real_ts64 (&now);return timestamp_truncate (now, inode); }
fs/stat.c +39 -2
56 lines not on this page · raw message
include/linux/fs.h +65 -1
90 lines not on this page · raw message
Signature · 1 line
2.40.1
Theodore Ts'o
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 4 lines · Jeff Layton
Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.
TIL.... that atomic_long_fetch_or() and atomic_long_fetch_andnot() exist. :-)
When I went looking for documentation about why they do or this particular usage pattern found in the patch, I didn't find any --- at least, certainly not in the Documentation in the kernel sources. The closest that I fond was this LWN article written by Neil Brown from 2016:
https://lwn.net/Articles/698315/
... and this only covered the use atomic_fetch_or(); I wasn't able to find anything discussing atomic_fetch_andnot().
It looks like Peter Zijlstra added some bare-bones documentation in 2017, in commit: 706eeb3e9c6f ("Documentation/locking/atomic: Add documents for new atomic_t APIs") so we do have Documentation that these functions *exist*, but there is nothing explaining what they do, or how they can be used (e.g., in this rather clever way to set and clear a flag in the high bits of the nsec field).
I know that it's best to report missing documentation in the form of a patch, but I fear I don't have the time or the expertise to really do this topic justice, so I'd just thought I'd just note this lack for now, and maybe in my copious spare time I'll try to get at least something that will no doubt contain errors, but might inspire some folks to correct the text. (Or maybe on someone on linux-doc will feel inspired and get there ahead of me. :-)
- Ted
Re: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
[Cc Paul and Mark]
Quoted 27 lines · Theodore Ts'o
Quoted 4 lines · Jeff Layton
Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.TIL.... that
atomic_long_fetch_or()andatomic_long_fetch_andnot()exist. :-)When I went looking for documentation about why they do or this particular usage pattern found in the patch, I didn't find any --- at least, certainly not in the Documentation in the kernel sources. The closest that I fond was this LWN article written by Neil Brown from 2016:
https://lwn.net/Articles/698315/... and this only covered the use
atomic_fetch_or(); I wasn't able to find anything discussingatomic_fetch_andnot().It looks like Peter Zijlstra added some bare-bones documentation in 2017, in commit:
706eeb3e9c6f("Documentation/locking/atomic: Add documents for newatomic_tAPIs") so we do have Documentation that these functions *exist*, but there is nothing explaining what they do, or how they can be used (e.g., in this rather clever way to set and clear a flag in the high bits of the nsec field).
Quoted 8 lines
I know that it's best to report missing documentation in the form of a patch, but I fear I don't have the time or the expertise to really do this topic justice, so I'd just thought I'd just note this lack for now, and maybe in my copious spare time I'll try to get at least something that will no doubt contain errors, but might inspire some folks to correct the text. (Or maybe on someone on linux-doc will feel inspired and get there ahead of me. :-)
Paul already started the work:
https://lore.kernel.org/lkml/19135936-06d7-4705-8bc8-bb31c2a478ca@paulmck-laptop/
;-)
Regards,
Boqun
Quoted 1 line
- Ted
Re: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 39 lines · Boqun Feng
[Cc Paul and Mark]
Quoted 27 lines · Theodore Ts'o
Quoted 4 lines · Jeff Layton
Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.TIL.... that
atomic_long_fetch_or()andatomic_long_fetch_andnot()exist. :-)When I went looking for documentation about why they do or this particular usage pattern found in the patch, I didn't find any --- at least, certainly not in the Documentation in the kernel sources. The closest that I fond was this LWN article written by Neil Brown from 2016:
https://lwn.net/Articles/698315/... and this only covered the use
atomic_fetch_or(); I wasn't able to find anything discussingatomic_fetch_andnot().It looks like Peter Zijlstra added some bare-bones documentation in 2017, in commit:
706eeb3e9c6f("Documentation/locking/atomic: Add documents for newatomic_tAPIs") so we do have Documentation that these functions *exist*, but there is nothing explaining what they do, or how they can be used (e.g., in this rather clever way to set and clear a flag in the high bits of the nsec field).Quoted 8 lines
I know that it's best to report missing documentation in the form of a patch, but I fear I don't have the time or the expertise to really do this topic justice, so I'd just thought I'd just note this lack for now, and maybe in my copious spare time I'll try to get at least something that will no doubt contain errors, but might inspire some folks to correct the text. (Or maybe on someone on linux-doc will feel inspired and get there ahead of me. :-)
Quoted 3 lines
Paul already started the work:
https://lore.kernel.org/lkml/19135936-06d7-4705-8bc8-bb31c2a478ca@paulmck-laptop/
Which reminds me, I need to forward-port that documentation to Mark's latest version. ;-)
Thanx, Paul
Re: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 40 lines · Paul E. McKenney
Quoted 39 lines · Boqun Feng
[Cc Paul and Mark]
Quoted 35 lines · Theodore Ts'o
Quoted 4 lines · Jeff Layton
Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.TIL.... that
atomic_long_fetch_or()andatomic_long_fetch_andnot()exist. :-)When I went looking for documentation about why they do or this particular usage pattern found in the patch, I didn't find any --- at least, certainly not in the Documentation in the kernel sources. The closest that I fond was this LWN article written by Neil Brown from 2016:
https://lwn.net/Articles/698315/... and this only covered the use
atomic_fetch_or(); I wasn't able to find anything discussingatomic_fetch_andnot().It looks like Peter Zijlstra added some bare-bones documentation in 2017, in commit:
706eeb3e9c6f("Documentation/locking/atomic: Add documents for newatomic_tAPIs") so we do have Documentation that these functions *exist*, but there is nothing explaining what they do, or how they can be used (e.g., in this rather clever way to set and clear a flag in the high bits of the nsec field).I know that it's best to report missing documentation in the form of a patch, but I fear I don't have the time or the expertise to really do this topic justice, so I'd just thought I'd just note this lack for now, and maybe in my copious spare time I'll try to get at least something that will no doubt contain errors, but might inspire some folks to correct the text. (Or maybe on someone on linux-doc will feel inspired and get there ahead of me. :-)
Quoted 6 lines
Quoted 3 lines
Paul already started the work:
https://lore.kernel.org/lkml/19135936-06d7-4705-8bc8-bb31c2a478ca@paulmck-laptop/Which reminds me, I need to forward-port that documentation to Mark's latest version. ;-)
Except that it appears that Mark beat me to it. ;-)
Thanx, Paul
Jan Kara
reviewerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
In reply to Jeff LaytonQuoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.
Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>
So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should rename i_ctime to something like __i_ctime and always use accessor function for it.
2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in current_time() relies on the fact that filesystems always do an equivalent of:
inode->i_mtime = inode->i_ctime = current_time();
Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only inode->i_mtime is set from current_time() e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renaming i_ctime to __i_ctime and using a function to set ctime. Mtime could then be updated with inode->i_mtime = ctime_peek().
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
Some more nits below.
Quoted 8 lines
+/**
+ *current_mg_time- Return FS time (possibly fine-grained)
+ * @inode: inode.
+ *
+ * Return the current time truncated to the time granularity supported by
+ * the fs, as suitable for a ctime/mtime change. If the ctime is flagged
+ * as having been QUERIED, get a fine-grained timestamp.
+ */
The comment should also mention that QUERIED flag is cleared from the ctime.
Quoted 26 lines
fs/inode.c +26
-0+static struct timespec64 current_mg_time (struct inode *inode) +{ +struct timespec64 now; +atomic_long_t *pnsec = (atomic_long_t *)&inode->i_ctime.tv_nsec; +long nsec =atomic_long_fetch_andnot (I_CTIME_QUERIED , pnsec); + +if (nsec &I_CTIME_QUERIED ) { +ktime_get_real_ts64 (&now); + }else { +struct timespec64 ctime; + +ktime_get_coarse_real_ts64 (&now); + +/* +* If we've recently fetched a fine-grained timestamp +* then the coarse-grained one may still be earlier than the +* existing one. Just keep the existing ctime if so. +*/ + ctime =ctime_peek (inode); +if (timespec64_compare (&ctime, &now) >0 ) + now = ctime; + } + +return now; +} +
...
Quoted 6 lines
include/linux/fs.h +6
-0+/** +* ctime_nsec_peek - peek at (but don't query) the ctime tv_nsec field +* @inode: inode to fetch the ctime from +* +* Grab the current ctime tv_nsec field from the inode, mask off the +* I_CTIME_QUERIED flag and return it. This is mostly intended for use by
include/linux/fs.h struct file_system_type { · line 2276
Quoted 10 lines
include/linux/fs.h +10
-0+* internal consumers of the ctime that aren't concerned with ensuring a +* fine-grained update on the next change (e.g. when preparing to store +* the value in the backing store for later retrieval). +* +* This is safe to call regardless of whether the underlying filesystem +* is using multigrain timestamps. +*/ +static inline long ctime_nsec_peek (const struct inode *inode) +{ +return inode->i_ctime.tv_nsec &~I_CTIME_QUERIED ;
This is somewhat unusual spacing. I'd use:
inode->i_ctime.tv_nsec & ~I_CTIME_QUERIED
Quoted 12 lines
include/linux/fs.h +12
-0+} + +/** +* ctime_peek - peek at (but don't query) the ctime +* @inode: inode to fetch the ctime from +* +* Grab the current ctime from the inode, sans I_CTIME_QUERIED flag. For +* use by internal consumers that don't require a fine-grained update on +* the next change. +* +* This is safe to call regardless of whether the underlying filesystem +* is using multigrain timestamps.
include/linux/fs.h struct file_system_type { · line 2298
Quoted 10 lines
include/linux/fs.h +10
-0+*/ +static inline struct timespec64 ctime_peek (const struct inode *inode) +{ +struct timespec64 ctime; + + ctime.tv_sec = inode->i_ctime.tv_sec; + ctime.tv_nsec =ctime_nsec_peek (inode); + +return ctime; +}
Given this is in a header that gets included in a lot of places, maybe we should call it like inode_ctime_peek() or inode_ctime_get() to reduce chances of a name clash?
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jan Kara
reviewerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 59 lines · Jan Kara
Quoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in
current_time()relies on the fact that filesystems always do an equivalent of:inode->i_mtime = inode->i_ctime = current_time();Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only
inode->i_mtimeis set fromcurrent_time()e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renamingi_ctimeto__i_ctimeand using a function to set ctime. Mtime could then be updated withinode->i_mtime=ctime_peek().
Quoted 3 lines
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
Also as I'm thinking about it your current scheme is slightly racy. Suppose the filesystem does:
CPU1 CPU2
statx()
inode->i_ctime = current_time()
current_mg_time()
nsec = atomic_long_fetch_andnot(QUERIED, &inode->i_ctime.tv_nsec)
nsec = atomic_long_fetch_or(QUERIED, &inode->i_ctime.tv_nsec)
if (nsec & QUERIED) - not set
ktime_get_coarse_real_ts64(&now)
return timestamp_truncate(now, inode);- QUERIED flag in the inode->i_ctime gets overwritten by the assignment => we need not update ctime due to granularity although it was queried
One more reason to use explicit function to update inode->i_ctime ;)
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jeff Layton
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 77 lines · Jan Kara
Quoted 59 lines · Jan Kara
Quoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in
current_time()relies on the fact that filesystems always do an equivalent of:inode->i_mtime = inode->i_ctime = current_time();Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only
inode->i_mtimeis set fromcurrent_time()e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renamingi_ctimeto__i_ctimeand using a function to set ctime. Mtime could then be updated withinode->i_mtime=ctime_peek().Quoted 3 lines
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
Also as I'm thinking about it your current scheme is slightly racy. Suppose the filesystem does:
CPU1 CPU2
statx() inode->i_ctime = current_time() current_mg_time() nsec = atomic_long_fetch_andnot(QUERIED, &inode->i_ctime.tv_nsec) nsec = atomic_long_fetch_or(QUERIED, &inode->i_ctime.tv_nsec) if (nsec & QUERIED) - not set ktime_get_coarse_real_ts64(&now) return timestamp_truncate(now, inode);
Quoted 4 lines
- QUERIED flag in the
inode->i_ctimegets overwritten by the assignment => we need not update ctime due to granularity although it was queriedOne more reason to use explicit function to update
inode->i_ctime;)
When we store the new time in the i_ctime field, the flag gets cleared because at that point we're storing a new (unseen) time.
However, you're correct: if the i_ctime in your above example starts at the same value that is currently being returned by ktime_get_coarse_real_ts64, then we'll lose the flag set in statx.
I think the right fix there would be to not update the ctime at all if it's a coarse grained time, and the value wouldn't have an apparent change to an observer. That would leave the flag intact.
That does mean we'd need to move to a function that does clock fetch and assigns it to i_ctime in one go (like you suggest). Something like:
inode_update_ctime(inode);
How we do that with atomic operations over two values (the tv_sec and tv_nsec) is a bit tricky. I'll have to think about it.
Christian, given Jan's concerns do you want to drop this series for now and let me respin it?
Thanks,
Signature · 1 line
Jeff Layton <[email protected]>
Christian Brauner
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 97 lines · Jeff Layton
Quoted 77 lines · Jan Kara
Quoted 59 lines · Jan Kara
Quoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in
current_time()relies on the fact that filesystems always do an equivalent of:inode->i_mtime = inode->i_ctime = current_time();Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only
inode->i_mtimeis set fromcurrent_time()e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renamingi_ctimeto__i_ctimeand using a function to set ctime. Mtime could then be updated withinode->i_mtime=ctime_peek().Quoted 3 lines
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
Also as I'm thinking about it your current scheme is slightly racy. Suppose the filesystem does:
CPU1 CPU2
statx() inode->i_ctime = current_time() current_mg_time() nsec = atomic_long_fetch_andnot(QUERIED, &inode->i_ctime.tv_nsec) nsec = atomic_long_fetch_or(QUERIED, &inode->i_ctime.tv_nsec) if (nsec & QUERIED) - not set ktime_get_coarse_real_ts64(&now) return timestamp_truncate(now, inode);Quoted 4 lines
- QUERIED flag in the
inode->i_ctimegets overwritten by the assignment => we need not update ctime due to granularity although it was queriedOne more reason to use explicit function to update
inode->i_ctime;)When we store the new time in the
i_ctimefield, the flag gets cleared because at that point we're storing a new (unseen) time.However, you're correct: if the
i_ctimein your above example starts at the same value that is currently being returned byktime_get_coarse_real_ts64, then we'll lose the flag set in statx.I think the right fix there would be to not update the ctime at all if it's a coarse grained time, and the value wouldn't have an apparent change to an observer. That would leave the flag intact.
That does mean we'd need to move to a function that does clock fetch and assigns it to
i_ctimein one go (like you suggest). Something like:
Quoted 7 lines
inode_update_ctime(inode);How we do that with atomic operations over two values (the
tv_secandtv_nsec) is a bit tricky. I'll have to think about it.Christian, given Jan's concerns do you want to drop this series for now and let me respin it?
I deliberately put it into a vfs.unstable.* branch. I would leave it there until you send a new one then drop it. If we get lucky the bots that run on -next will have time to report potential perf issues while it's not currently causing conflicts.
Jeff Layton
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 103 lines · Christian Brauner
Quoted 102 lines · Jeff Layton
Quoted 77 lines · Jan Kara
Quoted 59 lines · Jan Kara
Quoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in
current_time()relies on the fact that filesystems always do an equivalent of:inode->i_mtime = inode->i_ctime = current_time();Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only
inode->i_mtimeis set fromcurrent_time()e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renamingi_ctimeto__i_ctimeand using a function to set ctime. Mtime could then be updated withinode->i_mtime=ctime_peek().Quoted 3 lines
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
Also as I'm thinking about it your current scheme is slightly racy. Suppose the filesystem does:
CPU1 CPU2
statx() inode->i_ctime = current_time() current_mg_time() nsec = atomic_long_fetch_andnot(QUERIED, &inode->i_ctime.tv_nsec) nsec = atomic_long_fetch_or(QUERIED, &inode->i_ctime.tv_nsec) if (nsec & QUERIED) - not set ktime_get_coarse_real_ts64(&now) return timestamp_truncate(now, inode);Quoted 4 lines
- QUERIED flag in the
inode->i_ctimegets overwritten by the assignment => we need not update ctime due to granularity although it was queriedOne more reason to use explicit function to update
inode->i_ctime;)When we store the new time in the
i_ctimefield, the flag gets cleared because at that point we're storing a new (unseen) time.However, you're correct: if the
i_ctimein your above example starts at the same value that is currently being returned byktime_get_coarse_real_ts64, then we'll lose the flag set in statx.I think the right fix there would be to not update the ctime at all if it's a coarse grained time, and the value wouldn't have an apparent change to an observer. That would leave the flag intact.
That does mean we'd need to move to a function that does clock fetch and assigns it to
i_ctimein one go (like you suggest). Something like:inode_update_ctime(inode);How we do that with atomic operations over two values (the
tv_secandtv_nsec) is a bit tricky. I'll have to think about it.
Quoted 7 lines
Quoted 2 lines
Christian, given Jan's concerns do you want to drop this series for now and let me respin it?
I deliberately put it into a vfs.unstable.* branch. I would leave it there until you send a new one then drop it. If we get lucky the bots that run on -next will have time to report potential perf issues while it's not currently causing conflicts.
Sounds good to me. Thanks!
Signature · 1 line
Jeff Layton <[email protected]>
Jeff Layton
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
In reply to Jan KaraQuoted 77 lines · Jan Kara
Quoted 59 lines · Jan Kara
Quoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in
current_time()relies on the fact that filesystems always do an equivalent of:inode->i_mtime = inode->i_ctime = current_time();Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only
inode->i_mtimeis set fromcurrent_time()e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renamingi_ctimeto__i_ctimeand using a function to set ctime. Mtime could then be updated withinode->i_mtime=ctime_peek().Quoted 3 lines
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
Also as I'm thinking about it your current scheme is slightly racy. Suppose the filesystem does:
CPU1 CPU2
statx() inode->i_ctime = current_time() current_mg_time() nsec = atomic_long_fetch_andnot(QUERIED, &inode->i_ctime.tv_nsec) nsec = atomic_long_fetch_or(QUERIED, &inode->i_ctime.tv_nsec) if (nsec & QUERIED) - not set ktime_get_coarse_real_ts64(&now) return timestamp_truncate(now, inode);
Quoted 4 lines
- QUERIED flag in the
inode->i_ctimegets overwritten by the assignment => we need not update ctime due to granularity although it was queriedOne more reason to use explicit function to update
inode->i_ctime;)
Thinking about this some more, I think we can fix the race you pointed out by just not clearing the queried flag when we fetch the i_ctime.tv_nsec field when we're updating.
So, instead of atomic_long_fetch_andnot, we'd just want to use an atomic_long_fetch there, and just let the eventual assignment of inode->__i_ctime.tv_nsec be what clears the flag.
Any task that goes to update the time during the interim window will fetch a fine-grained time, but that's what we want anyway.
Since you bring up races though, there are a couple of other things we should be aware of. Note that both problems exist today too:
1) it's possible for two tasks to race in such a way that the ctime goes backward. There's no synchronization between tasks doing the updating, so an older time can overwrite a newer one. I think you'd need a pretty tight race to observe this though.
2) it's possible to fetch a "torn" timestamp out of the inode. timespec64 is two words, and we don't order its loads or stores. We could consider adding a seqcount_t and some barriers and fixing it that way. I'm not sure it's worth it though, given that we haven't worried about this in the past.
For now, I propose that we ignore both problems, unless and until we can prove that they are more than theoretical.
Signature · 1 line
Jeff Layton <[email protected]>
Christian Brauner
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
In reply to Jan KaraQuoted 59 lines · Jan Kara
Quoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in
current_time()relies on the fact that filesystems always do an equivalent of:inode->i_mtime = inode->i_ctime = current_time();Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only
inode->i_mtimeis set fromcurrent_time()e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renamingi_ctimeto__i_ctimeand using a function to set ctime. Mtime could then be updated withinode->i_mtime=ctime_peek().
Quoted 3 lines
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
Yeah, these are all good points.
Quoted 82 lines
Some more nits below.
Quoted 8 lines
+/**
+ *current_mg_time- Return FS time (possibly fine-grained)
+ * @inode: inode.
+ *
+ * Return the current time truncated to the time granularity supported by
+ * the fs, as suitable for a ctime/mtime change. If the ctime is flagged
+ * as having been QUERIED, get a fine-grained timestamp.
+ */The comment should also mention that QUERIED flag is cleared from the ctime.
Quoted 26 lines
fs/inode.c +26
-0+static struct timespec64 current_mg_time (struct inode *inode) +{ +struct timespec64 now; +atomic_long_t *pnsec = (atomic_long_t *)&inode->i_ctime.tv_nsec; +long nsec =atomic_long_fetch_andnot (I_CTIME_QUERIED , pnsec); + +if (nsec &I_CTIME_QUERIED ) { +ktime_get_real_ts64 (&now); + }else { +struct timespec64 ctime; + +ktime_get_coarse_real_ts64 (&now); + +/* +* If we've recently fetched a fine-grained timestamp +* then the coarse-grained one may still be earlier than the +* existing one. Just keep the existing ctime if so. +*/ + ctime =ctime_peek (inode); +if (timespec64_compare (&ctime, &now) >0 ) + now = ctime; + } + +return now; +} +...
Quoted 6 lines
include/linux/fs.h +6
-0+/** +* ctime_nsec_peek - peek at (but don't query) the ctime tv_nsec field +* @inode: inode to fetch the ctime from +* +* Grab the current ctime tv_nsec field from the inode, mask off the +* I_CTIME_QUERIED flag and return it. This is mostly intended for use by include/linux/fs.h struct file_system_type { · line 2276
Quoted 10 lines
include/linux/fs.h +10
-0+* internal consumers of the ctime that aren't concerned with ensuring a +* fine-grained update on the next change (e.g. when preparing to store +* the value in the backing store for later retrieval). +* +* This is safe to call regardless of whether the underlying filesystem +* is using multigrain timestamps. +*/ +static inline long ctime_nsec_peek (const struct inode *inode) +{ +return inode->i_ctime.tv_nsec &~I_CTIME_QUERIED ;This is somewhat unusual spacing. I'd use:
inode->i_ctime.tv_nsec & ~I_CTIME_QUERIEDQuoted 18 lines
include/linux/fs.h +18
-0+} + +/** +* ctime_peek - peek at (but don't query) the ctime +* @inode: inode to fetch the ctime from +* +* Grab the current ctime from the inode, sans I_CTIME_QUERIED flag. For +* use by internal consumers that don't require a fine-grained update on +* the next change. +* +* This is safe to call regardless of whether the underlying filesystem +* is using multigrain timestamps. +*/ +static inline struct timespec64 ctime_peek (const struct inode *inode) +{ +struct timespec64 ctime; + + ctime.tv_sec = inode->i_ctime.tv_sec;
Quoted 8 lines
include/linux/fs.h struct file_system_type { · line 2298
Quoted 4 lines
include/linux/fs.h +4
-0+ ctime.tv_nsec =ctime_nsec_peek (inode); + +return ctime; +}Given this is in a header that gets included in a lot of places, maybe we should call it like
inode_ctime_peek()orinode_ctime_get()to reduce chances of a name clash?
I think I mentioned this in an earlier comment. Independent of this series, it would be kinda nice if we could start moving stuff out of fs.h so we end up with a finer grained split of fs.h.
Jeff Layton
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
In reply to Jan KaraQuoted 35 lines · Jan Kara
Quoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>
Quoted 9 lines
So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.
We could do this, but it'll be quite invasive. We'd have to change any place that touches i_ctime (and there are a lot of them), even on filesystems that are not being converted.
Quoted 15 lines
2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in
current_time()relies on the fact that filesystems always do an equivalent of:inode->i_mtime = inode->i_ctime = current_time();Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only
inode->i_mtimeis set fromcurrent_time()e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renamingi_ctimeto__i_ctimeand using a function to set ctime. Mtime could then be updated withinode->i_mtime=ctime_peek().
Quoted 3 lines
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
AFAICT, under POSIX, you must _always_ set the ctime when you set the mtime, but the reverse is not true. That's why keeping the flag in the ctime makes sense. If we're updating the mtime, then we necessarily must update the ctime.
Quoted 11 lines
Some more nits below.
Quoted 8 lines
+/**
+ *current_mg_time- Return FS time (possibly fine-grained)
+ * @inode: inode.
+ *
+ * Return the current time truncated to the time granularity supported by
+ * the fs, as suitable for a ctime/mtime change. If the ctime is flagged
+ * as having been QUERIED, get a fine-grained timestamp.
+ */
Quoted 2 lines
The comment should also mention that QUERIED flag is cleared from the ctime.
Fair point. I can fix that up.
Quoted 48 lines
Quoted 26 lines
fs/inode.c +26
-0+static struct timespec64 current_mg_time (struct inode *inode) +{ +struct timespec64 now; +atomic_long_t *pnsec = (atomic_long_t *)&inode->i_ctime.tv_nsec; +long nsec =atomic_long_fetch_andnot (I_CTIME_QUERIED , pnsec); + +if (nsec &I_CTIME_QUERIED ) { +ktime_get_real_ts64 (&now); + }else { +struct timespec64 ctime; + +ktime_get_coarse_real_ts64 (&now); + +/* +* If we've recently fetched a fine-grained timestamp +* then the coarse-grained one may still be earlier than the +* existing one. Just keep the existing ctime if so. +*/ + ctime =ctime_peek (inode); +if (timespec64_compare (&ctime, &now) >0 ) + now = ctime; + } + +return now; +} +...
Quoted 6 lines
include/linux/fs.h +6
-0+/** +* ctime_nsec_peek - peek at (but don't query) the ctime tv_nsec field +* @inode: inode to fetch the ctime from +* +* Grab the current ctime tv_nsec field from the inode, mask off the +* I_CTIME_QUERIED flag and return it. This is mostly intended for use by include/linux/fs.h struct file_system_type { · line 2276
Quoted 10 lines
include/linux/fs.h +10
-0+* internal consumers of the ctime that aren't concerned with ensuring a +* fine-grained update on the next change (e.g. when preparing to store +* the value in the backing store for later retrieval). +* +* This is safe to call regardless of whether the underlying filesystem +* is using multigrain timestamps. +*/ +static inline long ctime_nsec_peek (const struct inode *inode) +{ +return inode->i_ctime.tv_nsec &~I_CTIME_QUERIED ;This is somewhat unusual spacing. I'd use:
Quoted 2 lines
inode->i_ctime.tv_nsec & ~I_CTIME_QUERIED
Yeah, I don't know what happened there. I'll fix that up.
Quoted 18 lines
Quoted 18 lines
include/linux/fs.h +18
-0+} + +/** +* ctime_peek - peek at (but don't query) the ctime +* @inode: inode to fetch the ctime from +* +* Grab the current ctime from the inode, sans I_CTIME_QUERIED flag. For +* use by internal consumers that don't require a fine-grained update on +* the next change. +* +* This is safe to call regardless of whether the underlying filesystem +* is using multigrain timestamps. +*/ +static inline struct timespec64 ctime_peek (const struct inode *inode) +{ +struct timespec64 ctime; + + ctime.tv_sec = inode->i_ctime.tv_sec;
Quoted 8 lines
include/linux/fs.h struct file_system_type { · line 2298
Quoted 4 lines
include/linux/fs.h +4
-0+ ctime.tv_nsec =ctime_nsec_peek (inode); + +return ctime; +}Given this is in a header that gets included in a lot of places, maybe we should call it like
inode_ctime_peek()orinode_ctime_get()to reduce chances of a name clash?
I'd be fine with that, but "ctime" sort of implies inode->i_ctime to me. We don't really use that nomenclature elsewhere.
Signature · 1 line
Jeff Layton <[email protected]>
Jan Kara
reviewerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 46 lines · Jeff Layton
Quoted 35 lines · Jan Kara
Quoted 27 lines · Jeff Layton
The VFS always uses coarse-grained timestamp updates for filling out the ctime and mtime after a change. This has the benefit of allowing filesystems to optimize away a lot metadata updates, down to around 1 per jiffy, even when a file is under heavy writes.
Unfortunately, this has always been an issue when we're exporting via NFSv3, which relies on timestamps to validate caches. Even with NFSv4, a lot of exported filesystems don't properly support a change attribute and are subject to the same problems with timestamp granularity. Other applications have similar issues (e.g backup applications).
Switching to always using fine-grained timestamps would improve the situation, but that becomes rather expensive, as the underlying filesystem will have to log a lot more metadata updates.
What we need is a way to only use fine-grained timestamps when they are being actively queried.
The kernel always stores normalized ctime values, so only the first 30 bits of the
tv_nsecfield are ever used. Whenever the mtime changes, the ctime must also change.Use the 31st bit of the ctime
tv_nsecfield to indicate that something has queried the inode for thei_mtimeori_ctime. When this flag is set, on the next timestamp update, the kernel can fetch a fine-grained timestamp instead of the usual coarse-grained one.Quoted 6 lines
This patch adds the infrastructure this scheme. Filesytems can opt into it by setting the
FS_MULTIGRAIN_TSflag in the fstype.Later patches will convert individual filesystems over to use it.
Signed-off-by Jeff Layton <[email protected]>Quoted 9 lines
So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.
Quoted 3 lines
We could do this, but it'll be quite invasive. We'd have to change any place that touches
i_ctime(and there are a lot of them), even on filesystems that are not being converted.
Yes, that's why I suggested Coccinelle to deal with this.
Quoted 15 lines
Quoted 15 lines
2) As I already commented in a previous version of the series, the scheme with just one flag for both ctime and mtime and flag getting cleared in
current_time()relies on the fact that filesystems always do an equivalent of:inode->i_mtime = inode->i_ctime = current_time();Otherwise we can do coarse grained update where we should have done a fine grained one. Filesystems often update timestamps like this but not universally. Grepping shows some instances where only
inode->i_mtimeis set fromcurrent_time()e.g. in autofs or bfs. Again a mistake that is rather easy to make and results in subtle issues. I think this would be also nicely solved by renamingi_ctimeto__i_ctimeand using a function to set ctime. Mtime could then be updated withinode->i_mtime=ctime_peek().
Quoted 8 lines
Quoted 3 lines
I understand this is quite some churn but a very mechanical one that could be just done with Coccinelle and a few manual fixups. So IMHO it is worth the more robust result.
AFAICT, under POSIX, you must _always_ set the ctime when you set the mtime, but the reverse is not true. That's why keeping the flag in the ctime makes sense. If we're updating the mtime, then we necessarily must update the ctime.
Yes, but technically speaking:
inode->i_mtime = current_time(); inode->i_ctime = current_time();
follows these requirements but is broken with your changes in unobvious way. And if mtime update is hidden in some function or condition, it is not even that suboptimal coding pattern.
Quoted 27 lines
Quoted 18 lines
Quoted 18 lines
include/linux/fs.h +18
-0+} + +/** +* ctime_peek - peek at (but don't query) the ctime +* @inode: inode to fetch the ctime from +* +* Grab the current ctime from the inode, sans I_CTIME_QUERIED flag. For +* use by internal consumers that don't require a fine-grained update on +* the next change. +* +* This is safe to call regardless of whether the underlying filesystem +* is using multigrain timestamps. +*/ +static inline struct timespec64 ctime_peek (const struct inode *inode) +{ +struct timespec64 ctime; + + ctime.tv_sec = inode->i_ctime.tv_sec;Quoted 8 lines
include/linux/fs.h struct file_system_type { · line 2298
Quoted 4 lines
include/linux/fs.h +4
-0+ ctime.tv_nsec =ctime_nsec_peek (inode); + +return ctime; +}Given this is in a header that gets included in a lot of places, maybe we should call it like
inode_ctime_peek()orinode_ctime_get()to reduce chances of a name clash?
Quoted 2 lines
I'd be fine with that, but "ctime" sort of implies
inode->i_ctimeto me. We don't really use that nomenclature elsewhere.
Yes, I don't insist here but we do have 'ctime' e.g. in IPC code (sem_ctime, shm_ctime, msg_ctime), we have ctime in md layer, ctime(3) is also a glibc function. So it is not *completely* impossible the clash happens but we can deal with it when it happens.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jeff Layton
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 13 lines · Jan Kara
Quoted 12 lines · Jeff Layton
Quoted 10 lines · Jan Kara
So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.
Quoted 5 lines
Quoted 3 lines
We could do this, but it'll be quite invasive. We'd have to change any place that touches
i_ctime(and there are a lot of them), even on filesystems that are not being converted.Yes, that's why I suggested Coccinelle to deal with this.
I've done the work to convert all of the accesses of i_ctime into accessor functions in the kernel. The current state of it is here:
https://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux.git/commit/?h=ctime
As expected, it touches a lot of code, all over the place. So far I have most of the conversion in one giant patch, and I need to split it up (probably per-subsystem).
What's the best way to feed this change into mainline? Should I try to get subsystem maintainers to pick these up, or are we better off feeding this in via a separate branch?
For reference, the diffstat for the big conversion patch is below:
arch/powerpc/platforms/cell/spufs/inode.c | 2 +- arch/s390/hypfs/inode.c | 4 +- drivers/android/binderfs.c | 8 ++-- drivers/infiniband/hw/qib/qib_fs.c | 4 +- drivers/misc/ibmasm/ibmasmfs.c | 2 +- drivers/misc/ibmvmc.c | 2 +- drivers/usb/core/devio.c | 16 ++++---- drivers/usb/gadget/function/f_fs.c | 6 +-- drivers/usb/gadget/legacy/inode.c | 3 +- fs/9p/vfs_inode.c | 6 ++- fs/9p/vfs_inode_dotl.c | 11 +++--- fs/adfs/inode.c | 4 +- fs/affs/amigaffs.c | 6 +-- fs/affs/inode.c | 17 ++++---- fs/afs/dynroot.c | 2 +- fs/afs/inode.c | 6 +-- fs/attr.c | 2 +- fs/autofs/inode.c | 2 +- fs/autofs/root.c | 6 +-- fs/bad_inode.c | 3 +- fs/befs/linuxvfs.c | 2 +- fs/bfs/dir.c | 16 ++++---- fs/bfs/inode.c | 6 +-- fs/binfmt_misc.c | 3 +- fs/btrfs/delayed-inode.c | 10 +++-- fs/btrfs/file.c | 24 ++++------- fs/btrfs/inode.c | 66 ++++++++++++------------ ------- fs/btrfs/ioctl.c | 2 +- fs/btrfs/reflink.c | 7 ++-- fs/btrfs/transaction.c | 3 +- fs/btrfs/tree-log.c | 4 +- fs/btrfs/xattr.c | 4 +- fs/ceph/acl.c | 2 +- fs/ceph/caps.c | 2 +- fs/ceph/inode.c | 17 ++++---- fs/ceph/snap.c | 2 +- fs/ceph/xattr.c | 2 +- fs/coda/coda_linux.c | 2 +- fs/coda/dir.c | 2 +- fs/coda/file.c | 2 +- fs/coda/inode.c | 2 +- fs/configfs/inode.c | 6 +-- fs/cramfs/inode.c | 2 +- fs/debugfs/inode.c | 2 +- fs/devpts/inode.c | 6 +-- fs/ecryptfs/inode.c | 2 +- fs/efivarfs/file.c | 2 +- fs/efivarfs/inode.c | 2 +- fs/efs/inode.c | 5 ++- fs/erofs/inode.c | 16 ++++---- fs/exfat/file.c | 4 +- fs/exfat/inode.c | 6 +-- fs/exfat/namei.c | 29 +++++++------- fs/exfat/super.c | 4 +- fs/ext2/acl.c | 2 +- fs/ext2/dir.c | 6 +-- fs/ext2/ialloc.c | 2 +- fs/ext2/inode.c | 11 +++--- fs/ext2/ioctl.c | 4 +- fs/ext2/namei.c | 8 ++-- fs/ext2/super.c | 2 +- fs/ext2/xattr.c | 2 +- fs/ext4/acl.c | 2 +- fs/ext4/ext4.h | 20 ++++++++++ fs/ext4/extents.c | 12 +++--- fs/ext4/ialloc.c | 2 +- fs/ext4/inline.c | 4 +- fs/ext4/inode.c | 16 ++++---- fs/ext4/ioctl.c | 9 +++-- fs/ext4/namei.c | 26 ++++++------ fs/ext4/super.c | 2 +- fs/ext4/xattr.c | 6 +-- fs/f2fs/dir.c | 8 ++-- fs/f2fs/f2fs.h | 5 ++- fs/f2fs/file.c | 16 ++++---- fs/f2fs/inline.c | 2 +- fs/f2fs/inode.c | 10 ++--- fs/f2fs/namei.c | 12 +++--- fs/f2fs/recovery.c | 4 +- fs/f2fs/super.c | 2 +- fs/f2fs/xattr.c | 2 +- fs/fat/inode.c | 8 ++-- fs/fat/misc.c | 7 +++- fs/freevxfs/vxfs_inode.c | 4 +- fs/fuse/control.c | 2 +- fs/fuse/dir.c | 8 ++-- fs/fuse/inode.c | 18 +++++---- fs/gfs2/acl.c | 2 +- fs/gfs2/bmap.c | 11 +++--- fs/gfs2/dir.c | 15 +++---- fs/gfs2/file.c | 2 +- fs/gfs2/glops.c | 4 +- fs/gfs2/inode.c | 8 ++-- fs/gfs2/super.c | 4 +- fs/gfs2/xattr.c | 8 ++-- fs/hfs/catalog.c | 8 ++-- fs/hfs/dir.c | 2 +- fs/hfs/inode.c | 13 +++--- fs/hfs/sysdep.c | 2 +- fs/hfsplus/catalog.c | 8 ++-- fs/hfsplus/dir.c | 6 +-- fs/hfsplus/inode.c | 14 +++---- fs/hostfs/hostfs_kern.c | 5 ++- fs/hpfs/dir.c | 8 ++-- fs/hpfs/inode.c | 6 +-- fs/hpfs/namei.c | 26 ++++++------ fs/hpfs/super.c | 5 ++- fs/hugetlbfs/inode.c | 12 +++--- fs/inode.c | 12 ++++-- fs/isofs/inode.c | 4 +- fs/isofs/rock.c | 16 ++++---- fs/jffs2/dir.c | 19 ++++----- fs/jffs2/file.c | 3 +- fs/jffs2/fs.c | 10 ++--- fs/jffs2/os-linux.h | 2 +- fs/jfs/acl.c | 2 +- fs/jfs/inode.c | 2 +- fs/jfs/ioctl.c | 2 +- fs/jfs/jfs_imap.c | 8 ++-- fs/jfs/jfs_inode.c | 4 +- fs/jfs/namei.c | 25 ++++++------ fs/jfs/super.c | 2 +- fs/jfs/xattr.c | 2 +- fs/kernfs/inode.c | 4 +- fs/libfs.c | 32 ++++++++------- fs/minix/bitmap.c | 2 +- fs/minix/dir.c | 6 +-- fs/minix/inode.c | 11 +++--- fs/minix/itree_common.c | 4 +- fs/minix/namei.c | 6 +-- fs/nfs/callback_proc.c | 2 +- fs/nfs/fscache.h | 4 +- fs/nfs/inode.c | 21 +++++----- fs/nfsd/nfsctl.c | 2 +- fs/nfsd/nfsfh.c | 4 +- fs/nfsd/vfs.c | 2 +- fs/nilfs2/dir.c | 6 +-- fs/nilfs2/inode.c | 12 +++--- fs/nilfs2/ioctl.c | 2 +- fs/nilfs2/namei.c | 8 ++-- fs/nsfs.c | 2 +- fs/ntfs/inode.c | 15 +++---- fs/ntfs/mft.c | 3 +- fs/ntfs3/file.c | 6 +-- fs/ntfs3/frecord.c | 4 +- fs/ntfs3/inode.c | 14 ++++--- fs/ntfs3/namei.c | 10 ++--- fs/ntfs3/xattr.c | 4 +- fs/ocfs2/acl.c | 6 +-- fs/ocfs2/alloc.c | 6 +-- fs/ocfs2/aops.c | 2 +- fs/ocfs2/dir.c | 8 ++-- fs/ocfs2/dlmfs/dlmfs.c | 4 +- fs/ocfs2/dlmglue.c | 10 +++-- fs/ocfs2/file.c | 16 ++++---- fs/ocfs2/inode.c | 14 ++++--- fs/ocfs2/move_extents.c | 6 +-- fs/ocfs2/namei.c | 22 ++++++----- fs/ocfs2/refcounttree.c | 14 +++---- fs/ocfs2/xattr.c | 6 +-- fs/omfs/dir.c | 4 +- fs/omfs/inode.c | 10 ++--- fs/openpromfs/inode.c | 4 +- fs/orangefs/namei.c | 2 +- fs/orangefs/orangefs-utils.c | 6 +-- fs/overlayfs/file.c | 7 +++- fs/overlayfs/util.c | 2 +- fs/pipe.c | 2 +- fs/posix_acl.c | 2 +- fs/proc/base.c | 2 +- fs/proc/inode.c | 2 +- fs/proc/proc_sysctl.c | 2 +- fs/proc/self.c | 2 +- fs/proc/thread_self.c | 2 +- fs/pstore/inode.c | 4 +- fs/qnx4/inode.c | 4 +- fs/qnx6/inode.c | 4 +- fs/ramfs/inode.c | 6 +-- fs/reiserfs/inode.c | 14 +++---- fs/reiserfs/ioctl.c | 4 +- fs/reiserfs/namei.c | 21 +++++----- fs/reiserfs/stree.c | 4 +- fs/reiserfs/super.c | 2 +- fs/reiserfs/xattr.c | 5 ++- fs/reiserfs/xattr_acl.c | 2 +- fs/romfs/super.c | 4 +- fs/smb/client/file.c | 4 +- fs/smb/client/fscache.h | 5 ++- fs/smb/client/inode.c | 15 ++++--- fs/smb/client/smb2ops.c | 2 +- fs/smb/server/smb2pdu.c | 8 ++-- fs/squashfs/inode.c | 2 +- fs/stack.c | 2 +- fs/stat.c | 2 +- fs/sysv/dir.c | 6 +-- fs/sysv/ialloc.c | 2 +- fs/sysv/inode.c | 6 +-- fs/sysv/itree.c | 4 +- fs/sysv/namei.c | 6 +-- fs/tracefs/inode.c | 2 +- fs/ubifs/debug.c | 4 +- fs/ubifs/dir.c | 39 +++++++++--------- fs/ubifs/file.c | 16 ++++---- fs/ubifs/ioctl.c | 2 +- fs/ubifs/journal.c | 4 +- fs/ubifs/super.c | 4 +- fs/ubifs/xattr.c | 6 +-- fs/udf/ialloc.c | 2 +- fs/udf/inode.c | 17 ++++---- fs/udf/namei.c | 24 +++++------ fs/ufs/dir.c | 6 +-- fs/ufs/ialloc.c | 2 +- fs/ufs/inode.c | 23 ++++++----- fs/ufs/namei.c | 8 ++-- fs/vboxsf/utils.c | 4 +- fs/xfs/libxfs/xfs_inode_buf.c | 4 +- fs/xfs/libxfs/xfs_trans_inode.c | 2 +- fs/xfs/xfs_acl.c | 2 +- fs/xfs/xfs_bmap_util.c | 6 ++- fs/xfs/xfs_inode.c | 2 +- fs/xfs/xfs_inode_item.c | 2 +- fs/xfs/xfs_iops.c | 4 +- fs/xfs/xfs_itable.c | 4 +- fs/zonefs/super.c | 8 ++-- include/linux/fs.h | 1 + include/linux/fs_stack.h | 2 +- ipc/mqueue.c | 20 +++++----- kernel/bpf/inode.c | 4 +- mm/shmem.c | 28 +++++++------ net/sunrpc/rpc_pipe.c | 2 +- security/apparmor/apparmorfs.c | 6 +-- security/apparmor/policy_unpack.c | 4 +- security/inode.c | 2 +- security/selinux/selinuxfs.c | 2 +- 234 files changed, 851 insertions(+), 808 deletions(-)
Signature · 1 line
Jeff Layton <[email protected]>
Christian Brauner
maintainerRe: [PATCH v4 2/9] fs: add infrastructure for multigrain inode i_m/ctime
Quoted 25 lines · Jeff Layton
Quoted 13 lines · Jan Kara
Quoted 12 lines · Jeff Layton
Quoted 10 lines · Jan Kara
So there are two things I dislike about this series because I think they are fragile:
1) If we have a filesystem supporting multigrain ts and someone accidentally directly uses the value of
inode->i_ctime, he can get bogus value (with QUERIED flag). This mistake is very easy to do. So I think we should renamei_ctimeto something like__i_ctimeand always use accessor function for it.Quoted 5 lines
Quoted 3 lines
We could do this, but it'll be quite invasive. We'd have to change any place that touches
i_ctime(and there are a lot of them), even on filesystems that are not being converted.Yes, that's why I suggested Coccinelle to deal with this.
I've done the work to convert all of the accesses of
i_ctimeinto accessor functions in the kernel. The current state of it is here:
Quoted 5 lines
https://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux.git/commit/?h=ctime
As expected, it touches a lot of code, all over the place. So far I have most of the conversion in one giant patch, and I need to split it up (probably per-subsystem).
Yeah, you have time since it'll be v6.6 material.
Quoted 4 lines
What's the best way to feed this change into mainline? Should I try to get subsystem maintainers to pick these up, or are we better off feeding this in via a separate branch?
I would prefer if we send them all through the vfs tree since trickle down conversions are otherwise very painful and potentially very slow.
Jeff Layton
maintainerPATCH v4 3/9 overlayfs: allow it to handle multigrain timestamps
In reply to Jeff LaytonEnsure that we strip off the I_CTIME_QUERIED bit when copying up.
--- fs/overlayfs/file.c | 7 +++++-- fs/overlayfs/util.c | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-)
fs/overlayfs/file.c +5 -2
@@ -222,6 +222,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)static void ovl_file_accessed (struct file *file) {struct inode *inode, *upperinode; +struct timespec64 ctime, uctime;if (file->f_flags &O_NOATIME )return ;
@@ -232,10 +233,12 @@ static void ovl_file_accessed(struct file *file)if (!upperinode)return ; + ctime =ctime_peek (inode); + uctime =ctime_peek (upperinode);if ((!timespec64_equal (&inode->i_mtime, &upperinode->i_mtime) || - !timespec64_equal (&inode->i_ctime, &upperinode->i_ctime))) { + !timespec64_equal (&ctime, &uctime))) { inode->i_mtime = upperinode->i_mtime; - inode->i_ctime = upperinode->i_ctime; + inode->i_ctime = uctime; }touch_atime (&file->f_path);
fs/overlayfs/util.c +1 -1
@@ -1117,6 +1117,6 @@ void ovl_copyattr(struct inode *inode) inode->i_mode = realinode->i_mode; inode->i_atime = realinode->i_atime; inode->i_mtime = realinode->i_mtime; - inode->i_ctime = realinode->i_ctime; + inode->i_ctime =ctime_peek (realinode);i_size_write (inode,i_size_read (realinode)); }
Signature · 1 line
2.40.1
Jeff Layton
maintainerPATCH v4 4/9 nfsd: ensure we use ctime_peek to grab the inode->i_ctime
In reply to Jeff LaytonIf getattr fails, then nfsd can end up scraping the time values directly out of the inode for pre and post-op attrs. This may or may not be the right thing to do, but for now make it at least use ctime_peek in this situation to ensure that the QUERIED flag is masked.
--- fs/nfsd/nfsfh.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
fs/nfsd/nfsfh.c +8 -3
@@ -624,9 +624,14 @@ void fh_fill_pre_attrs(struct svc_fh *fhp) inode =d_inode (fhp->fh_dentry); err =fh_getattr (fhp, &stat);if (err) { -/* Grab the times from inode anyway */ +/* +* Grab the times from inode anyway. +* +* FIXME: is this the right thing to do? Or should we just +* not send pre and post-op attrs in this case? +*/ stat.mtime = inode->i_mtime; - stat.ctime = inode->i_ctime; + stat.ctime =ctime_peek (inode); stat.size = inode->i_size;if (v4 &&IS_I_VERSION (inode)) { stat.change_cookie =inode_query_iversion (inode);
@@ -662,7 +667,7 @@ void fh_fill_post_attrs(struct svc_fh *fhp) err =fh_getattr (fhp, &fhp->fh_post_attr);if (err) { fhp->fh_post_saved = false; - fhp->fh_post_attr.ctime = inode->i_ctime; + fhp->fh_post_attr.ctime =ctime_peek (inode);if (v4 &&IS_I_VERSION (inode)) { fhp->fh_post_attr.change_cookie =inode_query_iversion (inode); fhp->fh_post_attr.result_mask |=STATX_CHANGE_COOKIE ;
Signature · 1 line
2.40.1
Re: [PATCH v4 4/9] nfsd: ensure we use ctime_peek to grab the inode->i_ctime
Quoted 8 lines · Jeff Layton
If getattr fails, then nfsd can end up scraping the time values directly out of the inode for pre and post-op attrs. This may or may not be the right thing to do, but for now make it at least use
ctime_peekin this situation to ensure that the QUERIED flag is masked.
That code comes from:
commit 39ca1bf624b6b82cc895b0217889eaaf572a7913
Author: Amir Goldstein <[email protected]> AuthorDate: Wed Jan 3 17:14:35 2018 +0200 Commit: J. Bruce Fields <[email protected]> CommitDate: Thu Feb 8 13:40:17 2018 -0500
nfsd: store stat times in fill_pre_wcc() instead of inode times
The time values in stat and inode may differ for overlayfs and stat time values are the correct ones to use. This is also consistent with the fact that fill_post_wcc() also stores stat time values.
This means introducing a stat call that could fail, where previously we were just copying values out of the inode. To be conservative about changing behavior, we fall back to copying values out of the inode in the error case. It might be better just to clear fh_pre_saved (though note the BUG_ON in set_change_info).
I was thinking it might have been added to handle odd corner cases around re-exporting NFS mounts, but that does not seem to be the case.
The fh_getattr() can fail for legitimate reasons -- like the file is in the middle of being deleted or renamed over -- I would think. This code should really deal with that by not adding pre-op attrs, since they are optional.
Quoted 38 lines
Signed-off-by Jeff Layton <[email protected]>---
fs/nfsd/nfsfh.c| 11 ++++++++---1 file changed, 8 insertions(+), 3 deletions(-)fs/nfsd/nfsfh.c +8
-3@@ -624,9 +624,14 @@ void fh_fill_pre_attrs(struct svc_fh *fhp) inode =d_inode (fhp->fh_dentry); err =fh_getattr (fhp, &stat);if (err) { -/* Grab the times from inode anyway */ +/* +* Grab the times from inode anyway. +* +* FIXME: is this the right thing to do? Or should we just +* not send pre and post-op attrs in this case? +*/ stat.mtime = inode->i_mtime; - stat.ctime = inode->i_ctime; + stat.ctime =ctime_peek (inode); stat.size = inode->i_size;if (v4 &&IS_I_VERSION (inode)) { stat.change_cookie =inode_query_iversion (inode);@@ -662,7 +667,7 @@ void fh_fill_post_attrs(struct svc_fh *fhp) err =fh_getattr (fhp, &fhp->fh_post_attr);if (err) { fhp->fh_post_saved = false; - fhp->fh_post_attr.ctime = inode->i_ctime; + fhp->fh_post_attr.ctime =ctime_peek (inode);if (v4 &&IS_I_VERSION (inode)) { fhp->fh_post_attr.change_cookie =inode_query_iversion (inode); fhp->fh_post_attr.result_mask |=STATX_CHANGE_COOKIE ;Signature · 1 line
2.40.1
--
Chuck Lever
Jeff Layton
maintainerRe: [PATCH v4 4/9] nfsd: ensure we use ctime_peek to grab the inode->i_ctime
Quoted 31 lines · Chuck Lever III
Quoted 7 lines · Jeff Layton
If getattr fails, then nfsd can end up scraping the time values directly out of the inode for pre and post-op attrs. This may or may not be the right thing to do, but for now make it at least use
ctime_peekin this situation to ensure that the QUERIED flag is masked.That code comes from:
commit
39ca1bf624b6b82cc895b0217889eaaf572a7913Author: Amir Goldstein <[email protected]> AuthorDate: Wed Jan 3 17:14:35 2018 +0200 Commit: J. Bruce Fields <[email protected]> CommitDate: Thu Feb 8 13:40:17 2018 -0500nfsd: store stat times in
fill_pre_wcc()instead of inode timesThe time values in stat and inode may differ for overlayfs and stat time values are the correct ones to use. This is also consistent with the fact that
fill_post_wcc()also stores stat time values.This means introducing a stat call that could fail, where previously we were just copying values out of the inode. To be conservative about changing behavior, we fall back to copying values out of the inode in the error case. It might be better just to clear
fh_pre_saved(though note theBUG_ONinset_change_info).Signed-off-by Amir Goldstein <[email protected]>Signed-off-by J. Bruce Fields <[email protected]>
Quoted 9 lines
I was thinking it might have been added to handle odd corner cases around re-exporting NFS mounts, but that does not seem to be the case.
The
fh_getattr()can fail for legitimate reasons -- like the file is in the middle of being deleted or renamed over -- I would think. This code should really deal with that by not adding pre-op attrs, since they are optional.
That sounds fine to me. I'll plan to drop this patch from the series and I'll send a separate patch to just remove those branches altogether (which should DTRT).
Signature · 1 line
Jeff Layton <[email protected]>
Christian Brauner
maintainerRe: [PATCH v4 4/9] nfsd: ensure we use ctime_peek to grab the inode->i_ctime
Quoted 42 lines · Jeff Layton
Quoted 31 lines · Chuck Lever III
Quoted 7 lines · Jeff Layton
If getattr fails, then nfsd can end up scraping the time values directly out of the inode for pre and post-op attrs. This may or may not be the right thing to do, but for now make it at least use
ctime_peekin this situation to ensure that the QUERIED flag is masked.That code comes from:
commit
39ca1bf624b6b82cc895b0217889eaaf572a7913Author: Amir Goldstein <[email protected]> AuthorDate: Wed Jan 3 17:14:35 2018 +0200 Commit: J. Bruce Fields <[email protected]> CommitDate: Thu Feb 8 13:40:17 2018 -0500nfsd: store stat times in
fill_pre_wcc()instead of inode timesThe time values in stat and inode may differ for overlayfs and stat time values are the correct ones to use. This is also consistent with the fact that
fill_post_wcc()also stores stat time values.This means introducing a stat call that could fail, where previously we were just copying values out of the inode. To be conservative about changing behavior, we fall back to copying values out of the inode in the error case. It might be better just to clear
fh_pre_saved(though note theBUG_ONinset_change_info).Signed-off-by Amir Goldstein <[email protected]>Signed-off-by J. Bruce Fields <[email protected]>Quoted 9 lines
I was thinking it might have been added to handle odd corner cases around re-exporting NFS mounts, but that does not seem to be the case.
The
fh_getattr()can fail for legitimate reasons -- like the file is in the middle of being deleted or renamed over -- I would think. This code should really deal with that by not adding pre-op attrs, since they are optional.
Quoted 3 lines
That sounds fine to me. I'll plan to drop this patch from the series and I'll send a separate patch to just remove those branches altogether (which should DTRT).
I'll wait with reviewing this until you send the next version then.
Jeff Layton
maintainerRe: [PATCH v4 4/9] nfsd: ensure we use ctime_peek to grab the inode->i_ctime
Quoted 43 lines · Christian Brauner
Quoted 42 lines · Jeff Layton
Quoted 40 lines · Chuck Lever III
Quoted 7 lines · Jeff Layton
If getattr fails, then nfsd can end up scraping the time values directly out of the inode for pre and post-op attrs. This may or may not be the right thing to do, but for now make it at least use
ctime_peekin this situation to ensure that the QUERIED flag is masked.That code comes from:
commit
39ca1bf624b6b82cc895b0217889eaaf572a7913Author: Amir Goldstein <[email protected]> AuthorDate: Wed Jan 3 17:14:35 2018 +0200 Commit: J. Bruce Fields <[email protected]> CommitDate: Thu Feb 8 13:40:17 2018 -0500nfsd: store stat times in
fill_pre_wcc()instead of inode timesThe time values in stat and inode may differ for overlayfs and stat time values are the correct ones to use. This is also consistent with the fact that
fill_post_wcc()also stores stat time values.This means introducing a stat call that could fail, where previously we were just copying values out of the inode. To be conservative about changing behavior, we fall back to copying values out of the inode in the error case. It might be better just to clear
fh_pre_saved(though note theBUG_ONinset_change_info).Signed-off-by Amir Goldstein <[email protected]>Signed-off-by J. Bruce Fields <[email protected]>I was thinking it might have been added to handle odd corner cases around re-exporting NFS mounts, but that does not seem to be the case.
The
fh_getattr()can fail for legitimate reasons -- like the file is in the middle of being deleted or renamed over -- I would think. This code should really deal with that by not adding pre-op attrs, since they are optional.
Quoted 5 lines
Quoted 3 lines
That sounds fine to me. I'll plan to drop this patch from the series and I'll send a separate patch to just remove those branches altogether (which should DTRT).
I'll wait with reviewing this until you send the next version then.
I don't have any other big changes queued up. So far, this would just be the exact same set, without this patch.
FWIW, I'm mostly interested in your review of patches #1 and 2. Is altering prototype for generic_fillattr, and changing the logic in current_time the right approach here?
Signature · 1 line
Jeff Layton <[email protected]>
Jeff Layton
maintainerPATCH v4 5/9 ksmbd: use ctime_peek to grab the ctime out of the inode
In reply to Jeff LaytonThis ensures that the flag is masked off in the result.
--- fs/ksmbd/smb2pdu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/ksmbd/smb2pdu.c +3 -3
@@ -4745,7 +4745,7 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, file_info->LastAccessTime =cpu_to_le64 (time); time =ksmbd_UnixTimeToNT (inode->i_mtime); file_info->LastWriteTime =cpu_to_le64 (time); - time =ksmbd_UnixTimeToNT (inode->i_ctime); + time =ksmbd_UnixTimeToNT (ctime_peek (inode)); file_info->ChangeTime =cpu_to_le64 (time); file_info->DosAttributes = fp->f_ci->m_fattr; file_info->Inode =cpu_to_le64 (inode->i_ino);
@@ -5386,7 +5386,7 @@ int smb2_close(struct ksmbd_work *work) rsp->LastAccessTime =cpu_to_le64 (time); time =ksmbd_UnixTimeToNT (inode->i_mtime); rsp->LastWriteTime =cpu_to_le64 (time); - time =ksmbd_UnixTimeToNT (inode->i_ctime); + time =ksmbd_UnixTimeToNT (ctime_peek (inode)); rsp->ChangeTime =cpu_to_le64 (time);ksmbd_fd_put (work, fp); }else {
@@ -5605,7 +5605,7 @@ static int set_file_basic_info(struct ksmbd_file *fp,if (file_info->ChangeTime) attrs.ia_ctime =ksmbd_NTtimeToUnix (file_info->ChangeTime);else - attrs.ia_ctime = inode->i_ctime; + attrs.ia_ctime =ctime_peek (inode);if (file_info->LastWriteTime) { attrs.ia_mtime =ksmbd_NTtimeToUnix (file_info->LastWriteTime);
Signature · 1 line
2.40.1
Jeff Layton
maintainerPATCH v4 6/9 tmpfs: add support for multigrain timestamps
In reply to Jeff Layton--- mm/shmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/shmem.c +1 -1
@@ -4085,7 +4085,7 @@ static struct file_system_type shmem_fs_type = {#endif .kill_sb = kill_litter_super,#ifdef CONFIG_SHMEM - .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP, + .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP | FS_MULTIGRAIN_TS, #else .fs_flags = FS_USERNS_MOUNT,#endif
Signature · 1 line
2.40.1
Jeff Layton
maintainerPATCH v4 7/9 xfs: switch to multigrain timestamps
In reply to Jeff Layton--- fs/xfs/libxfs/xfs_inode_buf.c | 2 +- fs/xfs/xfs_inode_item.c | 2 +- fs/xfs/xfs_iops.c | 4 ++-- fs/xfs/xfs_super.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-)
fs/xfs/libxfs/xfs_inode_buf.c +1 -1
@@ -316,7 +316,7 @@ xfs_inode_to_disk( to->di_atime =xfs_inode_to_disk_ts (ip, inode->i_atime); to->di_mtime =xfs_inode_to_disk_ts (ip, inode->i_mtime); - to->di_ctime =xfs_inode_to_disk_ts (ip, inode->i_ctime); + to->di_ctime =xfs_inode_to_disk_ts (ip,ctime_peek (inode)); to->di_nlink =cpu_to_be32 (inode->i_nlink); to->di_gen =cpu_to_be32 (inode->i_generation); to->di_mode =cpu_to_be16 (inode->i_mode);
fs/xfs/xfs_inode_item.c +1 -1
@@ -381,7 +381,7 @@ xfs_inode_to_log_dinode(memset (to->di_pad3,0 ,sizeof (to->di_pad3)); to->di_atime =xfs_inode_to_log_dinode_ts (ip, inode->i_atime); to->di_mtime =xfs_inode_to_log_dinode_ts (ip, inode->i_mtime); - to->di_ctime =xfs_inode_to_log_dinode_ts (ip, inode->i_ctime); + to->di_ctime =xfs_inode_to_log_dinode_ts (ip,ctime_peek (inode)); to->di_nlink = inode->i_nlink; to->di_gen = inode->i_generation; to->di_mode = inode->i_mode;
fs/xfs/xfs_iops.c +2 -2
@@ -573,10 +573,10 @@ xfs_vn_getattr( stat->gid =vfsgid_into_kgid (vfsgid); stat->ino = ip->i_ino; stat->atime = inode->i_atime; - stat->mtime = inode->i_mtime; - stat->ctime = inode->i_ctime; stat->blocks =XFS_FSB_TO_BB (mp, ip->i_nblocks + ip->i_delayed_blks); +fill_multigrain_cmtime (request_mask, inode, stat); +if (xfs_has_v3inodes (mp)) {if (request_mask &STATX_BTIME ) { stat->result_mask |=STATX_BTIME ;
fs/xfs/xfs_super.c +1 -1
@@ -1992,7 +1992,7 @@ static struct file_system_type xfs_fs_type = { .init_fs_context = xfs_init_fs_context, .parameters = xfs_fs_parameters, .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, + .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_MULTIGRAIN_TS, };MODULE_ALIAS_FS ("xfs" );
Signature · 1 line
2.40.1
Jeff Layton
maintainerPATCH v4 8/9 ext4: convert to multigrain timestamps
In reply to Jeff Layton--- fs/ext4/inode.c | 17 +++++++++++++++-- fs/ext4/super.c | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-)
fs/ext4/inode.c +15 -2
@@ -4235,6 +4235,19 @@ static int ext4_inode_blocks_set(struct ext4_inode *raw_inode,return 0 ; } +static void ext4_inode_set_ctime (struct inode *inode,struct ext4_inode *raw_inode) +{ +struct timespec64 ctime =ctime_peek (inode); + +if (EXT4_FITS_IN_INODE (raw_inode,EXT4_I (inode), i_ctime_extra)) { + raw_inode->i_ctime =cpu_to_le32 (ctime.tv_sec); + raw_inode->i_ctime_extra =ext4_encode_extra_time (&ctime); + }else { + raw_inode->i_ctime =cpu_to_le32 (clamp_t (int32_t, + ctime.tv_sec,S32_MIN ,S32_MAX )); + } +} +static int ext4_fill_raw_inode (struct inode *inode,struct ext4_inode *raw_inode) {struct ext4_inode_info *ei =EXT4_I (inode);
@@ -4275,7 +4288,7 @@ static int ext4_fill_raw_inode(struct inode *inode, struct ext4_inode *raw_inode } raw_inode->i_links_count =cpu_to_le16 (inode->i_nlink); -EXT4_INODE_SET_XTIME (i_ctime, inode, raw_inode); +ext4_inode_set_ctime (inode, raw_inode);EXT4_INODE_SET_XTIME (i_mtime, inode, raw_inode);EXT4_INODE_SET_XTIME (i_atime, inode, raw_inode);EXT4_EINODE_SET_XTIME (i_crtime, ei, raw_inode);
@@ -4983,7 +4996,7 @@ static void __ext4_update_other_inode_time(struct super_block *sb,spin_unlock (&inode->i_lock);spin_lock (&ei->i_raw_lock); -EXT4_INODE_SET_XTIME (i_ctime, inode, raw_inode); +ext4_inode_set_ctime (inode, raw_inode);EXT4_INODE_SET_XTIME (i_mtime, inode, raw_inode);EXT4_INODE_SET_XTIME (i_atime, inode, raw_inode);ext4_inode_csum_set (inode, raw_inode, ei);
fs/ext4/super.c +1 -1
@@ -7258,7 +7258,7 @@ static struct file_system_type ext4_fs_type = { .init_fs_context = ext4_init_fs_context, .parameters = ext4_param_specs, .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, + .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_MULTIGRAIN_TS, };MODULE_ALIAS_FS ("ext4" );
Signature · 1 line
2.40.1
Re: [PATCH v4 8/9] ext4: convert to multigrain timestamps
Jeff Layton
maintainerPATCH v4 9/9 btrfs: convert to multigrain timestamps
In reply to Jeff Layton--- fs/btrfs/delayed-inode.c | 2 +- fs/btrfs/inode.c | 2 +- fs/btrfs/super.c | 5 +++-- fs/btrfs/tree-log.c | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-)
fs/btrfs/delayed-inode.c +1 -1
@@ -1810,7 +1810,7 @@ static void fill_stack_inode_item(struct btrfs_trans_handle *trans,btrfs_set_stack_timespec_sec (&inode_item->ctime, inode->i_ctime.tv_sec);btrfs_set_stack_timespec_nsec (&inode_item->ctime, - inode->i_ctime.tv_nsec); +ctime_nsec_peek (inode));btrfs_set_stack_timespec_sec (&inode_item->otime,BTRFS_I (inode)->i_otime.tv_sec);
fs/btrfs/inode.c +1 -1
@@ -3970,7 +3970,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans,btrfs_set_token_timespec_sec (&token, &item->ctime, inode->i_ctime.tv_sec);btrfs_set_token_timespec_nsec (&token, &item->ctime, - inode->i_ctime.tv_nsec); +ctime_nsec_peek (inode));btrfs_set_token_timespec_sec (&token, &item->otime,BTRFS_I (inode)->i_otime.tv_sec);
fs/btrfs/super.c +3 -2
@@ -2144,7 +2144,7 @@ static struct file_system_type btrfs_fs_type = { .name = "btrfs", .mount = btrfs_mount, .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA, + .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | FS_MULTIGRAIN_TS, };static struct file_system_type btrfs_root_fs_type = {
@@ -2152,7 +2152,8 @@ static struct file_system_type btrfs_root_fs_type = { .name = "btrfs", .mount = btrfs_mount_root, .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | FS_ALLOW_IDMAP, + .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | + FS_ALLOW_IDMAP | FS_MULTIGRAIN_TS, };MODULE_ALIAS_FS ("btrfs" );
fs/btrfs/tree-log.c +1 -1
@@ -4150,7 +4150,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans,btrfs_set_token_timespec_sec (&token, &item->ctime, inode->i_ctime.tv_sec);btrfs_set_token_timespec_nsec (&token, &item->ctime, - inode->i_ctime.tv_nsec); +ctime_nsec_peek (inode));/* * We do not need to set the nbytes field, in fact during a fast fsync
Signature · 1 line
2.40.1
Re: [PATCH v4 9/9] btrfs: convert to multigrain timestamps
Quoted 1 line · Jeff Layton
Signed-off-by Jeff Layton <[email protected]>
Please add a brief description to the changelog too, there's a similar text in the patches adding the infrastructure. Something like "Allow to optimize lot of metadata updates by encoding the status in the cmtime. The fine grained time is needed for NFS."
Jeff Layton
maintainerRe: [PATCH v4 9/9] btrfs: convert to multigrain timestamps
Quoted 9 lines · David Sterba
Quoted 1 line · Jeff Layton
Signed-off-by Jeff Layton <[email protected]>Acked-by David Sterba <[email protected]>Please add a brief description to the changelog too, there's a similar text in the patches adding the infrastructure. Something like "Allow to optimize lot of metadata updates by encoding the status in the cmtime. The fine grained time is needed for NFS."
Sure thing.
Christian, do you want to just alter the changelog with David's suggestion, or would you rather I resend the series?
Thanks,
Signature · 1 line
Jeff Layton <[email protected]>
Christian Brauner
maintainerRe: [PATCH v4 9/9] btrfs: convert to multigrain timestamps
Quoted 11 lines · Jeff Layton
Quoted 9 lines · David Sterba
Quoted 1 line · Jeff Layton
Signed-off-by Jeff Layton <[email protected]>Acked-by David Sterba <[email protected]>Please add a brief description to the changelog too, there's a similar text in the patches adding the infrastructure. Something like "Allow to optimize lot of metadata updates by encoding the status in the cmtime. The fine grained time is needed for NFS."
Quoted 4 lines
Sure thing.
Christian, do you want to just alter the changelog with David's suggestion, or would you rather I resend the series?
Nah, don't bother resending I'll just add it to the fs specific patches. I'll end up updating the patch trailers anyway when individual maintainers add new Acks.
Quoted 9 lines · Jeff Layton
v4:
- addrequest_maskargument togeneric_fillattr
- Dropcurrent_ctimehelper and just code functionality intocurrent_time
- reworki_ctimeaccessor functionsA few weeks ago, during one of the discussions around
i_version, Dave Chinner wrote this:[...]
Let's get this into -next so we can see whether this leads to any performance or other regressions. It's moved to a vfs.unstable.* branch for now. If nothing bad happens it'll be upgraded to a vfs.* branch. Filesystems that prefer to carry their fs specific patch themselves can request a stable tag for the generic changes.
---
Applied to the vfs.unstable.ctime branch of the vfs/vfs.git tree. Patches in the vfs.unstable.ctime branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the patch has now been applied. If possible patch trailers will be updated.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git branch: vfs.unstable.ctime
[1/9] fs: pass the request_mask to generic_fillattr
https://git.kernel.org/vfs/vfs/c/ec1239bab5fd [2/9] fs: add infrastructure for multigrain inode i_m/ctime https://git.kernel.org/vfs/vfs/c/97e9fbb03240 [3/9] overlayfs: allow it to handle multigrain timestamps https://git.kernel.org/vfs/vfs/c/ada0fe43f748 [4/9] nfsd: ensure we use ctime_peek to grab the inode->i_ctime https://git.kernel.org/vfs/vfs/c/39493918b700 [5/9] ksmbd: use ctime_peek to grab the ctime out of the inode https://git.kernel.org/vfs/vfs/c/35527cdc7840 [6/9] tmpfs: add support for multigrain timestamps https://git.kernel.org/vfs/vfs/c/ce1dc211dbde [7/9] xfs: switch to multigrain timestamps https://git.kernel.org/vfs/vfs/c/78bbdfd2fb74 [8/9] ext4: convert to multigrain timestamps https://git.kernel.org/vfs/vfs/c/d2100ca52e14 [9/9] btrfs: convert to multigrain timestamps https://git.kernel.org/vfs/vfs/c/c725b40cfbd5