nfsd: quiet compile warning
[safe/jmp/linux-2.6] / fs / nfsd / nfs4state.c
index 9243dca..d24dd12 100644 (file)
@@ -68,6 +68,7 @@ static u32 current_delegid = 1;
 static u32 nfs4_init;
 static stateid_t zerostateid;             /* bits all 0 */
 static stateid_t onestateid;              /* bits all 1 */
+static u64 current_sessionid = 1;
 
 #define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t)))
 #define ONE_STATEID(stateid)  (!memcmp((stateid), &onestateid, sizeof(stateid_t)))
@@ -205,7 +206,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
        dp->dl_recall.cbr_dp = NULL;
        dp->dl_recall.cbr_ident = cb->cb_ident;
        dp->dl_recall.cbr_trunc = 0;
-       dp->dl_stateid.si_boot = boot_time;
+       dp->dl_stateid.si_boot = get_seconds();
        dp->dl_stateid.si_stateownerid = current_delegid++;
        dp->dl_stateid.si_fileid = 0;
        dp->dl_stateid.si_generation = 0;
@@ -401,6 +402,131 @@ dump_sessionid(const char *fn, struct nfs4_sessionid *sessionid)
        dprintk("%s: %u:%u:%u:%u\n", fn, ptr[0], ptr[1], ptr[2], ptr[3]);
 }
 
+static void
+gen_sessionid(struct nfsd4_session *ses)
+{
+       struct nfs4_client *clp = ses->se_client;
+       struct nfsd4_sessionid *sid;
+
+       sid = (struct nfsd4_sessionid *)ses->se_sessionid.data;
+       sid->clientid = clp->cl_clientid;
+       sid->sequence = current_sessionid++;
+       sid->reserved = 0;
+}
+
+/*
+ * Give the client the number of slots it requests bound by
+ * NFSD_MAX_SLOTS_PER_SESSION and by sv_drc_max_pages.
+ *
+ * If we run out of pages (sv_drc_pages_used == sv_drc_max_pages) we
+ * should (up to a point) re-negotiate active sessions and reduce their
+ * slot usage to make rooom for new connections. For now we just fail the
+ * create session.
+ */
+static int set_forechannel_maxreqs(struct nfsd4_channel_attrs *fchan)
+{
+       int status = 0, np = fchan->maxreqs * NFSD_PAGES_PER_SLOT;
+
+       spin_lock(&nfsd_serv->sv_lock);
+       if (np + nfsd_serv->sv_drc_pages_used > nfsd_serv->sv_drc_max_pages)
+               np = nfsd_serv->sv_drc_max_pages - nfsd_serv->sv_drc_pages_used;
+       nfsd_serv->sv_drc_pages_used += np;
+       spin_unlock(&nfsd_serv->sv_lock);
+
+       if (np <= 0) {
+               status = nfserr_resource;
+               fchan->maxreqs = 0;
+       } else
+               fchan->maxreqs = np / NFSD_PAGES_PER_SLOT;
+
+       return status;
+}
+
+/*
+ * fchan holds the client values on input, and the server values on output
+ */
+static int init_forechannel_attrs(struct svc_rqst *rqstp,
+                                   struct nfsd4_session *session,
+                                   struct nfsd4_channel_attrs *fchan)
+{
+       int status = 0;
+       __u32   maxcount = svc_max_payload(rqstp);
+
+       /* headerpadsz set to zero in encode routine */
+
+       /* Use the client's max request and max response size if possible */
+       if (fchan->maxreq_sz > maxcount)
+               fchan->maxreq_sz = maxcount;
+       session->se_fmaxreq_sz = fchan->maxreq_sz;
+
+       if (fchan->maxresp_sz > maxcount)
+               fchan->maxresp_sz = maxcount;
+       session->se_fmaxresp_sz = fchan->maxresp_sz;
+
+       /* Set the max response cached size our default which is
+        * a multiple of PAGE_SIZE and small */
+       session->se_fmaxresp_cached = NFSD_PAGES_PER_SLOT * PAGE_SIZE;
+       fchan->maxresp_cached = session->se_fmaxresp_cached;
+
+       /* Use the client's maxops if possible */
+       if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND)
+               fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND;
+       session->se_fmaxops = fchan->maxops;
+
+       /* try to use the client requested number of slots */
+       if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
+               fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
+
+       /* FIXME: Error means no more DRC pages so the server should
+        * recover pages from existing sessions. For now fail session
+        * creation.
+        */
+       status = set_forechannel_maxreqs(fchan);
+
+       session->se_fnumslots = fchan->maxreqs;
+       return status;
+}
+
+static int
+alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
+                  struct nfsd4_create_session *cses)
+{
+       struct nfsd4_session *new, tmp;
+       int idx, status = nfserr_resource, slotsize;
+
+       memset(&tmp, 0, sizeof(tmp));
+
+       /* FIXME: For now, we just accept the client back channel attributes. */
+       status = init_forechannel_attrs(rqstp, &tmp, &cses->fore_channel);
+       if (status)
+               goto out;
+
+       /* allocate struct nfsd4_session and slot table in one piece */
+       slotsize = tmp.se_fnumslots * sizeof(struct nfsd4_slot);
+       new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL);
+       if (!new)
+               goto out;
+
+       memcpy(new, &tmp, sizeof(*new));
+
+       new->se_client = clp;
+       gen_sessionid(new);
+       idx = hash_sessionid(&new->se_sessionid);
+       memcpy(clp->cl_sessionid.data, new->se_sessionid.data,
+              NFS4_MAX_SESSIONID_LEN);
+
+       new->se_flags = cses->flags;
+       kref_init(&new->se_ref);
+       spin_lock(&sessionid_lock);
+       list_add(&new->se_hash, &sessionid_hashtbl[idx]);
+       list_add(&new->se_perclnt, &clp->cl_sessions);
+       spin_unlock(&sessionid_lock);
+
+       status = nfs_ok;
+out:
+       return status;
+}
+
 /* caller must hold sessionid_lock */
 static struct nfsd4_session *
 find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid)
