diff options
author | Lee A. Roberts <lee.roberts@hp.com> | 2013-02-27 23:37:29 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-02-28 15:34:26 -0500 |
commit | 95ac7b859f508b1b3e6adf7dce307864e4384a69 (patch) | |
tree | 5e1c0cad931d5440c0434da15ef52ed2eaf002ab /net/sctp/ulpqueue.c | |
parent | e67f85ecd83de66d4f25f2e0f90bb0d01a52ddd8 (diff) |
sctp: fix association hangs due to errors when reneging events from the ordering queue
In sctp_ulpq_renege_list(), events being reneged from the
ordering queue may correspond to multiple TSNs. Identify
all affected packets; sum freed space and renege from the
tsnmap.
Signed-off-by: Lee A. Roberts <lee.roberts@hp.com>
Acked-by: Vlad Yasevich <vyasevich@gmail.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
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 63afddcbcd2c..f221fbbc80ac 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, | |||
962 | struct sk_buff_head *list, __u16 needed) | 962 | struct sk_buff_head *list, __u16 needed) |
963 | { | 963 | { |
964 | __u16 freed = 0; | 964 | __u16 freed = 0; |
965 | __u32 tsn; | 965 | __u32 tsn, last_tsn; |
966 | struct sk_buff *skb; | 966 | struct sk_buff *skb, *flist, *last; |
967 | struct sctp_ulpevent *event; | 967 | struct sctp_ulpevent *event; |
968 | struct sctp_tsnmap *tsnmap; | 968 | struct sctp_tsnmap *tsnmap; |
969 | 969 | ||
@@ -977,10 +977,28 @@ static __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq, | |||
977 | if (TSN_lte(tsn, sctp_tsnmap_get_ctsn(tsnmap))) | 977 | if (TSN_lte(tsn, sctp_tsnmap_get_ctsn(tsnmap))) |
978 | break; | 978 | break; |
979 | 979 | ||
980 | __skb_unlink(skb, list); | 980 | /* Events in ordering queue may have multiple fragments |
981 | * corresponding to additional TSNs. Sum the total | ||
982 | * freed space; find the last TSN. | ||
983 | */ | ||
981 | freed += skb_headlen(skb); | 984 | freed += skb_headlen(skb); |
985 | flist = skb_shinfo(skb)->frag_list; | ||
986 | for (last = flist; flist; flist = flist->next) { | ||
987 | last = flist; | ||
988 | freed += skb_headlen(last); | ||
989 | } | ||
990 | if (last) | ||
991 | last_tsn = sctp_skb2event(last)->tsn; | ||
992 | else | ||
993 | last_tsn = tsn; | ||
994 | |||
995 | /* Unlink the event, then renege all applicable TSNs. */ | ||
996 | __skb_unlink(skb, list); | ||
982 | sctp_ulpevent_free(event); | 997 | sctp_ulpevent_free(event); |
983 | sctp_tsnmap_renege(tsnmap, tsn); | 998 | while (TSN_lte(tsn, last_tsn)) { |
999 | sctp_tsnmap_renege(tsnmap, tsn); | ||
1000 | tsn++; | ||
1001 | } | ||
984 | if (freed >= needed) | 1002 | if (freed >= needed) |
985 | return freed; | 1003 | return freed; |
986 | } | 1004 | } |