Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfsd: split transport vs operation errors for callbacks

We must only increment the sequence id if the client has seen and responded
to a request. If we failed to deliver it to the client we must resend with
the same sequence id. So just like the client track errors at the transport
level differently from those returned in the XDR.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Christoph Hellwig and committed by
J. Bruce Fields
ef2a1b3e 9507271d

+25 -36
+24 -36
fs/nfsd/nfs4callback.c
··· 224 224 } 225 225 226 226 static int decode_cb_op_status(struct xdr_stream *xdr, enum nfs_opnum4 expected, 227 - enum nfsstat4 *status) 227 + int *status) 228 228 { 229 229 __be32 *p; 230 230 u32 op; ··· 235 235 op = be32_to_cpup(p++); 236 236 if (unlikely(op != expected)) 237 237 goto out_unexpected; 238 - *status = be32_to_cpup(p); 238 + *status = nfs_cb_stat_to_errno(be32_to_cpup(p)); 239 239 return 0; 240 240 out_overflow: 241 241 print_overflow_msg(__func__, xdr); ··· 446 446 static int decode_cb_sequence4res(struct xdr_stream *xdr, 447 447 struct nfsd4_callback *cb) 448 448 { 449 - enum nfsstat4 nfserr; 450 449 int status; 451 450 452 451 if (cb->cb_minorversion == 0) 453 452 return 0; 454 453 455 - status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &nfserr); 456 - if (unlikely(status)) 457 - goto out; 458 - if (unlikely(nfserr != NFS4_OK)) 459 - goto out_default; 460 - status = decode_cb_sequence4resok(xdr, cb); 461 - out: 462 - return status; 463 - out_default: 464 - return nfs_cb_stat_to_errno(nfserr); 454 + status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &cb->cb_status); 455 + if (unlikely(status || cb->cb_status)) 456 + return status; 457 + 458 + return decode_cb_sequence4resok(xdr, cb); 465 459 } 466 460 467 461 /* ··· 518 524 struct nfsd4_callback *cb) 519 525 { 520 526 struct nfs4_cb_compound_hdr hdr; 521 - enum nfsstat4 nfserr; 522 527 int status; 523 528 524 529 status = decode_cb_compound4res(xdr, &hdr); 525 530 if (unlikely(status)) 526 - goto out; 531 + return status; 527 532 528 533 if (cb != NULL) { 529 534 status = decode_cb_sequence4res(xdr, cb); 530 - if (unlikely(status)) 531 - goto out; 535 + if (unlikely(status || cb->cb_status)) 536 + return status; 532 537 } 533 538 534 - status = decode_cb_op_status(xdr, OP_CB_RECALL, &nfserr); 535 - if (unlikely(status)) 536 - goto out; 537 - if (unlikely(nfserr != NFS4_OK)) 538 - status = nfs_cb_stat_to_errno(nfserr); 539 - out: 540 - return status; 539 + return decode_cb_op_status(xdr, OP_CB_RECALL, &cb->cb_status); 541 540 } 542 541 543 542 #ifdef CONFIG_NFSD_PNFS ··· 608 621 struct nfsd4_callback *cb) 609 622 { 610 623 struct nfs4_cb_compound_hdr hdr; 611 - enum nfsstat4 nfserr; 612 624 int status; 613 625 614 626 status = decode_cb_compound4res(xdr, &hdr); 615 627 if (unlikely(status)) 616 - goto out; 628 + return status; 629 + 617 630 if (cb) { 618 631 status = decode_cb_sequence4res(xdr, cb); 619 - if (unlikely(status)) 620 - goto out; 632 + if (unlikely(status || cb->cb_status)) 633 + return status; 621 634 } 622 - status = decode_cb_op_status(xdr, OP_CB_LAYOUTRECALL, &nfserr); 623 - if (unlikely(status)) 624 - goto out; 625 - if (unlikely(nfserr != NFS4_OK)) 626 - status = nfs_cb_stat_to_errno(nfserr); 627 - out: 628 - return status; 635 + return decode_cb_op_status(xdr, OP_CB_LAYOUTRECALL, &cb->cb_status); 629 636 } 630 637 #endif /* CONFIG_NFSD_PNFS */ 631 638 ··· 899 918 900 919 if (clp->cl_minorversion) { 901 920 /* No need for lock, access serialized in nfsd4_cb_prepare */ 902 - ++clp->cl_cb_session->se_cb_seq_nr; 921 + if (!task->tk_status) 922 + ++clp->cl_cb_session->se_cb_seq_nr; 903 923 clear_bit(0, &clp->cl_cb_slot_busy); 904 924 rpc_wake_up_next(&clp->cl_cb_waitq); 905 925 dprintk("%s: freed slot, new seqid=%d\n", __func__, ··· 916 934 917 935 if (cb->cb_done) 918 936 return; 937 + 938 + if (cb->cb_status) { 939 + WARN_ON_ONCE(task->tk_status); 940 + task->tk_status = cb->cb_status; 941 + } 919 942 920 943 switch (cb->cb_ops->done(cb, task)) { 921 944 case 0: ··· 1086 1099 cb->cb_ops = ops; 1087 1100 INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); 1088 1101 INIT_LIST_HEAD(&cb->cb_per_client); 1102 + cb->cb_status = 0; 1089 1103 cb->cb_done = true; 1090 1104 } 1091 1105
+1
fs/nfsd/state.h
··· 68 68 struct rpc_message cb_msg; 69 69 struct nfsd4_callback_ops *cb_ops; 70 70 struct work_struct cb_work; 71 + int cb_status; 71 72 bool cb_done; 72 73 }; 73 74