Skip to content

Commit f4e44b3

Browse files
daimngoJ. Bruce Fields
authored andcommitted
NFSD: delay unmount source's export after inter-server copy completed.
Currently the source's export is mounted and unmounted on every inter-server copy operation. This patch is an enhancement to delay the unmount of the source export for a certain period of time to eliminate the mount and unmount overhead on subsequent copy operations. After a copy operation completes, a work entry is added to the delayed unmount list with an expiration time. This list is serviced by the laundromat thread to unmount the export of the expired entries. Each time the export is being used again, its expiration time is extended and the entry is re-inserted to the tail of the list. The unmount task and the mount operation of the copy request are synced to make sure the export is not unmounted while it's being used. Signed-off-by: Dai Ngo <[email protected]> Signed-off-by: J. Bruce Fields <[email protected]>
1 parent eac0b17 commit f4e44b3

File tree

6 files changed

+229
-4
lines changed

6 files changed

+229
-4
lines changed

fs/nfsd/netns.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ struct nfsd_net {
176176
unsigned int longest_chain_cachesize;
177177

178178
struct shrinker nfsd_reply_cache_shrinker;
179+
180+
/* tracking server-to-server copy mounts */
181+
spinlock_t nfsd_ssc_lock;
182+
struct list_head nfsd_ssc_mount_list;
183+
wait_queue_head_t nfsd_ssc_waitq;
184+
179185
/* utsname taken from the process that starts the server */
180186
char nfsd_name[UNX_MAXNODENAME+1];
181187
};

fs/nfsd/nfs4proc.c

Lines changed: 131 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ module_param(inter_copy_offload_enable, bool, 0644);
5555
MODULE_PARM_DESC(inter_copy_offload_enable,
5656
"Enable inter server to server copy offload. Default: false");
5757

58+
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
59+
static int nfsd4_ssc_umount_timeout = 900000; /* default to 15 mins */
60+
module_param(nfsd4_ssc_umount_timeout, int, 0644);
61+
MODULE_PARM_DESC(nfsd4_ssc_umount_timeout,
62+
"idle msecs before unmount export from source server");
63+
#endif
64+
5865
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
5966
#include <linux/security.h>
6067

@@ -1165,6 +1172,81 @@ extern void nfs_sb_deactive(struct super_block *sb);
11651172

11661173
#define NFSD42_INTERSSC_MOUNTOPS "vers=4.2,addr=%s,sec=sys"
11671174

1175+
/*
1176+
* setup a work entry in the ssc delayed unmount list.
1177+
*/
1178+
static int nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
1179+
struct nfsd4_ssc_umount_item **retwork, struct vfsmount **ss_mnt)
1180+
{
1181+
struct nfsd4_ssc_umount_item *ni = 0;
1182+
struct nfsd4_ssc_umount_item *work = NULL;
1183+
struct nfsd4_ssc_umount_item *tmp;
1184+
DEFINE_WAIT(wait);
1185+
1186+
*ss_mnt = NULL;
1187+
*retwork = NULL;
1188+
work = kzalloc(sizeof(*work), GFP_KERNEL);
1189+
try_again:
1190+
spin_lock(&nn->nfsd_ssc_lock);
1191+
list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
1192+
if (strncmp(ni->nsui_ipaddr, ipaddr, sizeof(ni->nsui_ipaddr)))
1193+
continue;
1194+
/* found a match */
1195+
if (ni->nsui_busy) {
1196+
/* wait - and try again */
1197+
prepare_to_wait(&nn->nfsd_ssc_waitq, &wait,
1198+
TASK_INTERRUPTIBLE);
1199+
spin_unlock(&nn->nfsd_ssc_lock);
1200+
1201+
/* allow 20secs for mount/unmount for now - revisit */
1202+
if (signal_pending(current) ||
1203+
(schedule_timeout(20*HZ) == 0)) {
1204+
kfree(work);
1205+
return nfserr_eagain;
1206+
}
1207+
finish_wait(&nn->nfsd_ssc_waitq, &wait);
1208+
goto try_again;
1209+
}
1210+
*ss_mnt = ni->nsui_vfsmount;
1211+
refcount_inc(&ni->nsui_refcnt);
1212+
spin_unlock(&nn->nfsd_ssc_lock);
1213+
kfree(work);
1214+
1215+
/* return vfsmount in ss_mnt */
1216+
return 0;
1217+
}
1218+
if (work) {
1219+
strncpy(work->nsui_ipaddr, ipaddr, sizeof(work->nsui_ipaddr));
1220+
refcount_set(&work->nsui_refcnt, 2);
1221+
work->nsui_busy = true;
1222+
list_add_tail(&work->nsui_list, &nn->nfsd_ssc_mount_list);
1223+
*retwork = work;
1224+
}
1225+
spin_unlock(&nn->nfsd_ssc_lock);
1226+
return 0;
1227+
}
1228+
1229+
static void nfsd4_ssc_update_dul_work(struct nfsd_net *nn,
1230+
struct nfsd4_ssc_umount_item *work, struct vfsmount *ss_mnt)
1231+
{
1232+
/* set nsui_vfsmount, clear busy flag and wakeup waiters */
1233+
spin_lock(&nn->nfsd_ssc_lock);
1234+
work->nsui_vfsmount = ss_mnt;
1235+
work->nsui_busy = false;
1236+
wake_up_all(&nn->nfsd_ssc_waitq);
1237+
spin_unlock(&nn->nfsd_ssc_lock);
1238+
}
1239+
1240+
static void nfsd4_ssc_cancel_dul_work(struct nfsd_net *nn,
1241+
struct nfsd4_ssc_umount_item *work)
1242+
{
1243+
spin_lock(&nn->nfsd_ssc_lock);
1244+
list_del(&work->nsui_list);
1245+
wake_up_all(&nn->nfsd_ssc_waitq);
1246+
spin_unlock(&nn->nfsd_ssc_lock);
1247+
kfree(work);
1248+
}
1249+
11681250
/*
11691251
* Support one copy source server for now.
11701252
*/
@@ -1181,6 +1263,8 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
11811263
char *ipaddr, *dev_name, *raw_data;
11821264
int len, raw_len;
11831265
__be32 status = nfserr_inval;
1266+
struct nfsd4_ssc_umount_item *work = NULL;
1267+
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
11841268

