]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fuse: clean up device cloning
authorMiklos Szeredi <mszeredi@redhat.com>
Thu, 12 Mar 2026 11:19:10 +0000 (12:19 +0100)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 2 Apr 2026 18:52:59 +0000 (20:52 +0200)
 - fuse_mutex is not needed for device cloning, because fuse_dev_install()
   uses cmpxcg() to set fud->fc, which prevents races between clone/mount
   or clone/clone.  This makes the logic simpler

 - Drop fc->dev_count.  This is only used to check in release if the device
   is the last clone, but checking list_empty(&fc->devices) is equivalent
   after removing the released device from the list.  Removing the fuse_dev
   before calling fuse_abort_conn() is okay, since the processing and io
   lists are now empty for this device.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/dev.c
fs/fuse/fuse_i.h
fs/fuse/inode.c

index 3d96e7a16103cd7072a1e9ce502f3a047fdf7e81..5dda7080f4a909740cd3dbf804a5260e7493b512 100644 (file)
@@ -2547,6 +2547,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)
                struct fuse_pqueue *fpq = &fud->pq;
                LIST_HEAD(to_end);
                unsigned int i;
+               bool last;
 
                spin_lock(&fpq->lock);
                WARN_ON(!list_empty(&fpq->io));
@@ -2556,14 +2557,16 @@ int fuse_dev_release(struct inode *inode, struct file *file)
 
                fuse_dev_end_requests(&to_end);
 
+               spin_lock(&fc->lock);
+               list_del(&fud->entry);
                /* Are we the last open device? */
-               if (atomic_dec_and_test(&fc->dev_count)) {
+               last = list_empty(&fc->devices);
+               spin_unlock(&fc->lock);
+
+               if (last) {
                        WARN_ON(fc->iq.fasync != NULL);
                        fuse_abort_conn(fc);
                }
-               spin_lock(&fc->lock);
-               list_del(&fud->entry);
-               spin_unlock(&fc->lock);
                fuse_conn_put(fc);
        }
        fuse_dev_put(fud);
@@ -2582,23 +2585,10 @@ static int fuse_dev_fasync(int fd, struct file *file, int on)
        return fasync_helper(fd, file, on, &fud->fc->iq.fasync);
 }
 
-static int fuse_device_clone(struct fuse_conn *fc, struct file *new)
-{
-       struct fuse_dev *new_fud = fuse_file_to_fud(new);
-
-       if (fuse_dev_fc_get(new_fud))
-               return -EINVAL;
-
-       fuse_dev_install(new_fud, fc);
-       atomic_inc(&fc->dev_count);
-
-       return 0;
-}
-
 static long fuse_dev_ioctl_clone(struct file *file, __u32 __user *argp)
 {
        int oldfd;
-       struct fuse_dev *fud;
+       struct fuse_dev *fud, *new_fud;
 
        if (get_user(oldfd, argp))
                return -EFAULT;
@@ -2618,8 +2608,13 @@ static long fuse_dev_ioctl_clone(struct file *file, __u32 __user *argp)
        if (IS_ERR(fud))
                return PTR_ERR(fud);
 
-       guard(mutex)(&fuse_mutex);
-       return fuse_device_clone(fud->fc, file);
+       new_fud = fuse_file_to_fud(file);
+       if (fuse_dev_fc_get(new_fud))
+               return -EINVAL;
+
+       fuse_dev_install(new_fud, fud->fc);
+
+       return 0;
 }
 
 static long fuse_dev_ioctl_backing_open(struct file *file,
index 6fafda68f4f1dcb217fcf49d450e87449b889538..17423d4e3cfa673b1377150fd469d20e168f952d 100644 (file)
@@ -650,9 +650,6 @@ struct fuse_conn {
        /** Refcount */
        refcount_t count;
 
-       /** Number of fuse_dev's */
-       atomic_t dev_count;
-
        /** Current epoch for up-to-date dentries */
        atomic_t epoch;
 
index 173c1ee11550d042d2a81bc2d3fb56b47fc5d3b5..ffe068b13a1e1a6bede149d356609360fcb6157f 100644 (file)
@@ -1001,7 +1001,6 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
        spin_lock_init(&fc->bg_lock);
        init_rwsem(&fc->killsb);
        refcount_set(&fc->count, 1);
-       atomic_set(&fc->dev_count, 1);
        atomic_set(&fc->epoch, 1);
        INIT_WORK(&fc->epoch_work, fuse_epoch_work);
        init_waitqueue_head(&fc->blocked_waitq);