Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
authorLinus Torvalds <torvalds@g5.osdl.org>
Fri, 30 Jun 2006 22:40:17 +0000 (15:40 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Fri, 30 Jun 2006 22:40:17 +0000 (15:40 -0700)
* master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6:
  [IPV6]: Added GSO support for TCPv6
  [NET]: Generalise TSO-specific bits from skb_setup_caps
  [IPV6]: Added GSO support for TCPv6
  [IPV6]: Remove redundant length check on input
  [NETFILTER]: SCTP conntrack: fix crash triggered by packet without chunks
  [TG3]: Update version and reldate
  [TG3]: Add TSO workaround using GSO
  [TG3]: Turn on hw fix for ASF problems
  [TG3]: Add rx BD workaround
  [TG3]: Add tg3_netif_stop() in vlan functions
  [TCP]: Reset gso_segs if packet is dodgy

23 files changed:
drivers/net/s2io.c
drivers/net/tg3.c
drivers/net/tg3.h
include/linux/netdevice.h
include/linux/skbuff.h
include/net/ip6_route.h
include/net/protocol.h
include/net/sock.h
include/net/tcp.h
include/net/tcp_ecn.h
net/ipv4/ip_output.c
net/ipv4/netfilter/ip_conntrack_proto_sctp.c
net/ipv4/tcp.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv6/af_inet6.c
net/ipv6/exthdrs.c
net/ipv6/inet6_connection_sock.c
net/ipv6/ip6_input.c
net/ipv6/ip6_output.c
net/ipv6/ipv6_sockglue.c
net/ipv6/tcp_ipv6.c
net/netfilter/nf_conntrack_proto_sctp.c

index 25d289eaaa204c44598612ae84583a47caaf6ae2..001344c0763ed3eadba132cc37e6e6996145d0ef 100644 (file)
@@ -3959,7 +3959,7 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
        txdp->Control_2 = 0;
 #ifdef NETIF_F_TSO
        mss = skb_shinfo(skb)->gso_size;
-       if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4) {
+       if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
                txdp->Control_1 |= TXD_TCP_LSO_EN;
                txdp->Control_1 |= TXD_TCP_LSO_MSS(mss);
        }
@@ -3979,7 +3979,7 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        frg_len = skb->len - skb->data_len;
-       if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) {
+       if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP) {
                int ufo_size;
 
                ufo_size = skb_shinfo(skb)->gso_size;
@@ -4008,7 +4008,7 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
        txdp->Host_Control = (unsigned long) skb;
        txdp->Control_1 |= TXD_BUFFER0_SIZE(frg_len);
 
-       if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4)
+       if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP)
                txdp->Control_1 |= TXD_UFO_EN;
 
        frg_cnt = skb_shinfo(skb)->nr_frags;
@@ -4023,12 +4023,12 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
                    (sp->pdev, frag->page, frag->page_offset,
                     frag->size, PCI_DMA_TODEVICE);
                txdp->Control_1 = TXD_BUFFER0_SIZE(frag->size);
-               if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4)
+               if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP)
                        txdp->Control_1 |= TXD_UFO_EN;
        }
        txdp->Control_1 |= TXD_GATHER_CODE_LAST;
 
-       if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4)
+       if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP)
                frg_cnt++; /* as Txd0 was used for inband header */
 
        tx_fifo = mac_control->tx_FIFO_start[queue];
@@ -4042,7 +4042,7 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
        if (mss)
                val64 |= TX_FIFO_SPECIAL_FUNC;
 #endif
-       if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4)
+       if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP)
                val64 |= TX_FIFO_SPECIAL_FUNC;
        writeq(val64, &tx_fifo->List_Control);
 
@@ -7019,6 +7019,9 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
                dev->features |= NETIF_F_HIGHDMA;
 #ifdef NETIF_F_TSO
        dev->features |= NETIF_F_TSO;
+#endif
+#ifdef NETIF_F_TSO6
+       dev->features |= NETIF_F_TSO6;
 #endif
        if (sp->device_type & XFRAME_II_DEVICE) {
                dev->features |= NETIF_F_UFO;
index 6bea2b89a915c35ec0fc001b8ca0a348f7021313..953255e926339b5509176a9bf339b39dc0e3afd3 100644 (file)
@@ -68,8 +68,8 @@
 
 #define DRV_MODULE_NAME                "tg3"
 #define PFX DRV_MODULE_NAME    ": "
-#define DRV_MODULE_VERSION     "3.60"
-#define DRV_MODULE_RELDATE     "June 17, 2006"
+#define DRV_MODULE_VERSION     "3.61"
+#define DRV_MODULE_RELDATE     "June 29, 2006"
 
 #define TG3_DEF_MAC_MODE       0
 #define TG3_DEF_RX_MODE                0
@@ -3194,7 +3194,7 @@ static int tg3_vlan_rx(struct tg3 *tp, struct sk_buff *skb, u16 vlan_tag)
  */
 static int tg3_rx(struct tg3 *tp, int budget)
 {
-       u32 work_mask;
+       u32 work_mask, rx_std_posted = 0;
        u32 sw_idx = tp->rx_rcb_ptr;
        u16 hw_idx;
        int received;
@@ -3221,6 +3221,7 @@ static int tg3_rx(struct tg3 *tp, int budget)
                                                  mapping);
                        skb = tp->rx_std_buffers[desc_idx].skb;
                        post_ptr = &tp->rx_std_ptr;
+                       rx_std_posted++;
                } else if (opaque_key == RXD_OPAQUE_RING_JUMBO) {
                        dma_addr = pci_unmap_addr(&tp->rx_jumbo_buffers[desc_idx],
                                                  mapping);
@@ -3308,6 +3309,15 @@ static int tg3_rx(struct tg3 *tp, int budget)
 
 next_pkt:
                (*post_ptr)++;
+
+               if (unlikely(rx_std_posted >= tp->rx_std_max_post)) {
+                       u32 idx = *post_ptr % TG3_RX_RING_SIZE;
+
+                       tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX +
+                                    TG3_64BIT_REG_LOW, idx);
+                       work_mask &= ~RXD_OPAQUE_RING_STD;
+                       rx_std_posted = 0;
+               }
 next_pkt_nopost:
                sw_idx++;
                sw_idx %= TG3_RX_RCB_RING_SIZE(tp);
@@ -3869,6 +3879,40 @@ out_unlock:
        return NETDEV_TX_OK;
 }
 
+#if TG3_TSO_SUPPORT != 0
+static int tg3_start_xmit_dma_bug(struct sk_buff *, struct net_device *);
+
+/* Use GSO to workaround a rare TSO bug that may be triggered when the
+ * TSO header is greater than 80 bytes.
+ */
+static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb)
+{
+       struct sk_buff *segs, *nskb;
+
+       /* Estimate the number of fragments in the worst case */
+       if (unlikely(TX_BUFFS_AVAIL(tp) <= (skb_shinfo(skb)->gso_segs * 3))) {
+               netif_stop_queue(tp->dev);
+               return NETDEV_TX_BUSY;
+       }
+
+       segs = skb_gso_segment(skb, tp->dev->features & ~NETIF_F_TSO);
+       if (unlikely(IS_ERR(segs)))
+               goto tg3_tso_bug_end;
+
+       do {
+               nskb = segs;
+               segs = segs->next;
+               nskb->next = NULL;
+               tg3_start_xmit_dma_bug(nskb, tp->dev);
+       } while (segs);
+
+tg3_tso_bug_end:
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
+}
+#endif
+
 /* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and
  * support TG3_FLG2_HW_TSO_1 or firmware TSO only.
  */
@@ -3905,7 +3949,7 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev)
        mss = 0;
        if (skb->len > (tp->dev->mtu + ETH_HLEN) &&
            (mss = skb_shinfo(skb)->gso_size) != 0) {
-               int tcp_opt_len, ip_tcp_len;
+               int tcp_opt_len, ip_tcp_len, hdr_len;
 
                if (skb_header_cloned(skb) &&
                    pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
@@ -3916,11 +3960,16 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev)
                tcp_opt_len = ((skb->h.th->doff - 5) * 4);
                ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);
 
