mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 16:54:21 +00:00 
			
		
		
		
	fuse: introduce per-instance fuse_dev structure
Allow fuse device clones to refer to be distinguished. This patch just adds the infrastructure by associating a separate "struct fuse_dev" with each clone. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Reviewed-by: Ashish Samant <ashish.samant@oracle.com>
This commit is contained in:
		
							parent
							
								
									00c570f4ba
								
							
						
					
					
						commit
						cc080e9e9b
					
				
					 4 changed files with 114 additions and 34 deletions
				
			
		|  | @ -489,6 +489,7 @@ static void cuse_fc_release(struct fuse_conn *fc) | |||
|  */ | ||||
| static int cuse_channel_open(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	struct fuse_dev *fud; | ||||
| 	struct cuse_conn *cc; | ||||
| 	int rc; | ||||
| 
 | ||||
|  | @ -499,16 +500,22 @@ static int cuse_channel_open(struct inode *inode, struct file *file) | |||
| 
 | ||||
| 	fuse_conn_init(&cc->fc); | ||||
| 
 | ||||
| 	fud = fuse_dev_alloc(&cc->fc); | ||||
| 	if (!fud) { | ||||
| 		kfree(cc); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&cc->list); | ||||
| 	cc->fc.release = cuse_fc_release; | ||||
| 
 | ||||
| 	cc->fc.initialized = 1; | ||||
| 	rc = cuse_send_init(cc); | ||||
| 	if (rc) { | ||||
| 		fuse_conn_put(&cc->fc); | ||||
| 		fuse_dev_free(fud); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	file->private_data = &cc->fc;	/* channel owns base reference to cc */ | ||||
| 	file->private_data = fud; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -526,7 +533,8 @@ static int cuse_channel_open(struct inode *inode, struct file *file) | |||
|  */ | ||||
| static int cuse_channel_release(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	struct cuse_conn *cc = fc_to_cc(file->private_data); | ||||
| 	struct fuse_dev *fud = file->private_data; | ||||
| 	struct cuse_conn *cc = fc_to_cc(fud->fc); | ||||
| 	int rc; | ||||
| 
 | ||||
| 	/* remove from the conntbl, no more access from this point on */ | ||||
|  |  | |||
|  | @ -25,13 +25,13 @@ MODULE_ALIAS("devname:fuse"); | |||
| 
 | ||||
| static struct kmem_cache *fuse_req_cachep; | ||||
| 
 | ||||
| static struct fuse_conn *fuse_get_conn(struct file *file) | ||||
| static 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. | ||||
| 	 */ | ||||
| 	return file->private_data; | ||||
| 	return ACCESS_ONCE(file->private_data); | ||||
| } | ||||
| 
 | ||||
| static void fuse_request_init(struct fuse_req *req, struct page **pages, | ||||
|  | @ -1348,8 +1348,9 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to) | |||
| { | ||||
| 	struct fuse_copy_state cs; | ||||
| 	struct file *file = iocb->ki_filp; | ||||
| 	struct fuse_conn *fc = fuse_get_conn(file); | ||||
| 	if (!fc) | ||||
| 	struct fuse_dev *fud = fuse_get_dev(file); | ||||
| 
 | ||||
| 	if (!fud) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	if (!iter_is_iovec(to)) | ||||
|  | @ -1357,7 +1358,7 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to) | |||
| 
 | ||||
| 	fuse_copy_init(&cs, 1, to); | ||||
| 
 | ||||
| 	return fuse_dev_do_read(fc, file, &cs, iov_iter_count(to)); | ||||
| 	return fuse_dev_do_read(fud->fc, file, &cs, iov_iter_count(to)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, | ||||
|  | @ -1369,8 +1370,9 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, | |||
| 	int do_wakeup = 0; | ||||
| 	struct pipe_buffer *bufs; | ||||
| 	struct fuse_copy_state cs; | ||||
| 	struct fuse_conn *fc = fuse_get_conn(in); | ||||
| 	if (!fc) | ||||
| 	struct fuse_dev *fud = fuse_get_dev(in); | ||||
| 
 | ||||
| 	if (!fud) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL); | ||||
|  | @ -1380,7 +1382,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, | |||
| 	fuse_copy_init(&cs, 1, NULL); | ||||
| 	cs.pipebufs = bufs; | ||||
| 	cs.pipe = pipe; | ||||
| 	ret = fuse_dev_do_read(fc, in, &cs, len); | ||||
| 	ret = fuse_dev_do_read(fud->fc, in, &cs, len); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
|  | @ -1954,8 +1956,9 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, | |||
| static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) | ||||
| { | ||||
| 	struct fuse_copy_state cs; | ||||
| 	struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp); | ||||
| 	if (!fc) | ||||
| 	struct fuse_dev *fud = fuse_get_dev(iocb->ki_filp); | ||||
| 
 | ||||
| 	if (!fud) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	if (!iter_is_iovec(from)) | ||||
|  | @ -1963,7 +1966,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) | |||
| 
 | ||||
| 	fuse_copy_init(&cs, 0, from); | ||||
| 
 | ||||
