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 28/36] fuse: Provide registration functions for fuse-bpf
Date: Thu, 28 Mar 2024 18:53:43 -0700	[thread overview]
Message-ID: <20240329015351.624249-29-drosen@google.com> (raw)
In-Reply-To: <20240329015351.624249-1-drosen@google.com>

Fuse may be built as a module, but verifier components are not. This
provides a means for fuse-bpf to handle struct op programs once the
module is loaded.

Signed-off-by: Daniel Rosenberg <drosen@google.com>
---
 fs/fuse/Makefile         |   2 +-
 fs/fuse/backing.c        |   2 +
 fs/fuse/bpf_register.c   | 207 +++++++++++++++++++++++++++++++++++++++
 fs/fuse/fuse_i.h         |  26 +++++
 include/linux/bpf_fuse.h |   8 ++
 5 files changed, 244 insertions(+), 1 deletion(-)
 create mode 100644 fs/fuse/bpf_register.c

diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile
index a0853c439db2..903253db7285 100644
--- a/fs/fuse/Makefile
+++ b/fs/fuse/Makefile
@@ -9,6 +9,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-$(CONFIG_FUSE_DAX) += dax.o
-fuse-$(CONFIG_FUSE_BPF) += backing.o
+fuse-$(CONFIG_FUSE_BPF) += backing.o bpf_register.o
 
 virtiofs-y := virtio_fs.o
diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
index 884c690becd5..d5fcaef8e6b5 100644
--- a/fs/fuse/backing.c
+++ b/fs/fuse/backing.c
@@ -3392,6 +3392,7 @@ int fuse_bpf_access(int *out, struct inode *inode, int mask)
 
 int __init fuse_bpf_init(void)
 {
+	init_fuse_bpf();
 	fuse_bpf_aio_request_cachep = kmem_cache_create("fuse_bpf_aio_req",
 						   sizeof(struct fuse_bpf_aio_req),
 						   0, SLAB_HWCACHE_ALIGN, NULL);
@@ -3403,5 +3404,6 @@ int __init fuse_bpf_init(void)
 
 void __exit fuse_bpf_cleanup(void)
 {
+	uninit_fuse_bpf();
 	kmem_cache_destroy(fuse_bpf_aio_request_cachep);
 }
diff --git a/fs/fuse/bpf_register.c b/fs/fuse/bpf_register.c
new file mode 100644
index 000000000000..32f96004b161
--- /dev/null
+++ b/fs/fuse/bpf_register.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FUSE-BPF: Filesystem in Userspace with BPF
+ * Copyright (c) 2021 Google LLC
+ */
+
+#include <linux/bpf_verifier.h>
+#include <linux/bpf_fuse.h>
+#include <linux/bpf.h>
+#include <linux/btf.h>
+#include <linux/hashtable.h>
+
+#include "fuse_i.h"
+
+struct fuse_ops tmp_f_op_empty = { 0 };
+struct fuse_ops *tmp_f_op = &tmp_f_op_empty;
+
+struct hashtable_entry {
+	struct hlist_node hlist;
+	struct hlist_node dlist; /* for deletion cleanup */
+	struct qstr key;
+	struct fuse_ops *ops;
+};
+
+static DEFINE_HASHTABLE(name_to_ops, 8);
+
+static unsigned int full_name_case_hash(const void *salt, const unsigned char *name, unsigned int len)
+{
+	unsigned long hash = init_name_hash(salt);
+
+	while (len--)
+		hash = partial_name_hash(tolower(*name++), hash);
+	return end_name_hash(hash);
+}
+
+static inline void qstr_init(struct qstr *q, const char *name)
+{
+	q->name = name;
+	q->len = strlen(q->name);
+	q->hash = full_name_case_hash(0, q->name, q->len);
+}
+
+static inline int qstr_copy(const struct qstr *src, struct qstr *dest)
+{
+	dest->name = kstrdup(src->name, GFP_KERNEL);
+	dest->hash_len = src->hash_len;
+	return !!dest->name;
+}
+
+static inline int qstr_eq(const struct qstr *s1, const struct qstr *s2)
+{
+	int res, r1, r2, r3;
+
+	r1 = s1->len == s2->len;
+	r2 = s1->hash == s2->hash;
+	r3 = memcmp(s1->name, s2->name, s1->len);
+	res = (s1->len == s2->len && s1->hash == s2->hash && !memcmp(s1->name, s2->name, s1->len));
+	return res;
+}
+
+static struct fuse_ops *__find_fuse_ops(const struct qstr *key)
+{
+	struct hashtable_entry *hash_cur;
+	unsigned int hash = key->hash;
+	struct fuse_ops *ret_ops;
+
+	rcu_read_lock();
+	hash_for_each_possible_rcu(name_to_ops, hash_cur, hlist, hash) {
+		if (qstr_eq(key, &hash_cur->key)) {
+			ret_ops = hash_cur->ops;
+			ret_ops = get_fuse_ops(ret_ops);
+			rcu_read_unlock();
+			return ret_ops;
+		}
+	}
+	rcu_read_unlock();
+	return NULL;
+}
+
+struct fuse_ops *get_fuse_ops(struct fuse_ops *ops)
+{
+	if (bpf_try_module_get(ops, BPF_MODULE_OWNER))
+		return ops;
+	else
+		return NULL;
+}
+
+void put_fuse_ops(struct fuse_ops *ops)
+{
+	if (ops)
+		bpf_module_put(ops, BPF_MODULE_OWNER);
+}
+
+struct fuse_ops *find_fuse_ops(const char *key)
+{
+	struct qstr q;
+
+	qstr_init(&q, key);
+	return __find_fuse_ops(&q);
+}
+
+static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key,
+		struct fuse_ops *value)
+{
+	struct hashtable_entry *ret = kzalloc(sizeof(*ret), GFP_KERNEL);
+	if (!ret)
+		return NULL;
+	INIT_HLIST_NODE(&ret->dlist);
+	INIT_HLIST_NODE(&ret->hlist);
+
+	if (!qstr_copy(key, &ret->key)) {
+		kfree(ret);
+		return NULL;
+	}
+
+	ret->ops = value;
+	return ret;
+}
+
+static int __register_fuse_op(struct fuse_ops *value)
+{
+	struct hashtable_entry *hash_cur;
+	struct hashtable_entry *new_entry;
+	struct qstr key;
+	unsigned int hash;
+
+	qstr_init(&key, value->name);
+	hash = key.hash;
+	hash_for_each_possible_rcu(name_to_ops, hash_cur, hlist, hash) {
+		if (qstr_eq(&key, &hash_cur->key)) {
+			return -EEXIST;
+		}
+	}
+	new_entry = alloc_hashtable_entry(&key, value);
+	if (!new_entry)
+		return -ENOMEM;
+	hash_add_rcu(name_to_ops, &new_entry->hlist, hash);
+	return 0;
+}
+
+static int register_fuse_op(struct fuse_ops *value)
+{
+	int err;
+
+	if (bpf_try_module_get(value, BPF_MODULE_OWNER))
+		err = __register_fuse_op(value);
+	else
+		return -EBUSY;
+
+	return err;
+}
+
+static void unregister_fuse_op(struct fuse_ops *value)
+{
+	struct hashtable_entry *hash_cur;
+	struct qstr key;
+	unsigned int hash;
+	struct hlist_node *h_t;
+	HLIST_HEAD(free_list);
+
+	qstr_init(&key, value->name);
+	hash = key.hash;
+
+	hash_for_each_possible_rcu(name_to_ops, hash_cur, hlist, hash) {
+		if (qstr_eq(&key, &hash_cur->key)) {
+			hash_del_rcu(&hash_cur->hlist);
+			hlist_add_head(&hash_cur->dlist, &free_list);
+		}
+	}
+	synchronize_rcu();
+	bpf_module_put(value, BPF_MODULE_OWNER);
+	hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
+		kfree(hash_cur);
+}
+
+static void fuse_op_list_destroy(void)
+{
+	struct hashtable_entry *hash_cur;
+	struct hlist_node *h_t;
+	HLIST_HEAD(free_list);
+	int i;
+
+	hash_for_each_rcu(name_to_ops, i, hash_cur, hlist) {
+		hash_del_rcu(&hash_cur->hlist);
+		hlist_add_head(&hash_cur->dlist, &free_list);
+	}
+	synchronize_rcu();
+	hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
+		kfree(hash_cur);
+	pr_info("fuse: destroyed fuse_op list\n");
+}
+
+static struct bpf_fuse_ops_attach bpf_fuse_ops_connect = {
+	.fuse_register_bpf = &register_fuse_op,
+	.fuse_unregister_bpf = &unregister_fuse_op,
+};
+
+int init_fuse_bpf(void)
+{
+	return register_fuse_bpf(&bpf_fuse_ops_connect);
+}
+
+void uninit_fuse_bpf(void)
+{
+	unregister_fuse_bpf(&bpf_fuse_ops_connect);
+	fuse_op_list_destroy();
+}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index f1a8f8a97f1f..082cfd14de53 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1423,6 +1423,32 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff,
 		       unsigned int open_flags, fl_owner_t id, bool isdir);
 
 /* backing.c */
