Skip to content

Commit

Permalink
[SCTP]: Fix receive buffer accounting.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Sridhar Samudrala <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Vlad Yasevich authored and David S. Miller committed Oct 12, 2006
1 parent 6e8c751 commit 331c4ee
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 15 deletions.
14 changes: 14 additions & 0 deletions include/net/sctp/sctp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions include/net/sctp/ulpevent.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
22 changes: 18 additions & 4 deletions net/sctp/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
}
}

Expand Down
25 changes: 15 additions & 10 deletions net/sctp/ulpevent.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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;

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion net/sctp/ulpqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 331c4ee

Please sign in to comment.