On Thu, Oct 8, 2026 at 1:20 PM Miklos Szeredi <[email protected]> wrote:
>
> Add support for server allocated 64-bit backing IDs alongside the
> existing kernel allocated 32-bit IDs.
>
> Opened with FUSE_DEV_IOC_BACKING_CREATE, the backing ID sent via
> fuse_backing_create_in.backing_id.
>
> Closed with FUSE_NOTIFY_BACKING_REMOVE. Since close only provides the
> backing ID, not the file descriptor, it doesn't have to be done with an
> ioctl.
>
> Backing ID can take any 64 bit value other than zero.
>
> Signed-off-by: Miklos Szeredi <[email protected]>
> ---
> fs/file.c | 1 +
> fs/fuse/backing.c | 178 ++++++++++++++++++++++++++++----------
> fs/fuse/dev.c | 54 +++++++++---
> fs/fuse/dev.h | 3 +
> fs/fuse/fuse_i.h | 21 ++++-
> fs/fuse/inode.c | 8 +-
> fs/fuse/notify.c | 28 ++++++
> fs/fuse/passthrough.c | 3 +
> include/uapi/linux/fuse.h | 22 ++++-
> 9 files changed, 252 insertions(+), 66 deletions(-)
>
[...]
> int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map);
> int fuse_backing_close(struct fuse_conn *fc, int backing_id);
> +int fuse_backing_open_64(struct fuse_conn *fc, struct fuse_backing_create_in
> *map);
> +int fuse_backing_close_64(struct fuse_conn *fc, u64 backing_id);
>
> int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size);
> int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop,
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index d9ec88028d7b..b1e450f096f6 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -32,6 +32,7 @@
> #include <linux/pid_namespace.h>
> #include <linux/refcount.h>
> #include <linux/user_namespace.h>
> +#include <linux/rhashtable-types.h>
>
> /** Default max number of pages that can be used in a single read request */
> #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
> @@ -92,7 +93,8 @@ struct fuse_submount_lookup {
> struct fuse_backing {
> struct file *file;
> const struct cred *cred;
> -
> + u64 backing_id;
> + struct rhash_head hash_node;
> /* refcount */
> refcount_t count;
> struct rcu_head rcu;
> @@ -713,6 +715,9 @@ struct fuse_conn {
> /** @passthrough: Passthrough support for read/write IO */
> unsigned int passthrough:1;
>
> + /** @backing_id_64: Backing ID is 64 bit and allocated by the server
> */
> + bool backing_id_64:1;
> +
> /** @use_pages_for_kvec_io: Use pages instead of pointer for kernel
> I/O */
> unsigned int use_pages_for_kvec_io:1;
>
> @@ -770,8 +775,14 @@ struct fuse_conn {
> struct fuse_sync_bucket __rcu *curr_bucket;
>
> #ifdef CONFIG_FUSE_PASSTHROUGH
> - /** @backing_files_map: IDR for backing files ids */
> - struct idr backing_files_map;
> + /* Selected by backing_id_64 */
> + union {
> + /** @backing_files_map: IDR for backing files ids */
> + struct idr backing_files_map;
> +
> + /** @backing_64_ht: 64 bit ID lookup hash table */
> + struct rhashtable backing_64_ht;
> + };
> #endif
> };
>
> @@ -1270,7 +1281,7 @@ void fuse_file_release(struct inode *inode, struct
> fuse_file *ff,
> /* backing.c */
> #ifdef CONFIG_FUSE_PASSTHROUGH
> void fuse_backing_put(struct fuse_backing *fb);
> -struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, int
> backing_id);
> +
> #else
>
> static inline void fuse_backing_put(struct fuse_backing *fb)
> @@ -1278,7 +1289,9 @@ static inline void fuse_backing_put(struct fuse_backing
> *fb)
> }
> #endif
>
> +struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, u64
> backing_id);
> void fuse_backing_files_init(struct fuse_conn *fc);
> +void fuse_backing_files_init_64(struct fuse_conn *fc);
> void fuse_backing_files_free(struct fuse_conn *fc);
>
> /* passthrough.c */
> diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
> index cbb10e19e7e8..b69c95ebb1ad 100644
> --- a/fs/fuse/inode.c
> +++ b/fs/fuse/inode.c
> @@ -1408,13 +1408,17 @@ static void process_init_reply(struct fuse_args
> *args, int error)
> * them together.
> */
> if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) &&
> - (flags & FUSE_PASSTHROUGH) &&
> + (flags & (FUSE_PASSTHROUGH |
> FUSE_PASSTHROUGH_V2)) &&
> arg->max_stack_depth > 0 &&
> arg->max_stack_depth <=
> FILESYSTEM_MAX_STACK_DEPTH &&
> !(flags & FUSE_WRITEBACK_CACHE)) {
> fc->passthrough = 1;
> fc->max_stack_depth = arg->max_stack_depth;
> fm->sb->s_stack_depth = arg->max_stack_depth;
> + if (flags & FUSE_PASSTHROUGH_V2) {
> + fc->backing_id_64 = true;
> + fuse_backing_files_init_64(fc);
> + }
> }
> if (flags & FUSE_NO_EXPORT_SUPPORT)
> fm->sb->s_export_op =
> &fuse_export_fid_operations;
Much better, but I still think it is somewhat ugly to call
fuse_backing_files_init_64()
after fuse_backing_files_init() for an init of a union.
Don't you think this is cleaner?
Thanks,
Amir.
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 330655af3bd0f..9abf6b1afef5d 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -996,9 +996,6 @@ void fuse_conn_init(struct fuse_conn *fc, struct
fuse_mount *fm,
fc->max_pages_limit = fuse_max_pages_limit;
fc->name_max = FUSE_NAME_LOW_MAX;
- if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
- fuse_backing_files_init(fc);
-
INIT_LIST_HEAD(&fc->mounts);
list_add(&fm->fc_entry, &fc->mounts);
fm->fc = fc;
@@ -1424,10 +1421,8 @@ static void process_init_reply(struct fuse_args
*args, int error)
fc->passthrough = 1;
fc->max_stack_depth = arg->max_stack_depth;
fm->sb->s_stack_depth = arg->max_stack_depth;
- if (flags & FUSE_PASSTHROUGH_V2) {
- fc->backing_id_64 = true;
- fuse_backing_files_init_64(fc);
- }
+ fc->backing_id_64 = !!(flags &
FUSE_PASSTHROUGH_V2);
+ fuse_backing_files_init(fc);
}
if (flags & FUSE_NO_EXPORT_SUPPORT)
fm->sb->s_export_op =
&fuse_export_fid_operations;
diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
index c93e4a45dd6f6..6157a18b5d554 100644
--- a/fs/fuse/backing.c
+++ b/fs/fuse/backing.c
@@ -46,11 +46,6 @@ void fuse_backing_put(struct fuse_backing *fb)
fuse_backing_free(fb);
}
-void fuse_backing_files_init(struct fuse_conn *fc)
-{
- idr_init(&fc->backing_files_map);
-}
-
static int fuse_backing_id_alloc(struct fuse_conn *fc, struct fuse_backing *fb)
{
int id;
@@ -338,6 +333,9 @@ void fuse_backing_files_free(struct fuse_conn *fc)
struct rhashtable_iter iter;
struct fuse_backing *fb;
+ if (!fc->passthrough)
+ return;
+
if (!fc->backing_id_64) {
idr_for_each(&fc->backing_files_map,
fuse_backing_idr_free, NULL);
idr_destroy(&fc->backing_files_map);
@@ -369,7 +367,12 @@ void fuse_backing_files_free(struct fuse_conn *fc)
rhashtable_free_and_destroy(&fc->backing_64_ht,
fuse_backing_rht_free, NULL);
}
-void fuse_backing_files_init_64(struct fuse_conn *fc)
+void fuse_backing_files_init(struct fuse_conn *fc)
{
+ if (!fc->backing_id_64) {
+ idr_init(&fc->backing_files_map);
+ return;
+ }
+
rhashtable_init(&fc->backing_64_ht, &fuse_backing_prm);
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 064fed459b93c..4235b8fc669b0 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1324,7 +1324,6 @@ struct fuse_backing *fuse_backing_lookup(struct
fuse_conn *fc, u64 backing_id);
int fuse_backing_add_64(struct fuse_conn *fc, struct fuse_backing *fb);
bool fuse_backing_is_dax(struct fuse_backing *fb);
void fuse_backing_files_init(struct fuse_conn *fc);
-void fuse_backing_files_init_64(struct fuse_conn *fc);
void fuse_backing_files_free(struct fuse_conn *fc);