Skip to content

Commit

Permalink
net: Add frag_list support to skb_segment
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Herbert Xu authored and David S. Miller committed Dec 16, 2008
1 parent eb14f01 commit 89319d3
Showing 1 changed file with 59 additions and 14 deletions.
73 changes: 59 additions & 14 deletions net/core/skbuff.c
Original file line number Diff line number Diff line change
Expand Up @@ -2428,6 +2428,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
{
struct sk_buff *segs = 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 doffset = skb->data - skb_mac_header(skb);
unsigned int offset = doffset;
Expand All @@ -2447,7 +2448,6 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
struct sk_buff *nskb;
skb_frag_t *frag;
int hsize;
int k;
int size;

len = skb->len - offset;
Expand All @@ -2460,9 +2460,36 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
if (hsize > len || !sg)
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)
tail->next = nskb;
Expand All @@ -2473,13 +2500,15 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
__copy_skb_header(nskb, skb);
nskb->mac_len = skb->mac_len;

skb_reserve(nskb, headroom);
skb_reset_mac_header(nskb);
skb_set_network_header(nskb, skb->mac_len);
nskb->transport_header = (nskb->network_header +
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) {
nskb->ip_summed = CHECKSUM_NONE;
nskb->csum = skb_copy_and_csum_bits(skb, offset,
Expand All @@ -2489,14 +2518,11 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
}

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

skb_copy_from_linear_data_offset(skb, offset,
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];
get_page(frag->page);
size = frag->size;
Expand All @@ -2506,20 +2532,39 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
frag->size -= offset - pos;
}

k++;
skb_shinfo(nskb)->nr_frags++;

if (pos + size <= offset + len) {
i++;
pos += size;
} else {
frag->size -= pos + size - (offset + len);
break;
goto skip_fraglist;
}

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->len += nskb->data_len;
nskb->truesize += nskb->data_len;
Expand Down

0 comments on commit 89319d3

Please sign in to comment.