const: constify remaining dev_pm_ops
[safe/jmp/linux-2.6] / net / sctp / ulpqueue.c
index 0fa4d4d..7b23803 100644 (file)
@@ -1,4 +1,4 @@
-/* SCTP kernel reference Implementation
+/* SCTP kernel implementation
  * (C) Copyright IBM Corp. 2001, 2004
  * Copyright (c) 1999-2000 Cisco, Inc.
  * Copyright (c) 1999-2001 Motorola, Inc.
@@ -8,13 +8,13 @@
  *
  * This abstraction carries sctp events to the ULP (sockets).
  *
- * The SCTP reference implementation is free software;
+ * This SCTP implementation is free software;
  * you can redistribute it and/or modify it under the terms of
  * the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
  * any later version.
  *
- * The SCTP reference implementation is distributed in the hope that it
+ * This SCTP implementation is distributed in the hope that it
  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
  *                 ************************
  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
@@ -53,6 +53,7 @@ static struct sctp_ulpevent * sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
                                              struct sctp_ulpevent *);
 static struct sctp_ulpevent * sctp_ulpq_order(struct sctp_ulpq *,
                                              struct sctp_ulpevent *);
+static void sctp_ulpq_reasm_drain(struct sctp_ulpq *ulpq);
 
 /* 1st Level Abstractions */
 
@@ -190,6 +191,7 @@ static void sctp_ulpq_set_pd(struct sctp_ulpq *ulpq)
 static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq)
 {
        ulpq->pd_mode = 0;
+       sctp_ulpq_reasm_drain(ulpq);
        return sctp_clear_pd(ulpq->asoc->base.sk, ulpq->asoc);
 }
 
@@ -281,7 +283,7 @@ out_free:
 /* 2nd Level Abstractions */
 
 /* Helper function to store chunks that need to be reassembled.  */
-static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
+static void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
                                         struct sctp_ulpevent *event)
 {
        struct sk_buff *pos;
@@ -315,7 +317,7 @@ static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
        }
 
        /* Insert before pos. */
-       __skb_insert(sctp_event2skb(event), pos->prev, pos, &ulpq->reasm);
+       __skb_queue_before(&ulpq->reasm, pos, sctp_event2skb(event));
 
 }
 
@@ -391,7 +393,7 @@ static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *qu
                        break;
                pos->next = pnext;
                pos = pnext;
-       };
+       }
 
        event = sctp_skb2event(f_frag);
        SCTP_INC_STATS(SCTP_MIB_REASMUSRMSGS);
@@ -403,7 +405,7 @@ static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *qu
 /* Helper function to check if an incoming chunk has filled up the last
  * missing fragment in a SCTP datagram and return the corresponding event.
  */
-static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq)
+static struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq)
 {
        struct sk_buff *pos;
        struct sctp_ulpevent *cevent;
@@ -476,7 +478,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u
                        else
                                first_frag = NULL;
                        break;
-               };
+               }
        }
 
        asoc = ulpq->asoc;
@@ -510,7 +512,7 @@ found:
 }
 
 /* Retrieve the next set of fragments of a partial message. */
-static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
+static struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
 {
        struct sk_buff *pos, *last_frag, *first_frag;
        struct sctp_ulpevent *cevent;
@@ -556,7 +558,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq
                        goto done;
                default:
                        return NULL;
-               };
+               }
        }
 
        /* We have the reassembled event. There is no need to look
@@ -604,7 +606,7 @@ static struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
 }
 
 /* Retrieve the first part (sequential fragments) for partial delivery.  */
-static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
+static struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
 {
        struct sk_buff *pos, *last_frag, *first_frag;
        struct sctp_ulpevent *cevent;
@@ -648,7 +650,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *u
                        break;
                default:
                        return NULL;
-               };
+               }
        }
 
        /* We have the reassembled event. There is no need to look
@@ -659,10 +661,81 @@ done:
        return retval;
 }
 
+/*
+ * Flush out stale fragments from the reassembly queue when processing
+ * a Forward TSN.
+ *
+ * RFC 3758, Section 3.6
+ *
+ * After receiving and processing a FORWARD TSN, the data receiver MUST
+ * take cautions in updating its re-assembly queue.  The receiver MUST
+ * remove any partially reassembled message, which is still missing one
+ * or more TSNs earlier than or equal to the new cumulative TSN point.
+ * In the event that the receiver has invoked the partial delivery API,
+ * a notification SHOULD also be generated to inform the upper layer API
+ * that the message being partially delivered will NOT be completed.
+ */
+void sctp_ulpq_reasm_flushtsn(struct sctp_ulpq *ulpq, __u32 fwd_tsn)
+{
+       struct sk_buff *pos, *tmp;
+       struct sctp_ulpevent *event;
+       __u32 tsn;
+
+       if (skb_queue_empty(&ulpq->reasm))
+               return;
+
+       skb_queue_walk_safe(&ulpq->reasm, pos, tmp) {
+               event = sctp_skb2event(pos);
+               tsn = event->tsn;
+
+               /* Since the entire message must be abandoned by the
+                * sender (item A3 in Section 3.5, RFC 3758), we can
+                * free all fragments on the list that are less then
+                * or equal to ctsn_point
+                */
+               if (TSN_lte(tsn, fwd_tsn)) {
+                       __skb_unlink(pos, &ulpq->reasm);
+                       sctp_ulpevent_free(event);
+               } else
+                       break;
+       }
+}
+
+/*
+ * Drain the reassembly queue.  If we just cleared parted delivery, it
+ * is possible that the reassembly queue will contain already reassembled
+ * messages.  Retrieve any such messages and give them to the user.
+ */
+static void sctp_ulpq_reasm_drain(struct sctp_ulpq *ulpq)
+{
+       struct sctp_ulpevent *event = NULL;
+       struct sk_buff_head temp;
+
+       if (skb_queue_empty(&ulpq->reasm))
+               return;
+
+       while ((event = sctp_ulpq_retrieve_reassembled(ulpq)) != NULL) {
+               /* Do ordering if needed.  */
+               if ((event) && (event->msg_flags & MSG_EOR)){
+                       skb_queue_head_init(&temp);
+                       __skb_queue_tail(&temp, sctp_event2skb(event));
+
+                       event = sctp_ulpq_order(ulpq, event);
+               }
+
+               /* Send event to the ULP.  'event' is the
+                * sctp_ulpevent for  very first SKB on the  temp' list.
+                */
+               if (event)
+                       sctp_ulpq_tail_event(ulpq, event);
+       }
+}
+
+
 /* Helper function to gather skbs that have possibly become
  * ordered by an an incoming chunk.
  */
-static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
+static void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
                                              struct sctp_ulpevent *event)
 {
        struct sk_buff_head *event_list;
@@ -706,7 +779,7 @@ static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
 }
 
 /* Helper function to store chunks needing ordering.  */
-static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
+static void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
                                           struct sctp_ulpevent *event)
 {
        struct sk_buff *pos;
@@ -752,8 +825,7 @@ static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
 
 
        /* Insert before pos. */
-       __skb_insert(sctp_event2skb(event), pos->prev, pos, &ulpq->lobby);
-
+       __skb_queue_before(&ulpq->lobby, pos, sctp_event2skb(event));
 }
 
 static struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq,
@@ -794,13 +866,14 @@ static struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq,
 /* Helper function to gather skbs that have possibly become
  * ordered by forward tsn skipping their dependencies.
  */
