Skip to content

Commit 971f10e

Browse files
Eric Dumazetdavem330
Eric Dumazet
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent a224772 commit 971f10e

File tree

4 files changed

+30
-13
lines changed

4 files changed

+30
-13
lines changed

include/net/tcp.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -696,12 +696,6 @@ static inline u32 tcp_skb_timestamp(const struct sk_buff *skb)
696696
* If this grows please adjust skbuff.h:skbuff->cb[xxx] size appropriately.
697697
*/
698698
struct tcp_skb_cb {
699-
union {
700-
struct inet_skb_parm h4;
701-
#if IS_ENABLED(CONFIG_IPV6)
702-
struct inet6_skb_parm h6;
703-
#endif
704-
} header; /* For incoming frames */
705699
__u32 seq; /* Starting sequence number */
706700
__u32 end_seq; /* SEQ + FIN + SYN + datalen */
707701
__u32 tcp_tw_isn; /* isn chosen by tcp_timewait_state_process() */
@@ -720,6 +714,12 @@ struct tcp_skb_cb {
720714
__u8 ip_dsfield; /* IPv4 tos or IPv6 dsfield */
721715
/* 1 byte hole */
722716
__u32 ack_seq; /* Sequence number ACK'd */
717+
union {
718+
struct inet_skb_parm h4;
719+
#if IS_ENABLED(CONFIG_IPV6)
720+
struct inet6_skb_parm h6;
721+
#endif
722+
} header; /* For incoming frames */
723723
};
724724

725725
#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))

net/ipv4/tcp_ipv4.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -886,18 +886,16 @@ EXPORT_SYMBOL(tcp_syn_flood_action);
886886
*/
887887
static struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb)
888888
{
889-
const struct ip_options *opt = &(IPCB(skb)->opt);
889+
const struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt;
890890
struct ip_options_rcu *dopt = NULL;
891891

892892
if (opt && opt->optlen) {
893893
int opt_size = sizeof(*dopt) + opt->optlen;
894894

895895
dopt = kmalloc(opt_size, GFP_ATOMIC);
896-
if (dopt) {
897-
if (ip_options_echo(&dopt->opt, skb)) {
898-
kfree(dopt);
899-
dopt = NULL;
900-
}
896+
if (dopt && __ip_options_echo(&dopt->opt, skb, opt)) {
897+
kfree(dopt);
898+
dopt = NULL;
901899
}
902900
}
903901
return dopt;
@@ -1431,7 +1429,7 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
14311429

14321430
#ifdef CONFIG_SYN_COOKIES
14331431
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);
14351433
#endif
14361434
return sk;
14371435
}
@@ -1636,6 +1634,13 @@ int tcp_v4_rcv(struct sk_buff *skb)
16361634

16371635
th = tcp_hdr(skb);
16381636
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+
16391644
TCP_SKB_CB(skb)->seq = ntohl(th->seq);
16401645
TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
16411646
skb->len - th->doff * 4);

net/ipv4/tcp_output.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,11 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
974974

975975
/* Our usage of tstamp should remain private */
976976
skb->tstamp.tv64 = 0;
977+
978+
/* Cleanup our debris for IP stacks */
979+
memset(skb->cb, 0, max(sizeof(struct inet_skb_parm),
980+
sizeof(struct inet6_skb_parm)));
981+
977982
err = icsk->icsk_af_ops->queue_xmit(sk, skb, &inet->cork.fl);
978983

979984
if (likely(err <= 0))

net/ipv6/tcp_ipv6.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,13 @@ static int tcp_v6_rcv(struct sk_buff *skb)
14121412

14131413
th = tcp_hdr(skb);
14141414
hdr = ipv6_hdr(skb);
1415+
/* This is tricky : We move IPCB at its correct location into TCP_SKB_CB()
1416+
* barrier() makes sure compiler wont play fool^Waliasing games.
1417+
*/
1418+
memmove(&TCP_SKB_CB(skb)->header.h6, IP6CB(skb),
1419+
sizeof(struct inet6_skb_parm));
1420+
barrier();
1421+
14151422
TCP_SKB_CB(skb)->seq = ntohl(th->seq);
14161423
TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
14171424
skb->len - th->doff*4);

0 commit comments

Comments
 (0)