NFSD: limit the number of v4 clients to 1024 per 1GB of system memory
authorDai Ngo <dai.ngo@oracle.com>
Fri, 15 Jul 2022 23:54:53 +0000 (16:54 -0700)
committerChuck Lever <chuck.lever@oracle.com>
Sat, 30 Jul 2022 00:16:56 +0000 (20:16 -0400)
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 1024 NFSv4 clients, including courtesy
clients, per 1GB of system memory.  When the number of the clients
reaches the limit, requests that create new clients are returned
with NFS4ERR_DELAY and the laundromat is kicked start to trim old
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 the 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 <dai.ngo@oracle.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/netns.h
fs/nfsd/nfs4state.c
fs/nfsd/nfsd.h

index ce864f001a3eeb04ed75beda94022c27ffce631e..ffe17743cc74be62a818481df64550677ed3bc5b 100644 (file)
@@ -191,6 +191,7 @@ struct nfsd_net {
        siphash_key_t           siphash_key;
 
        atomic_t                nfs4_client_count;
+       int                     nfs4_max_clients;
 };
 
 /* Simple check to find out if a given net was properly initialized */
index 4842cc827f082a0379884a20ff04fd026f315298..e46e3392d5577efc677e31a651f93900ab347967 100644 (file)
@@ -2059,6 +2059,10 @@ 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) {
+               mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
+               return NULL;
+       }
        clp = kmem_cache_zalloc(client_slab, GFP_KERNEL);
        if (clp == NULL)
                return NULL;
@@ -4336,6 +4340,9 @@ out:
 
 void nfsd4_init_leases_net(struct nfsd_net *nn)
 {
+       struct sysinfo si;
+       u64 max_clients;
+
        nn->nfsd4_lease = 90;   /* default lease time */
        nn->nfsd4_grace = 90;
        nn->somebody_reclaimed = false;
@@ -4346,6 +4353,10 @@ void nfsd4_init_leases_net(struct nfsd_net *nn)
        nn->s2s_cp_cl_id = nn->clientid_counter++;
 
        atomic_set(&nn->nfs4_client_count, 0);
+       si_meminfo(&si);
+       max_clients = (u64)si.totalram * si.mem_unit / (1024 * 1024 * 1024);
+       max_clients *= NFS4_CLIENTS_PER_GB;
+       nn->nfs4_max_clients = max_t(int, max_clients, NFS4_CLIENTS_PER_GB);
 }
 
 static void init_nfs4_replay(struct nfs4_replay *rp)
@@ -5811,9 +5822,12 @@ static void
 nfs4_get_client_reaplist(struct nfsd_net *nn, struct list_head *reaplist,
                                struct laundry_time *lt)
 {
+       unsigned int maxreap, reapcnt = 0;
        struct list_head *pos, *next;
        struct nfs4_client *clp;
 
+       maxreap = (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients) ?
+                       NFSD_CLIENT_MAX_TRIM_PER_RUN : 0;
        INIT_LIST_HEAD(reaplist);
        spin_lock(&nn->client_lock);
        list_for_each_safe(pos, next, &nn->client_lru) {
@@ -5824,14 +5838,15 @@ 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)) {
+               if (!nfs4_anylock_blockers(clp))
+                       if (reapcnt >= maxreap)
+                               continue;
 exp_client:
-                       if (!mark_client_expired_locked(clp))
-                               list_add(&clp->cl_lru, reaplist);
+               if (!mark_client_expired_locked(clp)) {
+                       list_add(&clp->cl_lru, reaplist);
+                       reapcnt++;
                }
        }
        spin_unlock(&nn->client_lock);
index ef8087691138a491e42b12e608e82a4a6c3a1653..57a468ed85c358b377af699a562c0b0fa6e21ed6 100644 (file)
@@ -341,6 +341,8 @@ void                nfsd_lockd_shutdown(void);
 
 #define NFSD_LAUNDROMAT_MINTIMEOUT      1   /* seconds */
 #define        NFSD_COURTESY_CLIENT_TIMEOUT    (24 * 60 * 60)  /* seconds */
+#define        NFSD_CLIENT_MAX_TRIM_PER_RUN    128
+#define        NFS4_CLIENTS_PER_GB             1024
 
 /*
  * The following attributes are currently not supported by the NFSv4 server: