From: Darrick J. Wong <djwong@xxxxxxxxxx> Advertise the single-fsblock atomic write capability that iomap can do. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- misc/fuse2fs.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- misc/fuse4fs.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c index a00c32e9f2cae8..04bb96f3438f23 100644 --- a/misc/fuse2fs.c +++ b/misc/fuse2fs.c @@ -281,6 +281,9 @@ struct fuse2fs { void (*old_alloc_stats)(ext2_filsys fs, blk64_t blk, int inuse); void (*old_alloc_stats_range)(ext2_filsys fs, blk64_t blk, blk_t num, int inuse); +#ifdef STATX_WRITE_ATOMIC + unsigned int awu_min, awu_max; +#endif #endif unsigned int blockmask; unsigned long offset; @@ -580,9 +583,21 @@ static inline int fuse2fs_iomap_enabled(const struct fuse2fs *ff) { return ff->iomap_state >= IOMAP_ENABLED; } + +static inline int fuse2fs_iomap_can_hw_atomic(const struct fuse2fs *ff) +{ + return fuse2fs_iomap_enabled(ff) && + (ff->iomap_cap & FUSE_IOMAP_SUPPORT_ATOMIC) && +#ifdef STATX_WRITE_ATOMIC + ff->awu_min > 0 && ff->awu_min > 0; +#else + 0; +#endif +} #else # define fuse2fs_iomap_enabled(...) (0) # define fuse2fs_iomap_enabled(...) (0) +# define fuse2fs_iomap_can_hw_atomic(...) (0) #endif static inline void fuse2fs_dump_extents(struct fuse2fs *ff, ext2_ino_t ino, @@ -1631,14 +1646,19 @@ static int op_getattr(const char *path, struct stat *statbuf static int op_getattr_iflags(const char *path, struct stat *statbuf, unsigned int *iflags, struct fuse_file_info *fi) { + struct fuse2fs *ff = fuse2fs_get(); int ret = op_getattr(path, statbuf, fi); if (ret) return ret; - if (fuse_fs_can_enable_iomap(statbuf)) + if (fuse_fs_can_enable_iomap(statbuf)) { *iflags |= FUSE_IFLAG_IOMAP; + if (fuse2fs_iomap_can_hw_atomic(ff)) + *iflags |= FUSE_IFLAG_ATOMIC; + } + return 0; } #endif @@ -1744,6 +1764,15 @@ static int fuse2fs_statx(struct fuse2fs *ff, ext2_ino_t ino, int statx_mask, fuse2fs_statx_directio(ff, stx); +#ifdef STATX_WRITE_ATOMIC + if (fuse_fs_can_enable_iomapx(stx) && fuse2fs_iomap_can_hw_atomic(ff)) { + stx->stx_mask |= STATX_WRITE_ATOMIC; + stx->stx_atomic_write_unit_min = ff->awu_min; + stx->stx_atomic_write_unit_max = ff->awu_max; + stx->stx_atomic_write_segments_max = 1; + } +#endif + return 0; } @@ -5868,6 +5897,9 @@ static int op_iomap_begin(const char *path, uint64_t nodeid, uint64_t attr_ino, } } + if (opflags & FUSE_IOMAP_OP_ATOMIC) + read->flags |= FUSE_IOMAP_F_ATOMIC_BIO; + out_unlock: fuse2fs_finish(ff, ret); return ret; @@ -6027,6 +6059,38 @@ static int fuse2fs_set_bdev_blocksize(struct fuse2fs *ff, int fd) return EIO; } +#ifdef STATX_WRITE_ATOMIC +static void fuse2fs_configure_atomic_write(struct fuse2fs *ff, int bdev_fd) +{ + struct statx devx; + unsigned int awu_min, awu_max; + int ret; + + if (!ext2fs_has_feature_extents(ff->fs->super)) + return; + + ret = statx(bdev_fd, "", AT_EMPTY_PATH, STATX_WRITE_ATOMIC, &devx); + if (ret) + return; + if (!(devx.stx_mask & STATX_WRITE_ATOMIC)) + return; + + awu_min = max(ff->fs->blocksize, devx.stx_atomic_write_unit_min); + awu_max = min(ff->fs->blocksize, devx.stx_atomic_write_unit_max); + if (awu_min > awu_max) + return; + + log_printf(ff, "%s awu_min: %u, awu_max: %u\n", + _("Supports (experimental) DIO atomic writes"), + awu_min, awu_max); + + ff->awu_min = awu_min; + ff->awu_max = awu_max; +} +#else +# define fuse2fs_configure_atomic_write(...) ((void)0) +#endif + static int fuse2fs_iomap_config_devices(struct fuse2fs *ff) { errcode_t err; @@ -6051,6 +6115,8 @@ static int fuse2fs_iomap_config_devices(struct fuse2fs *ff) dbg_printf(ff, "%s: registered iomap dev fd=%d iomap_dev=%u\n", __func__, fd, ff->iomap_dev); + fuse2fs_configure_atomic_write(ff, fd); + ff->iomap_dev = ret; return 0; } diff --git a/misc/fuse4fs.c b/misc/fuse4fs.c index b45f92a1cdbe25..43fc21149ba564 100644 --- a/misc/fuse4fs.c +++ b/misc/fuse4fs.c @@ -277,6 +277,9 @@ struct fuse4fs { void (*old_alloc_stats)(ext2_filsys fs, blk64_t blk, int inuse); void (*old_alloc_stats_range)(ext2_filsys fs, blk64_t blk, blk_t num, int inuse); +#ifdef STATX_WRITE_ATOMIC + unsigned int awu_min, awu_max; +#endif #endif unsigned int blockmask; unsigned long offset; @@ -735,8 +738,20 @@ static inline int fuse4fs_iomap_enabled(const struct fuse4fs *ff) { return ff->iomap_state >= IOMAP_ENABLED; } + +static inline int fuse4fs_iomap_can_hw_atomic(const struct fuse4fs *ff) +{ + return fuse4fs_iomap_enabled(ff) && + (ff->iomap_cap & FUSE_IOMAP_SUPPORT_ATOMIC) && +#ifdef STATX_WRITE_ATOMIC + ff->awu_min > 0 && ff->awu_min > 0; +#else + 0; +#endif +} #else # define fuse4fs_iomap_enabled(...) (0) +# define fuse4fs_iomap_can_hw_atomic(...) (0) #endif static inline void fuse4fs_dump_extents(struct fuse4fs *ff, ext2_ino_t ino, @@ -1737,8 +1752,12 @@ static int fuse4fs_stat_inode(struct fuse4fs *ff, ext2_ino_t ino, fstat->iflags = 0; #ifdef HAVE_FUSE_IOMAP - if (fuse4fs_iomap_enabled(ff)) + if (fuse4fs_iomap_enabled(ff)) { fstat->iflags |= FUSE_IFLAG_IOMAP; + + if (fuse4fs_iomap_can_hw_atomic(ff)) + fstat->iflags |= FUSE_IFLAG_ATOMIC; + } #endif return 0; @@ -1913,6 +1932,15 @@ static int fuse4fs_statx(struct fuse4fs *ff, ext2_ino_t ino, int statx_mask, fuse4fs_statx_directio(ff, stx); +#ifdef STATX_WRITE_ATOMIC + if (fuse4fs_iomap_can_hw_atomic(ff)) { + stx->stx_mask |= STATX_WRITE_ATOMIC; + stx->stx_atomic_write_unit_min = ff->awu_min; + stx->stx_atomic_write_unit_max = ff->awu_max; + stx->stx_atomic_write_segments_max = 1; + } +#endif + return 0; } @@ -6193,6 +6221,9 @@ static void op_iomap_begin(fuse_req_t req, fuse_ino_t fino, uint64_t dontcare, } } + if (opflags & FUSE_IOMAP_OP_ATOMIC) + read.flags |= FUSE_IOMAP_F_ATOMIC_BIO; + out_unlock: fuse4fs_finish(ff, ret); if (ret) @@ -6355,6 +6386,38 @@ static int fuse4fs_set_bdev_blocksize(struct fuse4fs *ff, int fd) return EIO; } +#ifdef STATX_WRITE_ATOMIC +static void fuse4fs_configure_atomic_write(struct fuse4fs *ff, int bdev_fd) +{ + struct statx devx; + unsigned int awu_min, awu_max; + int ret; + + if (!ext2fs_has_feature_extents(ff->fs->super)) + return; + + ret = statx(bdev_fd, "", AT_EMPTY_PATH, STATX_WRITE_ATOMIC, &devx); + if (ret) + return; + if (!(devx.stx_mask & STATX_WRITE_ATOMIC)) + return; + + awu_min = max(ff->fs->blocksize, devx.stx_atomic_write_unit_min); + awu_max = min(ff->fs->blocksize, devx.stx_atomic_write_unit_max); + if (awu_min > awu_max) + return; + + log_printf(ff, "%s awu_min: %u, awu_max: %u\n", + _("Supports (experimental) DIO atomic writes"), + awu_min, awu_max); + + ff->awu_min = awu_min; + ff->awu_max = awu_max; +} +#else +# define fuse4fs_configure_atomic_write(...) ((void)0) +#endif + static int fuse4fs_iomap_config_devices(struct fuse4fs *ff) { errcode_t err; @@ -6379,6 +6442,8 @@ static int fuse4fs_iomap_config_devices(struct fuse4fs *ff) dbg_printf(ff, "%s: registered iomap dev fd=%d iomap_dev=%u\n", __func__, fd, ff->iomap_dev); + fuse4fs_configure_atomic_write(ff, fd); + ff->iomap_dev = ret; return 0; }