[PATCH v4 6/8] NFSD: Give delegations their own state shrinker
Chuck Lever <[email protected]>
| Newsgroups | gmane.linux.nfs |
|---|---|
| Message-ID | <[email protected]> |
Since commit 44df6f439a17 ("NFSD: add delegation reaper to react to
low memory condition"), nfsd_client_shrinker has managed two
unrelated populations of objects.
One population is courtesy clients. Shrinking that population can
be done synchronously and without risk of deadlock. The shrinker
callback could return a precise count of the number of objects
that were released.
The other population is delegations. Shrinking that population
requires sending a CB_RECALL_ANY; clients are not obligated to
return any delegation. The shrinker callback is structurally
unable to report progress.
What's more, the single shrinker callback falls back to
delegation reaping only when there are no courtesy clients left to
reclaim. A single courtesy client is enough to keep a namespace's
delegations out of the count it reports.
To begin to resolve these issues, refactor the existing state
shrinker into two: one for courtesy clients and one for reaping
delegations. Each manages the size of its own population, and the
shrinker names become namespace-specific.
Signed-off-by: Chuck Lever <[email protected]>
Reviewed-by: Jeff Layton <[email protected]>
---
fs/nfsd/netns.h | 6 +++--
fs/nfsd/nfs4state.c | 77 ++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 65 insertions(+), 18 deletions(-)
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index bb62d19430bc..ef01a1cf72ac 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -240,8 +240,10 @@ struct nfsd_net {
atomic_t nfsd_courtesy_clients;
/* per-namespace; num_delegations in nfs4state.c is host-wide */
atomic_long_t nfsd_delegations;
- struct shrinker *nfsd_client_shrinker;
- struct work_struct nfsd_shrinker_work;
+ struct shrinker *nfsd_courtesy_shrinker;
+ struct shrinker *nfsd_deleg_shrinker;
+ struct work_struct nfsd_courtesy_work;
+ struct work_struct nfsd_deleg_work;
/* last time an admin-revoke happened for NFSv4.0 */
time64_t nfs40_last_revoke;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2c169bfabfbe..2dbc49a6dcaa 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5562,16 +5562,27 @@ nfsd4_init_slabs(void)
}
static unsigned long
-nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
+nfsd4_courtesy_shrinker_count(struct shrinker *shrink,
+ struct shrink_control *sc)
{
struct nfsd_net *nn = shrink->private_data;
long count;
count = atomic_read(&nn->nfsd_courtesy_clients);
- if (!count)
- count = atomic_long_read(&nn->nfsd_delegations);
if (count)
- queue_work(laundry_wq, &nn->nfsd_shrinker_work);
+ queue_work(laundry_wq, &nn->nfsd_courtesy_work);
+ return (unsigned long)count;
+}
+
+static unsigned long
+nfsd4_deleg_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
+{
+ struct nfsd_net *nn = shrink->private_data;
+ long count;
+
+ count = atomic_long_read(&nn->nfsd_delegations);
+ if (count)
+ queue_work(laundry_wq, &nn->nfsd_deleg_work);
return (unsigned long)count;
}
@@ -5581,6 +5592,25 @@ nfsd4_state_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
return SHRINK_STOP;
}
+static struct shrinker *
+nfsd4_alloc_state_shrinker(struct nfsd_net *nn, const char *name,
+ unsigned long (*count)(struct shrinker *,
+ struct shrink_control *))
+{
+ struct shrinker *shrink;
+
+ shrink = shrinker_alloc(0, "%s:%s", name, nn->nfsd_name);
+ if (!shrink)
+ return NULL;
+
+ shrink->count_objects = count;
+ shrink->scan_objects = nfsd4_state_shrinker_scan;
+ shrink->private_data = nn;
+
+ shrinker_register(shrink);
+ return shrink;
+}
+
void
nfsd4_init_leases_net(struct nfsd_net *nn)
{
@@ -7994,12 +8024,20 @@ deleg_reaper(struct nfsd_net *nn)
}
static void
-nfsd4_state_shrinker_worker(struct work_struct *work)
+nfsd4_courtesy_shrinker_worker(struct work_struct *work)
{
struct nfsd_net *nn = container_of(work, struct nfsd_net,
- nfsd_shrinker_work);
+ nfsd_courtesy_work);
courtesy_client_reaper(nn);
+}
+
+static void
+nfsd4_deleg_shrinker_worker(struct work_struct *work)
+{
+ struct nfsd_net *nn = container_of(work, struct nfsd_net,
+ nfsd_deleg_work);
+
deleg_reaper(nn);
}
@@ -9962,21 +10000,26 @@ static int nfs4_state_create_net(struct net *net)
INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
/* Make sure this cannot run until client tracking is initialised */
disable_delayed_work(&nn->laundromat_work);
- INIT_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker);
+ INIT_WORK(&nn->nfsd_courtesy_work, nfsd4_courtesy_shrinker_worker);
+ INIT_WORK(&nn->nfsd_deleg_work, nfsd4_deleg_shrinker_worker);
get_net(net);
- nn->nfsd_client_shrinker = shrinker_alloc(0, "nfsd-client");
- if (!nn->nfsd_client_shrinker)
+ nn->nfsd_courtesy_shrinker =
+ nfsd4_alloc_state_shrinker(nn, "nfsd-courtesy",
+ nfsd4_courtesy_shrinker_count);
+ if (!nn->nfsd_courtesy_shrinker)
goto err_shrinker;
- nn->nfsd_client_shrinker->scan_objects = nfsd4_state_shrinker_scan;
- nn->nfsd_client_shrinker->count_objects = nfsd4_state_shrinker_count;
- nn->nfsd_client_shrinker->private_data = nn;
-
- shrinker_register(nn->nfsd_client_shrinker);
+ nn->nfsd_deleg_shrinker =
+ nfsd4_alloc_state_shrinker(nn, "nfsd-delegation",
+ nfsd4_deleg_shrinker_count);
+ if (!nn->nfsd_deleg_shrinker)
+ goto err_deleg_shrinker;
return 0;
+err_deleg_shrinker:
+ shrinker_free(nn->nfsd_courtesy_shrinker);
err_shrinker:
put_net(net);
kfree(nn->sessionid_hashtbl);
@@ -10077,8 +10120,10 @@ nfs4_state_shutdown_net(struct net *net)
struct list_head *pos, *next, reaplist;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- shrinker_free(nn->nfsd_client_shrinker);
- cancel_work_sync(&nn->nfsd_shrinker_work);
+ shrinker_free(nn->nfsd_courtesy_shrinker);
+ shrinker_free(nn->nfsd_deleg_shrinker);
+ cancel_work_sync(&nn->nfsd_courtesy_work);
+ cancel_work_sync(&nn->nfsd_deleg_work);
disable_delayed_work_sync(&nn->laundromat_work);
locks_end_grace(&nn->nfsd4_manager);
--
2.54.0