Merge tag 'nfsd-6.2-4' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
[sfrench/cifs-2.6.git] / fs / nfsd / nfs4state.c
index 836bd825ca4ad66a9d70de578e523d2bcbfba12a..4ef529379065a10d6957680f1ab50e9392215cc5 100644 (file)
@@ -44,7 +44,9 @@
 #include <linux/jhash.h>
 #include <linux/string_helpers.h>
 #include <linux/fsnotify.h>
+#include <linux/rhashtable.h>
 #include <linux/nfs_ssc.h>
+
 #include "xdr4.h"
 #include "xdr4cb.h"
 #include "vfs.h"
@@ -84,6 +86,7 @@ static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner)
 static void nfs4_free_ol_stateid(struct nfs4_stid *stid);
 void nfsd4_end_grace(struct nfsd_net *nn);
 static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps);
+static void nfsd4_file_hash_remove(struct nfs4_file *fi);
 
 /* Locking: */
 
@@ -588,11 +591,8 @@ static void nfsd4_free_file_rcu(struct rcu_head *rcu)
 void
 put_nfs4_file(struct nfs4_file *fi)
 {
-       might_lock(&state_lock);
-
-       if (refcount_dec_and_lock(&fi->fi_ref, &state_lock)) {
-               hlist_del_rcu(&fi->fi_hash);
-               spin_unlock(&state_lock);
+       if (refcount_dec_and_test(&fi->fi_ref)) {
+               nfsd4_file_hash_remove(fi);
                WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate));
                WARN_ON_ONCE(!list_empty(&fi->fi_delegations));
                call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu);
@@ -675,15 +675,26 @@ find_any_file(struct nfs4_file *f)
        return ret;
 }
 
-static struct nfsd_file *find_deleg_file(struct nfs4_file *f)
+static struct nfsd_file *find_any_file_locked(struct nfs4_file *f)
 {
-       struct nfsd_file *ret = NULL;
+       lockdep_assert_held(&f->fi_lock);
+
+       if (f->fi_fds[O_RDWR])
+               return f->fi_fds[O_RDWR];
+       if (f->fi_fds[O_WRONLY])
+               return f->fi_fds[O_WRONLY];
+       if (f->fi_fds[O_RDONLY])
+               return f->fi_fds[O_RDONLY];
+       return NULL;
+}
+
+static struct nfsd_file *find_deleg_file_locked(struct nfs4_file *f)
+{
+       lockdep_assert_held(&f->fi_lock);
 
-       spin_lock(&f->fi_lock);
        if (f->fi_deleg_file)
-               ret = nfsd_file_get(f->fi_deleg_file);
-       spin_unlock(&f->fi_lock);
-       return ret;
+               return f->fi_deleg_file;
+       return NULL;
 }
 
 static atomic_long_t num_delegations;
@@ -706,19 +717,20 @@ static unsigned int ownerstr_hashval(struct xdr_netobj *ownername)
        return ret & OWNER_HASH_MASK;
 }
 
-/* hash table for nfs4_file */
-#define FILE_HASH_BITS                   8
-#define FILE_HASH_SIZE                  (1 << FILE_HASH_BITS)
+static struct rhltable nfs4_file_rhltable ____cacheline_aligned_in_smp;
 
