From: Darrick J. Wong <djwong@xxxxxxxxxx> Make it easier to invalidate the page cache for a block device that is being used in conjunction with iomap. This allows a fuse server to kill all cached data for a block that is being freed, so that block reuse doesn't result in file corruption. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- include/fuse_kernel.h | 9 +++++++++ include/fuse_lowlevel.h | 15 +++++++++++++++ lib/fuse_lowlevel.c | 22 ++++++++++++++++++++++ lib/fuse_versionscript | 1 + 4 files changed, 47 insertions(+) diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index 46960711691d99..1470b59d742165 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -241,6 +241,7 @@ * SEEK_{DATA,HOLE}, buffered I/O, and direct I/O * - add FUSE_ATTR_IOMAP to enable iomap for specific inodes * - add FUSE_IOMAP_CONFIG so the fuse server can configure more fs geometry + * - add FUSE_NOTIFY_IOMAP_DEV_INVAL to invalidate iomap bdev ranges */ #ifndef _LINUX_FUSE_H @@ -691,6 +692,7 @@ enum fuse_notify_code { FUSE_NOTIFY_DELETE = 6, FUSE_NOTIFY_RESEND = 7, FUSE_NOTIFY_INC_EPOCH = 8, + FUSE_NOTIFY_IOMAP_DEV_INVAL = 9, FUSE_NOTIFY_CODE_MAX, }; @@ -1389,4 +1391,11 @@ struct fuse_iomap_config_out { int64_t s_maxbytes; /* max file size */ }; +struct fuse_iomap_dev_inval { + uint32_t dev; /* device cookie */ + uint32_t reserved; /* zero */ + + uint64_t offset; /* range to invalidate pagecache, bytes */ + uint64_t length; +}; #endif /* _LINUX_FUSE_H */ diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index 1b2a6c00d0f9dc..b7a099bea6921e 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -2158,6 +2158,21 @@ int fuse_lowlevel_iomap_device_add(struct fuse_session *se, int fd, */ int fuse_lowlevel_iomap_device_remove(struct fuse_session *se, int device_id); +/* + * Invalidate the page cache of a block device opened for use with iomap. + * + * Added in FUSE protocol version 7.99. If the kernel does not support + * this (or a newer) version, the function will return -ENOSYS and do + * nothing. + * + * @param se the session object + * @param dev device cookie returned by fuse_lowlevel_iomap_add_device + * @param offset start of the range to invalidate, in bytes + * @return length length of the range to invalidate, in bytes + */ +int fuse_lowlevel_iomap_device_invalidate(struct fuse_session *se, int dev, + off_t offset, off_t length); + /* ----------------------------------------------------------- * * Utility functions * * ----------------------------------------------------------- */ diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 60627ec35cd367..f730a7fd4ead09 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -3480,6 +3480,28 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, return res; } +int fuse_lowlevel_iomap_device_invalidate(struct fuse_session *se, int dev, + off_t offset, off_t length) +{ + struct fuse_iomap_dev_inval arg = { + .dev = dev, + .offset = offset, + .length = length, + }; + struct iovec iov[2]; + + if (!se) + return -EINVAL; + + if (!(se->conn.want_ext & FUSE_CAP_IOMAP)) + return -ENOSYS; + + iov[1].iov_base = &arg; + iov[1].iov_len = sizeof(arg); + + return send_notify_iov(se, FUSE_NOTIFY_IOMAP_DEV_INVAL, iov, 2); +} + struct fuse_retrieve_req { struct fuse_notify_req nreq; void *cookie; diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index f886d268c8a99f..65ce70649b031c 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -233,6 +233,7 @@ FUSE_3.99 { fuse_fs_can_enable_iomapx; fuse_lowlevel_discover_iomap; fuse_reply_iomap_config; + fuse_lowlevel_iomap_device_invalidate; } FUSE_3.18; # Local Variables: