fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / sctp / ulpevent.c
index e804d6e..445e07a 100644 (file)
 static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
                                       struct sctp_association *asoc);
 static void sctp_ulpevent_release_data(struct sctp_ulpevent *event);
+static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event);
 
-/* Stub skb destructor.  */
-static void sctp_stub_rfree(struct sk_buff *skb)
+
+/* Initialize an ULP event from an given skb.  */
+SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event,
+                                   int msg_flags,
+                                   unsigned int len)
 {
-/* WARNING:  This function is just a warning not to use the
- * skb destructor.  If the skb is shared, we may get the destructor
- * callback on some processor that does not own the sock_lock.  This
- * was occuring with PACKET socket applications that were monitoring
- * our skbs.   We can't take the sock_lock, because we can't risk
- * recursing if we do really own the sock lock.  Instead, do all
- * of our rwnd manipulation while we own the sock_lock outright.
- */
+       memset(event, 0, sizeof(struct sctp_ulpevent));
+       event->msg_flags = msg_flags;
+       event->rmem_len = len;
 }
 
 /* Create a new sctp_ulpevent.  */
-struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, int gfp)
+SCTP_STATIC struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags,
+                                                   gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sk_buff *skb;
@@ -76,7 +76,7 @@ struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, int gfp)
                goto fail;
 
        event = sctp_skb2event(skb);
-       sctp_ulpevent_init(event, msg_flags);
+       sctp_ulpevent_init(event, msg_flags, skb->truesize);
 
        return event;
 
@@ -84,13 +84,6 @@ fail:
        return NULL;
 }
 
-/* Initialize an ULP event from an given skb.  */
-void sctp_ulpevent_init(struct sctp_ulpevent *event, int msg_flags)
-{
-       memset(event, 0, sizeof(struct sctp_ulpevent));
-       event->msg_flags = msg_flags;
-}
-
 /* Is this a MSG_NOTIFICATION?  */
 int sctp_ulpevent_is_notification(const struct sctp_ulpevent *event)
 {
@@ -110,15 +103,18 @@ static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
         */
        sctp_association_hold((struct sctp_association *)asoc);
        skb = sctp_event2skb(event);
-       skb->sk = asoc->base.sk;
        event->asoc = (struct sctp_association *)asoc;
-       skb->destructor = sctp_stub_rfree;
+       atomic_add(event->rmem_len, &event->asoc->rmem_alloc);
+       sctp_skb_set_owner_r(skb, asoc->base.sk);
 }
 
 /* A simple destructor to give up the reference to the association. */
 static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event)
 {
-       sctp_association_put(event->asoc);
+       struct sctp_association *asoc = event->asoc;
+
+       atomic_sub(event->rmem_len, &asoc->rmem_alloc);
+       sctp_association_put(asoc);
 }
 
 /* Create and initialize an SCTP_ASSOC_CHANGE event.
@@ -135,7 +131,7 @@ static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event)
 struct sctp_ulpevent  *sctp_ulpevent_make_assoc_change(
        const struct sctp_association *asoc,
        __u16 flags, __u16 state, __u16 error, __u16 outbound,
-       __u16 inbound, int gfp)
+       __u16 inbound, gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sctp_assoc_change *sac;
@@ -236,7 +232,7 @@ fail:
 struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
        const struct sctp_association *asoc,
        const struct sockaddr_storage *aaddr,
-       int flags, int state, int error, int gfp)
+       int flags, int state, int error, gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sctp_paddr_change  *spc;
@@ -349,13 +345,13 @@ fail:
  */
 struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
        const struct sctp_association *asoc, struct sctp_chunk *chunk,
-       __u16 flags, int gfp)
+       __u16 flags, gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sctp_remote_error *sre;
        struct sk_buff *skb;
        sctp_errhdr_t *ch;
-       __u16 cause;
+       __be16 cause;
        int elen;
 
        ch = (sctp_errhdr_t *)(chunk->skb->data);
