mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-17 10:46:33 +00:00
NFSD: enhance inter-server copy cleanup
[ Upstream commit df24ac7a2e3a9d0bc68f1756a880e50bfe4b4522 ] 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: Dai Ngo <dai.ngo@oracle.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> [ cel: adjusted whitespace and variable/function names ] Reviewed-by: Olga Kornievskaia <kolga@netapp.com> Stable-dep-of: 34e8f9ec4c9a ("NFSD: fix leaked reference count of nfsd4_ssc_umount_item") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
659d4c4ca5
commit
c8b346b79a
@ -1306,15 +1306,15 @@ extern void nfs_sb_deactive(struct super_block *sb);
|
|||||||
* setup a work entry in the ssc delayed unmount list.
|
* setup a work entry in the ssc delayed unmount list.
|
||||||
*/
|
*/
|
||||||
static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
|
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 *ni = NULL;
|
||||||
struct nfsd4_ssc_umount_item *work = NULL;
|
struct nfsd4_ssc_umount_item *work = NULL;
|
||||||
struct nfsd4_ssc_umount_item *tmp;
|
struct nfsd4_ssc_umount_item *tmp;
|
||||||
DEFINE_WAIT(wait);
|
DEFINE_WAIT(wait);
|
||||||
|
__be32 status = 0;
|
||||||
|
|
||||||
*ss_mnt = NULL;
|
*nsui = NULL;
|
||||||
*retwork = NULL;
|
|
||||||
work = kzalloc(sizeof(*work), GFP_KERNEL);
|
work = kzalloc(sizeof(*work), GFP_KERNEL);
|
||||||
try_again:
|
try_again:
|
||||||
spin_lock(&nn->nfsd_ssc_lock);
|
spin_lock(&nn->nfsd_ssc_lock);
|
||||||
@ -1338,12 +1338,12 @@ try_again:
|
|||||||
finish_wait(&nn->nfsd_ssc_waitq, &wait);
|
finish_wait(&nn->nfsd_ssc_waitq, &wait);
|
||||||
goto try_again;
|
goto try_again;
|
||||||
}
|
}
|
||||||
*ss_mnt = ni->nsui_vfsmount;
|
*nsui = ni;
|
||||||
refcount_inc(&ni->nsui_refcnt);
|
refcount_inc(&ni->nsui_refcnt);
|
||||||
spin_unlock(&nn->nfsd_ssc_lock);
|
spin_unlock(&nn->nfsd_ssc_lock);
|
||||||
kfree(work);
|
kfree(work);
|
||||||
|
|
||||||
/* return vfsmount in ss_mnt */
|
/* return vfsmount in (*nsui)->nsui_vfsmount */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (work) {
|
if (work) {
|
||||||
@ -1351,31 +1351,32 @@ try_again:
|
|||||||
refcount_set(&work->nsui_refcnt, 2);
|
refcount_set(&work->nsui_refcnt, 2);
|
||||||
work->nsui_busy = true;
|
work->nsui_busy = true;
|
||||||
list_add_tail(&work->nsui_list, &nn->nfsd_ssc_mount_list);
|
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);
|
spin_unlock(&nn->nfsd_ssc_lock);
|
||||||
return 0;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nfsd4_ssc_update_dul_work(struct nfsd_net *nn,
|
static void nfsd4_ssc_update_dul(struct nfsd_net *nn,
|
||||||
struct nfsd4_ssc_umount_item *work, struct vfsmount *ss_mnt)
|
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);
|
spin_lock(&nn->nfsd_ssc_lock);
|
||||||
work->nsui_vfsmount = ss_mnt;
|
nsui->nsui_vfsmount = ss_mnt;
|
||||||
work->nsui_busy = false;
|
nsui->nsui_busy = false;
|
||||||
wake_up_all(&nn->nfsd_ssc_waitq);
|
wake_up_all(&nn->nfsd_ssc_waitq);
|
||||||
spin_unlock(&nn->nfsd_ssc_lock);
|
spin_unlock(&nn->nfsd_ssc_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nfsd4_ssc_cancel_dul_work(struct nfsd_net *nn,
|
static void nfsd4_ssc_cancel_dul(struct nfsd_net *nn,
|
||||||
struct nfsd4_ssc_umount_item *work)
|
struct nfsd4_ssc_umount_item *nsui)
|
||||||
{
|
{
|
||||||
spin_lock(&nn->nfsd_ssc_lock);
|
spin_lock(&nn->nfsd_ssc_lock);
|
||||||
list_del(&work->nsui_list);
|
list_del(&nsui->nsui_list);
|
||||||
wake_up_all(&nn->nfsd_ssc_waitq);
|
wake_up_all(&nn->nfsd_ssc_waitq);
|
||||||
spin_unlock(&nn->nfsd_ssc_lock);
|
spin_unlock(&nn->nfsd_ssc_lock);
|
||||||
kfree(work);
|
kfree(nsui);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1383,7 +1384,7 @@ static void nfsd4_ssc_cancel_dul_work(struct nfsd_net *nn,
|
|||||||
*/
|
*/
|
||||||
static __be32
|
static __be32
|
||||||
nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
|
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 file_system_type *type;
|
||||||
struct vfsmount *ss_mnt;
|
struct vfsmount *ss_mnt;
|
||||||
@ -1394,7 +1395,6 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
|
|||||||
char *ipaddr, *dev_name, *raw_data;
|
char *ipaddr, *dev_name, *raw_data;
|
||||||
int len, raw_len;
|
int len, raw_len;
|
||||||
__be32 status = nfserr_inval;
|
__be32 status = nfserr_inval;
|
||||||
struct nfsd4_ssc_umount_item *work = NULL;
|
|
||||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||||
|
|
||||||
naddr = &nss->u.nl4_addr;
|
naddr = &nss->u.nl4_addr;
|
||||||
@ -1402,6 +1402,7 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
|
|||||||
naddr->addr_len,
|
naddr->addr_len,
|
||||||
(struct sockaddr *)&tmp_addr,
|
(struct sockaddr *)&tmp_addr,
|
||||||
sizeof(tmp_addr));
|
sizeof(tmp_addr));
|
||||||
|
*nsui = NULL;
|
||||||
if (tmp_addrlen == 0)
|
if (tmp_addrlen == 0)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
@ -1444,10 +1445,10 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
|
|||||||
goto out_free_rawdata;
|
goto out_free_rawdata;
|
||||||
snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);
|
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)
|
if (status)
|
||||||
goto out_free_devname;
|
goto out_free_devname;
|
||||||
if (ss_mnt)
|
if ((*nsui)->nsui_vfsmount)
|
||||||
goto out_done;
|
goto out_done;
|
||||||
|
|
||||||
/* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
|
/* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
|
||||||
@ -1455,15 +1456,12 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
|
|||||||
module_put(type->owner);
|
module_put(type->owner);
|
||||||
if (IS_ERR(ss_mnt)) {
|
if (IS_ERR(ss_mnt)) {
|
||||||
status = nfserr_nodev;
|
status = nfserr_nodev;
|
||||||
if (work)
|
nfsd4_ssc_cancel_dul(nn, *nsui);
|
||||||
nfsd4_ssc_cancel_dul_work(nn, work);
|
|
||||||
goto out_free_devname;
|
goto out_free_devname;
|
||||||
}
|
}
|
||||||
if (work)
|
nfsd4_ssc_update_dul(nn, *nsui, ss_mnt);
|
||||||
nfsd4_ssc_update_dul_work(nn, work, ss_mnt);
|
|
||||||
out_done:
|
out_done:
|
||||||
status = 0;
|
status = 0;
|
||||||
*mount = ss_mnt;
|
|
||||||
|
|
||||||
out_free_devname:
|
out_free_devname:
|
||||||
kfree(dev_name);
|
kfree(dev_name);
|
||||||
@ -1487,7 +1485,7 @@ out_err:
|
|||||||
static __be32
|
static __be32
|
||||||
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
|
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
|
||||||
struct nfsd4_compound_state *cstate,
|
struct nfsd4_compound_state *cstate,
|
||||||
struct nfsd4_copy *copy, struct vfsmount **mount)
|
struct nfsd4_copy *copy)
|
||||||
{
|
{
|
||||||
struct svc_fh *s_fh = NULL;
|
struct svc_fh *s_fh = NULL;
|
||||||
stateid_t *s_stid = ©->cp_src_stateid;
|
stateid_t *s_stid = ©->cp_src_stateid;
|
||||||
@ -1500,7 +1498,7 @@ nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
|
|||||||
if (status)
|
if (status)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
status = nfsd4_interssc_connect(copy->cp_src, rqstp, mount);
|
status = nfsd4_interssc_connect(copy->cp_src, rqstp, ©->ss_nsui);
|
||||||
if (status)
|
if (status)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -1518,45 +1516,27 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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)
|
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);
|
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);
|
nfs42_ssc_close(filp);
|
||||||
nfsd_file_put(dst);
|
nfsd_file_put(dst);
|
||||||
fput(filp);
|
fput(filp);
|
||||||
|
|
||||||
if (!nn) {
|
|
||||||
mntput(ss_mnt);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spin_lock(&nn->nfsd_ssc_lock);
|
spin_lock(&nn->nfsd_ssc_lock);
|
||||||
timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);
|
list_del(&nsui->nsui_list);
|
||||||
list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
|
/*
|
||||||
if (ni->nsui_vfsmount->mnt_sb == ss_mnt->mnt_sb) {
|
* vfsmount can be shared by multiple exports,
|
||||||
list_del(&ni->nsui_list);
|
* decrement refcnt. If the count drops to 1 it
|
||||||
/*
|
* will be unmounted when nsui_expire expires.
|
||||||
* vfsmount can be shared by multiple exports,
|
*/
|
||||||
* decrement refcnt. If the count drops to 1 it
|
refcount_dec(&nsui->nsui_refcnt);
|
||||||
* will be unmounted when nsui_expire expires.
|
nsui->nsui_expire = jiffies + timeout;
|
||||||
*/
|
list_add_tail(&nsui->nsui_list, &nn->nfsd_ssc_mount_list);
|
||||||
refcount_dec(&ni->nsui_refcnt);
|
|
||||||
ni->nsui_expire = jiffies + timeout;
|
|
||||||
list_add_tail(&ni->nsui_list, &nn->nfsd_ssc_mount_list);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spin_unlock(&nn->nfsd_ssc_lock);
|
spin_unlock(&nn->nfsd_ssc_lock);
|
||||||
if (!found) {
|
|
||||||
mntput(ss_mnt);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* CONFIG_NFSD_V4_2_INTER_SSC */
|
#else /* CONFIG_NFSD_V4_2_INTER_SSC */
|
||||||
@ -1564,15 +1544,13 @@ nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp,
|
|||||||
static __be32
|
static __be32
|
||||||
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
|
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
|
||||||
struct nfsd4_compound_state *cstate,
|
struct nfsd4_compound_state *cstate,
|
||||||
struct nfsd4_copy *copy,
|
struct nfsd4_copy *copy)
|
||||||
struct vfsmount **mount)
|
|
||||||
{
|
{
|
||||||
*mount = NULL;
|
|
||||||
return nfserr_inval;
|
return nfserr_inval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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)
|
struct nfsd_file *dst)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -1713,7 +1691,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->cp_src, src->cp_src, sizeof(struct nl4_server));
|
||||||
memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid));
|
memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid));
|
||||||
memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh));
|
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)
|
static void cleanup_async_copy(struct nfsd4_copy *copy)
|
||||||
@ -1762,8 +1740,8 @@ static int nfsd4_do_async_copy(void *data)
|
|||||||
if (nfsd4_ssc_is_inter(copy)) {
|
if (nfsd4_ssc_is_inter(copy)) {
|
||||||
struct file *filp;
|
struct file *filp;
|
||||||
|
|
||||||
filp = nfs42_ssc_open(copy->ss_mnt, ©->c_fh,
|
filp = nfs42_ssc_open(copy->ss_nsui->nsui_vfsmount,
|
||||||
©->stateid);
|
©->c_fh, ©->stateid);
|
||||||
if (IS_ERR(filp)) {
|
if (IS_ERR(filp)) {
|
||||||
switch (PTR_ERR(filp)) {
|
switch (PTR_ERR(filp)) {
|
||||||
case -EBADF:
|
case -EBADF:
|
||||||
@ -1777,7 +1755,7 @@ static int nfsd4_do_async_copy(void *data)
|
|||||||
}
|
}
|
||||||
nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
|
nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
|
||||||
false);
|
false);
|
||||||
nfsd4_cleanup_inter_ssc(copy->ss_mnt, filp, copy->nf_dst);
|
nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst);
|
||||||
} else {
|
} else {
|
||||||
nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
|
nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
|
||||||
copy->nf_dst->nf_file, false);
|
copy->nf_dst->nf_file, false);
|
||||||
@ -1803,8 +1781,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||||||
status = nfserr_notsupp;
|
status = nfserr_notsupp;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
status = nfsd4_setup_inter_ssc(rqstp, cstate, copy,
|
status = nfsd4_setup_inter_ssc(rqstp, cstate, copy);
|
||||||
©->ss_mnt);
|
|
||||||
if (status)
|
if (status)
|
||||||
return nfserr_offload_denied;
|
return nfserr_offload_denied;
|
||||||
} else {
|
} else {
|
||||||
|
@ -571,7 +571,7 @@ struct nfsd4_copy {
|
|||||||
struct task_struct *copy_task;
|
struct task_struct *copy_task;
|
||||||
refcount_t refcount;
|
refcount_t refcount;
|
||||||
|
|
||||||
struct vfsmount *ss_mnt;
|
struct nfsd4_ssc_umount_item *ss_nsui;
|
||||||
struct nfs_fh c_fh;
|
struct nfs_fh c_fh;
|
||||||
nfs4_stateid stateid;
|
nfs4_stateid stateid;
|
||||||
};
|
};
|
||||||
|
@ -53,6 +53,7 @@ static inline void nfs42_ssc_close(struct file *filep)
|
|||||||
if (nfs_ssc_client_tbl.ssc_nfs4_ops)
|
if (nfs_ssc_client_tbl.ssc_nfs4_ops)
|
||||||
(*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_close)(filep);
|
(*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_close)(filep);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct nfsd4_ssc_umount_item {
|
struct nfsd4_ssc_umount_item {
|
||||||
struct list_head nsui_list;
|
struct list_head nsui_list;
|
||||||
@ -66,7 +67,6 @@ struct nfsd4_ssc_umount_item {
|
|||||||
struct vfsmount *nsui_vfsmount;
|
struct vfsmount *nsui_vfsmount;
|
||||||
char nsui_ipaddr[RPC_MAX_ADDRBUFLEN + 1];
|
char nsui_ipaddr[RPC_MAX_ADDRBUFLEN + 1];
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NFS_FS
|
* NFS_FS
|
||||||
|
Loading…
x
Reference in New Issue
Block a user