@@ -2428,6 +2428,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2428
2428
{
2429
2429
struct sk_buff * segs = NULL ;
2430
2430
struct sk_buff * tail = NULL ;
2431
+ struct sk_buff * fskb = skb_shinfo (skb )-> frag_list ;
2431
2432
unsigned int mss = skb_shinfo (skb )-> gso_size ;
2432
2433
unsigned int doffset = skb -> data - skb_mac_header (skb );
2433
2434
unsigned int offset = doffset ;
@@ -2447,7 +2448,6 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2447
2448
struct sk_buff * nskb ;
2448
2449
skb_frag_t * frag ;
2449
2450
int hsize ;
2450
- int k ;
2451
2451
int size ;
2452
2452
2453
2453
len = skb -> len - offset ;
@@ -2460,9 +2460,36 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2460
2460
if (hsize > len || !sg )
2461
2461
hsize = len ;
2462
2462
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
+ }
2466
2493
2467
2494
if (segs )
2468
2495
tail -> next = nskb ;
@@ -2473,13 +2500,15 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2473
2500
__copy_skb_header (nskb , skb );
2474
2501
nskb -> mac_len = skb -> mac_len ;
2475
2502
2476
- skb_reserve (nskb , headroom );
2477
2503
skb_reset_mac_header (nskb );
2478
2504
skb_set_network_header (nskb , skb -> mac_len );
2479
2505
nskb -> transport_header = (nskb -> network_header +
2480
2506
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
+
2483
2512
if (!sg ) {
2484
2513
nskb -> ip_summed = CHECKSUM_NONE ;
2485
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
2520
frag = skb_shinfo (nskb )-> frags ;
2492
- k = 0 ;
2493
2521
2494
2522
skb_copy_from_linear_data_offset (skb , offset ,
2495
2523
skb_put (nskb , hsize ), hsize );
2496
2524
2497
- while (pos < offset + len ) {
2498
- BUG_ON (i >= nfrags );
2499
-
2525
+ while (pos < offset + len && i < nfrags ) {
2500
2526
* frag = skb_shinfo (skb )-> frags [i ];
2501
2527
get_page (frag -> page );
2502
2528
size = frag -> size ;
@@ -2506,20 +2532,39 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
2506
2532
frag -> size -= offset - pos ;
2507
2533
}
2508
2534
2509
- k ++ ;
2535
+ skb_shinfo ( nskb ) -> nr_frags ++ ;
2510
2536
2511
2537
if (pos + size <= offset + len ) {
2512
2538
i ++ ;
2513
2539
pos += size ;
2514
2540
} else {
2515
2541
frag -> size -= pos + size - (offset + len );
2516
- break ;
2542
+ goto skip_fraglist ;
2517
2543
}
2518
2544
2519
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
+
2567
+ skip_fraglist :
2523
2568
nskb -> data_len = len - hsize ;
2524
2569
nskb -> len += nskb -> data_len ;
2525
2570
nskb -> truesize += nskb -> data_len ;
0 commit comments