[NET]: Add software TSOv4
This patch adds the GSO implementation for IPv4 TCP. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
f6a78bfcb1
commit
f4c50d990d
@@ -1842,6 +1842,132 @@ unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len)
|
||||
|
||||
EXPORT_SYMBOL_GPL(skb_pull_rcsum);
|
||||
|
||||
/**
|
||||
* skb_segment - Perform protocol segmentation on skb.
|
||||
* @skb: buffer to segment
|
||||
* @sg: whether scatter-gather can be used for generated segments
|
||||
*
|
||||
* This function performs segmentation on the given skb. It returns
|
||||
* the segment at the given position. It returns NULL if there are
|
||||
* no more segments to generate, or when an error is encountered.
|
||||
*/
|
||||
struct sk_buff *skb_segment(struct sk_buff *skb, int sg)
|
||||
{
|
||||
struct sk_buff *segs = NULL;
|
||||
struct sk_buff *tail = NULL;
|
||||
unsigned int mss = skb_shinfo(skb)->gso_size;
|
||||
unsigned int doffset = skb->data - skb->mac.raw;
|
||||
unsigned int offset = doffset;
|
||||
unsigned int headroom;
|
||||
unsigned int len;
|
||||
int nfrags = skb_shinfo(skb)->nr_frags;
|
||||
int err = -ENOMEM;
|
||||
int i = 0;
|
||||
int pos;
|
||||
|
||||
__skb_push(skb, doffset);
|
||||
headroom = skb_headroom(skb);
|
||||
pos = skb_headlen(skb);
|
||||
|
||||
do {
|
||||
struct sk_buff *nskb;
|
||||
skb_frag_t *frag;
|
||||
int hsize, nsize;
|
||||
int k;
|
||||
int size;
|
||||
|
||||
len = skb->len - offset;
|
||||
if (len > mss)
|
||||
len = mss;
|
||||
|
||||
hsize = skb_headlen(skb) - offset;
|
||||
if (hsize < 0)
|
||||
hsize = 0;
|
||||
nsize = hsize + doffset;
|
||||
if (nsize > len + doffset || !sg)
|
||||
nsize = len + doffset;
|
||||
|
||||
nskb = alloc_skb(nsize + headroom, GFP_ATOMIC);
|
||||
if (unlikely(!nskb))
|
||||
goto err;
|
||||
|
||||
if (segs)
|
||||
tail->next = nskb;
|
||||
else
|
||||
segs = nskb;
|
||||
tail = nskb;
|
||||
|
||||
nskb->dev = skb->dev;
|
||||
nskb->priority = skb->priority;
|
||||
nskb->protocol = skb->protocol;
|
||||
nskb->dst = dst_clone(skb->dst);
|
||||
memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
|
||||
nskb->pkt_type = skb->pkt_type;
|
||||
nskb->mac_len = skb->mac_len;
|
||||
|
||||
skb_reserve(nskb, headroom);
|
||||
nskb->mac.raw = nskb->data;
|
||||
nskb->nh.raw = nskb->data + skb->mac_len;
|
||||
nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw);
|
||||
memcpy(skb_put(nskb, doffset), skb->data, doffset);
|
||||
|
||||
if (!sg) {
|
||||
nskb->csum = skb_copy_and_csum_bits(skb, offset,
|
||||
skb_put(nskb, len),
|
||||
len, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
frag = skb_shinfo(nskb)->frags;
|
||||
k = 0;
|
||||
|
||||
nskb->ip_summed = CHECKSUM_HW;
|
||||
nskb->csum = skb->csum;
|
||||
memcpy(skb_put(nskb, hsize), skb->data + offset, hsize);
|
||||
|
||||
while (pos < offset + len) {
|
||||
BUG_ON(i >= nfrags);
|
||||
|
||||
*frag = skb_shinfo(skb)->frags[i];
|
||||
get_page(frag->page);
|
||||
size = frag->size;
|
||||
|
||||
if (pos < offset) {
|
||||
frag->page_offset += offset - pos;
|
||||
frag->size -= offset - pos;
|
||||
}
|
||||
|
||||
k++;
|
||||
|
||||
if (pos + size <= offset + len) {
|
||||
i++;
|
||||
pos += size;
|
||||
} else {
|
||||
frag->size -= pos + size - (offset + len);
|
||||
break;
|
||||
}
|
||||
|
||||
frag++;
|
||||
}
|
||||
|
||||
skb_shinfo(nskb)->nr_frags = k;
|
||||
nskb->data_len = len - hsize;
|
||||
nskb->len += nskb->data_len;
|
||||
nskb->truesize += nskb->data_len;
|
||||
} while ((offset += len) < skb->len);
|
||||
|
||||
return segs;
|
||||
|
||||
err:
|
||||
while ((skb = segs)) {
|
||||
segs = skb->next;
|
||||
kfree(skb);
|
||||
}
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(skb_segment);
|
||||
|
||||
void __init skb_init(void)
|
||||
{
|
||||
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
|
||||
|
Reference in New Issue
Block a user