summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2026-03-12 14:19:10 +0300
committerMiklos Szeredi <mszeredi@redhat.com>2026-04-02 21:52:59 +0300
commit4ae404afd92e36be378eb120a2dc13031cdac7a6 (patch)
treed5a4f56cf4656960d795bcfb9b0b99aafda50171
parentd42eb23b2ef9fbe66ea7fd4b3153c3244818d47c (diff)
downloadlinux-4ae404afd92e36be378eb120a2dc13031cdac7a6.tar.xz
fuse: clean up device cloning
- 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>
-rw-r--r--fs/fuse/dev.c35
-rw-r--r--fs/fuse/fuse_i.h3
-rw-r--r--fs/fuse/inode.c1
3 files changed, 15 insertions, 24 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 3d96e7a16103..5dda7080f4a9 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -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,
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 6fafda68f4f1..17423d4e3cfa 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -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;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 173c1ee11550..ffe068b13a1e 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -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);