nfsd: have nfsd4_find_reclaim_client take a char * argument
[pandora-kernel.git] / fs / nfsd / nfs4state.c
index 48a1bad..18e5549 100644 (file)
@@ -340,7 +340,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
        fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
        dp->dl_time = 0;
        atomic_set(&dp->dl_count, 1);
-       INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc);
+       nfsd4_init_callback(&dp->dl_recall);
        return dp;
 }
 
@@ -758,7 +758,7 @@ static void nfsd4_put_drc_mem(int slotsize, int num)
        spin_unlock(&nfsd_drc_lock);
 }
 
-static struct nfsd4_session *alloc_session(int slotsize, int numslots)
+static struct nfsd4_session *__alloc_session(int slotsize, int numslots)
 {
        struct nfsd4_session *new;
        int mem, i;
@@ -852,35 +852,28 @@ static int nfsd4_register_conn(struct nfsd4_conn *conn)
        return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
 }
 
-static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, u32 dir)
+static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, struct nfsd4_session *ses)
 {
-       struct nfsd4_conn *conn;
        int ret;
 
-       conn = alloc_conn(rqstp, dir);
-       if (!conn)
-               return nfserr_jukebox;
        nfsd4_hash_conn(conn, ses);
        ret = nfsd4_register_conn(conn);
        if (ret)
                /* oops; xprt is already down: */
                nfsd4_conn_lost(&conn->cn_xpt_user);
-       if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN &&
-               dir & NFS4_CDFC4_BACK) {
+       if (conn->cn_flags & NFS4_CDFC4_BACK) {
                /* callback channel may be back up */
                nfsd4_probe_callback(ses->se_client);
        }
-       return nfs_ok;
 }
 
-static __be32 nfsd4_new_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_session *ses)
+static struct nfsd4_conn *alloc_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_create_session *cses)
 {
        u32 dir = NFS4_CDFC4_FORE;
 
-       if (ses->se_flags & SESSION4_BACK_CHAN)
+       if (cses->flags & SESSION4_BACK_CHAN)
                dir |= NFS4_CDFC4_BACK;
-
-       return nfsd4_new_conn(rqstp, ses, dir);
+       return alloc_conn(rqstp, dir);
 }
 
 /* must be called under client_lock */
@@ -903,20 +896,21 @@ static void nfsd4_del_conns(struct nfsd4_session *s)
        spin_unlock(&clp->cl_lock);
 }
 
+static void __free_session(struct nfsd4_session *ses)
+{
+       nfsd4_put_drc_mem(slot_bytes(&ses->se_fchannel), ses->se_fchannel.maxreqs);
+       free_session_slots(ses);
+       kfree(ses);
+}
+
 static void free_session(struct kref *kref)
 {
        struct nfsd4_session *ses;
-       int mem;
 
        lockdep_assert_held(&client_lock);
        ses = container_of(kref, struct nfsd4_session, se_ref);
        nfsd4_del_conns(ses);
-       spin_lock(&nfsd_drc_lock);
-       mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
-       nfsd_drc_mem_used -= mem;
-       spin_unlock(&nfsd_drc_lock);
-       free_session_slots(ses);
-       kfree(ses);
+       __free_session(ses);
 }
 
 void nfsd4_put_session(struct nfsd4_session *ses)
@@ -926,14 +920,10 @@ void nfsd4_put_session(struct nfsd4_session *ses)
        spin_unlock(&client_lock);
 }
 
