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>
This commit is contained in:
David S. Miller 2017-11-28 00:38:45 +09:00
commit 6b56b1a255
1 changed files with 65 additions and 12 deletions

View File

@ -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)
{
@ -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);
@ -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;
@ -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++) {
@ -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) {
@ -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;
@ -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))
@ -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);
@ -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);
@ -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) {
@ -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);
@ -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);
@ -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;