+               hdr_len = ip_tcp_len + tcp_opt_len;
+               if (unlikely((ETH_HLEN + hdr_len) > 80) &&
+                            (tp->tg3_flags2 & TG3_FLG2_HW_TSO_1_BUG))
+                       return (tg3_tso_bug(tp, skb));
+
                base_flags |= (TXD_FLAG_CPU_PRE_DMA |
                               TXD_FLAG_CPU_POST_DMA);
 
                skb->nh.iph->check = 0;
-               skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
+               skb->nh.iph->tot_len = htons(mss + hdr_len);
                if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
                        skb->h.th->check = 0;
                        base_flags &= ~TXD_FLAG_TCPUDP_CSUM;
@@ -5980,7 +6029,13 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
        }
 
        /* Setup replenish threshold. */
-       tw32(RCVBDI_STD_THRESH, tp->rx_pending / 8);
+       val = tp->rx_pending / 8;
+       if (val == 0)
+               val = 1;
+       else if (val > tp->rx_std_max_post)
+               val = tp->rx_std_max_post;
+
+       tw32(RCVBDI_STD_THRESH, val);
 
        /* Initialize TG3_BDINFO's at:
         *  RCVDBDI_STD_BD:     standard eth size rx ring
@@ -6140,8 +6195,12 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
 #endif
 
        /* Receive/send statistics. */
-       if ((rdmac_mode & RDMAC_MODE_FIFO_SIZE_128) &&
-           (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE)) {
+       if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) {
+               val = tr32(RCVLPC_STATS_ENABLE);
+               val &= ~RCVLPC_STATSENAB_DACK_FIX;
+               tw32(RCVLPC_STATS_ENABLE, val);
+       } else if ((rdmac_mode & RDMAC_MODE_FIFO_SIZE_128) &&
+                  (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE)) {
                val = tr32(RCVLPC_STATS_ENABLE);
                val &= ~RCVLPC_STATSENAB_LNGBRST_RFIX;
                tw32(RCVLPC_STATS_ENABLE, val);
@@ -8737,6 +8796,9 @@ static void tg3_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
 {
        struct tg3 *tp = netdev_priv(dev);
 
+       if (netif_running(dev))
+               tg3_netif_stop(tp);
+
        tg3_full_lock(tp, 0);
 
        tp->vlgrp = grp;
@@ -8745,16 +8807,25 @@ static void tg3_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
        __tg3_set_rx_mode(dev);
 
        tg3_full_unlock(tp);
+
+       if (netif_running(dev))
+               tg3_netif_start(tp);
 }
 
 static void tg3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
        struct tg3 *tp = netdev_priv(dev);
 
+       if (netif_running(dev))
+               tg3_netif_stop(tp);
+
        tg3_full_lock(tp, 0);
        if (tp->vlgrp)
                tp->vlgrp->vlan_devices[vid] = NULL;
        tg3_full_unlock(tp);
+
+       if (netif_running(dev))
+               tg3_netif_start(tp);
 }
 #endif
 
@@ -10159,8 +10230,14 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
                    GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) {
                        tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2;
                        tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI;
-               } else
-                       tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1;
+               } else {
+                       tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 |
+                                         TG3_FLG2_HW_TSO_1_BUG;
+                       if (GET_ASIC_REV(tp->pci_chip_rev_id) ==
+                               ASIC_REV_5750 &&
+                           tp->pci_chip_rev_id >= CHIPREV_ID_5750_C2)
+                               tp->tg3_flags2 &= ~TG3_FLG2_HW_TSO_1_BUG;
+               }
        }
 
        if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 &&
@@ -10532,6 +10609,16 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
            (tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0)
                tp->rx_offset = 0;
 