-static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
+static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan)
 {
        struct nfsd4_session *new;
-       struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
        int numslots, slotsize;
-       __be32 status;
-       int idx;
-
        /*
         * Note decreasing slot size below client's request may
         * make it difficult for client to function correctly, whereas
@@ -946,12 +936,18 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
        if (numslots < 1)
                return NULL;
 
-       new = alloc_session(slotsize, numslots);
+       new = __alloc_session(slotsize, numslots);
        if (!new) {
                nfsd4_put_drc_mem(slotsize, fchan->maxreqs);
                return NULL;
        }
        init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize);
+       return new;
+}
+
+void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses)
+{
+       int idx;
 
        new->se_client = clp;
        gen_sessionid(new);
@@ -961,6 +957,7 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
        new->se_cb_seq_nr = 1;
        new->se_flags = cses->flags;
        new->se_cb_prog = cses->callback_prog;
+       new->se_cb_sec = cses->cb_sec;
        kref_init(&new->se_ref);
        idx = hash_sessionid(&new->se_sessionid);
        spin_lock(&client_lock);
@@ -970,14 +967,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
        spin_unlock(&clp->cl_lock);
        spin_unlock(&client_lock);
 
-       status = nfsd4_new_conn_from_crses(rqstp, new);
-       /* whoops: benny points out, status is ignored! (err, or bogus) */
-       if (status) {
-               spin_lock(&client_lock);
-               free_session(&new->se_ref);
-               spin_unlock(&client_lock);
-               return NULL;
-       }
        if (cses->flags & SESSION4_BACK_CHAN) {
                struct sockaddr *sa = svc_addr(rqstp);
                /*
@@ -990,8 +979,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
                rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa);
                clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
        }
-       nfsd4_probe_callback(clp);
-       return new;
 }
 
 /* caller must hold client_lock */
@@ -1131,7 +1118,7 @@ unhash_client_locked(struct nfs4_client *clp)
 }
 
 static void
-expire_client(struct nfs4_client *clp)
+destroy_client(struct nfs4_client *clp)
 {
        struct nfs4_openowner *oo;
        struct nfs4_delegation *dp;
@@ -1165,6 +1152,12 @@ expire_client(struct nfs4_client *clp)
        spin_unlock(&client_lock);
 }
 
+static void expire_client(struct nfs4_client *clp)
+{
+       nfsd4_client_record_remove(clp);
+       destroy_client(clp);
+}
+
 static void copy_verf(struct nfs4_client *target, nfs4_verifier *source)
 {
        memcpy(target->cl_verifier.data, source->data,
@@ -1223,10 +1216,26 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2)
        return true;
 }
 
+/*
+ * RFC 3530 language requires clid_inuse be returned when the
+ * "principal" associated with a requests differs from that previously
+ * used.  We use uid, gid's, and gss principal string as our best
+ * approximation.  We also don't want to allow non-gss use of a client
+ * established using gss: in theory cr_principal should catch that
+ * change, but in practice cr_principal can be null even in the gss case
+ * since gssd doesn't always pass down a principal string.
+ */
+static bool is_gss_cred(struct svc_cred *cr)
+{
+       /* Is cr_flavor one of the gss "pseudoflavors"?: */
+       return (cr->cr_flavor > RPC_AUTH_MAXFLAVOR);
+}
+
+
 static bool
 same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
 {
-       if ((cr1->cr_flavor != cr2->cr_flavor)
+       if ((is_gss_cred(cr1) != is_gss_cred(cr2))
                || (cr1->cr_uid != cr2->cr_uid)
                || (cr1->cr_gid != cr2->cr_gid)
                || !groups_equal(cr1->cr_group_info, cr2->cr_group_info))
@@ -1304,7 +1313,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
        INIT_LIST_HEAD(&clp->cl_lru);
        INIT_LIST_HEAD(&clp->cl_callbacks);
        spin_lock_init(&clp->cl_lock);
-       INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
+       nfsd4_init_callback(&clp->cl_cb_null);
        clp->cl_time = get_seconds();
        clear_bit(0, &clp->cl_cb_slot_busy);
        rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
@@ -1340,13 +1349,15 @@ move_to_confirmed(struct nfs4_client *clp)
 }
 
 static struct nfs4_client *
-find_confirmed_client(clientid_t *clid)
+find_confirmed_client(clientid_t *clid, bool sessions)
 {
        struct nfs4_client *clp;
        unsigned int idhashval = clientid_hashval(clid->cl_id);
 
        list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
                if (same_clid(&clp->cl_clientid, clid)) {
+                       if ((bool)clp->cl_minorversion != sessions)
+                               return NULL;
                        renew_client(clp);
                        return clp;
                }
@@ -1355,14 +1366,17 @@ find_confirmed_client(clientid_t *clid)
 }
 
 static struct nfs4_client *
-find_unconfirmed_client(clientid_t *clid)
+find_unconfirmed_client(clientid_t *clid, bool sessions)
 {
        struct nfs4_client *clp;
        unsigned int idhashval = clientid_hashval(clid->cl_id);
 
        list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) {
-               if (same_clid(&clp->cl_clientid, clid))
+               if (same_clid(&clp->cl_clientid, clid)) {
+                       if ((bool)clp->cl_minorversion != sessions)
+                               return NULL;
                        return clp;
+               }
        }
        return NULL;
 }
@@ -1651,6 +1665,7 @@ out_new:
                status = nfserr_jukebox;
                goto out;
        }
