]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fuse: create fuse_dev on /dev/fuse open instead of mount
authorMiklos Szeredi <mszeredi@redhat.com>
Wed, 11 Mar 2026 20:02:41 +0000 (21:02 +0100)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 2 Apr 2026 18:43:24 +0000 (20:43 +0200)
Allocate struct fuse_dev when opening the device.  This means that unlike
before, ->private_data is always set to a valid pointer.

The use of USE_DEV_SYNC_INIT magic pointer for the private_data is now
replaced with a simple bool sync_init member.

If sync INIT is not set, I/O on the device returns error before mount.
Keep this behavior by checking for the ->fc member.  If fud->fc is set, the
mount has succeeded.  Testing this used READ_ONCE(file->private_data) and
smp_mb() to try and provide the necessary semantics.  Switch this to
smp_store_release() and smp_load_acquire().

Setting fud->fc is protected by fuse_mutex, this is unchanged.

Will need this later so the /dev/fuse open file reference is not held
during FSCONFIG_CMD_CREATE.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
fs/fuse/dev.c
fs/fuse/fuse_dev_i.h
fs/fuse/fuse_i.h
fs/fuse/inode.c
fs/fuse/virtio_fs.c

index 952dd56f0a02d60be556989b05289df45776381c..0c03bbb21c1282b5ca38b70aaac29239daf9fc5f 100644 (file)
@@ -1548,32 +1548,24 @@ out_end:
 
 static int fuse_dev_open(struct inode *inode, struct file *file)
 {
-       /*
-        * The fuse device's file's private_data is used to hold
-        * the fuse_conn(ection) when it is mounted, and is used to
-        * keep track of whether the file has been mounted already.
-        */
-       file->private_data = NULL;
+       struct fuse_dev *fud = fuse_dev_alloc();
+
+       if (!fud)
+               return -ENOMEM;
+
+       file->private_data = fud;
        return 0;
 }
 
 struct fuse_dev *fuse_get_dev(struct file *file)
 {
-       struct fuse_dev *fud = __fuse_get_dev(file);
+       struct fuse_dev *fud = fuse_file_to_fud(file);
        int err;
 
-       if (likely(fud))
-               return fud;
-
-       err = wait_event_interruptible(fuse_dev_waitq,
-                                      READ_ONCE(file->private_data) != FUSE_DEV_SYNC_INIT);
+       err = wait_event_interruptible(fuse_dev_waitq, fuse_dev_fc_get(fud) != NULL);
        if (err)
                return ERR_PTR(err);
 
-       fud = __fuse_get_dev(file);
-       if (!fud)
-               return ERR_PTR(-EPERM);
-
        return fud;
 }
 
@@ -2547,10 +2539,10 @@ void fuse_wait_aborted(struct fuse_conn *fc)
 
 int fuse_dev_release(struct inode *inode, struct file *file)
 {
-       struct fuse_dev *fud = __fuse_get_dev(file);
+       struct fuse_dev *fud = fuse_file_to_fud(file);
+       struct fuse_conn *fc = fuse_dev_fc_get(fud);
 
-       if (fud) {
-               struct fuse_conn *fc = fud->fc;
+       if (fc) {
                struct fuse_pqueue *fpq = &fud->pq;
                LIST_HEAD(to_end);
                unsigned int i;
@@ -2568,8 +2560,8 @@ int fuse_dev_release(struct inode *inode, struct file *file)
                        WARN_ON(fc->iq.fasync != NULL);
                        fuse_abort_conn(fc);
                }
-               fuse_dev_free(fud);
        }
+       fuse_dev_free(fud);
        return 0;
 }
 EXPORT_SYMBOL_GPL(fuse_dev_release);