@@ -441,12 +567,19 @@ release_session(struct nfsd4_session *ses)
        nfsd4_put_session(ses);
 }
 
+static void nfsd4_release_respages(struct page **respages, short resused);
+
 void
 free_session(struct kref *kref)
 {
        struct nfsd4_session *ses;
+       int i;
 
        ses = container_of(kref, struct nfsd4_session, se_ref);
+       for (i = 0; i < ses->se_fnumslots; i++) {
+               struct nfsd4_cache_entry *e = &ses->se_slots[i].sl_cache_entry;
+               nfsd4_release_respages(e->ce_respages, e->ce_resused);
+       }
        kfree(ses->se_slots);
        kfree(ses);
 }
@@ -470,8 +603,8 @@ STALE_CLIENTID(clientid_t *clid)
 {
        if (clid->cl_boot == boot_time)
                return 0;
-       dprintk("NFSD stale clientid (%08x/%08x)\n", 
-                       clid->cl_boot, clid->cl_id);
+       dprintk("NFSD stale clientid (%08x/%08x) boot_time %08lx\n",
+               clid->cl_boot, clid->cl_id, boot_time);
        return 1;
 }
 
@@ -516,6 +649,8 @@ static inline void
 free_client(struct nfs4_client *clp)
 {
        shutdown_callback_client(clp);
+       nfsd4_release_respages(clp->cl_slot.sl_cache_entry.ce_respages,
+                            clp->cl_slot.sl_cache_entry.ce_resused);
        if (clp->cl_cred.cr_group_info)
                put_group_info(clp->cl_cred.cr_group_info);
        kfree(clp->cl_principal);
@@ -852,6 +987,179 @@ out_err:
        return;
 }
 
