diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-05-11 17:42:52 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-05-11 17:42:52 -0400 |
commit | 4cfceaf0c087f47033f5e61a801f4136d6fb68c6 (patch) | |
tree | 59e15849506b505daf2298ed333fd04cda38e22c | |
parent | ef208162b7b4c40de1825a61677e167c081f5fee (diff) | |
parent | 4bd9e9b77fc6787c45b8bb439f6511aa3478606c (diff) |
Merge branch 'for-4.1' of git://linux-nfs.org/~bfields/linux
Pull nfsd bugfixes from Bruce Fields:
"Mainly pnfs fixes (and for problems with generic callback code made
more obvious by pnfs)"
* 'for-4.1' of git://linux-nfs.org/~bfields/linux:
nfsd: skip CB_NULL probes for 4.1 or later
nfsd: fix callback restarts
nfsd: split transport vs operation errors for callbacks
svcrpc: fix potential GSSX_ACCEPT_SEC_CONTEXT decoding failures
nfsd: fix pNFS return on close semantics
nfsd: fix the check for confirmed openowner in nfs4_preprocess_stateid_op
nfsd/blocklayout: pretend we can send deviceid notifications
-rw-r--r-- | fs/nfsd/blocklayout.c | 11 | ||||
-rw-r--r-- | fs/nfsd/nfs4callback.c | 119 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 147 | ||||
-rw-r--r-- | fs/nfsd/state.h | 19 | ||||
-rw-r--r-- | fs/nfsd/xdr4.h | 1 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_rpc_xdr.c | 23 |
6 files changed, 228 insertions, 92 deletions
diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c index 03d647bf195d..cdefaa331a07 100644 --- a/fs/nfsd/blocklayout.c +++ b/fs/nfsd/blocklayout.c | |||
@@ -181,6 +181,17 @@ nfsd4_block_proc_layoutcommit(struct inode *inode, | |||
181 | } | 181 | } |
182 | 182 | ||
183 | const struct nfsd4_layout_ops bl_layout_ops = { | 183 | const struct nfsd4_layout_ops bl_layout_ops = { |
184 | /* | ||
185 | * Pretend that we send notification to the client. This is a blatant | ||
186 | * lie to force recent Linux clients to cache our device IDs. | ||
187 | * We rarely ever change the device ID, so the harm of leaking deviceids | ||
188 | * for a while isn't too bad. Unfortunately RFC5661 is a complete mess | ||
189 | * in this regard, but I filed errata 4119 for this a while ago, and | ||
190 | * hopefully the Linux client will eventually start caching deviceids | ||
191 | * without this again. | ||
192 | */ | ||
193 | .notify_types = | ||
194 | NOTIFY_DEVICEID4_DELETE | NOTIFY_DEVICEID4_CHANGE, | ||
184 | .proc_getdeviceinfo = nfsd4_block_proc_getdeviceinfo, | 195 | .proc_getdeviceinfo = nfsd4_block_proc_getdeviceinfo, |
185 | .encode_getdeviceinfo = nfsd4_block_encode_getdeviceinfo, | 196 | .encode_getdeviceinfo = nfsd4_block_encode_getdeviceinfo, |
186 | .proc_layoutget = nfsd4_block_proc_layoutget, | 197 | .proc_layoutget = nfsd4_block_proc_layoutget, |
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) |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 38f2d7abe3a7..039f9c8a95e8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -94,6 +94,7 @@ static struct kmem_cache *lockowner_slab; | |||
94 | static struct kmem_cache *file_slab; | 94 | static struct kmem_cache *file_slab; |
95 | static struct kmem_cache *stateid_slab; | 95 | static struct kmem_cache *stateid_slab; |
96 | static struct kmem_cache *deleg_slab; | 96 | static struct kmem_cache *deleg_slab; |
97 | static struct kmem_cache *odstate_slab; | ||
97 | 98 | ||
98 | static void free_session(struct nfsd4_session *); | 99 | static void free_session(struct nfsd4_session *); |
99 | 100 | ||
@@ -281,6 +282,7 @@ put_nfs4_file(struct nfs4_file *fi) | |||
281 | if (atomic_dec_and_lock(&fi->fi_ref, &state_lock)) { | 282 | if (atomic_dec_and_lock(&fi->fi_ref, &state_lock)) { |
282 | hlist_del_rcu(&fi->fi_hash); | 283 | hlist_del_rcu(&fi->fi_hash); |
283 | spin_unlock(&state_lock); | 284 | spin_unlock(&state_lock); |
285 | WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate)); | ||
284 | WARN_ON_ONCE(!list_empty(&fi->fi_delegations)); | 286 | WARN_ON_ONCE(!list_empty(&fi->fi_delegations)); |
285 | call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu); | 287 | call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu); |
286 | } | 288 | } |
@@ -471,6 +473,86 @@ static void nfs4_file_put_access(struct nfs4_file *fp, u32 access) | |||
471 | __nfs4_file_put_access(fp, O_RDONLY); | 473 | __nfs4_file_put_access(fp, O_RDONLY); |
472 | } | 474 | } |
473 | 475 | ||
476 | /* | ||
477 | * Allocate a new open/delegation state counter. This is needed for | ||
478 | * pNFS for proper return on close semantics. | ||
479 | * | ||
480 | * Note that we only allocate it for pNFS-enabled exports, otherwise | ||
481 | * all pointers to struct nfs4_clnt_odstate are always NULL. | ||
482 | */ | ||
483 | static struct nfs4_clnt_odstate * | ||
484 | alloc_clnt_odstate(struct nfs4_client *clp) | ||
485 | { | ||
486 | struct nfs4_clnt_odstate *co; | ||
487 | |||
488 | co = kmem_cache_zalloc(odstate_slab, GFP_KERNEL); | ||
489 | if (co) { | ||
490 | co->co_client = clp; | ||
491 | atomic_set(&co->co_odcount, 1); | ||
492 | } | ||
493 | return co; | ||
494 | } | ||
495 | |||
496 | static void | ||
497 | hash_clnt_odstate_locked(struct nfs4_clnt_odstate *co) | ||
498 | { | ||
499 | struct nfs4_file *fp = co->co_file; | ||
500 | |||
501 | lockdep_assert_held(&fp->fi_lock); | ||
502 | list_add(&co->co_perfile, &fp->fi_clnt_odstate); | ||
503 | } | ||
504 | |||
505 | static inline void | ||
506 | get_clnt_odstate(struct nfs4_clnt_odstate *co) | ||
507 | { | ||
508 | if (co) | ||
509 | atomic_inc(&co->co_odcount); | ||
510 | } | ||
511 | |||
512 | static void | ||
513 | put_clnt_odstate(struct nfs4_clnt_odstate *co) | ||
514 | { | ||
515 | struct nfs4_file *fp; | ||
516 | |||
517 | if (!co) | ||
518 | return; | ||
519 | |||
520 | fp = co->co_file; | ||
521 | if (atomic_dec_and_lock(&co->co_odcount, &fp->fi_lock)) { | ||
522 | list_del(&co->co_perfile); | ||
523 | spin_unlock(&fp->fi_lock); | ||
524 | |||
525 | nfsd4_return_all_file_layouts(co->co_client, fp); | ||
526 | kmem_cache_free(odstate_slab, co); | ||
527 | } | ||
528 | } | ||
529 | |||
530 | static struct nfs4_clnt_odstate * | ||
531 | find_or_hash_clnt_odstate(struct nfs4_file *fp, struct nfs4_clnt_odstate *new) | ||
532 | { | ||
533 | struct nfs4_clnt_odstate *co; | ||
534 | struct nfs4_client *cl; | ||
535 | |||
536 | if (!new) | ||
537 | return NULL; | ||
538 | |||
539 | cl = new->co_client; | ||
540 | |||
541 | spin_lock(&fp->fi_lock); | ||
542 | list_for_each_entry(co, &fp->fi_clnt_odstate, co_perfile) { | ||
543 | if (co->co_client == cl) { | ||
544 | get_clnt_odstate(co); | ||
545 | goto out; | ||
546 | } | ||
547 | } | ||
548 | co = new; | ||
549 | co->co_file = fp; | ||
550 | hash_clnt_odstate_locked(new); | ||
551 | out: | ||
552 | spin_unlock(&fp->fi_lock); | ||
553 | return co; | ||
554 | } | ||
555 | |||
474 | struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, | 556 | struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, |
475 | struct kmem_cache *slab) | 557 | struct kmem_cache *slab) |
476 | { | 558 | { |
@@ -606,7 +688,8 @@ static void block_delegations(struct knfsd_fh *fh) | |||
606 | } | 688 | } |
607 | 689 | ||
608 | static struct nfs4_delegation * | 690 | static struct nfs4_delegation * |
609 | alloc_init_deleg(struct nfs4_client *clp, struct svc_fh *current_fh) | 691 | alloc_init_deleg(struct nfs4_client *clp, struct svc_fh *current_fh, |
692 | struct nfs4_clnt_odstate *odstate) | ||
610 | { | 693 | { |
611 | struct nfs4_delegation *dp; | 694 | struct nfs4_delegation *dp; |
612 | long n; | 695 | long n; |
@@ -631,6 +714,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct svc_fh *current_fh) | |||
631 | INIT_LIST_HEAD(&dp->dl_perfile); | 714 | INIT_LIST_HEAD(&dp->dl_perfile); |
632 | INIT_LIST_HEAD(&dp->dl_perclnt); | 715 | INIT_LIST_HEAD(&dp->dl_perclnt); |
633 | INIT_LIST_HEAD(&dp->dl_recall_lru); | 716 | INIT_LIST_HEAD(&dp->dl_recall_lru); |
717 | dp->dl_clnt_odstate = odstate; | ||
718 | get_clnt_odstate(odstate); | ||
634 | dp->dl_type = NFS4_OPEN_DELEGATE_READ; | 719 | dp->dl_type = NFS4_OPEN_DELEGATE_READ; |
635 | dp->dl_retries = 1; | 720 | dp->dl_retries = 1; |
636 | nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client, | 721 | nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client, |
@@ -714,6 +799,7 @@ static void destroy_delegation(struct nfs4_delegation *dp) | |||
714 | spin_lock(&state_lock); | 799 | spin_lock(&state_lock); |
715 | unhash_delegation_locked(dp); | 800 | unhash_delegation_locked(dp); |
716 | spin_unlock(&state_lock); | 801 | spin_unlock(&state_lock); |
802 | put_clnt_odstate(dp->dl_clnt_odstate); | ||
717 | nfs4_put_deleg_lease(dp->dl_stid.sc_file); | 803 | nfs4_put_deleg_lease(dp->dl_stid.sc_file); |
718 | nfs4_put_stid(&dp->dl_stid); | 804 | nfs4_put_stid(&dp->dl_stid); |
719 | } | 805 | } |
@@ -724,6 +810,7 @@ static void revoke_delegation(struct nfs4_delegation *dp) | |||
724 | 810 | ||
725 | WARN_ON(!list_empty(&dp->dl_recall_lru)); | 811 | WARN_ON(!list_empty(&dp->dl_recall_lru)); |
726 | 812 | ||
813 | put_clnt_odstate(dp->dl_clnt_odstate); | ||
727 | nfs4_put_deleg_lease(dp->dl_stid.sc_file); | 814 | nfs4_put_deleg_lease(dp->dl_stid.sc_file); |
728 | 815 | ||
729 | if (clp->cl_minorversion == 0) | 816 | if (clp->cl_minorversion == 0) |
@@ -933,6 +1020,7 @@ static void nfs4_free_ol_stateid(struct nfs4_stid *stid) | |||
933 | { | 1020 | { |
934 | struct nfs4_ol_stateid *stp = openlockstateid(stid); | 1021 | struct nfs4_ol_stateid *stp = openlockstateid(stid); |
935 | 1022 | ||
1023 | put_clnt_odstate(stp->st_clnt_odstate); | ||
936 | release_all_access(stp); | 1024 | release_all_access(stp); |
937 | if (stp->st_stateowner) | 1025 | if (stp->st_stateowner) |
938 | nfs4_put_stateowner(stp->st_stateowner); | 1026 | nfs4_put_stateowner(stp->st_stateowner); |
@@ -1538,7 +1626,6 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) | |||
1538 | INIT_LIST_HEAD(&clp->cl_openowners); | 1626 | INIT_LIST_HEAD(&clp->cl_openowners); |
1539 | INIT_LIST_HEAD(&clp->cl_delegations); | 1627 | INIT_LIST_HEAD(&clp->cl_delegations); |
1540 | INIT_LIST_HEAD(&clp->cl_lru); | 1628 | INIT_LIST_HEAD(&clp->cl_lru); |
1541 | INIT_LIST_HEAD(&clp->cl_callbacks); | ||
1542 | INIT_LIST_HEAD(&clp->cl_revoked); | 1629 | INIT_LIST_HEAD(&clp->cl_revoked); |
1543 | #ifdef CONFIG_NFSD_PNFS | 1630 | #ifdef CONFIG_NFSD_PNFS |
1544 | INIT_LIST_HEAD(&clp->cl_lo_states); | 1631 | INIT_LIST_HEAD(&clp->cl_lo_states); |
@@ -1634,6 +1721,7 @@ __destroy_client(struct nfs4_client *clp) | |||
1634 | while (!list_empty(&reaplist)) { | 1721 | while (!list_empty(&reaplist)) { |
1635 | dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); | 1722 | dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); |
1636 | list_del_init(&dp->dl_recall_lru); | 1723 | list_del_init(&dp->dl_recall_lru); |
1724 | put_clnt_odstate(dp->dl_clnt_odstate); | ||
1637 | nfs4_put_deleg_lease(dp->dl_stid.sc_file); | 1725 | nfs4_put_deleg_lease(dp->dl_stid.sc_file); |
1638 | nfs4_put_stid(&dp->dl_stid); | 1726 | nfs4_put_stid(&dp->dl_stid); |
1639 | } | 1727 | } |
@@ -3057,6 +3145,7 @@ static void nfsd4_init_file(struct knfsd_fh *fh, unsigned int hashval, | |||
3057 | spin_lock_init(&fp->fi_lock); | 3145 | spin_lock_init(&fp->fi_lock); |
3058 | INIT_LIST_HEAD(&fp->fi_stateids); | 3146 | INIT_LIST_HEAD(&fp->fi_stateids); |
3059 | INIT_LIST_HEAD(&fp->fi_delegations); | 3147 | INIT_LIST_HEAD(&fp->fi_delegations); |
3148 | INIT_LIST_HEAD(&fp->fi_clnt_odstate); | ||
3060 | fh_copy_shallow(&fp->fi_fhandle, fh); | 3149 | fh_copy_shallow(&fp->fi_fhandle, fh); |
3061 | fp->fi_deleg_file = NULL; | 3150 | fp->fi_deleg_file = NULL; |
3062 | fp->fi_had_conflict = false; | 3151 | fp->fi_had_conflict = false; |
@@ -3073,6 +3162,7 @@ static void nfsd4_init_file(struct knfsd_fh *fh, unsigned int hashval, | |||
3073 | void | 3162 | void |
3074 | nfsd4_free_slabs(void) | 3163 | nfsd4_free_slabs(void) |
3075 | { | 3164 | { |
3165 | kmem_cache_destroy(odstate_slab); | ||
3076 | kmem_cache_destroy(openowner_slab); | 3166 | kmem_cache_destroy(openowner_slab); |
3077 | kmem_cache_destroy(lockowner_slab); | 3167 | kmem_cache_destroy(lockowner_slab); |
3078 | kmem_cache_destroy(file_slab); | 3168 | kmem_cache_destroy(file_slab); |
@@ -3103,8 +3193,14 @@ nfsd4_init_slabs(void) | |||
3103 | sizeof(struct nfs4_delegation), 0, 0, NULL); | 3193 | sizeof(struct nfs4_delegation), 0, 0, NULL); |
3104 | if (deleg_slab == NULL) | 3194 | if (deleg_slab == NULL) |
3105 | goto out_free_stateid_slab; | 3195 | goto out_free_stateid_slab; |
3196 | odstate_slab = kmem_cache_create("nfsd4_odstate", | ||
3197 | sizeof(struct nfs4_clnt_odstate), 0, 0, NULL); | ||
3198 | if (odstate_slab == NULL) | ||
3199 | goto out_free_deleg_slab; | ||
3106 | return 0; | 3200 | return 0; |
3107 | 3201 | ||
3202 | out_free_deleg_slab: | ||
3203 | kmem_cache_destroy(deleg_slab); | ||
3108 | out_free_stateid_slab: | 3204 | out_free_stateid_slab: |
3109 | kmem_cache_destroy(stateid_slab); | 3205 | kmem_cache_destroy(stateid_slab); |
3110 | out_free_file_slab: | 3206 | out_free_file_slab: |
@@ -3581,6 +3677,14 @@ alloc_stateid: | |||
3581 | open->op_stp = nfs4_alloc_open_stateid(clp); | 3677 | open->op_stp = nfs4_alloc_open_stateid(clp); |
3582 | if (!open->op_stp) | 3678 | if (!open->op_stp) |
3583 | return nfserr_jukebox; | 3679 | return nfserr_jukebox; |
3680 | |||
3681 | if (nfsd4_has_session(cstate) && | ||
3682 | (cstate->current_fh.fh_export->ex_flags & NFSEXP_PNFS)) { | ||
3683 | open->op_odstate = alloc_clnt_odstate(clp); | ||
3684 | if (!open->op_odstate) | ||
3685 | return nfserr_jukebox; | ||
3686 | } | ||
3687 | |||
3584 | return nfs_ok; | 3688 | return nfs_ok; |
3585 | } | 3689 | } |
3586 | 3690 | ||
@@ -3869,7 +3973,7 @@ out_fput: | |||
3869 | 3973 | ||
3870 | static struct nfs4_delegation * | 3974 | static struct nfs4_delegation * |
3871 | nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, | 3975 | nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, |
3872 | struct nfs4_file *fp) | 3976 | struct nfs4_file *fp, struct nfs4_clnt_odstate *odstate) |
3873 | { | 3977 | { |
3874 | int status; | 3978 | int status; |
3875 | struct nfs4_delegation *dp; | 3979 | struct nfs4_delegation *dp; |
@@ -3877,7 +3981,7 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, | |||
3877 | if (fp->fi_had_conflict) | 3981 | if (fp->fi_had_conflict) |
3878 | return ERR_PTR(-EAGAIN); | 3982 | return ERR_PTR(-EAGAIN); |
3879 | 3983 | ||
3880 | dp = alloc_init_deleg(clp, fh); | 3984 | dp = alloc_init_deleg(clp, fh, odstate); |
3881 | if (!dp) | 3985 | if (!dp) |
3882 | return ERR_PTR(-ENOMEM); | 3986 | return ERR_PTR(-ENOMEM); |
3883 | 3987 | ||
@@ -3903,6 +4007,7 @@ out_unlock: | |||
3903 | spin_unlock(&state_lock); | 4007 | spin_unlock(&state_lock); |
3904 | out: | 4008 | out: |
3905 | if (status) { | 4009 | if (status) { |
4010 | put_clnt_odstate(dp->dl_clnt_odstate); | ||
3906 | nfs4_put_stid(&dp->dl_stid); | 4011 | nfs4_put_stid(&dp->dl_stid); |
3907 | return ERR_PTR(status); | 4012 | return ERR_PTR(status); |
3908 | } | 4013 | } |
@@ -3980,7 +4085,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, | |||
3980 | default: | 4085 | default: |
3981 | goto out_no_deleg; | 4086 | goto out_no_deleg; |
3982 | } | 4087 | } |
3983 | dp = nfs4_set_delegation(clp, fh, stp->st_stid.sc_file); | 4088 | dp = nfs4_set_delegation(clp, fh, stp->st_stid.sc_file, stp->st_clnt_odstate); |
3984 | if (IS_ERR(dp)) | 4089 | if (IS_ERR(dp)) |
3985 | goto out_no_deleg; | 4090 | goto out_no_deleg; |
3986 | 4091 | ||
@@ -4069,6 +4174,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
4069 | release_open_stateid(stp); | 4174 | release_open_stateid(stp); |
4070 | goto out; | 4175 | goto out; |
4071 | } | 4176 | } |
4177 | |||
4178 | stp->st_clnt_odstate = find_or_hash_clnt_odstate(fp, | ||
4179 | open->op_odstate); | ||
4180 | if (stp->st_clnt_odstate == open->op_odstate) | ||
4181 | open->op_odstate = NULL; | ||
4072 | } | 4182 | } |
4073 | update_stateid(&stp->st_stid.sc_stateid); | 4183 | update_stateid(&stp->st_stid.sc_stateid); |
4074 | memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); | 4184 | memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); |
@@ -4129,6 +4239,8 @@ void nfsd4_cleanup_open_state(struct nfsd4_compound_state *cstate, | |||
4129 | kmem_cache_free(file_slab, open->op_file); | 4239 | kmem_cache_free(file_slab, open->op_file); |
4130 | if (open->op_stp) | 4240 | if (open->op_stp) |
4131 | nfs4_put_stid(&open->op_stp->st_stid); | 4241 | nfs4_put_stid(&open->op_stp->st_stid); |
4242 | if (open->op_odstate) | ||
4243 | kmem_cache_free(odstate_slab, open->op_odstate); | ||
4132 | } | 4244 | } |
4133 | 4245 | ||
4134 | __be32 | 4246 | __be32 |
@@ -4385,10 +4497,17 @@ static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_s | |||
4385 | return nfserr_old_stateid; | 4497 | return nfserr_old_stateid; |
4386 | } | 4498 | } |
4387 | 4499 | ||
4500 | static __be32 nfsd4_check_openowner_confirmed(struct nfs4_ol_stateid *ols) | ||
4501 | { | ||
4502 | if (ols->st_stateowner->so_is_open_owner && | ||
4503 | !(openowner(ols->st_stateowner)->oo_flags & NFS4_OO_CONFIRMED)) | ||
4504 | return nfserr_bad_stateid; | ||
4505 | return nfs_ok; | ||
4506 | } | ||
4507 | |||
4388 | static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) | 4508 | static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) |
4389 | { | 4509 | { |
4390 | struct nfs4_stid *s; | 4510 | struct nfs4_stid *s; |
4391 | struct nfs4_ol_stateid *ols; | ||
4392 | __be32 status = nfserr_bad_stateid; | 4511 | __be32 status = nfserr_bad_stateid; |
4393 | 4512 | ||
4394 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) | 4513 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) |
@@ -4418,13 +4537,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) | |||
4418 | break; | 4537 | break; |
4419 | case NFS4_OPEN_STID: | 4538 | case NFS4_OPEN_STID: |
4420 | case NFS4_LOCK_STID: | 4539 | case NFS4_LOCK_STID: |
4421 | ols = openlockstateid(s); | 4540 | status = nfsd4_check_openowner_confirmed(openlockstateid(s)); |
4422 | if (ols->st_stateowner->so_is_open_owner | ||
4423 | && !(openowner(ols->st_stateowner)->oo_flags | ||
4424 | & NFS4_OO_CONFIRMED)) | ||
4425 | status = nfserr_bad_stateid; | ||
4426 | else | ||
4427 | status = nfs_ok; | ||
4428 | break; | 4541 | break; |
4429 | default: | 4542 | default: |
4430 | printk("unknown stateid type %x\n", s->sc_type); | 4543 | printk("unknown stateid type %x\n", s->sc_type); |
@@ -4516,8 +4629,8 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, | |||
4516 | status = nfs4_check_fh(current_fh, stp); | 4629 | status = nfs4_check_fh(current_fh, stp); |
4517 | if (status) | 4630 | if (status) |
4518 | goto out; | 4631 | goto out; |
4519 | if (stp->st_stateowner->so_is_open_owner | 4632 | status = nfsd4_check_openowner_confirmed(stp); |
4520 | && !(openowner(stp->st_stateowner)->oo_flags & NFS4_OO_CONFIRMED)) | 4633 | if (status) |
4521 | goto out; | 4634 | goto out; |
4522 | status = nfs4_check_openmode(stp, flags); | 4635 | status = nfs4_check_openmode(stp, flags); |
4523 | if (status) | 4636 | if (status) |
@@ -4852,9 +4965,6 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4852 | update_stateid(&stp->st_stid.sc_stateid); | 4965 | update_stateid(&stp->st_stid.sc_stateid); |
4853 | memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); | 4966 | memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); |
4854 | 4967 | ||
4855 | nfsd4_return_all_file_layouts(stp->st_stateowner->so_client, | ||
4856 | stp->st_stid.sc_file); | ||
4857 | |||
4858 | nfsd4_close_open_stateid(stp); | 4968 | nfsd4_close_open_stateid(stp); |
4859 | 4969 | ||
4860 | /* put reference from nfs4_preprocess_seqid_op */ | 4970 | /* put reference from nfs4_preprocess_seqid_op */ |
@@ -6488,6 +6598,7 @@ nfs4_state_shutdown_net(struct net *net) | |||
6488 | list_for_each_safe(pos, next, &reaplist) { | 6598 | list_for_each_safe(pos, next, &reaplist) { |
6489 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 6599 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
6490 | list_del_init(&dp->dl_recall_lru); | 6600 | list_del_init(&dp->dl_recall_lru); |
6601 | put_clnt_odstate(dp->dl_clnt_odstate); | ||
6491 | nfs4_put_deleg_lease(dp->dl_stid.sc_file); | 6602 | nfs4_put_deleg_lease(dp->dl_stid.sc_file); |
6492 | nfs4_put_stid(&dp->dl_stid); | 6603 | nfs4_put_stid(&dp->dl_stid); |
6493 | } | 6604 | } |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 4f3bfeb11766..dbc4f85a5008 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -63,12 +63,12 @@ typedef struct { | |||
63 | 63 | ||
64 | struct nfsd4_callback { | 64 | struct nfsd4_callback { |
65 | struct nfs4_client *cb_clp; | 65 | struct nfs4_client *cb_clp; |
66 | struct list_head cb_per_client; | ||
67 | u32 cb_minorversion; | 66 | u32 cb_minorversion; |
68 | struct rpc_message cb_msg; | 67 | struct rpc_message cb_msg; |
69 | struct nfsd4_callback_ops *cb_ops; | 68 | struct nfsd4_callback_ops *cb_ops; |
70 | struct work_struct cb_work; | 69 | struct work_struct cb_work; |
71 | bool cb_done; | 70 | int cb_status; |
71 | bool cb_need_restart; | ||
72 | }; | 72 | }; |
73 | 73 | ||
74 | struct nfsd4_callback_ops { | 74 | struct nfsd4_callback_ops { |
@@ -126,6 +126,7 @@ struct nfs4_delegation { | |||
126 | struct list_head dl_perfile; | 126 | struct list_head dl_perfile; |
127 | struct list_head dl_perclnt; | 127 | struct list_head dl_perclnt; |
128 | struct list_head dl_recall_lru; /* delegation recalled */ | 128 | struct list_head dl_recall_lru; /* delegation recalled */ |
129 | struct nfs4_clnt_odstate *dl_clnt_odstate; | ||
129 | u32 dl_type; | 130 | u32 dl_type; |
130 | time_t dl_time; | 131 | time_t dl_time; |
131 | /* For recall: */ | 132 | /* For recall: */ |
@@ -332,7 +333,6 @@ struct nfs4_client { | |||
332 | int cl_cb_state; | 333 | int cl_cb_state; |
333 | struct nfsd4_callback cl_cb_null; | 334 | struct nfsd4_callback cl_cb_null; |
334 | struct nfsd4_session *cl_cb_session; | 335 | struct nfsd4_session *cl_cb_session; |
335 | struct list_head cl_callbacks; /* list of in-progress callbacks */ | ||
336 | 336 | ||
337 | /* for all client information that callback code might need: */ | 337 | /* for all client information that callback code might need: */ |
338 | spinlock_t cl_lock; | 338 | spinlock_t cl_lock; |
@@ -465,6 +465,17 @@ static inline struct nfs4_lockowner * lockowner(struct nfs4_stateowner *so) | |||
465 | } | 465 | } |
466 | 466 | ||
467 | /* | 467 | /* |
468 | * Per-client state indicating no. of opens and outstanding delegations | ||
469 | * on a file from a particular client.'od' stands for 'open & delegation' | ||
470 | */ | ||
471 | struct nfs4_clnt_odstate { | ||
472 | struct nfs4_client *co_client; | ||
473 | struct nfs4_file *co_file; | ||
474 | struct list_head co_perfile; | ||
475 | atomic_t co_odcount; | ||
476 | }; | ||
477 | |||
478 | /* | ||
468 | * nfs4_file: a file opened by some number of (open) nfs4_stateowners. | 479 | * nfs4_file: a file opened by some number of (open) nfs4_stateowners. |
469 | * | 480 | * |
470 | * These objects are global. nfsd keeps one instance of a nfs4_file per | 481 | * These objects are global. nfsd keeps one instance of a nfs4_file per |
@@ -485,6 +496,7 @@ struct nfs4_file { | |||
485 | struct list_head fi_delegations; | 496 | struct list_head fi_delegations; |
486 | struct rcu_head fi_rcu; | 497 | struct rcu_head fi_rcu; |
487 | }; | 498 | }; |
499 | struct list_head fi_clnt_odstate; | ||
488 | /* One each for O_RDONLY, O_WRONLY, O_RDWR: */ | 500 | /* One each for O_RDONLY, O_WRONLY, O_RDWR: */ |
489 | struct file * fi_fds[3]; | 501 | struct file * fi_fds[3]; |
490 | /* | 502 | /* |
@@ -526,6 +538,7 @@ struct nfs4_ol_stateid { | |||
526 | struct list_head st_perstateowner; | 538 | struct list_head st_perstateowner; |
527 | struct list_head st_locks; | 539 | struct list_head st_locks; |
528 | struct nfs4_stateowner * st_stateowner; | 540 | struct nfs4_stateowner * st_stateowner; |
541 | struct nfs4_clnt_odstate * st_clnt_odstate; | ||
529 | unsigned char st_access_bmap; | 542 | unsigned char st_access_bmap; |
530 | unsigned char st_deny_bmap; | 543 | unsigned char st_deny_bmap; |
531 | struct nfs4_ol_stateid * st_openstp; | 544 | struct nfs4_ol_stateid * st_openstp; |
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index f982ae84f0cd..2f8c092be2b3 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h | |||
@@ -247,6 +247,7 @@ struct nfsd4_open { | |||
247 | struct nfs4_openowner *op_openowner; /* used during processing */ | 247 | struct nfs4_openowner *op_openowner; /* used during processing */ |
248 | struct nfs4_file *op_file; /* used during processing */ | 248 | struct nfs4_file *op_file; /* used during processing */ |
249 | struct nfs4_ol_stateid *op_stp; /* used during processing */ | 249 | struct nfs4_ol_stateid *op_stp; /* used during processing */ |
250 | struct nfs4_clnt_odstate *op_odstate; /* used during processing */ | ||
250 | struct nfs4_acl *op_acl; | 251 | struct nfs4_acl *op_acl; |
251 | struct xdr_netobj op_label; | 252 | struct xdr_netobj op_label; |
252 | }; | 253 | }; |
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c index 1ec19f6f0c2b..eeeba5adee6d 100644 --- a/net/sunrpc/auth_gss/gss_rpc_xdr.c +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c | |||
@@ -793,20 +793,26 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, | |||
793 | { | 793 | { |
794 | u32 value_follows; | 794 | u32 value_follows; |
795 | int err; | 795 | int err; |
796 | struct page *scratch; | ||
797 | |||
798 | scratch = alloc_page(GFP_KERNEL); | ||
799 | if (!scratch) | ||
800 | return -ENOMEM; | ||
801 | xdr_set_scratch_buffer(xdr, page_address(scratch), PAGE_SIZE); | ||
796 | 802 | ||
797 | /* res->status */ | 803 | /* res->status */ |
798 | err = gssx_dec_status(xdr, &res->status); | 804 | err = gssx_dec_status(xdr, &res->status); |
799 | if (err) | 805 | if (err) |
800 | return err; | 806 | goto out_free; |
801 | 807 | ||
802 | /* res->context_handle */ | 808 | /* res->context_handle */ |
803 | err = gssx_dec_bool(xdr, &value_follows); | 809 | err = gssx_dec_bool(xdr, &value_follows); |
804 | if (err) | 810 | if (err) |
805 | return err; | 811 | goto out_free; |
806 | if (value_follows) { | 812 | if (value_follows) { |
807 | err = gssx_dec_ctx(xdr, res->context_handle); | 813 | err = gssx_dec_ctx(xdr, res->context_handle); |
808 | if (err) | 814 | if (err) |
809 | return err; | 815 | goto out_free; |
810 | } else { | 816 | } else { |
811 | res->context_handle = NULL; | 817 | res->context_handle = NULL; |
812 | } | 818 | } |
@@ -814,11 +820,11 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, | |||
814 | /* res->output_token */ | 820 | /* res->output_token */ |
815 | err = gssx_dec_bool(xdr, &value_follows); | 821 | err = gssx_dec_bool(xdr, &value_follows); |
816 | if (err) | 822 | if (err) |
817 | return err; | 823 | goto out_free; |
818 | if (value_follows) { | 824 | if (value_follows) { |
819 | err = gssx_dec_buffer(xdr, res->output_token); | 825 | err = gssx_dec_buffer(xdr, res->output_token); |
820 | if (err) | 826 | if (err) |
821 | return err; | 827 | goto out_free; |
822 | } else { | 828 | } else { |
823 | res->output_token = NULL; | 829 | res->output_token = NULL; |
824 | } | 830 | } |
@@ -826,14 +832,17 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, | |||
826 | /* res->delegated_cred_handle */ | 832 | /* res->delegated_cred_handle */ |
827 | err = gssx_dec_bool(xdr, &value_follows); | 833 | err = gssx_dec_bool(xdr, &value_follows); |
828 | if (err) | 834 | if (err) |
829 | return err; | 835 | goto out_free; |
830 | if (value_follows) { | 836 | if (value_follows) { |
831 | /* we do not support upcall servers sending this data. */ | 837 | /* we do not support upcall servers sending this data. */ |
832 | return -EINVAL; | 838 | err = -EINVAL; |
839 | goto out_free; | ||
833 | } | 840 | } |
834 | 841 | ||
835 | /* res->options */ | 842 | /* res->options */ |
836 | err = gssx_dec_option_array(xdr, &res->options); | 843 | err = gssx_dec_option_array(xdr, &res->options); |
837 | 844 | ||
845 | out_free: | ||
846 | __free_page(scratch); | ||
838 | return err; | 847 | return err; |
839 | } | 848 | } |