-static inline void sctp_ulpq_reap_ordered(struct sctp_ulpq *ulpq)
+static void sctp_ulpq_reap_ordered(struct sctp_ulpq *ulpq, __u16 sid)
 {
        struct sk_buff *pos, *tmp;
        struct sctp_ulpevent *cevent;
        struct sctp_ulpevent *event;
        struct sctp_stream *in;
        struct sk_buff_head temp;
+       struct sk_buff_head *lobby = &ulpq->lobby;
        __u16 csid, cssn;
 
        in  = &ulpq->asoc->ssnmap->in;
@@ -808,36 +881,61 @@ static inline void sctp_ulpq_reap_ordered(struct sctp_ulpq *ulpq)
        /* We are holding the chunks by stream, by SSN.  */
        skb_queue_head_init(&temp);
        event = NULL;
-       sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
+       sctp_skb_for_each(pos, lobby, tmp) {
                cevent = (struct sctp_ulpevent *) pos->cb;
                csid = cevent->stream;
                cssn = cevent->ssn;
 
-               if (cssn != sctp_ssn_peek(in, csid))
+               /* Have we gone too far?  */
+               if (csid > sid)
                        break;
 
-               /* Found it, so mark in the ssnmap. */
-               sctp_ssn_next(in, csid);
+               /* Have we not gone far enough?  */
+               if (csid < sid)
+                       continue;
 
-               __skb_unlink(pos, &ulpq->lobby);
-               if (!event) {
+               /* see if this ssn has been marked by skipping */
+               if (!SSN_lt(cssn, sctp_ssn_peek(in, csid)))
+                       break;
+
+               __skb_unlink(pos, lobby);
+               if (!event)
                        /* Create a temporary list to collect chunks on.  */
                        event = sctp_skb2event(pos);
-                       __skb_queue_tail(&temp, sctp_event2skb(event));
-               } else {
-                       /* Attach all gathered skbs to the event.  */
+
+               /* Attach all gathered skbs to the event.  */
+               __skb_queue_tail(&temp, pos);
+       }
+
+       /* If we didn't reap any data, see if the next expected SSN
+        * is next on the queue and if so, use that.
+        */
+       if (event == NULL && pos != (struct sk_buff *)lobby) {
+               cevent = (struct sctp_ulpevent *) pos->cb;
+               csid = cevent->stream;
+               cssn = cevent->ssn;
+
+               if (csid == sid && cssn == sctp_ssn_peek(in, csid)) {
+                       sctp_ssn_next(in, csid);
+                       __skb_unlink(pos, lobby);
                        __skb_queue_tail(&temp, pos);
+                       event = sctp_skb2event(pos);
                }
        }
 
        /* Send event to the ULP.  'event' is the sctp_ulpevent for
         * very first SKB on the 'temp' list.
         */
-       if (event)
+       if (event) {
+               /* see if we have more ordered that we can deliver */
+               sctp_ulpq_retrieve_ordered(ulpq, event);
                sctp_ulpq_tail_event(ulpq, event);
+       }
 }
 
-/* Skip over an SSN. */
+/* Skip over an SSN. This is used during the processing of
+ * Forwared TSN chunk to skip over the abandoned ordered data
+ */
 void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
 {
        struct sctp_stream *in;
@@ -855,12 +953,12 @@ void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
        /* Go find any other chunks that were waiting for
         * ordering and deliver them if needed.
         */
-       sctp_ulpq_reap_ordered(ulpq);
+       sctp_ulpq_reap_ordered(ulpq, sid);
        return;
 }
 
-/* Renege 'needed' bytes from the ordering queue. */
-static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
+static __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,
+               struct sk_buff_head *list, __u16 needed)
 {
        __u16 freed = 0;
        __u32 tsn;
@@ -870,7 +968,7 @@ static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
 
        tsnmap = &ulpq->asoc->peer.tsn_map;
 
-       while ((skb = __skb_dequeue_tail(&ulpq->lobby)) != NULL) {
+       while ((skb = __skb_dequeue_tail(list)) != NULL) {
                freed += skb_headlen(skb);
                event = sctp_skb2event(skb);
                tsn = event->tsn;
@@ -884,30 +982,16 @@ static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
        return freed;
 }
 
+/* Renege 'needed' bytes from the ordering queue. */
+static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
+{
+       return sctp_ulpq_renege_list(ulpq, &ulpq->lobby, needed);
+}
+
 /* Renege 'needed' bytes from the reassembly queue. */
 static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed)
 {
-       __u16 freed = 0;
-       __u32 tsn;
-       struct sk_buff *skb;
-       struct sctp_ulpevent *event;
-       struct sctp_tsnmap *tsnmap;
-
-       tsnmap = &ulpq->asoc->peer.tsn_map;
-
-       /* Walk backwards through the list, reneges the newest tsns. */
-       while ((skb = __skb_dequeue_tail(&ulpq->reasm)) != NULL) {
-               freed += skb_headlen(skb);
-               event = sctp_skb2event(skb);
-               tsn = event->tsn;
-
-               sctp_ulpevent_free(event);
-               sctp_tsnmap_renege(tsnmap, tsn);
-               if (freed >= needed)
-                       return freed;
-       }
-
-       return freed;
+       return sctp_ulpq_renege_list(ulpq, &ulpq->reasm, needed);
 }
 
 /* Partial deliver the first message as there is pressure on rwnd. */
@@ -978,6 +1062,7 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
                sctp_ulpq_partial_delivery(ulpq, chunk, gfp);
        }
 
+       sk_mem_reclaim(asoc->base.sk);
        return;
 }