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)
committerDavid S. Miller <davem@davemloft.net>
Tue, 27 Apr 2010 22:13:20 +0000 (15:13 -0700)
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>
include/net/sock.h
net/core/sock.c
net/ipv4/udp.c
net/ipv6/udp.c
net/sctp/socket.c

index 07822280d9536d2d0429f1784a599773c9081f73..cf12b1e61fa6062de8e7d421f6dd2404c87d4c05 100644 (file)
@@ -256,7 +256,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;
@@ -608,10 +607,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 58ebd146ce5ae95a4b510e37af48a927f95b0da2..51041759517e2dc461134471ded4306f5e3eca6b 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 fa3d2874db41283c7f6a3c85acfaa18e8094158a..63eb56b2d873e1455b8e7e45c2aaf6b22d8650a9 100644 (file)
@@ -1372,6 +1372,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 2850e35cee3d5ce65f132f4c18c424dd87244048..3ead20ad9d07b69a74ca1029bad44132079d2194 100644 (file)
@@ -584,6 +584,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);
@@ -759,6 +763,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 f34adcca8a8ce042e85ec69e972c2e792251a640..13d8229f3a9c58a5ba7705b76119ca5f96e81c5d 100644 (file)
@@ -3721,9 +3721,6 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        SCTP_DBG_OBJCNT_INC(sock);
        percpu_counter_inc(&sctp_sockets_allocated);
 
-       /* Set socket backlog limit. */
-       sk->sk_backlog.limit = sysctl_sctp_rmem[1];
-
        local_bh_disable();
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
        local_bh_enable();