Commit 4ae404af authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

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: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent d42eb23b
Loading
Loading
Loading
Loading
+15 −20
Original line number Diff line number Diff line
@@ -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,
+0 −3
Original line number Diff line number Diff line
@@ -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;

+0 −1
Original line number Diff line number Diff line
@@ -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);