NFSD: add delegation reaper to react to low memory condition
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>
This commit is contained in:
parent
3959066b69
commit
44df6f439a
@ -2144,6 +2144,7 @@ static void __free_client(struct kref *k)
|
|||||||
kfree(clp->cl_nii_domain.data);
|
kfree(clp->cl_nii_domain.data);
|
||||||
kfree(clp->cl_nii_name.data);
|
kfree(clp->cl_nii_name.data);
|
||||||
idr_destroy(&clp->cl_stateids);
|
idr_destroy(&clp->cl_stateids);
|
||||||
|
kfree(clp->cl_ra);
|
||||||
kmem_cache_free(client_slab, clp);
|
kmem_cache_free(client_slab, clp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2871,6 +2872,36 @@ static const struct tree_descr client_files[] = {
|
|||||||
[3] = {""},
|
[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,
|
static struct nfs4_client *create_client(struct xdr_netobj name,
|
||||||
struct svc_rqst *rqstp, nfs4_verifier *verf)
|
struct svc_rqst *rqstp, nfs4_verifier *verf)
|
||||||
{
|
{
|
||||||
@ -2908,6 +2939,14 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
|
|||||||
free_client(clp);
|
free_client(clp);
|
||||||
return NULL;
|
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;
|
return clp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4363,14 +4402,16 @@ out:
|
|||||||
static unsigned long
|
static unsigned long
|
||||||
nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
|
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 *nn = container_of(shrink,
|
||||||
struct nfsd_net, nfsd_client_shrinker);
|
struct nfsd_net, nfsd_client_shrinker);
|
||||||
|
|
||||||
cnt = atomic_read(&nn->nfsd_courtesy_clients);
|
count = atomic_read(&nn->nfsd_courtesy_clients);
|
||||||
if (cnt > 0)
|
if (!count)
|
||||||
|
count = atomic_long_read(&num_delegations);
|
||||||
|
if (count)
|
||||||
mod_delayed_work(laundry_wq, &nn->nfsd_shrinker_work, 0);
|
mod_delayed_work(laundry_wq, &nn->nfsd_shrinker_work, 0);
|
||||||
return (unsigned long)cnt;
|
return (unsigned long)count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long
|
static unsigned long
|
||||||
@ -6159,6 +6200,44 @@ courtesy_client_reaper(struct nfsd_net *nn)
|
|||||||
nfs4_process_client_reaplist(&reaplist);
|
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
|
static void
|
||||||
nfsd4_state_shrinker_worker(struct work_struct *work)
|
nfsd4_state_shrinker_worker(struct work_struct *work)
|
||||||
{
|
{
|
||||||
@ -6167,6 +6246,7 @@ nfsd4_state_shrinker_worker(struct work_struct *work)
|
|||||||
nfsd_shrinker_work);
|
nfsd_shrinker_work);
|
||||||
|
|
||||||
courtesy_client_reaper(nn);
|
courtesy_client_reaper(nn);
|
||||||
|
deleg_reaper(nn);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stid *stp)
|
static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stid *stp)
|
||||||
|
@ -368,6 +368,7 @@ struct nfs4_client {
|
|||||||
#define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */
|
#define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */
|
||||||
#define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \
|
#define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \
|
||||||
1 << NFSD4_CLIENT_CB_KILL)
|
1 << NFSD4_CLIENT_CB_KILL)
|
||||||
|
#define NFSD4_CLIENT_CB_RECALL_ANY (6)
|
||||||
unsigned long cl_flags;
|
unsigned long cl_flags;
|
||||||
const struct cred *cl_cb_cred;
|
const struct cred *cl_cb_cred;
|
||||||
struct rpc_clnt *cl_cb_client;
|
struct rpc_clnt *cl_cb_client;
|
||||||
@ -411,6 +412,10 @@ struct nfs4_client {
|
|||||||
|
|
||||||
unsigned int cl_state;
|
unsigned int cl_state;
|
||||||
atomic_t cl_delegs_in_recall;
|
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
|
/* struct nfs4_client_reset
|
||||||
|
@ -732,4 +732,17 @@ enum nfs4_setxattr_options {
|
|||||||
SETXATTR4_CREATE = 1,
|
SETXATTR4_CREATE = 1,
|
||||||
SETXATTR4_REPLACE = 2,
|
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
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user