aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp/ulpqueue.c
diff options
context:
space:
mode:
authorLee A. Roberts <lee.roberts@hp.com>2013-02-27 23:37:29 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-28 15:34:26 -0500
commit95ac7b859f508b1b3e6adf7dce307864e4384a69 (patch)
tree5e1c0cad931d5440c0434da15ef52ed2eaf002ab /net/sctp/ulpqueue.c
parente67f85ecd83de66d4f25f2e0f90bb0d01a52ddd8 (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.c26
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 }