diff options
Diffstat (limited to 'net/sctp/outqueue.c')
| -rw-r--r-- | net/sctp/outqueue.c | 99 | 
1 files changed, 99 insertions, 0 deletions
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 084718f9b3da..72e54a416af6 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -326,6 +326,9 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)  			sctp_chunk_hold(chunk);  			sctp_outq_tail_data(q, chunk); +			if (chunk->asoc->prsctp_enable && +			    SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags)) +				chunk->asoc->sent_cnt_removable++;  			if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)  				SCTP_INC_STATS(net, SCTP_MIB_OUTUNORDERCHUNKS);  			else @@ -372,6 +375,96 @@ static void sctp_insert_list(struct list_head *head, struct list_head *new)  		list_add_tail(new, head);  } +static int sctp_prsctp_prune_sent(struct sctp_association *asoc, +				  struct sctp_sndrcvinfo *sinfo, +				  struct list_head *queue, int msg_len) +{ +	struct sctp_chunk *chk, *temp; + +	list_for_each_entry_safe(chk, temp, queue, transmitted_list) { +		if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) || +		    chk->prsctp_param <= sinfo->sinfo_timetolive) +			continue; + +		list_del_init(&chk->transmitted_list); +		sctp_insert_list(&asoc->outqueue.abandoned, +				 &chk->transmitted_list); + +		asoc->sent_cnt_removable--; +		asoc->abandoned_sent[SCTP_PR_INDEX(PRIO)]++; + +		if (!chk->tsn_gap_acked) { +			if (chk->transport) +				chk->transport->flight_size -= +						sctp_data_size(chk); +			asoc->outqueue.outstanding_bytes -= sctp_data_size(chk); +		} + +		msg_len -= SCTP_DATA_SNDSIZE(chk) + +			   sizeof(struct sk_buff) + +			   sizeof(struct sctp_chunk); +		if (msg_len <= 0) +			break; +	} + +	return msg_len; +} + +static int sctp_prsctp_prune_unsent(struct sctp_association *asoc, +				    struct sctp_sndrcvinfo *sinfo, +				    struct list_head *queue, int msg_len) +{ +	struct sctp_chunk *chk, *temp; + +	list_for_each_entry_safe(chk, temp, queue, list) { +		if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) || +		    chk->prsctp_param <= sinfo->sinfo_timetolive) +			continue; + +		list_del_init(&chk->list); +		asoc->sent_cnt_removable--; +		asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; + +		msg_len -= SCTP_DATA_SNDSIZE(chk) + +			   sizeof(struct sk_buff) + +			   sizeof(struct sctp_chunk); +		sctp_chunk_free(chk); +		if (msg_len <= 0) +			break; +	} + +	return msg_len; +} + +/* Abandon the chunks according their priorities */ +void sctp_prsctp_prune(struct sctp_association *asoc, +		       struct sctp_sndrcvinfo *sinfo, int msg_len) +{ +	struct sctp_transport *transport; + +	if (!asoc->prsctp_enable || !asoc->sent_cnt_removable) +		return; + +	msg_len = sctp_prsctp_prune_sent(asoc, sinfo, +					 &asoc->outqueue.retransmit, +					 msg_len); +	if (msg_len <= 0) +		return; + +	list_for_each_entry(transport, &asoc->peer.transport_addr_list, +			    transports) { +		msg_len = sctp_prsctp_prune_sent(asoc, sinfo, +						 &transport->transmitted, +						 msg_len); +		if (msg_len <= 0) +			return; +	} + +	sctp_prsctp_prune_unsent(asoc, sinfo, +				 &asoc->outqueue.out_chunk_list, +				 msg_len); +} +  /* Mark all the eligible packets on a transport for retransmission.  */  void sctp_retransmit_mark(struct sctp_outq *q,  			  struct sctp_transport *transport, @@ -962,6 +1055,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)  				/* Mark as failed send. */  				sctp_chunk_fail(chunk, SCTP_ERROR_INV_STRM); +				if (asoc->prsctp_enable && +				    SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags)) +					asoc->sent_cnt_removable--;  				sctp_chunk_free(chunk);  				continue;  			} @@ -1251,6 +1347,9 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)  		tsn = ntohl(tchunk->subh.data_hdr->tsn);  		if (TSN_lte(tsn, ctsn)) {  			list_del_init(&tchunk->transmitted_list); +			if (asoc->prsctp_enable && +			    SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags)) +				asoc->sent_cnt_removable--;  			sctp_chunk_free(tchunk);  		}  	}  |