diff options
Diffstat (limited to 'fs/nfsd/nfs4callback.c')
-rw-r--r-- | fs/nfsd/nfs4callback.c | 119 |
1 files changed, 55 insertions, 64 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 58277859a467..5694cfb7a47b 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -224,7 +224,7 @@ static int nfs_cb_stat_to_errno(int status) | |||
224 | } | 224 | } |
225 | 225 | ||
226 | static int decode_cb_op_status(struct xdr_stream *xdr, enum nfs_opnum4 expected, | 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 | __be32 *p; | 229 | __be32 *p; |
230 | u32 op; | 230 | u32 op; |
@@ -235,7 +235,7 @@ static int decode_cb_op_status(struct xdr_stream *xdr, enum nfs_opnum4 expected, | |||
235 | op = be32_to_cpup(p++); | 235 | op = be32_to_cpup(p++); |
236 | if (unlikely(op != expected)) | 236 | if (unlikely(op != expected)) |
237 | goto out_unexpected; | 237 | goto out_unexpected; |
238 | *status = be32_to_cpup(p); | 238 | *status = nfs_cb_stat_to_errno(be32_to_cpup(p)); |
239 | return 0; | 239 | return 0; |
240 | out_overflow: | 240 | out_overflow: |
241 | print_overflow_msg(__func__, xdr); | 241 | print_overflow_msg(__func__, xdr); |
@@ -446,22 +446,16 @@ out_overflow: | |||
446 | static int decode_cb_sequence4res(struct xdr_stream *xdr, | 446 | static int decode_cb_sequence4res(struct xdr_stream *xdr, |
447 | struct nfsd4_callback *cb) | 447 | struct nfsd4_callback *cb) |
448 | { | 448 | { |
449 | enum nfsstat4 nfserr; | ||
450 | int status; | 449 | int status; |
451 | 450 | ||
452 | if (cb->cb_minorversion == 0) | 451 | if (cb->cb_minorversion == 0) |
453 | return 0; | 452 | return 0; |
454 | 453 | ||
455 | status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &nfserr); | 454 | status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &cb->cb_status); |
456 | if (unlikely(status)) | 455 | if (unlikely(status || cb->cb_status)) |
457 | goto out; | 456 | return status; |
458 | if (unlikely(nfserr != NFS4_OK)) | 457 | |
459 | goto out_default; | 458 | return decode_cb_sequence4resok(xdr, cb); |
460 | status = decode_cb_sequence4resok(xdr, cb); | ||
461 | out: | ||
462 | return status; | ||
463 | out_default: | ||
464 | return nfs_cb_stat_to_errno(nfserr); | ||
465 | } | 459 | } |
466 | 460 | ||
467 | /* | 461 | /* |
@@ -524,26 +518,19 @@ static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, | |||
524 | struct nfsd4_callback *cb) | 518 | struct nfsd4_callback *cb) |
525 | { | 519 | { |
526 | struct nfs4_cb_compound_hdr hdr; | 520 | struct nfs4_cb_compound_hdr hdr; |
527 | enum nfsstat4 nfserr; | ||
528 | int status; | 521 | int status; |
529 | 522 | ||
530 | status = decode_cb_compound4res(xdr, &hdr); | 523 | status = decode_cb_compound4res(xdr, &hdr); |
531 | if (unlikely(status)) | 524 | if (unlikely(status)) |
532 | goto out; | 525 | return status; |
533 | 526 | ||
534 | if (cb != NULL) { | 527 | if (cb != NULL) { |
535 | status = decode_cb_sequence4res(xdr, cb); | 528 | status = decode_cb_sequence4res(xdr, cb); |
536 | if (unlikely(status)) | 529 | if (unlikely(status || cb->cb_status)) |
537 | goto out; | 530 | return status; |
538 | } | 531 | } |
539 | 532 | ||
540 | status = decode_cb_op_status(xdr, OP_CB_RECALL, &nfserr); | 533 | return decode_cb_op_status(xdr, OP_CB_RECALL, &cb->cb_status); |
541 | if (unlikely(status)) | ||
542 | goto out; | ||
543 | if (unlikely(nfserr != NFS4_OK)) | ||
544 | status = nfs_cb_stat_to_errno(nfserr); | ||
545 | out: | ||
546 | return status; | ||
547 | } | 534 | } |
548 | 535 | ||
549 | #ifdef CONFIG_NFSD_PNFS | 536 | #ifdef CONFIG_NFSD_PNFS |
@@ -621,24 +608,18 @@ static int nfs4_xdr_dec_cb_layout(struct rpc_rqst *rqstp, | |||
621 | struct nfsd4_callback *cb) | 608 | struct nfsd4_callback *cb) |
622 | { | 609 | { |
623 | struct nfs4_cb_compound_hdr hdr; | 610 | struct nfs4_cb_compound_hdr hdr; |
624 | enum nfsstat4 nfserr; | ||
625 | int status; | 611 | int status; |
626 | 612 | ||
627 | status = decode_cb_compound4res(xdr, &hdr); | 613 | status = decode_cb_compound4res(xdr, &hdr); |
628 | if (unlikely(status)) | 614 | if (unlikely(status)) |
629 | goto out; | 615 | return status; |
616 | |||
630 | if (cb) { | 617 | if (cb) { |
631 | status = decode_cb_sequence4res(xdr, cb); | 618 | status = decode_cb_sequence4res(xdr, cb); |
632 | if (unlikely(status)) | 619 | if (unlikely(status || cb->cb_status)) |
633 | goto out; | 620 | return status; |
634 | } | 621 | } |
635 | status = decode_cb_op_status(xdr, OP_CB_LAYOUTRECALL, &nfserr); | 622 | return decode_cb_op_status(xdr, OP_CB_LAYOUTRECALL, &cb->cb_status); |
636 | if (unlikely(status)) | ||
637 | goto out; | ||
638 | if (unlikely(nfserr != NFS4_OK)) | ||
639 | status = nfs_cb_stat_to_errno(nfserr); | ||
640 | out: | ||
641 | return status; | ||
642 | } | 623 | } |
643 | #endif /* CONFIG_NFSD_PNFS */ | 624 | #endif /* CONFIG_NFSD_PNFS */ |
644 | 625 | ||
@@ -898,13 +879,6 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) | |||
898 | if (!nfsd41_cb_get_slot(clp, task)) | 879 | if (!nfsd41_cb_get_slot(clp, task)) |
899 | return; | 880 | return; |
900 | } | 881 | } |
901 | spin_lock(&clp->cl_lock); | ||
902 | if (list_empty(&cb->cb_per_client)) { | ||
903 | /* This is the first call, not a restart */ | ||
904 | cb->cb_done = false; | ||
905 | list_add(&cb->cb_per_client, &clp->cl_callbacks); | ||
906 | } | ||
907 | spin_unlock(&clp->cl_lock); | ||
908 | rpc_call_start(task); | 882 | rpc_call_start(task); |
909 | } | 883 | } |
910 | 884 | ||
@@ -918,22 +892,33 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) | |||
918 | 892 | ||
919 | if (clp->cl_minorversion) { | 893 | if (clp->cl_minorversion) { |
920 | /* No need for lock, access serialized in nfsd4_cb_prepare */ | 894 | /* No need for lock, access serialized in nfsd4_cb_prepare */ |
921 | ++clp->cl_cb_session->se_cb_seq_nr; | 895 | if (!task->tk_status) |
896 | ++clp->cl_cb_session->se_cb_seq_nr; | ||
922 | clear_bit(0, &clp->cl_cb_slot_busy); | 897 | clear_bit(0, &clp->cl_cb_slot_busy); |
923 | rpc_wake_up_next(&clp->cl_cb_waitq); | 898 | rpc_wake_up_next(&clp->cl_cb_waitq); |
924 | dprintk("%s: freed slot, new seqid=%d\n", __func__, | 899 | dprintk("%s: freed slot, new seqid=%d\n", __func__, |
925 | clp->cl_cb_session->se_cb_seq_nr); | 900 | clp->cl_cb_session->se_cb_seq_nr); |
926 | } | 901 | } |
927 | 902 | ||
928 | if (clp->cl_cb_client != task->tk_client) { | 903 | /* |
929 | /* We're shutting down or changing cl_cb_client; leave | 904 | * If the backchannel connection was shut down while this |
930 | * it to nfsd4_process_cb_update to restart the call if | 905 | * task was queued, we need to resubmit it after setting up |
931 | * necessary. */ | 906 | * a new backchannel connection. |
907 | * | ||
908 | * Note that if we lost our callback connection permanently | ||
909 | * the submission code will error out, so we don't need to | ||
910 | * handle that case here. | ||
911 | */ | ||
912 | if (task->tk_flags & RPC_TASK_KILLED) { | ||
913 | task->tk_status = 0; | ||
914 | cb->cb_need_restart = true; | ||
932 | return; | 915 | return; |
933 | } | 916 | } |
934 | 917 | ||
935 | if (cb->cb_done) | 918 | if (cb->cb_status) { |
936 | return; | 919 | WARN_ON_ONCE(task->tk_status); |
920 | task->tk_status = cb->cb_status; | ||
921 | } | ||
937 | 922 | ||
938 | switch (cb->cb_ops->done(cb, task)) { | 923 | switch (cb->cb_ops->done(cb, task)) { |
939 | case 0: | 924 | case 0: |
@@ -949,21 +934,17 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) | |||
949 | default: | 934 | default: |
950 | BUG(); | 935 | BUG(); |
951 | } | 936 | } |
952 | cb->cb_done = true; | ||
953 | } | 937 | } |
954 | 938 | ||
955 | static void nfsd4_cb_release(void *calldata) | 939 | static void nfsd4_cb_release(void *calldata) |
956 | { | 940 | { |
957 | struct nfsd4_callback *cb = calldata; | 941 | struct nfsd4_callback *cb = calldata; |
958 | struct nfs4_client *clp = cb->cb_clp; | ||
959 | |||
960 | if (cb->cb_done) { | ||
961 | spin_lock(&clp->cl_lock); | ||
962 | list_del(&cb->cb_per_client); | ||
963 | spin_unlock(&clp->cl_lock); | ||
964 | 942 | ||
943 | if (cb->cb_need_restart) | ||
944 | nfsd4_run_cb(cb); | ||
945 | else | ||
965 | cb->cb_ops->release(cb); | 946 | cb->cb_ops->release(cb); |
966 | } | 947 | |
967 | } | 948 | } |
968 | 949 | ||
969 | static const struct rpc_call_ops nfsd4_cb_ops = { | 950 | static const struct rpc_call_ops nfsd4_cb_ops = { |
@@ -1058,9 +1039,6 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
1058 | nfsd4_mark_cb_down(clp, err); | 1039 | nfsd4_mark_cb_down(clp, err); |
1059 | return; | 1040 | return; |
1060 | } | 1041 | } |
1061 | /* Yay, the callback channel's back! Restart any callbacks: */ | ||
1062 | list_for_each_entry(cb, &clp->cl_callbacks, cb_per_client) | ||
1063 | queue_work(callback_wq, &cb->cb_work); | ||
1064 | } | 1042 | } |
1065 | 1043 | ||
1066 | static void | 1044 | static void |
@@ -1071,8 +1049,12 @@ nfsd4_run_cb_work(struct work_struct *work) | |||
1071 | struct nfs4_client *clp = cb->cb_clp; | 1049 | struct nfs4_client *clp = cb->cb_clp; |
1072 | struct rpc_clnt *clnt; | 1050 | struct rpc_clnt *clnt; |
1073 | 1051 | ||
1074 | if (cb->cb_ops && cb->cb_ops->prepare) | 1052 | if (cb->cb_need_restart) { |
1075 | cb->cb_ops->prepare(cb); | 1053 | cb->cb_need_restart = false; |
1054 | } else { | ||
1055 | if (cb->cb_ops && cb->cb_ops->prepare) | ||
1056 | cb->cb_ops->prepare(cb); | ||
1057 | } | ||
1076 | 1058 | ||
1077 | if (clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK) | 1059 | if (clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK) |
1078 | nfsd4_process_cb_update(cb); | 1060 | nfsd4_process_cb_update(cb); |
@@ -1084,6 +1066,15 @@ nfsd4_run_cb_work(struct work_struct *work) | |||
1084 | cb->cb_ops->release(cb); | 1066 | cb->cb_ops->release(cb); |
1085 | return; | 1067 | return; |
1086 | } | 1068 | } |
1069 | |||
1070 | /* | ||
1071 | * Don't send probe messages for 4.1 or later. | ||
1072 | */ | ||
1073 | if (!cb->cb_ops && clp->cl_minorversion) { | ||
1074 | clp->cl_cb_state = NFSD4_CB_UP; | ||
1075 | return; | ||
1076 | } | ||
1077 | |||
1087 | cb->cb_msg.rpc_cred = clp->cl_cb_cred; | 1078 | cb->cb_msg.rpc_cred = clp->cl_cb_cred; |
1088 | rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, | 1079 | rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, |
1089 | cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb); | 1080 | cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb); |
@@ -1098,8 +1089,8 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, | |||
1098 | cb->cb_msg.rpc_resp = cb; | 1089 | cb->cb_msg.rpc_resp = cb; |
1099 | cb->cb_ops = ops; | 1090 | cb->cb_ops = ops; |
1100 | INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); | 1091 | INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); |
1101 | INIT_LIST_HEAD(&cb->cb_per_client); | 1092 | cb->cb_status = 0; |
1102 | cb->cb_done = true; | 1093 | cb->cb_need_restart = false; |
1103 | } | 1094 | } |
1104 | 1095 | ||
1105 | void nfsd4_run_cb(struct nfsd4_callback *cb) | 1096 | void nfsd4_run_cb(struct nfsd4_callback *cb) |