2022-08-26 23:10:28

by Dai Ngo

[permalink] [raw]
Subject: [PATCH 2/2] NFSD: add shrinker to reap courtesy clients on low memory condition

Add the courtesy client shrinker to react to low memory condition
triggered by the memory shrinker.

On the shrinker's count callback, we increment a callback counter
and return the number of outstanding courtesy clients. When the
laundromat runs, it checks if this counter is not zero and starts
reaping old courtesy clients. The maximum number of clients to be
reaped is limited to NFSD_CIENT_MAX_TRIM_PER_RUN (128). This limit
is to prevent the laundromat from spending too much time reaping
the clients and not processing other tasks in a timely manner.

The laundromat is rescheduled to run sooner if it detects low
low memory condition and there are more clients to reap.

On the shrinker's scan callback, we return the number of clients
That were reaped since the last scan callback. We can not reap
the clients on the scan callback context since destroying the
client might require call into the underlying filesystem or other
subsystems which might allocate memory which can cause deadlock.

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

diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 2695dff1378a..2a604951623f 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -194,6 +194,9 @@ struct nfsd_net {
int nfs4_max_clients;

atomic_t nfsd_courtesy_client_count;
+ atomic_t nfsd_client_shrinker_cb_count;
+ atomic_t nfsd_client_shrinker_reapcount;
+ struct shrinker nfsd_client_shrinker;
};

/* 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 3d8d7ebb5b91..9d5a20f0c3c4 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4341,7 +4341,39 @@ nfsd4_init_slabs(void)
return -ENOMEM;
}

-void nfsd4_init_leases_net(struct nfsd_net *nn)
+static unsigned long
+nfsd_courtesy_client_count(struct shrinker *shrink, struct shrink_control *sc)
+{
+ struct nfsd_net *nn = container_of(shrink,
+ struct nfsd_net, nfsd_client_shrinker);
+
+ atomic_inc(&nn->nfsd_client_shrinker_cb_count);
+ return (unsigned long)atomic_read(&nn->nfsd_courtesy_client_count);
+}
+
+static unsigned long
+nfsd_courtesy_client_scan(struct shrinker *shrink, struct shrink_control *sc)
+{
+ struct nfsd_net *nn = container_of(shrink,
+ struct nfsd_net, nfsd_client_shrinker);
+ unsigned long cnt;
+
+ cnt = atomic_read(&nn->nfsd_client_shrinker_reapcount);
+ atomic_set(&nn->nfsd_client_shrinker_reapcount, 0);
+ return cnt;
+}
+
+static int
+nfsd_register_client_shrinker(struct nfsd_net *nn)
+{
+ nn->nfsd_client_shrinker.scan_objects = nfsd_courtesy_client_scan;
+ nn->nfsd_client_shrinker.count_objects = nfsd_courtesy_client_count;
+ nn->nfsd_client_shrinker.seeks = DEFAULT_SEEKS;
+ return register_shrinker(&nn->nfsd_client_shrinker, "nfsd-client");
+}
+
+int
+nfsd4_init_leases_net(struct nfsd_net *nn)
{
struct sysinfo si;
u64 max_clients;
@@ -4362,6 +4394,8 @@ void nfsd4_init_leases_net(struct nfsd_net *nn)
nn->nfs4_max_clients = max_t(int, max_clients, NFS4_CLIENTS_PER_GB);

atomic_set(&nn->nfsd_courtesy_client_count, 0);
+ atomic_set(&nn->nfsd_client_shrinker_cb_count, 0);
+ return nfsd_register_client_shrinker(nn);
}

static void init_nfs4_replay(struct nfs4_replay *rp)
@@ -5870,12 +5904,17 @@ static void
nfs4_get_client_reaplist(struct nfsd_net *nn, struct list_head *reaplist,
struct laundry_time *lt)
{
- unsigned int oldstate, maxreap, reapcnt = 0;
+ unsigned int oldstate, maxreap = 0, reapcnt = 0;
+ int cb_cnt;
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;
+ cb_cnt = atomic_read(&nn->nfsd_client_shrinker_cb_count);
+ if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients ||
+ cb_cnt) {
+ maxreap = NFSD_CLIENT_MAX_TRIM_PER_RUN;
+ atomic_set(&nn->nfsd_client_shrinker_cb_count, 0);
+ }
INIT_LIST_HEAD(reaplist);
spin_lock(&nn->client_lock);
list_for_each_safe(pos, next, &nn->client_lru) {
@@ -5902,6 +5941,8 @@ nfs4_get_client_reaplist(struct nfsd_net *nn, struct list_head *reaplist,
}
}
spin_unlock(&nn->client_lock);
+ if (cb_cnt)
+ atomic_add(reapcnt, &nn->nfsd_client_shrinker_reapcount);
}

static time64_t
@@ -5942,6 +5983,8 @@ nfs4_laundromat(struct nfsd_net *nn)
list_del_init(&clp->cl_lru);
expire_client(clp);
}
+ if (atomic_read(&nn->nfsd_client_shrinker_cb_count) > 0)
+ lt.new_timeo = NFSD_LAUNDROMAT_MINTIMEOUT;
spin_lock(&state_lock);
list_for_each_safe(pos, next, &nn->del_recall_lru) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 917fa1892fd2..597a26ad4183 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1481,11 +1481,12 @@ static __net_init int nfsd_init_net(struct net *net)
goto out_idmap_error;
nn->nfsd_versions = NULL;
nn->nfsd4_minorversions = NULL;
+ retval = nfsd4_init_leases_net(nn);
+ if (retval)
+ goto out_drc_error;
retval = nfsd_reply_cache_init(nn);
if (retval)
goto out_drc_error;
- nfsd4_init_leases_net(nn);
-
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
seqlock_init(&nn->writeverf_lock);

@@ -1507,6 +1508,7 @@ static __net_exit void nfsd_exit_net(struct net *net)
nfsd_idmap_shutdown(net);
nfsd_export_shutdown(net);
nfsd_netns_free_versions(net_generic(net, nfsd_net_id));
+ nfsd4_leases_net_shutdown(nn);
}

static struct pernet_operations nfsd_net_ops = {
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 57a468ed85c3..ed4b48a9b260 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -498,7 +498,11 @@ extern void unregister_cld_notifier(void);
extern void nfsd4_ssc_init_umount_work(struct nfsd_net *nn);
#endif

-extern void nfsd4_init_leases_net(struct nfsd_net *nn);
+extern int nfsd4_init_leases_net(struct nfsd_net *nn);
+static inline void nfsd4_leases_net_shutdown(struct nfsd_net *nn)
+{
+ unregister_shrinker(&nn->nfsd_client_shrinker);
+};

#else /* CONFIG_NFSD_V4 */
static inline int nfsd4_is_junction(struct dentry *dentry)
@@ -506,7 +510,8 @@ static inline int nfsd4_is_junction(struct dentry *dentry)
return 0;
}

