Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 66284
b: refs/heads/master
c: 4d93df0
h: refs/heads/master
v: v3
  • Loading branch information
Neil Horman authored and David S. Miller committed Oct 10, 2007
1 parent 6dd2f6c commit 1333e88
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 69 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 13c99b248f06e0b71d925f162d8e3b0084886a21
refs/heads/master: 4d93df0abd50b9c9e2d4561439a1a1d21ec5e68f
5 changes: 5 additions & 0 deletions trunk/include/net/sctp/sctp.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,11 @@ static inline void sctp_skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
skb->sk = sk;
skb->destructor = sctp_sock_rfree;
atomic_add(event->rmem_len, &sk->sk_rmem_alloc);
/*
* This mimics the behavior of
* sk_stream_set_owner_r
*/
sk->sk_forward_alloc -= event->rmem_len;
}

/* Tests if the list has one and only one entry. */
Expand Down
1 change: 1 addition & 0 deletions trunk/net/sctp/endpointola.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,

/* Use SCTP specific send buffer space queues. */
ep->sndbuf_policy = sctp_sndbuf_policy;

sk->sk_write_space = sctp_write_space;
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);

Expand Down
32 changes: 32 additions & 0 deletions trunk/net/sctp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/seq_file.h>
#include <linux/bootmem.h>
#include <net/protocol.h>
#include <net/ip.h>
#include <net/ipv6.h>
Expand Down Expand Up @@ -82,6 +83,10 @@ static struct sctp_af *sctp_af_v6_specific;
struct kmem_cache *sctp_chunk_cachep __read_mostly;
struct kmem_cache *sctp_bucket_cachep __read_mostly;

extern int sysctl_sctp_mem[3];
extern int sysctl_sctp_rmem[3];
extern int sysctl_sctp_wmem[3];

