net: sk_add_backlog() take rmem_alloc into account
authorEric Dumazet <eric.dumazet@gmail.com>
Tue, 27 Apr 2010 22:13:20 +0000 (15:13 -0700)
committerPaul Gortmaker <paul.gortmaker@windriver.com>
Wed, 14 Mar 2012 14:58:05 +0000 (10:58 -0400)
commit c377411f2494a931ff7facdbb3a6839b1266bcf6 upstream.

Current socket backlog limit is not enough to really stop DDOS attacks,
because user thread spend many time to process a full backlog each
round, and user might crazy spin on socket lock.

We should add backlog size and receive_queue size (aka rmem_alloc) to
pace writers, and let user run without being slow down too much.

Introduce a sk_rcvqueues_full() helper, to avoid taking socket lock in
stress situations.

Under huge stress from a multiqueue/RPS enabled NIC, a single flow udp
receiver can now process ~200.000 pps (instead of ~100 pps before the
patch) on a 8 core machine.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
include/net/sock.h
net/core/sock.c
net/ipv4/udp.c
net/ipv6/udp.c
net/sctp/socket.c

index 721c7b3a7a7324a16f6e363f3092058014892e16..b365fc2597c3a48c2624808ac89479b62c77ee00 100644 (file)
@@ -255,7 +255,6 @@ struct sock {
                struct sk_buff *head;
                struct sk_buff *tail;
                int len;
-               int limit;
        } sk_backlog;
        wait_queue_head_t       *sk_sleep;
        struct dst_entry        *sk_dst_cache;
@@ -604,10 +603,20 @@ static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb)
        skb->next = NULL;
 }
 
+/*
+ * Take into account size of receive queue and backlog queue
+ */
+static inline bool sk_rcvqueues_full(const struct sock *sk, const struct sk_buff *skb)
+{
+       unsigned int qsize = sk->sk_backlog.len + atomic_read(&sk->sk_rmem_alloc);
+
+       return qsize + skb->truesize > sk->sk_rcvbuf;
+}
+
 /* The per-socket spinlock must be held here. */
 static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *skb)
 {
-       if (sk->sk_backlog.len >= max(sk->sk_backlog.limit, sk->sk_rcvbuf << 1))
+       if (sk_rcvqueues_full(sk, skb))
                return -ENOBUFS;
 
        __sk_add_backlog(sk, skb);
index cf3b9aa3f264256021092c449f4759e8267b8f07..78b708780d3004c71960b3ef28243485c1da4d01 100644 (file)
@@ -327,6 +327,10 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
 
        skb->dev = NULL;
 
+       if (sk_rcvqueues_full(sk, skb)) {
+               atomic_inc(&sk->sk_drops);
+               goto discard_and_relse;
+       }
        if (nested)
                bh_lock_sock_nested(sk);
        else
@@ -1885,7 +1889,6 @@ void sock_init_data(struct socket *sock, struct sock *sk)
        sk->sk_allocation       =       GFP_KERNEL;
        sk->sk_rcvbuf           =       sysctl_rmem_default;
        sk->sk_sndbuf           =       sysctl_wmem_default;
-       sk->sk_backlog.limit    =       sk->sk_rcvbuf << 1;
        sk->sk_state            =       TCP_CLOSE;
        sk_set_socket(sk, sock);
 
index fd510bcdc1c2b3167118fe33defb0768ef8046e6..ff6a18ea815115969c29e4f72baf852d6f0d1126 100644 (file)
@@ -1410,6 +1410,10 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                        goto drop;
        }
 
+
+       if (sk_rcvqueues_full(sk, skb))
+               goto drop;
+
        rc = 0;
 
        bh_lock_sock(sk);
index 402a61cd8ef7009bba66ce52385bc4b8dea2be89..479f9266a199bac9acc6c0f4b9fd9e1f73780f16 100644 (file)
@@ -590,6 +590,10 @@ static void flush_stack(struct sock **stack, unsigned int count,
 
                sk = stack[i];
                if (skb1) {
+                       if (sk_rcvqueues_full(sk, skb)) {
+                               kfree_skb(skb1);
+                               goto drop;
+                       }
                        bh_lock_sock(sk);
                        if (!sock_owned_by_user(sk))
                                udpv6_queue_rcv_skb(sk, skb1);
@@ -765,6 +769,10 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
 
        /* deliver */
 
+       if (sk_rcvqueues_full(sk, skb)) {
+               sock_put(sk);
+               goto discard;
+       }
        bh_lock_sock(sk);
        if (!sock_owned_by_user(sk))
                udpv6_queue_rcv_skb(sk, skb);
index 7f28df58d251c820723bb92af916771146e52d18..8375609fc423293c063bfe489d74d75cd5aaa121 100644 (file)
@@ -3720,9 +3720,6 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
 
        SCTP_DBG_OBJCNT_INC(sock);
 
-       /* Set socket backlog limit. */
-       sk->sk_backlog.limit = sysctl_sctp_rmem[1];
-
        local_bh_disable();
        percpu_counter_inc(&sctp_sockets_allocated);
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);