-static inline void nfsd4_init_leases_net(struct nfsd_net *nn) {};
+static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
+static inline void nfsd4_leases_shutdown(struct nfsd_net *nn) { };

#define register_cld_notifier() 0
#define unregister_cld_notifier() do { } while(0)
--
2.9.5


2022-08-27 06:39:55

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 2/2] NFSD: add shrinker to reap courtesy clients on low memory condition

Hi Dai,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.0-rc2 next-20220826]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Dai-Ngo/NFSD-memory-shrinker-for-NFSv4-clients/20220827-070241
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git e022620b5d056e822e42eb9bc0f24fcb97389d86
config: i386-randconfig-a015 (https://download.01.org/0day-ci/archive/20220827/[email protected]/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/8430e1a9491e9b4470a68f989a004b579e37fb73
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Dai-Ngo/NFSD-memory-shrinker-for-NFSv4-clients/20220827-070241
git checkout 8430e1a9491e9b4470a68f989a004b579e37fb73
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash fs/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

In file included from fs/nfsd/trace.c:4:
In file included from fs/nfsd/trace.h:488:
In file included from fs/nfsd/state.h:42:
>> fs/nfsd/nfsd.h:513:72: error: expected ';' after return statement
static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
^
;
1 error generated.
--
In file included from fs/nfsd/nfsctl.c:23:
>> fs/nfsd/nfsd.h:513:72: error: expected ';' after return statement
static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
^
;
>> fs/nfsd/nfsctl.c:1511:2: error: implicit declaration of function 'nfsd4_leases_net_shutdown' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
nfsd4_leases_net_shutdown(nn);
^
fs/nfsd/nfsctl.c:1511:2: note: did you mean 'nfsd4_leases_shutdown'?
fs/nfsd/nfsd.h:514:20: note: 'nfsd4_leases_shutdown' declared here
static inline void nfsd4_leases_shutdown(struct nfsd_net *nn) { };
^
2 errors generated.
--
In file included from fs/nfsd/export.c:21:
>> fs/nfsd/nfsd.h:513:72: error: expected ';' after return statement
static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
^
;
fs/nfsd/export.c:979:17: warning: variable 'inode' set but not used [-Wunused-but-set-variable]
struct inode *inode;
^
1 warning and 1 error generated.
--
In file included from fs/nfsd/trace.c:4:
In file included from fs/nfsd/trace.h:488:
In file included from fs/nfsd/state.h:42:
>> fs/nfsd/nfsd.h:513:72: error: expected ';' after return statement
static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
^
;
In file included from fs/nfsd/trace.c:4:
In file included from fs/nfsd/trace.h:1407:
include/trace/define_trace.h:95:10: fatal error: './trace.h' file not found
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/trace/define_trace.h:90:32: note: expanded from macro 'TRACE_INCLUDE'
# define TRACE_INCLUDE(system) __TRACE_INCLUDE(system)
^~~~~~~~~~~~~~~~~~~~~~~
include/trace/define_trace.h:87:34: note: expanded from macro '__TRACE_INCLUDE'
# define __TRACE_INCLUDE(system) __stringify(TRACE_INCLUDE_PATH/system.h)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/stringify.h:10:27: note: expanded from macro '__stringify'
#define __stringify(x...) __stringify_1(x)
^~~~~~~~~~~~~~~~
include/linux/stringify.h:9:29: note: expanded from macro '__stringify_1'
#define __stringify_1(x...) #x
^~
<scratch space>:56:1: note: expanded from here
"./trace.h"
^~~~~~~~~~~
2 errors generated.


vim +513 fs/nfsd/nfsd.h

512
> 513 static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
514 static inline void nfsd4_leases_shutdown(struct nfsd_net *nn) { };
515

--
0-DAY CI Kernel Test Service
https://01.org/lkp

2022-08-27 09:07:55

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 2/2] NFSD: add shrinker to reap courtesy clients on low memory condition

