aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2017-11-27 10:38:45 -0500
committerDavid S. Miller <davem@davemloft.net>2017-11-27 10:39:09 -0500
commit6b56b1a255bda11fcec0b5f4f8ac7dcec43808af (patch)
tree43ba6d5322c022f3cc0477820f59d3017fbaf76a
parent67c8d22a73128ff910e2287567132530abcf5b71 (diff)
parent52a395896a051a3d5c34fba67c324f69ec5e67c6 (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.c77
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
257static 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
257int sctp_send_reset_streams(struct sctp_association *asoc, 281int 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;