[PATCH 2/4] nfsd: use mutex for global config management and clean up on module unload

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



nfsd_mutex is used for two quite different things:
1/ it prevents races when start/stoping global resources:
   the filecache and the v4 state table
2/ it prevents races for per-netns config, typically
   ensure config changes are synchronised w.r.t. server
   startup/shutdown.

This patch splits out the first used.  A subsequent patch improves the
second.

"nfsd_users" is replaced by a simple bool that records if global
initialisation has completed.  If not a new mutex (nfsd_startup_mutex)
is taken, the check is repeated, and if needed the initialisation is
done.

Cleanup is now left to module unload (nfsd_exit()) so there is no
possibility of races with server shutdown.

This replaces NFSD_FILE_CACHE_UP which is effective just a flag which
says nfsd_users is non-zero.

We no longer need to clear the filecache stats because all nfsd-local
memory is effectively cleared on unload.

Signed-off-by: NeilBrown <neil@xxxxxxxxxx>
---
 fs/nfsd/export.c    |  6 ----
 fs/nfsd/filecache.c | 75 ++++++++++-----------------------------------
 fs/nfsd/nfsctl.c    |  5 +++
 fs/nfsd/nfsd.h      |  2 ++
 fs/nfsd/nfssvc.c    | 34 ++++++++++----------
 5 files changed, 40 insertions(+), 82 deletions(-)

diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index cadfc2bae60e..1ea3d72ef5c9 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -243,13 +243,7 @@ static struct cache_head *expkey_alloc(void)
 
 static void expkey_flush(void)
 {
-	/*
-	 * Take the nfsd_mutex here to ensure that the file cache is not
-	 * destroyed while we're in the middle of flushing.
-	 */
-	mutex_lock(&nfsd_mutex);
 	nfsd_file_cache_purge(current->nsproxy->net_ns);
-	mutex_unlock(&nfsd_mutex);
 }
 
 static const struct cache_detail svc_expkey_cache_template = {
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index e108b6c705b4..bd636d0bc05a 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -50,8 +50,6 @@
 
 #define NFSD_LAUNDRETTE_DELAY		     (2 * HZ)
 
-#define NFSD_FILE_CACHE_UP		     (0)
-
 /* We only care about NFSD_MAY_READ/WRITE for this cache */
 #define NFSD_FILE_MAY_MASK	(NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
 
@@ -70,7 +68,6 @@ struct nfsd_fcache_disposal {
 static struct kmem_cache		*nfsd_file_slab;
 static struct kmem_cache		*nfsd_file_mark_slab;
 static struct list_lru			nfsd_file_lru;
-static unsigned long			nfsd_file_flags;
 static struct fsnotify_group		*nfsd_file_fsnotify_group;
 static struct delayed_work		nfsd_filecache_laundrette;
 static struct rhltable			nfsd_file_rhltable
@@ -112,9 +109,9 @@ static const struct rhashtable_params nfsd_file_rhash_params = {
 static void
 nfsd_file_schedule_laundrette(void)
 {
-	if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
-		queue_delayed_work(system_unbound_wq, &nfsd_filecache_laundrette,
-				   NFSD_LAUNDRETTE_DELAY);
+	queue_delayed_work(system_unbound_wq,
+			   &nfsd_filecache_laundrette,
+			   NFSD_LAUNDRETTE_DELAY);
 }
 
 static void
@@ -795,10 +792,6 @@ nfsd_file_cache_init(void)
 {
 	int ret;
 
-	lockdep_assert_held(&nfsd_mutex);
-	if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
-		return 0;
-
 	ret = rhltable_init(&nfsd_file_rhltable, &nfsd_file_rhash_params);
 	if (ret)
 		goto out;
@@ -853,8 +846,6 @@ nfsd_file_cache_init(void)
 
 	INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
 out:
-	if (ret)
-		clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags);
 	return ret;
 out_notifier:
 	lease_unregister_notifier(&nfsd_file_lease_notifier);
@@ -872,15 +863,15 @@ nfsd_file_cache_init(void)
 }
 
 /**
- * __nfsd_file_cache_purge: clean out the cache for shutdown
+ * nfsd_file_cache_purge: clean out the cache for shutdown
  * @net: net-namespace to shut down the cache (may be NULL)
  *
  * Walk the nfsd_file cache and close out any that match @net. If @net is NULL,
  * then close out everything. Called when an nfsd instance is being shut down,
  * and when the exports table is flushed.
  */
-static void
-__nfsd_file_cache_purge(struct net *net)
+void
+nfsd_file_cache_purge(struct net *net)
 {
 	struct rhashtable_iter iter;
 	struct nfsd_file *nf;
@@ -950,19 +941,6 @@ nfsd_file_cache_start_net(struct net *net)
 	return nn->fcache_disposal ? 0 : -ENOMEM;
 }
 
-/**
- * nfsd_file_cache_purge - Remove all cache items associated with @net
- * @net: target net namespace
- *
- */
-void
-nfsd_file_cache_purge(struct net *net)
-{
-	lockdep_assert_held(&nfsd_mutex);
-	if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
-		__nfsd_file_cache_purge(net);
-}
-
 void
 nfsd_file_cache_shutdown_net(struct net *net)
 {
@@ -973,12 +951,6 @@ nfsd_file_cache_shutdown_net(struct net *net)
 void
 nfsd_file_cache_shutdown(void)
 {
-	int i;
-
-	lockdep_assert_held(&nfsd_mutex);
-	if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
-		return;
-
 	lease_unregister_notifier(&nfsd_file_lease_notifier);
 	shrinker_free(nfsd_file_shrinker);
 	/*
@@ -986,7 +958,7 @@ nfsd_file_cache_shutdown(void)
 	 * calling nfsd_file_cache_purge
 	 */
 	cancel_delayed_work_sync(&nfsd_filecache_laundrette);
-	__nfsd_file_cache_purge(NULL);
+	nfsd_file_cache_purge(NULL);
 	list_lru_destroy(&nfsd_file_lru);
 	rcu_barrier();
 	fsnotify_put_group(nfsd_file_fsnotify_group);
@@ -997,15 +969,6 @@ nfsd_file_cache_shutdown(void)
 	kmem_cache_destroy(nfsd_file_mark_slab);
 	nfsd_file_mark_slab = NULL;
 	rhltable_destroy(&nfsd_file_rhltable);
-
-	for_each_possible_cpu(i) {
-		per_cpu(nfsd_file_cache_hits, i) = 0;
-		per_cpu(nfsd_file_acquisitions, i) = 0;
-		per_cpu(nfsd_file_allocations, i) = 0;
-		per_cpu(nfsd_file_releases, i) = 0;
-		per_cpu(nfsd_file_total_age, i) = 0;
-		per_cpu(nfsd_file_evictions, i) = 0;
-	}
 }
 
 static struct nfsd_file *
@@ -1345,23 +1308,17 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
 	unsigned long hits = 0, acquisitions = 0;
 	unsigned int i, count = 0, buckets = 0;
 	unsigned long lru = 0, total_age = 0;
+	struct bucket_table *tbl;
+	struct rhashtable *ht;
 
-	/* Serialize with server shutdown */
-	mutex_lock(&nfsd_mutex);
-	if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
-		struct bucket_table *tbl;
-		struct rhashtable *ht;
-
-		lru = list_lru_count(&nfsd_file_lru);
+	lru = list_lru_count(&nfsd_file_lru);
 
-		rcu_read_lock();
-		ht = &nfsd_file_rhltable.ht;
-		count = atomic_read(&ht->nelems);
-		tbl = rht_dereference_rcu(ht->tbl, ht);
-		buckets = tbl->size;
-		rcu_read_unlock();
-	}
-	mutex_unlock(&nfsd_mutex);
+	rcu_read_lock();
+	ht = &nfsd_file_rhltable.ht;
+	count = atomic_read(&ht->nelems);
+	tbl = rht_dereference_rcu(ht->tbl, ht);
+	buckets = tbl->size;
+	rcu_read_unlock();
 
 	for_each_possible_cpu(i) {
 		hits += per_cpu(nfsd_file_cache_hits, i);
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 6b0cad81b5fa..2a1e54af89e5 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -2371,6 +2371,11 @@ static int __init init_nfsd(void)
 
 static void __exit exit_nfsd(void)
 {
+	if (nfsd_is_started()) {
+		nfs4_state_shutdown();
+		nfsd_file_cache_shutdown();
+	}
+
 	remove_proc_entry("fs/nfs/exports", NULL);
 	remove_proc_entry("fs/nfs", NULL);
 	genl_unregister_family(&nfsd_nl_family);
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 1bfd0b4e9af7..6c432133608f 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -80,6 +80,8 @@ extern const struct svc_version	nfsd_version2, nfsd_version3, nfsd_version4;
 extern struct mutex		nfsd_mutex;
 extern atomic_t			nfsd_th_cnt;		/* number of available threads */
 
+bool nfsd_is_started(void);
+
 extern const struct seq_operations nfs_exports_op;
 
 /*
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 82b0111ac469..0eb144426e95 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -270,40 +270,42 @@ static int nfsd_init_socks(struct net *net, const struct cred *cred)
 	return 0;
 }
 
-static int nfsd_users = 0;
+static bool nfsd_started;
+static DEFINE_MUTEX(nfsd_startup_mutex);
+
+bool nfsd_is_started(void)
+{
+	return smp_load_acquire(&nfsd_started);
+}
 
 static int nfsd_startup_generic(void)
 {
-	int ret;
+	int ret = 0;
 
-	if (nfsd_users++)
+	if (nfsd_is_started())
 		return 0;
+	mutex_lock(&nfsd_startup_mutex);
+	if (nfsd_is_started())
+		goto out_unlock;
 
 	ret = nfsd_file_cache_init();
 	if (ret)
-		goto dec_users;
+		goto out_unlock;
 
 	ret = nfs4_state_start();
 	if (ret)
 		goto out_file_cache;
+	smp_store_release(&nfsd_started, true);
+	mutex_unlock(&nfsd_startup_mutex);
 	return 0;
 
 out_file_cache:
 	nfsd_file_cache_shutdown();
-dec_users:
-	nfsd_users--;
+out_unlock:
+	mutex_unlock(&nfsd_startup_mutex);
 	return ret;
 }
 
-static void nfsd_shutdown_generic(void)
-{
-	if (--nfsd_users)
-		return;
-
-	nfs4_state_shutdown();
-	nfsd_file_cache_shutdown();
-}
-
 static bool nfsd_needs_lockd(struct nfsd_net *nn)
 {
 	return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST);
@@ -416,7 +418,6 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
 		nn->lockd_up = false;
 	}
 out_socks:
-	nfsd_shutdown_generic();
 	return ret;
 }
 
@@ -443,7 +444,6 @@ static void nfsd_shutdown_net(struct net *net)
 	percpu_ref_exit(&nn->nfsd_net_ref);
 
 	nn->nfsd_net_up = false;
-	nfsd_shutdown_generic();
 }
 
 static DEFINE_SPINLOCK(nfsd_notifier_lock);
-- 
2.49.0





[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux