Skip to content

Commit

Permalink
Merge branch 'sctp-stream-reconfig-fixes'
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
davem330 committed Nov 27, 2017
2 parents 67c8d22 + 52a3958 commit 6b56b1a
Showing 1 changed file with 65 additions and 12 deletions.
77 changes: 65 additions & 12 deletions net/sctp/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,30 @@ static int sctp_send_reconf(struct sctp_association *asoc,
return retval;
}

static bool sctp_stream_outq_is_empty(struct sctp_stream *stream,
__u16 str_nums, __be16 *str_list)
{
struct sctp_association *asoc;
__u16 i;

asoc = container_of(stream, struct sctp_association, stream);
if (!asoc->outqueue.out_qlen)
return true;

if (!str_nums)
return false;

for (i = 0; i < str_nums; i++) {
__u16 sid = ntohs(str_list[i]);

if (stream->out[sid].ext &&
!list_empty(&stream->out[sid].ext->outq))
return false;
}

return true;
}

int sctp_send_reset_streams(struct sctp_association *asoc,
struct sctp_reset_streams *params)
{
Expand Down Expand Up @@ -317,6 +341,11 @@ int sctp_send_reset_streams(struct sctp_association *asoc,
for (i = 0; i < str_nums; i++)
nstr_list[i] = htons(str_list[i]);

if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) {
retval = -EAGAIN;
goto out;
}

chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in);

kfree(nstr_list);
Expand Down Expand Up @@ -377,6 +406,9 @@ int sctp_send_reset_assoc(struct sctp_association *asoc)
if (asoc->strreset_outstanding)
return -EINPROGRESS;

if (!sctp_outq_is_empty(&asoc->outqueue))
return -EAGAIN;

chunk = sctp_make_strreset_tsnreq(asoc);
if (!chunk)
return -ENOMEM;
Expand Down Expand Up @@ -563,7 +595,7 @@ struct sctp_chunk *sctp_process_strreset_outreq(
flags = SCTP_STREAM_RESET_INCOMING_SSN;
}

nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2;
nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16);
if (nums) {
str_p = outreq->list_of_streams;
for (i = 0; i < nums; i++) {
Expand Down Expand Up @@ -627,7 +659,7 @@ struct sctp_chunk *sctp_process_strreset_inreq(
goto out;
}

nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2;
nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16);
str_p = inreq->list_of_streams;
for (i = 0; i < nums; i++) {
if (ntohs(str_p[i]) >= stream->outcnt) {
Expand All @@ -636,6 +668,12 @@ struct sctp_chunk *sctp_process_strreset_inreq(
}
}

if (!sctp_stream_outq_is_empty(stream, nums, str_p)) {
result = SCTP_STRRESET_IN_PROGRESS;
asoc->strreset_inseq--;
goto err;
}

chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);
if (!chunk)
goto out;
Expand Down Expand Up @@ -687,12 +725,18 @@ struct sctp_chunk *sctp_process_strreset_tsnreq(
i = asoc->strreset_inseq - request_seq - 1;
result = asoc->strreset_result[i];
if (result == SCTP_STRRESET_PERFORMED) {
next_tsn = asoc->next_tsn;
next_tsn = asoc->ctsn_ack_point + 1;
init_tsn =
sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1;
}
goto err;
}

if (!sctp_outq_is_empty(&asoc->outqueue)) {
result = SCTP_STRRESET_IN_PROGRESS;
goto err;
}

asoc->strreset_inseq++;

if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ))
Expand All @@ -703,9 +747,10 @@ struct sctp_chunk *sctp_process_strreset_tsnreq(
goto out;
}

/* G3: The same processing as though a SACK chunk with no gap report
* and a cumulative TSN ACK of the Sender's Next TSN minus 1 were
* received MUST be performed.
/* G4: The same processing as though a FWD-TSN chunk (as defined in
* [RFC3758]) with all streams affected and a new cumulative TSN
* ACK of the Receiver's Next TSN minus 1 were received MUST be
* performed.
*/
max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen);
Expand All @@ -720,10 +765,9 @@ struct sctp_chunk *sctp_process_strreset_tsnreq(
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
init_tsn, GFP_ATOMIC);

/* G4: The same processing as though a FWD-TSN chunk (as defined in
* [RFC3758]) with all streams affected and a new cumulative TSN
* ACK of the Receiver's Next TSN minus 1 were received MUST be
* performed.
/* G3: The same processing as though a SACK chunk with no gap report
* and a cumulative TSN ACK of the Sender's Next TSN minus 1 were
* received MUST be performed.
*/
sctp_outq_free(&asoc->outqueue);

Expand Down Expand Up @@ -927,7 +971,8 @@ struct sctp_chunk *sctp_process_strreset_resp(

outreq = (struct sctp_strreset_outreq *)req;
str_p = outreq->list_of_streams;
nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2;
nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) /
sizeof(__u16);

if (result == SCTP_STRRESET_PERFORMED) {
if (nums) {
Expand Down Expand Up @@ -956,7 +1001,8 @@ struct sctp_chunk *sctp_process_strreset_resp(

inreq = (struct sctp_strreset_inreq *)req;
str_p = inreq->list_of_streams;
nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2;
nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) /
sizeof(__u16);

*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
nums, str_p, GFP_ATOMIC);
Expand All @@ -975,6 +1021,7 @@ struct sctp_chunk *sctp_process_strreset_resp(
if (result == SCTP_STRRESET_PERFORMED) {
__u32 mtsn = sctp_tsnmap_get_max_tsn_seen(
&asoc->peer.tsn_map);
LIST_HEAD(temp);

sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn);
sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
Expand All @@ -983,7 +1030,13 @@ struct sctp_chunk *sctp_process_strreset_resp(
SCTP_TSN_MAP_INITIAL,
stsn, GFP_ATOMIC);

/* Clean up sacked and abandoned queues only. As the
* out_chunk_list may not be empty, splice it to temp,
* then get it back after sctp_outq_free is done.
*/
list_splice_init(&asoc->outqueue.out_chunk_list, &temp);
sctp_outq_free(&asoc->outqueue);
list_splice_init(&temp, &asoc->outqueue.out_chunk_list);

asoc->next_tsn = rtsn;
asoc->ctsn_ack_point = asoc->next_tsn - 1;
Expand Down

0 comments on commit 6b56b1a

Please sign in to comment.