All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Rosenberg <drosen@google.com>
To: Miklos Szeredi <miklos@szeredi.hu>,
	bpf@vger.kernel.org,  Alexei Starovoitov <ast@kernel.org>
Cc: Amir Goldstein <amir73il@gmail.com>,
	linux-kernel@vger.kernel.org,  linux-fsdevel@vger.kernel.org,
	linux-unionfs@vger.kernel.org,
	 Daniel Borkmann <daniel@iogearbox.net>,
	John Fastabend <john.fastabend@gmail.com>,
	 Andrii Nakryiko <andrii@kernel.org>,
	Martin KaFai Lau <martin.lau@linux.dev>,
	Song Liu <song@kernel.org>,  Eduard Zingerman <eddyz87@gmail.com>,
	Yonghong Song <yonghong.song@linux.dev>,
	 KP Singh <kpsingh@kernel.org>,
	Stanislav Fomichev <sdf@google.com>, Hao Luo <haoluo@google.com>,
	 Jiri Olsa <jolsa@kernel.org>, Shuah Khan <shuah@kernel.org>,
	Jonathan Corbet <corbet@lwn.net>,
	 Joanne Koong <joannelkoong@gmail.com>,
	Mykola Lysenko <mykolal@fb.com>,
	 Christian Brauner <brauner@kernel.org>,
	kernel-team@android.com,  Daniel Rosenberg <drosen@google.com>
Subject: [RFC PATCH v4 29/36] fuse-bpf: Set fuse_ops at mount or lookup time
Date: Thu, 28 Mar 2024 18:53:44 -0700	[thread overview]
Message-ID: <20240329015351.624249-30-drosen@google.com> (raw)
In-Reply-To: <20240329015351.624249-1-drosen@google.com>

This adds the ability to associate a fuse_op struct_op program with
inodes in fuse. This can be done at mount time at the root level, or by
inode at lookup time.

Signed-off-by: Daniel Rosenberg <drosen@google.com>
---
 fs/fuse/backing.c | 100 +++++++++++++++++++++++++++++++++++++++++-----
 fs/fuse/dir.c     |  19 ++++++++-
 fs/fuse/fuse_i.h  |  12 ++++++
 fs/fuse/inode.c   |  45 ++++++++++++++++++---
 4 files changed, 160 insertions(+), 16 deletions(-)

diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
index d5fcaef8e6b5..5fdca3a6183f 100644
--- a/fs/fuse/backing.c
+++ b/fs/fuse/backing.c
@@ -6,6 +6,7 @@
 
 #include "fuse_i.h"
 
+#include <linux/bpf.h>
 #include <linux/bpf_fuse.h>
 #include <linux/fdtable.h>
 #include <linux/file.h>
@@ -176,12 +177,13 @@ static void fuse_get_backing_path(struct file *file, struct path *path)
 
 static bool has_file(int type)
 {
-	return type == FUSE_ENTRY_BACKING;
+	return (type == FUSE_ENTRY_BACKING);
 }
 
 /*
- * The optional fuse bpf entry lists the backing file for a particular
- * lookup. These are inherited by default.
+ * The optional fuse bpf entry lists the bpf and backing files for a particular
+ * lookup. These are inherited by default. A Bpf requires a backing file to be
+ * meaningful.
  *
  * In the future, we may support multiple bpfs, and multiple backing files for
  * the bpf to choose between.
@@ -190,14 +192,14 @@ static bool has_file(int type)
  * file. Changing only the bpf is valid, though meaningless if there isn't an
  * inherited backing file.
  *
- * Support for the bpf program will be added in a later patch
- *
  */
 int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num)
 {
 	struct fuse_bpf_entry_out *fbeo;
+	struct fuse_ops *ops;
 	struct file *file;
 	bool has_backing = false;
+	bool has_bpf_ops = false;
 	int num_entries;
 	int err = -EINVAL;
 	int i;
@@ -235,6 +237,11 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num)
 				goto out_err;
 			fbe->backing_action = FUSE_BPF_REMOVE;
 			break;
+		case FUSE_ENTRY_REMOVE_BPF:
+			if (fbe->bpf_action || i == 2)
+				goto out_err;
+			fbe->bpf_action = FUSE_BPF_REMOVE;
+			break;
 		case FUSE_ENTRY_BACKING:
 			if (fbe->backing_action)
 				goto out_err;