+void
+nfsd4_set_statp(struct svc_rqst *rqstp, __be32 *statp)
+{
+       struct nfsd4_compoundres *resp = rqstp->rq_resp;
+
+       resp->cstate.statp = statp;
+}
+
+/*
+ * Dereference the result pages.
+ */
+static void
+nfsd4_release_respages(struct page **respages, short resused)
+{
+       int i;
+
+       dprintk("--> %s\n", __func__);
+       for (i = 0; i < resused; i++) {
+               if (!respages[i])
+                       continue;
+               put_page(respages[i]);
+               respages[i] = NULL;
+       }
+}
+
+static void
+nfsd4_copy_pages(struct page **topages, struct page **frompages, short count)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               topages[i] = frompages[i];
+               if (!topages[i])
+                       continue;
+               get_page(topages[i]);
+       }
+}
+
+/*
+ * Cache the reply pages up to NFSD_PAGES_PER_SLOT + 1, clearing the previous
+ * pages. We add a page to NFSD_PAGES_PER_SLOT for the case where the total
+ * length of the XDR response is less than se_fmaxresp_cached
+ * (NFSD_PAGES_PER_SLOT * PAGE_SIZE) but the xdr_buf pages is used for a
+ * of the reply (e.g. readdir).
+ *
+ * Store the base and length of the rq_req.head[0] page
+ * of the NFSv4.1 data, just past the rpc header.
+ */
+void
+nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
+{
+       struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry;
+       struct svc_rqst *rqstp = resp->rqstp;
+       struct nfsd4_compoundargs *args = rqstp->rq_argp;
+       struct nfsd4_op *op = &args->ops[resp->opcnt];
+       struct kvec *resv = &rqstp->rq_res.head[0];
+
+       dprintk("--> %s entry %p\n", __func__, entry);
+
+       /* Don't cache a failed OP_SEQUENCE. */
+       if (resp->opcnt == 1 && op->opnum == OP_SEQUENCE && resp->cstate.status)
+               return;
+
+       nfsd4_release_respages(entry->ce_respages, entry->ce_resused);
+       entry->ce_opcnt = resp->opcnt;
+       entry->ce_status = resp->cstate.status;
+
+       /*
+        * Don't need a page to cache just the sequence operation - the slot
+        * does this for us!
+        */
+
+       if (nfsd4_not_cached(resp)) {
+               entry->ce_resused = 0;
+               entry->ce_rpchdrlen = 0;
+               dprintk("%s Just cache SEQUENCE. ce_cachethis %d\n", __func__,
+                       resp->cstate.slot->sl_cache_entry.ce_cachethis);
+               return;
+       }
+       entry->ce_resused = rqstp->rq_resused;
+       if (entry->ce_resused > NFSD_PAGES_PER_SLOT + 1)
+               entry->ce_resused = NFSD_PAGES_PER_SLOT + 1;
+       nfsd4_copy_pages(entry->ce_respages, rqstp->rq_respages,
+                        entry->ce_resused);
+       entry->ce_datav.iov_base = resp->cstate.statp;
+       entry->ce_datav.iov_len = resv->iov_len - ((char *)resp->cstate.statp -
+                               (char *)page_address(rqstp->rq_respages[0]));
+       /* Current request rpc header length*/
+       entry->ce_rpchdrlen = (char *)resp->cstate.statp -
+                               (char *)page_address(rqstp->rq_respages[0]);
+}
+
+/*
+ * We keep the rpc header, but take the nfs reply from the replycache.
+ */
+static int
+nfsd41_copy_replay_data(struct nfsd4_compoundres *resp,
+                       struct nfsd4_cache_entry *entry)
+{
+       struct svc_rqst *rqstp = resp->rqstp;
+       struct kvec *resv = &resp->rqstp->rq_res.head[0];
+       int len;
+
+       /* Current request rpc header length*/
+       len = (char *)resp->cstate.statp -
+                       (char *)page_address(rqstp->rq_respages[0]);
+       if (entry->ce_datav.iov_len + len > PAGE_SIZE) {
+               dprintk("%s v41 cached reply too large (%Zd).\n", __func__,
+                       entry->ce_datav.iov_len);
+               return 0;
+       }
+       /* copy the cached reply nfsd data past the current rpc header */
+       memcpy((char *)resv->iov_base + len, entry->ce_datav.iov_base,
+               entry->ce_datav.iov_len);
+       resv->iov_len = len + entry->ce_datav.iov_len;
+       return 1;
+}
+
+/*
+ * Keep the first page of the replay. Copy the NFSv4.1 data from the first
+ * cached page.  Replace any futher replay pages from the cache.
+ */
+__be32
+nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp,
+                        struct nfsd4_sequence *seq)
+{
+       struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry;
+       __be32 status;
+
+       dprintk("--> %s entry %p\n", __func__, entry);
+
+       /*
+        * If this is just the sequence operation, we did not keep
+        * a page in the cache entry because we can just use the
+        * slot info stored in struct nfsd4_sequence that was checked
+        * against the slot in nfsd4_sequence().
+        *
+        * This occurs when seq->cachethis is FALSE, or when the client
+        * session inactivity timer fires and a solo sequence operation
+        * is sent (lease renewal).
+        */
+       if (seq && nfsd4_not_cached(resp)) {
+               seq->maxslots = resp->cstate.session->se_fnumslots;
+               return nfs_ok;
+       }
+
+       if (!nfsd41_copy_replay_data(resp, entry)) {
+               /*
+                * Not enough room to use the replay rpc header, send the
+                * cached header. Release all the allocated result pages.
+                */
+               svc_free_res_pages(resp->rqstp);
+               nfsd4_copy_pages(resp->rqstp->rq_respages, entry->ce_respages,
+                       entry->ce_resused);
+       } else {
+               /* Release all but the first allocated result page */
+
+               resp->rqstp->rq_resused--;
+               svc_free_res_pages(resp->rqstp);
+
+               nfsd4_copy_pages(&resp->rqstp->rq_respages[1],
+                                &entry->ce_respages[1],
+                                entry->ce_resused - 1);
+       }
+
+       resp->rqstp->rq_resused = entry->ce_resused;
+       resp->opcnt = entry->ce_opcnt;
+       resp->cstate.iovlen = entry->ce_datav.iov_len + entry->ce_rpchdrlen;
+       status = entry->ce_status;
+
+       return status;
+}
+
 /*
  * Set the exchange_id flags returned by the server.
  */
