diff options
Diffstat (limited to 'net/sctp/ulpqueue.c')
| -rw-r--r-- | net/sctp/ulpqueue.c | 26 | 
1 files changed, 22 insertions, 4 deletions
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 63afddcbcd2..f221fbbc80a 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -962,8 +962,8 @@ static __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,  		struct sk_buff_head *list, __u16 needed)  {  	__u16 freed = 0; -	__u32 tsn; -	struct sk_buff *skb; +	__u32 tsn, last_tsn; +	struct sk_buff *skb, *flist, *last;  	struct sctp_ulpevent *event;  	struct sctp_tsnmap *tsnmap; @@ -977,10 +977,28 @@ static __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,  		if (TSN_lte(tsn, sctp_tsnmap_get_ctsn(tsnmap)))  			break; -		__skb_unlink(skb, list); +		/* Events in ordering queue may have multiple fragments +		 * corresponding to additional TSNs.  Sum the total +		 * freed space; find the last TSN. +		 */  		freed += skb_headlen(skb); +		flist = skb_shinfo(skb)->frag_list; +		for (last = flist; flist; flist = flist->next) { +			last = flist; +			freed += skb_headlen(last); +		} +		if (last) +			last_tsn = sctp_skb2event(last)->tsn; +		else +			last_tsn = tsn; + +		/* Unlink the event, then renege all applicable TSNs. */ +		__skb_unlink(skb, list);  		sctp_ulpevent_free(event); -		sctp_tsnmap_renege(tsnmap, tsn); +		while (TSN_lte(tsn, last_tsn)) { +			sctp_tsnmap_renege(tsnmap, tsn); +			tsn++; +		}  		if (freed >= needed)  			return freed;  	}  |