| 	return fuse_dev_do_write(fc, &cs, iov_iter_count(from)); | ||||
| 	return fuse_dev_do_write(fud->fc, &cs, iov_iter_count(from)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, | ||||
|  | @ -1974,12 +1977,12 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, | |||
| 	unsigned idx; | ||||
| 	struct pipe_buffer *bufs; | ||||
| 	struct fuse_copy_state cs; | ||||
| 	struct fuse_conn *fc; | ||||
| 	struct fuse_dev *fud; | ||||
| 	size_t rem; | ||||
| 	ssize_t ret; | ||||
| 
 | ||||
| 	fc = fuse_get_conn(out); | ||||
| 	if (!fc) | ||||
| 	fud = fuse_get_dev(out); | ||||
| 	if (!fud) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL); | ||||
|  | @ -2034,7 +2037,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, | |||
| 	if (flags & SPLICE_F_MOVE) | ||||
| 		cs.move_pages = 1; | ||||
| 
 | ||||
| 	ret = fuse_dev_do_write(fc, &cs, len); | ||||
| 	ret = fuse_dev_do_write(fud->fc, &cs, len); | ||||
| 
 | ||||
| 	for (idx = 0; idx < nbuf; idx++) { | ||||
| 		struct pipe_buffer *buf = &bufs[idx]; | ||||
|  | @ -2049,11 +2052,12 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait) | |||
| { | ||||
| 	unsigned mask = POLLOUT | POLLWRNORM; | ||||
| 	struct fuse_iqueue *fiq; | ||||
| 	struct fuse_conn *fc = fuse_get_conn(file); | ||||
| 	if (!fc) | ||||
| 	struct fuse_dev *fud = fuse_get_dev(file); | ||||
| 
 | ||||
| 	if (!fud) | ||||
| 		return POLLERR; | ||||
| 
 | ||||
| 	fiq = &fc->iq; | ||||
| 	fiq = &fud->fc->iq; | ||||
| 	poll_wait(file, &fiq->waitq, wait); | ||||
| 
 | ||||
| 	spin_lock(&fiq->waitq.lock); | ||||
|  | @ -2175,12 +2179,15 @@ EXPORT_SYMBOL_GPL(fuse_abort_conn); | |||
| 
 | ||||
| int fuse_dev_release(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	struct fuse_conn *fc = fuse_get_conn(file); | ||||
| 	if (fc) { | ||||
| 	struct fuse_dev *fud = fuse_get_dev(file); | ||||
| 
 | ||||
| 	if (fud) { | ||||
| 		struct fuse_conn *fc = fud->fc; | ||||
| 
 | ||||
| 		WARN_ON(!list_empty(&fc->pq.io)); | ||||
| 		WARN_ON(fc->iq.fasync != NULL); | ||||
| 		fuse_abort_conn(fc); | ||||
| 		fuse_conn_put(fc); | ||||
| 		fuse_dev_free(fud); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -2189,20 +2196,27 @@ EXPORT_SYMBOL_GPL(fuse_dev_release); | |||
| 
 | ||||
| static int fuse_dev_fasync(int fd, struct file *file, int on) | ||||
| { | ||||
| 	struct fuse_conn *fc = fuse_get_conn(file); | ||||
| 	if (!fc) | ||||
| 	struct fuse_dev *fud = fuse_get_dev(file); | ||||
| 
 | ||||
| 	if (!fud) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	/* No locking - fasync_helper does its own locking */ | ||||
| 	return fasync_helper(fd, file, on, &fc->iq.fasync); | ||||
| 	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 *fud; | ||||
| 
 | ||||
| 	if (new->private_data) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	new->private_data = fuse_conn_get(fc); | ||||
| 	fud = fuse_dev_alloc(fc); | ||||
| 	if (!fud) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	new->private_data = fud; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -2221,11 +2235,11 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd, | |||
| 
 | ||||
| 			err = -EINVAL; | ||||
| 			if (old) { | ||||
| 				struct fuse_conn *fc = fuse_get_conn(old); | ||||
| 				struct fuse_dev *fud = fuse_get_dev(old); | ||||
| 
 | ||||
| 				if (fc) { | ||||
| 				if (fud) { | ||||
| 					mutex_lock(&fuse_mutex); | ||||
| 					err = fuse_device_clone(fc, file); | ||||
| 					err = fuse_device_clone(fud->fc, file); | ||||
| 					mutex_unlock(&fuse_mutex); | ||||
| 				} | ||||
| 				fput(old); | ||||
|  |  | |||
|  | @ -417,6 +417,17 @@ struct fuse_pqueue { | |||
| 	struct list_head io; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Fuse device instance | ||||
|  */ | ||||
| struct fuse_dev { | ||||
| 	/** Fuse connection for this device */ | ||||
| 	struct fuse_conn *fc; | ||||
| 
 | ||||
| 	/** list entry on fc->devices */ | ||||
| 	struct list_head entry; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * A Fuse connection. | ||||
|  * | ||||
|  | @ -629,6 +640,9 @@ struct fuse_conn { | |||
| 
 | ||||
| 	/** Read/write semaphore to hold when accessing sb. */ | ||||
| 	struct rw_semaphore killsb; | ||||
| 
 | ||||
| 	/** List of device instances belonging to this connection */ | ||||
| 	struct list_head devices; | ||||
| }; | ||||
| 
 | ||||
| static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) | ||||
|  | @ -841,6 +855,9 @@ void fuse_conn_init(struct fuse_conn *fc); | |||
|  */ | ||||
| void fuse_conn_put(struct fuse_conn *fc); | ||||
| 
 | ||||
| struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc); | ||||
| void fuse_dev_free(struct fuse_dev *fud); | ||||
| 
 | ||||
| /**
 | ||||
|  * Add connection to control filesystem | ||||
|  */ | ||||
|  |  | |||
|  | @ -598,6 +598,7 @@ void fuse_conn_init(struct fuse_conn *fc) | |||
| 	fuse_pqueue_init(&fc->pq); | ||||
| 	INIT_LIST_HEAD(&fc->bg_queue); | ||||
| 	INIT_LIST_HEAD(&fc->entry); | ||||
| 	INIT_LIST_HEAD(&fc->devices); | ||||
| 	atomic_set(&fc->num_waiting, 0); | ||||
| 	fc->max_background = FUSE_DEFAULT_MAX_BACKGROUND; | ||||
| 	fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD; | ||||
|  | @ -945,6 +946,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) | |||
| 
 | ||||
| static void fuse_free_conn(struct fuse_conn *fc) | ||||
| { | ||||
| 	WARN_ON(!list_empty(&fc->devices)); | ||||
| 	kfree_rcu(fc, rcu); | ||||
| } | ||||
| 
 | ||||
|  | @ -990,8 +992,41 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc) | ||||
| { | ||||
| 	struct fuse_dev *fud; | ||||
| 
 | ||||
| 	fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL); | ||||
| 	if (fud) { | ||||
| 		fud->fc = fuse_conn_get(fc); | ||||
| 
 | ||||
| 		spin_lock(&fc->lock); | ||||
| 		list_add_tail(&fud->entry, &fc->devices); | ||||
| 		spin_unlock(&fc->lock); | ||||
| 	} | ||||
| 
 | ||||
| 	return fud; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(fuse_dev_alloc); | ||||
| 
 | ||||
| void fuse_dev_free(struct fuse_dev *fud) | ||||
| { | ||||
| 	struct fuse_conn *fc = fud->fc; | ||||
| 
 | ||||
| 	if (fc) { | ||||
| 		spin_lock(&fc->lock); | ||||
| 		list_del(&fud->entry); | ||||
| 		spin_unlock(&fc->lock); | ||||
| 
 | ||||
| 		fuse_conn_put(fc); | ||||
| 	} | ||||
| 	kfree(fud); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(fuse_dev_free); | ||||
| 
 | ||||
| static int fuse_fill_super(struct super_block *sb, void *data, int silent) | ||||
| { | ||||
| 	struct fuse_dev *fud; | ||||
| 	struct fuse_conn *fc; | ||||
| 	struct inode *root; | ||||
| 	struct fuse_mount_data d; | ||||
|  | @ -1043,11 +1078,15 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
| 	fuse_conn_init(fc); | ||||
| 	fc->release = fuse_free_conn; | ||||
| 
 | ||||
| 	fud = fuse_dev_alloc(fc); | ||||
| 	if (!fud) | ||||
| 		goto err_put_conn; | ||||
| 
 | ||||
| 	fc->dev = sb->s_dev; | ||||
| 	fc->sb = sb; | ||||
| 	err = fuse_bdi_init(fc, sb); | ||||
| 	if (err) | ||||
| 		goto err_put_conn; | ||||
| 		goto err_dev_free; | ||||
| 
 | ||||
| 	sb->s_bdi = &fc->bdi; | ||||
| 
 | ||||
|  | @ -1068,7 +1107,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
| 	root = fuse_get_root_inode(sb, d.rootmode); | ||||
| 	root_dentry = d_make_root(root); | ||||
| 	if (!root_dentry) | ||||
| 		goto err_put_conn; | ||||
| 		goto err_dev_free; | ||||
| 	/* only now - we want root dentry with NULL ->d_op */ | ||||
| 	sb->s_d_op = &fuse_dentry_operations; | ||||
| 
 | ||||
|  | @ -1094,7 +1133,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
| 
 | ||||
| 	list_add_tail(&fc->entry, &fuse_conn_list); | ||||
| 	sb->s_root = root_dentry; | ||||
| 	file->private_data = fuse_conn_get(fc); | ||||
| 	file->private_data = fud; | ||||
| 	mutex_unlock(&fuse_mutex); | ||||
| 	/*
 | ||||
| 	 * atomic_dec_and_test() in fput() provides the necessary | ||||
|  | @ -1113,6 +1152,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
| 	fuse_request_free(init_req); | ||||
|  err_put_root: | ||||
| 	dput(root_dentry); | ||||
|  err_dev_free: | ||||
| 	fuse_dev_free(fud); | ||||
|  err_put_conn: | ||||
| 	fuse_bdi_destroy(fc); | ||||
| 	fuse_conn_put(fc); | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Miklos Szeredi
						Miklos Szeredi