@@ -986,11 +1294,12 @@ out_copy:
        exid->clientid.cl_boot = new->cl_clientid.cl_boot;
        exid->clientid.cl_id = new->cl_clientid.cl_id;
 
-       new->cl_seqid = exid->seqid = 1;
+       new->cl_slot.sl_seqid = 0;
+       exid->seqid = 1;
        nfsd4_set_ex_flags(new, exid);
 
        dprintk("nfsd4_exchange_id seqid %d flags %x\n",
-               new->cl_seqid, new->cl_exchange_flags);
+               new->cl_slot.sl_seqid, new->cl_exchange_flags);
        status = nfs_ok;
 
 out:
@@ -1031,7 +1340,81 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                     struct nfsd4_compound_state *cstate,
                     struct nfsd4_create_session *cr_ses)
 {
-       return -1;      /* stub */
+       u32 ip_addr = svc_addr_in(rqstp)->sin_addr.s_addr;
+       struct nfsd4_compoundres *resp = rqstp->rq_resp;
+       struct nfs4_client *conf, *unconf;
+       struct nfsd4_slot *slot = NULL;
+       int status = 0;
+
+       nfs4_lock_state();
+       unconf = find_unconfirmed_client(&cr_ses->clientid);
+       conf = find_confirmed_client(&cr_ses->clientid);
+
+       if (conf) {
+               slot = &conf->cl_slot;
+               status = check_slot_seqid(cr_ses->seqid, slot);
+               if (status == nfserr_replay_cache) {
+                       dprintk("Got a create_session replay! seqid= %d\n",
+                               slot->sl_seqid);
+                       cstate->slot = slot;
+                       cstate->status = status;
+                       /* Return the cached reply status */
+                       status = nfsd4_replay_cache_entry(resp, NULL);
+                       goto out;
+               } else if (cr_ses->seqid != conf->cl_slot.sl_seqid + 1) {
+                       status = nfserr_seq_misordered;
+                       dprintk("Sequence misordered!\n");
+                       dprintk("Expected seqid= %d but got seqid= %d\n",
+                               slot->sl_seqid, cr_ses->seqid);
+                       goto out;
+               }
+               conf->cl_slot.sl_seqid++;
+       } else if (unconf) {
+               if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
+                   (ip_addr != unconf->cl_addr)) {
+                       status = nfserr_clid_inuse;
+                       goto out;
+               }
+
+               slot = &unconf->cl_slot;
+               status = check_slot_seqid(cr_ses->seqid, slot);
+               if (status) {
+                       /* an unconfirmed replay returns misordered */
+                       status = nfserr_seq_misordered;
+                       goto out;
+               }
+
+               slot->sl_seqid++; /* from 0 to 1 */
+               move_to_confirmed(unconf);
+
+               /*
+                * We do not support RDMA or persistent sessions
+                */
+               cr_ses->flags &= ~SESSION4_PERSIST;
+               cr_ses->flags &= ~SESSION4_RDMA;
+
+               conf = unconf;
+       } else {
+               status = nfserr_stale_clientid;
+               goto out;
+       }
+
+       status = alloc_init_session(rqstp, conf, cr_ses);
+       if (status)
+               goto out;
+
+       memcpy(cr_ses->sessionid.data, conf->cl_sessionid.data,
+              NFS4_MAX_SESSIONID_LEN);
+       cr_ses->seqid = slot->sl_seqid;
+
+       slot->sl_inuse = true;
+       cstate->slot = slot;
+       /* Ensure a page is used for the cache */
+       slot->sl_cache_entry.ce_cachethis = 1;
+out:
+       nfs4_unlock_state();
+       dprintk("%s returns %d\n", __func__, ntohl(status));
+       return status;
 }
 
 __be32
