From bec3cfdca36bf43cfa3751ad7b56db1a307e0760 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 3 Oct 2014 20:59:19 -0700 Subject: [PATCH] net: skb_segment() provides list head and tail Its unfortunate we have to walk again skb list to find the tail after segmentation, even if data is probably hot in cpu caches. skb_segment() can store the tail of the list into segs->prev, and validate_xmit_skb_list() can immediately get the tail. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 27 +++++++++++++++------------ net/core/skbuff.c | 5 +++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 1a90530f83ff..7d5691cc1f47 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2724,22 +2724,25 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d { struct sk_buff *next, *head = NULL, *tail; - while (skb) { + for (; skb != NULL; skb = next) { next = skb->next; skb->next = NULL; + + /* in case skb wont be segmented, point to itself */ + skb->prev = skb; + skb = validate_xmit_skb(skb, dev); - if (skb) { - struct sk_buff *end = skb; + if (!skb) + continue; - while (end->next) - end = end->next; - if (!head) - head = skb; - else - tail->next = skb; - tail = end; - } - skb = next; + if (!head) + head = skb; + else + tail->next = skb; + /* If skb was segmented, skb->prev points to + * the last segment. If not, it still contains skb. + */ + tail = skb->prev; } return head; } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 9a423e2c5766..7b3df0d518ab 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3083,6 +3083,11 @@ perform_csum_check: } } while ((offset += len) < head_skb->len); + /* Some callers want to get the end of the list. + * Put it in segs->prev to avoid walking the list. + * (see validate_xmit_skb_list() for example) + */ + segs->prev = tail; return segs; err: -- 2.11.0