diff options
| author | Miklos Szeredi <mszeredi@redhat.com> | 2026-03-12 12:19:10 +0100 |
|---|---|---|
| committer | Miklos Szeredi <mszeredi@redhat.com> | 2026-04-02 20:52:59 +0200 |
| commit | 4ae404afd92e36be378eb120a2dc13031cdac7a6 (patch) | |
| tree | d5a4f56cf4656960d795bcfb9b0b99aafda50171 /fs/fuse/dev.c | |
| parent | d42eb23b2ef9fbe66ea7fd4b3153c3244818d47c (diff) | |
| download | linux-next-4ae404afd92e36be378eb120a2dc13031cdac7a6.tar.gz linux-next-4ae404afd92e36be378eb120a2dc13031cdac7a6.zip | |
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>
Diffstat (limited to 'fs/fuse/dev.c')
| -rw-r--r-- | fs/fuse/dev.c | 35 |
1 files changed, 15 insertions, 20 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, |