+       tp->rx_std_max_post = TG3_RX_RING_SIZE;
+
+       /* Increment the rx prod index on the rx std ring by at most
+        * 8 for these chips to workaround hw errata.
+        */
+       if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 ||
+           GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 ||
+           GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755)
+               tp->rx_std_max_post = 8;
+
        /* By default, disable wake-on-lan.  User can change this
         * using ETHTOOL_SWOL.
         */
index 8209da5dd15fee447c2b989a11215b2f75a8e21b..ba2c98711c88dc9d524f35204652e823b9d4c177 100644 (file)
 #define  CHIPREV_ID_5750_A0             0x4000
 #define  CHIPREV_ID_5750_A1             0x4001
 #define  CHIPREV_ID_5750_A3             0x4003
+#define  CHIPREV_ID_5750_C2             0x4202
 #define  CHIPREV_ID_5752_A0_HW          0x5000
 #define  CHIPREV_ID_5752_A0             0x6000
 #define  CHIPREV_ID_5752_A1             0x6001
 #define  RCVLPC_STATSCTRL_ENABLE        0x00000001
 #define  RCVLPC_STATSCTRL_FASTUPD       0x00000002
 #define RCVLPC_STATS_ENABLE            0x00002018
+#define  RCVLPC_STATSENAB_DACK_FIX      0x00040000
 #define  RCVLPC_STATSENAB_LNGBRST_RFIX  0x00400000
 #define RCVLPC_STATS_INCMASK           0x0000201c
 /* 0x2020 --> 0x2100 unused */
