Commit 7dc4e97a authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi
Browse files

fuse: introduce FUSE_PASSTHROUGH capability



FUSE_PASSTHROUGH capability to passthrough FUSE operations to backing
files will be made available with kernel config CONFIG_FUSE_PASSTHROUGH.

When requesting FUSE_PASSTHROUGH, userspace needs to specify the
max_stack_depth that is allowed for FUSE on top of backing files.

Introduce the flag FOPEN_PASSTHROUGH and backing_id to fuse_open_out
argument that can be used when replying to OPEN request, to setup
passthrough of io operations on the fuse inode to a backing file.

Introduce a refcounted fuse_backing object that will be used to
associate an open backing file with a fuse inode.

Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent aed91831
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -52,3 +52,14 @@ config FUSE_DAX

	  If you want to allow mounting a Virtio Filesystem with the "dax"
	  option, answer Y.

config FUSE_PASSTHROUGH
	bool "FUSE passthrough operations support"
	default y
	depends on FUSE_FS
	select FS_STACK
	help
	  This allows bypassing FUSE server by mapping specific FUSE operations
	  to be performed directly on a backing file.

	  If you want to allow passthrough operations, answer Y.
+1 −0
Original line number Diff line number Diff line
@@ -10,5 +10,6 @@ obj-$(CONFIG_VIRTIO_FS) += virtiofs.o
fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o
fuse-y += iomode.o
fuse-$(CONFIG_FUSE_DAX) += dax.o
fuse-$(CONFIG_FUSE_PASSTHROUGH) += passthrough.o

virtiofs-y := virtio_fs.o
+42 −0
Original line number Diff line number Diff line
@@ -76,6 +76,15 @@ struct fuse_submount_lookup {
	struct fuse_forget_link *forget;
};

/** Container for data related to mapping to backing file */
struct fuse_backing {
	struct file *file;

	/** refcount */
	refcount_t count;
	struct rcu_head rcu;
};

/** FUSE inode */
struct fuse_inode {
	/** Inode data */
@@ -179,6 +188,10 @@ struct fuse_inode {
#endif
	/** Submount specific lookup tracking */
	struct fuse_submount_lookup *submount_lookup;
#ifdef CONFIG_FUSE_PASSTHROUGH
	/** Reference to backing file in passthrough mode */
	struct fuse_backing *fb;
#endif
};

/** FUSE inode state bits */
@@ -829,6 +842,12 @@ struct fuse_conn {
	/* Is statx not implemented by fs? */
	unsigned int no_statx:1;

	/** Passthrough support for read/write IO */
	unsigned int passthrough:1;

	/** Maximum stack depth for passthrough backing files */
	int max_stack_depth;

	/** The number of requests waiting for completion */
	atomic_t num_waiting;

@@ -1368,4 +1387,27 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
void fuse_file_release(struct inode *inode, struct fuse_file *ff,
		       unsigned int open_flags, fl_owner_t id, bool isdir);

/* passthrough.c */
static inline struct fuse_backing *fuse_inode_backing(struct fuse_inode *fi)
{
#ifdef CONFIG_FUSE_PASSTHROUGH
	return READ_ONCE(fi->fb);
#else
	return NULL;
#endif
}

static inline struct fuse_backing *fuse_inode_backing_set(struct fuse_inode *fi,
							  struct fuse_backing *fb)
{
#ifdef CONFIG_FUSE_PASSTHROUGH
	return xchg(&fi->fb, fb);
#else
	return NULL;
#endif
}

struct fuse_backing *fuse_backing_get(struct fuse_backing *fb);
void fuse_backing_put(struct fuse_backing *fb);

#endif /* _FS_FUSE_I_H */
+26 −0
Original line number Diff line number Diff line
@@ -111,6 +111,9 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
	if (IS_ENABLED(CONFIG_FUSE_DAX) && !fuse_dax_inode_alloc(sb, fi))
		goto out_free_forget;

	if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
		fuse_inode_backing_set(fi, NULL);

	return &fi->inode;

out_free_forget:
@@ -129,6 +132,9 @@ static void fuse_free_inode(struct inode *inode)
#ifdef CONFIG_FUSE_DAX
	kfree(fi->dax);
#endif
	if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
		fuse_backing_put(fuse_inode_backing(fi));

	kmem_cache_free(fuse_inode_cachep, fi);
}

@@ -1284,6 +1290,24 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
				fc->create_supp_group = 1;
			if (flags & FUSE_DIRECT_IO_ALLOW_MMAP)
				fc->direct_io_allow_mmap = 1;
			/*
			 * max_stack_depth is the max stack depth of FUSE fs,
			 * so it has to be at least 1 to support passthrough
			 * to backing files.
			 *
			 * with max_stack_depth > 1, the backing files can be
			 * on a stacked fs (e.g. overlayfs) themselves and with
			 * max_stack_depth == 1, FUSE fs can be stacked as the
			 * underlying fs of a stacked fs (e.g. overlayfs).
			 */
			if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) &&
			    (flags & FUSE_PASSTHROUGH) &&
			    arg->max_stack_depth > 0 &&
			    arg->max_stack_depth <= FILESYSTEM_MAX_STACK_DEPTH) {
				fc->passthrough = 1;
				fc->max_stack_depth = arg->max_stack_depth;
				fm->sb->s_stack_depth = arg->max_stack_depth;
			}
		} else {
			ra_pages = fc->max_read / PAGE_SIZE;
			fc->no_lock = 1;
@@ -1339,6 +1363,8 @@ void fuse_send_init(struct fuse_mount *fm)
#endif
	if (fm->fc->auto_submounts)
		flags |= FUSE_SUBMOUNTS;
	if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
		flags |= FUSE_PASSTHROUGH;

	ia->in.flags = flags;
	ia->in.flags2 = flags >> 32;

fs/fuse/passthrough.c

0 → 100644
+30 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * FUSE passthrough to backing file.
 *
 * Copyright (c) 2023 CTERA Networks.
 */

#include "fuse_i.h"

#include <linux/file.h>

struct fuse_backing *fuse_backing_get(struct fuse_backing *fb)
{
	if (fb && refcount_inc_not_zero(&fb->count))
		return fb;
	return NULL;
}

static void fuse_backing_free(struct fuse_backing *fb)
{
	if (fb->file)
		fput(fb->file);
	kfree_rcu(fb, rcu);
}

void fuse_backing_put(struct fuse_backing *fb)
{
	if (fb && refcount_dec_and_test(&fb->count))
		fuse_backing_free(fb);
}
Loading