-static unsigned int file_hashval(struct svc_fh *fh)
-{
-       struct inode *inode = d_inode(fh->fh_dentry);
+static const struct rhashtable_params nfs4_file_rhash_params = {
+       .key_len                = sizeof_field(struct nfs4_file, fi_inode),
+       .key_offset             = offsetof(struct nfs4_file, fi_inode),
+       .head_offset            = offsetof(struct nfs4_file, fi_rlist),
 
-       /* XXX: why not (here & in file cache) use inode? */
-       return (unsigned int)hash_long(inode->i_ino, FILE_HASH_BITS);
-}
-
-static struct hlist_head file_hashtbl[FILE_HASH_SIZE];
+       /*
+        * Start with a single page hash table to reduce resizing churn
+        * on light workloads.
+        */
+       .min_size               = 256,
+       .automatic_shrinking    = true,
+};
 
 /*
  * Check if courtesy clients have conflicting access and resolve it if possible
@@ -831,9 +843,9 @@ static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag)
                        swap(f2, fp->fi_fds[O_RDWR]);
                spin_unlock(&fp->fi_lock);
                if (f1)
-                       nfsd_file_close(f1);
+                       nfsd_file_put(f1);
                if (f2)
-                       nfsd_file_close(f2);
+                       nfsd_file_put(f2);
        }
 }
 
@@ -1355,6 +1367,8 @@ static void revoke_delegation(struct nfs4_delegation *dp)
 
        WARN_ON(!list_empty(&dp->dl_recall_lru));
 
+       trace_nfsd_stid_revoke(&dp->dl_stid);
+
        if (clp->cl_minorversion) {
                dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID;
                refcount_inc(&dp->dl_stid.sc_count);
@@ -1819,13 +1833,12 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fattrs,
        int numslots = fattrs->maxreqs;
        int slotsize = slot_bytes(fattrs);
        struct nfsd4_session *new;
-       int mem, i;
+       int i;
 
-       BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *)
-                       + sizeof(struct nfsd4_session) > PAGE_SIZE);
-       mem = numslots * sizeof(struct nfsd4_slot *);
+       BUILD_BUG_ON(struct_size(new, se_slots, NFSD_MAX_SLOTS_PER_SESSION)
+                    > PAGE_SIZE);
 
-       new = kzalloc(sizeof(*new) + mem, GFP_KERNEL);
+       new = kzalloc(struct_size(new, se_slots, numslots), GFP_KERNEL);
        if (!new)
                return NULL;
        /* allocate each struct nfsd4_slot and data cache in one piece */
@@ -2131,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);
 }
 
@@ -2613,9 +2627,11 @@ static int nfs4_show_open(struct seq_file *s, struct nfs4_stid *st)
        ols = openlockstateid(st);
        oo = ols->st_stateowner;
        nf = st->sc_file;
-       file = find_any_file(nf);
+
+       spin_lock(&nf->fi_lock);
+       file = find_any_file_locked(nf);
        if (!file)
-               return 0;
+               goto out;
 
        seq_printf(s, "- ");
        nfs4_show_stateid(s, &st->sc_stateid);
@@ -2637,8 +2653,8 @@ static int nfs4_show_open(struct seq_file *s, struct nfs4_stid *st)
        seq_printf(s, ", ");
        nfs4_show_owner(s, oo);
        seq_printf(s, " }\n");
-       nfsd_file_put(file);
-
+out:
+       spin_unlock(&nf->fi_lock);
        return 0;
 }
 
@@ -2652,9 +2668,10 @@ static int nfs4_show_lock(struct seq_file *s, struct nfs4_stid *st)
        ols = openlockstateid(st);
        oo = ols->st_stateowner;
        nf = st->sc_file;
-       file = find_any_file(nf);
+       spin_lock(&nf->fi_lock);
+       file = find_any_file_locked(nf);
        if (!file)
-               return 0;
+               goto out;
 
        seq_printf(s, "- ");
        nfs4_show_stateid(s, &st->sc_stateid);
@@ -2674,8 +2691,8 @@ static int nfs4_show_lock(struct seq_file *s, struct nfs4_stid *st)
        seq_printf(s, ", ");
        nfs4_show_owner(s, oo);
        seq_printf(s, " }\n");
-       nfsd_file_put(file);
-
+out:
+       spin_unlock(&nf->fi_lock);
        return 0;
 }
 
@@ -2687,9 +2704,10 @@ static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
 
        ds = delegstateid(st);
        nf = st->sc_file;
-       file = find_deleg_file(nf);
+       spin_lock(&nf->fi_lock);
+       file = find_deleg_file_locked(nf);
        if (!file)
-               return 0;
+               goto out;
 
        seq_printf(s, "- ");
        nfs4_show_stateid(s, &st->sc_stateid);
@@ -2705,8 +2723,8 @@ static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
        seq_printf(s, ", ");
        nfs4_show_fname(s, file);
        seq_printf(s, " }\n");
-       nfsd_file_put(file);
-
+out:
+       spin_unlock(&nf->fi_lock);
        return 0;
 }
 
@@ -2854,6 +2872,37 @@ static const struct tree_descr client_files[] = {
        [3] = {""},
 };
 
+static int
+nfsd4_cb_recall_any_done(struct nfsd4_callback *cb,
+                               struct rpc_task *task)
+{
+       trace_nfsd_cb_recall_any_done(cb, 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)
 {
@@ -2891,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;
 }
 
@@ -4260,11 +4317,9 @@ static struct nfs4_file *nfsd4_alloc_file(void)
 }
 
 /* OPEN Share state helper functions */