@@ -1039,7 +1422,34 @@ nfsd4_destroy_session(struct svc_rqst *r,
                      struct nfsd4_compound_state *cstate,
                      struct nfsd4_destroy_session *sessionid)
 {
-       return -1;      /* stub */
+       struct nfsd4_session *ses;
+       u32 status = nfserr_badsession;
+
+       /* Notes:
+        * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
+        * - Should we return nfserr_back_chan_busy if waiting for
+        *   callbacks on to-be-destroyed session?
+        * - Do we need to clear any callback info from previous session?
+        */
+
+       dump_sessionid(__func__, &sessionid->sessionid);
+       spin_lock(&sessionid_lock);
+       ses = find_in_sessionid_hashtbl(&sessionid->sessionid);
+       if (!ses) {
+               spin_unlock(&sessionid_lock);
+               goto out;
+       }
+
+       unhash_session(ses);
+       spin_unlock(&sessionid_lock);
+
+       /* wait for callbacks */
+       shutdown_callback_client(ses->se_client);
+       nfsd4_put_session(ses);
+       status = nfs_ok;
+out:
+       dprintk("%s returns %d\n", __func__, ntohl(status));
+       return status;
 }
 
 __be32
@@ -1072,6 +1482,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        if (status == nfserr_replay_cache) {
                cstate->slot = slot;
                cstate->session = session;
+               /* Return the cached reply status and set cstate->status
+                * for nfsd4_svc_encode_compoundres processing */
+               status = nfsd4_replay_cache_entry(resp, seq);
+               cstate->status = nfserr_replay_cache;
                goto replay_cache;
        }
        if (status)
@@ -1080,6 +1494,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        /* Success! bump slot seqid */
        slot->sl_inuse = true;
        slot->sl_seqid = seq->seqid;
+       slot->sl_cache_entry.ce_cachethis = seq->cachethis;
+       /* Always set the cache entry cachethis for solo sequence */
+       if (nfsd4_is_solo_sequence(resp))
+               slot->sl_cache_entry.ce_cachethis = 1;
 
        cstate->slot = slot;
        cstate->session = session;
@@ -1465,13 +1883,14 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *
        stp->st_stateowner = sop;
        get_nfs4_file(fp);
        stp->st_file = fp;
-       stp->st_stateid.si_boot = boot_time;
+       stp->st_stateid.si_boot = get_seconds();
        stp->st_stateid.si_stateownerid = sop->so_id;
        stp->st_stateid.si_fileid = fp->fi_id;
        stp->st_stateid.si_generation = 0;
        stp->st_access_bmap = 0;
        stp->st_deny_bmap = 0;
-       __set_bit(open->op_share_access, &stp->st_access_bmap);
+       __set_bit(open->op_share_access & ~NFS4_SHARE_WANT_MASK,
+                 &stp->st_access_bmap);
        __set_bit(open->op_share_deny, &stp->st_deny_bmap);
        stp->st_openstp = NULL;
 }
@@ -1525,11 +1944,21 @@ find_file(struct inode *ino)
        return NULL;
 }
 
-static inline int access_valid(u32 x)
+static inline int access_valid(u32 x, u32 minorversion)
 {
-       if (x < NFS4_SHARE_ACCESS_READ)
+       if ((x & NFS4_SHARE_ACCESS_MASK) < NFS4_SHARE_ACCESS_READ)
                return 0;
-       if (x > NFS4_SHARE_ACCESS_BOTH)
+       if ((x & NFS4_SHARE_ACCESS_MASK) > NFS4_SHARE_ACCESS_BOTH)
+               return 0;
+       x &= ~NFS4_SHARE_ACCESS_MASK;
+       if (minorversion && x) {
+               if ((x & NFS4_SHARE_WANT_MASK) > NFS4_SHARE_WANT_CANCEL)
+                       return 0;
+               if ((x & NFS4_SHARE_WHEN_MASK) > NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED)
+                       return 0;
+               x &= ~(NFS4_SHARE_WANT_MASK | NFS4_SHARE_WHEN_MASK);
+       }
+       if (x)
                return 0;
        return 1;
 }
