net: s2io: fix buffer overflow
[pandora-kernel.git] / drivers / net / s2io.c
index 1d37f0c..18bc5b7 100644 (file)
@@ -1,6 +1,6 @@
 /************************************************************************
  * s2io.c: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC
- * Copyright(c) 2002-2007 Neterion Inc.
+ * Copyright(c) 2002-2010 Exar Corp.
  *
  * This software may be used and distributed according to the terms of
  * the GNU General Public License (GPL), incorporated herein by reference.
@@ -38,7 +38,7 @@
  * Tx descriptors that can be associated with each corresponding FIFO.
  * intr_type: This defines the type of interrupt. The values can be 0(INTA),
  *     2(MSI_X). Default value is '2(MSI_X)'
- * lro_enable: Specifies whether to enable Large Receive Offload (LRO) or not.
+ * lro: Specifies whether to enable Large Receive Offload (LRO) or not.
  *     Possible values '1' for enable '0' for disable. Default is '0'
  * lro_max_pkts: This parameter defines maximum number of packets can be
  *     aggregated as a single large packet
@@ -90,7 +90,7 @@
 #include "s2io.h"
 #include "s2io-regs.h"
 
-#define DRV_VERSION "2.0.26.25"
+#define DRV_VERSION "2.0.26.26"
 
 /* S2io Driver name & version. */
 static char s2io_driver_name[] = "Neterion";
@@ -496,7 +496,7 @@ S2IO_PARM_INT(rxsync_frequency, 3);
 /* Interrupt type. Values can be 0(INTA), 2(MSI_X) */
 S2IO_PARM_INT(intr_type, 2);
 /* Large receive offload feature */
-static unsigned int lro_enable;
+static unsigned int lro_enable = 1;
 module_param_named(lro, lro_enable, uint, 0);
 
 /* Max pkts to be aggregated by LRO at one time. If not specified,
@@ -795,7 +795,6 @@ static int init_shared_mem(struct s2io_nic *nic)
                ring->rx_curr_put_info.ring_len = rx_cfg->num_rxd - 1;
                ring->nic = nic;
                ring->ring_no = i;
-               ring->lro = lro_enable;
 
                blk_cnt = rx_cfg->num_rxd / (rxd_count[nic->rxd_mode] + 1);
                /*  Allocating all the Rx blocks */
@@ -5797,7 +5796,7 @@ static void s2io_vpd_read(struct s2io_nic *nic)
 {
        u8 *vpd_data;
        u8 data;
-       int i = 0, cnt, fail = 0;
+       int i = 0, cnt, len, fail = 0;
        int vpd_addr = 0x80;
        struct swStat *swstats = &nic->mac_control.stats_info->sw_stat;
 
@@ -5838,20 +5837,28 @@ static void s2io_vpd_read(struct s2io_nic *nic)
 
        if (!fail) {
                /* read serial number of adapter */
-               for (cnt = 0; cnt < 256; cnt++) {
+               for (cnt = 0; cnt < 252; cnt++) {
                        if ((vpd_data[cnt] == 'S') &&
-                           (vpd_data[cnt+1] == 'N') &&
-                           (vpd_data[cnt+2] < VPD_STRING_LEN)) {
-                               memset(nic->serial_num, 0, VPD_STRING_LEN);
-                               memcpy(nic->serial_num, &vpd_data[cnt + 3],
-                                      vpd_data[cnt+2]);
-                               break;
+                           (vpd_data[cnt+1] == 'N')) {
+                               len = vpd_data[cnt+2];
+                               if (len < min(VPD_STRING_LEN, 256-cnt-2)) {
+                                       memcpy(nic->serial_num,
+                                              &vpd_data[cnt + 3],
+                                              len);
+                                       memset(nic->serial_num+len,
+                                              0,
+                                              VPD_STRING_LEN-len);
+                                       break;
+                               }
                        }
                }
        }
 
-       if ((!fail) && (vpd_data[1] < VPD_STRING_LEN))
-               memcpy(nic->product_name, &vpd_data[3], vpd_data[1]);
+       if ((!fail) && (vpd_data[1] < VPD_STRING_LEN)) {
+               len = vpd_data[1];
+               memcpy(nic->product_name, &vpd_data[3], len);
+               nic->product_name[len] = 0;
+       }
        kfree(vpd_data);
        swstats->mem_freed += 256;
 }
@@ -6707,6 +6714,7 @@ static u32 s2io_ethtool_op_get_tso(struct net_device *dev)
 {
        return (dev->features & NETIF_F_TSO) != 0;
 }
+
 static int s2io_ethtool_op_set_tso(struct net_device *dev, u32 data)
 {
        if (data)
@@ -6717,6 +6725,42 @@ static int s2io_ethtool_op_set_tso(struct net_device *dev, u32 data)
        return 0;
 }
 
+static int s2io_ethtool_set_flags(struct net_device *dev, u32 data)
+{
+       struct s2io_nic *sp = netdev_priv(dev);
+       int rc = 0;
+       int changed = 0;
+
+       if (data & ~ETH_FLAG_LRO)
+               return -EINVAL;
+
+       if (data & ETH_FLAG_LRO) {
+               if (lro_enable) {
+                       if (!(dev->features & NETIF_F_LRO)) {
+                               dev->features |= NETIF_F_LRO;
+                               changed = 1;
+                       }
+               } else
+                       rc = -EINVAL;
+       } else if (dev->features & NETIF_F_LRO) {
+               dev->features &= ~NETIF_F_LRO;
+               changed = 1;
+       }
+
+       if (changed && netif_running(dev)) {
+               s2io_stop_all_tx_queue(sp);
+               s2io_card_down(sp);
+               sp->lro = !!(dev->features & NETIF_F_LRO);
+               rc = s2io_card_up(sp);
+               if (rc)
+                       s2io_reset(sp);
+               else
+                       s2io_start_all_tx_queue(sp);
+       }
+
+       return rc;
+}
+
 static const struct ethtool_ops netdev_ethtool_ops = {
        .get_settings = s2io_ethtool_gset,
        .set_settings = s2io_ethtool_sset,
@@ -6733,6 +6777,8 @@ static const struct ethtool_ops netdev_ethtool_ops = {
        .get_rx_csum = s2io_ethtool_get_rx_csum,
        .set_rx_csum = s2io_ethtool_set_rx_csum,
        .set_tx_csum = s2io_ethtool_op_set_tx_csum,
+       .set_flags = s2io_ethtool_set_flags,
+       .get_flags = ethtool_op_get_flags,
        .set_sg = ethtool_op_set_sg,
        .get_tso = s2io_ethtool_op_get_tso,
        .set_tso = s2io_ethtool_op_set_tso,
@@ -7261,6 +7307,7 @@ static int s2io_card_up(struct s2io_nic *sp)
                struct ring_info *ring = &mac_control->rings[i];
 
                ring->mtu = dev->mtu;
+               ring->lro = sp->lro;
                ret = fill_rx_buffers(sp, ring, 1);
                if (ret) {
                        DBG_PRINT(ERR_DBG, "%s: Out of memory in Open\n",
@@ -7847,7 +7894,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
 
        /*  Private member variable initialized to s2io NIC structure */
        sp = netdev_priv(dev);
-       memset(sp, 0, sizeof(struct s2io_nic));
        sp->dev = dev;
        sp->pdev = pdev;
        sp->high_dma_flag = dma_flag;
@@ -8001,7 +8047,8 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
        dev->netdev_ops = &s2io_netdev_ops;
        SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
        dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-
+       if (lro_enable)
+               dev->features |= NETIF_F_LRO;
        dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
        if (sp->high_dma_flag == true)
                dev->features |= NETIF_F_HIGHDMA;
@@ -8159,7 +8206,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
                goto register_failed;
        }
        s2io_vpd_read(sp);
-       DBG_PRINT(ERR_DBG, "Copyright(c) 2002-2007 Neterion Inc.\n");
+       DBG_PRINT(ERR_DBG, "Copyright(c) 2002-2010 Exar Corp.\n");
        DBG_PRINT(ERR_DBG, "%s: Neterion %s (rev %d)\n", dev->name,
                  sp->product_name, pdev->revision);
        DBG_PRINT(ERR_DBG, "%s: Driver version %s\n", dev->name,