Skip to content

Commit 89c905b

Browse files
author
J. Bruce Fields
committed
nfsd: allow forced expiration of NFSv4 clients
NFSv4 clients are automatically expired and all their locks removed if they don't contact the server for a certain amount of time (the lease period, 90 seconds by default). There can still be situations where that's not enough, so allow userspace to force expiry by writing "expire\n" to the new nfsd/client/#/ctl file. (The generic "ctl" name is because I expect we may want to allow other operations on clients in the future.) The write will not return until the client is expired and all of its locks and other state removed. The fault injection code also provides a way of expiring clients, but it fails if there are any in-progress RPC's referencing the client. Also, its method of selecting a client to expire is a little more primitive--it uses an IP address, which can't always uniquely specify an NFSv4 client. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
1 parent a204f25 commit 89c905b

File tree

1 file changed

+69
-1
lines changed

1 file changed

+69
-1
lines changed

fs/nfsd/nfs4state.c

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ enum nfsd4_st_mutex_lock_subclass {
100100
*/
101101
static DECLARE_WAIT_QUEUE_HEAD(close_wq);
102102

103+
/*
104+
* A waitqueue where a writer to clients/#/ctl destroying a client can
105+
* wait for cl_rpc_users to drop to 0 and then for the client to be
106+
* unhashed.
107+
*/
108+
static DECLARE_WAIT_QUEUE_HEAD(expiry_wq);
109+
103110
static struct kmem_cache *client_slab;
104111
static struct kmem_cache *openowner_slab;
105112
static struct kmem_cache *lockowner_slab;
@@ -175,6 +182,8 @@ static void put_client_renew_locked(struct nfs4_client *clp)
175182
return;
176183
if (!is_client_expired(clp))
177184
renew_client_locked(clp);
185+
else
186+
wake_up_all(&expiry_wq);
178187
}
179188

180189
static void put_client_renew(struct nfs4_client *clp)
@@ -185,6 +194,8 @@ static void put_client_renew(struct nfs4_client *clp)
185194
return;
186195
if (!is_client_expired(clp))
187196
renew_client_locked(clp);
197+
else
198+
wake_up_all(&expiry_wq);
188199
spin_unlock(&nn->client_lock);
189200
}
190201

@@ -1910,8 +1921,11 @@ free_client(struct nfs4_client *clp)
19101921
free_session(ses);
19111922
}
19121923
rpc_destroy_wait_queue(&clp->cl_cb_waitq);
1913-
if (clp->cl_nfsd_dentry)
1924+
if (clp->cl_nfsd_dentry) {
19141925
nfsd_client_rmdir(clp->cl_nfsd_dentry);
1926+
clp->cl_nfsd_dentry = NULL;
1927+
wake_up_all(&expiry_wq);
1928+
}
19151929
drop_client(clp);
19161930
}
19171931

@@ -2006,6 +2020,7 @@ __destroy_client(struct nfs4_client *clp)
20062020
if (clp->cl_cb_conn.cb_xprt)
20072021
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
20082022
free_client(clp);
2023+
wake_up_all(&expiry_wq);
20092024
}
20102025

20112026
static void
@@ -2484,9 +2499,62 @@ static const struct file_operations client_states_fops = {
24842499
.release = client_opens_release,
24852500
};
24862501

2502+
/*
2503+
* Normally we refuse to destroy clients that are in use, but here the
2504+
* administrator is telling us to just do it. We also want to wait
2505+
* so the caller has a guarantee that the client's locks are gone by
2506+
* the time the write returns:
2507+
*/
2508+
void force_expire_client(struct nfs4_client *clp)
2509+
{
2510+
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
2511+
bool already_expired;
2512+
2513+
spin_lock(&clp->cl_lock);
2514+
clp->cl_time = 0;
2515+
spin_unlock(&clp->cl_lock);
2516+
2517+
wait_event(expiry_wq, atomic_read(&clp->cl_rpc_users) == 0);
2518+
spin_lock(&nn->client_lock);
2519+
already_expired = list_empty(&clp->cl_lru);
2520+
if (!already_expired)
2521+
unhash_client_locked(clp);
2522+
spin_unlock(&nn->client_lock);
2523+
2524+
if (!already_expired)
2525+
expire_client(clp);
2526+
else
2527+
wait_event(expiry_wq, clp->cl_nfsd_dentry == NULL);
2528+
}
2529+
2530+
static ssize_t client_ctl_write(struct file *file, const char __user *buf,
2531+
size_t size, loff_t *pos)
2532+
{
2533+
char *data;
2534+
struct nfs4_client *clp;
2535+
2536+
data = simple_transaction_get(file, buf, size);
2537+
if (IS_ERR(data))
2538+
return PTR_ERR(data);
2539+
if (size != 7 || 0 != memcmp(data, "expire\n", 7))
2540+
return -EINVAL;
2541+
clp = get_nfsdfs_clp(file_inode(file));
2542+
if (!clp)
2543+
return -ENXIO;
2544+
force_expire_client(clp);
2545+
drop_client(clp);
2546+
return 7;
2547+
}
2548+
2549+
static const struct file_operations client_ctl_fops = {
2550+
.write = client_ctl_write,
2551+
.release = simple_transaction_release,
2552+
};
2553+
24872554
static const struct tree_descr client_files[] = {
24882555
[0] = {"info", &client_info_fops, S_IRUSR},
24892556
[1] = {"states", &client_states_fops, S_IRUSR},
2557+
[2] = {"ctl", &client_ctl_fops, S_IRUSR|S_IWUSR},
24902558
[3] = {""},
24912559
};
24922560

0 commit comments

Comments
 (0)