Received: by 2002:a25:ab43:0:0:0:0:0 with SMTP id u61csp2021188ybi; Thu, 20 Jun 2019 07:52:31 -0700 (PDT) X-Google-Smtp-Source: APXvYqxmqhBsfAV65Meda7GJoZcTTPgktFoLSNUOZatmGryiLBCqih+By8VHO0nsXC+SdLoSeM8x X-Received: by 2002:a17:902:e512:: with SMTP id ck18mr18586182plb.53.1561042351439; Thu, 20 Jun 2019 07:52:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1561042351; cv=none; d=google.com; s=arc-20160816; b=ii/0p5vVdogW3/OSBjsf3AwHGcXjU/VCvAKtm05K19q2NzLd2A1Ac54rPuNweYmDHr MJz1yEvnKftVYBSBJ614yDrnevANXOfhPR9z5ShWrouQIy74DnkSlMpvJh5VKi3cX8ZL 0Jl/GwICWZYQgPjU8aUPls6aXXorPx4cIx5vwceS754FKGco9QKYdbUehNb/pm/9hZGn jAqslVzLHh0EKWX7gV3dyIGAKFrgihfsgx4MduGVmhUpyH5aI2lEN6ggIIC6XRyTXt+u 1Zvjy/m/X+j1bf7ChBbLAKPzfCbET2aMb1rvrxuPvbk5zNZt2tCKDvJWtmHQxoQ1Mh0U 5bKw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from; bh=l5ik91ndnV+qi0Mv7x2AimH6uN2Oge5Pgkw3tdvB9X8=; b=AerR3lCwcZfwV0kBOMXE7ZioeJwVmS3C/a9PC3IJ23tIYgrXmsusTiNaeG3qEoQ0EM R9h3Rpegvqjj75cJn4BftQENAxs21M3HiLWKiONEX8MTEWp7/2359xKYS3DPpazCR5Zo 1gHq3aKhCc7UdcwZYYDF1JLxn3gvqqgT2avIH5V2RKKk56C1nA6gp/i0ArbKmizSs5/K PUeJHkLoTN6OH+v1E7Of74BJq+3974HjWD4gKoddVdgT7uEhbSNFpLIk0dQBhrFyhMj5 ch4YkuhrsD7tMh0qU9OS262brqbiuEJAI9Ib5zil5aTyiZ4DZHus3BRYmWzzeO+Z9GEc sGKw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-nfs-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-nfs-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id y134si3121023pfc.285.2019.06.20.07.52.17; Thu, 20 Jun 2019 07:52:31 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-nfs-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-nfs-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-nfs-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727552AbfFTOvY (ORCPT + 99 others); Thu, 20 Jun 2019 10:51:24 -0400 Received: from fieldses.org ([173.255.197.46]:43456 "EHLO fieldses.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731678AbfFTOvW (ORCPT ); Thu, 20 Jun 2019 10:51:22 -0400 Received: by fieldses.org (Postfix, from userid 2815) id 39E6A6803; Thu, 20 Jun 2019 10:51:21 -0400 (EDT) From: "J. Bruce Fields" To: linux-nfs@vger.kernel.org Cc: "J. Bruce Fields" Subject: [PATCH 14/16] nfsd: allow forced expiration of NFSv4 clients Date: Thu, 20 Jun 2019 10:51:13 -0400 Message-Id: <1561042275-12723-15-git-send-email-bfields@redhat.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1561042275-12723-1-git-send-email-bfields@redhat.com> References: <1561042275-12723-1-git-send-email-bfields@redhat.com> Sender: linux-nfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org From: "J. Bruce Fields" 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 --- fs/nfsd/nfs4state.c | 70 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 63f6b87e178e..12e370e62453 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -100,6 +100,13 @@ enum nfsd4_st_mutex_lock_subclass { */ static DECLARE_WAIT_QUEUE_HEAD(close_wq); +/* + * A waitqueue where a writer to clients/#/ctl destroying a client can + * wait for cl_rpc_users to drop to 0 and then for the client to be + * unhashed. + */ +static DECLARE_WAIT_QUEUE_HEAD(expiry_wq); + static struct kmem_cache *client_slab; static struct kmem_cache *openowner_slab; static struct kmem_cache *lockowner_slab; @@ -175,6 +182,8 @@ static void put_client_renew_locked(struct nfs4_client *clp) return; if (!is_client_expired(clp)) renew_client_locked(clp); + else + wake_up_all(&expiry_wq); } static void put_client_renew(struct nfs4_client *clp) @@ -185,6 +194,8 @@ static void put_client_renew(struct nfs4_client *clp) return; if (!is_client_expired(clp)) renew_client_locked(clp); + else + wake_up_all(&expiry_wq); spin_unlock(&nn->client_lock); } @@ -1910,8 +1921,11 @@ free_client(struct nfs4_client *clp) free_session(ses); } rpc_destroy_wait_queue(&clp->cl_cb_waitq); - if (clp->cl_nfsd_dentry) + if (clp->cl_nfsd_dentry) { nfsd_client_rmdir(clp->cl_nfsd_dentry); + clp->cl_nfsd_dentry = NULL; + wake_up_all(&expiry_wq); + } drop_client(clp); } @@ -2006,6 +2020,7 @@ __destroy_client(struct nfs4_client *clp) if (clp->cl_cb_conn.cb_xprt) svc_xprt_put(clp->cl_cb_conn.cb_xprt); free_client(clp); + wake_up_all(&expiry_wq); } static void @@ -2484,9 +2499,62 @@ static const struct file_operations client_states_fops = { .release = client_opens_release, }; +/* + * Normally we refuse to destroy clients that are in use, but here the + * administrator is telling us to just do it. We also want to wait + * so the caller has a guarantee that the client's locks are gone by + * the time the write returns: + */ +void force_expire_client(struct nfs4_client *clp) +{ + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + bool already_expired; + + spin_lock(&clp->cl_lock); + clp->cl_time = 0; + spin_unlock(&clp->cl_lock); + + wait_event(expiry_wq, atomic_read(&clp->cl_rpc_users) == 0); + spin_lock(&nn->client_lock); + already_expired = list_empty(&clp->cl_lru); + if (!already_expired) + unhash_client_locked(clp); + spin_unlock(&nn->client_lock); + + if (!already_expired) + expire_client(clp); + else + wait_event(expiry_wq, clp->cl_nfsd_dentry == NULL); +} + +static ssize_t client_ctl_write(struct file *file, const char __user *buf, + size_t size, loff_t *pos) +{ + char *data; + struct nfs4_client *clp; + + data = simple_transaction_get(file, buf, size); + if (IS_ERR(data)) + return PTR_ERR(data); + if (size != 7 || 0 != memcmp(data, "expire\n", 7)) + return -EINVAL; + clp = get_nfsdfs_clp(file_inode(file)); + if (!clp) + return -ENXIO; + force_expire_client(clp); + drop_client(clp); + return 7; +} + +static const struct file_operations client_ctl_fops = { + .write = client_ctl_write, + .release = simple_transaction_release, +}; + static const struct tree_descr client_files[] = { [0] = {"info", &client_info_fops, S_IRUSR}, [1] = {"states", &client_states_fops, S_IRUSR}, + [2] = {"ctl", &client_ctl_fops, S_IRUSR|S_IWUSR}, [3] = {""}, }; -- 2.21.0