@@ -2137,6 +2139,7 @@ struct tg3 {
        struct tg3_rx_buffer_desc       *rx_std;
        struct ring_info                *rx_std_buffers;
        dma_addr_t                      rx_std_mapping;
+       u32                             rx_std_max_post;
 
        struct tg3_rx_buffer_desc       *rx_jumbo;
        struct ring_info                *rx_jumbo_buffers;
@@ -2191,7 +2194,7 @@ struct tg3 {
 #define TG3_FLAG_INIT_COMPLETE         0x80000000
        u32                             tg3_flags2;
 #define TG3_FLG2_RESTART_TIMER         0x00000001
-/*                                     0x00000002 available */
+#define TG3_FLG2_HW_TSO_1_BUG          0x00000002
 #define TG3_FLG2_NO_ETH_WIRE_SPEED     0x00000004
 #define TG3_FLG2_IS_5788               0x00000008
 #define TG3_FLG2_MAX_RXPEND_64         0x00000010
index aa2d3c12c4d80cffdb6a011ee1d01a0ca57f8af9..85f99f60deea46c3f5af0f24f9c3a806b51dfd7c 100644 (file)
@@ -313,10 +313,12 @@ struct net_device
 
        /* Segmentation offload features */
 #define NETIF_F_GSO_SHIFT      16
+#define NETIF_F_GSO_MASK       0xffff0000
 #define NETIF_F_TSO            (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT)
-#define NETIF_F_UFO            (SKB_GSO_UDPV4 << NETIF_F_GSO_SHIFT)
+#define NETIF_F_UFO            (SKB_GSO_UDP << NETIF_F_GSO_SHIFT)
 #define NETIF_F_GSO_ROBUST     (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT)
-#define NETIF_F_TSO_ECN                (SKB_GSO_TCPV4_ECN << NETIF_F_GSO_SHIFT)
+#define NETIF_F_TSO_ECN                (SKB_GSO_TCP_ECN << NETIF_F_GSO_SHIFT)
+#define NETIF_F_TSO6           (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT)
 
 #define NETIF_F_GEN_CSUM       (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
 #define NETIF_F_ALL_CSUM       (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM)
@@ -991,13 +993,18 @@ extern void dev_seq_stop(struct seq_file *seq, void *v);
 
 extern void linkwatch_run_queue(void);
 
-static inline int skb_gso_ok(struct sk_buff *skb, int features)
+static inline int net_gso_ok(int features, int gso_type)
 {
-       int feature = skb_shinfo(skb)->gso_size ?
-                     skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT : 0;
+       int feature = gso_type << NETIF_F_GSO_SHIFT;
        return (features & feature) == feature;
 }
 
+static inline int skb_gso_ok(struct sk_buff *skb, int features)
+{
+       return net_gso_ok(features, skb_shinfo(skb)->gso_size ?
+                                   skb_shinfo(skb)->gso_type : 0);
+}
+
 static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb)
 {
        return !skb_gso_ok(skb, dev->features);
index 59918be91d0a95c203e43356ae95371861e97132..57d7d4965f9aa0fc13a1cd10c06501d90c67e844 100644 (file)
@@ -171,13 +171,15 @@ enum {
 
 enum {
        SKB_GSO_TCPV4 = 1 << 0,
-       SKB_GSO_UDPV4 = 1 << 1,
+       SKB_GSO_UDP = 1 << 1,
 
        /* This indicates the skb is from an untrusted source. */
        SKB_GSO_DODGY = 1 << 2,
 
        /* This indicates the tcp segment has CWR set. */
-       SKB_GSO_TCPV4_ECN = 1 << 3,
+       SKB_GSO_TCP_ECN = 1 << 3,
+
+       SKB_GSO_TCPV6 = 1 << 4,
 };
 
 /** 
index a398ae5e30f991c427638af44216de453492474f..ab29dafb1a6af2711557a202e70127e882e3b18c 100644 (file)
@@ -146,7 +146,7 @@ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
        struct rt6_info *rt = (struct rt6_info *) dst;
 
        write_lock(&sk->sk_dst_lock);
-       __sk_dst_set(sk, dst);
+       sk_setup_caps(sk, dst);
        np->daddr_cache = daddr;
        np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
        write_unlock(&sk->sk_dst_lock);
index 40b6b9c9973f5dd2756fd3dc4c6439d7676784e6..a225d6371cb12f5d5efa6b4318d1a54004c4bc01 100644 (file)
@@ -50,11 +50,17 @@ struct inet6_protocol
                               struct inet6_skb_parm *opt,
                               int type, int code, int offset,
                               __u32 info);
+
+       struct sk_buff *(*gso_segment)(struct sk_buff *skb,
+                                      int features);
+
        unsigned int    flags;  /* INET6_PROTO_xxx */
 };
 
 #define INET6_PROTO_NOPOLICY   0x1
 #define INET6_PROTO_FINAL      0x2
+/* This should be set for any extension header which is compatible with GSO. */
+#define INET6_PROTO_GSO_EXTHDR 0x4
 #endif
 
 /* This is used to register socket interfaces for IP protocols.  */
index 7136bae48c2f1d98977542f253d7b0b97a055829..7b3d6b856946d7689aa430e157927bef0741eff2 100644 (file)
@@ -140,6 +140,7 @@ struct sock_common {
   *    @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, %SO_OOBINLINE settings
   *    @sk_no_check: %SO_NO_CHECK setting, wether or not checkup packets
   *    @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO)
+  *    @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
   *    @sk_lingertime: %SO_LINGER l_linger setting
   *    @sk_backlog: always used with the per-socket spinlock held
   *    @sk_callback_lock: used with the callbacks in the end of this struct
@@ -211,6 +212,7 @@ struct sock {
        gfp_t                   sk_allocation;
        int                     sk_sndbuf;
        int                     sk_route_caps;
+       int                     sk_gso_type;
        int                     sk_rcvlowat;
        unsigned long           sk_flags;
        unsigned long           sk_lingertime;
@@ -1025,15 +1027,20 @@ extern struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie);
 
 extern struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie);
 
+static inline int sk_can_gso(const struct sock *sk)
+{
+       return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type);
+}
+
 static inline void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
 {
        __sk_dst_set(sk, dst);
        sk->sk_route_caps = dst->dev->features;
        if (sk->sk_route_caps & NETIF_F_GSO)
-               sk->sk_route_caps |= NETIF_F_TSO;
-       if (sk->sk_route_caps & NETIF_F_TSO) {
+               sk->sk_route_caps |= NETIF_F_GSO_MASK;
+       if (sk_can_gso(sk)) {
                if (dst->header_len)
-                       sk->sk_route_caps &= ~NETIF_F_TSO;
+                       sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
                else 
                        sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
        }
index 624921e763329d952cf5b68805cc75ca20399d5c..3cd803b0d7a531537961483aec4ea77923b8d418 100644 (file)
@@ -751,7 +751,7 @@ static inline int tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
        if (in_flight >= tp->snd_cwnd)
                return 1;
 
-       if (!(sk->sk_route_caps & NETIF_F_TSO))
+       if (!sk_can_gso(sk))
                return 0;
 
        left = tp->snd_cwnd - in_flight;
index 7bb366f70934787e19b7dfa2f0ddd05895cd12a0..4629d77173f23e9954cdf6106d581de1b668c559 100644 (file)
@@ -55,9 +55,7 @@ static inline void TCP_ECN_send(struct sock *sk, struct tcp_sock *tp,
                        if (tp->ecn_flags&TCP_ECN_QUEUE_CWR) {
                                tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR;
                                skb->h.th->cwr = 1;
-                               if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
-                                       skb_shinfo(skb)->gso_type |=
-                                               SKB_GSO_TCPV4_ECN;
+                               skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
                        }
                } else {
                        /* ACK or retransmitted segment: clear ECT|CE */
index 0c872fd0849dff985ec824307a79a8400efa427f..ca0e714613fb504bf495dad4eb20b2e2579aecc3 100644 (file)
@@ -743,7 +743,7 @@ static inline int ip_ufo_append_data(struct sock *sk,
        if (!err) {
                /* specify the length of each IP datagram fragment*/
                skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4;
+               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
                __skb_queue_tail(&sk->sk_write_queue, skb);
 
                return 0;
@@ -1088,7 +1088,7 @@ ssize_t   ip_append_page(struct sock *sk, struct page *page,
        if ((sk->sk_protocol == IPPROTO_UDP) &&
            (rt->u.dst.dev->features & NETIF_F_UFO)) {
                skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4;
+               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
        }
 
 
index 0416073c5600c109c56d85d3a49359a829fbc4cb..2d3612cd5f18cd1c442ac7407e7d5202c8d5ebbd 100644 (file)
@@ -254,7 +254,7 @@ static int do_basic_checks(struct ip_conntrack *conntrack,
        }
 
        DEBUGP("Basic checks passed\n");
-       return 0;
+       return count == 0;
 }
 
 static int new_state(enum ip_conntrack_dir dir,
index a97450e9eb34692558246464101ff58259aadda6..804458712d881dee4065f2e4cbd7e7c99296d545 100644 (file)
@@ -642,7 +642,7 @@ static inline int select_size(struct sock *sk, struct tcp_sock *tp)
        int tmp = tp->mss_cache;
 
        if (sk->sk_route_caps & NETIF_F_SG) {
-               if (sk->sk_route_caps & NETIF_F_TSO)
+               if (sk_can_gso(sk))
                        tmp = 0;
                else {
                        int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER);
@@ -2165,13 +2165,19 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features)
        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;
@@ -2208,6 +2214,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features)
 out:
        return segs;
 }
+EXPORT_SYMBOL(tcp_tso_segment);
 
 extern void __skb_cb_too_small_for_tcp(int, int);
 extern struct tcp_congestion_ops tcp_reno;
index e02a84cf9a530086adc874024a25c8a6d6592126..8355b729fa9574ad8025c9b0620c3287c46c891b 100644 (file)
@@ -241,6 +241,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                goto failure;
 
        /* OK, now commit destination to socket.  */
+       sk->sk_gso_type = SKB_GSO_TCPV4;
        sk_setup_caps(sk, &rt->u.dst);
 
        if (!tp->write_seq)
@@ -883,6 +884,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        if (!newsk)
                goto exit;
 
+       newsk->sk_gso_type = SKB_GSO_TCPV4;
        sk_setup_caps(newsk, dst);
 
        newtp                 = tcp_sk(newsk);
index 5a7cb4a9c867f7a0cd4c1edf738dae2138a9e6c6..5c08ea20a18dfb8fa965fc23070729c176aba4f0 100644 (file)
@@ -510,8 +510,7 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
 
 static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now)
 {
-       if (skb->len <= mss_now ||
-           !(sk->sk_route_caps & NETIF_F_TSO)) {
+       if (skb->len <= mss_now || !sk_can_gso(sk)) {
                /* Avoid the costly divide in the normal
                 * non-TSO case.
                 */
@@ -525,7 +524,7 @@ static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned
                factor /= mss_now;
                skb_shinfo(skb)->gso_segs = factor;
                skb_shinfo(skb)->gso_size = mss_now;
-               skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+               skb_shinfo(skb)->gso_type = sk->sk_gso_type;
        }
 }
 
@@ -824,9 +823,7 @@ unsigned int tcp_current_mss(struct sock *sk, int large_allowed)
 
        mss_now = tp->mss_cache;
 
-       if (large_allowed &&
-           (sk->sk_route_caps & NETIF_F_TSO) &&
-           !tp->urg_mode)
+       if (large_allowed && sk_can_gso(sk) && !tp->urg_mode)
                doing_tso = 1;
 
        if (dst) {
index a94c91b407ef78fdc98ce67236a18549bc403594..5a0ba58b86cc055845c74dba1ca8c6c0a47fdc31 100644 (file)
@@ -659,8 +659,6 @@ int inet6_sk_rebuild_header(struct sock *sk)
                }
 
                ip6_dst_store(sk, dst, NULL);
-               sk->sk_route_caps = dst->dev->features &
-                       ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
        }
 
        return 0;
index a18d4256372cf988e66d1feef51507b7163b8103..9d0ee7f0eeb5296dfb44514295c1c5c53027fb59 100644 (file)
@@ -179,7 +179,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp)
 
 static struct inet6_protocol destopt_protocol = {
        .handler        =       ipv6_destopt_rcv,
-       .flags          =       INET6_PROTO_NOPOLICY,
+       .flags          =       INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
 };
 
 void __init ipv6_destopt_init(void)
@@ -340,7 +340,7 @@ looped_back:
 
 static struct inet6_protocol rthdr_protocol = {
        .handler        =       ipv6_rthdr_rcv,
-       .flags          =       INET6_PROTO_NOPOLICY,
+       .flags          =       INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
 };
 
 void __init ipv6_rthdr_init(void)
index 11536db225d780e84a1e2e278f7c0e226c0dd8d1..5c950cc79d80a3c04384b8238e2573e9f827fdb5 100644 (file)
@@ -186,8 +186,6 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok)
                }
 
                ip6_dst_store(sk, dst, NULL);