+       new->cl_minorversion = 1;
 
        gen_clid(new);
        add_to_unconfirmed(new, strhashval);
@@ -1743,67 +1758,71 @@ nfsd4_create_session(struct svc_rqst *rqstp,
        struct sockaddr *sa = svc_addr(rqstp);
        struct nfs4_client *conf, *unconf;
        struct nfsd4_session *new;
+       struct nfsd4_conn *conn;
        struct nfsd4_clid_slot *cs_slot = NULL;
-       bool confirm_me = false;
        __be32 status = 0;
 
        if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)
                return nfserr_inval;
+       if (check_forechannel_attrs(cr_ses->fore_channel))
+               return nfserr_toosmall;
+       new = alloc_session(&cr_ses->fore_channel);
+       if (!new)
+               return nfserr_jukebox;
+       status = nfserr_jukebox;
+       conn = alloc_conn_from_crses(rqstp, cr_ses);
+       if (!conn)
+               goto out_free_session;
 
        nfs4_lock_state();
-       unconf = find_unconfirmed_client(&cr_ses->clientid);
-       conf = find_confirmed_client(&cr_ses->clientid);
+       unconf = find_unconfirmed_client(&cr_ses->clientid, true);
+       conf = find_confirmed_client(&cr_ses->clientid, true);
 
        if (conf) {
                cs_slot = &conf->cl_cs_slot;
                status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status == nfserr_replay_cache) {
                        status = nfsd4_replay_create_session(cr_ses, cs_slot);
-                       goto out;
+                       goto out_free_conn;
                } else if (cr_ses->seqid != cs_slot->sl_seqid + 1) {
                        status = nfserr_seq_misordered;
-                       goto out;
+                       goto out_free_conn;
                }
        } else if (unconf) {
+               unsigned int hash;
+               struct nfs4_client *old;
                if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
                    !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
                        status = nfserr_clid_inuse;
-                       goto out;
+                       goto out_free_conn;
                }
                cs_slot = &unconf->cl_cs_slot;
                status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status) {
                        /* an unconfirmed replay returns misordered */
                        status = nfserr_seq_misordered;
-                       goto out;
+                       goto out_free_conn;
                }
-               confirm_me = true;
+               hash = clientstr_hashval(unconf->cl_recdir);
+               old = find_confirmed_client_by_str(unconf->cl_recdir, hash);
+               if (old)
+                       expire_client(old);
+               move_to_confirmed(unconf);
                conf = unconf;
        } else {
                status = nfserr_stale_clientid;
-               goto out;
+               goto out_free_conn;
        }
-
-       /*
-        * XXX: we should probably set this at creation time, and check
-        * for consistent minorversion use throughout:
-        */
-       conf->cl_minorversion = 1;
+       status = nfs_ok;
        /*
         * We do not support RDMA or persistent sessions
         */
        cr_ses->flags &= ~SESSION4_PERSIST;
        cr_ses->flags &= ~SESSION4_RDMA;
 
-       status = nfserr_toosmall;
-       if (check_forechannel_attrs(cr_ses->fore_channel))
-               goto out;
+       init_session(rqstp, new, conf, cr_ses);
+       nfsd4_init_conn(rqstp, conn, new);
 
-       status = nfserr_jukebox;
-       new = alloc_init_session(rqstp, conf, cr_ses);
-       if (!new)
-               goto out;
-       status = nfs_ok;
        memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
               NFS4_MAX_SESSIONID_LEN);
        memcpy(&cr_ses->fore_channel, &new->se_fchannel,
@@ -1813,18 +1832,15 @@ nfsd4_create_session(struct svc_rqst *rqstp,
 
        /* cache solo and embedded create sessions under the state lock */
        nfsd4_cache_create_session(cr_ses, cs_slot, status);
-       if (confirm_me) {
-               unsigned int hash = clientstr_hashval(unconf->cl_recdir);
-               struct nfs4_client *old =
-                       find_confirmed_client_by_str(conf->cl_recdir, hash);
-               if (old)
-                       expire_client(old);
-               move_to_confirmed(conf);
-       }
 out:
        nfs4_unlock_state();
        dprintk("%s returns %d\n", __func__, ntohl(status));
        return status;
+out_free_conn:
+       free_conn(conn);
+out_free_session:
+       __free_session(new);
+       goto out;
 }
 
 static bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
@@ -1849,11 +1865,26 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir)
        return nfserr_inval;
 }
 
+__be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc)
+{
+       struct nfsd4_session *session = cstate->session;
+
+       spin_lock(&client_lock);
+       session->se_cb_prog = bc->bc_cb_program;
+       session->se_cb_sec = bc->bc_cb_sec;
+       spin_unlock(&client_lock);
+
+       nfsd4_probe_callback(session->se_client);
+
+       return nfs_ok;
+}
+
 __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
                     struct nfsd4_compound_state *cstate,
                     struct nfsd4_bind_conn_to_session *bcts)
 {
        __be32 status;
+       struct nfsd4_conn *conn;
 
        if (!nfsd4_last_compound_op(rqstp))
                return nfserr_not_only_op;
@@ -1870,9 +1901,13 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
                return nfserr_badsession;
 
        status = nfsd4_map_bcts_dir(&bcts->dir);
-       if (!status)
-               nfsd4_new_conn(rqstp, cstate->session, bcts->dir);
-       return status;
+       if (status)
+               return status;
+       conn = alloc_conn(rqstp, bcts->dir);
+       if (!conn)
+               return nfserr_jukebox;
+       nfsd4_init_conn(rqstp, conn, cstate->session);
+       return nfs_ok;
 }
 
 static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid)
@@ -2085,8 +2120,8 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
        __be32 status = 0;
 
        nfs4_lock_state();
