Skip to content

Commit 89319d3

Browse files
herbertxdavem330
authored andcommitted
net: Add frag_list support to skb_segment
This patch adds limited support for handling frag_list packets in skb_segment. The intention is to support GRO (Generic Receive Offload) packets which will be constructed by chaining normal packets using frag_list. As such we require all frag_list members terminate on exact MSS boundaries. This is checked using BUG_ON. As there should only be one producer in the kernel of such packets, namely GRO, this requirement should not be difficult to maintain. Signed-off-by: Herbert Xu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent eb14f01 commit 89319d3

File tree

1 file changed

+59
-14
lines changed

1 file changed

+59
-14
lines changed

net/core/skbuff.c

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,6 +2428,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
24282428
{
24292429
struct sk_buff *segs = NULL;
24302430
struct sk_buff *tail = NULL;
2431+
struct sk_buff *fskb = skb_shinfo(skb)->frag_list;
24312432
unsigned int mss = skb_shinfo(skb)->gso_size;
24322433
unsigned int doffset = skb->data - skb_mac_header(skb);
24332434
unsigned int offset = doffset;
@@ -2447,7 +2448,6 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
24472448
struct sk_buff *nskb;
24482449
skb_frag_t *frag;
24492450
int hsize;
2450-
int k;
24512451
int size;
24522452

24532453
len = skb->len - offset;
@@ -2460,9 +2460,36 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
24602460
if (hsize > len || !sg)
24612461
hsize = len;
24622462

2463-
nskb = alloc_skb(hsize + doffset + headroom, GFP_ATOMIC);
2464-
if (unlikely(!nskb))
2465-
goto err;
2463+
if (!hsize && i >= nfrags) {
2464+
BUG_ON(fskb->len != len);
2465+
2466+
pos += len;
2467+
nskb = skb_clone(fskb, GFP_ATOMIC);
2468+
fskb = fskb->next;
2469+
2470+
if (unlikely(!nskb))
2471+
goto err;
2472+
2473+
hsize = skb_end_pointer(nskb) - nskb->head;
2474+
if (skb_cow_head(nskb, doffset + headroom)) {
2475+
kfree_skb(nskb);
2476+
goto err;
2477+
}
2478+
2479+
nskb->truesize += skb_end_pointer(nskb) - nskb->head -
2480+
hsize;
2481+
skb_release_head_state(nskb);
2482+
__skb_push(nskb, doffset);
2483+
} else {
2484+
nskb = alloc_skb(hsize + doffset + headroom,
2485+
GFP_ATOMIC);
2486+
2487+
if (unlikely(!nskb))
2488+
goto err;
2489+
2490+
skb_reserve(nskb, headroom);
2491+
__skb_put(nskb, doffset);
2492+
}
24662493

24672494
if (segs)
24682495
tail->next = nskb;
@@ -2473,13 +2500,15 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
24732500
__copy_skb_header(nskb, skb);
24742501
nskb->mac_len = skb->mac_len;
24752502

2476-
skb_reserve(nskb, headroom);
24772503
skb_reset_mac_header(nskb);
24782504
skb_set_network_header(nskb, skb->mac_len);
24792505
nskb->transport_header = (nskb->network_header +
24802506
skb_network_header_len(skb));
2481-
skb_copy_from_linear_data(skb, skb_put(nskb, doffset),
2482-
doffset);
2507+
skb_copy_from_linear_data(skb, nskb->data, doffset);
2508+
2509+
if (pos >= offset + len)
2510+
continue;
2511+
24832512
if (!sg) {
24842513
nskb->ip_summed = CHECKSUM_NONE;
24852514
nskb->csum = skb_copy_and_csum_bits(skb, offset,
@@ -2489,14 +2518,11 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
24892518
}
24902519

24912520
frag = skb_shinfo(nskb)->frags;
2492-
k = 0;
24932521

24942522
skb_copy_from_linear_data_offset(skb, offset,
24952523
skb_put(nskb, hsize), hsize);
24962524

2497-
while (pos < offset + len) {
2498-
BUG_ON(i >= nfrags);
2499-
2525+
while (pos < offset + len && i < nfrags) {
25002526
*frag = skb_shinfo(skb)->frags[i];
25012527
get_page(frag->page);
25022528
size = frag->size;
@@ -2506,20 +2532,39 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
25062532
frag->size -= offset - pos;
25072533
}
25082534

2509-
k++;
2535+
skb_shinfo(nskb)->nr_frags++;
25102536

25112537
if (pos + size <= offset + len) {
25122538
i++;
25132539
pos += size;
25142540
} else {
25152541
frag->size -= pos + size - (offset + len);
2516-
break;
2542+
goto skip_fraglist;
25172543
}
25182544

25192545
frag++;
25202546
}
25212547

2522-
skb_shinfo(nskb)->nr_frags = k;
2548+
if (pos < offset + len) {
2549+
struct sk_buff *fskb2 = fskb;
2550+
2551+
BUG_ON(pos + fskb->len != offset + len);
2552+
2553+
pos += fskb->len;
2554+
fskb = fskb->next;
2555+
2556+
if (fskb2->next) {
2557+
fskb2 = skb_clone(fskb2, GFP_ATOMIC);
2558+
if (!fskb2)
2559+
goto err;
2560+
} else
2561+
skb_get(fskb2);
2562+
2563+
BUG_ON(skb_shinfo(nskb)->frag_list);
2564+
skb_shinfo(nskb)->frag_list = fskb2;
2565+
}
2566+
2567+
skip_fraglist:
25232568
nskb->data_len = len - hsize;
25242569
nskb->len += nskb->data_len;
25252570
nskb->truesize += nskb->data_len;

0 commit comments

Comments
 (0)