|
@@ -2428,6 +2428,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
|
|
{
|
|
{
|
|
struct sk_buff *segs = NULL;
|
|
struct sk_buff *segs = NULL;
|
|
struct sk_buff *tail = NULL;
|
|
struct sk_buff *tail = NULL;
|
|
|
|
+ struct sk_buff *fskb = skb_shinfo(skb)->frag_list;
|
|
unsigned int mss = skb_shinfo(skb)->gso_size;
|
|
unsigned int mss = skb_shinfo(skb)->gso_size;
|
|
unsigned int doffset = skb->data - skb_mac_header(skb);
|
|
unsigned int doffset = skb->data - skb_mac_header(skb);
|
|
unsigned int offset = doffset;
|
|
unsigned int offset = doffset;
|
|
@@ -2447,7 +2448,6 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
|
|
struct sk_buff *nskb;
|
|
struct sk_buff *nskb;
|
|
skb_frag_t *frag;
|
|
skb_frag_t *frag;
|
|
int hsize;
|
|
int hsize;
|
|
- int k;
|
|
|
|
int size;
|
|
int size;
|
|
|
|
|
|
len = skb->len - offset;
|
|
len = skb->len - offset;
|
|
@@ -2460,9 +2460,36 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
|
|
if (hsize > len || !sg)
|
|
if (hsize > len || !sg)
|
|
hsize = len;
|
|
hsize = len;
|
|
|
|
|
|
- nskb = alloc_skb(hsize + doffset + headroom, GFP_ATOMIC);
|
|
|
|
- if (unlikely(!nskb))
|
|
|
|
- goto err;
|
|
|
|
|
|
+ if (!hsize && i >= nfrags) {
|
|
|
|
+ BUG_ON(fskb->len != len);
|
|
|
|
+
|
|
|
|
+ pos += len;
|
|
|
|
+ nskb = skb_clone(fskb, GFP_ATOMIC);
|
|
|
|
+ fskb = fskb->next;
|
|
|
|
+
|
|
|
|
+ if (unlikely(!nskb))
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ hsize = skb_end_pointer(nskb) - nskb->head;
|
|
|
|
+ if (skb_cow_head(nskb, doffset + headroom)) {
|
|
|
|
+ kfree_skb(nskb);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ nskb->truesize += skb_end_pointer(nskb) - nskb->head -
|
|
|
|
+ hsize;
|
|
|
|
+ skb_release_head_state(nskb);
|
|
|
|
+ __skb_push(nskb, doffset);
|
|
|
|
+ } else {
|
|
|
|
+ nskb = alloc_skb(hsize + doffset + headroom,
|
|
|
|
+ GFP_ATOMIC);
|
|
|
|
+
|
|
|
|
+ if (unlikely(!nskb))
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ skb_reserve(nskb, headroom);
|
|
|
|
+ __skb_put(nskb, doffset);
|
|
|
|
+ }
|
|
|
|
|
|
if (segs)
|
|
if (segs)
|
|
tail->next = nskb;
|
|
tail->next = nskb;
|
|
@@ -2473,13 +2500,15 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
|
|
__copy_skb_header(nskb, skb);
|
|
__copy_skb_header(nskb, skb);
|
|
nskb->mac_len = skb->mac_len;
|
|
nskb->mac_len = skb->mac_len;
|
|
|
|
|
|
- skb_reserve(nskb, headroom);
|
|
|
|
skb_reset_mac_header(nskb);
|
|
skb_reset_mac_header(nskb);
|
|
skb_set_network_header(nskb, skb->mac_len);
|
|
skb_set_network_header(nskb, skb->mac_len);
|
|
nskb->transport_header = (nskb->network_header +
|
|
nskb->transport_header = (nskb->network_header +
|
|
skb_network_header_len(skb));
|
|
skb_network_header_len(skb));
|
|
- skb_copy_from_linear_data(skb, skb_put(nskb, doffset),
|
|
|
|
- doffset);
|
|
|
|
|
|
+ skb_copy_from_linear_data(skb, nskb->data, doffset);
|
|
|
|
+
|
|
|
|
+ if (pos >= offset + len)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
if (!sg) {
|
|
if (!sg) {
|
|
nskb->ip_summed = CHECKSUM_NONE;
|
|
nskb->ip_summed = CHECKSUM_NONE;
|
|
nskb->csum = skb_copy_and_csum_bits(skb, offset,
|
|
nskb->csum = skb_copy_and_csum_bits(skb, offset,
|
|
@@ -2489,14 +2518,11 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
|
|
}
|
|
}
|
|
|
|
|
|
frag = skb_shinfo(nskb)->frags;
|
|
frag = skb_shinfo(nskb)->frags;
|
|
- k = 0;
|
|
|
|
|
|
|
|
skb_copy_from_linear_data_offset(skb, offset,
|
|
skb_copy_from_linear_data_offset(skb, offset,
|
|
skb_put(nskb, hsize), hsize);
|
|
skb_put(nskb, hsize), hsize);
|
|
|
|
|
|
- while (pos < offset + len) {
|
|
|
|
- BUG_ON(i >= nfrags);
|
|
|
|
-
|
|
|
|
|
|
+ while (pos < offset + len && i < nfrags) {
|
|
*frag = skb_shinfo(skb)->frags[i];
|
|
*frag = skb_shinfo(skb)->frags[i];
|
|
get_page(frag->page);
|
|
get_page(frag->page);
|
|
size = frag->size;
|
|
size = frag->size;
|
|
@@ -2506,20 +2532,39 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
|
|
frag->size -= offset - pos;
|
|
frag->size -= offset - pos;
|
|
}
|
|
}
|
|
|
|
|
|
- k++;
|
|
|
|
|
|
+ skb_shinfo(nskb)->nr_frags++;
|
|
|
|
|
|
if (pos + size <= offset + len) {
|
|
if (pos + size <= offset + len) {
|
|
i++;
|
|
i++;
|
|
pos += size;
|
|
pos += size;
|
|
} else {
|
|
} else {
|
|
frag->size -= pos + size - (offset + len);
|
|
frag->size -= pos + size - (offset + len);
|
|
- break;
|
|
|
|
|
|
+ goto skip_fraglist;
|
|
}
|
|
}
|
|
|
|
|
|
frag++;
|
|
frag++;
|
|
}
|
|
}
|
|
|
|
|
|
- skb_shinfo(nskb)->nr_frags = k;
|
|
|
|
|
|
+ if (pos < offset + len) {
|
|
|
|
+ struct sk_buff *fskb2 = fskb;
|
|
|
|
+
|
|
|
|
+ BUG_ON(pos + fskb->len != offset + len);
|
|
|
|
+
|
|
|
|
+ pos += fskb->len;
|
|
|
|
+ fskb = fskb->next;
|
|
|
|
+
|
|
|
|
+ if (fskb2->next) {
|
|
|
|
+ fskb2 = skb_clone(fskb2, GFP_ATOMIC);
|
|
|
|
+ if (!fskb2)
|
|
|
|
+ goto err;
|
|
|
|
+ } else
|
|
|
|
+ skb_get(fskb2);
|
|
|
|
+
|
|
|
|
+ BUG_ON(skb_shinfo(nskb)->frag_list);
|
|
|
|
+ skb_shinfo(nskb)->frag_list = fskb2;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+skip_fraglist:
|
|
nskb->data_len = len - hsize;
|
|
nskb->data_len = len - hsize;
|
|
nskb->len += nskb->data_len;
|
|
nskb->len += nskb->data_len;
|
|
nskb->truesize += nskb->data_len;
|
|
nskb->truesize += nskb->data_len;
|