Commit fcee216b authored by Max Reitz's avatar Max Reitz Committed by Miklos Szeredi
Browse files

fuse: split fuse_mount off of fuse_conn



We want to allow submounts for the same fuse_conn, but with different
superblocks so that each of the submounts has its own device ID.  To do
so, we need to split all mount-specific information off of fuse_conn
into a new fuse_mount structure, so that multiple mounts can share a
single fuse_conn.

We need to take care only to perform connection-level actions once (i.e.
when the fuse_conn and thus the first fuse_mount are established, or
when the last fuse_mount and thus the fuse_conn are destroyed).  For
example, fuse_sb_destroy() must invoke fuse_send_destroy() until the
last superblock is released.

To do so, we keep track of which fuse_mount is the root mount and
perform all fuse_conn-level actions only when this fuse_mount is
involved.

Signed-off-by: default avatarMax Reitz <mreitz@redhat.com>
Reviewed-by: default avatarStefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 8f622e94
Loading
Loading
Loading
Loading
+15 −5
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
{
	unsigned val;
	struct fuse_conn *fc;
	struct fuse_mount *fm;
	ssize_t ret;

	ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
@@ -174,18 +175,27 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
	if (!fc)
		goto out;

	down_read(&fc->killsb);
	spin_lock(&fc->bg_lock);
	fc->congestion_threshold = val;
	if (fc->sb) {

	/*
	 * Get any fuse_mount belonging to this fuse_conn; s_bdi is
	 * shared between all of them
	 */

	if (!list_empty(&fc->mounts)) {
		fm = list_first_entry(&fc->mounts, struct fuse_mount, fc_entry);
		if (fc->num_background < fc->congestion_threshold) {
			clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
			clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
			clear_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
			clear_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
		} else {
			set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
			set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
			set_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
			set_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
		}
	}
	spin_unlock(&fc->bg_lock);
	up_read(&fc->killsb);
	fuse_conn_put(fc);
out:
	return ret;
+12 −9
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@

struct cuse_conn {
	struct list_head	list;	/* linked on cuse_conntbl */
	struct fuse_mount	fm;	/* Dummy mount referencing fc */
	struct fuse_conn	fc;	/* fuse connection */
	struct cdev		*cdev;	/* associated character device */
	struct device		*dev;	/* device representing @cdev */
@@ -134,7 +135,7 @@ static int cuse_open(struct inode *inode, struct file *file)
	 * Generic permission check is already done against the chrdev
	 * file, proceed to open.
	 */
	rc = fuse_do_open(&cc->fc, 0, file, 0);
	rc = fuse_do_open(&cc->fm, 0, file, 0);
	if (rc)
		fuse_conn_put(&cc->fc);
	return rc;
@@ -143,10 +144,10 @@ static int cuse_open(struct inode *inode, struct file *file)
static int cuse_release(struct inode *inode, struct file *file)
{
	struct fuse_file *ff = file->private_data;
	struct fuse_conn *fc = ff->fc;
	struct fuse_mount *fm = ff->fm;

	fuse_sync_release(NULL, ff, file->f_flags);
	fuse_conn_put(fc);
	fuse_conn_put(fm->fc);

	return 0;
}
@@ -155,7 +156,7 @@ static long cuse_file_ioctl(struct file *file, unsigned int cmd,
			    unsigned long arg)
{
	struct fuse_file *ff = file->private_data;
	struct cuse_conn *cc = fc_to_cc(ff->fc);
	struct cuse_conn *cc = fc_to_cc(ff->fm->fc);
	unsigned int flags = 0;

	if (cc->unrestricted_ioctl)
@@ -168,7 +169,7 @@ static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd,
				   unsigned long arg)
{
	struct fuse_file *ff = file->private_data;
	struct cuse_conn *cc = fc_to_cc(ff->fc);
	struct cuse_conn *cc = fc_to_cc(ff->fm->fc);
	unsigned int flags = FUSE_IOCTL_COMPAT;

	if (cc->unrestricted_ioctl)
@@ -313,9 +314,10 @@ struct cuse_init_args {
 * required data structures for it.  Please read the comment at the
 * top of this file for high level overview.
 */
static void cuse_process_init_reply(struct fuse_conn *fc,
static void cuse_process_init_reply(struct fuse_mount *fm,
				    struct fuse_args *args, int error)
{
	struct fuse_conn *fc = fm->fc;
	struct cuse_init_args *ia = container_of(args, typeof(*ia), ap.args);
	struct fuse_args_pages *ap = &ia->ap;
	struct cuse_conn *cc = fc_to_cc(fc), *pos;
@@ -424,7 +426,7 @@ static int cuse_send_init(struct cuse_conn *cc)
{
	int rc;
	struct page *page;
	struct fuse_conn *fc = &cc->fc;
	struct fuse_mount *fm = &cc->fm;
	struct cuse_init_args *ia;
	struct fuse_args_pages *ap;

@@ -460,7 +462,7 @@ static int cuse_send_init(struct cuse_conn *cc)
	ia->desc.length = ap->args.out_args[1].size;
	ap->args.end = cuse_process_init_reply;

	rc = fuse_simple_background(fc, &ap->args, GFP_KERNEL);
	rc = fuse_simple_background(fm, &ap->args, GFP_KERNEL);
	if (rc) {
		kfree(ia);
err_free_page:
@@ -506,7 +508,8 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
	 * Limit the cuse channel to requests that can
	 * be represented in file->f_cred->user_ns.
	 */
	fuse_conn_init(&cc->fc, file->f_cred->user_ns, &fuse_dev_fiq_ops, NULL);
	fuse_conn_init(&cc->fc, &cc->fm, file->f_cred->user_ns,
		       &fuse_dev_fiq_ops, NULL);

	fud = fuse_dev_alloc_install(&cc->fc);
	if (!fud) {
+5 −5
Original line number Diff line number Diff line
@@ -182,8 +182,8 @@ static int fuse_setup_one_mapping(struct inode *inode, unsigned long start_idx,
				  struct fuse_dax_mapping *dmap, bool writable,
				  bool upgrade)
{
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_conn_dax *fcd = fc->dax;
	struct fuse_mount *fm = get_fuse_mount(inode);
	struct fuse_conn_dax *fcd = fm->fc->dax;
	struct fuse_inode *fi = get_fuse_inode(inode);
	struct fuse_setupmapping_in inarg;
	loff_t offset = start_idx << FUSE_DAX_SHIFT;
@@ -206,7 +206,7 @@ static int fuse_setup_one_mapping(struct inode *inode, unsigned long start_idx,
	args.in_numargs = 1;
	args.in_args[0].size = sizeof(inarg);
	args.in_args[0].value = &inarg;
	err = fuse_simple_request(fc, &args);
	err = fuse_simple_request(fm, &args);
	if (err < 0)
		return err;
	dmap->writable = writable;
@@ -234,7 +234,7 @@ static int fuse_send_removemapping(struct inode *inode,
				   struct fuse_removemapping_one *remove_one)
{
	struct fuse_inode *fi = get_fuse_inode(inode);
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_mount *fm = get_fuse_mount(inode);
	FUSE_ARGS(args);

	args.opcode = FUSE_REMOVEMAPPING;
@@ -244,7 +244,7 @@ static int fuse_send_removemapping(struct inode *inode,
	args.in_args[0].value = inargp;
	args.in_args[1].size = inargp->count * sizeof(*remove_one);
	args.in_args[1].value = remove_one;
	return fuse_simple_request(fc, &args);
	return fuse_simple_request(fm, &args);
}

static int dmap_removemapping_list(struct inode *inode, unsigned int num,
+50 −56
Original line number Diff line number Diff line
@@ -40,21 +40,21 @@ static struct fuse_dev *fuse_get_dev(struct file *file)
	return READ_ONCE(file->private_data);
}

static void fuse_request_init(struct fuse_conn *fc, struct fuse_req *req)
static void fuse_request_init(struct fuse_mount *fm, struct fuse_req *req)
{
	INIT_LIST_HEAD(&req->list);
	INIT_LIST_HEAD(&req->intr_entry);
	init_waitqueue_head(&req->waitq);
	refcount_set(&req->count, 1);
	__set_bit(FR_PENDING, &req->flags);
	req->fc = fc;
	req->fm = fm;
}

static struct fuse_req *fuse_request_alloc(struct fuse_conn *fc, gfp_t flags)
static struct fuse_req *fuse_request_alloc(struct fuse_mount *fm, gfp_t flags)
{
	struct fuse_req *req = kmem_cache_zalloc(fuse_req_cachep, flags);
	if (req)
		fuse_request_init(fc, req);
		fuse_request_init(fm, req);

	return req;
}
@@ -103,8 +103,9 @@ static void fuse_drop_waiting(struct fuse_conn *fc)

static void fuse_put_request(struct fuse_req *req);

static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
static struct fuse_req *fuse_get_req(struct fuse_mount *fm, bool for_background)
{
	struct fuse_conn *fc = fm->fc;
	struct fuse_req *req;
	int err;
	atomic_inc(&fc->num_waiting);
@@ -126,7 +127,7 @@ static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
	if (fc->conn_error)
		goto out;

	req = fuse_request_alloc(fc, GFP_KERNEL);
	req = fuse_request_alloc(fm, GFP_KERNEL);
	err = -ENOMEM;
	if (!req) {
		if (for_background)
@@ -156,7 +157,7 @@ static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)

static void fuse_put_request(struct fuse_req *req)
{
	struct fuse_conn *fc = req->fc;
	struct fuse_conn *fc = req->fm->fc;

	if (refcount_dec_and_test(&req->count)) {
		if (test_bit(FR_BACKGROUND, &req->flags)) {
@@ -278,7 +279,8 @@ static void flush_bg_queue(struct fuse_conn *fc)
 */
void fuse_request_end(struct fuse_req *req)
{
	struct fuse_conn *fc = req->fc;
	struct fuse_mount *fm = req->fm;
	struct fuse_conn *fc = fm->fc;
	struct fuse_iqueue *fiq = &fc->iq;

	if (test_and_set_bit(FR_FINISHED, &req->flags))
@@ -313,9 +315,9 @@ void fuse_request_end(struct fuse_req *req)
				wake_up(&fc->blocked_waitq);
		}

		if (fc->num_background == fc->congestion_threshold && fc->sb) {
			clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
			clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
		if (fc->num_background == fc->congestion_threshold && fm->sb) {
			clear_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
			clear_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
		}
		fc->num_background--;
		fc->active_background--;
@@ -327,7 +329,7 @@ void fuse_request_end(struct fuse_req *req)
	}

	if (test_bit(FR_ASYNC, &req->flags))
		req->args->end(fc, req->args, req->out.h.error);
		req->args->end(fm, req->args, req->out.h.error);
put_request:
	fuse_put_request(req);
}
@@ -335,7 +337,7 @@ EXPORT_SYMBOL_GPL(fuse_request_end);

static int queue_interrupt(struct fuse_req *req)
{
	struct fuse_iqueue *fiq = &req->fc->iq;
	struct fuse_iqueue *fiq = &req->fm->fc->iq;

	spin_lock(&fiq->lock);
	/* Check for we've sent request to interrupt this req */
@@ -365,7 +367,7 @@ static int queue_interrupt(struct fuse_req *req)

static void request_wait_answer(struct fuse_req *req)
{
	struct fuse_conn *fc = req->fc;
	struct fuse_conn *fc = req->fm->fc;
	struct fuse_iqueue *fiq = &fc->iq;
	int err;

@@ -411,7 +413,7 @@ static void request_wait_answer(struct fuse_req *req)

static void __fuse_request_send(struct fuse_req *req)
{
	struct fuse_iqueue *fiq = &req->fc->iq;
	struct fuse_iqueue *fiq = &req->fm->fc->iq;

	BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
	spin_lock(&fiq->lock);
@@ -466,7 +468,7 @@ static void fuse_adjust_compat(struct fuse_conn *fc, struct fuse_args *args)

static void fuse_force_creds(struct fuse_req *req)
{
	struct fuse_conn *fc = req->fc;
	struct fuse_conn *fc = req->fm->fc;

	req->in.h.uid = from_kuid_munged(fc->user_ns, current_fsuid());
	req->in.h.gid = from_kgid_munged(fc->user_ns, current_fsgid());
@@ -482,14 +484,15 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
		__set_bit(FR_ASYNC, &req->flags);
}

ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args)
{
	struct fuse_conn *fc = fm->fc;
	struct fuse_req *req;
	ssize_t ret;

	if (args->force) {
		atomic_inc(&fc->num_waiting);
		req = fuse_request_alloc(fc, GFP_KERNEL | __GFP_NOFAIL);
		req = fuse_request_alloc(fm, GFP_KERNEL | __GFP_NOFAIL);

		if (!args->nocreds)
			fuse_force_creds(req);
@@ -498,7 +501,7 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
		__set_bit(FR_FORCE, &req->flags);
	} else {
		WARN_ON(args->nocreds);
		req = fuse_get_req(fc, false);
		req = fuse_get_req(fm, false);
		if (IS_ERR(req))
			return PTR_ERR(req);
	}
@@ -522,7 +525,8 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)

static bool fuse_request_queue_background(struct fuse_req *req)
{
	struct fuse_conn *fc = req->fc;
	struct fuse_mount *fm = req->fm;
	struct fuse_conn *fc = fm->fc;
	bool queued = false;

	WARN_ON(!test_bit(FR_BACKGROUND, &req->flags));
@@ -536,9 +540,9 @@ static bool fuse_request_queue_background(struct fuse_req *req)
		fc->num_background++;
		if (fc->num_background == fc->max_background)
			fc->blocked = 1;
		if (fc->num_background == fc->congestion_threshold && fc->sb) {
			set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
			set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
		if (fc->num_background == fc->congestion_threshold && fm->sb) {
			set_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
			set_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
		}
		list_add_tail(&req->list, &fc->bg_queue);
		flush_bg_queue(fc);
@@ -549,20 +553,20 @@ static bool fuse_request_queue_background(struct fuse_req *req)
	return queued;
}

int fuse_simple_background(struct fuse_conn *fc, struct fuse_args *args,
int fuse_simple_background(struct fuse_mount *fm, struct fuse_args *args,
			    gfp_t gfp_flags)
{
	struct fuse_req *req;

	if (args->force) {
		WARN_ON(!args->nocreds);
		req = fuse_request_alloc(fc, gfp_flags);
		req = fuse_request_alloc(fm, gfp_flags);
		if (!req)
			return -ENOMEM;
		__set_bit(FR_BACKGROUND, &req->flags);
	} else {
		WARN_ON(args->nocreds);
		req = fuse_get_req(fc, true);
		req = fuse_get_req(fm, true);
		if (IS_ERR(req))
			return PTR_ERR(req);
	}
@@ -578,14 +582,14 @@ int fuse_simple_background(struct fuse_conn *fc, struct fuse_args *args,
}
EXPORT_SYMBOL_GPL(fuse_simple_background);

static int fuse_simple_notify_reply(struct fuse_conn *fc,
static int fuse_simple_notify_reply(struct fuse_mount *fm,
				    struct fuse_args *args, u64 unique)
{
	struct fuse_req *req;
	struct fuse_iqueue *fiq = &fc->iq;
	struct fuse_iqueue *fiq = &fm->fc->iq;
	int err = 0;

	req = fuse_get_req(fc, false);
	req = fuse_get_req(fm, false);
	if (IS_ERR(req))
		return PTR_ERR(req);

@@ -1433,11 +1437,8 @@ static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size,
	fuse_copy_finish(cs);

	down_read(&fc->killsb);
	err = -ENOENT;
	if (fc->sb) {
		err = fuse_reverse_inval_inode(fc->sb, outarg.ino,
	err = fuse_reverse_inval_inode(fc, outarg.ino,
				       outarg.off, outarg.len);
	}
	up_read(&fc->killsb);
	return err;

@@ -1483,9 +1484,7 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
	buf[outarg.namelen] = 0;

	down_read(&fc->killsb);
	err = -ENOENT;
	if (fc->sb)
		err = fuse_reverse_inval_entry(fc->sb, outarg.parent, 0, &name);
	err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name);
	up_read(&fc->killsb);
	kfree(buf);
	return err;
@@ -1533,10 +1532,7 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
	buf[outarg.namelen] = 0;

	down_read(&fc->killsb);
	err = -ENOENT;
	if (fc->sb)
		err = fuse_reverse_inval_entry(fc->sb, outarg.parent,
					       outarg.child, &name);
	err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name);
	up_read(&fc->killsb);
	kfree(buf);
	return err;
@@ -1578,10 +1574,7 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
	down_read(&fc->killsb);

	err = -ENOENT;
	if (!fc->sb)
		goto out_up_killsb;

	inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
	inode = fuse_ilookup(fc, nodeid,  NULL);
	if (!inode)
		goto out_up_killsb;

@@ -1638,7 +1631,7 @@ struct fuse_retrieve_args {
	struct fuse_notify_retrieve_in inarg;
};

static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_args *args,
static void fuse_retrieve_end(struct fuse_mount *fm, struct fuse_args *args,
			      int error)
{
	struct fuse_retrieve_args *ra =
@@ -1648,7 +1641,7 @@ static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_args *args,
	kfree(ra);
}

static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
			 struct fuse_notify_retrieve_out *outarg)
{
	int err;
@@ -1659,6 +1652,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
	unsigned int offset;
	size_t total_len = 0;
	unsigned int num_pages;
	struct fuse_conn *fc = fm->fc;
	struct fuse_retrieve_args *ra;
	size_t args_size = sizeof(*ra);
	struct fuse_args_pages *ap;
@@ -1720,9 +1714,9 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
	args->in_args[0].value = &ra->inarg;
	args->in_args[1].size = total_len;

	err = fuse_simple_notify_reply(fc, args, outarg->notify_unique);
	err = fuse_simple_notify_reply(fm, args, outarg->notify_unique);
	if (err)
		fuse_retrieve_end(fc, args, err);
		fuse_retrieve_end(fm, args, err);

	return err;
}
@@ -1731,7 +1725,9 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
				struct fuse_copy_state *cs)
{
	struct fuse_notify_retrieve_out outarg;
	struct fuse_mount *fm;
	struct inode *inode;
	u64 nodeid;
	int err;

	err = -EINVAL;
@@ -1746,15 +1742,13 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,

	down_read(&fc->killsb);
	err = -ENOENT;
	if (fc->sb) {
		u64 nodeid = outarg.nodeid;
	nodeid = outarg.nodeid;

		inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
	inode = fuse_ilookup(fc, nodeid, &fm);
	if (inode) {
			err = fuse_retrieve(fc, inode, &outarg);
		err = fuse_retrieve(fm, inode, &outarg);
		iput(inode);
	}
	}
	up_read(&fc->killsb);

	return err;
+56 −54
Original line number Diff line number Diff line
@@ -196,7 +196,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
{
	struct inode *inode;
	struct dentry *parent;
	struct fuse_conn *fc;
	struct fuse_mount *fm;
	struct fuse_inode *fi;
	int ret;

@@ -218,19 +218,19 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
		if (flags & LOOKUP_RCU)
			goto out;

		fc = get_fuse_conn(inode);
		fm = get_fuse_mount(inode);

		forget = fuse_alloc_forget();
		ret = -ENOMEM;
		if (!forget)
			goto out;

		attr_version = fuse_get_attr_version(fc);
		attr_version = fuse_get_attr_version(fm->fc);

		parent = dget_parent(entry);
		fuse_lookup_init(fc, &args, get_node_id(d_inode(parent)),
		fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)),
				 &entry->d_name, &outarg);
		ret = fuse_simple_request(fc, &args);
		ret = fuse_simple_request(fm, &args);
		dput(parent);
		/* Zero nodeid is same as -ENOENT */
		if (!ret && !outarg.nodeid)
@@ -238,7 +238,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
		if (!ret) {
			fi = get_fuse_inode(inode);
			if (outarg.nodeid != get_node_id(inode)) {
				fuse_queue_forget(fc, forget, outarg.nodeid, 1);
				fuse_queue_forget(fm->fc, forget,
						  outarg.nodeid, 1);
				goto invalid;
			}
			spin_lock(&fi->lock);
@@ -329,7 +330,7 @@ bool fuse_invalid_attr(struct fuse_attr *attr)
int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name,
		     struct fuse_entry_out *outarg, struct inode **inode)
{
	struct fuse_conn *fc = get_fuse_conn_super(sb);
	struct fuse_mount *fm = get_fuse_mount_super(sb);
	FUSE_ARGS(args);
	struct fuse_forget_link *forget;
	u64 attr_version;
@@ -346,10 +347,10 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
	if (!forget)
		goto out;

	attr_version = fuse_get_attr_version(fc);
	attr_version = fuse_get_attr_version(fm->fc);

	fuse_lookup_init(fc, &args, nodeid, name, outarg);
	err = fuse_simple_request(fc, &args);
	fuse_lookup_init(fm->fc, &args, nodeid, name, outarg);
	err = fuse_simple_request(fm, &args);
	/* Zero nodeid is same as -ENOENT, but with valid timeout */
	if (err || !outarg->nodeid)
		goto out_put_forget;
@@ -365,7 +366,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
			   attr_version);
	err = -ENOMEM;
	if (!*inode) {
		fuse_queue_forget(fc, forget, outarg->nodeid, 1);
		fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1);
		goto out;
	}
	err = 0;
@@ -434,7 +435,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
{
	int err;
	struct inode *inode;
	struct fuse_conn *fc = get_fuse_conn(dir);
	struct fuse_mount *fm = get_fuse_mount(dir);
	FUSE_ARGS(args);
	struct fuse_forget_link *forget;
	struct fuse_create_in inarg;
@@ -452,11 +453,11 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
		goto out_err;

	err = -ENOMEM;
	ff = fuse_file_alloc(fc);
	ff = fuse_file_alloc(fm);
	if (!ff)
		goto out_put_forget_req;

	if (!fc->dont_mask)
	if (!fm->fc->dont_mask)
		mode &= ~current_umask();

	flags &= ~O_NOCTTY;
@@ -477,7 +478,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
	args.out_args[0].value = &outentry;
	args.out_args[1].size = sizeof(outopen);
	args.out_args[1].value = &outopen;
	err = fuse_simple_request(fc, &args);
	err = fuse_simple_request(fm, &args);
	if (err)
		goto out_free_ff;

@@ -494,7 +495,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
	if (!inode) {
		flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
		fuse_sync_release(NULL, ff, flags);
		fuse_queue_forget(fc, forget, outentry.nodeid, 1);
		fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1);
		err = -ENOMEM;
		goto out_err;
	}
@@ -567,7 +568,7 @@ no_open:
/*
 * Code shared between mknod, mkdir, symlink and link
 */
static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
			    struct inode *dir, struct dentry *entry,
			    umode_t mode)
{
@@ -586,7 +587,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
	args->out_numargs = 1;
	args->out_args[0].size = sizeof(outarg);
	args->out_args[0].value = &outarg;
	err = fuse_simple_request(fc, args);
	err = fuse_simple_request(fm, args);
	if (err)
		goto out_put_forget_req;

@@ -600,7 +601,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
	inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
			  &outarg.attr, entry_attr_timeout(&outarg), 0);
	if (!inode) {
		fuse_queue_forget(fc, forget, outarg.nodeid, 1);
		fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1);
		return -ENOMEM;
	}
	kfree(forget);
@@ -628,10 +629,10 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode,
		      dev_t rdev)
{
	struct fuse_mknod_in inarg;
	struct fuse_conn *fc = get_fuse_conn(dir);
	struct fuse_mount *fm = get_fuse_mount(dir);
	FUSE_ARGS(args);

	if (!fc->dont_mask)
	if (!fm->fc->dont_mask)
		mode &= ~current_umask();

	memset(&inarg, 0, sizeof(inarg));
@@ -644,7 +645,7 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode,
	args.in_args[0].value = &inarg;
	args.in_args[1].size = entry->d_name.len + 1;
	args.in_args[1].value = entry->d_name.name;
	return create_new_entry(fc, &args, dir, entry, mode);
	return create_new_entry(fm, &args, dir, entry, mode);
}

static int fuse_create(struct inode *dir, struct dentry *entry, umode_t mode,
@@ -656,10 +657,10 @@ static int fuse_create(struct inode *dir, struct dentry *entry, umode_t mode,
static int fuse_mkdir(struct inode *dir, struct dentry *entry, umode_t mode)
{
	struct fuse_mkdir_in inarg;
	struct fuse_conn *fc = get_fuse_conn(dir);
	struct fuse_mount *fm = get_fuse_mount(dir);
	FUSE_ARGS(args);

	if (!fc->dont_mask)
	if (!fm->fc->dont_mask)
		mode &= ~current_umask();

	memset(&inarg, 0, sizeof(inarg));
@@ -671,13 +672,13 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, umode_t mode)
	args.in_args[0].value = &inarg;
	args.in_args[1].size = entry->d_name.len + 1;
	args.in_args[1].value = entry->d_name.name;
	return create_new_entry(fc, &args, dir, entry, S_IFDIR);
	return create_new_entry(fm, &args, dir, entry, S_IFDIR);
}

static int fuse_symlink(struct inode *dir, struct dentry *entry,
			const char *link)
{
	struct fuse_conn *fc = get_fuse_conn(dir);
	struct fuse_mount *fm = get_fuse_mount(dir);
	unsigned len = strlen(link) + 1;
	FUSE_ARGS(args);

@@ -687,7 +688,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
	args.in_args[0].value = entry->d_name.name;
	args.in_args[1].size = len;
	args.in_args[1].value = link;
	return create_new_entry(fc, &args, dir, entry, S_IFLNK);
	return create_new_entry(fm, &args, dir, entry, S_IFLNK);
}

void fuse_update_ctime(struct inode *inode)
@@ -701,7 +702,7 @@ void fuse_update_ctime(struct inode *inode)
static int fuse_unlink(struct inode *dir, struct dentry *entry)
{
	int err;
	struct fuse_conn *fc = get_fuse_conn(dir);
	struct fuse_mount *fm = get_fuse_mount(dir);
	FUSE_ARGS(args);

	args.opcode = FUSE_UNLINK;
@@ -709,13 +710,13 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
	args.in_numargs = 1;
	args.in_args[0].size = entry->d_name.len + 1;
	args.in_args[0].value = entry->d_name.name;
	err = fuse_simple_request(fc, &args);
	err = fuse_simple_request(fm, &args);
	if (!err) {
		struct inode *inode = d_inode(entry);
		struct fuse_inode *fi = get_fuse_inode(inode);

		spin_lock(&fi->lock);
		fi->attr_version = atomic64_inc_return(&fc->attr_version);
		fi->attr_version = atomic64_inc_return(&fm->fc->attr_version);
		/*
		 * If i_nlink == 0 then unlink doesn't make sense, yet this can
		 * happen if userspace filesystem is careless.  It would be
@@ -737,7 +738,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
static int fuse_rmdir(struct inode *dir, struct dentry *entry)
{
	int err;
	struct fuse_conn *fc = get_fuse_conn(dir);
	struct fuse_mount *fm = get_fuse_mount(dir);
	FUSE_ARGS(args);

	args.opcode = FUSE_RMDIR;
@@ -745,7 +746,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
	args.in_numargs = 1;
	args.in_args[0].size = entry->d_name.len + 1;
	args.in_args[0].value = entry->d_name.name;
	err = fuse_simple_request(fc, &args);
	err = fuse_simple_request(fm, &args);
	if (!err) {
		clear_nlink(d_inode(entry));
		fuse_dir_changed(dir);
@@ -761,7 +762,7 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
{
	int err;
	struct fuse_rename2_in inarg;
	struct fuse_conn *fc = get_fuse_conn(olddir);
	struct fuse_mount *fm = get_fuse_mount(olddir);
	FUSE_ARGS(args);

	memset(&inarg, 0, argsize);
@@ -776,7 +777,7 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
	args.in_args[1].value = oldent->d_name.name;
	args.in_args[2].size = newent->d_name.len + 1;
	args.in_args[2].value = newent->d_name.name;
	err = fuse_simple_request(fc, &args);
	err = fuse_simple_request(fm, &args);
	if (!err) {
		/* ctime changes */
		fuse_invalidate_attr(d_inode(oldent));
@@ -847,7 +848,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
	int err;
	struct fuse_link_in inarg;
	struct inode *inode = d_inode(entry);
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_mount *fm = get_fuse_mount(inode);
	FUSE_ARGS(args);

	memset(&inarg, 0, sizeof(inarg));
@@ -858,7 +859,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
	args.in_args[0].value = &inarg;
	args.in_args[1].size = newent->d_name.len + 1;
	args.in_args[1].value = newent->d_name.name;
	err = create_new_entry(fc, &args, newdir, newent, inode->i_mode);
	err = create_new_entry(fm, &args, newdir, newent, inode->i_mode);
	/* Contrary to "normal" filesystems it can happen that link
	   makes two "logical" inodes point to the same "physical"
	   inode.  We invalidate the attributes of the old one, so it
@@ -869,7 +870,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
		struct fuse_inode *fi = get_fuse_inode(inode);

		spin_lock(&fi->lock);
		fi->attr_version = atomic64_inc_return(&fc->attr_version);
		fi->attr_version = atomic64_inc_return(&fm->fc->attr_version);
		if (likely(inode->i_nlink < UINT_MAX))
			inc_nlink(inode);
		spin_unlock(&fi->lock);
@@ -926,11 +927,11 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
	int err;
	struct fuse_getattr_in inarg;
	struct fuse_attr_out outarg;
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_mount *fm = get_fuse_mount(inode);
	FUSE_ARGS(args);
	u64 attr_version;

	attr_version = fuse_get_attr_version(fc);
	attr_version = fuse_get_attr_version(fm->fc);

	memset(&inarg, 0, sizeof(inarg));
	memset(&outarg, 0, sizeof(outarg));
@@ -949,7 +950,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
	args.out_numargs = 1;
	args.out_args[0].size = sizeof(outarg);
	args.out_args[0].value = &outarg;
	err = fuse_simple_request(fc, &args);
	err = fuse_simple_request(fm, &args);
	if (!err) {
		if (fuse_invalid_attr(&outarg.attr) ||
		    (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
@@ -1002,7 +1003,7 @@ int fuse_update_attributes(struct inode *inode, struct file *file)
				    STATX_BASIC_STATS & ~STATX_ATIME, 0);
}

int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
			     u64 child_nodeid, struct qstr *name)
{
	int err = -ENOTDIR;
@@ -1010,7 +1011,7 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
	struct dentry *dir;
	struct dentry *entry;

	parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid);
	parent = fuse_ilookup(fc, parent_nodeid, NULL);
	if (!parent)
		return -ENOENT;

@@ -1102,14 +1103,14 @@ int fuse_allow_current_process(struct fuse_conn *fc)

static int fuse_access(struct inode *inode, int mask)
{
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_mount *fm = get_fuse_mount(inode);
	FUSE_ARGS(args);
	struct fuse_access_in inarg;
	int err;

	BUG_ON(mask & MAY_NOT_BLOCK);

	if (fc->no_access)
	if (fm->fc->no_access)
		return 0;

	memset(&inarg, 0, sizeof(inarg));
@@ -1119,9 +1120,9 @@ static int fuse_access(struct inode *inode, int mask)
	args.in_numargs = 1;
	args.in_args[0].size = sizeof(inarg);
	args.in_args[0].value = &inarg;
	err = fuse_simple_request(fc, &args);
	err = fuse_simple_request(fm, &args);
	if (err == -ENOSYS) {
		fc->no_access = 1;
		fm->fc->no_access = 1;
		err = 0;
	}
	return err;
@@ -1209,7 +1210,7 @@ static int fuse_permission(struct inode *inode, int mask)

static int fuse_readlink_page(struct inode *inode, struct page *page)
{
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_mount *fm = get_fuse_mount(inode);
	struct fuse_page_desc desc = { .length = PAGE_SIZE - 1 };
	struct fuse_args_pages ap = {
		.num_pages = 1,
@@ -1226,7 +1227,7 @@ static int fuse_readlink_page(struct inode *inode, struct page *page)
	ap.args.page_zeroing = true;
	ap.args.out_numargs = 1;
	ap.args.out_args[0].size = desc.length;
	res = fuse_simple_request(fc, &ap.args);
	res = fuse_simple_request(fm, &ap.args);

	fuse_invalidate_atime(inode);

@@ -1454,7 +1455,7 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_args *args,
 */
int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
{
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_mount *fm = get_fuse_mount(inode);
	FUSE_ARGS(args);
	struct fuse_setattr_in inarg;
	struct fuse_attr_out outarg;
@@ -1465,7 +1466,7 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
	inarg.valid = FATTR_MTIME;
	inarg.mtime = inode->i_mtime.tv_sec;
	inarg.mtimensec = inode->i_mtime.tv_nsec;
	if (fc->minor >= 23) {
	if (fm->fc->minor >= 23) {
		inarg.valid |= FATTR_CTIME;
		inarg.ctime = inode->i_ctime.tv_sec;
		inarg.ctimensec = inode->i_ctime.tv_nsec;
@@ -1474,9 +1475,9 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
		inarg.valid |= FATTR_FH;
		inarg.fh = ff->fh;
	}
	fuse_setattr_fill(fc, &args, inode, &inarg, &outarg);
	fuse_setattr_fill(fm->fc, &args, inode, &inarg, &outarg);

	return fuse_simple_request(fc, &args);
	return fuse_simple_request(fm, &args);
}

/*
@@ -1491,7 +1492,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
		    struct file *file)
{
	struct inode *inode = d_inode(dentry);
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_mount *fm = get_fuse_mount(inode);
	struct fuse_conn *fc = fm->fc;
	struct fuse_inode *fi = get_fuse_inode(inode);
	FUSE_ARGS(args);
	struct fuse_setattr_in inarg;
@@ -1577,7 +1579,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
		inarg.lock_owner = fuse_lock_owner_id(fc, current->files);
	}
	fuse_setattr_fill(fc, &args, inode, &inarg, &outarg);
	err = fuse_simple_request(fc, &args);
	err = fuse_simple_request(fm, &args);
	if (err) {
		if (err == -EINTR)
			fuse_invalidate_attr(inode);
Loading