@@ -1765,7 +2194,8 @@ static struct lock_manager_operations nfsd_lease_mng_ops = {
 
 
 __be32
-nfsd4_process_open1(struct nfsd4_open *open)
+nfsd4_process_open1(struct nfsd4_compound_state *cstate,
+                   struct nfsd4_open *open)
 {
        clientid_t *clientid = &open->op_clientid;
        struct nfs4_client *clp = NULL;
@@ -1788,6 +2218,9 @@ nfsd4_process_open1(struct nfsd4_open *open)
                        return nfserr_expired;
                goto renew;
        }
+       /* When sessions are used, skip open sequenceid processing */
+       if (nfsd4_has_session(cstate))
+               goto renew;
        if (!sop->so_confirmed) {
                /* Replace unconfirmed owners without checking for replay. */
                clp = sop->so_client;
@@ -2065,6 +2498,7 @@ out:
 __be32
 nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
 {
+       struct nfsd4_compoundres *resp = rqstp->rq_resp;
        struct nfs4_file *fp = NULL;
        struct inode *ino = current_fh->fh_dentry->d_inode;
        struct nfs4_stateid *stp = NULL;
@@ -2072,7 +2506,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
        __be32 status;
 
        status = nfserr_inval;
-       if (!access_valid(open->op_share_access)
+       if (!access_valid(open->op_share_access, resp->cstate.minorversion)
                        || !deny_valid(open->op_share_deny))
                goto out;
        /*
@@ -2123,9 +2557,14 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
                        release_open_stateid(stp);
                        goto out;
                }
+               if (nfsd4_has_session(&resp->cstate))
+                       update_stateid(&stp->st_stateid);
        }
        memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t));
 
+       if (nfsd4_has_session(&resp->cstate))
+               open->op_stateowner->so_confirmed = 1;
+
        /*
        * Attempt to hand out a delegation. No error return, because the
        * OPEN succeeds even if we fail.
@@ -2146,7 +2585,8 @@ out:
        * To finish the open response, we just need to set the rflags.
        */
        open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX;
-       if (!open->op_stateowner->so_confirmed)
+       if (!open->op_stateowner->so_confirmed &&
+           !nfsd4_has_session(&resp->cstate))
                open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM;
 
        return status;
@@ -2299,12 +2739,42 @@ nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp)
 static int
 STALE_STATEID(stateid_t *stateid)
 {
-       if (stateid->si_boot == boot_time)
-               return 0;
-       dprintk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
-               stateid->si_boot, stateid->si_stateownerid, stateid->si_fileid,
-               stateid->si_generation);
-       return 1;
+       if (time_after((unsigned long)boot_time,
+                       (unsigned long)stateid->si_boot)) {
+               dprintk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
+                       stateid->si_boot, stateid->si_stateownerid,
+                       stateid->si_fileid, stateid->si_generation);
+               return 1;
+       }
+       return 0;
+}
+
+static int
+EXPIRED_STATEID(stateid_t *stateid)
+{
+       if (time_before((unsigned long)boot_time,
+                       ((unsigned long)stateid->si_boot)) &&
+           time_before((unsigned long)(stateid->si_boot + lease_time), get_seconds())) {
+               dprintk("NFSD: expired stateid (%08x/%08x/%08x/%08x)!\n",
+                       stateid->si_boot, stateid->si_stateownerid,
+                       stateid->si_fileid, stateid->si_generation);
+               return 1;
+       }
+       return 0;
+}
+
+static __be32
+stateid_error_map(stateid_t *stateid)
+{
+       if (STALE_STATEID(stateid))
+               return nfserr_stale_stateid;
+       if (EXPIRED_STATEID(stateid))
+               return nfserr_expired;
+
+       dprintk("NFSD: bad stateid (%08x/%08x/%08x/%08x)!\n",
+               stateid->si_boot, stateid->si_stateownerid,
+               stateid->si_fileid, stateid->si_generation);
+       return nfserr_bad_stateid;
 }
 
 static inline int
@@ -2363,8 +2833,15 @@ grace_disallows_io(struct inode *inode)
        return locks_in_grace() && mandatory_lock(inode);
 }
 
-static int check_stateid_generation(stateid_t *in, stateid_t *ref)
+static int check_stateid_generation(stateid_t *in, stateid_t *ref, int flags)
 {
+       /*
+        * When sessions are used the stateid generation number is ignored
+        * when it is zero.
+        */
+       if ((flags & HAS_SESSION) && in->si_generation == 0)
+               goto out;
+
        /* If the client sends us a stateid from the future, it's buggy: */
        if (in->si_generation > ref->si_generation)
                return nfserr_bad_stateid;
@@ -2380,6 +2857,7 @@ static int check_stateid_generation(stateid_t *in, stateid_t *ref)
         */
        if (in->si_generation < ref->si_generation)
                return nfserr_old_stateid;
+out:
        return nfs_ok;
 }
 