11851269
naddr = &nss->u.nl4_addr;
11861270
tmp_addrlen = rpc_uaddr2sockaddr(SVC_NET(rqstp), naddr->addr,
@@ -1229,12 +1313,23 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
12291313
goto out_free_rawdata;
12301314
snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);
12311315

1316+
status = nfsd4_ssc_setup_dul(nn, ipaddr, &work, &ss_mnt);
1317+
if (status)
1318+
goto out_free_devname;
1319+
if (ss_mnt)
1320+
goto out_done;
1321+
12321322
/* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
12331323
ss_mnt = vfs_kern_mount(type, SB_KERNMOUNT, dev_name, raw_data);
12341324
module_put(type->owner);
1235-
if (IS_ERR(ss_mnt))
1325+
if (IS_ERR(ss_mnt)) {
1326+
if (work)
1327+
nfsd4_ssc_cancel_dul_work(nn, work);
12361328
goto out_free_devname;
1237-
1329+
}
1330+
if (work)
1331+
nfsd4_ssc_update_dul_work(nn, work, ss_mnt);
1332+
out_done:
12381333
status = 0;
12391334
*mount = ss_mnt;
12401335

@@ -1301,10 +1396,42 @@ static void
13011396
nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src,
13021397
struct nfsd_file *dst)
13031398
{
1399+
bool found = false;
1400+
long timeout;
1401+
struct nfsd4_ssc_umount_item *tmp;
1402+
struct nfsd4_ssc_umount_item *ni = 0;
1403+
struct nfsd_net *nn = net_generic(dst->nf_net, nfsd_net_id);
1404+
13041405
nfs42_ssc_close(src->nf_file);
1305-
fput(src->nf_file);
13061406
nfsd_file_put(dst);
1307-
mntput(ss_mnt);
1407+
fput(src->nf_file);
1408+
1409+
if (!nn) {
1410+
mntput(ss_mnt);
1411+
return;
1412+
}
1413+
spin_lock(&nn->nfsd_ssc_lock);
1414+
timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);
1415+
list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
1416+
if (ni->nsui_vfsmount->mnt_sb == ss_mnt->mnt_sb) {
1417+
list_del(&ni->nsui_list);
1418+
/*
1419+
* vfsmount can be shared by multiple exports,
1420+
* decrement refcnt. If the count drops to 1 it
1421+
* will be unmounted when nsui_expire expires.
1422+
*/
1423+
refcount_dec(&ni->nsui_refcnt);
1424+
ni->nsui_expire = jiffies + timeout;
1425+
list_add_tail(&ni->nsui_list, &nn->nfsd_ssc_mount_list);
1426+
found = true;
1427+
break;
1428+
}
1429+
}
1430+
spin_unlock(&nn->nfsd_ssc_lock);
1431+
if (!found) {
1432+
mntput(ss_mnt);
1433+
return;
1434+
}
13081435
}
13091436

13101437
#else /* CONFIG_NFSD_V4_2_INTER_SSC */

fs/nfsd/nfs4state.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <linux/jhash.h>
4545
#include <linux/string_helpers.h>
4646
#include <linux/fsnotify.h>
47+
#include <linux/nfs_ssc.h>
4748
#include "xdr4.h"
4849
#include "xdr4cb.h"
4950
#include "vfs.h"
@@ -5480,6 +5481,69 @@ static bool state_expired(struct laundry_time *lt, time64_t last_refresh)
54805481
return false;
54815482
}
54825483

