qlcnic: support rcv ring configuration through ethtool
authorSucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Thu, 28 Apr 2011 11:48:18 +0000 (11:48 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 29 Apr 2011 19:59:59 +0000 (12:59 -0700)
o Support ethtool command ETHTOOL_GCHANNELS and ETHTOOL_SCHANNELS.
o Number of rcv rings configuration depend upon number of msix vector.

Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/qlcnic/qlcnic.h
drivers/net/qlcnic/qlcnic_ethtool.c
drivers/net/qlcnic/qlcnic_main.c

index f7acb807a03d9a28949a2e4c52f264f91c4cb179..1934ed9a1aad4d5396e5daeddc15c51f86a97593 100644 (file)
 #define PHAN_PEG_RCV_INITIALIZED       0xff01
 
 #define NUM_RCV_DESC_RINGS     3
-#define NUM_STS_DESC_RINGS     4
 
 #define RCV_RING_NORMAL 0
 #define RCV_RING_JUMBO 1
@@ -871,7 +870,8 @@ struct qlcnic_ipaddr {
 #define QLCNIC_IS_MSI_FAMILY(adapter) \
        ((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
 
-#define MSIX_ENTRIES_PER_ADAPTER       NUM_STS_DESC_RINGS
+#define QLCNIC_DEF_NUM_STS_DESC_RINGS  4
+#define QLCNIC_MIN_NUM_RSS_RINGS       2
 #define QLCNIC_MSIX_TBL_SPACE          8192
 #define QLCNIC_PCI_REG_MSIX_TBL        0x44
 #define QLCNIC_MSIX_TBL_PGSIZE         4096
@@ -987,7 +987,7 @@ struct qlcnic_adapter {
        void __iomem    *crb_int_state_reg;
        void __iomem    *isr_int_vec;
 
-       struct msix_entry msix_entries[MSIX_ENTRIES_PER_ADAPTER];
+       struct msix_entry *msix_entries;
 
        struct delayed_work fw_work;
 
@@ -1262,6 +1262,8 @@ u32 qlcnic_issue_cmd(struct qlcnic_adapter *adapter,
 void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings);
 int qlcnic_diag_alloc_res(struct net_device *netdev, int test);
 netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
+int qlcnic_validate_max_rss(struct net_device *netdev, u8 max_hw, u8 val);
+int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data);
 
 /* Management functions */
 int qlcnic_get_mac_address(struct qlcnic_adapter *, u8*);
index de65847f3559290a58c8d59f0b765e6f54eb23d8..8db1d1983cfe3f6fae7f305850128bea39d66b81 100644 (file)
@@ -474,6 +474,39 @@ qlcnic_set_ringparam(struct net_device *dev,
        return qlcnic_reset_context(adapter);
 }
 
+static void qlcnic_get_channels(struct net_device *dev,
+               struct ethtool_channels *channel)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(dev);
+
+       channel->max_rx = rounddown_pow_of_two(min_t(int,
+                       adapter->max_rx_ques, num_online_cpus()));
+       channel->max_tx = adapter->max_tx_ques;
+
+       channel->rx_count = adapter->max_sds_rings;
+       channel->tx_count = adapter->max_tx_ques;
+}
+
+static int qlcnic_set_channels(struct net_device *dev,
+               struct ethtool_channels *channel)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(dev);
+       int err;
+
+       if (channel->other_count || channel->combined_count ||
+           channel->tx_count != channel->max_tx)
+               return -EINVAL;
+
+       err = qlcnic_validate_max_rss(dev, channel->max_rx, channel->rx_count);
+       if (err)
+               return err;
+
+       err = qlcnic_set_max_rss(adapter, channel->rx_count);
+       netdev_info(dev, "allocated 0x%x sds rings\n",
+                                adapter->max_sds_rings);
+       return err;
+}
+
 static void
 qlcnic_get_pauseparam(struct net_device *netdev,
                          struct ethtool_pauseparam *pause)
@@ -949,6 +982,8 @@ const struct ethtool_ops qlcnic_ethtool_ops = {
        .get_eeprom = qlcnic_get_eeprom,
        .get_ringparam = qlcnic_get_ringparam,
        .set_ringparam = qlcnic_set_ringparam,
+       .get_channels = qlcnic_get_channels,
+       .set_channels = qlcnic_set_channels,
        .get_pauseparam = qlcnic_get_pauseparam,
        .set_pauseparam = qlcnic_set_pauseparam,
        .get_wol = qlcnic_get_wol,
index 6e619514feeebb6d0de692dc036789701972fa8e..d6cc4d47959143f5b4b20a284dfa0fd6ae810206 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/inetdevice.h>
 #include <linux/sysfs.h>
 #include <linux/aer.h>
+#include <linux/log2.h>
 
 MODULE_DESCRIPTION("QLogic 1/10 GbE Converged/Intelligent Ethernet Driver");
 MODULE_LICENSE("GPL");
@@ -350,39 +351,17 @@ static struct qlcnic_nic_template qlcnic_vf_ops = {
        .start_firmware = qlcnicvf_start_firmware
 };
 
-static void
-qlcnic_setup_intr(struct qlcnic_adapter *adapter)
+static int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
 {
-       const struct qlcnic_legacy_intr_set *legacy_intrp;
        struct pci_dev *pdev = adapter->pdev;
-       int err, num_msix;
-
-       if (adapter->msix_supported) {
-               num_msix = (num_online_cpus() >= MSIX_ENTRIES_PER_ADAPTER) ?
-                       MSIX_ENTRIES_PER_ADAPTER : 2;
-       } else
-               num_msix = 1;
+       int err = -1;
 
        adapter->max_sds_rings = 1;
-
        adapter->flags &= ~(QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED);
-
-       legacy_intrp = &legacy_intr[adapter->ahw->pci_func];
-
-       adapter->int_vec_bit = legacy_intrp->int_vec_bit;
-       adapter->tgt_status_reg = qlcnic_get_ioaddr(adapter,
-                       legacy_intrp->tgt_status_reg);
-       adapter->tgt_mask_reg = qlcnic_get_ioaddr(adapter,
-                       legacy_intrp->tgt_mask_reg);
-       adapter->isr_int_vec = qlcnic_get_ioaddr(adapter, ISR_INT_VECTOR);
-
-       adapter->crb_int_state_reg = qlcnic_get_ioaddr(adapter,
-                       ISR_INT_STATE_REG);
-
        qlcnic_set_msix_bit(pdev, 0);
 
        if (adapter->msix_supported) {
-
+ enable_msix:
                qlcnic_init_msix_entries(adapter, num_msix);
                err = pci_enable_msix(pdev, adapter->msix_entries, num_msix);
                if (err == 0) {
@@ -392,14 +371,22 @@ qlcnic_setup_intr(struct qlcnic_adapter *adapter)
                        adapter->max_sds_rings = num_msix;
 
                        dev_info(&pdev->dev, "using msi-x interrupts\n");
-                       return;
+                       return err;
                }
+               if (err > 0) {
+                       num_msix = rounddown_pow_of_two(err);
+                       if (num_msix)
+                               goto enable_msix;
+               }
+       }
+       return err;
+}
 
-               if (err > 0)
-                       pci_disable_msix(pdev);
 
-               /* fall through for msi */
-       }
+static void qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
+{
+       const struct qlcnic_legacy_intr_set *legacy_intrp;
+       struct pci_dev *pdev = adapter->pdev;
 
        if (use_msi && !pci_enable_msi(pdev)) {
                adapter->flags |= QLCNIC_MSI_ENABLED;
@@ -410,10 +397,40 @@ qlcnic_setup_intr(struct qlcnic_adapter *adapter)
                return;
        }
 
+       legacy_intrp = &legacy_intr[adapter->ahw->pci_func];
+
+       adapter->int_vec_bit = legacy_intrp->int_vec_bit;
+       adapter->tgt_status_reg = qlcnic_get_ioaddr(adapter,
+                       legacy_intrp->tgt_status_reg);
+       adapter->tgt_mask_reg = qlcnic_get_ioaddr(adapter,
+                       legacy_intrp->tgt_mask_reg);
+       adapter->isr_int_vec = qlcnic_get_ioaddr(adapter, ISR_INT_VECTOR);
+
+       adapter->crb_int_state_reg = qlcnic_get_ioaddr(adapter,
+                       ISR_INT_STATE_REG);
        dev_info(&pdev->dev, "using legacy interrupts\n");
        adapter->msix_entries[0].vector = pdev->irq;
 }
 
+static void
+qlcnic_setup_intr(struct qlcnic_adapter *adapter)
+{
+       int num_msix;
+
+       if (adapter->msix_supported) {
+               num_msix = (num_online_cpus() >=
+                       QLCNIC_DEF_NUM_STS_DESC_RINGS) ?
+                       QLCNIC_DEF_NUM_STS_DESC_RINGS :
+                       QLCNIC_MIN_NUM_RSS_RINGS;
+       } else
+               num_msix = 1;
+
+       if (!qlcnic_enable_msix(adapter, num_msix))
+               return;
+
+       qlcnic_enable_msi_legacy(adapter);
+}
+
 static void
 qlcnic_teardown_intr(struct qlcnic_adapter *adapter)
 {
@@ -1493,6 +1510,19 @@ static int qlcnic_set_dma_mask(struct pci_dev *pdev, u8 *pci_using_dac)
        return 0;
 }
 
+static int
+qlcnic_alloc_msix_entries(struct qlcnic_adapter *adapter, u16 count)
+{
+       adapter->msix_entries = kcalloc(count, sizeof(struct msix_entry),
+                                       GFP_KERNEL);
+
+       if (adapter->msix_entries)
+               return 0;
+
+       dev_err(&adapter->pdev->dev, "failed allocating msix_entries\n");
+       return -ENOMEM;
+}
+
 static int __devinit
 qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
@@ -1587,6 +1617,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        qlcnic_clear_stats(adapter);
 
+       err = qlcnic_alloc_msix_entries(adapter, adapter->max_rx_ques);
+       if (err)
+               goto err_out_decr_ref;
+
        qlcnic_setup_intr(adapter);
 
        err = qlcnic_setup_netdev(adapter, netdev, pci_using_dac);
@@ -1615,6 +1649,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 err_out_disable_msi:
        qlcnic_teardown_intr(adapter);
+       kfree(adapter->msix_entries);
 
 err_out_decr_ref:
        qlcnic_clr_all_drv_state(adapter, 0);
@@ -1666,6 +1701,7 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev)
        qlcnic_free_lb_filters_mem(adapter);
 
        qlcnic_teardown_intr(adapter);
+       kfree(adapter->msix_entries);
 
        qlcnic_remove_diag_entries(adapter);
 
@@ -3299,6 +3335,56 @@ static struct device_attribute dev_attr_diag_mode = {
        .store = qlcnic_store_diag_mode,
 };
 
+int qlcnic_validate_max_rss(struct net_device *netdev, u8 max_hw, u8 val)
+{
+       if (!use_msi_x && !use_msi) {
+               netdev_info(netdev, "no msix or msi support, hence no rss\n");
+               return -EINVAL;
+       }
+
+       if ((val > max_hw) || (val <  2) || !is_power_of_2(val)) {
+               netdev_info(netdev, "rss_ring valid range [2 - %x] in "
+                       " powers of 2\n", max_hw);
+               return -EINVAL;
+       }
+       return 0;
+
+}
+
+int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data)
+{
+       struct net_device *netdev = adapter->netdev;
+       int err = 0;
+
+       if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
+               return -EBUSY;
+
+       netif_device_detach(netdev);
+       if (netif_running(netdev))
+               __qlcnic_down(adapter, netdev);
+       qlcnic_detach(adapter);
+       qlcnic_teardown_intr(adapter);
+
+       if (qlcnic_enable_msix(adapter, data)) {
+               netdev_info(netdev, "failed setting max_rss; rss disabled\n");
+               qlcnic_enable_msi_legacy(adapter);
+       }
+
+       if (netif_running(netdev)) {
+               err = qlcnic_attach(adapter);
+               if (err)
+                       goto done;
+               err = __qlcnic_up(adapter, netdev);
+               if (err)
+                       goto done;
+               qlcnic_restore_indev_addr(netdev, NETDEV_UP);
+       }
+ done:
+       netif_device_attach(netdev);
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       return err;
+}
+
 static int
 qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter,
                loff_t offset, size_t size)