[SCTP]: Fix receive buffer accounting.
authorVlad Yasevich <vladislav.yasevich@hp.com>
Tue, 10 Oct 2006 04:34:04 +0000 (21:34 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Thu, 12 Oct 2006 06:59:44 +0000 (23:59 -0700)
When doing receiver buffer accounting, we always used skb->truesize.
This is problematic when processing bundled DATA chunks because for
every DATA chunk that could be small part of one large skb, we would
charge the size of the entire skb.  The new approach is to store the
size of the DATA chunk we are accounting for in the sctp_ulpevent
structure and use that stored value for accounting.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/sctp.h
include/net/sctp/ulpevent.h
net/sctp/socket.c
net/sctp/ulpevent.c
net/sctp/ulpqueue.c

index ee68a3124076554d08d3dcc45cf96fc801834253..764e3af5be9340c04b303b82416d29c6bbbc5e8e 100644 (file)
@@ -139,6 +139,7 @@ int sctp_inet_listen(struct socket *sock, int backlog);
 void sctp_write_space(struct sock *sk);
 unsigned int sctp_poll(struct file *file, struct socket *sock,
                poll_table *wait);
+void sctp_sock_rfree(struct sk_buff *skb);
 
 /*
  * sctp/primitive.c
@@ -444,6 +445,19 @@ static inline struct list_head *sctp_list_dequeue(struct list_head *list)
        return result;
 }
 
+/* SCTP version of skb_set_owner_r.  We need this one because
+ * of the way we have to do receive buffer accounting on bundled
+ * chunks.
+ */
+static inline void sctp_skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
+{
+       struct sctp_ulpevent *event = sctp_skb2event(skb);
+
+       skb->sk = sk;
+       skb->destructor = sctp_sock_rfree;
+       atomic_add(event->rmem_len, &sk->sk_rmem_alloc);
+}
+
 /* Tests if the list has one and only one entry. */
 static inline int sctp_list_single_entry(struct list_head *head)
 {
index 6c40cfc4832d4a8beb136b8944fb05a7fad9cd68..1a4ddc1ec7d24cfd9a6b5acfe090c430a40a1502 100644 (file)
@@ -63,6 +63,7 @@ struct sctp_ulpevent {
        __u32 cumtsn;
        int msg_flags;
        int iif;
+       unsigned int rmem_len;
 };
 
 /* Retrieve the skb this event sits inside of. */
index 3fe906d6506982f9e1c7a0bdcf6e67b6d2772d58..9deec43911871c1107a17e764fd2d8a7ffffb361 100644 (file)
@@ -5362,6 +5362,20 @@ static void sctp_wfree(struct sk_buff *skb)
        sctp_association_put(asoc);
 }
 
+/* Do accounting for the receive space on the socket.
+ * Accounting for the association is done in ulpevent.c
+ * We set this as a destructor for the cloned data skbs so that
+ * accounting is done at the correct time.
+ */
+void sctp_sock_rfree(struct sk_buff *skb)
+{
+       struct sock *sk = skb->sk;
+       struct sctp_ulpevent *event = sctp_skb2event(skb);
+
+       atomic_sub(event->rmem_len, &sk->sk_rmem_alloc);
+}
+
+
 /* Helper function to wait for space in the sndbuf.  */
 static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
                                size_t msg_len)
@@ -5634,10 +5648,10 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
                event = sctp_skb2event(skb);
                if (event->asoc == assoc) {
-                       sock_rfree(skb);
+                       sctp_sock_rfree(skb);
                        __skb_unlink(skb, &oldsk->sk_receive_queue);
                        __skb_queue_tail(&newsk->sk_receive_queue, skb);
-                       skb_set_owner_r(skb, newsk);
+                       sctp_skb_set_owner_r(skb, newsk);
                }
        }
 
@@ -5665,10 +5679,10 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
                sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
                        event = sctp_skb2event(skb);
                        if (event->asoc == assoc) {
-                               sock_rfree(skb);
+                               sctp_sock_rfree(skb);
                                __skb_unlink(skb, &oldsp->pd_lobby);
                                __skb_queue_tail(queue, skb);
-                               skb_set_owner_r(skb, newsk);
+                               sctp_skb_set_owner_r(skb, newsk);
                        }
                }
 
index ee236784a6bb91ea8bbf3787b776aabf65971ae9..a015283a90870bcb5f9c5c85d08d5742c865813a 100644 (file)
@@ -55,10 +55,13 @@ static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event);
 
 
 /* Initialize an ULP event from an given skb.  */
-SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event, int msg_flags)
+SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event,
+                                   int msg_flags,
+                                   unsigned int len)
 {
        memset(event, 0, sizeof(struct sctp_ulpevent));
        event->msg_flags = msg_flags;
+       event->rmem_len = len;
 }
 
 /* Create a new sctp_ulpevent.  */
@@ -73,7 +76,7 @@ SCTP_STATIC struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags,
                goto fail;
 
        event = sctp_skb2event(skb);
-       sctp_ulpevent_init(event, msg_flags);
+       sctp_ulpevent_init(event, msg_flags, skb->truesize);
 
        return event;
 
@@ -101,17 +104,16 @@ static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
        sctp_association_hold((struct sctp_association *)asoc);
        skb = sctp_event2skb(event);
        event->asoc = (struct sctp_association *)asoc;
-       atomic_add(skb->truesize, &event->asoc->rmem_alloc);
-       skb_set_owner_r(skb, asoc->base.sk);
+       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)
 {
        struct sctp_association *asoc = event->asoc;
-       struct sk_buff *skb = sctp_event2skb(event);
 
-       atomic_sub(skb->truesize, &asoc->rmem_alloc);
+       atomic_sub(event->rmem_len, &asoc->rmem_alloc);
        sctp_association_put(asoc);
 }
 
@@ -372,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));
@@ -464,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));
@@ -682,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);
 
index 575e556aeb3eb58c30dd80f86c6158defb3c664f..e1d144275f97aab998d4edc5155e608a316945e9 100644 (file)
@@ -309,7 +309,7 @@ static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *qu
                        if (!new)
                                return NULL;    /* try again later */
 
-                       new->sk = f_frag->sk;
+                       sctp_skb_set_owner_r(new, f_frag->sk);
 
                        skb_shinfo(new)->frag_list = pos;
                } else