Re: [3/5] [NET]: Add software TSOv4
From: Michael Chan [EMAIL PROTECTED] Date: Fri, 23 Jun 2006 20:08:41 -0700 On Sat, 2006-06-24 at 09:53 +1000, Herbert Xu wrote: Nevermind, I obviously complete ignored your other fix to the length of the last segment :) Here is a fixed version. [NET]: Fix CHECKSUM_HW GSO problems. Fix checksum problems in the GSO code path for CHECKSUM_HW packets. The ipv4 TCP pseudo header checksum has to be adjusted for GSO segmented packets. The adjustment is needed because the length field in the pseudo-header changes. However, because we have the inequality oldlen newlen, we know that delta = (u16)~oldlen + newlen is still a 16-bit quantity. This also means that htonl(delta) + th-check still fits in 32 bits. Therefore we don't have to use csum_add on this operations. This is based on a patch by Michael Chan [EMAIL PROTECTED]. Signed-off-by: Herbert Xu [EMAIL PROTECTED] Yes, this should work. ACK. Applied, thanks a lot guys. - To unsubscribe from this list: send the line unsubscribe netdev in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [3/5] [NET]: Add software TSOv4
We are working on it. Ravi -Original Message- From: YOSHIFUJI Hideaki / g!?p-? [mailto:[EMAIL PROTECTED] Sent: Friday, June 23, 2006 6:33 PM To: [EMAIL PROTECTED] Cc: [EMAIL PROTECTED]; [EMAIL PROTECTED]; netdev@vger.kernel.org; [EMAIL PROTECTED] Subject: Re: [3/5] [NET]: Add software TSOv4 In article [EMAIL PROTECTED] (at Fri, 23 Jun 2006 17:28:12 -0700), Ravinandan Arakali [EMAIL PROTECTED] says: Neterion's Xframe adapter supports TSO over IPv6. I remember you posted some patches. Would you post revised version reflecting Stephen's comment, please? --yoshfuji - To unsubscribe from this list: send the line unsubscribe netdev in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [3/5] [NET]: Add software TSOv4
On Thu, 2006-06-22 at 18:14 +1000, Herbert Xu wrote: [NET]: Add software TSOv4 This patch adds the GSO implementation for IPv4 TCP. Herbert, Looks like there were some problems in the CHECKSUM_HW case. This patch should fix it. Please double-check my checksum math. [NET]: Fix CHECKSUM_HW GSO problems. Fix the following 2 problems in the GSO code path for CHECKSUM_HW packets: 1. Adjust ipv4 TCP pseudo header checksum. 2. Initialize skb-tail. Signed-off-by: Michael Chan [EMAIL PROTECTED] diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 8e5044b..3f19b3d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1954,6 +1954,7 @@ struct sk_buff *skb_segment(struct sk_bu nskb-data_len = len - hsize; nskb-len += nskb-data_len; nskb-truesize += nskb-data_len; + nskb-tail += nskb-data_len; } while ((offset += len) skb-len); return segs; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0e029c4..3399110 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2186,7 +2186,8 @@ struct sk_buff *tcp_tso_segment(struct s if (skb-ip_summed == CHECKSUM_NONE) { th-check = csum_fold(csum_partial( skb-h.raw, thlen, csum_add(skb-csum, delta))); - } + } else if (skb-ip_summed == CHECKSUM_HW) + th-check = ~csum_fold(csum_add(th-check, delta)); seq += len; skb = skb-next; @@ -2196,11 +2197,12 @@ struct sk_buff *tcp_tso_segment(struct s th-cwr = 0; } while (skb-next); + delta = csum_add(oldlen, htonl(skb-tail - skb-h.raw)); if (skb-ip_summed == CHECKSUM_NONE) { - delta = csum_add(oldlen, htonl(skb-tail - skb-h.raw)); th-check = csum_fold(csum_partial( skb-h.raw, thlen, csum_add(skb-csum, delta))); - } + } else if (skb-ip_summed == CHECKSUM_HW) + th-check = ~csum_fold(csum_add(th-check, delta)); out: return segs; - To unsubscribe from this list: send the line unsubscribe netdev in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [3/5] [NET]: Add software TSOv4
On Fri, Jun 23, 2006 at 02:26:16PM -0700, Michael Chan wrote: This patch is more correct. Please ignore the previous one. [NET]: Fix CHECKSUM_HW GSO problems. Fix checksum problems in the GSO code path for CHECKSUM_HW packets. The ipv4 TCP pseudo header checksum has to be adjusted for GSO segmented packets. Signed-off-by: Michael Chan [EMAIL PROTECTED] Good catch. Obviously the only CHECKSUM_HW I tested was loop :) Looking at this again it seems that we can optimise it further so how about this? [NET]: Fix CHECKSUM_HW GSO problems. Fix checksum problems in the GSO code path for CHECKSUM_HW packets. The ipv4 TCP pseudo header checksum has to be adjusted for GSO segmented packets. The adjustment is needed because the length field in the pseudo-header changes. However, because we have the inequality oldlen newlen, we know that delta = (u16)~oldlen + newlen is still a 16-bit quantity. This also means that htonl(delta) + th-check still fits in 32 bits. Therefore we don't have to use csum_add on this operations. This is based on a patch by Michael Chan [EMAIL PROTECTED]. Signed-off-by: Herbert Xu [EMAIL PROTECTED] Cheers, -- Visit Openswan at http://www.openswan.org/ Email: Herbert Xu ~{PmVHI~} [EMAIL PROTECTED] Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt -- diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0e029c4..10f1a8c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2166,7 +2166,7 @@ struct sk_buff *tcp_tso_segment(struct s if (!pskb_may_pull(skb, thlen)) goto out; - oldlen = ~htonl(skb-len); + oldlen = (u16)~skb-len; __skb_pull(skb, thlen); segs = skb_segment(skb, sg); @@ -2174,7 +2174,7 @@ struct sk_buff *tcp_tso_segment(struct s goto out; len = skb_shinfo(skb)-gso_size; - delta = csum_add(oldlen, htonl(thlen + len)); + delta = htonl(oldlen + (thlen + len)); skb = segs; th = skb-h.th; @@ -2183,10 +2183,10 @@ struct sk_buff *tcp_tso_segment(struct s do { th-fin = th-psh = 0; - if (skb-ip_summed == CHECKSUM_NONE) { - th-check = csum_fold(csum_partial( - skb-h.raw, thlen, csum_add(skb-csum, delta))); - } + th-check = ~csum_fold(th-check + delta); + if (skb-ip_summed != CHECKSUM_HW) + th-check = csum_fold(csum_partial(skb-h.raw, thlen, + skb-csum)); seq += len; skb = skb-next; @@ -2196,11 +2196,11 @@ struct sk_buff *tcp_tso_segment(struct s th-cwr = 0; } while (skb-next); - if (skb-ip_summed == CHECKSUM_NONE) { - delta = csum_add(oldlen, htonl(skb-tail - skb-h.raw)); - th-check = csum_fold(csum_partial( - skb-h.raw, thlen, csum_add(skb-csum, delta))); - } + delta = htonl(oldlen + (skb-tail - skb-h.raw)); + th-check = ~csum_fold(th-check + delta); + if (skb-ip_summed != CHECKSUM_HW) + th-check = csum_fold(csum_partial(skb-h.raw, thlen, + skb-csum)); out: return segs; - To unsubscribe from this list: send the line unsubscribe netdev in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [3/5] [NET]: Add software TSOv4
On Sat, Jun 24, 2006 at 09:38:40AM +1000, herbert wrote: Good catch. Obviously the only CHECKSUM_HW I tested was loop :) Looking at this again it seems that we can optimise it further so how about this? Nevermind, I obviously complete ignored your other fix to the length of the last segment :) Here is a fixed version. [NET]: Fix CHECKSUM_HW GSO problems. Fix checksum problems in the GSO code path for CHECKSUM_HW packets. The ipv4 TCP pseudo header checksum has to be adjusted for GSO segmented packets. The adjustment is needed because the length field in the pseudo-header changes. However, because we have the inequality oldlen newlen, we know that delta = (u16)~oldlen + newlen is still a 16-bit quantity. This also means that htonl(delta) + th-check still fits in 32 bits. Therefore we don't have to use csum_add on this operations. This is based on a patch by Michael Chan [EMAIL PROTECTED]. Signed-off-by: Herbert Xu [EMAIL PROTECTED] Cheers, -- Visit Openswan at http://www.openswan.org/ Email: Herbert Xu ~{PmVHI~} [EMAIL PROTECTED] Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt -- cfecbf18c32a6dca8954538b5d5fb7186ed336d1 diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0e029c4..c04176b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2166,7 +2166,7 @@ struct sk_buff *tcp_tso_segment(struct s if (!pskb_may_pull(skb, thlen)) goto out; - oldlen = ~htonl(skb-len); + oldlen = (u16)~skb-len; __skb_pull(skb, thlen); segs = skb_segment(skb, sg); @@ -2174,7 +2174,7 @@ struct sk_buff *tcp_tso_segment(struct s goto out; len = skb_shinfo(skb)-gso_size; - delta = csum_add(oldlen, htonl(thlen + len)); + delta = htonl(oldlen + (thlen + len)); skb = segs; th = skb-h.th; @@ -2183,10 +2183,10 @@ struct sk_buff *tcp_tso_segment(struct s do { th-fin = th-psh = 0; - if (skb-ip_summed == CHECKSUM_NONE) { - th-check = csum_fold(csum_partial( - skb-h.raw, thlen, csum_add(skb-csum, delta))); - } + th-check = ~csum_fold(th-check + delta); + if (skb-ip_summed != CHECKSUM_HW) + th-check = csum_fold(csum_partial(skb-h.raw, thlen, + skb-csum)); seq += len; skb = skb-next; @@ -2196,11 +2196,11 @@ struct sk_buff *tcp_tso_segment(struct s th-cwr = 0; } while (skb-next); - if (skb-ip_summed == CHECKSUM_NONE) { - delta = csum_add(oldlen, htonl(skb-tail - skb-h.raw)); - th-check = csum_fold(csum_partial( - skb-h.raw, thlen, csum_add(skb-csum, delta))); - } + delta = htonl(oldlen + (skb-tail - skb-h.raw) + skb-data_len); + th-check = ~csum_fold(th-check + delta); + if (skb-ip_summed != CHECKSUM_HW) + th-check = csum_fold(csum_partial(skb-h.raw, thlen, + skb-csum)); out: return segs; - To unsubscribe from this list: send the line unsubscribe netdev in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [3/5] [NET]: Add software TSOv4
On Sat, 2006-06-24 at 09:53 +1000, Herbert Xu wrote: Nevermind, I obviously complete ignored your other fix to the length of the last segment :) Here is a fixed version. [NET]: Fix CHECKSUM_HW GSO problems. Fix checksum problems in the GSO code path for CHECKSUM_HW packets. The ipv4 TCP pseudo header checksum has to be adjusted for GSO segmented packets. The adjustment is needed because the length field in the pseudo-header changes. However, because we have the inequality oldlen newlen, we know that delta = (u16)~oldlen + newlen is still a 16-bit quantity. This also means that htonl(delta) + th-check still fits in 32 bits. Therefore we don't have to use csum_add on this operations. This is based on a patch by Michael Chan [EMAIL PROTECTED]. Signed-off-by: Herbert Xu [EMAIL PROTECTED] Yes, this should work. ACK. - To unsubscribe from this list: send the line unsubscribe netdev in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
[3/5] [NET]: Add software TSOv4
Hi: [NET]: Add software TSOv4 This patch adds the GSO implementation for IPv4 TCP. Signed-off-by: Herbert Xu [EMAIL PROTECTED] Cheers, -- Visit Openswan at http://www.openswan.org/ Email: Herbert Xu ~{PmVHI~} [EMAIL PROTECTED] Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1299,6 +1299,7 @@ extern void skb_split(struct sk_b struct sk_buff *skb1, const u32 len); extern void skb_release_data(struct sk_buff *skb); +extern struct sk_buff *skb_segment(struct sk_buff *skb, int sg); static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) diff --git a/include/net/protocol.h b/include/net/protocol.h --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -37,6 +37,7 @@ struct net_protocol { int (*handler)(struct sk_buff *skb); void(*err_handler)(struct sk_buff *skb, u32 info); + struct sk_buff *(*gso_segment)(struct sk_buff *skb, int sg); int no_policy; }; diff --git a/include/net/tcp.h b/include/net/tcp.h --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1087,6 +1087,8 @@ extern struct request_sock_ops tcp_reque extern int tcp_v4_destroy_sock(struct sock *sk); +extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg); + #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); extern void tcp4_proc_exit(void); diff --git a/net/core/skbuff.c b/net/core/skbuff.c --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1826,6 +1826,132 @@ unsigned char *skb_pull_rcsum(struct sk_ 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); +
Re: [3/5] [NET]: Add software TSOv4
On Thu, Jun 22, 2006 at 06:14:00PM +1000, herbert wrote: [NET]: Add software TSOv4 Doh, forgot to remove an unused declaration. Here is an updated version. [NET]: Add software TSOv4 This patch adds the GSO implementation for IPv4 TCP. Signed-off-by: Herbert Xu [EMAIL PROTECTED] Cheers, -- Visit Openswan at http://www.openswan.org/ Email: Herbert Xu ~{PmVHI~} [EMAIL PROTECTED] Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt -- diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -406,6 +406,9 @@ struct net_device struct list_headqdisc_list; unsigned long tx_queue_len; /* Max frames per queue allowed */ + /* Partially transmitted GSO packet. */ + struct sk_buff *gso_skb; + /* ingress path synchronizer */ spinlock_t ingress_lock; struct Qdisc*qdisc_ingress; @@ -540,6 +543,7 @@ struct packet_type { struct net_device *, struct packet_type *, struct net_device *); + struct sk_buff *(*gso_segment)(struct sk_buff *skb, int sg); void*af_packet_priv; struct list_headlist; }; @@ -690,7 +694,8 @@ extern int dev_change_name(struct net_d extern int dev_set_mtu(struct net_device *, int); extern int dev_set_mac_address(struct net_device *, struct sockaddr *); -extern voiddev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev); +extern int dev_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev); extern voiddev_init(void); @@ -964,6 +969,7 @@ extern int netdev_max_backlog; extern int weight_p; extern int netdev_set_master(struct net_device *dev, struct net_device *master); extern int skb_checksum_help(struct sk_buff *skb, int inward); +extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg); #ifdef CONFIG_BUG extern void netdev_rx_csum_fault(struct net_device *dev); #else diff --git a/net/core/dev.c b/net/core/dev.c --- a/net/core/dev.c +++ b/net/core/dev.c @@ -116,6 +116,7 @@ #include asm/current.h #include linux/audit.h #include linux/dmaengine.h +#include linux/err.h /* * The list of packet types we will receive (as opposed to discard) @@ -1048,7 +1049,7 @@ static inline void net_timestamp(struct * taps currently in use. */ -void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) +static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) { struct packet_type *ptype; @@ -1186,6 +1187,40 @@ out: return ret; } +/** + * skb_gso_segment - Perform segmentation on skb. + * @skb: buffer to segment + * @sg: whether scatter-gather is supported on the target. + * + * This function segments the given skb and returns a list of segments. + */ +struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg) +{ + struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); + struct packet_type *ptype; + int type = skb-protocol; + + BUG_ON(skb_shinfo(skb)-frag_list); + BUG_ON(skb-ip_summed != CHECKSUM_HW); + + skb-mac.raw = skb-data; + skb-mac_len = skb-nh.raw - skb-data; + __skb_pull(skb, skb-mac_len); + + rcu_read_lock(); + list_for_each_entry_rcu(ptype, ptype_base[ntohs(type) 15], list) { + if (ptype-type == type !ptype-dev ptype-gso_segment) { + segs = ptype-gso_segment(skb, sg); + break; + } + } + rcu_read_unlock(); + + return segs; +} + +EXPORT_SYMBOL(skb_gso_segment); + /* Take action when hardware reception checksum errors are detected. */ #ifdef CONFIG_BUG void netdev_rx_csum_fault(struct net_device *dev) @@ -1222,6 +1257,86 @@ static inline int illegal_highdma(struct #define illegal_highdma(dev, skb) (0) #endif +struct dev_gso_cb { + void (*destructor)(struct sk_buff *skb); +}; + +#define DEV_GSO_CB(skb) ((struct dev_gso_cb *)(skb)-cb) + +static void dev_gso_skb_destructor(struct sk_buff *skb) +{ + struct dev_gso_cb *cb; + + do { + struct sk_buff *nskb = skb-next; + + skb-next = nskb-next; + nskb-next = NULL; + kfree_skb(nskb); + } while (skb-next); + + cb = DEV_GSO_CB(skb); + if (cb-destructor) + cb-destructor(skb); +} + +/** + * dev_gso_segment - Perform emulated hardware segmentation on skb. + * @skb: buffer to segment + * + * This function segments the given skb and stores the list of segments + * in
Re: [3/5] [NET]: Add software TSOv4
In article [EMAIL PROTECTED] (at Thu, 22 Jun 2006 18:14:00 +1000), Herbert Xu [EMAIL PROTECTED] says: [NET]: Add software TSOv4 This patch adds the GSO implementation for IPv4 TCP. Signed-off-by: Herbert Xu [EMAIL PROTECTED] I'd appreciate if you code up IPv6 TCP as well. :-) Regards, --yoshfuji - To unsubscribe from this list: send the line unsubscribe netdev in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [3/5] [NET]: Add software TSOv4
From: YOSHIFUJI Hideaki [EMAIL PROTECTED] Date: Fri, 23 Jun 2006 00:04:03 +0900 (JST) In article [EMAIL PROTECTED] (at Thu, 22 Jun 2006 18:14:00 +1000), Herbert Xu [EMAIL PROTECTED] says: [NET]: Add software TSOv4 This patch adds the GSO implementation for IPv4 TCP. Signed-off-by: Herbert Xu [EMAIL PROTECTED] I'd appreciate if you code up IPv6 TCP as well. :-) To my understanding doing IPV6 TCP TSO is a non-trivial task, even in software. The header editing is a lot more complicated because things like routing and other extension headers can sit between IPV6 and TCP header. It is probably why IPV6 TSO hardware does not exist yet :) Do not take this to mean I think it should not be implemented, I think it should. - To unsubscribe from this list: send the line unsubscribe netdev in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
[3/5] [NET]: Add software TSOv4
Hi: [NET]: Add software TSOv4 This patch adds the GSO implementation for IPv4 TCP. Signed-off-by: Herbert Xu [EMAIL PROTECTED] Cheers, -- Visit Openswan at http://www.openswan.org/ Email: Herbert Xu ~{PmVHI~} [EMAIL PROTECTED] Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1299,6 +1299,7 @@ extern void skb_split(struct sk_b struct sk_buff *skb1, const u32 len); extern void skb_release_data(struct sk_buff *skb); +extern struct sk_buff *skb_segment(struct sk_buff *skb, int sg); static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) diff --git a/include/net/protocol.h b/include/net/protocol.h --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -37,6 +37,7 @@ struct net_protocol { int (*handler)(struct sk_buff *skb); void(*err_handler)(struct sk_buff *skb, u32 info); + struct sk_buff *(*gso_segment)(struct sk_buff *skb, int sg); int no_policy; }; diff --git a/include/net/tcp.h b/include/net/tcp.h --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1087,6 +1087,8 @@ extern struct request_sock_ops tcp_reque extern int tcp_v4_destroy_sock(struct sock *sk); +extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg); + #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); extern void tcp4_proc_exit(void); diff --git a/net/core/skbuff.c b/net/core/skbuff.c --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1826,6 +1826,132 @@ unsigned char *skb_pull_rcsum(struct sk_ 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); +