diff options
-rw-r--r-- | fs/nfsd/nfs4state.c | 52 | ||||
-rw-r--r-- | fs/nfsd/state.h | 3 |
2 files changed, 26 insertions, 29 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0198328c0e84..106e8fa63cdf 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -3168,6 +3168,12 @@ grace_disallows_io(struct inode *inode) | |||
3168 | return locks_in_grace() && mandatory_lock(inode); | 3168 | return locks_in_grace() && mandatory_lock(inode); |
3169 | } | 3169 | } |
3170 | 3170 | ||
3171 | /* Returns true iff a is later than b: */ | ||
3172 | static bool stateid_generation_after(stateid_t *a, stateid_t *b) | ||
3173 | { | ||
3174 | return (s32)a->si_generation - (s32)b->si_generation > 0; | ||
3175 | } | ||
3176 | |||
3171 | static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session) | 3177 | static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session) |
3172 | { | 3178 | { |
3173 | /* | 3179 | /* |
@@ -3175,25 +3181,25 @@ static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_sess | |||
3175 | * when it is zero. | 3181 | * when it is zero. |
3176 | */ | 3182 | */ |
3177 | if (has_session && in->si_generation == 0) | 3183 | if (has_session && in->si_generation == 0) |
3178 | goto out; | 3184 | return nfs_ok; |
3185 | |||
3186 | if (in->si_generation == ref->si_generation) | ||
3187 | return nfs_ok; | ||
3179 | 3188 | ||
3180 | /* If the client sends us a stateid from the future, it's buggy: */ | 3189 | /* If the client sends us a stateid from the future, it's buggy: */ |
3181 | if (in->si_generation > ref->si_generation) | 3190 | if (stateid_generation_after(in, ref)) |
3182 | return nfserr_bad_stateid; | 3191 | return nfserr_bad_stateid; |
3183 | /* | 3192 | /* |
3184 | * The following, however, can happen. For example, if the | 3193 | * However, we could see a stateid from the past, even from a |
3185 | * client sends an open and some IO at the same time, the open | 3194 | * non-buggy client. For example, if the client sends a lock |
3186 | * may bump si_generation while the IO is still in flight. | 3195 | * while some IO is outstanding, the lock may bump si_generation |
3187 | * Thanks to hard links and renames, the client never knows what | 3196 | * while the IO is still in flight. The client could avoid that |
3188 | * file an open will affect. So it could avoid that situation | 3197 | * situation by waiting for responses on all the IO requests, |
3189 | * only by serializing all opens and IO from the same open | 3198 | * but better performance may result in retrying IO that |
3190 | * owner. To recover from the old_stateid error, the client | 3199 | * receives an old_stateid error if requests are rarely |
3191 | * will just have to retry the IO: | 3200 | * reordered in flight: |
3192 | */ | 3201 | */ |
3193 | if (in->si_generation < ref->si_generation) | 3202 | return nfserr_old_stateid; |
3194 | return nfserr_old_stateid; | ||
3195 | out: | ||
3196 | return nfs_ok; | ||
3197 | } | 3203 | } |
3198 | 3204 | ||
3199 | static int is_delegation_stateid(stateid_t *stateid) | 3205 | static int is_delegation_stateid(stateid_t *stateid) |
@@ -3353,16 +3359,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3353 | ret = nfserr_bad_stateid; | 3359 | ret = nfserr_bad_stateid; |
3354 | goto out; | 3360 | goto out; |
3355 | } | 3361 | } |
3356 | if (stateid->si_generation != 0) { | 3362 | ret = check_stateid_generation(stateid, &stp->st_stateid, 1); |
3357 | if (stateid->si_generation < stp->st_stateid.si_generation) { | 3363 | if (ret) |
3358 | ret = nfserr_old_stateid; | 3364 | goto out; |
3359 | goto out; | ||
3360 | } | ||
3361 | if (stateid->si_generation > stp->st_stateid.si_generation) { | ||
3362 | ret = nfserr_bad_stateid; | ||
3363 | goto out; | ||
3364 | } | ||
3365 | } | ||
3366 | 3365 | ||
3367 | if (stp->st_type == NFS4_OPEN_STID) { | 3366 | if (stp->st_type == NFS4_OPEN_STID) { |
3368 | ret = nfserr_locks_held; | 3367 | ret = nfserr_locks_held; |
@@ -3439,11 +3438,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, | |||
3439 | return nfserr_bad_stateid; | 3438 | return nfserr_bad_stateid; |
3440 | } | 3439 | } |
3441 | 3440 | ||
3442 | /* | ||
3443 | * We now validate the seqid and stateid generation numbers. | ||
3444 | * For the moment, we ignore the possibility of | ||
3445 | * generation number wraparound. | ||
3446 | */ | ||
3447 | if (!nfsd4_has_session(cstate) && seqid != sop->so_seqid) | 3441 | if (!nfsd4_has_session(cstate) && seqid != sop->so_seqid) |
3448 | goto check_replay; | 3442 | goto check_replay; |
3449 | 3443 | ||
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index a06f55bd38b6..c425717715f6 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -293,6 +293,9 @@ static inline void | |||
293 | update_stateid(stateid_t *stateid) | 293 | update_stateid(stateid_t *stateid) |
294 | { | 294 | { |
295 | stateid->si_generation++; | 295 | stateid->si_generation++; |
296 | /* Wraparound recommendation from 3530bis-13 9.1.3.2: */ | ||
297 | if (stateid->si_generation == 0) | ||
298 | stateid->si_generation = 1; | ||
296 | } | 299 | } |
297 | 300 | ||
298 | /* A reasonable value for REPLAY_ISIZE was estimated as follows: | 301 | /* A reasonable value for REPLAY_ISIZE was estimated as follows: |