@@ -2587,16 +2579,12 @@ static int fuse_dev_fasync(int fd, struct file *file, int on)
 
 static int fuse_device_clone(struct fuse_conn *fc, struct file *new)
 {
-       struct fuse_dev *fud;
+       struct fuse_dev *new_fud = fuse_file_to_fud(new);
 
-       if (__fuse_get_dev(new))
+       if (fuse_dev_fc_get(new_fud))
                return -EINVAL;
 
-       fud = fuse_dev_alloc_install(fc);
-       if (!fud)
-               return -ENOMEM;
-
-       new->private_data = fud;
+       fuse_dev_install(new_fud, fc);
        atomic_inc(&fc->dev_count);
 
        return 0;
@@ -2667,10 +2655,11 @@ static long fuse_dev_ioctl_backing_close(struct file *file, __u32 __user *argp)
 static long fuse_dev_ioctl_sync_init(struct file *file)
 {
        int err = -EINVAL;
+       struct fuse_dev *fud = fuse_file_to_fud(file);
 
        mutex_lock(&fuse_mutex);
-       if (!__fuse_get_dev(file)) {
-               WRITE_ONCE(file->private_data, FUSE_DEV_SYNC_INIT);
+       if (!fuse_dev_fc_get(fud)) {
+               fud->sync_init = true;
                err = 0;
        }
        mutex_unlock(&fuse_mutex);
index 134bf44aff0d39ae8d5d47cf1518efcf2f1cfc23..522b2012cd1f5ca20fdb0bb58d3b1b0cb80bc57f 100644 (file)
@@ -39,18 +39,35 @@ struct fuse_copy_state {
        } ring;
 };
 
-#define FUSE_DEV_SYNC_INIT ((struct fuse_dev *) 1)
-#define FUSE_DEV_PTR_MASK (~1UL)
+/*
+ * Lockless access is OK, because fud->fc is set once during mount and is valid
+ * until the file is released.
+ */
+static inline struct fuse_conn *fuse_dev_fc_get(struct fuse_dev *fud)
+{
+       /* Pairs with smp_store_release() in fuse_dev_fc_set() */
+       return smp_load_acquire(&fud->fc);
+}
+
+static inline void fuse_dev_fc_set(struct fuse_dev *fud, struct fuse_conn *fc)
+{
+       /* Pairs with smp_load_acquire() in fuse_dev_fc_get() */
+       smp_store_release(&fud->fc, fc);
+}
+
+static inline struct fuse_dev *fuse_file_to_fud(struct file *file)
+{
+       return file->private_data;
+}
 
 static inline struct fuse_dev *__fuse_get_dev(struct file *file)
 {
-       /*
-        * Lockless access is OK, because file->private data is set
-        * once during mount and is valid until the file is released.
-        */
-       struct fuse_dev *fud = READ_ONCE(file->private_data);
+       struct fuse_dev *fud = fuse_file_to_fud(file);
+
+       if (!fuse_dev_fc_get(fud))
+               return NULL;
 
-       return (typeof(fud)) ((unsigned long) fud & FUSE_DEV_PTR_MASK);
+       return fud;
 }
 
 struct fuse_dev *fuse_get_dev(struct file *file);
index 23a241f18623a4e256ffb9805eaf40044db5bc0f..94b49384a2f765061c50406879861a9042b6bfbe 100644 (file)
@@ -577,6 +577,9 @@ struct fuse_pqueue {
  * Fuse device instance
  */
 struct fuse_dev {
+       /** Issue FUSE_INIT synchronously */
+       bool sync_init;
+
        /** Fuse connection for this device */
        struct fuse_conn *fc;
 
@@ -623,9 +626,6 @@ struct fuse_fs_context {
 
        /* DAX device, may be NULL */
        struct dax_device *dax_dev;
-
-       /* fuse_dev pointer to fill in, should contain NULL on entry */
-       void **fudptr;
 };
 
 struct fuse_sync_bucket {
index 8b64034ab0bbdc8a9db91198807f3cd8e948fb73..d34a7fbf849ca2b4c55071b6937599c829683533 100644 (file)
@@ -1641,7 +1641,7 @@ EXPORT_SYMBOL_GPL(fuse_dev_alloc);
 
 void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc)
 {
-       fud->fc = fuse_conn_get(fc);
+       fuse_dev_fc_set(fud, fuse_conn_get(fc));
        spin_lock(&fc->lock);
        list_add_tail(&fud->entry, &fc->devices);
        spin_unlock(&fc->lock);
@@ -1663,7 +1663,7 @@ EXPORT_SYMBOL_GPL(fuse_dev_alloc_install);
 
 void fuse_dev_free(struct fuse_dev *fud)
 {
-       struct fuse_conn *fc = fud->fc;
+       struct fuse_conn *fc = fuse_dev_fc_get(fud);
 
        if (fc) {
                spin_lock(&fc->lock);
@@ -1826,7 +1826,7 @@ EXPORT_SYMBOL_GPL(fuse_init_fs_context_submount);
 
 int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
 {
-       struct fuse_dev *fud = NULL;
+       struct fuse_dev *fud = ctx->file ? fuse_file_to_fud(ctx->file) : NULL;
        struct fuse_mount *fm = get_fuse_mount_super(sb);
        struct fuse_conn *fc = fm->fc;
        struct inode *root;
@@ -1860,18 +1860,11 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
                        goto err;
        }
 
-       if (ctx->fudptr) {
-               err = -ENOMEM;
-               fud = fuse_dev_alloc_install(fc);
-               if (!fud)
-                       goto err_free_dax;
-       }
-
        fc->dev = sb->s_dev;
        fm->sb = sb;
        err = fuse_bdi_init(fc, sb);
        if (err)
-               goto err_dev_free;
+               goto err_free_dax;
 
        /* Handle umasking inside the fuse code */
        if (sb->s_flags & SB_POSIXACL)
@@ -1893,15 +1886,15 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
        set_default_d_op(sb, &fuse_dentry_operations);
        root_dentry = d_make_root(root);
        if (!root_dentry)
-               goto err_dev_free;
+               goto err_free_dax;
 
        mutex_lock(&fuse_mutex);
        err = -EINVAL;
-       if (ctx->fudptr && *ctx->fudptr) {
-               if (*ctx->fudptr == FUSE_DEV_SYNC_INIT)
-                       fc->sync_init = 1;
-               else
+       if (fud) {
+               if (fuse_dev_fc_get(fud))
                        goto err_unlock;
+               if (fud->sync_init)
+                       fc->sync_init = 1;
        }
 
        err = fuse_ctl_add_conn(fc);
@@ -1910,8 +1903,8 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
 
        list_add_tail(&fc->entry, &fuse_conn_list);
        sb->s_root = root_dentry;
-       if (ctx->fudptr) {
-               *ctx->fudptr = fud;
+       if (fud) {
+               fuse_dev_install(fud, fc);
                wake_up_all(&fuse_dev_waitq);
        }
        mutex_unlock(&fuse_mutex);
@@ -1920,9 +1913,6 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
  err_unlock:
        mutex_unlock(&fuse_mutex);
        dput(root_dentry);
- err_dev_free:
-       if (fud)
-               fuse_dev_free(fud);
  err_free_dax:
        if (IS_ENABLED(CONFIG_FUSE_DAX))
                fuse_dax_conn_free(fc);
@@ -1948,13 +1938,10 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
        if ((ctx->file->f_op != &fuse_dev_operations) ||
            (ctx->file->f_cred->user_ns != sb->s_user_ns))
                return -EINVAL;
-       ctx->fudptr = &ctx->file->private_data;
 
        err = fuse_fill_super_common(sb, ctx);
        if (err)
                return err;
-       /* file->private_data shall be visible on all CPUs after this */
-       smp_mb();
 
        fm = get_fuse_mount_super(sb);
 
index 2f7485ffac527444fa0668f2134d02b1cc56de53..f685916754ad4ded6efc0a40d9497198c494ee24 100644 (file)
@@ -1590,8 +1590,6 @@ static int virtio_fs_fill_super(struct super_block *sb, struct fs_context *fsc)
                        goto err_free_fuse_devs;
        }
 
-       /* virtiofs allocates and installs its own fuse devices */
-       ctx->fudptr = NULL;
        if (ctx->dax_mode != FUSE_DAX_NEVER) {
                if (ctx->dax_mode == FUSE_DAX_ALWAYS && !fs->dax_dev) {
                        err = -EINVAL;