aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2008-12-16 02:26:06 -0500
committerDavid S. Miller <davem@davemloft.net>2008-12-16 02:26:06 -0500
commit89319d3801d1d3ac29c7df1f067038986f267d29 (patch)
treea717482d5cdfe9d50bed14a2114ece905b5e88d7 /net
parenteb14f019597cd86c21a6c601d7e900f40030c2e7 (diff)
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 <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/skbuff.c73
1 files changed, 59 insertions, 14 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index b1f628741f4c..18e224af05a6 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2428,6 +2428,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2428{ 2428{
2429 struct sk_buff *segs = NULL; 2429 struct sk_buff *segs = NULL;
2430 struct sk_buff *tail = NULL; 2430 struct sk_buff *tail = NULL;
2431 struct sk_buff *fskb = skb_shinfo(skb)->frag_list;
2431 unsigned int mss = skb_shinfo(skb)->gso_size; 2432 unsigned int mss = skb_shinfo(skb)->gso_size;
2432 unsigned int doffset = skb->data - skb_mac_header(skb); 2433 unsigned int doffset = skb->data - skb_mac_header(skb);
2433 unsigned int offset = doffset; 2434 unsigned int offset = doffset;
@@ -2447,7 +2448,6 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2447 struct sk_buff *nskb; 2448 struct sk_buff *nskb;
2448 skb_frag_t *frag; 2449 skb_frag_t *frag;
2449 int hsize; 2450 int hsize;
2450 int k;
2451 int size; 2451 int size;
2452 2452
2453 len = skb->len - offset; 2453 len = skb->len - offset;
@@ -2460,9 +2460,36 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2460 if (hsize > len || !sg) 2460 if (hsize > len || !sg)
2461 hsize = len; 2461 hsize = len;
2462 2462
2463 nskb = alloc_skb(hsize + doffset + headroom, GFP_ATOMIC); 2463 if (!hsize && i >= nfrags) {
2464 if (unlikely(!nskb)) 2464 BUG_ON(fskb->len != len);
2465 goto err; 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 }
2466 2493
2467 if (segs) 2494 if (segs)
2468 tail->next = nskb; 2495 tail->next = nskb;
@@ -2473,13 +2500,15 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2473 __copy_skb_header(nskb, skb); 2500 __copy_skb_header(nskb, skb);
2474 nskb->mac_len = skb->mac_len; 2501 nskb->mac_len = skb->mac_len;
2475 2502
2476 skb_reserve(nskb, headroom);
2477 skb_reset_mac_header(nskb); 2503 skb_reset_mac_header(nskb);
2478 skb_set_network_header(nskb, skb->mac_len); 2504 skb_set_network_header(nskb, skb->mac_len);
2479 nskb->transport_header = (nskb->network_header + 2505 nskb->transport_header = (nskb->network_header +
2480 skb_network_header_len(skb)); 2506 skb_network_header_len(skb));
2481 skb_copy_from_linear_data(skb, skb_put(nskb, doffset), 2507 skb_copy_from_linear_data(skb, nskb->data, doffset);
2482 doffset); 2508
2509 if (pos >= offset + len)
2510 continue;
2511
2483 if (!sg) { 2512 if (!sg) {
2484 nskb->ip_summed = CHECKSUM_NONE; 2513 nskb->ip_summed = CHECKSUM_NONE;
2485 nskb->csum = skb_copy_and_csum_bits(skb, offset, 2514 nskb->csum = skb_copy_and_csum_bits(skb, offset,
@@ -2489,14 +2518,11 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2489 } 2518 }
2490 2519
2491 frag = skb_shinfo(nskb)->frags; 2520 frag = skb_shinfo(nskb)->frags;
2492 k = 0;
2493 2521
2494 skb_copy_from_linear_data_offset(skb, offset, 2522 skb_copy_from_linear_data_offset(skb, offset,
2495 skb_put(nskb, hsize), hsize); 2523 skb_put(nskb, hsize), hsize);
2496 2524
2497 while (pos < offset + len) { 2525 while (pos < offset + len && i < nfrags) {
2498 BUG_ON(i >= nfrags);
2499
2500 *frag = skb_shinfo(skb)->frags[i]; 2526 *frag = skb_shinfo(skb)->frags[i];
2501 get_page(frag->page); 2527 get_page(frag->page);
2502 size = frag->size; 2528 size = frag->size;
@@ -2506,20 +2532,39 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2506 frag->size -= offset - pos; 2532 frag->size -= offset - pos;
2507 } 2533 }
2508 2534
2509 k++; 2535 skb_shinfo(nskb)->nr_frags++;
2510 2536
2511 if (pos + size <= offset + len) { 2537 if (pos + size <= offset + len) {
2512 i++; 2538 i++;
2513 pos += size; 2539 pos += size;
2514 } else { 2540 } else {
2515 frag->size -= pos + size - (offset + len); 2541 frag->size -= pos + size - (offset + len);
2516 break; 2542 goto skip_fraglist;
2517 } 2543 }
2518 2544
2519 frag++; 2545 frag++;
2520 } 2546 }
2521 2547
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
2567skip_fraglist:
2523 nskb->data_len = len - hsize; 2568 nskb->data_len = len - hsize;
2524 nskb->len += nskb->data_len; 2569 nskb->len += nskb->data_len;
2525 nskb->truesize += nskb->data_len; 2570 nskb->truesize += nskb->data_len;