NFSD: add delegation reaper to react to low memory condition

[ Upstream commit 44df6f439a ]

The delegation reaper is called by nfsd memory shrinker's on
the 'count' callback. It scans the client list and sends the
courtesy CB_RECALL_ANY to the clients that hold delegations.

To avoid flooding the clients with CB_RECALL_ANY requests, the
delegation reaper sends only one CB_RECALL_ANY request to each
client per 5 seconds.

Signed-off-by: Dai Ngo <dai.ngo@oracle.com>
[ cel: moved definition of RCA4_TYPE_MASK_RDATA_DLG ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Dai Ngo 2022-11-16 19:44:47 -08:00 committed by Greg Kroah-Hartman
parent f30f07ba57
commit f28dae5463
3 changed files with 102 additions and 4 deletions

View file

@ -2145,6 +2145,7 @@ static void __free_client(struct kref *k)
kfree(clp->cl_nii_domain.data);
kfree(clp->cl_nii_name.data);
idr_destroy(&clp->cl_stateids);
kfree(clp->cl_ra);
kmem_cache_free(client_slab, clp);
}
@ -2872,6 +2873,36 @@ static const struct tree_descr client_files[] = {
[3] = {""},
};
static int
nfsd4_cb_recall_any_done(struct nfsd4_callback *cb,
struct rpc_task *task)
{
switch (task->tk_status) {
case -NFS4ERR_DELAY:
rpc_delay(task, 2 * HZ);
return 0;
default:
return 1;
}
}
static void
nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
spin_lock(&nn->client_lock);
clear_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags);
put_client_renew_locked(clp);
spin_unlock(&nn->client_lock);
}
static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
.done = nfsd4_cb_recall_any_done,
.release = nfsd4_cb_recall_any_release,
};
static struct nfs4_client *create_client(struct xdr_netobj name,
struct svc_rqst *rqstp, nfs4_verifier *verf)
{
@ -2909,6 +2940,14 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
free_client(clp);
return NULL;
}
clp->cl_ra = kzalloc(sizeof(*clp->cl_ra), GFP_KERNEL);
if (!clp->cl_ra) {
free_client(clp);
return NULL;
}
clp->cl_ra_time = 0;
nfsd4_init_cb(&clp->cl_ra->ra_cb, clp, &nfsd4_cb_recall_any_ops,
NFSPROC4_CLNT_CB_RECALL_ANY);
return clp;
}
@ -4364,14 +4403,16 @@ nfsd4_init_slabs(void)
static unsigned long
nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
{
int cnt;
int count;
struct nfsd_net *nn = container_of(shrink,
struct nfsd_net, nfsd_client_shrinker);
cnt = atomic_read(&nn->nfsd_courtesy_clients);
if (cnt > 0)
count = atomic_read(&nn->nfsd_courtesy_clients);
if (!count)
count = atomic_long_read(&num_delegations);
if (count)
mod_delayed_work(laundry_wq, &nn->nfsd_shrinker_work, 0);
return (unsigned long)cnt;
return (unsigned long)count;
}
static unsigned long
@ -6179,6 +6220,44 @@ courtesy_client_reaper(struct nfsd_net *nn)
nfs4_process_client_reaplist(&reaplist);
}
static void
deleg_reaper(struct nfsd_net *nn)
{
struct list_head *pos, *next;
struct nfs4_client *clp;
struct list_head cblist;
INIT_LIST_HEAD(&cblist);
spin_lock(&nn->client_lock);
list_for_each_safe(pos, next, &nn->client_lru) {
clp = list_entry(pos, struct nfs4_client, cl_lru);
if (clp->cl_state != NFSD4_ACTIVE ||
list_empty(&clp->cl_delegations) ||
atomic_read(&clp->cl_delegs_in_recall) ||
test_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags) ||
(ktime_get_boottime_seconds() -
clp->cl_ra_time < 5)) {
continue;
}
list_add(&clp->cl_ra_cblist, &cblist);
/* release in nfsd4_cb_recall_any_release */
atomic_inc(&clp->cl_rpc_users);
set_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags);
clp->cl_ra_time = ktime_get_boottime_seconds();
}
spin_unlock(&nn->client_lock);
while (!list_empty(&cblist)) {
clp = list_first_entry(&cblist, struct nfs4_client,
cl_ra_cblist);
list_del_init(&clp->cl_ra_cblist);
clp->cl_ra->ra_keep = 0;
clp->cl_ra->ra_bmval[0] = BIT(RCA4_TYPE_MASK_RDATA_DLG);
nfsd4_run_cb(&clp->cl_ra->ra_cb);
}
}
static void
nfsd4_state_shrinker_worker(struct work_struct *work)
{
@ -6187,6 +6266,7 @@ nfsd4_state_shrinker_worker(struct work_struct *work)
nfsd_shrinker_work);
courtesy_client_reaper(nn);
deleg_reaper(nn);
}
static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stid *stp)

View file

@ -368,6 +368,7 @@ struct nfs4_client {
#define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */
#define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \
1 << NFSD4_CLIENT_CB_KILL)
#define NFSD4_CLIENT_CB_RECALL_ANY (6)
unsigned long cl_flags;
const struct cred *cl_cb_cred;
struct rpc_clnt *cl_cb_client;
@ -411,6 +412,10 @@ struct nfs4_client {
unsigned int cl_state;
atomic_t cl_delegs_in_recall;
struct nfsd4_cb_recall_any *cl_ra;
time64_t cl_ra_time;
struct list_head cl_ra_cblist;
};
/* struct nfs4_client_reset

View file

@ -732,4 +732,17 @@ enum nfs4_setxattr_options {
SETXATTR4_CREATE = 1,
SETXATTR4_REPLACE = 2,
};
enum {
RCA4_TYPE_MASK_RDATA_DLG = 0,
RCA4_TYPE_MASK_WDATA_DLG = 1,
RCA4_TYPE_MASK_DIR_DLG = 2,
RCA4_TYPE_MASK_FILE_LAYOUT = 3,
RCA4_TYPE_MASK_BLK_LAYOUT = 4,
RCA4_TYPE_MASK_OBJ_LAYOUT_MIN = 8,
RCA4_TYPE_MASK_OBJ_LAYOUT_MAX = 9,
RCA4_TYPE_MASK_OTHER_LAYOUT_MIN = 12,
RCA4_TYPE_MASK_OTHER_LAYOUT_MAX = 15,
};
#endif