/* Return the address of the control sock. */
struct sock *sctp_get_ctl_sock(void)
{
Expand Down Expand Up @@ -987,6 +992,8 @@ SCTP_STATIC __init int sctp_init(void)
int i;
int status = -EINVAL;
unsigned long goal;
unsigned long limit;
int max_share;
int order;

/* SCTP_DEBUG sanity check. */
Expand Down Expand Up @@ -1077,6 +1084,31 @@ SCTP_STATIC __init int sctp_init(void)
/* Initialize handle used for association ids. */
idr_init(&sctp_assocs_id);

/* Set the pressure threshold to be a fraction of global memory that
* is up to 1/2 at 256 MB, decreasing toward zero with the amount of
* memory, with a floor of 128 pages.
* Note this initalizes the data in sctpv6_prot too
* Unabashedly stolen from tcp_init
*/
limit = min(num_physpages, 1UL<<(28-PAGE_SHIFT)) >> (20-PAGE_SHIFT);
limit = (limit * (num_physpages >> (20-PAGE_SHIFT))) >> (PAGE_SHIFT-11);
limit = max(limit, 128UL);
sysctl_sctp_mem[0] = limit / 4 * 3;
sysctl_sctp_mem[1] = limit;
sysctl_sctp_mem[2] = sysctl_sctp_mem[0] * 2;

/* Set per-socket limits to no more than 1/128 the pressure threshold*/
limit = (sysctl_sctp_mem[1]) << (PAGE_SHIFT - 7);
max_share = min(4UL*1024*1024, limit);

sysctl_sctp_rmem[0] = PAGE_SIZE; /* give each asoc 1 page min */
sysctl_sctp_rmem[1] = (1500 *(sizeof(struct sk_buff) + 1));
sysctl_sctp_rmem[2] = max(sysctl_sctp_rmem[1], max_share);

sysctl_sctp_wmem[0] = SK_STREAM_MEM_QUANTUM;
sysctl_sctp_wmem[1] = 16*1024;
sysctl_sctp_wmem[2] = max(64*1024, max_share);

/* Size and allocate the association hash table.
* The methodology is similar to that of the tcp hash tables.
*/
Expand Down
74 changes: 18 additions & 56 deletions trunk/net/sctp/sm_statefuns.c
Original file line number Diff line number Diff line change
Expand Up @@ -5428,10 +5428,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
sctp_verb_t deliver;
int tmp;
__u32 tsn;
int account_value;
struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
struct sock *sk = asoc->base.sk;
int rcvbuf_over = 0;

data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data;
skb_pull(chunk->skb, sizeof(sctp_datahdr_t));
Expand All @@ -5441,48 +5439,6 @@ static int sctp_eat_data(const struct sctp_association *asoc,

/* ASSERT: Now skb->data is really the user data. */

/*
* If we are established, and we have used up our receive buffer
* memory, think about droping the frame.
* Note that we have an opportunity to improve performance here.
* If we accept one chunk from an skbuff, we have to keep all the
* memory of that skbuff around until the chunk is read into user
* space. Therefore, once we accept 1 chunk we may as well accept all
* remaining chunks in the skbuff. The data_accepted flag helps us do
* that.
*/
if ((asoc->state == SCTP_STATE_ESTABLISHED) && (!chunk->data_accepted)) {
/*
* If the receive buffer policy is 1, then each
* association can allocate up to sk_rcvbuf bytes
* otherwise, all the associations in aggregate
* may allocate up to sk_rcvbuf bytes
*/
if (asoc->ep->rcvbuf_policy)
account_value = atomic_read(&asoc->rmem_alloc);
else
account_value = atomic_read(&sk->sk_rmem_alloc);
if (account_value > sk->sk_rcvbuf) {
/*
* We need to make forward progress, even when we are
* under memory pressure, so we always allow the
* next tsn after the ctsn ack point to be accepted.
* This lets us avoid deadlocks in which we have to
* drop frames that would otherwise let us drain the
* receive queue.
*/
if ((sctp_tsnmap_get_ctsn(map) + 1) != tsn)
return SCTP_IERROR_IGNORE_TSN;

/*
* We're going to accept the frame but we should renege
* to make space for it. This will send us down that
* path later in this function.
*/
rcvbuf_over = 1;
}
}

/* Process ECN based congestion.
*
* Since the chunk structure is reused for all chunks within
Expand Down Expand Up @@ -5542,18 +5498,9 @@ static int sctp_eat_data(const struct sctp_association *asoc,
* seems a bit troublesome in that frag_point varies based on
* PMTU. In cases, such as loopback, this might be a rather
* large spill over.
* NOTE: If we have a full receive buffer here, we only renege if
* our receiver can still make progress without the tsn being
* received. We do this because in the event that the associations
* receive queue is empty we are filling a leading gap, and since
* reneging moves the gap to the end of the tsn stream, we are likely
* to stall again very shortly. Avoiding the renege when we fill a
* leading gap is a good heuristic for avoiding such steady state
* stalls.
*/
if (!asoc->rwnd || asoc->rwnd_over ||
(datalen > asoc->rwnd + asoc->frag_point) ||
(rcvbuf_over && (!skb_queue_len(&sk->sk_receive_queue)))) {
*/
if ((!chunk->data_accepted) && (!asoc->rwnd || asoc->rwnd_over ||
(datalen > asoc->rwnd + asoc->frag_point))) {

/* If this is the next TSN, consider reneging to make
* room. Note: Playing nice with a confused sender. A
Expand All @@ -5573,6 +5520,21 @@ static int sctp_eat_data(const struct sctp_association *asoc,
}
}

/*
* Also try to renege to limit our memory usage in the event that
* we are under memory pressure
* If we can't renege, don't worry about it, the sk_stream_rmem_schedule
* in sctp_ulpevent_make_rcvmsg will drop the frame if we grow our
* memory usage too much
*/
if (*sk->sk_prot_creator->memory_pressure) {
if (sctp_tsnmap_has_gap(map) &&
(sctp_tsnmap_get_ctsn(map) + 1) == tsn) {
SCTP_DEBUG_PRINTK("Under Pressure! Reneging for tsn:%u\n", tsn);
deliver = SCTP_CMD_RENEGE;
}
}

/*
* Section 3.3.10.9 No User Data (9)
*
Expand Down
69 changes: 57 additions & 12 deletions trunk/net/sctp/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,23 +107,42 @@ static void sctp_sock_migrate(struct sock *, struct sock *,
struct sctp_association *, sctp_socket_type_t);
static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;

extern struct kmem_cache *sctp_bucket_cachep;
extern int sysctl_sctp_mem[3];
extern int sysctl_sctp_rmem[3];
extern int sysctl_sctp_wmem[3];

int sctp_memory_pressure;
atomic_t sctp_memory_allocated;
atomic_t sctp_sockets_allocated;

static void sctp_enter_memory_pressure(void)
{
sctp_memory_pressure = 1;
}


/* Get the sndbuf space available at the time on the association. */
static inline int sctp_wspace(struct sctp_association *asoc)
{
struct sock *sk = asoc->base.sk;
int amt = 0;
int amt;

if (asoc->ep->sndbuf_policy) {
/* make sure that no association uses more than sk_sndbuf */
amt = sk->sk_sndbuf - asoc->sndbuf_used;
if (asoc->ep->sndbuf_policy)
amt = asoc->sndbuf_used;
else
amt = atomic_read(&asoc->base.sk->sk_wmem_alloc);

if (amt >= asoc->base.sk->sk_sndbuf) {
if (asoc->base.sk->sk_userlocks & SOCK_SNDBUF_LOCK)
amt = 0;
else {
amt = sk_stream_wspace(asoc->base.sk);
if (amt < 0)
amt = 0;
}
} else {
/* do socket level accounting */
amt = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
amt = asoc->base.sk->sk_sndbuf - amt;
}

if (amt < 0)
amt = 0;

return amt;
}

Expand Down Expand Up @@ -155,6 +174,7 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
sizeof(struct sctp_chunk);

atomic_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
sk_charge_skb(sk, chunk->skb);
}

/* Verify that this is a valid address. */
Expand Down Expand Up @@ -3293,6 +3313,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp->hmac = NULL;

SCTP_DBG_OBJCNT_INC(sock);
atomic_inc(&sctp_sockets_allocated);
return 0;
}

Expand All @@ -3306,7 +3327,7 @@ SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
/* Release our hold on the endpoint. */
ep = sctp_sk(sk)->ep;
sctp_endpoint_free(ep);

atomic_dec(&sctp_sockets_allocated);
return 0;
}

Expand Down Expand Up @@ -5720,6 +5741,12 @@ static void sctp_wfree(struct sk_buff *skb)

atomic_sub(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);

/*
* This undoes what is done via sk_charge_skb
*/
sk->sk_wmem_queued -= skb->truesize;
sk->sk_forward_alloc += skb->truesize;

sock_wfree(skb);
__sctp_write_space(asoc);

Expand All @@ -5737,6 +5764,11 @@ void sctp_sock_rfree(struct sk_buff *skb)
struct sctp_ulpevent *event = sctp_skb2event(skb);

atomic_sub(event->rmem_len, &sk->sk_rmem_alloc);

/*
* Mimic the behavior of sk_stream_rfree
*/
sk->sk_forward_alloc += event->rmem_len;
}


Expand Down Expand Up @@ -6126,6 +6158,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
sctp_release_sock(newsk);
}


/* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = {
.name = "SCTP",
Expand All @@ -6148,6 +6181,12 @@ struct proto sctp_prot = {
.unhash = sctp_unhash,
.get_port = sctp_get_port,
.obj_size = sizeof(struct sctp_sock),
.sysctl_mem = sysctl_sctp_mem,
.sysctl_rmem = sysctl_sctp_rmem,
.sysctl_wmem = sysctl_sctp_wmem,
.memory_pressure = &sctp_memory_pressure,
.enter_memory_pressure = sctp_enter_memory_pressure,
.memory_allocated = &sctp_memory_allocated,
};

#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
Expand All @@ -6172,5 +6211,11 @@ struct proto sctpv6_prot = {
.unhash = sctp_unhash,
.get_port = sctp_get_port,
.obj_size = sizeof(struct sctp6_sock),
.sysctl_mem = sysctl_sctp_mem,
.sysctl_rmem = sysctl_sctp_rmem,
.sysctl_wmem = sysctl_sctp_wmem,
.memory_pressure = &sctp_memory_pressure,
.enter_memory_pressure = sctp_enter_memory_pressure,
.memory_allocated = &sctp_memory_allocated,
};
#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
33 changes: 33 additions & 0 deletions trunk/net/sctp/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ static int int_max = INT_MAX;
static long sack_timer_min = 1;
static long sack_timer_max = 500;

int sysctl_sctp_mem[3];
int sysctl_sctp_rmem[3];
int sysctl_sctp_wmem[3];

/*
* per assoc memory limitationf for sends
*/
int sysctl_sctp_wmem[3];

static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_RTO_INITIAL,
Expand Down Expand Up @@ -226,6 +235,30 @@ static ctl_table sctp_table[] = {
.extra1 = &sack_timer_min,
.extra2 = &sack_timer_max,
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "sctp_mem",
.data = &sysctl_sctp_mem,
.maxlen = sizeof(sysctl_sctp_mem),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "sctp_rmem",
.data = &sysctl_sctp_rmem,
.maxlen = sizeof(sysctl_sctp_rmem),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "sctp_wmem",
.data = &sysctl_sctp_wmem,
.maxlen = sizeof(sysctl_sctp_wmem),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{ .ctl_name = 0 }
};

Expand Down
18 changes: 18 additions & 0 deletions trunk/net/sctp/ulpevent.c
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,24 @@ 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_stream_rmem_schedule(asoc->base.sk, chunk->skb)))
goto fail;
}

/* Clone the original skb, sharing the data. */
skb = skb_clone(chunk->skb, gfp);
Expand Down
Loading

0 comments on commit 1333e88

Please sign in to comment.