aboutsummaryrefslogtreecommitdiffstats
path: root/patches
diff options
context:
space:
mode:
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>2006-07-25 15:06:39 +0100
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>2006-07-25 15:06:39 +0100
commita4517dd065d9d6b05fd9969bd773096c69edb13a (patch)
tree31a61c47df25b99d5c1b7bcdba8a70ddc0029151 /patches
parent032e54ea22d8f717cad6abbfbe242ca8e9f6e0f6 (diff)
downloadxen-a4517dd065d9d6b05fd9969bd773096c69edb13a.tar.gz
xen-a4517dd065d9d6b05fd9969bd773096c69edb13a.tar.bz2
xen-a4517dd065d9d6b05fd9969bd773096c69edb13a.zip
Split networking GSO patch into base portion plus additions.
Signed-off-by: Keir Fraser <keir@xensource.com>
Diffstat (limited to 'patches')
-rw-r--r--patches/linux-2.6.16.13/net-gso-0-base.patch (renamed from patches/linux-2.6.16.13/net-gso.patch)298
-rw-r--r--patches/linux-2.6.16.13/net-gso-1-check-dodgy.patch27
-rw-r--r--patches/linux-2.6.16.13/net-gso-2-checksum-fix.patch451
3 files changed, 551 insertions, 225 deletions
diff --git a/patches/linux-2.6.16.13/net-gso.patch b/patches/linux-2.6.16.13/net-gso-0-base.patch
index eb5d6b0ab7..4c69d1e4a6 100644
--- a/patches/linux-2.6.16.13/net-gso.patch
+++ b/patches/linux-2.6.16.13/net-gso-0-base.patch
@@ -104,7 +104,7 @@ index dd41049..6615583 100644
if (skb_shinfo(skb)->nr_frags == 0) {
struct cp_desc *txd = &cp->tx_ring[entry];
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
-index a24200d..29d9218 100644
+index a24200d..b5e39a1 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -1593,7 +1593,7 @@ bnx2_tx_int(struct bnx2 *bp)
@@ -112,7 +112,7 @@ index a24200d..29d9218 100644
#ifdef BCM_TSO
/* partial BD completions possible with TSO packets */
- if (skb_shinfo(skb)->tso_size) {
-+ if (skb_is_gso(skb)) {
++ if (skb_shinfo(skb)->gso_size) {
u16 last_idx, last_ring_idx;
last_idx = sw_cons +
@@ -178,7 +178,7 @@ index bcf9f17..e970921 100644
bond_dev->features |= NETIF_F_LLTX;
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c
-index 30ff8ea..7d72e16 100644
+index 30ff8ea..7b7d360 100644
--- a/drivers/net/chelsio/sge.c
+++ b/drivers/net/chelsio/sge.c
@@ -1419,7 +1419,7 @@ int t1_start_xmit(struct sk_buff *skb, s
@@ -186,7 +186,7 @@ index 30ff8ea..7d72e16 100644
#ifdef NETIF_F_TSO
- if (skb_shinfo(skb)->tso_size) {
-+ if (skb_is_gso(skb)) {
++ if (skb_shinfo(skb)->gso_size) {
int eth_type;
struct cpl_tx_pkt_lso *hdr;
@@ -200,7 +200,7 @@ index 30ff8ea..7d72e16 100644
cpl = (struct cpl_tx_pkt *)hdr;
sge->stats.tx_lso_pkts++;
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
-index fa29402..96ddc24 100644
+index fa29402..681d284 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -2526,7 +2526,7 @@ #ifdef NETIF_F_TSO
@@ -208,7 +208,7 @@ index fa29402..96ddc24 100644
int err;
- if (skb_shinfo(skb)->tso_size) {
-+ if (skb_is_gso(skb)) {
++ if (skb_shinfo(skb)->gso_size) {
if (skb_header_cloned(skb)) {
err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
if (err)
@@ -226,7 +226,7 @@ index fa29402..96ddc24 100644
* DMAd to the controller */
if (!skb->data_len && tx_ring->last_tx_tso &&
- !skb_shinfo(skb)->tso_size) {
-+ !skb_is_gso(skb)) {
++ !skb_shinfo(skb)->gso_size) {
tx_ring->last_tx_tso = 0;
size -= 4;
}
@@ -239,18 +239,17 @@ index fa29402..96ddc24 100644
/* The controller does a simple calculation to
* make sure there is enough room in the FIFO before
* initiating the DMA for each buffer. The calc is:
-@@ -2934,8 +2934,7 @@ #endif
-
+@@ -2935,7 +2935,7 @@ #endif
#ifdef NETIF_F_TSO
/* Controller Erratum workaround */
-- if (!skb->data_len && tx_ring->last_tx_tso &&
+ if (!skb->data_len && tx_ring->last_tx_tso &&
- !skb_shinfo(skb)->tso_size)
-+ if (!skb->data_len && tx_ring->last_tx_tso && !skb_is_gso(skb))
++ !skb_shinfo(skb)->gso_size)
count++;
#endif
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
-index 3682ec6..c6ca459 100644
+index 3682ec6..c35f16e 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -482,9 +482,9 @@ #define LPA_1000HALF 0x0400
@@ -280,7 +279,7 @@ index 3682ec6..c6ca459 100644
#ifdef NETIF_F_TSO
- if (skb_shinfo(skb)->tso_size)
- tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->tso_size << NV_TX2_TSO_SHIFT);
-+ if (skb_is_gso(skb))
++ if (skb_shinfo(skb)->gso_size)
+ tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT);
else
#endif
@@ -451,7 +450,7 @@ index a9f49f0..339d4a7 100644
}
diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
-index f9f77e4..7d187d0 100644
+index f9f77e4..bdab369 100644
--- a/drivers/net/ixgb/ixgb_main.c
+++ b/drivers/net/ixgb/ixgb_main.c
@@ -1163,7 +1163,7 @@ #ifdef NETIF_F_TSO
@@ -459,7 +458,7 @@ index f9f77e4..7d187d0 100644
int err;
- if(likely(skb_shinfo(skb)->tso_size)) {
-+ if (likely(skb_is_gso(skb))) {
++ if(likely(skb_shinfo(skb)->gso_size)) {
if (skb_header_cloned(skb)) {
err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
if (err)
@@ -473,7 +472,7 @@ index f9f77e4..7d187d0 100644
skb->nh.iph->check = 0;
skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr,
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
-index 690a1aa..3843e0a 100644
+index 690a1aa..9bcaa80 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -74,7 +74,7 @@ static void emulate_large_send_offload(s
@@ -490,7 +489,7 @@ index 690a1aa..3843e0a 100644
#ifdef LOOPBACK_TSO
- if (skb_shinfo(skb)->tso_size) {
-+ if (skb_is_gso(skb)) {
++ if (skb_shinfo(skb)->gso_size) {
BUG_ON(skb->protocol != htons(ETH_P_IP));
BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP);
@@ -601,7 +600,7 @@ index b7f00d6..439f45f 100644
writeq(val64, &tx_fifo->List_Control);
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
-index 0618cd5..aa06a82 100644
+index 0618cd5..2a55eb3 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -1125,7 +1125,7 @@ static unsigned tx_le_req(const struct s
@@ -609,7 +608,7 @@ index 0618cd5..aa06a82 100644
count += skb_shinfo(skb)->nr_frags * count;
- if (skb_shinfo(skb)->tso_size)
-+ if (skb_is_gso(skb))
++ if (skb_shinfo(skb)->gso_size)
++count;
if (skb->ip_summed == CHECKSUM_HW)
@@ -668,7 +667,7 @@ index 5b1af39..11de5af 100644
np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
-index 4c76cb7..3d62abc 100644
+index 4c76cb7..30c48c9 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -340,7 +340,7 @@ #define typhoon_synchronize_irq(x) synch
@@ -680,24 +679,6 @@ index 4c76cb7..3d62abc 100644
#define TSO_NUM_DESCRIPTORS 2
#define TSO_OFFLOAD_ON TYPHOON_OFFLOAD_TCP_SEGMENT
#else
-@@ -805,7 +805,7 @@ typhoon_start_tx(struct sk_buff *skb, st
- * If problems develop with TSO, check this first.
- */
- numDesc = skb_shinfo(skb)->nr_frags + 1;
-- if(skb_tso_size(skb))
-+ if (skb_is_gso(skb))
- numDesc++;
-
- /* When checking for free space in the ring, we need to also
-@@ -845,7 +845,7 @@ typhoon_start_tx(struct sk_buff *skb, st
- TYPHOON_TX_PF_VLAN_TAG_SHIFT);
- }
-
-- if(skb_tso_size(skb)) {
-+ if (skb_is_gso(skb)) {
- first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT;
- first_txd->numDesc++;
-
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index ed1f837..2eb6b5f 100644
--- a/drivers/net/via-velocity.c
@@ -788,7 +769,7 @@ index 82cb4af..57cec40 100644
static inline struct qeth_eddp_context *
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
-index dba7f7f..a3ea8e0 100644
+index dba7f7f..d9cc997 100644
--- a/drivers/s390/net/qeth_main.c
+++ b/drivers/s390/net/qeth_main.c
@@ -4454,7 +4454,7 @@ qeth_send_packet(struct qeth_card *card,
@@ -796,20 +777,19 @@ index dba7f7f..a3ea8e0 100644
[qeth_get_priority_queue(card, skb, ipv, cast_type)];
- if (skb_shinfo(skb)->tso_size)
-+ if (skb_is_gso(skb))
++ if (skb_shinfo(skb)->gso_size)
large_send = card->options.large_send;
/*are we able to do TSO ? If so ,prepare and send it from here */
-@@ -4501,8 +4501,7 @@ qeth_send_packet(struct qeth_card *card,
+@@ -4501,7 +4501,7 @@ qeth_send_packet(struct qeth_card *card,
card->stats.tx_packets++;
card->stats.tx_bytes += skb->len;
#ifdef CONFIG_QETH_PERF_STATS
- if (skb_shinfo(skb)->tso_size &&
-- !(large_send == QETH_LARGE_SEND_NO)) {
-+ if (skb_is_gso(skb) && !(large_send == QETH_LARGE_SEND_NO)) {
++ if (skb_shinfo(skb)->gso_size &&
+ !(large_send == QETH_LARGE_SEND_NO)) {
card->perf_stats.large_send_bytes += skb->len;
card->perf_stats.large_send_cnt++;
- }
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h
index 1286dde..89cbf34 100644
--- a/drivers/s390/net/qeth_tso.h
@@ -837,7 +817,7 @@ index 93535f0..9269df7 100644
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
-index 7fda03d..9865736 100644
+index 7fda03d..47b0965 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -230,7 +230,8 @@ enum netdev_state_t
@@ -889,17 +869,16 @@ index 7fda03d..9865736 100644
/* cpu id of processor entered to hard_start_xmit or -1,
if nobody entered there.
*/
-@@ -527,6 +539,9 @@ struct packet_type {
+@@ -527,6 +539,8 @@ struct packet_type {
struct net_device *,
struct packet_type *,
struct net_device *);
+ struct sk_buff *(*gso_segment)(struct sk_buff *skb,
+ int features);
-+ int (*gso_send_check)(struct sk_buff *skb);
void *af_packet_priv;
struct list_head list;
};
-@@ -693,7 +708,8 @@ extern int dev_change_name(struct net_d
+@@ -693,7 +707,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 *);
@@ -909,7 +888,7 @@ index 7fda03d..9865736 100644
extern void dev_init(void);
-@@ -900,11 +916,43 @@ static inline void __netif_rx_complete(s
+@@ -900,11 +915,43 @@ static inline void __netif_rx_complete(s
clear_bit(__LINK_STATE_RX_SCHED, &dev->state);
}
@@ -955,7 +934,7 @@ index 7fda03d..9865736 100644
}
/* These functions live elsewhere (drivers/net/net_init.c, but related) */
-@@ -932,6 +980,7 @@ extern int netdev_max_backlog;
+@@ -932,6 +979,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);
@@ -963,28 +942,27 @@ index 7fda03d..9865736 100644
#ifdef CONFIG_BUG
extern void netdev_rx_csum_fault(struct net_device *dev);
#else
-@@ -951,6 +1000,19 @@ #endif
+@@ -951,6 +999,18 @@ #endif
extern void linkwatch_run_queue(void);
+static inline int skb_gso_ok(struct sk_buff *skb, int features)
+{
-+ int feature = skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT;
++ int feature = skb_shinfo(skb)->gso_size ?
++ skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT : 0;
+ return (features & feature) == feature;
+}
+
+static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb)
+{
-+ return skb_is_gso(skb) &&
-+ (!skb_gso_ok(skb, dev->features) ||
-+ unlikely(skb->ip_summed != CHECKSUM_HW));
++ return !skb_gso_ok(skb, dev->features);
+}
+
#endif /* __KERNEL__ */
#endif /* _LINUX_DEV_H */
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
-index ad7cc22..adfe3a8 100644
+index ad7cc22..b19d45d 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -134,9 +134,10 @@ struct skb_frag_struct {
@@ -1063,17 +1041,6 @@ index ad7cc22..adfe3a8 100644
static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
int len, void *buffer)
-@@ -1377,5 +1403,10 @@ #else /* CONFIG_NETFILTER */
- static inline void nf_reset(struct sk_buff *skb) {}
- #endif /* CONFIG_NETFILTER */
-
-+static inline int skb_is_gso(const struct sk_buff *skb)
-+{
-+ return skb_shinfo(skb)->gso_size;
-+}
-+
- #endif /* __KERNEL__ */
- #endif /* _LINUX_SKBUFF_H */
diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
index b94d1ad..75b5b93 100644
--- a/include/net/pkt_sched.h
@@ -1096,14 +1063,13 @@ index b94d1ad..75b5b93 100644
extern int tc_classify(struct sk_buff *skb, struct tcf_proto *tp,
diff --git a/include/net/protocol.h b/include/net/protocol.h
-index 6dc5970..d516c58 100644
+index 6dc5970..0d2dcdb 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
-@@ -37,6 +37,9 @@ #define MAX_INET_PROTOS 256 /* Must be
+@@ -37,6 +37,8 @@ #define MAX_INET_PROTOS 256 /* Must be
struct net_protocol {
int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, u32 info);
-+ int (*gso_send_check)(struct sk_buff *skb);
+ struct sk_buff *(*gso_segment)(struct sk_buff *skb,
+ int features);
int no_policy;
@@ -1128,7 +1094,7 @@ index f63d0d5..a8e8d21 100644
}
diff --git a/include/net/tcp.h b/include/net/tcp.h
-index 77f21c6..22dbbac 100644
+index 77f21c6..70e1d5f 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -552,13 +552,13 @@ #include <net/tcp_ecn.h>
@@ -1147,11 +1113,10 @@ index 77f21c6..22dbbac 100644
}
static inline void tcp_dec_pcount_approx(__u32 *count,
-@@ -1063,6 +1063,9 @@ extern struct request_sock_ops tcp_reque
+@@ -1063,6 +1063,8 @@ extern struct request_sock_ops tcp_reque
extern int tcp_v4_destroy_sock(struct sock *sk);
-+extern int tcp_v4_gso_send_check(struct sk_buff *skb);
+extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features);
+
#ifdef CONFIG_PROC_FS
@@ -1205,7 +1170,7 @@ index 0b33a7b..180e79b 100644
+ NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_GSO_ROBUST;
}
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
-index 2d24fb4..b34e76f 100644
+index 2d24fb4..00b1128 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -32,7 +32,7 @@ static inline int should_deliver(const s
@@ -1213,7 +1178,7 @@ index 2d24fb4..b34e76f 100644
{
/* drop mtu oversized packets except tso */
- if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->tso_size)
-+ if (skb->len > skb->dev->mtu && !skb_is_gso(skb))
++ if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->gso_size)
kfree_skb(skb);
else {
#ifdef CONFIG_BRIDGE_NETFILTER
@@ -1257,7 +1222,7 @@ index f36b35e..0617146 100644
/* called with RTNL */
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
-index 9e27373..b2dba74 100644
+index 9e27373..588207f 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -743,7 +743,7 @@ static int br_nf_dev_queue_xmit(struct s
@@ -1265,12 +1230,12 @@ index 9e27373..b2dba74 100644
if (skb->protocol == htons(ETH_P_IP) &&
skb->len > skb->dev->mtu &&
- !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size))
-+ !skb_is_gso(skb))
++ !skb_shinfo(skb)->gso_size)
return ip_fragment(skb, br_dev_queue_push_xmit);
else
return br_dev_queue_push_xmit(skb);
diff --git a/net/core/dev.c b/net/core/dev.c
-index 12a214c..e814a89 100644
+index 12a214c..32e1056 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -115,6 +115,7 @@ #include <linux/wireless.h> /* Note : w
@@ -1290,35 +1255,7 @@ index 12a214c..e814a89 100644
{
struct packet_type *ptype;
-@@ -1082,9 +1083,17 @@ int skb_checksum_help(struct sk_buff *sk
- unsigned int csum;
- int ret = 0, offset = skb->h.raw - skb->data;
-
-- if (inward) {
-- skb->ip_summed = CHECKSUM_NONE;
-- goto out;
-+ if (inward)
-+ goto out_set_summed;
-+
-+ if (unlikely(skb_shinfo(skb)->gso_size)) {
-+ static int warned;
-+
-+ WARN_ON(!warned);
-+ warned = 1;
-+
-+ /* Let GSO fix up the checksum. */
-+ goto out_set_summed;
- }
-
- if (skb_cloned(skb)) {
-@@ -1101,11 +1110,70 @@ int skb_checksum_help(struct sk_buff *sk
- BUG_ON(skb->csum + 2 > offset);
-
- *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
-+
-+out_set_summed:
- skb->ip_summed = CHECKSUM_NONE;
- out:
+@@ -1106,6 +1107,45 @@ out:
return ret;
}
@@ -1337,35 +1274,17 @@ index 12a214c..e814a89 100644
+ struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
+ struct packet_type *ptype;
+ int type = skb->protocol;
-+ int err;
+
+ 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);
+
-+ if (unlikely(skb->ip_summed != CHECKSUM_HW)) {
-+ static int warned;
-+
-+ WARN_ON(!warned);
-+ warned = 1;
-+
-+ if (skb_header_cloned(skb) &&
-+ (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
-+ return ERR_PTR(err);
-+ }
-+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) {
+ if (ptype->type == type && !ptype->dev && ptype->gso_segment) {
-+ if (unlikely(skb->ip_summed != CHECKSUM_HW)) {
-+ err = ptype->gso_send_check(skb);
-+ segs = ERR_PTR(err);
-+ if (err || skb_gso_ok(skb, features))
-+ break;
-+ __skb_push(skb, skb->data - skb->nh.raw);
-+ }
+ segs = ptype->gso_segment(skb, features);
+ break;
+ }
@@ -1382,7 +1301,7 @@ index 12a214c..e814a89 100644
/* Take action when hardware reception checksum errors are detected. */
#ifdef CONFIG_BUG
void netdev_rx_csum_fault(struct net_device *dev)
-@@ -1142,75 +1210,108 @@ #else
+@@ -1142,75 +1182,108 @@ #else
#define illegal_highdma(dev, skb) (0)
#endif
@@ -1550,7 +1469,7 @@ index 12a214c..e814a89 100644
} \
}
-@@ -1246,9 +1347,13 @@ int dev_queue_xmit(struct sk_buff *skb)
+@@ -1246,9 +1319,13 @@ int dev_queue_xmit(struct sk_buff *skb)
struct Qdisc *q;
int rc = -ENOMEM;
@@ -1565,7 +1484,7 @@ index 12a214c..e814a89 100644
goto out_kfree_skb;
/* Fragmented skb is linearized if device does not support SG,
-@@ -1257,25 +1362,26 @@ int dev_queue_xmit(struct sk_buff *skb)
+@@ -1257,25 +1334,26 @@ int dev_queue_xmit(struct sk_buff *skb)
*/
if (skb_shinfo(skb)->nr_frags &&
(!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
@@ -1595,7 +1514,7 @@ index 12a214c..e814a89 100644
/* Updates of qdisc are serialized by queue_lock.
* The struct Qdisc which is pointed to by qdisc is now a
-@@ -1309,8 +1415,8 @@ #endif
+@@ -1309,8 +1387,8 @@ #endif
/* The device has no queue. Common case for software devices:
loopback, all the sorts of tunnels...
@@ -1606,7 +1525,7 @@ index 12a214c..e814a89 100644
counters.)
However, it is possible, that they rely on protection
made by us here.
-@@ -1326,11 +1432,8 @@ #endif
+@@ -1326,11 +1404,8 @@ #endif
HARD_TX_LOCK(dev, cpu);
if (!netif_queue_stopped(dev)) {
@@ -1619,7 +1538,7 @@ index 12a214c..e814a89 100644
HARD_TX_UNLOCK(dev);
goto out;
}
-@@ -1349,13 +1452,13 @@ #endif
+@@ -1349,13 +1424,13 @@ #endif
}
rc = -ENETDOWN;
@@ -1635,7 +1554,7 @@ index 12a214c..e814a89 100644
return rc;
}
-@@ -2670,7 +2773,7 @@ int register_netdevice(struct net_device
+@@ -2670,7 +2745,7 @@ int register_netdevice(struct net_device
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
spin_lock_init(&dev->queue_lock);
@@ -1644,7 +1563,7 @@ index 12a214c..e814a89 100644
dev->xmit_lock_owner = -1;
#ifdef CONFIG_NET_CLS_ACT
spin_lock_init(&dev->ingress_lock);
-@@ -2714,9 +2817,7 @@ #endif
+@@ -2714,9 +2789,7 @@ #endif
/* Fix illegal SG+CSUM combinations. */
if ((dev->features & NETIF_F_SG) &&
@@ -1655,7 +1574,7 @@ index 12a214c..e814a89 100644
printk("%s: Dropping NETIF_F_SG since no checksum feature.\n",
dev->name);
dev->features &= ~NETIF_F_SG;
-@@ -3268,7 +3369,6 @@ subsys_initcall(net_dev_init);
+@@ -3268,7 +3341,6 @@ subsys_initcall(net_dev_init);
EXPORT_SYMBOL(__dev_get_by_index);
EXPORT_SYMBOL(__dev_get_by_name);
EXPORT_SYMBOL(__dev_remove_pack);
@@ -2123,7 +2042,7 @@ index 3407f19..a0a25e0 100644
switch(flags & DN_RT_CNTL_MSK) {
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
-index 97c276f..0a8c559 100644
+index 97c276f..5ba719e 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -68,6 +68,7 @@
@@ -2134,44 +2053,10 @@ index 97c276f..0a8c559 100644
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
-@@ -1084,6 +1085,88 @@ int inet_sk_rebuild_header(struct sock *
+@@ -1084,6 +1085,54 @@ int inet_sk_rebuild_header(struct sock *
EXPORT_SYMBOL(inet_sk_rebuild_header);
-+static int inet_gso_send_check(struct sk_buff *skb)
-+{
-+ struct iphdr *iph;
-+ struct net_protocol *ops;
-+ int proto;
-+ int ihl;
-+ int err = -EINVAL;
-+
-+ if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
-+ goto out;
-+
-+ iph = skb->nh.iph;
-+ ihl = iph->ihl * 4;
-+ if (ihl < sizeof(*iph))
-+ goto out;
-+
-+ if (unlikely(!pskb_may_pull(skb, ihl)))
-+ goto out;
-+
-+ skb->h.raw = __skb_pull(skb, ihl);
-+ iph = skb->nh.iph;
-+ proto = iph->protocol & (MAX_INET_PROTOS - 1);
-+ err = -EPROTONOSUPPORT;
-+
-+ rcu_read_lock();
-+ ops = rcu_dereference(inet_protos[proto]);
-+ if (likely(ops && ops->gso_send_check))
-+ err = ops->gso_send_check(skb);
-+ rcu_read_unlock();
-+
-+out:
-+ return err;
-+}
-+
+static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
@@ -2223,26 +2108,24 @@ index 97c276f..0a8c559 100644
#ifdef CONFIG_IP_MULTICAST
static struct net_protocol igmp_protocol = {
.handler = igmp_rcv,
-@@ -1093,6 +1176,8 @@ #endif
+@@ -1093,6 +1142,7 @@ #endif
static struct net_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
-+ .gso_send_check = tcp_v4_gso_send_check,
+ .gso_segment = tcp_tso_segment,
.no_policy = 1,
};
-@@ -1138,6 +1223,8 @@ static int ipv4_proc_init(void);
+@@ -1138,6 +1188,7 @@ static int ipv4_proc_init(void);
static struct packet_type ip_packet_type = {
.type = __constant_htons(ETH_P_IP),
.func = ip_rcv,
-+ .gso_send_check = inet_gso_send_check,
+ .gso_segment = inet_gso_segment,
};
static int __init inet_init(void)
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
-index 8dcba38..2de887c 100644
+index 8dcba38..19c3c73 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -210,8 +210,7 @@ #if defined(CONFIG_NETFILTER) && defined
@@ -2251,7 +2134,7 @@ index 8dcba38..2de887c 100644
#endif
- if (skb->len > dst_mtu(skb->dst) &&
- !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size))
-+ if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
++ if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size)
return ip_fragment(skb, ip_finish_output2);
else
return ip_finish_output2(skb);
@@ -2299,7 +2182,7 @@ index 8dcba38..2de887c 100644
int i;
- if (skb_shinfo(skb)->ufo_size)
-+ if (skb_is_gso(skb))
++ if (skb_shinfo(skb)->gso_size)
len = size;
else {
@@ -2342,7 +2225,7 @@ index d64e2ec..7494823 100644
err = ipcomp_compress(x, skb);
iph = skb->nh.iph;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
-index 00aa80e..30c81a8 100644
+index 00aa80e..84130c9 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -257,6 +257,7 @@ #include <linux/smp_lock.h>
@@ -2398,7 +2281,7 @@ index 00aa80e..30c81a8 100644
from += copy;
copied += copy;
-@@ -2026,6 +2021,77 @@ int tcp_getsockopt(struct sock *sk, int
+@@ -2026,6 +2021,71 @@ int tcp_getsockopt(struct sock *sk, int
}
@@ -2423,19 +2306,13 @@ index 00aa80e..30c81a8 100644
+ if (!pskb_may_pull(skb, thlen))
+ goto out;
+
++ segs = NULL;
++ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST))
++ goto out;
++
+ oldlen = (u16)~skb->len;
+ __skb_pull(skb, thlen);
+
-+ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
-+ /* Packet is from an untrusted source, reset gso_segs. */
-+ int mss = skb_shinfo(skb)->gso_size;
-+
-+ skb_shinfo(skb)->gso_segs = (skb->len + mss - 1) / mss;
-+
-+ segs = NULL;
-+ goto out;
-+ }
-+
+ segs = skb_segment(skb, features);
+ if (IS_ERR(segs))
+ goto out;
@@ -2489,35 +2366,6 @@ index e9a54ae..defe77a 100644
break;
pcount = tcp_skb_pcount(skb);
}
-diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
-index 233bdf2..b4240b4 100644
---- a/net/ipv4/tcp_ipv4.c
-+++ b/net/ipv4/tcp_ipv4.c
-@@ -495,6 +495,24 @@ void tcp_v4_send_check(struct sock *sk,
- }
- }
-
-+int tcp_v4_gso_send_check(struct sk_buff *skb)
-+{
-+ struct iphdr *iph;
-+ struct tcphdr *th;
-+
-+ if (!pskb_may_pull(skb, sizeof(*th)))
-+ return -EINVAL;
-+
-+ iph = skb->nh.iph;
-+ th = skb->h.th;
-+
-+ th->check = 0;
-+ th->check = ~tcp_v4_check(th, skb->len, iph->saddr, iph->daddr, 0);
-+ skb->csum = offsetof(struct tcphdr, check);
-+ skb->ip_summed = CHECKSUM_HW;
-+ return 0;
-+}
-+
- /*
- * This routine will send an RST to the other tcp.
- *
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 310f2e6..ee01f69 100644
--- a/net/ipv4/tcp_output.c
@@ -2638,7 +2486,7 @@ index 310f2e6..ee01f69 100644
/* Use a previous sequence. This should cause the other
* end to send an ack. Don't queue or clone SKB, just
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
-index 32ad229..62ead52 100644
+index 32ad229..737c1db 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -9,6 +9,8 @@
@@ -2692,7 +2540,7 @@ index 32ad229..62ead52 100644
+ }
+#endif
+
-+ if (!skb_is_gso(skb))
++ if (!skb_shinfo(skb)->gso_size)
+ return xfrm4_output_finish2(skb);
+
+ skb->protocol = htons(ETH_P_IP);
@@ -2727,7 +2575,7 @@ index 32ad229..62ead52 100644
{
return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev,
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
-index 5bf70b1..33a5850 100644
+index 5bf70b1..cf5d17e 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -147,7 +147,7 @@ static int ip6_output2(struct sk_buff *s
@@ -2735,7 +2583,7 @@ index 5bf70b1..33a5850 100644
int ip6_output(struct sk_buff *skb)
{
- if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->ufo_size) ||
-+ if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
++ if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) ||
dst_allfrag(skb->dst))
return ip6_fragment(skb, ip6_output2);
else
@@ -2790,7 +2638,7 @@ index d511a88..ef56d5d 100644
/* compression */
plen = skb->len - hdr_len;
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
-index 8024217..e9ea338 100644
+index 8024217..39bdeec 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -151,7 +151,7 @@ error_nolock:
@@ -2819,7 +2667,7 @@ index 8024217..e9ea338 100644
+{
+ struct sk_buff *segs;
+
-+ if (!skb_is_gso(skb))
++ if (!skb_shinfo(skb)->gso_size)
+ return xfrm6_output_finish2(skb);
+
+ skb->protocol = htons(ETH_P_IP);
diff --git a/patches/linux-2.6.16.13/net-gso-1-check-dodgy.patch b/patches/linux-2.6.16.13/net-gso-1-check-dodgy.patch
new file mode 100644
index 0000000000..ab8812af61
--- /dev/null
+++ b/patches/linux-2.6.16.13/net-gso-1-check-dodgy.patch
@@ -0,0 +1,27 @@
+diff -urp a/net/ipv4/tcp.c b/net/ipv4/tcp.c
+--- a/net/ipv4/tcp.c 2006-07-25 14:42:53.194910626 +0100
++++ b/net/ipv4/tcp.c 2006-07-25 14:41:00.955501910 +0100
+@@ -2042,13 +2042,19 @@ struct sk_buff *tcp_tso_segment(struct s
+ if (!pskb_may_pull(skb, thlen))
+ goto out;
+
+- segs = NULL;
+- if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST))
+- goto out;
+-
+ oldlen = (u16)~skb->len;
+ __skb_pull(skb, thlen);
+
++ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
++ /* Packet is from an untrusted source, reset gso_segs. */
++ int mss = skb_shinfo(skb)->gso_size;
++
++ skb_shinfo(skb)->gso_segs = (skb->len + mss - 1) / mss;
++
++ segs = NULL;
++ goto out;
++ }
++
+ segs = skb_segment(skb, features);
+ if (IS_ERR(segs))
+ goto out;
diff --git a/patches/linux-2.6.16.13/net-gso-2-checksum-fix.patch b/patches/linux-2.6.16.13/net-gso-2-checksum-fix.patch
new file mode 100644
index 0000000000..d8b7ba7a75
--- /dev/null
+++ b/patches/linux-2.6.16.13/net-gso-2-checksum-fix.patch
@@ -0,0 +1,451 @@
+diff -urp a/drivers/net/bnx2.c b/drivers/net/bnx2.c
+--- a/drivers/net/bnx2.c 2006-07-25 14:41:00.905507519 +0100
++++ b/drivers/net/bnx2.c 2006-07-25 14:36:00.288561400 +0100
+@@ -1593,7 +1593,7 @@ bnx2_tx_int(struct bnx2 *bp)
+ skb = tx_buf->skb;
+ #ifdef BCM_TSO
+ /* partial BD completions possible with TSO packets */
+- if (skb_shinfo(skb)->gso_size) {
++ if (skb_is_gso(skb)) {
+ u16 last_idx, last_ring_idx;
+
+ last_idx = sw_cons +
+diff -urp a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c
+--- a/drivers/net/chelsio/sge.c 2006-07-25 14:41:00.908507183 +0100
++++ b/drivers/net/chelsio/sge.c 2006-07-25 14:36:00.291561087 +0100
+@@ -1419,7 +1419,7 @@ int t1_start_xmit(struct sk_buff *skb, s
+ struct cpl_tx_pkt *cpl;
+
+ #ifdef NETIF_F_TSO
+- if (skb_shinfo(skb)->gso_size) {
++ if (skb_is_gso(skb)) {
+ int eth_type;
+ struct cpl_tx_pkt_lso *hdr;
+
+diff -urp a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
+--- a/drivers/net/e1000/e1000_main.c 2006-07-25 14:41:00.910506958 +0100
++++ b/drivers/net/e1000/e1000_main.c 2006-07-25 14:36:00.293560878 +0100
+@@ -2526,7 +2526,7 @@ e1000_tso(struct e1000_adapter *adapter,
+ uint8_t ipcss, ipcso, tucss, tucso, hdr_len;
+ int err;
+
+- if (skb_shinfo(skb)->gso_size) {
++ if (skb_is_gso(skb)) {
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (err)
+@@ -2651,7 +2651,7 @@ e1000_tx_map(struct e1000_adapter *adapt
+ * tso gets written back prematurely before the data is fully
+ * DMAd to the controller */
+ if (!skb->data_len && tx_ring->last_tx_tso &&
+- !skb_shinfo(skb)->gso_size) {
++ !skb_is_gso(skb)) {
+ tx_ring->last_tx_tso = 0;
+ size -= 4;
+ }
+@@ -2934,8 +2934,7 @@ e1000_xmit_frame(struct sk_buff *skb, st
+
+ #ifdef NETIF_F_TSO
+ /* Controller Erratum workaround */
+- if (!skb->data_len && tx_ring->last_tx_tso &&
+- !skb_shinfo(skb)->gso_size)
++ if (!skb->data_len && tx_ring->last_tx_tso && !skb_is_gso(skb))
+ count++;
+ #endif
+
+diff -urp a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
+--- a/drivers/net/forcedeth.c 2006-07-25 14:41:00.912506734 +0100
++++ b/drivers/net/forcedeth.c 2006-07-25 14:36:00.295560669 +0100
+@@ -1105,7 +1105,7 @@ static int nv_start_xmit(struct sk_buff
+ np->tx_skbuff[nr] = skb;
+
+ #ifdef NETIF_F_TSO
+- if (skb_shinfo(skb)->gso_size)
++ if (skb_is_gso(skb))
+ tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT);
+ else
+ #endif
+diff -urp a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
+--- a/drivers/net/ixgb/ixgb_main.c 2006-07-25 14:41:00.915506397 +0100
++++ b/drivers/net/ixgb/ixgb_main.c 2006-07-25 14:36:00.298560355 +0100
+@@ -1163,7 +1163,7 @@ ixgb_tso(struct ixgb_adapter *adapter, s
+ uint16_t ipcse, tucse, mss;
+ int err;
+
+- if(likely(skb_shinfo(skb)->gso_size)) {
++ if (likely(skb_is_gso(skb))) {
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (err)
+diff -urp a/drivers/net/loopback.c b/drivers/net/loopback.c
+--- a/drivers/net/loopback.c 2006-07-25 14:41:00.915506397 +0100
++++ b/drivers/net/loopback.c 2006-07-25 14:36:00.298560355 +0100
+@@ -139,7 +139,7 @@ static int loopback_xmit(struct sk_buff
+ #endif
+
+ #ifdef LOOPBACK_TSO
+- if (skb_shinfo(skb)->gso_size) {
++ if (skb_is_gso(skb)) {
+ BUG_ON(skb->protocol != htons(ETH_P_IP));
+ BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP);
+
+diff -urp a/drivers/net/sky2.c b/drivers/net/sky2.c
+--- a/drivers/net/sky2.c 2006-07-25 14:41:00.924505388 +0100
++++ b/drivers/net/sky2.c 2006-07-25 14:36:00.306559519 +0100
+@@ -1125,7 +1125,7 @@ static unsigned tx_le_req(const struct s
+ count = sizeof(dma_addr_t) / sizeof(u32);
+ count += skb_shinfo(skb)->nr_frags * count;
+
+- if (skb_shinfo(skb)->gso_size)
++ if (skb_is_gso(skb))
+ ++count;
+
+ if (skb->ip_summed == CHECKSUM_HW)
+diff -urp a/drivers/net/typhoon.c b/drivers/net/typhoon.c
+--- a/drivers/net/typhoon.c 2006-07-25 14:41:00.931504603 +0100
++++ b/drivers/net/typhoon.c 2006-07-25 14:36:00.314558683 +0100
+@@ -805,7 +805,7 @@ typhoon_start_tx(struct sk_buff *skb, st
+ * If problems develop with TSO, check this first.
+ */
+ numDesc = skb_shinfo(skb)->nr_frags + 1;
+- if(skb_tso_size(skb))
++ if (skb_is_gso(skb))
+ numDesc++;
+
+ /* When checking for free space in the ring, we need to also
+@@ -845,7 +845,7 @@ typhoon_start_tx(struct sk_buff *skb, st
+ TYPHOON_TX_PF_VLAN_TAG_SHIFT);
+ }
+
+- if(skb_tso_size(skb)) {
++ if (skb_is_gso(skb)) {
+ first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT;
+ first_txd->numDesc++;
+
+diff -urp a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
+--- a/drivers/s390/net/qeth_main.c 2006-07-25 14:41:00.939503705 +0100
++++ b/drivers/s390/net/qeth_main.c 2006-07-25 14:36:00.321557952 +0100
+@@ -4454,7 +4454,7 @@ qeth_send_packet(struct qeth_card *card,
+ queue = card->qdio.out_qs
+ [qeth_get_priority_queue(card, skb, ipv, cast_type)];
+
+- if (skb_shinfo(skb)->gso_size)
++ if (skb_is_gso(skb))
+ large_send = card->options.large_send;
+
+ /*are we able to do TSO ? If so ,prepare and send it from here */
+@@ -4501,8 +4501,7 @@ qeth_send_packet(struct qeth_card *card,
+ card->stats.tx_packets++;
+ card->stats.tx_bytes += skb->len;
+ #ifdef CONFIG_QETH_PERF_STATS
+- if (skb_shinfo(skb)->gso_size &&
+- !(large_send == QETH_LARGE_SEND_NO)) {
++ if (skb_is_gso(skb) && !(large_send == QETH_LARGE_SEND_NO)) {
+ card->perf_stats.large_send_bytes += skb->len;
+ card->perf_stats.large_send_cnt++;
+ }
+diff -urp a/include/linux/netdevice.h b/include/linux/netdevice.h
+--- a/include/linux/netdevice.h 2006-07-25 14:41:00.940503593 +0100
++++ b/include/linux/netdevice.h 2006-07-25 14:36:00.323557743 +0100
+@@ -541,6 +541,7 @@ struct packet_type {
+ struct net_device *);
+ struct sk_buff *(*gso_segment)(struct sk_buff *skb,
+ int features);
++ int (*gso_send_check)(struct sk_buff *skb);
+ void *af_packet_priv;
+ struct list_head list;
+ };
+@@ -1001,14 +1002,15 @@ extern void linkwatch_run_queue(void);
+
+ static inline int skb_gso_ok(struct sk_buff *skb, int features)
+ {
+- int feature = skb_shinfo(skb)->gso_size ?
+- skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT : 0;
++ int feature = skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT;
+ return (features & feature) == feature;
+ }
+
+ static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb)
+ {
+- return !skb_gso_ok(skb, dev->features);
++ return skb_is_gso(skb) &&
++ (!skb_gso_ok(skb, dev->features) ||
++ unlikely(skb->ip_summed != CHECKSUM_HW));
+ }
+
+ #endif /* __KERNEL__ */
+diff -urp a/include/linux/skbuff.h b/include/linux/skbuff.h
+--- a/include/linux/skbuff.h 2006-07-25 14:41:00.941503481 +0100
++++ b/include/linux/skbuff.h 2006-07-25 14:36:00.323557743 +0100
+@@ -1403,5 +1403,10 @@ static inline void nf_bridge_get(struct
+ static inline void nf_reset(struct sk_buff *skb) {}
+ #endif /* CONFIG_NETFILTER */
+
++static inline int skb_is_gso(const struct sk_buff *skb)
++{
++ return skb_shinfo(skb)->gso_size;
++}
++
+ #endif /* __KERNEL__ */
+ #endif /* _LINUX_SKBUFF_H */
+diff -urp a/include/net/protocol.h b/include/net/protocol.h
+--- a/include/net/protocol.h 2006-07-25 14:41:00.942503369 +0100
++++ b/include/net/protocol.h 2006-07-25 14:36:00.324557639 +0100
+@@ -37,6 +37,7 @@
+ struct net_protocol {
+ int (*handler)(struct sk_buff *skb);
+ void (*err_handler)(struct sk_buff *skb, u32 info);
++ int (*gso_send_check)(struct sk_buff *skb);
+ struct sk_buff *(*gso_segment)(struct sk_buff *skb,
+ int features);
+ int no_policy;
+diff -urp a/include/net/tcp.h b/include/net/tcp.h
+--- a/include/net/tcp.h 2006-07-25 14:41:00.943503256 +0100
++++ b/include/net/tcp.h 2006-07-25 14:36:00.325557534 +0100
+@@ -1063,6 +1063,7 @@ extern struct request_sock_ops tcp_reque
+
+ extern int tcp_v4_destroy_sock(struct sock *sk);
+
++extern int tcp_v4_gso_send_check(struct sk_buff *skb);
+ extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features);
+
+ #ifdef CONFIG_PROC_FS
+diff -urp a/net/bridge/br_forward.c b/net/bridge/br_forward.c
+--- a/net/bridge/br_forward.c 2006-07-25 14:41:00.944503144 +0100
++++ b/net/bridge/br_forward.c 2006-07-25 14:36:00.326557430 +0100
+@@ -32,7 +32,7 @@ static inline int should_deliver(const s
+ int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ /* drop mtu oversized packets except tso */
+- if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->gso_size)
++ if (skb->len > skb->dev->mtu && !skb_is_gso(skb))
+ kfree_skb(skb);
+ else {
+ #ifdef CONFIG_BRIDGE_NETFILTER
+diff -urp a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
+--- a/net/bridge/br_netfilter.c 2006-07-25 14:41:00.945503032 +0100
++++ b/net/bridge/br_netfilter.c 2006-07-25 14:36:00.327557325 +0100
+@@ -743,7 +743,7 @@ static int br_nf_dev_queue_xmit(struct s
+ {
+ if (skb->protocol == htons(ETH_P_IP) &&
+ skb->len > skb->dev->mtu &&
+- !skb_shinfo(skb)->gso_size)
++ !skb_is_gso(skb))
+ return ip_fragment(skb, br_dev_queue_push_xmit);
+ else
+ return br_dev_queue_push_xmit(skb);
+diff -urp a/net/core/dev.c b/net/core/dev.c
+--- a/net/core/dev.c 2006-07-25 14:41:00.947502808 +0100
++++ b/net/core/dev.c 2006-07-25 14:36:00.329557116 +0100
+@@ -1083,9 +1083,17 @@ int skb_checksum_help(struct sk_buff *sk
+ unsigned int csum;
+ int ret = 0, offset = skb->h.raw - skb->data;
+
+- if (inward) {
+- skb->ip_summed = CHECKSUM_NONE;
+- goto out;
++ if (inward)
++ goto out_set_summed;
++
++ if (unlikely(skb_shinfo(skb)->gso_size)) {
++ static int warned;
++
++ WARN_ON(!warned);
++ warned = 1;
++
++ /* Let GSO fix up the checksum. */
++ goto out_set_summed;
+ }
+
+ if (skb_cloned(skb)) {
+@@ -1102,6 +1110,8 @@ int skb_checksum_help(struct sk_buff *sk
+ BUG_ON(skb->csum + 2 > offset);
+
+ *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
++
++out_set_summed:
+ skb->ip_summed = CHECKSUM_NONE;
+ out:
+ return ret;
+@@ -1122,17 +1132,35 @@ struct sk_buff *skb_gso_segment(struct s
+ struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
+ struct packet_type *ptype;
+ int type = skb->protocol;
++ int err;
+
+ 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);
+
++ if (unlikely(skb->ip_summed != CHECKSUM_HW)) {
++ static int warned;
++
++ WARN_ON(!warned);
++ warned = 1;
++
++ if (skb_header_cloned(skb) &&
++ (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
++ return ERR_PTR(err);
++ }
++
+ rcu_read_lock();
+ list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) {
+ if (ptype->type == type && !ptype->dev && ptype->gso_segment) {
++ if (unlikely(skb->ip_summed != CHECKSUM_HW)) {
++ err = ptype->gso_send_check(skb);
++ segs = ERR_PTR(err);
++ if (err || skb_gso_ok(skb, features))
++ break;
++ __skb_push(skb, skb->data - skb->nh.raw);
++ }
+ segs = ptype->gso_segment(skb, features);
+ break;
+ }
+diff -urp a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
+--- a/net/ipv4/af_inet.c 2006-07-25 14:41:00.952502247 +0100
++++ b/net/ipv4/af_inet.c 2006-07-25 14:36:00.334556594 +0100
+@@ -1085,6 +1085,40 @@ int inet_sk_rebuild_header(struct sock *
+
+ EXPORT_SYMBOL(inet_sk_rebuild_header);
+
++static int inet_gso_send_check(struct sk_buff *skb)
++{
++ struct iphdr *iph;
++ struct net_protocol *ops;
++ int proto;
++ int ihl;
++ int err = -EINVAL;
++
++ if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
++ goto out;
++
++ iph = skb->nh.iph;
++ ihl = iph->ihl * 4;
++ if (ihl < sizeof(*iph))
++ goto out;
++
++ if (unlikely(!pskb_may_pull(skb, ihl)))
++ goto out;
++
++ skb->h.raw = __skb_pull(skb, ihl);
++ iph = skb->nh.iph;
++ proto = iph->protocol & (MAX_INET_PROTOS - 1);
++ err = -EPROTONOSUPPORT;
++
++ rcu_read_lock();
++ ops = rcu_dereference(inet_protos[proto]);
++ if (likely(ops && ops->gso_send_check))
++ err = ops->gso_send_check(skb);
++ rcu_read_unlock();
++
++out:
++ return err;
++}
++
+ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
+ {
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+@@ -1142,6 +1176,7 @@ static struct net_protocol igmp_protocol
+ static struct net_protocol tcp_protocol = {
+ .handler = tcp_v4_rcv,
+ .err_handler = tcp_v4_err,
++ .gso_send_check = tcp_v4_gso_send_check,
+ .gso_segment = tcp_tso_segment,
+ .no_policy = 1,
+ };
+@@ -1188,6 +1223,7 @@ static int ipv4_proc_init(void);
+ static struct packet_type ip_packet_type = {
+ .type = __constant_htons(ETH_P_IP),
+ .func = ip_rcv,
++ .gso_send_check = inet_gso_send_check,
+ .gso_segment = inet_gso_segment,
+ };
+
+diff -urp a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+--- a/net/ipv4/ip_output.c 2006-07-25 14:41:00.953502135 +0100
++++ b/net/ipv4/ip_output.c 2006-07-25 14:36:00.335556489 +0100
+@@ -210,7 +210,7 @@ static inline int ip_finish_output(struc
+ return dst_output(skb);
+ }
+ #endif
+- if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size)
++ if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
+ return ip_fragment(skb, ip_finish_output2);
+ else
+ return ip_finish_output2(skb);
+@@ -1095,7 +1095,7 @@ ssize_t ip_append_page(struct sock *sk,
+ while (size > 0) {
+ int i;
+
+- if (skb_shinfo(skb)->gso_size)
++ if (skb_is_gso(skb))
+ len = size;
+ else {
+
+diff -urp a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
+--- a/net/ipv4/tcp_ipv4.c 2006-07-25 14:39:15.985080788 +0100
++++ b/net/ipv4/tcp_ipv4.c 2006-07-25 14:36:00.339556071 +0100
+@@ -495,6 +495,24 @@ void tcp_v4_send_check(struct sock *sk,
+ }
+ }
+
++int tcp_v4_gso_send_check(struct sk_buff *skb)
++{
++ struct iphdr *iph;
++ struct tcphdr *th;
++
++ if (!pskb_may_pull(skb, sizeof(*th)))
++ return -EINVAL;
++
++ iph = skb->nh.iph;
++ th = skb->h.th;
++
++ th->check = 0;
++ th->check = ~tcp_v4_check(th, skb->len, iph->saddr, iph->daddr, 0);
++ skb->csum = offsetof(struct tcphdr, check);
++ skb->ip_summed = CHECKSUM_HW;
++ return 0;
++}
++
+ /*
+ * This routine will send an RST to the other tcp.
+ *
+diff -urp a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
+--- a/net/ipv4/xfrm4_output.c 2006-07-25 14:41:00.958501574 +0100
++++ b/net/ipv4/xfrm4_output.c 2006-07-25 14:36:00.341555862 +0100
+@@ -189,7 +189,7 @@ static int xfrm4_output_finish(struct sk
+ }
+ #endif
+
+- if (!skb_shinfo(skb)->gso_size)
++ if (!skb_is_gso(skb))
+ return xfrm4_output_finish2(skb);
+
+ skb->protocol = htons(ETH_P_IP);
+diff -urp a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+--- a/net/ipv6/ip6_output.c 2006-07-25 14:41:00.959501461 +0100
++++ b/net/ipv6/ip6_output.c 2006-07-25 14:36:00.341555862 +0100
+@@ -147,7 +147,7 @@ static int ip6_output2(struct sk_buff *s
+
+ int ip6_output(struct sk_buff *skb)
+ {
+- if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) ||
++ if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
+ dst_allfrag(skb->dst))
+ return ip6_fragment(skb, ip6_output2);
+ else
+diff -urp a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
+--- a/net/ipv6/xfrm6_output.c 2006-07-25 14:41:00.960501349 +0100
++++ b/net/ipv6/xfrm6_output.c 2006-07-25 14:36:00.342555758 +0100
+@@ -179,7 +179,7 @@ static int xfrm6_output_finish(struct sk
+ {
+ struct sk_buff *segs;
+
+- if (!skb_shinfo(skb)->gso_size)
++ if (!skb_is_gso(skb))
+ return xfrm6_output_finish2(skb);
+
+ skb->protocol = htons(ETH_P_IP);