nfsd4: simplify stateid sequencing checks
authorJ. Bruce Fields <bfields@citi.umich.edu>
Sun, 27 Jan 2008 00:08:12 +0000 (19:08 -0500)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Wed, 23 Apr 2008 20:13:37 +0000 (16:13 -0400)
Pull this common code into a separate function.

Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
fs/nfsd/nfs4state.c

index b73e96d..b83b58d 100644 (file)
@@ -1975,6 +1975,26 @@ io_during_grace_disallowed(struct inode *inode, int flags)
                && mandatory_lock(inode);
 }
 
+static int check_stateid_generation(stateid_t *in, stateid_t *ref)
+{
+       /* If the client sends us a stateid from the future, it's buggy: */
+       if (in->si_generation > ref->si_generation)
+               return nfserr_bad_stateid;
+       /*
+        * The following, however, can happen.  For example, if the
+        * client sends an open and some IO at the same time, the open
+        * may bump si_generation while the IO is still in flight.
+        * Thanks to hard links and renames, the client never knows what
+        * file an open will affect.  So it could avoid that situation
+        * only by serializing all opens and IO from the same open
+        * owner.  To recover from the old_stateid error, the client
+        * will just have to retry the IO:
+        */
+       if (in->si_generation < ref->si_generation)
+               return nfserr_old_stateid;
+       return nfs_ok;
+}
+
 /*
 * Checks for stateid operations
 */
@@ -2023,12 +2043,8 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
                        goto out;
                stidp = &stp->st_stateid;
        }
-       if (stateid->si_generation > stidp->si_generation)
-               goto out;
-
-       /* OLD STATEID */
-       status = nfserr_old_stateid;
-       if (stateid->si_generation < stidp->si_generation)
+       status = check_stateid_generation(stateid, stidp);
+       if (status)
                goto out;
        if (stp) {
                if ((status = nfs4_check_openmode(stp,flags)))
@@ -2065,6 +2081,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
 {
        struct nfs4_stateid *stp;
        struct nfs4_stateowner *sop;
+       __be32 status;
 
        dprintk("NFSD: preprocess_seqid_op: seqid=%d " 
                        "stateid = (%08x/%08x/%08x/%08x)\n", seqid,
@@ -2150,15 +2167,9 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
                                " confirmed yet!\n");
                return nfserr_bad_stateid;
        }
-       if (stateid->si_generation > stp->st_stateid.si_generation) {
-               dprintk("NFSD: preprocess_seqid_op: future stateid?!\n");
-               return nfserr_bad_stateid;
-       }
-
-       if (stateid->si_generation < stp->st_stateid.si_generation) {
-               dprintk("NFSD: preprocess_seqid_op: old stateid!\n");
-               return nfserr_old_stateid;
-       }
+       status = check_stateid_generation(stateid, &stp->st_stateid);
+       if (status)
+               return status;
        renew_client(sop->so_client);
        return nfs_ok;