Commit 47e98814 authored by Mike Snitzer's avatar Mike Snitzer Committed by Anna Schumaker

nfsd: add nfsd_serv_try_get and nfsd_serv_put

Introduce nfsd_serv_try_get and nfsd_serv_put and update the nfsd code
to prevent nfsd_destroy_serv from destroying nn->nfsd_serv until any
caller of nfsd_serv_try_get releases their reference using nfsd_serv_put.

A percpu_ref is used to implement the interlock between
nfsd_destroy_serv and any caller of nfsd_serv_try_get.

This interlock is needed to properly wait for the completion of client
initiated localio calls to nfsd (that are _not_ in the context of nfsd).
Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
Reviewed-by: default avatarChuck Lever <chuck.lever@oracle.com>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Reviewed-by: default avatarNeilBrown <neilb@suse.de>
Signed-off-by: default avatarAnna Schumaker <anna.schumaker@oracle.com>
parent c63f0e48
......@@ -13,6 +13,7 @@
#include <linux/filelock.h>
#include <linux/nfs4.h>
#include <linux/percpu_counter.h>
#include <linux/percpu-refcount.h>
#include <linux/siphash.h>
#include <linux/sunrpc/stats.h>
......@@ -139,7 +140,9 @@ struct nfsd_net {
struct svc_info nfsd_info;
#define nfsd_serv nfsd_info.serv
struct percpu_ref nfsd_serv_ref;
struct completion nfsd_serv_confirm_done;
struct completion nfsd_serv_free_done;
/*
* clientid and stateid data for construction of net unique COPY
......@@ -222,6 +225,9 @@ struct nfsd_net {
extern bool nfsd_support_version(int vers);
extern unsigned int nfsd_net_id;
bool nfsd_serv_try_get(struct net *net);
void nfsd_serv_put(struct net *net);
void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn);
void nfsd_reset_write_verifier(struct nfsd_net *nn);
#endif /* __NFSD_NETNS_H__ */
......@@ -193,6 +193,34 @@ int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change
return 0;
}
bool nfsd_serv_try_get(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
return (nn && percpu_ref_tryget_live(&nn->nfsd_serv_ref));
}
void nfsd_serv_put(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
percpu_ref_put(&nn->nfsd_serv_ref);
}
static void nfsd_serv_done(struct percpu_ref *ref)
{
struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_serv_ref);
complete(&nn->nfsd_serv_confirm_done);
}
static void nfsd_serv_free(struct percpu_ref *ref)
{
struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_serv_ref);
complete(&nn->nfsd_serv_free_done);
}
/*
* Maximum number of nfsd processes
*/
......@@ -392,6 +420,7 @@ static void nfsd_shutdown_net(struct net *net)
lockd_down(net);
nn->lockd_up = false;
}
percpu_ref_exit(&nn->nfsd_serv_ref);
nn->nfsd_net_up = false;
nfsd_shutdown_generic();
}
......@@ -471,6 +500,13 @@ void nfsd_destroy_serv(struct net *net)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv = nn->nfsd_serv;
lockdep_assert_held(&nfsd_mutex);
percpu_ref_kill_and_confirm(&nn->nfsd_serv_ref, nfsd_serv_done);
wait_for_completion(&nn->nfsd_serv_confirm_done);
wait_for_completion(&nn->nfsd_serv_free_done);
/* percpu_ref_exit is called in nfsd_shutdown_net */
spin_lock(&nfsd_notifier_lock);
nn->nfsd_serv = NULL;
spin_unlock(&nfsd_notifier_lock);
......@@ -595,6 +631,13 @@ int nfsd_create_serv(struct net *net)
if (nn->nfsd_serv)
return 0;
error = percpu_ref_init(&nn->nfsd_serv_ref, nfsd_serv_free,
0, GFP_KERNEL);
if (error)
return error;
init_completion(&nn->nfsd_serv_free_done);
init_completion(&nn->nfsd_serv_confirm_done);
if (nfsd_max_blksize == 0)
nfsd_max_blksize = nfsd_get_default_max_blksize();
nfsd_reset_versions(nn);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment