Skip to content

Commit 48c2afc

Browse files
edumazetgregkh
authored andcommitted
inet: frags: get rid of ipfrag_skb_cb/FRAG_CB
ip_defrag uses skb->cb[] to store the fragment offset, and unfortunately this integer is currently in a different cache line than skb->next, meaning that we use two cache lines per skb when finding the insertion point. By aliasing skb->ip_defrag_offset and skb->dev, we pack all the fields in a single cache line and save precious memory bandwidth. Note that after the fast path added by Changli Gao in commit d6bebca ("fragment: add fast path for in-order fragments") this change wont help the fast path, since we still need to access prev->len (2nd cache line), but will show great benefits when slow path is entered, since we perform a linear scan of a potentially long list. Also, note that this potential long list is an attack vector, we might consider also using an rb-tree there eventually. Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net> (cherry picked from commit bf66337) Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8291cd9 commit 48c2afc

2 files changed

Lines changed: 15 additions & 21 deletions

File tree

include/linux/skbuff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ struct sk_buff {
678678
* UDP receive path is one user.
679679
*/
680680
unsigned long dev_scratch;
681+
int ip_defrag_offset;
681682
};
682683
/*
683684
* This is the control buffer. It is free to use for every

net/ipv4/ip_fragment.c

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,6 @@
5757
*/
5858
static const char ip_frag_cache_name[] = "ip4-frags";
5959

60-
struct ipfrag_skb_cb
61-
{
62-
struct inet_skb_parm h;
63-
int offset;
64-
};
65-
66-
#define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb))
67-
6860
/* Describe an entry in the "incomplete datagrams" queue. */
6961
struct ipq {
7062
struct inet_frag_queue q;
@@ -353,13 +345,13 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
353345
* this fragment, right?
354346
*/
355347
prev = qp->q.fragments_tail;
356-
if (!prev || FRAG_CB(prev)->offset < offset) {
348+
if (!prev || prev->ip_defrag_offset < offset) {
357349
next = NULL;
358350
goto found;
359351
}
360352
prev = NULL;
361353
for (next = qp->q.fragments; next != NULL; next = next->next) {
362-
if (FRAG_CB(next)->offset >= offset)
354+
if (next->ip_defrag_offset >= offset)
363355
break; /* bingo! */
364356
prev = next;
365357
}
@@ -370,7 +362,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
370362
* any overlaps are eliminated.
371363
*/
372364
if (prev) {
373-
int i = (FRAG_CB(prev)->offset + prev->len) - offset;
365+
int i = (prev->ip_defrag_offset + prev->len) - offset;
374366

375367
if (i > 0) {
376368
offset += i;
@@ -387,8 +379,8 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
387379

388380
err = -ENOMEM;
389381

390-
while (next && FRAG_CB(next)->offset < end) {
391-
int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */
382+
while (next && next->ip_defrag_offset < end) {
383+
int i = end - next->ip_defrag_offset; /* overlap is 'i' bytes */
392384

393385
if (i < next->len) {
394386
int delta = -next->truesize;
@@ -401,7 +393,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
401393
delta += next->truesize;
402394
if (delta)
403395
add_frag_mem_limit(qp->q.net, delta);
404-
FRAG_CB(next)->offset += i;
396+
next->ip_defrag_offset += i;
405397
qp->q.meat -= i;
406398
if (next->ip_summed != CHECKSUM_UNNECESSARY)
407399
next->ip_summed = CHECKSUM_NONE;
@@ -425,7 +417,13 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
425417
}
426418
}
427419

428-
FRAG_CB(skb)->offset = offset;
420+
/* Note : skb->ip_defrag_offset and skb->dev share the same location */
421+
dev = skb->dev;
422+
if (dev)
423+
qp->iif = dev->ifindex;
424+
/* Makes sure compiler wont do silly aliasing games */
425+
barrier();
426+
skb->ip_defrag_offset = offset;
429427

430428
/* Insert this fragment in the chain of fragments. */
431429
skb->next = next;
@@ -436,11 +434,6 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
436434
else
437435
qp->q.fragments = skb;
438436

439-
dev = skb->dev;
440-
if (dev) {
441-
qp->iif = dev->ifindex;
442-
skb->dev = NULL;
443-
}
444437
qp->q.stamp = skb->tstamp;
445438
qp->q.meat += skb->len;
446439
qp->ecn |= ecn;
@@ -516,7 +509,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
516509
}
517510

518511
WARN_ON(!head);
519-
WARN_ON(FRAG_CB(head)->offset != 0);
512+
WARN_ON(head->ip_defrag_offset != 0);
520513

521514
/* Allocate a new buffer for the datagram. */
522515
ihlen = ip_hdrlen(head);

0 commit comments

Comments
 (0)