-       unconf = find_unconfirmed_client(&dc->clientid);
-       conf = find_confirmed_client(&dc->clientid);
+       unconf = find_unconfirmed_client(&dc->clientid, true);
+       conf = find_confirmed_client(&dc->clientid, true);
 
        if (conf) {
                clp = conf;
@@ -2200,10 +2235,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                copy_clid(new, conf);
        else /* case 4 (new client) or cases 2, 3 (client reboot): */
                gen_clid(new);
-       /*
-        * XXX: we should probably set this at creation time, and check
-        * for consistent minorversion use throughout:
-        */
        new->cl_minorversion = 0;
        gen_callback(new, setclid, rqstp);
        add_to_unconfirmed(new, strhashval);
@@ -2232,8 +2263,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                return nfserr_stale_clientid;
        nfs4_lock_state();
 
-       conf = find_confirmed_client(clid);
-       unconf = find_unconfirmed_client(clid);
+       conf = find_confirmed_client(clid, false);
+       unconf = find_unconfirmed_client(clid, false);
        /*
         * We try hard to give out unique clientid's, so if we get an
         * attempt to confirm the same clientid with a different cred,
@@ -2262,10 +2293,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                unsigned int hash = clientstr_hashval(unconf->cl_recdir);
 
                conf = find_confirmed_client_by_str(unconf->cl_recdir, hash);
-               if (conf) {
-                       nfsd4_client_record_remove(conf);
+               if (conf)
                        expire_client(conf);
-               }
                move_to_confirmed(unconf);
                nfsd4_probe_callback(unconf);
        }
@@ -2325,7 +2354,7 @@ nfsd4_init_slabs(void)
        if (openowner_slab == NULL)
                goto out_nomem;
        lockowner_slab = kmem_cache_create("nfsd4_lockowners",
-                       sizeof(struct nfs4_openowner), 0, 0, NULL);
+                       sizeof(struct nfs4_lockowner), 0, 0, NULL);
        if (lockowner_slab == NULL)
                goto out_nomem;
        file_slab = kmem_cache_create("nfsd4_files",
@@ -2447,16 +2476,20 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner,
 }
 
 static struct nfs4_openowner *
-find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open)
+find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, bool sessions)
 {
        struct nfs4_stateowner *so;
        struct nfs4_openowner *oo;
+       struct nfs4_client *clp;
 
        list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) {
                if (!so->so_is_open_owner)
                        continue;
                if (same_owner_str(so, &open->op_owner, &open->op_clientid)) {
                        oo = openowner(so);
+                       clp = oo->oo_owner.so_client;
+                       if ((bool)clp->cl_minorversion != sessions)
+                               return NULL;
                        renew_client(oo->oo_owner.so_client);
                        return oo;
                }
@@ -2536,9 +2569,14 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
        struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner;
        struct nfs4_delegation *dp;
 
-       BUG_ON(!fp);
-       /* We assume break_lease is only called once per lease: */
-       BUG_ON(fp->fi_had_conflict);
+       if (!fp) {
+               WARN(1, "(%p)->fl_owner NULL\n", fl);
+               return;
+       }
+       if (fp->fi_had_conflict) {
+               WARN(1, "duplicate break on %p\n", fp);
+               return;
+       }
        /*
         * We don't want the locks code to timeout the lease for us;
         * we'll remove it ourself if a delegation isn't returned
@@ -2600,10 +2638,10 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
                return nfserr_jukebox;
 
        strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner);
-       oo = find_openstateowner_str(strhashval, open);
+       oo = find_openstateowner_str(strhashval, open, cstate->minorversion);
        open->op_openowner = oo;
        if (!oo) {
-               clp = find_confirmed_client(clientid);
+               clp = find_confirmed_client(clientid, cstate->minorversion);
                if (clp == NULL)
                        return nfserr_expired;
                goto new_owner;
@@ -2705,11 +2743,6 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st
        return nfs_ok;
 }
 
-static void nfs4_free_stateid(struct nfs4_ol_stateid *s)
-{
-       kmem_cache_free(stateid_slab, s);
-}
-
 static inline int nfs4_access_to_access(u32 nfs4_access)
 {
        int flags = 0;
@@ -3087,7 +3120,7 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
        if (open->op_file)
                nfsd4_free_file(open->op_file);
        if (open->op_stp)
-               nfs4_free_stateid(open->op_stp);
+               free_generic_stateid(open->op_stp);
 }
 
 __be32
@@ -3104,7 +3137,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        status = nfserr_stale_clientid;
        if (STALE_CLIENTID(clid, nn))
                goto out;
-       clp = find_confirmed_client(clid);
+       clp = find_confirmed_client(clid, cstate->minorversion);
        status = nfserr_expired;
        if (clp == NULL) {
                /* We assume the client took too long to RENEW. */
@@ -3180,7 +3213,6 @@ nfs4_laundromat(void)
                clp = list_entry(pos, struct nfs4_client, cl_lru);
                dprintk("NFSD: purging unused client (clientid %08x)\n",
                        clp->cl_clientid.cl_id);
-               nfsd4_client_record_remove(clp);
                expire_client(clp);
        }
        spin_lock(&recall_lock);
@@ -3372,7 +3404,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
        return nfs_ok;
 }
 
-static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s)
+static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions)
 {
        struct nfs4_client *cl;
        struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
@@ -3381,7 +3413,7 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, s
                return nfserr_bad_stateid;
        if (STALE_STATEID(stateid, nn))
                return nfserr_stale_stateid;
-       cl = find_confirmed_client(&stateid->si_opaque.so_clid);
+       cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions);
        if (!cl)
                return nfserr_expired;
        *s = find_stateid_by_type(cl, stateid, typemask);