@@ -378,7 +374,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
 
        /* Embed the event fields inside the cloned skb.  */
        event = sctp_skb2event(skb);
-       sctp_ulpevent_init(event, MSG_NOTIFICATION);
+       sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
 
        sre = (struct sctp_remote_error *)
                skb_push(skb, sizeof(struct sctp_remote_error));
@@ -447,7 +443,7 @@ fail:
  */
 struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
        const struct sctp_association *asoc, struct sctp_chunk *chunk,
-       __u16 flags, __u32 error, int gfp)
+       __u16 flags, __u32 error, gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sctp_send_failed *ssf;
@@ -470,7 +466,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
 
        /* Embed the event fields inside the cloned skb.  */
        event = sctp_skb2event(skb);
-       sctp_ulpevent_init(event, MSG_NOTIFICATION);
+       sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
 
        ssf = (struct sctp_send_failed *)
                skb_push(skb, sizeof(struct sctp_send_failed));
@@ -556,13 +552,13 @@ fail:
  */
 struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
        const struct sctp_association *asoc,
-       __u16 flags, int gfp)
+       __u16 flags, gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sctp_shutdown_event *sse;
        struct sk_buff *skb;
 
-       event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
+       event = sctp_ulpevent_new(sizeof(struct sctp_shutdown_event),
                                  MSG_NOTIFICATION, gfp);
        if (!event)
                goto fail;
@@ -613,6 +609,40 @@ fail:
        return NULL;
 }
 
+/* Create and initialize a SCTP_ADAPTATION_INDICATION notification.
+ *
+ * Socket Extensions for SCTP
+ * 5.3.1.6 SCTP_ADAPTATION_INDICATION
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_adaptation_indication(
+       const struct sctp_association *asoc, gfp_t gfp)
+{
+       struct sctp_ulpevent *event;
+       struct sctp_adaptation_event *sai;
+       struct sk_buff *skb;
+
+       event = sctp_ulpevent_new(sizeof(struct sctp_adaptation_event),
+                                 MSG_NOTIFICATION, gfp);
+       if (!event)
+               goto fail;
+
+       skb = sctp_event2skb(event);
+       sai = (struct sctp_adaptation_event *)
+               skb_put(skb, sizeof(struct sctp_adaptation_event));
+
+       sai->sai_type = SCTP_ADAPTATION_INDICATION;
+       sai->sai_flags = 0;
+       sai->sai_length = sizeof(struct sctp_adaptation_event);
+       sai->sai_adaptation_ind = asoc->peer.adaptation_ind;
+       sctp_ulpevent_set_owner(event, asoc);
+       sai->sai_assoc_id = sctp_assoc2id(asoc);
+
+       return event;
+
+fail:
+       return NULL;
+}
+
 /* A message has been received.  Package this message as a notification
  * to pass it to the upper layers.  Go ahead and calculate the sndrcvinfo
  * even if filtered out later.
@@ -622,7 +652,7 @@ fail:
  */
 struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
                                                struct sctp_chunk *chunk,
-                                               int gfp)
+                                               gfp_t gfp)
 {
        struct sctp_ulpevent *event = NULL;
        struct sk_buff *skb;
@@ -654,8 +684,11 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
        /* Embed the event fields inside the cloned skb.  */
        event = sctp_skb2event(skb);
 
-       /* Initialize event with flags 0.  */
-       sctp_ulpevent_init(event, 0);
+       /* Initialize event with flags 0  and correct length
+        * Since this is a clone of the original skb, only account for
+        * the data of this chunk as other chunks will be accounted separately.
+        */
+       sctp_ulpevent_init(event, 0, skb->len + sizeof(struct sk_buff));
 
        sctp_ulpevent_receive_data(event, asoc);
 
@@ -663,7 +696,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
        event->ssn = ntohs(chunk->subh.data_hdr->ssn);
        event->ppid = chunk->subh.data_hdr->ppid;
        if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
-               event->flags |= MSG_UNORDERED;
+               event->flags |= SCTP_UNORDERED;
                event->cumtsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
        }
        event->tsn = ntohl(chunk->subh.data_hdr->tsn);