@@ -242,8 +249,17 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num)
 			fbe->backing_action = FUSE_BPF_SET;
 			has_backing = true;
 			break;
+		case FUSE_ENTRY_BPF:
+			if (fbe->bpf_action || i == 2)
+				goto out_err;
+			ops = find_fuse_ops(fbeo->name);
+			if (!ops)
+				goto out_err;
+			has_bpf_ops = true;
+			fbe->bpf_action = FUSE_BPF_SET;
+			fbe->ops = ops;
+			break;
 		default:
-			err = -EINVAL;
 			goto out_err;
 		}
 		if (has_file(fbeo->entry_type)) {
@@ -260,6 +276,10 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num)
 		fput(file);
 	if (has_backing)
 		path_put_init(&fbe->backing_path);
+	if (has_bpf_ops) {
+		put_fuse_ops(fbe->ops);
+		fbe->ops = NULL;
+	}
 	return err;
 }
 
@@ -538,6 +558,15 @@ static int fuse_create_open_backing(struct bpf_fuse_args *fa, int *out,
 		goto out;
 	}
 
+	if (get_fuse_inode(inode)->bpf_ops)
+		put_fuse_ops(get_fuse_inode(inode)->bpf_ops);
+	get_fuse_inode(inode)->bpf_ops = dir_fuse_inode->bpf_ops;
+	if (get_fuse_inode(inode)->bpf_ops)
+		if (!get_fuse_ops(get_fuse_inode(inode)->bpf_ops)) {
+			*out = -EINVAL;
+			goto out;
+		}
+
 	newent = d_splice_alias(inode, entry);
 	if (IS_ERR(newent)) {
 		*out = PTR_ERR(newent);
@@ -1858,12 +1887,56 @@ int fuse_handle_backing(struct fuse_bpf_entry *fbe, struct path *backing_path)
 	return 0;
 }
 
