Commit df24ac7a authored by Dai Ngo's avatar Dai Ngo Committed by Chuck Lever
Browse files

NFSD: enhance inter-server copy cleanup



Currently nfsd4_setup_inter_ssc returns the vfsmount of the source
server's export when the mount completes. After the copy is done
nfsd4_cleanup_inter_ssc is called with the vfsmount of the source
server and it searches nfsd_ssc_mount_list for a matching entry
to do the clean up.

The problems with this approach are (1) the need to search the
nfsd_ssc_mount_list and (2) the code has to handle the case where
the matching entry is not found which looks ugly.

The enhancement is instead of nfsd4_setup_inter_ssc returning the
vfsmount, it returns the nfsd4_ssc_umount_item which has the
vfsmount embedded in it. When nfsd4_cleanup_inter_ssc is called
it's passed with the nfsd4_ssc_umount_item directly to do the
clean up so no searching is needed and there is no need to handle
the 'not found' case.

Signed-off-by: default avatarDai Ngo <dai.ngo@oracle.com>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
[ cel: adjusted whitespace and variable/function names ]
Reviewed-by: default avatarOlga Kornievskaia <kolga@netapp.com>
parent f4afc8fe
Loading
Loading
Loading
Loading
+44 −67
Original line number Diff line number Diff line
@@ -1293,15 +1293,15 @@ extern void nfs_sb_deactive(struct super_block *sb);
 * setup a work entry in the ssc delayed unmount list.
 */
static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
		struct nfsd4_ssc_umount_item **retwork, struct vfsmount **ss_mnt)
				  struct nfsd4_ssc_umount_item **nsui)
{
	struct nfsd4_ssc_umount_item *ni = NULL;
	struct nfsd4_ssc_umount_item *work = NULL;
	struct nfsd4_ssc_umount_item *tmp;
	DEFINE_WAIT(wait);
	__be32 status = 0;

	*ss_mnt = NULL;
	*retwork = NULL;
	*nsui = NULL;
	work = kzalloc(sizeof(*work), GFP_KERNEL);
try_again:
	spin_lock(&nn->nfsd_ssc_lock);
@@ -1325,12 +1325,12 @@ static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
			finish_wait(&nn->nfsd_ssc_waitq, &wait);
			goto try_again;
		}
		*ss_mnt = ni->nsui_vfsmount;
		*nsui = ni;
		refcount_inc(&ni->nsui_refcnt);
		spin_unlock(&nn->nfsd_ssc_lock);
		kfree(work);

		/* return vfsmount in ss_mnt */
		/* return vfsmount in (*nsui)->nsui_vfsmount */
		return 0;
	}
	if (work) {
@@ -1338,31 +1338,32 @@ static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
		refcount_set(&work->nsui_refcnt, 2);
		work->nsui_busy = true;
		list_add_tail(&work->nsui_list, &nn->nfsd_ssc_mount_list);
		*retwork = work;
	}
		*nsui = work;
	} else
		status = nfserr_resource;
	spin_unlock(&nn->nfsd_ssc_lock);
	return 0;
	return status;
}

static void nfsd4_ssc_update_dul_work(struct nfsd_net *nn,
		struct nfsd4_ssc_umount_item *work, struct vfsmount *ss_mnt)
static void nfsd4_ssc_update_dul(struct nfsd_net *nn,
				 struct nfsd4_ssc_umount_item *nsui,
				 struct vfsmount *ss_mnt)
{
	/* set nsui_vfsmount, clear busy flag and wakeup waiters */
	spin_lock(&nn->nfsd_ssc_lock);
	work->nsui_vfsmount = ss_mnt;
	work->nsui_busy = false;
	nsui->nsui_vfsmount = ss_mnt;
	nsui->nsui_busy = false;
	wake_up_all(&nn->nfsd_ssc_waitq);
	spin_unlock(&nn->nfsd_ssc_lock);
}

