NFSD: add delegation reaper to react to low memory condition
authorDai Ngo <dai.ngo@oracle.com>
Thu, 17 Nov 2022 03:44:47 +0000 (19:44 -0800)
committerChuck Lever <cel@kernel.org>
Sat, 10 Dec 2022 16:01:12 +0000 (11:01 -0500)
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>
fs/nfsd/nfs4state.c
fs/nfsd/state.h
include/linux/nfs4.h

index 00b3fa1..c981704 100644 (file)
@@ -2144,6 +2144,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);
 }
 
@@ -2871,6 +2872,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)
 {
@@ -2908,6 +2939,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;
 }
 
@@ -4363,14 +4402,16 @@ out:
 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
@@ -6160,6 +6201,44 @@ courtesy_client_reaper(struct nfsd_net *nn)
 }
 
 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)
 {
        struct delayed_work *dwork = to_delayed_work(work);
@@ -6167,6 +6246,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)
index e30882f..e94634d 100644 (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
index 8d04b6a..730003c 100644 (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