-static void nfsd4_init_file(struct svc_fh *fh, unsigned int hashval,
-                               struct nfs4_file *fp)
-{
-       lockdep_assert_held(&state_lock);
 
+static void nfsd4_file_init(const struct svc_fh *fh, struct nfs4_file *fp)
+{
        refcount_set(&fp->fi_ref, 1);
        spin_lock_init(&fp->fi_lock);
        INIT_LIST_HEAD(&fp->fi_stateids);
@@ -4282,7 +4337,6 @@ static void nfsd4_init_file(struct svc_fh *fh, unsigned int hashval,
        INIT_LIST_HEAD(&fp->fi_lo_states);
        atomic_set(&fp->fi_lo_recalls, 0);
 #endif
-       hlist_add_head_rcu(&fp->fi_hash, &file_hashtbl[hashval]);
 }
 
 void
@@ -4347,25 +4401,27 @@ out:
 }
 
 static unsigned long
-nfsd_courtesy_client_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, nfsd_client_shrinker);
 
-       cnt = atomic_read(&nn->nfsd_courtesy_clients);
-       if (cnt > 0)
-               mod_delayed_work(laundry_wq, &nn->nfsd_shrinker_work, 0);
-       return (unsigned long)cnt;
+       count = atomic_read(&nn->nfsd_courtesy_clients);
+       if (!count)
+               count = atomic_long_read(&num_delegations);
+       if (count)
+               queue_work(laundry_wq, &nn->nfsd_shrinker_work);
+       return (unsigned long)count;
 }
 
 static unsigned long
-nfsd_courtesy_client_scan(struct shrinker *shrink, struct shrink_control *sc)
+nfsd4_state_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
 {
        return SHRINK_STOP;
 }
 
-int
+void
 nfsd4_init_leases_net(struct nfsd_net *nn)
 {
        struct sysinfo si;
@@ -4387,16 +4443,6 @@ 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_clients, 0);
-       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");
-}
-
-void
-nfsd4_leases_net_shutdown(struct nfsd_net *nn)
-{
-       unregister_shrinker(&nn->nfsd_client_shrinker);
 }
 
 static void init_nfs4_replay(struct nfs4_replay *rp)
@@ -4667,71 +4713,80 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
                nfs4_put_stid(&last->st_stid);
 }
 