+int fuse_handle_bpf_ops(struct fuse_bpf_entry *fbe, struct inode *parent,
+			 struct fuse_ops **ops)
+{
+	struct fuse_ops *new_ops = NULL;
+
+	switch (fbe->bpf_action) {
+	case FUSE_BPF_UNCHANGED: {
+		/* Parent isn't presented, but we want to keep
+		 * Don't touch bpf program at all in this case
+		 */
+		if (!parent)
+			return 0;
+
+		new_ops = get_fuse_inode(parent)->bpf_ops;
+		if (new_ops && !get_fuse_ops(new_ops))
+			return -EINVAL;
+		break;
+	}
+
+	case FUSE_BPF_REMOVE:
+		break;
+
+	case FUSE_BPF_SET:
+		new_ops = fbe->ops;
+
+		if (!new_ops)
+			return -EINVAL;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* Cannot change existing program */
+	if (*ops) {
+		if (new_ops)
+			put_fuse_ops(new_ops);
+		return new_ops == *ops ? 0 : -EINVAL;
+	}
+
+	*ops = new_ops;
+	return 0;
+}
+
 static int fuse_lookup_finalize(struct bpf_fuse_args *fa, struct dentry **out,
 				struct inode *dir, struct dentry *entry, unsigned int flags)
 {
 	struct fuse_dentry *fd;
 	struct dentry *backing_dentry;
-	struct inode *inode, *backing_inode;
+	struct inode *inode = NULL, *backing_inode;
 	struct inode *d_inode = entry->d_inode;
 	struct fuse_entry_out *feo = fa->out_args[0].value;
 	struct fuse_bpf_entry_out *febo = fa->out_args[1].buffer->data;
@@ -1891,13 +1964,22 @@ static int fuse_lookup_finalize(struct bpf_fuse_args *fa, struct dentry **out,
 		target_nodeid = get_fuse_inode(d_inode)->nodeid;
 
 	inode = fuse_iget_backing(dir->i_sb, target_nodeid, backing_inode);
+	if (!inode)
+		return -EIO;
 
-	if (IS_ERR(inode))
-		return PTR_ERR(inode);
+	error = fuse_handle_bpf_ops(fbe, dir, &get_fuse_inode(inode)->bpf_ops);
+	if (error) {
+		return error;
+		goto out;
+	}
 
 	get_fuse_inode(inode)->nodeid = feo->nodeid;
 
 	*out = d_splice_alias(inode, entry);
+	if (!IS_ERR(*out))
+		inode = NULL;
+out:
+	iput(inode);
 	return 0;
 }
 
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 08f93a981665..f5ab3b79b3cc 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -10,6 +10,7 @@
 
 #include <linux/pagemap.h>
 #include <linux/file.h>
+#include <linux/filter.h>
 #include <linux/fs_context.h>
 #include <linux/moduleparam.h>
 #include <linux/sched.h>
@@ -202,6 +203,7 @@ static bool backing_data_changed(struct fuse_inode *fi, struct dentry *entry,
 {
 	struct path new_backing_path;
 	struct inode *new_backing_inode;
+	struct fuse_ops *ops = NULL;
 	int err;
 	bool ret = true;
 
@@ -216,9 +218,15 @@ static bool backing_data_changed(struct fuse_inode *fi, struct dentry *entry,
 	if (err)
 		goto put_inode;
 
-	ret = (fi->backing_inode != new_backing_inode ||
-			!path_equal(&get_fuse_dentry(entry)->backing_path, &new_backing_path));
+	err = fuse_handle_bpf_ops(bpf_arg, entry->d_parent->d_inode, &ops);
+	if (err)
+		goto put_bpf;
 
+	ret = (ops != fi->bpf_ops || fi->backing_inode != new_backing_inode ||
+			!path_equal(&get_fuse_dentry(entry)->backing_path, &new_backing_path));
+put_bpf:
+	if (ops)
+		put_fuse_ops(ops);
 put_inode:
 	path_put(&new_backing_path);
 	return ret;
@@ -486,6 +494,13 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid,
 		*inode = fuse_iget_backing(sb, outarg->nodeid, backing_inode);
 		if (!*inode)
 			goto out_queue_forget;
+
+		err = fuse_handle_bpf_ops(&bpf_arg, NULL, &get_fuse_inode(*inode)->bpf_ops);
+		if (err) {
+			iput(*inode);
+			*inode = NULL;
+			goto out_put_forget;
+		}
 	} else
 #endif
 	{
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 082cfd14de53..665673d91753 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -33,6 +33,7 @@
 #include <linux/pid_namespace.h>
 #include <linux/refcount.h>
 #include <linux/user_namespace.h>
+#include <linux/bpf_fuse.h>
 #include <linux/magic.h>
 
 /** Default max number of pages that can be used in a single read request */
@@ -122,6 +123,12 @@ struct fuse_inode {
 	 * If this is set, nodeid is 0.
 	 */
 	struct inode *backing_inode;
+
+	/**
+	 * fuse_ops, provides handlers to run on all operations to determine
+	 * whether to pass through or handle in place
+	 */
+	struct fuse_ops *bpf_ops;
 #endif
 
 	/** Unique ID, which identifies the inode between userspace
@@ -591,6 +598,7 @@ struct fuse_fs_context {
 	unsigned int max_read;
 	unsigned int blksize;
 	const char *subtype;
+	struct fuse_ops *root_ops;
 	struct file *root_dir;
 
 	/* DAX device, may be NULL */
@@ -1461,6 +1469,8 @@ struct fuse_bpf_entry {
 
 	enum fuse_bpf_set backing_action;
 	struct path backing_path;
+	enum fuse_bpf_set bpf_action;
+	struct fuse_ops *ops;
 	bool is_used;
 };
 
@@ -1687,6 +1697,8 @@ int fuse_file_flock_backing(struct file *file, int cmd, struct file_lock *fl);
 ssize_t fuse_backing_mmap(struct file *file, struct vm_area_struct *vma);
 
 int fuse_handle_backing(struct fuse_bpf_entry *fbe, struct path *backing_path);
+int fuse_handle_bpf_ops(struct fuse_bpf_entry *fbe, struct inode *parent,
+			 struct fuse_ops **ops);
 
 int fuse_revalidate_backing(struct dentry *entry, unsigned int flags);
 
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index b4332416e23a..67d70b3c4abb 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -98,6 +98,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
 	fi->inval_mask = ~0;
 #ifdef CONFIG_FUSE_BPF
 	fi->backing_inode = NULL;
+	fi->bpf_ops = NULL;
 #endif
 	fi->nodeid = 0;
 	fi->nlookup = 0;
@@ -131,6 +132,10 @@ static void fuse_free_inode(struct inode *inode)
 	kfree(fi->forget);
 #ifdef CONFIG_FUSE_DAX
 	kfree(fi->dax);
+#endif
+#ifdef CONFIG_FUSE_BPF
+	if (fi->bpf_ops)
+		put_fuse_ops(fi->bpf_ops);
 #endif
 	kmem_cache_free(fuse_inode_cachep, fi);
 }
@@ -153,10 +158,6 @@ static void fuse_evict_inode(struct inode *inode)
 	/* Will write inode on close/munmap and in all other dirtiers */
 	WARN_ON(inode->i_state & I_DIRTY_INODE);
 
-#ifdef CONFIG_FUSE_BPF
-	iput(fi->backing_inode);
-#endif
-
 	truncate_inode_pages_final(&inode->i_data);
 	clear_inode(inode);
 	if (inode->i_sb->s_flags & SB_ACTIVE) {
@@ -181,6 +182,15 @@ static void fuse_evict_inode(struct inode *inode)
 	}
 }
 
+#ifdef CONFIG_FUSE_BPF
+static void fuse_destroy_inode(struct inode *inode)
+{
+	struct fuse_inode *fi = get_fuse_inode(inode);
+
+	iput(fi->backing_inode);
+}
+#endif
+
 static int fuse_reconfigure(struct fs_context *fsc)
 {
 	struct super_block *sb = fsc->root->d_sb;
@@ -828,6 +838,7 @@ enum {
 	OPT_ALLOW_OTHER,
 	OPT_MAX_READ,
 	OPT_BLKSIZE,
+	OPT_ROOT_BPF,
 	OPT_ROOT_DIR,
 	OPT_NO_DAEMON,
 	OPT_ERR
@@ -844,6 +855,7 @@ static const struct fs_parameter_spec fuse_fs_parameters[] = {
 	fsparam_u32	("max_read",		OPT_MAX_READ),
 	fsparam_u32	("blksize",		OPT_BLKSIZE),
 	fsparam_string	("subtype",		OPT_SUBTYPE),
+	fsparam_string	("root_bpf",		OPT_ROOT_BPF),
 	fsparam_u32	("root_dir",		OPT_ROOT_DIR),
 	fsparam_flag	("no_daemon",		OPT_NO_DAEMON),
 	{}
@@ -929,6 +941,18 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
 		ctx->blksize = result.uint_32;
 		break;
 
+	case OPT_ROOT_BPF:
+		if (strnlen(param->string, BPF_FUSE_NAME_MAX + 1) > BPF_FUSE_NAME_MAX) {
+			return invalfc(fsc, "root_bpf name too long. Max length is %d", BPF_FUSE_NAME_MAX);
+		}
+
+		ctx->root_ops = find_fuse_ops(param->string);
+		if (IS_ERR_OR_NULL(ctx->root_ops)) {
+			ctx->root_ops = NULL;
+			return invalfc(fsc, "Unable to find bpf program");
+		}
+		break;
+
 	case OPT_ROOT_DIR:
 		ctx->root_dir = fget(result.uint_32);
 		if (!ctx->root_dir)
@@ -954,6 +978,8 @@ static void fuse_free_fsc(struct fs_context *fsc)
 	if (ctx) {
 		if (ctx->root_dir)
 			fput(ctx->root_dir);
+		if (ctx->root_ops)
+			put_fuse_ops(ctx->root_ops);
 		kfree(ctx->subtype);
 		kfree(ctx);
 	}
@@ -1090,6 +1116,7 @@ EXPORT_SYMBOL_GPL(fuse_conn_get);
 
 static struct inode *fuse_get_root_inode(struct super_block *sb,
 					 unsigned int mode,
+					 struct fuse_ops *root_bpf_ops,
 					 struct file *backing_fd)
 {
 	struct fuse_attr attr;
@@ -1104,6 +1131,10 @@ static struct inode *fuse_get_root_inode(struct super_block *sb,
 		return NULL;
 
 #ifdef CONFIG_FUSE_BPF
+	get_fuse_inode(inode)->bpf_ops = root_bpf_ops;
+	if (root_bpf_ops)
+		get_fuse_ops(root_bpf_ops);
+
 	if (backing_fd) {
 		get_fuse_inode(inode)->backing_inode = backing_fd->f_inode;
 		ihold(backing_fd->f_inode);
@@ -1274,6 +1305,9 @@ static const struct export_operations fuse_export_operations = {
 
 static const struct super_operations fuse_super_operations = {
 	.alloc_inode    = fuse_alloc_inode,
+#ifdef CONFIG_FUSE_BPF
+	.destroy_inode	= fuse_destroy_inode,
+#endif
 	.free_inode     = fuse_free_inode,
 	.evict_inode	= fuse_evict_inode,
 	.write_inode	= fuse_write_inode,
@@ -1808,7 +1842,8 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
 	fc->no_daemon = ctx->no_daemon;
 
 	err = -ENOMEM;
-	root = fuse_get_root_inode(sb, ctx->rootmode, ctx->root_dir);
+	root = fuse_get_root_inode(sb, ctx->rootmode, ctx->root_ops,
+				   ctx->root_dir);
 	sb->s_d_op = &fuse_root_dentry_operations;
 	root_dentry = d_make_root(root);
 	if (!root_dentry)
-- 
2.44.0.478.gd926399ef9-goog


  parent reply	other threads:[~2024-03-29  1:55 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-29  1:53 [RFC PATCH v4 00/36] Fuse-BPF and plans on merging with Fuse Passthrough Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 01/36] fuse-bpf: Update fuse side uapi Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 02/36] fuse-bpf: Add data structures for fuse-bpf Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 03/36] fuse-bpf: Prepare for fuse-bpf patch Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 04/36] fuse: Add fuse-bpf, a stacked fs extension for FUSE Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 05/36] fuse-bpf: Add ioctl interface for /dev/fuse Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 06/36] fuse-bpf: Don't support export_operations Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 07/36] fuse-bpf: Add support for access Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 08/36] fuse-bpf: Partially add mapping support Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 09/36] fuse-bpf: Add lseek support Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 10/36] fuse-bpf: Add support for fallocate Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 11/36] fuse-bpf: Support file/dir open/close Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 12/36] fuse-bpf: Support mknod/unlink/mkdir/rmdir Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 13/36] fuse-bpf: Add support for read/write iter Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 14/36] fuse-bpf: support readdir Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 15/36] fuse-bpf: Add support for sync operations Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 16/36] fuse-bpf: Add Rename support Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 17/36] fuse-bpf: Add attr support Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 18/36] fuse-bpf: Add support for FUSE_COPY_FILE_RANGE Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 19/36] fuse-bpf: Add xattr support Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 20/36] fuse-bpf: Add symlink/link support Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 21/36] fuse-bpf: Add partial flock support Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 22/36] fuse-bpf: Add partial ioctl support Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 23/36] fuse-bpf: allow mounting with no userspace daemon Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 24/36] fuse-bpf: Add fuse-bpf constants Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 25/36] bpf: Increase struct_op max members Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 26/36] WIP: bpf: Add fuse_ops struct_op programs Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 27/36] fuse-bpf: Export Functions Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 28/36] fuse: Provide registration functions for fuse-bpf Daniel Rosenberg
2024-03-29  1:53 ` Daniel Rosenberg [this message]
2024-03-29  1:53 ` [RFC PATCH v4 30/36] fuse-bpf: Call bpf for pre/post filters Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 31/36] fuse-bpf: Add userspace " Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 32/36] WIP: fuse-bpf: add error_out Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 33/36] fuse-bpf: Add default filter op Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 34/36] tools: Add FUSE, update bpf includes Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 35/36] fuse-bpf: Add selftests Daniel Rosenberg
2024-03-29  1:53 ` [RFC PATCH v4 36/36] fuse: Provide easy way to test fuse struct_op call Daniel Rosenberg
2024-03-29  6:44 ` [RFC PATCH v4 00/36] Fuse-BPF and plans on merging with Fuse Passthrough Amir Goldstein
2024-03-30  0:59   ` Daniel Rosenberg
2024-04-01 14:43     ` Amir Goldstein
2024-04-01 20:27       ` Bernd Schubert

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240329015351.624249-30-drosen@google.com \
    --to=drosen@google.com \
    --cc=amir73il@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=brauner@kernel.org \
    --cc=corbet@lwn.net \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=haoluo@google.com \
    --cc=joannelkoong@gmail.com \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=kernel-team@android.com \
    --cc=kpsingh@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-unionfs@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=miklos@szeredi.hu \
    --cc=mykolal@fb.com \
    --cc=sdf@google.com \
    --cc=shuah@kernel.org \
    --cc=song@kernel.org \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.