@@ -2392,10 +2870,12 @@ static int is_delegation_stateid(stateid_t *stateid)
 * Checks for stateid operations
 */
 __be32
-nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int flags, struct file **filpp)
+nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
+                          stateid_t *stateid, int flags, struct file **filpp)
 {
        struct nfs4_stateid *stp = NULL;
        struct nfs4_delegation *dp = NULL;
+       struct svc_fh *current_fh = &cstate->current_fh;
        struct inode *ino = current_fh->fh_dentry->d_inode;
        __be32 status;
 
@@ -2405,6 +2885,9 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
        if (grace_disallows_io(ino))
                return nfserr_grace;
 
+       if (nfsd4_has_session(cstate))
+               flags |= HAS_SESSION;
+
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
                return check_special_stateids(current_fh, stateid, flags);
 
@@ -2415,9 +2898,12 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
        status = nfserr_bad_stateid;
        if (is_delegation_stateid(stateid)) {
                dp = find_delegation_stateid(ino, stateid);
-               if (!dp)
+               if (!dp) {
+                       status = stateid_error_map(stateid);
                        goto out;
-               status = check_stateid_generation(stateid, &dp->dl_stateid);
+               }
+               status = check_stateid_generation(stateid, &dp->dl_stateid,
+                                                 flags);
                if (status)
                        goto out;
                status = nfs4_check_delegmode(dp, flags);
@@ -2428,13 +2914,16 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
                        *filpp = dp->dl_vfs_file;
        } else { /* open or lock stateid */
                stp = find_stateid(stateid, flags);
-               if (!stp)
+               if (!stp) {
+                       status = stateid_error_map(stateid);
                        goto out;
+               }
                if (nfs4_check_fh(current_fh, stp))
                        goto out;
                if (!stp->st_stateowner->so_confirmed)
                        goto out;
-               status = check_stateid_generation(stateid, &stp->st_stateid);
+               status = check_stateid_generation(stateid, &stp->st_stateid,
+                                                 flags);
                if (status)
                        goto out;
                status = nfs4_check_openmode(stp, flags);
@@ -2460,10 +2949,14 @@ setlkflg (int type)
  * Checks for sequence id mutating operations. 
  */
 static __be32
-nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, struct nfsd4_lock *lock)
+nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
+                        stateid_t *stateid, int flags,
+                        struct nfs4_stateowner **sopp,
+                        struct nfs4_stateid **stpp, struct nfsd4_lock *lock)
 {
        struct nfs4_stateid *stp;
        struct nfs4_stateowner *sop;
+       struct svc_fh *current_fh = &cstate->current_fh;
        __be32 status;
 
        dprintk("NFSD: preprocess_seqid_op: seqid=%d " 
@@ -2481,6 +2974,10 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
 
        if (STALE_STATEID(stateid))
                return nfserr_stale_stateid;
+
+       if (nfsd4_has_session(cstate))
+               flags |= HAS_SESSION;
+
        /*
        * We return BAD_STATEID if filehandle doesn't match stateid, 
        * the confirmed flag is incorrecly set, or the generation 
@@ -2494,7 +2991,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
                 */
                sop = search_close_lru(stateid->si_stateownerid, flags);
                if (sop == NULL)
-                       return nfserr_bad_stateid;
+                       return stateid_error_map(stateid);
                *sopp = sop;
                goto check_replay;
        }
@@ -2513,8 +3010,9 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
                if (lock->lk_is_new) {
                        if (!sop->so_is_open_owner)
                                return nfserr_bad_stateid;
-                       if (!same_clid(&clp->cl_clientid, lockclid))
-                              return nfserr_bad_stateid;
+                       if (!(flags & HAS_SESSION) &&
+                           !same_clid(&clp->cl_clientid, lockclid))
+                               return nfserr_bad_stateid;
                        /* stp is the open stateid */
                        status = nfs4_check_openmode(stp, lkflg);
                        if (status)
@@ -2537,7 +3035,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
        *  For the moment, we ignore the possibility of 
        *  generation number wraparound.
        */
-       if (seqid != sop->so_seqid)
+       if (!(flags & HAS_SESSION) && seqid != sop->so_seqid)
                goto check_replay;
 
        if (sop->so_confirmed && flags & CONFIRM) {
@@ -2550,7 +3048,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
                                " confirmed yet!\n");
                return nfserr_bad_stateid;
        }
-       status = check_stateid_generation(stateid, &stp->st_stateid);
+       status = check_stateid_generation(stateid, &stp->st_stateid, flags);
        if (status)
                return status;
        renew_client(sop->so_client);
@@ -2586,7 +3084,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        nfs4_lock_state();
 
-       if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+       if ((status = nfs4_preprocess_seqid_op(cstate,
                                        oc->oc_seqid, &oc->oc_req_stateid,
                                        CONFIRM | OPEN_STATE,
                                        &oc->oc_stateowner, &stp, NULL)))
@@ -2651,12 +3149,12 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
                        (int)cstate->current_fh.fh_dentry->d_name.len,
                        cstate->current_fh.fh_dentry->d_name.name);
 
