diff options
author | Eric Dumazet <edumazet@google.com> | 2014-09-27 12:50:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-28 16:35:43 -0400 |
commit | 971f10eca186cab238c49daa91f703c5a001b0b1 (patch) | |
tree | 0d769e6155899c89ae95c3e31c79ce011eb96a39 /net/ipv4/tcp_ipv4.c | |
parent | a224772db8420ecb7ce91a9ba5d535ee3a50d982 (diff) |
tcp: better TCP_SKB_CB layout to reduce cache line misses
TCP maintains lists of skb in write queue, and in receive queues
(in order and out of order queues)
Scanning these lists both in input and output path usually requires
access to skb->next, TCP_SKB_CB(skb)->seq, and TCP_SKB_CB(skb)->end_seq
These fields are currently in two different cache lines, meaning we
waste lot of memory bandwidth when these queues are big and flows
have either packet drops or packet reorders.
We can move TCP_SKB_CB(skb)->header at the end of TCP_SKB_CB, because
this header is not used in fast path. This allows TCP to search much faster
in the skb lists.
Even with regular flows, we save one cache line miss in fast path.
Thanks to Christoph Paasch for noticing we need to cleanup
skb->cb[] (IPCB/IP6CB) before entering IP stack in tx path,
and that I forgot IPCB use in tcp_v4_hnd_req() and tcp_v4_save_options().
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 19 |
1 files changed, 12 insertions, 7 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 28ab90382c01..9ce3eac02957 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -886,18 +886,16 @@ EXPORT_SYMBOL(tcp_syn_flood_action); | |||
886 | */ | 886 | */ |
887 | static struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb) | 887 | static struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb) |
888 | { | 888 | { |
889 | const struct ip_options *opt = &(IPCB(skb)->opt); | 889 | const struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt; |
890 | struct ip_options_rcu *dopt = NULL; | 890 | struct ip_options_rcu *dopt = NULL; |
891 | 891 | ||
892 | if (opt && opt->optlen) { | 892 | if (opt && opt->optlen) { |
893 | int opt_size = sizeof(*dopt) + opt->optlen; | 893 | int opt_size = sizeof(*dopt) + opt->optlen; |
894 | 894 | ||
895 | dopt = kmalloc(opt_size, GFP_ATOMIC); | 895 | dopt = kmalloc(opt_size, GFP_ATOMIC); |
896 | if (dopt) { | 896 | if (dopt && __ip_options_echo(&dopt->opt, skb, opt)) { |
897 | if (ip_options_echo(&dopt->opt, skb)) { | 897 | kfree(dopt); |
898 | kfree(dopt); | 898 | dopt = NULL; |
899 | dopt = NULL; | ||
900 | } | ||
901 | } | 899 | } |
902 | } | 900 | } |
903 | return dopt; | 901 | return dopt; |
@@ -1431,7 +1429,7 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) | |||
1431 | 1429 | ||
1432 | #ifdef CONFIG_SYN_COOKIES | 1430 | #ifdef CONFIG_SYN_COOKIES |
1433 | if (!th->syn) | 1431 | if (!th->syn) |
1434 | sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt)); | 1432 | sk = cookie_v4_check(sk, skb, &TCP_SKB_CB(skb)->header.h4.opt); |
1435 | #endif | 1433 | #endif |
1436 | return sk; | 1434 | return sk; |
1437 | } | 1435 | } |
@@ -1636,6 +1634,13 @@ int tcp_v4_rcv(struct sk_buff *skb) | |||
1636 | 1634 | ||
1637 | th = tcp_hdr(skb); | 1635 | th = tcp_hdr(skb); |
1638 | iph = ip_hdr(skb); | 1636 | iph = ip_hdr(skb); |
1637 | /* This is tricky : We move IPCB at its correct location into TCP_SKB_CB() | ||
1638 | * barrier() makes sure compiler wont play fool^Waliasing games. | ||
1639 | */ | ||
1640 | memmove(&TCP_SKB_CB(skb)->header.h4, IPCB(skb), | ||
1641 | sizeof(struct inet_skb_parm)); | ||
1642 | barrier(); | ||
1643 | |||
1639 | TCP_SKB_CB(skb)->seq = ntohl(th->seq); | 1644 | TCP_SKB_CB(skb)->seq = ntohl(th->seq); |
1640 | TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + | 1645 | TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + |
1641 | skb->len - th->doff * 4); | 1646 | skb->len - th->doff * 4); |