static void nfsd4_ssc_cancel_dul_work(struct nfsd_net *nn,
		struct nfsd4_ssc_umount_item *work)
static void nfsd4_ssc_cancel_dul(struct nfsd_net *nn,
				 struct nfsd4_ssc_umount_item *nsui)
{
	spin_lock(&nn->nfsd_ssc_lock);
	list_del(&work->nsui_list);
	list_del(&nsui->nsui_list);
	wake_up_all(&nn->nfsd_ssc_waitq);
	spin_unlock(&nn->nfsd_ssc_lock);
	kfree(work);
	kfree(nsui);
}

/*
@@ -1370,7 +1371,7 @@ static void nfsd4_ssc_cancel_dul_work(struct nfsd_net *nn,
 */
static __be32
nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
		       struct vfsmount **mount)
		       struct nfsd4_ssc_umount_item **nsui)
{
	struct file_system_type *type;
	struct vfsmount *ss_mnt;
@@ -1381,7 +1382,6 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
	char *ipaddr, *dev_name, *raw_data;
	int len, raw_len;
	__be32 status = nfserr_inval;
	struct nfsd4_ssc_umount_item *work = NULL;
	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

	naddr = &nss->u.nl4_addr;
@@ -1389,6 +1389,7 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
					 naddr->addr_len,
					 (struct sockaddr *)&tmp_addr,
					 sizeof(tmp_addr));
	*nsui = NULL;
	if (tmp_addrlen == 0)
		goto out_err;

@@ -1431,10 +1432,10 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
		goto out_free_rawdata;
	snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);

	status = nfsd4_ssc_setup_dul(nn, ipaddr, &work, &ss_mnt);
	status = nfsd4_ssc_setup_dul(nn, ipaddr, nsui);
	if (status)
		goto out_free_devname;
	if (ss_mnt)
	if ((*nsui)->nsui_vfsmount)
		goto out_done;

	/* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
@@ -1442,15 +1443,12 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
	module_put(type->owner);
	if (IS_ERR(ss_mnt)) {
		status = nfserr_nodev;
		if (work)
			nfsd4_ssc_cancel_dul_work(nn, work);
		nfsd4_ssc_cancel_dul(nn, *nsui);
		goto out_free_devname;
	}
	if (work)
		nfsd4_ssc_update_dul_work(nn, work, ss_mnt);
	nfsd4_ssc_update_dul(nn, *nsui, ss_mnt);
out_done:
	status = 0;
	*mount = ss_mnt;

out_free_devname:
	kfree(dev_name);
@@ -1474,7 +1472,7 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
static __be32
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
		      struct nfsd4_compound_state *cstate,
		      struct nfsd4_copy *copy, struct vfsmount **mount)
		      struct nfsd4_copy *copy)
{
	struct svc_fh *s_fh = NULL;
	stateid_t *s_stid = &copy->cp_src_stateid;
@@ -1487,7 +1485,7 @@ nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
	if (status)
		goto out;

	status = nfsd4_interssc_connect(copy->cp_src, rqstp, mount);
	status = nfsd4_interssc_connect(copy->cp_src, rqstp, &copy->ss_nsui);
	if (status)
		goto out;

@@ -1505,45 +1503,27 @@ nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
}

static void
nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp,
nfsd4_cleanup_inter_ssc(struct nfsd4_ssc_umount_item *nsui, struct file *filp,
			struct nfsd_file *dst)
{
	bool found = false;
	long timeout;
	struct nfsd4_ssc_umount_item *tmp;
	struct nfsd4_ssc_umount_item *ni = NULL;
	struct nfsd_net *nn = net_generic(dst->nf_net, nfsd_net_id);
	long timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);

	nfs42_ssc_close(filp);
	nfsd_file_put(dst);
	fput(filp);

	if (!nn) {
		mntput(ss_mnt);
		return;
	}
	spin_lock(&nn->nfsd_ssc_lock);
	timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);
	list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
		if (ni->nsui_vfsmount->mnt_sb == ss_mnt->mnt_sb) {
			list_del(&ni->nsui_list);
	list_del(&nsui->nsui_list);
	/*
	 * vfsmount can be shared by multiple exports,
	 * decrement refcnt. If the count drops to 1 it
	 * will be unmounted when nsui_expire expires.
	 */
			refcount_dec(&ni->nsui_refcnt);
			ni->nsui_expire = jiffies + timeout;
			list_add_tail(&ni->nsui_list, &nn->nfsd_ssc_mount_list);
			found = true;
			break;
		}
	}
	refcount_dec(&nsui->nsui_refcnt);
	nsui->nsui_expire = jiffies + timeout;
	list_add_tail(&nsui->nsui_list, &nn->nfsd_ssc_mount_list);
	spin_unlock(&nn->nfsd_ssc_lock);
	if (!found) {
		mntput(ss_mnt);
		return;
	}
}