5484+
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
5485+
void nfsd4_ssc_init_umount_work(struct nfsd_net *nn)
5486+
{
5487+
spin_lock_init(&nn->nfsd_ssc_lock);
5488+
INIT_LIST_HEAD(&nn->nfsd_ssc_mount_list);
5489+
init_waitqueue_head(&nn->nfsd_ssc_waitq);
5490+
}
5491+
EXPORT_SYMBOL_GPL(nfsd4_ssc_init_umount_work);
5492+
5493+
/*
5494+
* This is called when nfsd is being shutdown, after all inter_ssc
5495+
* cleanup were done, to destroy the ssc delayed unmount list.
5496+
*/
5497+
static void nfsd4_ssc_shutdown_umount(struct nfsd_net *nn)
5498+
{
5499+
struct nfsd4_ssc_umount_item *ni = 0;
5500+
struct nfsd4_ssc_umount_item *tmp;
5501+
5502+
spin_lock(&nn->nfsd_ssc_lock);
5503+
list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
5504+
list_del(&ni->nsui_list);
5505+
spin_unlock(&nn->nfsd_ssc_lock);
5506+
mntput(ni->nsui_vfsmount);
5507+
kfree(ni);
5508+
spin_lock(&nn->nfsd_ssc_lock);
5509+
}
5510+
spin_unlock(&nn->nfsd_ssc_lock);
5511+
}
5512+
5513+
static void nfsd4_ssc_expire_umount(struct nfsd_net *nn)
5514+
{
5515+
bool do_wakeup = false;
5516+
struct nfsd4_ssc_umount_item *ni = 0;
5517+
struct nfsd4_ssc_umount_item *tmp;
5518+
5519+
spin_lock(&nn->nfsd_ssc_lock);
5520+
list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
5521+
if (time_after(jiffies, ni->nsui_expire)) {
5522+
if (refcount_read(&ni->nsui_refcnt) > 1)
5523+
continue;
5524+
5525+
/* mark being unmount */
5526+
ni->nsui_busy = true;
5527+
spin_unlock(&nn->nfsd_ssc_lock);
5528+
mntput(ni->nsui_vfsmount);
5529+
spin_lock(&nn->nfsd_ssc_lock);
5530+
5531+
/* waiters need to start from begin of list */
5532+
list_del(&ni->nsui_list);
5533+
kfree(ni);
5534+
5535+
/* wakeup ssc_connect waiters */
5536+
do_wakeup = true;
5537+
continue;
5538+
}
5539+
break;
5540+
}
5541+
if (do_wakeup)
5542+
wake_up_all(&nn->nfsd_ssc_waitq);
5543+
spin_unlock(&nn->nfsd_ssc_lock);
5544+
}
5545+
#endif
5546+
54835547
static time64_t
54845548
nfs4_laundromat(struct nfsd_net *nn)
54855549
{
@@ -5589,6 +5653,10 @@ nfs4_laundromat(struct nfsd_net *nn)
55895653
list_del_init(&nbl->nbl_lru);
55905654
free_blocked_lock(nbl);
55915655
}
5656+
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
5657+
/* service the server-to-server copy delayed unmount list */
5658+
nfsd4_ssc_expire_umount(nn);
5659+
#endif
55925660
out:
55935661
return max_t(time64_t, lt.new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
55945662
}
@@ -7506,6 +7574,9 @@ nfs4_state_shutdown_net(struct net *net)
75067574

75077575
nfsd4_client_tracking_exit(net);
75087576
nfs4_state_destroy_net(net);
7577+
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
7578+
nfsd4_ssc_shutdown_umount(nn);
7579+
#endif
75097580
}
75107581

75117582
void

fs/nfsd/nfsd.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,10 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
484484
extern int nfsd4_is_junction(struct dentry *dentry);
485485
extern int register_cld_notifier(void);
486486
extern void unregister_cld_notifier(void);
487+
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
488+
extern void nfsd4_ssc_init_umount_work(struct nfsd_net *nn);
489+
#endif
490+
487491
#else /* CONFIG_NFSD_V4 */
488492
static inline int nfsd4_is_junction(struct dentry *dentry)
489493
{

fs/nfsd/nfssvc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,9 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
403403
if (ret)
404404
goto out_filecache;
405405

406+
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
407+
nfsd4_ssc_init_umount_work(nn);
408+
#endif
406409
nn->nfsd_net_up = true;
407410
return 0;
408411

include/linux/nfs_ssc.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#include <linux/nfs_fs.h>
11+
#include <linux/sunrpc/svc.h>
1112

1213
extern struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl;
1314

@@ -52,6 +53,19 @@ static inline void nfs42_ssc_close(struct file *filep)
5253
if (nfs_ssc_client_tbl.ssc_nfs4_ops)
5354
(*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_close)(filep);
5455
}
56+
57+
struct nfsd4_ssc_umount_item {
58+
struct list_head nsui_list;
59+
bool nsui_busy;
60+
/*
61+
* nsui_refcnt inited to 2, 1 on list and 1 for consumer. Entry
62+
* is removed when refcnt drops to 1 and nsui_expire expires.
63+
*/
64+
refcount_t nsui_refcnt;
65+
unsigned long nsui_expire;
66+
struct vfsmount *nsui_vfsmount;
67+
char nsui_ipaddr[RPC_MAX_ADDRBUFLEN];
68+
};
5569
#endif
5670

5771
/*

0 commit comments

Comments
 (0)