@@ -3414,7 +3446,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
                return check_special_stateids(net, current_fh, stateid, flags);
 
-       status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s);
+       status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion);
        if (status)
                return status;
        status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
@@ -3564,7 +3596,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
                seqid, STATEID_VAL(stateid));
 
        *stpp = NULL;
-       status = nfsd4_lookup_stateid(stateid, typemask, &s);
+       status = nfsd4_lookup_stateid(stateid, typemask, &s, cstate->minorversion);
        if (status)
                return status;
        *stpp = openlockstateid(s);
@@ -3765,6 +3797,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
 
        nfsd4_close_open_stateid(stp);
+       release_last_closed_stateid(oo);
        oo->oo_last_closed_stid = stp;
 
        if (list_empty(&oo->oo_owner.so_stateids)) {
@@ -3793,15 +3826,13 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        struct nfs4_delegation *dp;
        stateid_t *stateid = &dr->dr_stateid;
        struct nfs4_stid *s;
-       struct inode *inode;
        __be32 status;
 
        if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
                return status;
-       inode = cstate->current_fh.fh_dentry->d_inode;
 
        nfs4_lock_state();
-       status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s);
+       status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion);
        if (status)
                goto out;
        dp = delegstateid(s);
@@ -4045,8 +4076,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        struct nfs4_lockowner *lock_sop = NULL;
        struct nfs4_ol_stateid *lock_stp;
        struct file *filp = NULL;
-       struct file_lock file_lock;
-       struct file_lock conflock;
+       struct file_lock *file_lock = NULL;
+       struct file_lock *conflock = NULL;
        __be32 status = 0;
        bool new_state = false;
        int lkflg;
@@ -4116,21 +4147,28 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim)
                goto out;
 
-       locks_init_lock(&file_lock);
+       file_lock = locks_alloc_lock();
+       if (!file_lock) {
+               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
+
+       locks_init_lock(file_lock);
        switch (lock->lk_type) {
                case NFS4_READ_LT:
                case NFS4_READW_LT:
                        filp = find_readable_file(lock_stp->st_file);
                        if (filp)
                                get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
-                       file_lock.fl_type = F_RDLCK;
+                       file_lock->fl_type = F_RDLCK;
                        break;
                case NFS4_WRITE_LT:
                case NFS4_WRITEW_LT:
                        filp = find_writeable_file(lock_stp->st_file);
                        if (filp)
                                get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
-                       file_lock.fl_type = F_WRLCK;
+                       file_lock->fl_type = F_WRLCK;
                        break;
                default:
                        status = nfserr_inval;
@@ -4140,22 +4178,23 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                status = nfserr_openmode;
                goto out;
        }
-       file_lock.fl_owner = (fl_owner_t)lock_sop;
-       file_lock.fl_pid = current->tgid;
-       file_lock.fl_file = filp;
-       file_lock.fl_flags = FL_POSIX;
-       file_lock.fl_lmops = &nfsd_posix_mng_ops;
-
-       file_lock.fl_start = lock->lk_offset;
-       file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
-       nfs4_transform_lock_offset(&file_lock);
-
-       /*
-       * Try to lock the file in the VFS.
-       * Note: locks.c uses the BKL to protect the inode's lock list.
-       */
+       file_lock->fl_owner = (fl_owner_t)lock_sop;
+       file_lock->fl_pid = current->tgid;
+       file_lock->fl_file = filp;
+       file_lock->fl_flags = FL_POSIX;
+       file_lock->fl_lmops = &nfsd_posix_mng_ops;
+       file_lock->fl_start = lock->lk_offset;
+       file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
+       nfs4_transform_lock_offset(file_lock);
+
+       conflock = locks_alloc_lock();
+       if (!conflock) {
+               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
 
-       err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock);
+       err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
        switch (-err) {
        case 0: /* success! */
                update_stateid(&lock_stp->st_stid.sc_stateid);
@@ -4166,7 +4205,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        case (EAGAIN):          /* conflock holds conflicting lock */
                status = nfserr_denied;
                dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
-               nfs4_set_lock_denied(&conflock, &lock->lk_denied);
+               nfs4_set_lock_denied(conflock, &lock->lk_denied);
                break;
        case (EDEADLK):
                status = nfserr_deadlock;
@@ -4181,6 +4220,10 @@ out:
                release_lockowner(lock_sop);
        if (!cstate->replay_owner)
                nfs4_unlock_state();
+       if (file_lock)
+               locks_free_lock(file_lock);
+       if (conflock)
+               locks_free_lock(conflock);
        return status;
 }
 
@@ -4209,7 +4252,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
            struct nfsd4_lockt *lockt)
 {
        struct inode *inode;
-       struct file_lock file_lock;
+       struct file_lock *file_lock = NULL;
        struct nfs4_lockowner *lo;
        __be32 status;
        struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
@@ -4230,15 +4273,21 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                goto out;
 
        inode = cstate->current_fh.fh_dentry->d_inode;
-       locks_init_lock(&file_lock);
+       file_lock = locks_alloc_lock();
+       if (!file_lock) {
+               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
+       locks_init_lock(file_lock);
        switch (lockt->lt_type) {
                case NFS4_READ_LT:
                case NFS4_READW_LT:
-                       file_lock.fl_type = F_RDLCK;
+                       file_lock->fl_type = F_RDLCK;
                break;
                case NFS4_WRITE_LT:
                case NFS4_WRITEW_LT:
-                       file_lock.fl_type = F_WRLCK;
+                       file_lock->fl_type = F_WRLCK;
                break;
                default:
                        dprintk("NFSD: nfs4_lockt: bad lock type!\n");
@@ -4248,25 +4297,27 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner);
        if (lo)
-               file_lock.fl_owner = (fl_owner_t)lo;
-       file_lock.fl_pid = current->tgid;
-       file_lock.fl_flags = FL_POSIX;
+               file_lock->fl_owner = (fl_owner_t)lo;
+       file_lock->fl_pid = current->tgid;
+       file_lock->fl_flags = FL_POSIX;
 
-       file_lock.fl_start = lockt->lt_offset;
-       file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
+       file_lock->fl_start = lockt->lt_offset;
+       file_lock->fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
 
-       nfs4_transform_lock_offset(&file_lock);
+       nfs4_transform_lock_offset(file_lock);
 
-       status = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock);
+       status = nfsd_test_lock(rqstp, &cstate->current_fh, file_lock);
        if (status)
                goto out;
 
-       if (file_lock.fl_type != F_UNLCK) {
+       if (file_lock->fl_type != F_UNLCK) {
                status = nfserr_denied;
-               nfs4_set_lock_denied(&file_lock, &lockt->lt_denied);
+               nfs4_set_lock_denied(file_lock, &lockt->lt_denied);
        }
 out:
        nfs4_unlock_state();
+       if (file_lock)
+               locks_free_lock(file_lock);
        return status;
 }
 
@@ -4276,7 +4327,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 {
        struct nfs4_ol_stateid *stp;
        struct file *filp = NULL;
-       struct file_lock file_lock;
+       struct file_lock *file_lock = NULL;
        __be32 status;
        int err;
                                                        
@@ -4298,23 +4349,29 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                status = nfserr_lock_range;
                goto out;
        }
-       BUG_ON(!filp);
-       locks_init_lock(&file_lock);
-       file_lock.fl_type = F_UNLCK;
-       file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
-       file_lock.fl_pid = current->tgid;
-       file_lock.fl_file = filp;
-       file_lock.fl_flags = FL_POSIX; 
-       file_lock.fl_lmops = &nfsd_posix_mng_ops;
-       file_lock.fl_start = locku->lu_offset;
-
-       file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length);
-       nfs4_transform_lock_offset(&file_lock);
+       file_lock = locks_alloc_lock();
+       if (!file_lock) {
+               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
+       locks_init_lock(file_lock);
+       file_lock->fl_type = F_UNLCK;
+       file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
+       file_lock->fl_pid = current->tgid;
+       file_lock->fl_file = filp;
+       file_lock->fl_flags = FL_POSIX;
+       file_lock->fl_lmops = &nfsd_posix_mng_ops;
+       file_lock->fl_start = locku->lu_offset;
+
+       file_lock->fl_end = last_byte_offset(locku->lu_offset,
+                                               locku->lu_length);
+       nfs4_transform_lock_offset(file_lock);
 
        /*
        *  Try to unlock the file in the VFS.
        */
-       err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL);
+       err = vfs_lock_file(filp, F_SETLK, file_lock, NULL);
        if (err) {
                dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n");
                goto out_nfserr;
@@ -4328,6 +4385,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 out:
        if (!cstate->replay_owner)
                nfs4_unlock_state();
+       if (file_lock)
+               locks_free_lock(file_lock);
        return status;
 
 out_nfserr:
@@ -4425,7 +4484,7 @@ alloc_reclaim(void)
 }
 
 int
