diff options
author | David S. Miller <davem@davemloft.net> | 2017-11-27 10:38:45 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-11-27 10:39:09 -0500 |
commit | 6b56b1a255bda11fcec0b5f4f8ac7dcec43808af (patch) | |
tree | 43ba6d5322c022f3cc0477820f59d3017fbaf76a | |
parent | 67c8d22a73128ff910e2287567132530abcf5b71 (diff) | |
parent | 52a395896a051a3d5c34fba67c324f69ec5e67c6 (diff) |
Merge branch 'sctp-stream-reconfig-fixes'
Xin Long says:
====================
sctp: a bunch of fixes for stream reconfig
This patchset is to make stream reset and asoc reset work more correctly
for stream reconfig.
Thank to Marcelo making them very clear.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/sctp/stream.c | 77 |
1 files changed, 65 insertions, 12 deletions
diff --git a/net/sctp/stream.c b/net/sctp/stream.c index a11db21dc8a0..a20145b3a949 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c | |||
@@ -254,6 +254,30 @@ static int sctp_send_reconf(struct sctp_association *asoc, | |||
254 | return retval; | 254 | return retval; |
255 | } | 255 | } |
256 | 256 | ||
257 | static bool sctp_stream_outq_is_empty(struct sctp_stream *stream, | ||
258 | __u16 str_nums, __be16 *str_list) | ||
259 | { | ||
260 | struct sctp_association *asoc; | ||
261 | __u16 i; | ||
262 | |||
263 | asoc = container_of(stream, struct sctp_association, stream); | ||
264 | if (!asoc->outqueue.out_qlen) | ||
265 | return true; | ||
266 | |||
267 | if (!str_nums) | ||
268 | return false; | ||
269 | |||
270 | for (i = 0; i < str_nums; i++) { | ||
271 | __u16 sid = ntohs(str_list[i]); | ||
272 | |||
273 | if (stream->out[sid].ext && | ||
274 | !list_empty(&stream->out[sid].ext->outq)) | ||
275 | return false; | ||
276 | } | ||
277 | |||
278 | return true; | ||
279 | } | ||
280 | |||
257 | int sctp_send_reset_streams(struct sctp_association *asoc, | 281 | int sctp_send_reset_streams(struct sctp_association *asoc, |
258 | struct sctp_reset_streams *params) | 282 | struct sctp_reset_streams *params) |
259 | { | 283 | { |
@@ -317,6 +341,11 @@ int sctp_send_reset_streams(struct sctp_association *asoc, | |||
317 | for (i = 0; i < str_nums; i++) | 341 | for (i = 0; i < str_nums; i++) |
318 | nstr_list[i] = htons(str_list[i]); | 342 | nstr_list[i] = htons(str_list[i]); |
319 | 343 | ||
344 | if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) { | ||
345 | retval = -EAGAIN; | ||
346 | goto out; | ||
347 | } | ||
348 | |||
320 | chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in); | 349 | chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in); |
321 | 350 | ||
322 | kfree(nstr_list); | 351 | kfree(nstr_list); |
@@ -377,6 +406,9 @@ int sctp_send_reset_assoc(struct sctp_association *asoc) | |||
377 | if (asoc->strreset_outstanding) | 406 | if (asoc->strreset_outstanding) |
378 | return -EINPROGRESS; | 407 | return -EINPROGRESS; |
379 | 408 | ||
409 | if (!sctp_outq_is_empty(&asoc->outqueue)) | ||
410 | return -EAGAIN; | ||
411 | |||
380 | chunk = sctp_make_strreset_tsnreq(asoc); | 412 | chunk = sctp_make_strreset_tsnreq(asoc); |
381 | if (!chunk) | 413 | if (!chunk) |
382 | return -ENOMEM; | 414 | return -ENOMEM; |
@@ -563,7 +595,7 @@ struct sctp_chunk *sctp_process_strreset_outreq( | |||
563 | flags = SCTP_STREAM_RESET_INCOMING_SSN; | 595 | flags = SCTP_STREAM_RESET_INCOMING_SSN; |
564 | } | 596 | } |
565 | 597 | ||
566 | nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2; | 598 | nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16); |
567 | if (nums) { | 599 | if (nums) { |
568 | str_p = outreq->list_of_streams; | 600 | str_p = outreq->list_of_streams; |
569 | for (i = 0; i < nums; i++) { | 601 | for (i = 0; i < nums; i++) { |
@@ -627,7 +659,7 @@ struct sctp_chunk *sctp_process_strreset_inreq( | |||
627 | goto out; | 659 | goto out; |
628 | } | 660 | } |
629 | 661 | ||
630 | nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2; | 662 | nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); |
631 | str_p = inreq->list_of_streams; | 663 | str_p = inreq->list_of_streams; |
632 | for (i = 0; i < nums; i++) { | 664 | for (i = 0; i < nums; i++) { |
633 | if (ntohs(str_p[i]) >= stream->outcnt) { | 665 | if (ntohs(str_p[i]) >= stream->outcnt) { |
@@ -636,6 +668,12 @@ struct sctp_chunk *sctp_process_strreset_inreq( | |||
636 | } | 668 | } |
637 | } | 669 | } |
638 | 670 | ||
671 | if (!sctp_stream_outq_is_empty(stream, nums, str_p)) { | ||
672 | result = SCTP_STRRESET_IN_PROGRESS; | ||
673 | asoc->strreset_inseq--; | ||
674 | goto err; | ||
675 | } | ||
676 | |||
639 | chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); | 677 | chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0); |
640 | if (!chunk) | 678 | if (!chunk) |
641 | goto out; | 679 | goto out; |
@@ -687,12 +725,18 @@ struct sctp_chunk *sctp_process_strreset_tsnreq( | |||
687 | i = asoc->strreset_inseq - request_seq - 1; | 725 | i = asoc->strreset_inseq - request_seq - 1; |
688 | result = asoc->strreset_result[i]; | 726 | result = asoc->strreset_result[i]; |
689 | if (result == SCTP_STRRESET_PERFORMED) { | 727 | if (result == SCTP_STRRESET_PERFORMED) { |
690 | next_tsn = asoc->next_tsn; | 728 | next_tsn = asoc->ctsn_ack_point + 1; |
691 | init_tsn = | 729 | init_tsn = |
692 | sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; | 730 | sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; |
693 | } | 731 | } |
694 | goto err; | 732 | goto err; |
695 | } | 733 | } |
734 | |||
735 | if (!sctp_outq_is_empty(&asoc->outqueue)) { | ||
736 | result = SCTP_STRRESET_IN_PROGRESS; | ||
737 | goto err; | ||
738 | } | ||
739 | |||
696 | asoc->strreset_inseq++; | 740 | asoc->strreset_inseq++; |
697 | 741 | ||
698 | if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) | 742 | if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) |
@@ -703,9 +747,10 @@ struct sctp_chunk *sctp_process_strreset_tsnreq( | |||
703 | goto out; | 747 | goto out; |
704 | } | 748 | } |
705 | 749 | ||
706 | /* G3: The same processing as though a SACK chunk with no gap report | 750 | /* G4: The same processing as though a FWD-TSN chunk (as defined in |
707 | * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were | 751 | * [RFC3758]) with all streams affected and a new cumulative TSN |
708 | * received MUST be performed. | 752 | * ACK of the Receiver's Next TSN minus 1 were received MUST be |
753 | * performed. | ||
709 | */ | 754 | */ |
710 | max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); | 755 | max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); |
711 | sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen); | 756 | sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen); |
@@ -720,10 +765,9 @@ struct sctp_chunk *sctp_process_strreset_tsnreq( | |||
720 | sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, | 765 | sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, |
721 | init_tsn, GFP_ATOMIC); | 766 | init_tsn, GFP_ATOMIC); |
722 | 767 | ||
723 | /* G4: The same processing as though a FWD-TSN chunk (as defined in | 768 | /* G3: The same processing as though a SACK chunk with no gap report |
724 | * [RFC3758]) with all streams affected and a new cumulative TSN | 769 | * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were |
725 | * ACK of the Receiver's Next TSN minus 1 were received MUST be | 770 | * received MUST be performed. |
726 | * performed. | ||
727 | */ | 771 | */ |
728 | sctp_outq_free(&asoc->outqueue); | 772 | sctp_outq_free(&asoc->outqueue); |
729 | 773 | ||
@@ -927,7 +971,8 @@ struct sctp_chunk *sctp_process_strreset_resp( | |||
927 | 971 | ||
928 | outreq = (struct sctp_strreset_outreq *)req; | 972 | outreq = (struct sctp_strreset_outreq *)req; |
929 | str_p = outreq->list_of_streams; | 973 | str_p = outreq->list_of_streams; |
930 | nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2; | 974 | nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / |
975 | sizeof(__u16); | ||
931 | 976 | ||
932 | if (result == SCTP_STRRESET_PERFORMED) { | 977 | if (result == SCTP_STRRESET_PERFORMED) { |
933 | if (nums) { | 978 | if (nums) { |
@@ -956,7 +1001,8 @@ struct sctp_chunk *sctp_process_strreset_resp( | |||
956 | 1001 | ||
957 | inreq = (struct sctp_strreset_inreq *)req; | 1002 | inreq = (struct sctp_strreset_inreq *)req; |
958 | str_p = inreq->list_of_streams; | 1003 | str_p = inreq->list_of_streams; |
959 | nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2; | 1004 | nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / |
1005 | sizeof(__u16); | ||
960 | 1006 | ||
961 | *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, | 1007 | *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, |
962 | nums, str_p, GFP_ATOMIC); | 1008 | nums, str_p, GFP_ATOMIC); |
@@ -975,6 +1021,7 @@ struct sctp_chunk *sctp_process_strreset_resp( | |||
975 | if (result == SCTP_STRRESET_PERFORMED) { | 1021 | if (result == SCTP_STRRESET_PERFORMED) { |
976 | __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( | 1022 | __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( |
977 | &asoc->peer.tsn_map); | 1023 | &asoc->peer.tsn_map); |
1024 | LIST_HEAD(temp); | ||
978 | 1025 | ||
979 | sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn); | 1026 | sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn); |
980 | sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); | 1027 | sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); |
@@ -983,7 +1030,13 @@ struct sctp_chunk *sctp_process_strreset_resp( | |||
983 | SCTP_TSN_MAP_INITIAL, | 1030 | SCTP_TSN_MAP_INITIAL, |
984 | stsn, GFP_ATOMIC); | 1031 | stsn, GFP_ATOMIC); |
985 | 1032 | ||
1033 | /* Clean up sacked and abandoned queues only. As the | ||
1034 | * out_chunk_list may not be empty, splice it to temp, | ||
1035 | * then get it back after sctp_outq_free is done. | ||
1036 | */ | ||
1037 | list_splice_init(&asoc->outqueue.out_chunk_list, &temp); | ||
986 | sctp_outq_free(&asoc->outqueue); | 1038 | sctp_outq_free(&asoc->outqueue); |
1039 | list_splice_init(&temp, &asoc->outqueue.out_chunk_list); | ||
987 | 1040 | ||
988 | asoc->next_tsn = rtsn; | 1041 | asoc->next_tsn = rtsn; |
989 | asoc->ctsn_ack_point = asoc->next_tsn - 1; | 1042 | asoc->ctsn_ack_point = asoc->next_tsn - 1; |