-               sk->sk_route_caps = dst->dev->features &
-                       ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
        }
 
        skb->dst = dst_clone(dst);
index aceee252503dfe84f5ccb34b950949f284577d0a..df8f051c0fce2eb910429a9313ec0db8e3ba9462 100644 (file)
@@ -84,14 +84,9 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
         */
        IP6CB(skb)->iif = skb->dst ? ((struct rt6_info *)skb->dst)->rt6i_idev->dev->ifindex : dev->ifindex;
 
-       if (skb->len < sizeof(struct ipv6hdr))
+       if (unlikely(!pskb_may_pull(skb, sizeof(*hdr))))
                goto err;
 
-       if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) {
-               IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
-               goto drop;
-       }
-
        hdr = skb->nh.ipv6h;
 
        if (hdr->version != 6)
index aa2b2c3e5076ddda101c53062bc4954b37fe74cb..2c5b44575af0fee96124d8e37580af9e2d2cb4b4 100644 (file)
@@ -229,7 +229,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        skb->priority = sk->sk_priority;
 
        mtu = dst_mtu(dst);
-       if ((skb->len <= mtu) || ipfragok) {
+       if ((skb->len <= mtu) || ipfragok || skb_shinfo(skb)->gso_size) {
                IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
                return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev,
                                dst_output);
@@ -834,7 +834,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                /* specify the length of each IP datagram fragment*/
                skb_shinfo(skb)->gso_size = mtu - fragheaderlen - 
                                            sizeof(struct frag_hdr);
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4;
+               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
                ipv6_select_ident(skb, &fhdr);
                skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
                __skb_queue_tail(&sk->sk_write_queue, skb);
index 97199d6ec79fad5d6dfe24199da7f176db3167a4..c28e5c287447105d36334b480f02b912deb0a2af 100644 (file)
 
 DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly;
 
+static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
+{
+       struct sk_buff *segs = ERR_PTR(-EINVAL);
+       struct ipv6hdr *ipv6h;
+       struct inet6_protocol *ops;
+       int proto;
+
+       if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
+               goto out;
+
+       ipv6h = skb->nh.ipv6h;
+       proto = ipv6h->nexthdr;
+       __skb_pull(skb, sizeof(*ipv6h));
+
+       rcu_read_lock();
+       for (;;) {
+               struct ipv6_opt_hdr *opth;
+               int len;
+
+               if (proto != NEXTHDR_HOP) {
+                       ops = rcu_dereference(inet6_protos[proto]);
+
+                       if (unlikely(!ops))
+                               goto unlock;
+
+                       if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
+                               break;
+               }
+
+               if (unlikely(!pskb_may_pull(skb, 8)))
+                       goto unlock;
+
+               opth = (void *)skb->data;
+               len = opth->hdrlen * 8 + 8;
+
+               if (unlikely(!pskb_may_pull(skb, len)))
+                       goto unlock;
+
+               proto = opth->nexthdr;
+               __skb_pull(skb, len);
+       }
+
+       skb->h.raw = skb->data;
+       if (likely(ops->gso_segment))
+               segs = ops->gso_segment(skb, features);
+
+unlock:
+       rcu_read_unlock();
+
+       if (unlikely(IS_ERR(segs)))
+               goto out;
+
+       for (skb = segs; skb; skb = skb->next) {
+               ipv6h = skb->nh.ipv6h;
+               ipv6h->payload_len = htons(skb->len - skb->mac_len);
+       }
+
+out:
+       return segs;
+}
+
 static struct packet_type ipv6_packet_type = {
        .type = __constant_htons(ETH_P_IPV6), 
        .func = ipv6_rcv,
+       .gso_segment = ipv6_gso_segment,
 };
 
 struct ip6_ra_chain *ip6_ra_chain;
index 81dbc9c3bf04090ebbf226cbb89e16d539963ed5..5bdcb9002cf733ee670c05d12e2801e3a9488302 100644 (file)
@@ -269,9 +269,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        ipv6_addr_copy(&np->saddr, saddr);
        inet->rcv_saddr = LOOPBACK4_IPV6;
 
+       sk->sk_gso_type = SKB_GSO_TCPV6;
        ip6_dst_store(sk, dst, NULL);
-       sk->sk_route_caps = dst->dev->features &
-               ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
 
        icsk->icsk_ext_hdr_len = 0;
        if (np->opt)
@@ -929,9 +928,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
         * comment in that function for the gory details. -acme
         */
 
+       sk->sk_gso_type = SKB_GSO_TCPV6;
        ip6_dst_store(newsk, dst, NULL);
-       newsk->sk_route_caps = dst->dev->features &
-               ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
 
        newtcp6sk = (struct tcp6_sock *)newsk;
        inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
@@ -1605,6 +1603,7 @@ struct proto tcpv6_prot = {
 static struct inet6_protocol tcpv6_protocol = {
        .handler        =       tcp_v6_rcv,
        .err_handler    =       tcp_v6_err,
+       .gso_segment    =       tcp_tso_segment,
        .flags          =       INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
index 0839b701b9309f3fd9a98e58c31c0f1a4e897406..9bd8a7877fd54648e86ab904778d4e32159627cb 100644 (file)
@@ -261,7 +261,7 @@ static int do_basic_checks(struct nf_conn *conntrack,
        }
 
        DEBUGP("Basic checks passed\n");
-       return 0;
+       return count == 0;
 }
 
 static int new_state(enum ip_conntrack_dir dir,