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

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
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -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__ */
+43 −0
Original line number Diff line number Diff line
@@ -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);