-nfs4_has_reclaimed_state(const char *name, bool use_exchange_id)
+nfs4_has_reclaimed_state(const char *name)
 {
        unsigned int strhashval = clientstr_hashval(name);
        struct nfs4_client *clp;
@@ -4478,19 +4537,16 @@ nfs4_release_reclaim(void)
 /*
  * called from OPEN, CLAIM_PREVIOUS with a new clientid. */
 struct nfs4_client_reclaim *
-nfsd4_find_reclaim_client(struct nfs4_client *clp)
+nfsd4_find_reclaim_client(const char *recdir)
 {
        unsigned int strhashval;
        struct nfs4_client_reclaim *crp = NULL;
 
-       dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n",
-                           clp->cl_name.len, clp->cl_name.data,
-                           clp->cl_recdir);
+       dprintk("NFSD: nfs4_find_reclaim_client for recdir %s\n", recdir);
 
-       /* find clp->cl_name in reclaim_str_hashtbl */
-       strhashval = clientstr_hashval(clp->cl_recdir);
+       strhashval = clientstr_hashval(recdir);
        list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) {
-               if (same_name(crp->cr_recdir, clp->cl_recdir)) {
+               if (same_name(crp->cr_recdir, recdir)) {
                        return crp;
                }
        }
@@ -4501,12 +4557,12 @@ nfsd4_find_reclaim_client(struct nfs4_client *clp)
 * Called from OPEN. Look for clientid in reclaim list.
 */
 __be32
-nfs4_check_open_reclaim(clientid_t *clid)
+nfs4_check_open_reclaim(clientid_t *clid, bool sessions)
 {
        struct nfs4_client *clp;
 
        /* find clientid in conf_id_hashtbl */
-       clp = find_confirmed_client(clid);
+       clp = find_confirmed_client(clid, sessions);
        if (clp == NULL)
                return nfserr_reclaim_bad;
 
@@ -4522,7 +4578,6 @@ void nfsd_forget_clients(u64 num)
 
        nfs4_lock_state();
        list_for_each_entry_safe(clp, next, &client_lru, cl_lru) {
-               nfsd4_client_record_remove(clp);
                expire_client(clp);
                if (++count == num)
                        break;
@@ -4582,7 +4637,7 @@ void nfsd_forget_openowners(u64 num)
        printk(KERN_INFO "NFSD: Forgot %d open owners", count);
 }
 
-int nfsd_process_n_delegations(u64 num, struct list_head *list)
+static int nfsd_process_n_delegations(u64 num, struct list_head *list)
 {
        int i, count = 0;
        struct nfs4_file *fp, *fnext;
@@ -4747,11 +4802,11 @@ __nfs4_state_shutdown(void)
        for (i = 0; i < CLIENT_HASH_SIZE; i++) {
                while (!list_empty(&conf_id_hashtbl[i])) {
                        clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash);
-                       expire_client(clp);
+                       destroy_client(clp);
                }
                while (!list_empty(&unconf_str_hashtbl[i])) {
                        clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash);
-                       expire_client(clp);
+                       destroy_client(clp);
                }
        }
        INIT_LIST_HEAD(&reaplist);