2022-07-12 20:25:20

by Dai Ngo

[permalink] [raw]
Subject:

s patch series enforces a limit on the number of v4 clients allowed
in the system. With Courteous server support there are potentially a
lots courtesy clients exist in the system that use up memory resource
preventing them to be used by other components in the system. Also
without a limit on the number of clients, the number of clients can
grow to a very large number even for system with small memory configuration
eventually render the system into an unusable state.

---

Dai Ngo (2):
NFSD: keep track of the number of v4 clients in the system
NFSD: limit the number of v4 clients to 4096 per 4GB of system memory

fs/nfsd/netns.h | 3 +++
fs/nfsd/nfs4state.c | 25 +++++++++++++++++++------
fs/nfsd/nfsctl.c | 10 ++++++++++
3 files changed, 32 insertions(+), 6 deletions(-)

--
Dai Ngo


2022-07-12 20:25:20

by Dai Ngo

[permalink] [raw]
Subject: [PATCH 1/2] NFSD: keep track of the number of v4 clients in the system

Add counter nfs4_client_count to keep track of the total number
of v4 clients, including courtesy clients, in the system.

Signed-off-by: Dai Ngo <[email protected]>
---
fs/nfsd/netns.h | 2 ++
fs/nfsd/nfs4state.c | 8 ++++++--
fs/nfsd/nfsctl.c | 2 ++
3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 1b1a962a1804..ce864f001a3e 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -189,6 +189,8 @@ struct nfsd_net {
struct nfsd_fcache_disposal *fcache_disposal;

siphash_key_t siphash_key;
+
+ atomic_t nfs4_client_count;
};

/* Simple check to find out if a given net was properly initialized */
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9409a0dc1b76..30e16d9e8657 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2053,7 +2053,8 @@ STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn)
* This type of memory management is somewhat inefficient, but we use it
* anyway since SETCLIENTID is not a common operation.
*/
-static struct nfs4_client *alloc_client(struct xdr_netobj name)
+static struct nfs4_client *alloc_client(struct xdr_netobj name,
+ struct nfsd_net *nn)
{
struct nfs4_client *clp;
int i;
@@ -2076,6 +2077,7 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
atomic_set(&clp->cl_rpc_users, 0);
clp->cl_cb_state = NFSD4_CB_UNKNOWN;
clp->cl_state = NFSD4_ACTIVE;
+ atomic_inc(&nn->nfs4_client_count);
atomic_set(&clp->cl_delegs_in_recall, 0);
INIT_LIST_HEAD(&clp->cl_idhash);
INIT_LIST_HEAD(&clp->cl_openowners);
@@ -2187,6 +2189,7 @@ __destroy_client(struct nfs4_client *clp)
struct nfs4_openowner *oo;
struct nfs4_delegation *dp;
struct list_head reaplist;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

INIT_LIST_HEAD(&reaplist);
spin_lock(&state_lock);
@@ -2226,6 +2229,7 @@ __destroy_client(struct nfs4_client *clp)
nfsd4_shutdown_callback(clp);
if (clp->cl_cb_conn.cb_xprt)
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
+ atomic_add_unless(&nn->nfs4_client_count, -1, 0);
free_client(clp);
wake_up_all(&expiry_wq);
}
@@ -2848,7 +2852,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct dentry *dentries[ARRAY_SIZE(client_files)];

- clp = alloc_client(name);
+ clp = alloc_client(name, nn);
if (clp == NULL)
return NULL;

diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 0621c2faf242..547f4c4b9668 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1487,6 +1487,8 @@ static __net_init int nfsd_init_net(struct net *net)
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
seqlock_init(&nn->writeverf_lock);

+ atomic_set(&nn->nfs4_client_count, 0);
+
return 0;

out_drc_error:
--
2.9.5

2022-07-12 20:25:20

by Dai Ngo

[permalink] [raw]
Subject: [PATCH 2/2] NFSD: limit the number of v4 clients to 4096 per 4GB of system memory

Currently there is no limit on how many v4 clients are supported
by the system. This can be a problem in systems with small memory
configuration to function properly when a very large number of
clients exist that creates memory shortage conditions.