-/* search file_hashtbl[] for file */
-static struct nfs4_file *
-find_file_locked(struct svc_fh *fh, unsigned int hashval)
+static noinline_for_stack struct nfs4_file *
+nfsd4_file_hash_lookup(const struct svc_fh *fhp)
 {
-       struct nfs4_file *fp;
+       struct inode *inode = d_inode(fhp->fh_dentry);
+       struct rhlist_head *tmp, *list;
+       struct nfs4_file *fi;
 
-       hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash,
-                               lockdep_is_held(&state_lock)) {
-               if (fh_match(&fp->fi_fhandle, &fh->fh_handle)) {
-                       if (refcount_inc_not_zero(&fp->fi_ref))
-                               return fp;
+       rcu_read_lock();
+       list = rhltable_lookup(&nfs4_file_rhltable, &inode,
+                              nfs4_file_rhash_params);
+       rhl_for_each_entry_rcu(fi, tmp, list, fi_rlist) {
+               if (fh_match(&fi->fi_fhandle, &fhp->fh_handle)) {
+                       if (refcount_inc_not_zero(&fi->fi_ref)) {
+                               rcu_read_unlock();
+                               return fi;
+                       }
                }
        }
+       rcu_read_unlock();
        return NULL;
 }
 
-static struct nfs4_file *insert_file(struct nfs4_file *new, struct svc_fh *fh,
-                                    unsigned int hashval)
+/*
+ * On hash insertion, identify entries with the same inode but
+ * distinct filehandles. They will all be on the list returned
+ * by rhltable_lookup().
+ *
+ * inode->i_lock prevents racing insertions from adding an entry
+ * for the same inode/fhp pair twice.
+ */
+static noinline_for_stack struct nfs4_file *
+nfsd4_file_hash_insert(struct nfs4_file *new, const struct svc_fh *fhp)
 {
-       struct nfs4_file *fp;
+       struct inode *inode = d_inode(fhp->fh_dentry);
+       struct rhlist_head *tmp, *list;
        struct nfs4_file *ret = NULL;
        bool alias_found = false;
+       struct nfs4_file *fi;
+       int err;
 
-       spin_lock(&state_lock);
-       hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash,
-                                lockdep_is_held(&state_lock)) {
-               if (fh_match(&fp->fi_fhandle, &fh->fh_handle)) {
-                       if (refcount_inc_not_zero(&fp->fi_ref))
-                               ret = fp;
-               } else if (d_inode(fh->fh_dentry) == fp->fi_inode)
-                       fp->fi_aliased = alias_found = true;
-       }
-       if (likely(ret == NULL)) {
-               nfsd4_init_file(fh, hashval, new);
-               new->fi_aliased = alias_found;
-               ret = new;
+       rcu_read_lock();
+       spin_lock(&inode->i_lock);
+
+       list = rhltable_lookup(&nfs4_file_rhltable, &inode,
+                              nfs4_file_rhash_params);
+       rhl_for_each_entry_rcu(fi, tmp, list, fi_rlist) {
+               if (fh_match(&fi->fi_fhandle, &fhp->fh_handle)) {
+                       if (refcount_inc_not_zero(&fi->fi_ref))
+                               ret = fi;
+               } else
+                       fi->fi_aliased = alias_found = true;
        }
-       spin_unlock(&state_lock);
-       return ret;
-}
+       if (ret)
+               goto out_unlock;
 
-static struct nfs4_file * find_file(struct svc_fh *fh)
-{
-       struct nfs4_file *fp;
-       unsigned int hashval = file_hashval(fh);
+       nfsd4_file_init(fhp, new);
+       err = rhltable_insert(&nfs4_file_rhltable, &new->fi_rlist,
+                             nfs4_file_rhash_params);
+       if (err)
+               goto out_unlock;
 
-       rcu_read_lock();
-       fp = find_file_locked(fh, hashval);
+       new->fi_aliased = alias_found;
+       ret = new;
+
+out_unlock:
+       spin_unlock(&inode->i_lock);
        rcu_read_unlock();
-       return fp;
+       return ret;
 }
 
-static struct nfs4_file *
-find_or_add_file(struct nfs4_file *new, struct svc_fh *fh)
+static noinline_for_stack void nfsd4_file_hash_remove(struct nfs4_file *fi)
 {
-       struct nfs4_file *fp;
-       unsigned int hashval = file_hashval(fh);
-
-       rcu_read_lock();
-       fp = find_file_locked(fh, hashval);
-       rcu_read_unlock();
-       if (fp)
-               return fp;
-
-       return insert_file(new, fh, hashval);
+       rhltable_remove(&nfs4_file_rhltable, &fi->fi_rlist,
+                       nfs4_file_rhash_params);
 }
 
 /*
@@ -4744,9 +4799,10 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
        struct nfs4_file *fp;
        __be32 ret = nfs_ok;
 
-       fp = find_file(current_fh);
+       fp = nfsd4_file_hash_lookup(current_fh);
        if (!fp)
                return ret;
+
        /* Check for conflicting share reservations */
        spin_lock(&fp->fi_lock);
        if (fp->fi_share_deny & deny_type)
@@ -4758,7 +4814,7 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
 
 static bool nfsd4_deleg_present(const struct inode *inode)
 {
-       struct file_lock_context *ctx = smp_load_acquire(&inode->i_flctx);
+       struct file_lock_context *ctx = locks_inode_context(inode);
 
        return ctx && !list_empty_careful(&ctx->flc_lease);
 }
@@ -5196,18 +5252,10 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
        if (!fp->fi_fds[oflag]) {
                spin_unlock(&fp->fi_lock);
 
-               if (!open->op_filp) {
-                       status = nfsd_file_acquire(rqstp, cur_fh, access, &nf);
-                       if (status != nfs_ok)
-                               goto out_put_access;
-               } else {
-                       status = nfsd_file_create(rqstp, cur_fh, access, &nf);
-                       if (status != nfs_ok)
-                               goto out_put_access;
-                       nf->nf_file = open->op_filp;
-                       open->op_filp = NULL;
-                       trace_nfsd_file_create(rqstp, access, nf);
-               }
+               status = nfsd_file_acquire_opened(rqstp, cur_fh, access,
+                                                 open->op_filp, &nf);
+               if (status != nfs_ok)
+                       goto out_put_access;
 
                spin_lock(&fp->fi_lock);
                if (!fp->fi_fds[oflag]) {
@@ -5620,7 +5668,9 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
         * and check for delegations in the process of being recalled.
         * If not found, create the nfs4_file struct
         */
-       fp = find_or_add_file(open->op_file, current_fh);
+       fp = nfsd4_file_hash_insert(open->op_file, current_fh);
+       if (unlikely(!fp))
+               return nfserr_jukebox;
        if (fp != open->op_file) {
                status = nfs4_check_deleg(cl, open, &dp);
                if (status)
@@ -5897,7 +5947,7 @@ nfs4_lockowner_has_blockers(struct nfs4_lockowner *lo)
 
        list_for_each_entry(stp, &lo->lo_owner.so_stateids, st_perstateowner) {
                nf = stp->st_stid.sc_file;
-               ctx = nf->fi_inode->i_flctx;
+               ctx = locks_inode_context(nf->fi_inode);
                if (!ctx)
                        continue;
                if (locks_owner_has_blockers(ctx, lo))
@@ -6125,17 +6175,63 @@ laundromat_main(struct work_struct *laundry)
 }
 
 static void
-courtesy_client_reaper(struct work_struct *reaper)
+courtesy_client_reaper(struct nfsd_net *nn)
 {
        struct list_head reaplist;
-       struct delayed_work *dwork = to_delayed_work(reaper);
-       struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
-                                       nfsd_shrinker_work);
 
        nfs4_get_courtesy_client_reaplist(nn, &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);
+               trace_nfsd_cb_recall_any(clp->cl_ra);
+               nfsd4_run_cb(&clp->cl_ra->ra_cb);
+       }
+}
+
+static void
+nfsd4_state_shrinker_worker(struct work_struct *work)
+{
+       struct nfsd_net *nn = container_of(work, struct nfsd_net,
+                               nfsd_shrinker_work);
+
+       courtesy_client_reaper(nn);
+       deleg_reaper(nn);
+}
+
 static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stid *stp)
 {
        if (!fh_match(&fhp->fh_handle, &stp->sc_file->fi_fhandle))
@@ -6902,6 +6998,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                goto put_stateid;
 
+       trace_nfsd_deleg_return(stateid);
        wake_up_var(d_inode(cstate->current_fh.fh_dentry));
        destroy_delegation(dp);
 put_stateid:
@@ -7713,7 +7810,7 @@ check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner)
        }
 
        inode = locks_inode(nf->nf_file);