@@ -678,18 +711,19 @@ fail:
  *
  * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
  *
- *   When a reciever is engaged in a partial delivery of a
- *   message this notification will be used to inidicate
+ *   When a receiver is engaged in a partial delivery of a
+ *   message this notification will be used to indicate
  *   various events.
  */
 struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
-       const struct sctp_association *asoc, __u32 indication, int gfp)
+       const struct sctp_association *asoc, __u32 indication,
+       gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sctp_pdapi_event *pd;
        struct sk_buff *skb;
 
-       event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
+       event = sctp_ulpevent_new(sizeof(struct sctp_pdapi_event),
                                  MSG_NOTIFICATION, gfp);
        if (!event)
                goto fail;
@@ -788,7 +822,7 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
         *
         * recvmsg() flags:
         *
-        * MSG_UNORDERED - This flag is present when the message was sent
+        * SCTP_UNORDERED - This flag is present when the message was sent
         *                 non-ordered.
         */
        sinfo.sinfo_flags = event->flags;
@@ -803,7 +837,7 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
         * This field will hold the current cumulative TSN as
         * known by the underlying SCTP layer.  Note this field is
         * ignored when sending and only valid for a receive
-        * operation when sinfo_flags are set to MSG_UNORDERED.
+        * operation when sinfo_flags are set to SCTP_UNORDERED.
         */
        sinfo.sinfo_cumtsn = event->cumtsn;
        /* sinfo_assoc_id: sizeof (sctp_assoc_t)
@@ -815,8 +849,10 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
         */
        sinfo.sinfo_assoc_id = sctp_assoc2id(event->asoc);
 
+       /* context value that is set via SCTP_CONTEXT socket option. */
+       sinfo.sinfo_context = event->asoc->default_rcv_context;
+
        /* These fields are not used while receiving. */
-       sinfo.sinfo_context = 0;
        sinfo.sinfo_timetolive = 0;
 
        put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV,
@@ -856,6 +892,7 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
 static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
 {
        struct sk_buff *skb, *frag;
+       unsigned int    len;
 
        /* Current stack structures assume that the rcv buffer is
         * per socket.   For UDP style sockets this is not true as
@@ -865,7 +902,30 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
         */
 
        skb = sctp_event2skb(event);
-       sctp_assoc_rwnd_increase(event->asoc, skb_headlen(skb));
+       len = skb->len;
+
+       if (!skb->data_len)
+               goto done;
+
+       /* Don't forget the fragments. */
+       for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+               /* NOTE:  skb_shinfos are recursive. Although IP returns
+                * skb's with only 1 level of fragments, SCTP reassembly can
+                * increase the levels.
+                */
+               sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
+       }
+
+done:
+       sctp_assoc_rwnd_increase(event->asoc, len);
+       sctp_ulpevent_release_owner(event);
+}
+
+static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event)
+{
+       struct sk_buff *skb, *frag;
+
+       skb = sctp_event2skb(event);
 
        if (!skb->data_len)
                goto done;
@@ -876,7 +936,7 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
                 * skb's with only 1 level of fragments, SCTP reassembly can
                 * increase the levels.
                 */
-               sctp_ulpevent_release_data(sctp_skb2event(frag));
+               sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
        }
 
 done:
@@ -886,7 +946,6 @@ done:
 /* Free a ulpevent that has an owner.  It includes releasing the reference
  * to the owner, updating the rwnd in case of a DATA event and freeing the
  * skb.
- * See comments in sctp_stub_rfree().
  */
 void sctp_ulpevent_free(struct sctp_ulpevent *event)
 {