-       if (!access_valid(od->od_share_access)
+       if (!access_valid(od->od_share_access, cstate->minorversion)
                        || !deny_valid(od->od_share_deny))
                return nfserr_inval;
 
        nfs4_lock_state();
-       if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+       if ((status = nfs4_preprocess_seqid_op(cstate,
                                        od->od_seqid,
                                        &od->od_stateid, 
                                        OPEN_STATE,
@@ -2709,7 +3207,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        nfs4_lock_state();
        /* check close_lru for replay */
-       if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+       if ((status = nfs4_preprocess_seqid_op(cstate,
                                        close->cl_seqid,
                                        &close->cl_stateid, 
                                        OPEN_STATE | CLOSE_STATE,
@@ -2745,11 +3243,14 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        stateid_t *stateid = &dr->dr_stateid;
        struct inode *inode;
        __be32 status;
+       int flags = 0;
 
        if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
                return status;
        inode = cstate->current_fh.fh_dentry->d_inode;
 
+       if (nfsd4_has_session(cstate))
+               flags |= HAS_SESSION;
        nfs4_lock_state();
        status = nfserr_bad_stateid;
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
@@ -2761,9 +3262,11 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (!is_delegation_stateid(stateid))
                goto out;
        dp = find_delegation_stateid(inode, stateid);
-       if (!dp)
+       if (!dp) {
+               status = stateid_error_map(stateid);
                goto out;
-       status = check_stateid_generation(stateid, &dp->dl_stateid);
+       }
+       status = check_stateid_generation(stateid, &dp->dl_stateid, flags);
        if (status)
                goto out;
        renew_client(dp->dl_client);
@@ -2989,7 +3492,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc
        stp->st_stateowner = sop;
        get_nfs4_file(fp);
        stp->st_file = fp;
-       stp->st_stateid.si_boot = boot_time;
+       stp->st_stateid.si_boot = get_seconds();
        stp->st_stateid.si_stateownerid = sop->so_id;
        stp->st_stateid.si_fileid = fp->fi_id;
        stp->st_stateid.si_generation = 0;
@@ -3052,11 +3555,12 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                struct nfs4_file *fp;
                
                status = nfserr_stale_clientid;
-               if (STALE_CLIENTID(&lock->lk_new_clientid))
+               if (!nfsd4_has_session(cstate) &&
+                   STALE_CLIENTID(&lock->lk_new_clientid))
                        goto out;
 
                /* validate and update open stateid and open seqid */
-               status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+               status = nfs4_preprocess_seqid_op(cstate,
                                        lock->lk_new_open_seqid,
                                        &lock->lk_new_open_stateid,
                                        OPEN_STATE,
@@ -3083,7 +3587,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        goto out;
        } else {
                /* lock (lock owner + lock stateid) already exists */
-               status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+               status = nfs4_preprocess_seqid_op(cstate,
                                       lock->lk_old_lock_seqid, 
                                       &lock->lk_old_lock_stateid, 
                                       LOCK_STATE,
@@ -3206,7 +3710,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        nfs4_lock_state();
 
        status = nfserr_stale_clientid;
-       if (STALE_CLIENTID(&lockt->lt_clientid))
+       if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid))
                goto out;
 
        if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) {
@@ -3279,7 +3783,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        nfs4_lock_state();
                                                                                
-       if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+       if ((status = nfs4_preprocess_seqid_op(cstate,
                                        locku->lu_seqid, 
                                        &locku->lu_stateid, 
                                        LOCK_STATE,