#else /* CONFIG_NFSD_V4_2_INTER_SSC */
@@ -1551,15 +1531,13 @@ nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp,
static __be32
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
		      struct nfsd4_compound_state *cstate,
		      struct nfsd4_copy *copy,
		      struct vfsmount **mount)
		      struct nfsd4_copy *copy)
{
	*mount = NULL;
	return nfserr_inval;
}

static void
nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp,
nfsd4_cleanup_inter_ssc(struct nfsd4_ssc_umount_item *nsui, struct file *filp,
			struct nfsd_file *dst)
{
}
@@ -1700,7 +1678,7 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
	memcpy(dst->cp_src, src->cp_src, sizeof(struct nl4_server));
	memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid));
	memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh));
	dst->ss_mnt = src->ss_mnt;
	dst->ss_nsui = src->ss_nsui;
}

static void cleanup_async_copy(struct nfsd4_copy *copy)
@@ -1749,8 +1727,8 @@ static int nfsd4_do_async_copy(void *data)
	if (nfsd4_ssc_is_inter(copy)) {
		struct file *filp;

		filp = nfs42_ssc_open(copy->ss_mnt, &copy->c_fh,
				      &copy->stateid);
		filp = nfs42_ssc_open(copy->ss_nsui->nsui_vfsmount,
				      &copy->c_fh, &copy->stateid);
		if (IS_ERR(filp)) {
			switch (PTR_ERR(filp)) {
			case -EBADF:
@@ -1764,7 +1742,7 @@ static int nfsd4_do_async_copy(void *data)
		}
		nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
				       false);
		nfsd4_cleanup_inter_ssc(copy->ss_mnt, filp, copy->nf_dst);
		nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst);
	} else {
		nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
				       copy->nf_dst->nf_file, false);
@@ -1790,8 +1768,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
			status = nfserr_notsupp;
			goto out;
		}
		status = nfsd4_setup_inter_ssc(rqstp, cstate, copy,
				&copy->ss_mnt);
		status = nfsd4_setup_inter_ssc(rqstp, cstate, copy);
		if (status)
			return nfserr_offload_denied;
	} else {
+1 −1
Original line number Diff line number Diff line
@@ -571,7 +571,7 @@ struct nfsd4_copy {
	struct task_struct	*copy_task;
	refcount_t		refcount;

	struct vfsmount		*ss_mnt;
	struct nfsd4_ssc_umount_item *ss_nsui;
	struct nfs_fh		c_fh;
	nfs4_stateid		stateid;
};
+1 −1
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ static inline void nfs42_ssc_close(struct file *filep)
	if (nfs_ssc_client_tbl.ssc_nfs4_ops)
		(*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_close)(filep);
}
#endif

struct nfsd4_ssc_umount_item {
	struct list_head nsui_list;
@@ -66,7 +67,6 @@ struct nfsd4_ssc_umount_item {
	struct vfsmount *nsui_vfsmount;
	char nsui_ipaddr[RPC_MAX_ADDRBUFLEN + 1];
};
#endif

/*
 * NFS_FS