summaryrefslogtreecommitdiffstats
path: root/fs/fuse/iomode.c
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2024-02-09 17:14:50 +0200
committerMiklos Szeredi <mszeredi@redhat.com>2024-03-05 13:40:42 +0100
commit4a90451bbc7f7fde94041fbb9ca96dd915069943 (patch)
treecc15dfbe11bae5fade8742be0732ca0a3e8bf7fa /fs/fuse/iomode.c
parentfc8ff397b2a91590031ae08534de627f957005cb (diff)
downloadlinux-4a90451bbc7f7fde94041fbb9ca96dd915069943.tar.gz
linux-4a90451bbc7f7fde94041fbb9ca96dd915069943.tar.bz2
linux-4a90451bbc7f7fde94041fbb9ca96dd915069943.zip
fuse: implement open in passthrough mode
After getting a backing file id with FUSE_DEV_IOC_BACKING_OPEN ioctl, a FUSE server can reply to an OPEN request with flag FOPEN_PASSTHROUGH and the backing file id. The FUSE server should reuse the same backing file id for all the open replies of the same FUSE inode and open will fail (with -EIO) if a the server attempts to open the same inode with conflicting io modes or to setup passthrough to two different backing files for the same FUSE inode. Using the same backing file id for several different inodes is allowed. Opening a new file with FOPEN_DIRECT_IO for an inode that is already open for passthrough is allowed, but only if the FOPEN_PASSTHROUGH flag and correct backing file id are specified as well. The read/write IO of such files will not use passthrough operations to the backing file, but mmap, which does not support direct_io, will use the backing file insead of using the page cache as it always did. Even though all FUSE passthrough files of the same inode use the same backing file as a backing inode reference, each FUSE file opens a unique instance of a backing_file object to store the FUSE path that was used to open the inode and the open flags of the specific open file. The per-file, backing_file object is released along with the FUSE file. The inode associated fuse_backing object is released when the last FUSE passthrough file of that inode is released AND when the backing file id is closed by the server using the FUSE_DEV_IOC_BACKING_CLOSE ioctl. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse/iomode.c')
-rw-r--r--fs/fuse/iomode.c60
1 files changed, 54 insertions, 6 deletions
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index 2161bdf91db2..c653ddcf0578 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -17,7 +17,7 @@
*/
static inline bool fuse_is_io_cache_wait(struct fuse_inode *fi)
{
- return READ_ONCE(fi->iocachectr) < 0;
+ return READ_ONCE(fi->iocachectr) < 0 && !fuse_inode_backing(fi);
}
/*
@@ -45,6 +45,17 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
wait_event(fi->direct_io_waitq, !fuse_is_io_cache_wait(fi));
spin_lock(&fi->lock);
}
+
+ /*
+ * Check if inode entered passthrough io mode while waiting for parallel
+ * dio write completion.
+ */
+ if (fuse_inode_backing(fi)) {
+ clear_bit(FUSE_I_CACHE_IO_MODE, &fi->state);
+ spin_unlock(&fi->lock);
+ return -ETXTBSY;
+ }
+
WARN_ON(ff->iomode == IOM_UNCACHED);
if (ff->iomode == IOM_NONE) {
ff->iomode = IOM_CACHED;
@@ -71,12 +82,19 @@ static void fuse_file_cached_io_end(struct inode *inode, struct fuse_file *ff)
}
/* Start strictly uncached io mode where cache access is not allowed */
-int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff)
+int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struct fuse_backing *fb)
{
struct fuse_inode *fi = get_fuse_inode(inode);
+ struct fuse_backing *oldfb;
int err = 0;
spin_lock(&fi->lock);
+ /* deny conflicting backing files on same fuse inode */
+ oldfb = fuse_inode_backing(fi);
+ if (oldfb && oldfb != fb) {
+ err = -EBUSY;
+ goto unlock;
+ }
if (fi->iocachectr > 0) {
err = -ETXTBSY;
goto unlock;
@@ -84,6 +102,14 @@ int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff)
WARN_ON(ff->iomode != IOM_NONE);
fi->iocachectr--;
ff->iomode = IOM_UNCACHED;
+
+ /* fuse inode holds a single refcount of backing file */
+ if (!oldfb) {
+ oldfb = fuse_inode_backing_set(fi, fb);
+ WARN_ON_ONCE(oldfb != NULL);
+ } else {
+ fuse_backing_put(fb);
+ }
unlock:
spin_unlock(&fi->lock);
return err;
@@ -92,15 +118,20 @@ unlock:
void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff)
{
struct fuse_inode *fi = get_fuse_inode(inode);
+ struct fuse_backing *oldfb = NULL;
spin_lock(&fi->lock);
WARN_ON(fi->iocachectr >= 0);
WARN_ON(ff->iomode != IOM_UNCACHED);
ff->iomode = IOM_NONE;
fi->iocachectr++;
- if (!fi->iocachectr)
+ if (!fi->iocachectr) {
wake_up(&fi->direct_io_waitq);
+ oldfb = fuse_inode_backing_set(fi, NULL);
+ }
spin_unlock(&fi->lock);
+ if (oldfb)
+ fuse_backing_put(oldfb);
}
/*
@@ -118,6 +149,7 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
{
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_backing *fb;
int err;
/* Check allowed conditions for file open in passthrough mode */
@@ -125,11 +157,18 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
(ff->open_flags & ~FOPEN_PASSTHROUGH_MASK))
return -EINVAL;
- /* TODO: implement backing file open */
- return -EOPNOTSUPP;
+ fb = fuse_passthrough_open(file, inode,
+ ff->args->open_outarg.backing_id);
+ if (IS_ERR(fb))
+ return PTR_ERR(fb);
/* First passthrough file open denies caching inode io mode */
- err = fuse_file_uncached_io_start(inode, ff);
+ err = fuse_file_uncached_io_start(inode, ff, fb);
+ if (!err)
+ return 0;
+
+ fuse_passthrough_release(ff, fb);
+ fuse_backing_put(fb);
return err;
}
@@ -138,6 +177,7 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
int fuse_file_io_open(struct file *file, struct inode *inode)
{
struct fuse_file *ff = file->private_data;
+ struct fuse_inode *fi = get_fuse_inode(inode);
int err;
/*
@@ -148,6 +188,14 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
return 0;
/*
+ * Server is expected to use FOPEN_PASSTHROUGH for all opens of an inode
+ * which is already open for passthrough.
+ */
+ err = -EINVAL;
+ if (fuse_inode_backing(fi) && !(ff->open_flags & FOPEN_PASSTHROUGH))
+ goto fail;
+
+ /*
* FOPEN_PARALLEL_DIRECT_WRITES requires FOPEN_DIRECT_IO.
*/
if (!(ff->open_flags & FOPEN_DIRECT_IO))