-       flctx = inode->i_flctx;
+       flctx = locks_inode_context(inode);
 
        if (flctx && !list_empty_careful(&flctx->flc_posix)) {
                spin_lock(&flctx->flc_lock);
@@ -7958,11 +8055,20 @@ static int nfs4_state_create_net(struct net *net)
        INIT_LIST_HEAD(&nn->blocked_locks_lru);
 
        INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
-       INIT_DELAYED_WORK(&nn->nfsd_shrinker_work, courtesy_client_reaper);
+       INIT_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker);
        get_net(net);
 
+       nn->nfsd_client_shrinker.scan_objects = nfsd4_state_shrinker_scan;
+       nn->nfsd_client_shrinker.count_objects = nfsd4_state_shrinker_count;
+       nn->nfsd_client_shrinker.seeks = DEFAULT_SEEKS;
+
+       if (register_shrinker(&nn->nfsd_client_shrinker, "nfsd-client"))
+               goto err_shrinker;
        return 0;
 
+err_shrinker:
+       put_net(net);
+       kfree(nn->sessionid_hashtbl);
 err_sessionid:
        kfree(nn->unconf_id_hashtbl);
 err_unconf_id:
@@ -8034,10 +8140,16 @@ nfs4_state_start(void)
 {
        int ret;
 
-       ret = nfsd4_create_callback_queue();
+       ret = rhltable_init(&nfs4_file_rhltable, &nfs4_file_rhash_params);
        if (ret)
                return ret;
 
+       ret = nfsd4_create_callback_queue();
+       if (ret) {
+               rhltable_destroy(&nfs4_file_rhltable);
+               return ret;
+       }
+
        set_max_delegations();
        return 0;
 }
@@ -8049,6 +8161,8 @@ nfs4_state_shutdown_net(struct net *net)
        struct list_head *pos, *next, reaplist;
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
+       unregister_shrinker(&nn->nfsd_client_shrinker);
+       cancel_work(&nn->nfsd_shrinker_work);
        cancel_delayed_work_sync(&nn->laundromat_work);
        locks_end_grace(&nn->nfsd4_manager);
 
@@ -8068,6 +8182,7 @@ nfs4_state_shutdown_net(struct net *net)
 
        nfsd4_client_tracking_exit(net);
        nfs4_state_destroy_net(net);
+       rhltable_destroy(&nfs4_file_rhltable);
 #ifdef CONFIG_NFSD_V4_2_INTER_SSC
        nfsd4_ssc_shutdown_umount(nn);
 #endif