summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2017-10-18 16:17:18 -0400
committerJ. Bruce Fields <bfields@redhat.com>2017-11-07 16:43:57 -0500
commit085def3ade52f2ffe3e31f42e98c27dcc222dd37 (patch)
tree6654e3c086ce7b0b2cc7ab33ee936252cc1e4f4d /fs/nfsd
parentda36e6dbf478cb67a5df0da6afcd78df226e4c64 (diff)
nfsd4: fix cached replies to solo SEQUENCE compounds
Currently our handling of 4.1+ requests without "cachethis" set is confusing and not quite correct. Suppose a client sends a compound consisting of only a single SEQUENCE op, and it matches the seqid in a session slot (so it's a retry), but the previous request with that seqid did not have "cachethis" set. The obvious thing to do might be to return NFS4ERR_RETRY_UNCACHED_REP, but the protocol only allows that to be returned on the op following the SEQUENCE, and there is no such op in this case. The protocol permits us to cache replies even if the client didn't ask us to. And it's easy to do so in the case of solo SEQUENCE compounds. So, when we get a solo SEQUENCE, we can either return the previously cached reply or NFSERR_SEQ_FALSE_RETRY if we notice it differs in some way from the original call. Currently, we're returning a corrupt reply in the case a solo SEQUENCE matches a previous compound with more ops. This actually matters because the Linux client recently started doing this as a way to recover from lost replies to idempotent operations in the case the process doing the original reply was killed: in that case it's difficult to keep the original arguments around to do a real retry, and the client no longer cares what the result is anyway, but it would like to make sure that the slot's sequence id has been incremented, and the solo SEQUENCE assures that: if the server never got the original reply, it will increment the sequence id. If it did get the original reply, it won't increment, and nothing else that about the reply really matters much. But we can at least attempt to return valid xdr! Tested-by: Olga Kornievskaia <aglo@umich.edu> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4state.c20
-rw-r--r--fs/nfsd/state.h1
-rw-r--r--fs/nfsd/xdr4.h13
3 files changed, 27 insertions, 7 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9db8a19cceaa..3e96e02496bd 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2292,14 +2292,16 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
2292 2292
2293 dprintk("--> %s slot %p\n", __func__, slot); 2293 dprintk("--> %s slot %p\n", __func__, slot);
2294 2294
2295 slot->sl_flags |= NFSD4_SLOT_INITIALIZED;
2295 slot->sl_opcnt = resp->opcnt; 2296 slot->sl_opcnt = resp->opcnt;
2296 slot->sl_status = resp->cstate.status; 2297 slot->sl_status = resp->cstate.status;
2297 2298
2298 slot->sl_flags |= NFSD4_SLOT_INITIALIZED; 2299 if (!nfsd4_cache_this(resp)) {
2299 if (nfsd4_not_cached(resp)) { 2300 slot->sl_flags &= ~NFSD4_SLOT_CACHED;
2300 slot->sl_datalen = 0;
2301 return; 2301 return;
2302 } 2302 }
2303 slot->sl_flags |= NFSD4_SLOT_CACHED;
2304
2303 base = resp->cstate.data_offset; 2305 base = resp->cstate.data_offset;
2304 slot->sl_datalen = buf->len - base; 2306 slot->sl_datalen = buf->len - base;
2305 if (read_bytes_from_xdr_buf(buf, base, slot->sl_data, slot->sl_datalen)) 2307 if (read_bytes_from_xdr_buf(buf, base, slot->sl_data, slot->sl_datalen))
@@ -2326,8 +2328,16 @@ nfsd4_enc_sequence_replay(struct nfsd4_compoundargs *args,
2326 op = &args->ops[resp->opcnt - 1]; 2328 op = &args->ops[resp->opcnt - 1];
2327 nfsd4_encode_operation(resp, op); 2329 nfsd4_encode_operation(resp, op);
2328 2330
2329 /* Return nfserr_retry_uncached_rep in next operation. */ 2331 if (slot->sl_flags & NFSD4_SLOT_CACHED)
2330 if (args->opcnt > 1 && !(slot->sl_flags & NFSD4_SLOT_CACHETHIS)) { 2332 return op->status;
2333 if (args->opcnt == 1) {
2334 /*
2335 * The original operation wasn't a solo sequence--we
2336 * always cache those--so this retry must not match the
2337 * original:
2338 */
2339 op->status = nfserr_seq_false_retry;
2340 } else {
2331 op = &args->ops[resp->opcnt++]; 2341 op = &args->ops[resp->opcnt++];
2332 op->status = nfserr_retry_uncached_rep; 2342 op->status = nfserr_retry_uncached_rep;
2333 nfsd4_encode_operation(resp, op); 2343 nfsd4_encode_operation(resp, op);
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 005c911b34ac..2488b7df1b35 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -174,6 +174,7 @@ struct nfsd4_slot {
174#define NFSD4_SLOT_INUSE (1 << 0) 174#define NFSD4_SLOT_INUSE (1 << 0)
175#define NFSD4_SLOT_CACHETHIS (1 << 1) 175#define NFSD4_SLOT_CACHETHIS (1 << 1)
176#define NFSD4_SLOT_INITIALIZED (1 << 2) 176#define NFSD4_SLOT_INITIALIZED (1 << 2)
177#define NFSD4_SLOT_CACHED (1 << 3)
177 u8 sl_flags; 178 u8 sl_flags;
178 char sl_data[]; 179 char sl_data[];
179}; 180};
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 1e4edbf70052..bc29511b6405 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -649,9 +649,18 @@ static inline bool nfsd4_is_solo_sequence(struct nfsd4_compoundres *resp)
649 return resp->opcnt == 1 && args->ops[0].opnum == OP_SEQUENCE; 649 return resp->opcnt == 1 && args->ops[0].opnum == OP_SEQUENCE;
650} 650}
651 651
652static inline bool nfsd4_not_cached(struct nfsd4_compoundres *resp) 652/*
653 * The session reply cache only needs to cache replies that the client
654 * actually asked us to. But it's almost free for us to cache compounds
655 * consisting of only a SEQUENCE op, so we may as well cache those too.
656 * Also, the protocol doesn't give us a convenient response in the case
657 * of a replay of a solo SEQUENCE op that wasn't cached
658 * (RETRY_UNCACHED_REP can only be returned in the second op of a
659 * compound).
660 */
661static inline bool nfsd4_cache_this(struct nfsd4_compoundres *resp)
653{ 662{
654 return !(resp->cstate.slot->sl_flags & NFSD4_SLOT_CACHETHIS) 663 return (resp->cstate.slot->sl_flags & NFSD4_SLOT_CACHETHIS)
655 || nfsd4_is_solo_sequence(resp); 664 || nfsd4_is_solo_sequence(resp);
656} 665}
657 666