Hi Dai,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.0-rc2 next-20220826]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Dai-Ngo/NFSD-memory-shrinker-for-NFSv4-clients/20220827-070241
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git e022620b5d056e822e42eb9bc0f24fcb97389d86
config: parisc-defconfig
compiler: hppa-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/8430e1a9491e9b4470a68f989a004b579e37fb73
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Dai-Ngo/NFSD-memory-shrinker-for-NFSv4-clients/20220827-070241
git checkout 8430e1a9491e9b4470a68f989a004b579e37fb73
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=parisc SHELL=/bin/bash fs/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

In file included from fs/nfsd/nfssvc.c:27:
fs/nfsd/nfsd.h: In function 'nfsd4_init_leases_net':
>> fs/nfsd/nfsd.h:513:72: error: expected ';' before '}' token
513 | static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
| ^~
| ;
--
In file included from fs/nfsd/export.c:21:
fs/nfsd/nfsd.h: In function 'nfsd4_init_leases_net':
>> fs/nfsd/nfsd.h:513:72: error: expected ';' before '}' token
513 | static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
| ^~
| ;
fs/nfsd/export.c: In function 'exp_rootfh':
fs/nfsd/export.c:979:34: warning: variable 'inode' set but not used [-Wunused-but-set-variable]
979 | struct inode *inode;
| ^~~~~
--
In file included from fs/nfsd/nfsctl.c:23:
fs/nfsd/nfsd.h: In function 'nfsd4_init_leases_net':
>> fs/nfsd/nfsd.h:513:72: error: expected ';' before '}' token
513 | static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
| ^~
| ;
fs/nfsd/nfsctl.c: In function 'nfsd_exit_net':
>> fs/nfsd/nfsctl.c:1511:9: error: implicit declaration of function 'nfsd4_leases_net_shutdown'; did you mean 'nfsd4_leases_shutdown'? [-Werror=implicit-function-declaration]
1511 | nfsd4_leases_net_shutdown(nn);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
| nfsd4_leases_shutdown
cc1: some warnings being treated as errors
--
In file included from fs/nfsd/state.h:42,
from fs/nfsd/trace.h:488,
from fs/nfsd/trace.c:4:
fs/nfsd/nfsd.h: In function 'nfsd4_init_leases_net':
>> fs/nfsd/nfsd.h:513:72: error: expected ';' before '}' token
513 | static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
| ^~
| ;
In file included from fs/nfsd/trace.h:1407:
include/trace/define_trace.h: At top level:
include/trace/define_trace.h:95:42: fatal error: ./trace.h: No such file or directory
95 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
| ^
compilation terminated.


vim +513 fs/nfsd/nfsd.h

512
> 513 static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0 };
514 static inline void nfsd4_leases_shutdown(struct nfsd_net *nn) { };
515

--
0-DAY CI Kernel Test Service
https://01.org/lkp


Attachments:
(No filename) (4.51 kB)
config (82.69 kB)
Download all attachments