+#ifdef CONFIG_FUSE_BPF
+struct fuse_ops *find_fuse_ops(const char *key);
+struct fuse_ops *get_fuse_ops(struct fuse_ops *ops);
+void put_fuse_ops(struct fuse_ops *ops);
+int init_fuse_bpf(void);
+void uninit_fuse_bpf(void);
+#else
+int init_fuse_bpf(void)
+{
+	return -EOPNOTSUPP;
+}
+void uninit_fuse_bpf(void)
+{
+}
+struct fuse_ops *find_fuse_ops(const char *key)
+{
+	return NULL;
+}
+struct fuse_ops *get_fuse_ops(struct fuse_ops *ops)
+{
+	return NULL;
+}
+void put_fuse_ops(struct fuse_ops *ops)
+{
+}
+#endif
 
 enum fuse_bpf_set {
 	FUSE_BPF_UNCHANGED = 0,
diff --git a/include/linux/bpf_fuse.h b/include/linux/bpf_fuse.h
index 780a7889aea2..2183a7a45c92 100644
--- a/include/linux/bpf_fuse.h
+++ b/include/linux/bpf_fuse.h
@@ -270,4 +270,12 @@ struct fuse_ops {
 	char name[BPF_FUSE_NAME_MAX];
 };
 
+struct bpf_fuse_ops_attach {
+	int (*fuse_register_bpf)(struct fuse_ops *f_ops);
+	void (*fuse_unregister_bpf)(struct fuse_ops *f_ops);
+};
+
+int register_fuse_bpf(struct bpf_fuse_ops_attach *reg_ops);
+void unregister_fuse_bpf(struct bpf_fuse_ops_attach *reg_ops);
+
 #endif /* _BPF_FUSE_H */
-- 
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 ` Daniel Rosenberg [this message]
2024-03-29  1:53 ` [RFC PATCH v4 29/36] fuse-bpf: Set fuse_ops at mount or lookup time Daniel Rosenberg
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-29-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.