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:
commit
6b56b1a255
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue