From: Darrick J. Wong <djwong@xxxxxxxxxx> Allow the fuse server to supply us with the more recently added fields of struct statx. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- fs/fuse/fuse_i.h | 8 +++++ include/uapi/linux/fuse.h | 15 +++++++++ fs/fuse/dir.c | 73 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 84 insertions(+), 12 deletions(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 362fa87241ac70..4ca29315b2a434 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1677,6 +1677,14 @@ void fuse_iomap_sysfs_cleanup(struct kobject *kobj); sector_t fuse_bmap(struct address_space *mapping, sector_t block); +#if IS_ENABLED(CONFIG_FUSE_IOMAP_DEBUG) +int fuse_iomap_sysfs_init(struct kobject *kobj); +void fuse_iomap_sysfs_cleanup(struct kobject *kobj); +#else +# define fuse_iomap_sysfs_init(...) (0) +# define fuse_iomap_sysfs_cleanup(...) ((void)0) +#endif + #if IS_ENABLED(CONFIG_FUSE_IOMAP) bool fuse_iomap_enabled(void); diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 1f8e3ba60e7ec5..cfeee8a280896a 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -335,7 +335,20 @@ struct fuse_statx { uint32_t rdev_minor; uint32_t dev_major; uint32_t dev_minor; - uint64_t __spare2[14]; + + uint64_t mnt_id; + uint32_t dio_mem_align; + uint32_t dio_offset_align; + uint64_t subvol; + + uint32_t atomic_write_unit_min; + uint32_t atomic_write_unit_max; + uint32_t atomic_write_segments_max; + uint32_t dio_read_offset_align; + uint32_t atomic_write_unit_max_opt; + uint32_t __spare2[1]; + + uint64_t __spare3[8]; }; struct fuse_kstatfs { diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 02c8e705af1e35..305b926b4a589a 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1276,6 +1276,48 @@ static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr) attr->blksize = sx->blksize; } +#define FUSE_SUPPORTED_STATX_MASK (STATX_BASIC_STATS | \ + STATX_BTIME | \ + STATX_DIOALIGN | \ + STATX_SUBVOL | \ + STATX_WRITE_ATOMIC) + +#define FUSE_UNCACHED_STATX_MASK (STATX_DIOALIGN | \ + STATX_SUBVOL | \ + STATX_WRITE_ATOMIC) + +static void kstat_from_fuse_statx(struct kstat *stat, + const struct fuse_statx *sx) +{ + stat->result_mask = sx->mask & FUSE_SUPPORTED_STATX_MASK; + + stat->attributes = sx->attributes; + stat->attributes_mask = sx->attributes_mask; + + if (sx->mask & STATX_BTIME) { + stat->btime.tv_sec = sx->btime.tv_sec; + stat->btime.tv_nsec = min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1); + } + + if (sx->mask & STATX_DIOALIGN) { + stat->dio_mem_align = sx->dio_mem_align; + stat->dio_offset_align = sx->dio_offset_align; + } + + if (sx->mask & STATX_SUBVOL) + stat->subvol = sx->subvol; + + if (sx->mask & STATX_WRITE_ATOMIC) { + stat->atomic_write_unit_min = sx->atomic_write_unit_min; + stat->atomic_write_unit_max = sx->atomic_write_unit_max; + stat->atomic_write_unit_max_opt = sx->atomic_write_unit_max_opt; + stat->atomic_write_segments_max = sx->atomic_write_segments_max; + } + + if (sx->mask & STATX_DIO_READ_ALIGN) + stat->dio_read_offset_align = sx->dio_read_offset_align; +} + static int fuse_do_statx(struct mnt_idmap *idmap, struct inode *inode, struct file *file, struct kstat *stat) { @@ -1299,7 +1341,7 @@ static int fuse_do_statx(struct mnt_idmap *idmap, struct inode *inode, } /* For now leave sync hints as the default, request all stats. */ inarg.sx_flags = 0; - inarg.sx_mask = STATX_BASIC_STATS | STATX_BTIME; + inarg.sx_mask = FUSE_SUPPORTED_STATX_MASK; args.opcode = FUSE_STATX; args.nodeid = get_node_id(inode); args.in_numargs = 1; @@ -1327,11 +1369,7 @@ static int fuse_do_statx(struct mnt_idmap *idmap, struct inode *inode, } if (stat) { - stat->result_mask = sx->mask & (STATX_BASIC_STATS | STATX_BTIME); - stat->btime.tv_sec = sx->btime.tv_sec; - stat->btime.tv_nsec = min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1); - stat->attributes = sx->attributes; - stat->attributes_mask = sx->attributes_mask; + kstat_from_fuse_statx(stat, sx); fuse_fillattr(idmap, inode, &attr, stat); stat->result_mask |= STATX_TYPE; } @@ -1396,16 +1434,29 @@ static int fuse_update_get_attr(struct mnt_idmap *idmap, struct inode *inode, u32 inval_mask = READ_ONCE(fi->inval_mask); u32 cache_mask = fuse_get_cache_mask(inode); - - /* FUSE only supports basic stats and possibly btime */ - request_mask &= STATX_BASIC_STATS | STATX_BTIME; + /* Only ask for supported stats */ + request_mask &= FUSE_SUPPORTED_STATX_MASK; retry: if (fc->no_statx) request_mask &= STATX_BASIC_STATS; if (!request_mask) sync = false; - else if (flags & AT_STATX_FORCE_SYNC) + else if (request_mask & FUSE_UNCACHED_STATX_MASK) { + switch (flags & AT_STATX_SYNC_TYPE) { + case AT_STATX_DONT_SYNC: + request_mask &= ~FUSE_UNCACHED_STATX_MASK; + sync = false; + break; + case AT_STATX_FORCE_SYNC: + case AT_STATX_SYNC_AS_STAT: + sync = true; + break; + default: + WARN_ON(1); + break; + } + } else if (flags & AT_STATX_FORCE_SYNC) sync = true; else if (flags & AT_STATX_DONT_SYNC) sync = false; @@ -1416,7 +1467,7 @@ static int fuse_update_get_attr(struct mnt_idmap *idmap, struct inode *inode, if (sync) { forget_all_cached_acls(inode); - /* Try statx if BTIME is requested */ + /* Try statx if a field not covered by regular stat is wanted */ if (!fc->no_statx && (request_mask & ~STATX_BASIC_STATS)) { err = fuse_do_statx(idmap, inode, file, stat); if (err == -ENOSYS) {