hwmon: tegra: tsensor: improve counter accuracy
[linux-2.6.git] / net / sctp / ulpevent.c
index bfecb35..8a84017 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,14 @@
  *
  * These functions manipulate an sctp event.   The struct ulpevent is used
  * to carry notifications and data 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.
@@ -42,6 +43,7 @@
  * be incorporated into the next SCTP release.
  */
 
+#include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/skbuff.h>
 #include <net/sctp/structs.h>
@@ -205,7 +207,7 @@ struct sctp_ulpevent  *sctp_ulpevent_make_assoc_change(
         * This field is the total length of the notification data, including
         * the notification header.
         */
-       sac->sac_length = sizeof(struct sctp_assoc_change);
+       sac->sac_length = skb->len;
 
        /* Socket Extensions for SCTP
         * 5.3.1.1 SCTP_ASSOC_CHANGE
@@ -552,7 +554,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
        memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo));
 
        /* Per TSVWG discussion with Randy. Allow the application to
-        * ressemble a fragmented message.
+        * reassemble a fragmented message.
         */
        ssf->ssf_info.sinfo_flags = chunk->chunk_hdr->flags;
 
@@ -685,12 +687,37 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
        struct sctp_ulpevent *event = NULL;
        struct sk_buff *skb;
        size_t padding, len;
+       int rx_count;
+
+       /*
+        * check to see if we need to make space for this
+        * new skb, expand the rcvbuffer if needed, or drop
+        * the frame
+        */
+       if (asoc->ep->rcvbuf_policy)
+               rx_count = atomic_read(&asoc->rmem_alloc);
+       else
+               rx_count = atomic_read(&asoc->base.sk->sk_rmem_alloc);
+
+       if (rx_count >= asoc->base.sk->sk_rcvbuf) {
+
+               if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) ||
+                   (!sk_rmem_schedule(asoc->base.sk, chunk->skb->truesize)))
+                       goto fail;
+       }
 
        /* Clone the original skb, sharing the data.  */
        skb = skb_clone(chunk->skb, gfp);
        if (!skb)
                goto fail;
 
+       /* Now that all memory allocations for this chunk succeeded, we
+        * can mark it as received so the tsn_map is updated correctly.
+        */
+       if (sctp_tsnmap_mark(&asoc->peer.tsn_map,
+                            ntohl(chunk->subh.data_hdr->tsn)))
+               goto fail_mark;
+
        /* First calculate the padding, so we don't inadvertently
         * pass up the wrong length to the user.
         *
@@ -731,8 +758,12 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
        event->msg_flags |= chunk->chunk_hdr->flags;
        event->iif = sctp_chunk_iif(chunk);
 
-fail:
        return event;
+
+fail_mark:
+       kfree_skb(skb);
+fail:
+       return NULL;
 }
 
 /* Create a partial delivery related event.
@@ -795,6 +826,71 @@ fail:
        return NULL;
 }
 
+struct sctp_ulpevent *sctp_ulpevent_make_authkey(
+       const struct sctp_association *asoc, __u16 key_id,
+       __u32 indication, gfp_t gfp)
+{
+       struct sctp_ulpevent *event;
+       struct sctp_authkey_event *ak;
+       struct sk_buff *skb;
+
+       event = sctp_ulpevent_new(sizeof(struct sctp_authkey_event),
+                                 MSG_NOTIFICATION, gfp);
+       if (!event)
+               goto fail;
+
+       skb = sctp_event2skb(event);
+       ak = (struct sctp_authkey_event *)
+               skb_put(skb, sizeof(struct sctp_authkey_event));
+
+       ak->auth_type = SCTP_AUTHENTICATION_EVENT;
+       ak->auth_flags = 0;
+       ak->auth_length = sizeof(struct sctp_authkey_event);
+
+       ak->auth_keynumber = key_id;
+       ak->auth_altkeynumber = 0;
+       ak->auth_indication = indication;
+
+       /*
+        * The association id field, holds the identifier for the association.
+        */
+       sctp_ulpevent_set_owner(event, asoc);
+       ak->auth_assoc_id = sctp_assoc2id(asoc);
+
+       return event;
+fail:
+       return NULL;
+}
+
+/*
+ * Socket Extensions for SCTP
+ * 6.3.10. SCTP_SENDER_DRY_EVENT
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_sender_dry_event(
+       const struct sctp_association *asoc, gfp_t gfp)
+{
+       struct sctp_ulpevent *event;
+       struct sctp_sender_dry_event *sdry;
+       struct sk_buff *skb;
+
+       event = sctp_ulpevent_new(sizeof(struct sctp_sender_dry_event),
+                                 MSG_NOTIFICATION, gfp);
+       if (!event)
+               return NULL;
+
+       skb = sctp_event2skb(event);
+       sdry = (struct sctp_sender_dry_event *)
+               skb_put(skb, sizeof(struct sctp_sender_dry_event));
+
+       sdry->sender_dry_type = SCTP_SENDER_DRY_EVENT;
+       sdry->sender_dry_flags = 0;
+       sdry->sender_dry_length = sizeof(struct sctp_sender_dry_event);
+       sctp_ulpevent_set_owner(event, asoc);
+       sdry->sender_dry_assoc_id = sctp_assoc2id(asoc);
+
+       return event;
+}
+
 /* Return the notification type, assuming this is a notification
  * event.
  */
@@ -803,7 +899,7 @@ __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event)
        union sctp_notification *notification;
        struct sk_buff *skb;
 
-       skb = sctp_event2skb((struct sctp_ulpevent *)event);
+       skb = sctp_event2skb(event);
        notification = (union sctp_notification *) skb->data;
        return notification->sn_header.sn_type;
 }
@@ -909,9 +1005,8 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
         * In general, the skb passed from IP can have only 1 level of
         * fragments. But we allow multiple levels of fragments.
         */
-       for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+       skb_walk_frags(skb, frag)
                sctp_ulpevent_receive_data(sctp_skb2event(frag), asoc);
-       }
 }
 
 /* Do accounting for bytes just read by user and release the references to
@@ -936,7 +1031,7 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
                goto done;
 
        /* Don't forget the fragments. */
-       for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+       skb_walk_frags(skb, frag) {
                /* NOTE:  skb_shinfos are recursive. Although IP returns
                 * skb's with only 1 level of fragments, SCTP reassembly can
                 * increase the levels.
@@ -959,7 +1054,7 @@ static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event)
                goto done;
 
        /* Don't forget the fragments. */
-       for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+       skb_walk_frags(skb, frag) {
                /* NOTE:  skb_shinfos are recursive. Although IP returns
                 * skb's with only 1 level of fragments, SCTP reassembly can
                 * increase the levels.
@@ -986,9 +1081,19 @@ void sctp_ulpevent_free(struct sctp_ulpevent *event)
 }
 
 /* Purge the skb lists holding ulpevents. */
-void sctp_queue_purge_ulpevents(struct sk_buff_head *list)
+unsigned int sctp_queue_purge_ulpevents(struct sk_buff_head *list)
 {
        struct sk_buff *skb;
-       while ((skb = skb_dequeue(list)) != NULL)
-               sctp_ulpevent_free(sctp_skb2event(skb));
+       unsigned int data_unread = 0;
+
+       while ((skb = skb_dequeue(list)) != NULL) {
+               struct sctp_ulpevent *event = sctp_skb2event(skb);
+
+               if (!sctp_ulpevent_is_notification(event))
+                       data_unread += skb->len;
+
+               sctp_ulpevent_free(event);
+       }
+
+       return data_unread;
 }