Skip to content

Commit

Permalink
NFSD: delay unmount source's export after inter-server copy completed.
Browse files Browse the repository at this point in the history
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 delayed task is scheduled to
unmount the export after a configurable idle time. Each time the
export is being used again, its expire time is extended to allow
the export to remain mounted.

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 <dai.ngo@oracle.com>
  • Loading branch information
daimngo authored and intel-lab-lkp committed Apr 1, 2021
1 parent a5e13c6 commit 84c442c
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 3 deletions.
136 changes: 133 additions & 3 deletions fs/nfsd/nfs4proc.c
Expand Up @@ -55,6 +55,74 @@ module_param(inter_copy_offload_enable, bool, 0644);
MODULE_PARM_DESC(inter_copy_offload_enable,
"Enable inter server to server copy offload. Default: false");

#ifdef CONFIG_NFSD_V4_2_INTER_SSC
static int nfsd4_ssc_umount_timeout = 900000; /* default to 15 mins */
module_param(nfsd4_ssc_umount_timeout, int, 0644);
MODULE_PARM_DESC(nfsd4_ssc_umount_timeout,
"idle msecs before unmount export from source server");

static struct nfsd4_ssc_umount nfsd4_ssc_umount;

/* nfsd4_ssc_umount.nsu_lock must be held */
static void nfsd4_scc_update_umnt_timo(void)
{
struct nfsd4_ssc_umount_item *ni = 0;

if (!list_empty(&nfsd4_ssc_umount.nsu_list)) {
ni = list_first_entry(&nfsd4_ssc_umount.nsu_list,
struct nfsd4_ssc_umount_item, nsui_list);
nfsd4_ssc_umount.nsu_expire = ni->nsui_expire;
schedule_delayed_work(&nfsd4_ssc_umount.nsu_umount_work,
ni->nsui_expire - jiffies);
} else
nfsd4_ssc_umount.nsu_expire = 0;
}

void nfsd4_ssc_expire_umount(struct work_struct *work)
{
struct nfsd4_ssc_umount_item *ni = 0;
struct nfsd4_ssc_umount_item *tmp;

down_write(&nfsd4_ssc_umount.nsu_sem);
spin_lock(&nfsd4_ssc_umount.nsu_lock);
list_for_each_entry_safe(ni, tmp, &nfsd4_ssc_umount.nsu_list, nsui_list) {
if (time_after(jiffies, ni->nsui_expire)) {
list_del(&ni->nsui_list);
cancel_delayed_work(&nfsd4_ssc_umount.nsu_umount_work);
spin_unlock(&nfsd4_ssc_umount.nsu_lock);
up_write(&nfsd4_ssc_umount.nsu_sem);

mntput(ni->nsui_vfsmount);
kfree(ni);

down_write(&nfsd4_ssc_umount.nsu_sem);
spin_lock(&nfsd4_ssc_umount.nsu_lock);
continue;
}
break;
}
nfsd4_scc_update_umnt_timo();
spin_unlock(&nfsd4_ssc_umount.nsu_lock);
up_write(&nfsd4_ssc_umount.nsu_sem);
}
EXPORT_SYMBOL_GPL(nfsd4_ssc_expire_umount);

static DECLARE_DELAYED_WORK(nfsd4, nfsd4_ssc_expire_umount);

void nfsd4_ssc_init_umount_work(void)
{
if (nfsd4_ssc_umount.nsu_inited)
return;
INIT_DELAYED_WORK(&nfsd4_ssc_umount.nsu_umount_work,
nfsd4_ssc_expire_umount);
INIT_LIST_HEAD(&nfsd4_ssc_umount.nsu_list);
spin_lock_init(&nfsd4_ssc_umount.nsu_lock);
init_rwsem(&nfsd4_ssc_umount.nsu_sem);
nfsd4_ssc_umount.nsu_inited = true;
}
EXPORT_SYMBOL_GPL(nfsd4_ssc_init_umount_work);
#endif

#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#include <linux/security.h>

Expand Down Expand Up @@ -1181,6 +1249,8 @@ 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 *ni = 0;
struct nfsd4_ssc_umount_item *tmp;

naddr = &nss->u.nl4_addr;
tmp_addrlen = rpc_uaddr2sockaddr(SVC_NET(rqstp), naddr->addr,
Expand Down Expand Up @@ -1229,11 +1299,33 @@ 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);

/* wait for ssc unmount task */
down_read(&nfsd4_ssc_umount.nsu_sem);

/* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
ss_mnt = vfs_kern_mount(type, SB_KERNMOUNT, dev_name, raw_data);
module_put(type->owner);
if (IS_ERR(ss_mnt))
if (IS_ERR(ss_mnt)) {
up_read(&nfsd4_ssc_umount.nsu_sem);
goto out_free_devname;
}

/* delete work entry if it exists */
spin_lock(&nfsd4_ssc_umount.nsu_lock);
list_for_each_entry_safe(ni, tmp, &nfsd4_ssc_umount.nsu_list, nsui_list) {
if (ni->nsui_vfsmount->mnt_sb != ss_mnt->mnt_sb)
continue;
list_del(&ni->nsui_list);
cancel_delayed_work(&nfsd4_ssc_umount.nsu_umount_work);
nfsd4_scc_update_umnt_timo();
spin_unlock(&nfsd4_ssc_umount.nsu_lock);
mntput(ni->nsui_vfsmount);
kfree(ni);
goto out_done;
}
spin_unlock(&nfsd4_ssc_umount.nsu_lock);
out_done:
up_read(&nfsd4_ssc_umount.nsu_sem);

status = 0;
*mount = ss_mnt;
Expand Down Expand Up @@ -1301,10 +1393,48 @@ static void
nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src,
struct nfsd_file *dst)
{
long timeout;
struct nfsd4_ssc_umount_item *work, *tmp;
struct nfsd4_ssc_umount_item *ni = 0;

nfs42_ssc_close(src->nf_file);
fput(src->nf_file);
nfsd_file_put(dst);
mntput(ss_mnt);
fput(src->nf_file);

work = kzalloc(sizeof(*work), GFP_KERNEL);
if (!work) {
mntput(ss_mnt);
return;
}
timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);
work->nsui_vfsmount = ss_mnt;
work->nsui_expire = jiffies + timeout;

spin_lock(&nfsd4_ssc_umount.nsu_lock);
/*
* check if entry for vfsmount->mnt_sb exists, if it does
* then remove it, update expire time and re-insert at tail,
* do the mntput for this call and return. Otherwise create
* new work entry.
*/
list_for_each_entry_safe(ni, tmp, &nfsd4_ssc_umount.nsu_list,
nsui_list) {
if (ni->nsui_vfsmount->mnt_sb == ss_mnt->mnt_sb) {
list_del(&ni->nsui_list);
mntput(ss_mnt);
kfree(work);
ni->nsui_expire = jiffies + timeout;
work = ni;
break;
}
}
list_add_tail(&work->nsui_list, &nfsd4_ssc_umount.nsu_list);
if (!nfsd4_ssc_umount.nsu_expire) {
nfsd4_ssc_umount.nsu_expire = work->nsui_expire;
schedule_delayed_work(&nfsd4_ssc_umount.nsu_umount_work,
timeout);
}
spin_unlock(&nfsd4_ssc_umount.nsu_lock);
}

#else /* CONFIG_NFSD_V4_2_INTER_SSC */
Expand Down
4 changes: 4 additions & 0 deletions fs/nfsd/nfsd.h
Expand Up @@ -483,6 +483,10 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
extern int nfsd4_is_junction(struct dentry *dentry);
extern int register_cld_notifier(void);
extern void unregister_cld_notifier(void);
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
extern void nfsd4_ssc_init_umount_work(void);
#endif

#else /* CONFIG_NFSD_V4 */
static inline int nfsd4_is_junction(struct dentry *dentry)
{
Expand Down
3 changes: 3 additions & 0 deletions fs/nfsd/nfssvc.c
Expand Up @@ -322,6 +322,9 @@ static int nfsd_startup_generic(int nrservs)
ret = nfs4_state_start();
if (ret)
goto out_file_cache;
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
nfsd4_ssc_init_umount_work();
#endif
return 0;

out_file_cache:
Expand Down
17 changes: 17 additions & 0 deletions include/linux/nfs_ssc.h
Expand Up @@ -8,6 +8,7 @@
*/

#include <linux/nfs_fs.h>
#include <linux/sunrpc/svc.h>

extern struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl;

Expand Down Expand Up @@ -52,6 +53,22 @@ 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);
}

struct nfsd4_ssc_umount_item {
struct list_head nsui_list;
unsigned long nsui_expire;
struct vfsmount *nsui_vfsmount;
};

struct nfsd4_ssc_umount {
struct list_head nsu_list;
struct delayed_work nsu_umount_work;
spinlock_t nsu_lock;
struct rw_semaphore nsu_sem;
unsigned long nsu_expire;
bool nsu_inited;
};

#endif

/*
Expand Down

0 comments on commit 84c442c

Please sign in to comment.