Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b56b1a2 authored by David S. Miller's avatar David S. Miller
Browse files

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 67c8d22a 52a39589
Loading
Loading
Loading
Loading
+65 −12
Original line number Diff line number Diff line
@@ -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;