This patch enforces a limit of 4096 NFSv4 clients, including courtesy
clients, per 4GB of system memory. When the number of the clients
reaches the limit, requests that create new clients are returned
with NFS4ERR_DELAY. The laundromat detects this condition and removes
older courtesy clients. Due to the overhead of the upcall to remove
the client record, the maximun number of clients the laundromat
removes on each run is limited to 128. This is done to ensure the
laundromat can still process other tasks in a timely manner.

Since there is now a limit of the number of clients, the 24-hr
idle time limit of courtesy client is no longer needed and was
removed.

Signed-off-by: Dai Ngo <[email protected]>
---
fs/nfsd/netns.h | 1 +
fs/nfsd/nfs4state.c | 17 +++++++++++++----
fs/nfsd/nfsctl.c | 8 ++++++++
3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index ce864f001a3e..8d72b302a49c 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -191,6 +191,7 @@ struct nfsd_net {
siphash_key_t siphash_key;

atomic_t nfs4_client_count;
+ unsigned int nfs4_max_clients;
};

/* Simple check to find out if a given net was properly initialized */
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 30e16d9e8657..e54db346dc00 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -126,6 +126,7 @@ static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;

static struct workqueue_struct *laundry_wq;
+#define NFSD_CLIENT_MAX_TRIM_PER_RUN 128

int nfsd4_create_laundry_wq(void)
{
@@ -2059,6 +2060,8 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name,
struct nfs4_client *clp;
int i;

+ if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients)
+ return NULL;
clp = kmem_cache_zalloc(client_slab, GFP_KERNEL);
if (clp == NULL)
return NULL;
@@ -5796,9 +5799,12 @@ static void
nfs4_get_client_reaplist(struct nfsd_net *nn, struct list_head *reaplist,
struct laundry_time *lt)
{
+ unsigned int maxreap = 0, reapcnt = 0;
struct list_head *pos, *next;
struct nfs4_client *clp;

+ if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients)
+ maxreap = NFSD_CLIENT_MAX_TRIM_PER_RUN;
INIT_LIST_HEAD(reaplist);
spin_lock(&nn->client_lock);
list_for_each_safe(pos, next, &nn->client_lru) {
@@ -5809,14 +5815,17 @@ nfs4_get_client_reaplist(struct nfsd_net *nn, struct list_head *reaplist,
break;
if (!atomic_read(&clp->cl_rpc_users))
clp->cl_state = NFSD4_COURTESY;
- if (!client_has_state(clp) ||
- ktime_get_boottime_seconds() >=
- (clp->cl_time + NFSD_COURTESY_CLIENT_TIMEOUT))
+ if (!client_has_state(clp))
goto exp_client;
if (nfs4_anylock_blockers(clp)) {
exp_client:
- if (!mark_client_expired_locked(clp))
+ if (!mark_client_expired_locked(clp)) {
list_add(&clp->cl_lru, reaplist);
+ reapcnt++;
+ }
+ } else {
+ if (reapcnt < maxreap)
+ goto exp_client;
}
}
spin_unlock(&nn->client_lock);
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 547f4c4b9668..223659e15af3 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -96,6 +96,8 @@ static ssize_t (*const write_op[])(struct file *, char *, size_t) = {
#endif
};

+#define NFS4_MAX_CLIENTS_PER_4GB 4096
+
static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
{
ino_t ino = file_inode(file)->i_ino;
@@ -1462,6 +1464,8 @@ unsigned int nfsd_net_id;
static __net_init int nfsd_init_net(struct net *net)
{
int retval;
+ unsigned long lowmem;
+ struct sysinfo si;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);

retval = nfsd_export_init(net);
@@ -1488,6 +1492,10 @@ static __net_init int nfsd_init_net(struct net *net)
seqlock_init(&nn->writeverf_lock);

atomic_set(&nn->nfs4_client_count, 0);
+ si_meminfo(&si);
+ lowmem = (si.totalram - si.totalhigh) * si.mem_unit;
+ nn->nfs4_max_clients = (((lowmem * 100) >> 32) *
+ NFS4_MAX_CLIENTS_PER_4GB) / 100;

return 0;

--
2.9.5