netxen: add receive side scaling (rss) support
This patch enables the load balancing capability of firmware
and hardware to spray traffic into different cpus through
separate rx msix interrupts.
The feature is being enabled for NX3031, NX2031 (old) will be
enabled later. This depends on msi-x and compatibility with
msi and legacy is maintained by enabling single rx ring.
Signed-off-by: Dhananjay Phadke <dhananjay@netxen.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 00eaeee..274d1e0 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -135,20 +135,71 @@
static struct netxen_legacy_intr_set legacy_intr[] = NX_LEGACY_INTR_CONFIG;
-static inline void netxen_nic_disable_int(struct netxen_adapter *adapter)
+static inline void netxen_nic_disable_int(struct nx_host_sds_ring *sds_ring)
{
- adapter->pci_write_normalize(adapter, adapter->crb_intr_mask, 0);
+ struct netxen_adapter *adapter = sds_ring->adapter;
+
+ adapter->pci_write_normalize(adapter, sds_ring->crb_intr_mask, 0);
}
-static inline void netxen_nic_enable_int(struct netxen_adapter *adapter)
+static inline void netxen_nic_enable_int(struct nx_host_sds_ring *sds_ring)
{
- adapter->pci_write_normalize(adapter, adapter->crb_intr_mask, 0x1);
+ struct netxen_adapter *adapter = sds_ring->adapter;
+
+ adapter->pci_write_normalize(adapter, sds_ring->crb_intr_mask, 0x1);
if (!NETXEN_IS_MSI_FAMILY(adapter))
adapter->pci_write_immediate(adapter,
adapter->legacy_intr.tgt_mask_reg, 0xfbff);
}
+static void
+netxen_napi_add(struct netxen_adapter *adapter, struct net_device *netdev)
+{
+ int ring;
+ struct nx_host_sds_ring *sds_ring;
+ struct netxen_recv_context *recv_ctx = &adapter->recv_ctx;
+
+ if (adapter->flags & NETXEN_NIC_MSIX_ENABLED)
+ adapter->max_sds_rings = (num_online_cpus() >= 4) ? 4 : 2;
+ else
+ adapter->max_sds_rings = 1;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ netif_napi_add(netdev, &sds_ring->napi,
+ netxen_nic_poll, NETXEN_NETDEV_WEIGHT);
+ }
+}
+
+static void
+netxen_napi_enable(struct netxen_adapter *adapter)
+{
+ int ring;
+ struct nx_host_sds_ring *sds_ring;
+ struct netxen_recv_context *recv_ctx = &adapter->recv_ctx;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ napi_enable(&sds_ring->napi);
+ netxen_nic_enable_int(sds_ring);
+ }
+}
+
+static void
+netxen_napi_disable(struct netxen_adapter *adapter)
+{
+ int ring;
+ struct nx_host_sds_ring *sds_ring;
+ struct netxen_recv_context *recv_ctx = &adapter->recv_ctx;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ netxen_nic_disable_int(sds_ring);
+ napi_disable(&sds_ring->napi);
+ }
+}
+
static int nx_set_dma_mask(struct netxen_adapter *adapter, uint8_t revision_id)
{
struct pci_dev *pdev = adapter->pdev;
@@ -226,7 +277,6 @@
adapter->num_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS;
adapter->num_lro_rxd = MAX_LRO_RCV_DESCRIPTORS;
- adapter->max_possible_rss_rings = 1;
return;
}
@@ -447,6 +497,7 @@
dev_info(&pdev->dev, "using msi interrupts\n");
} else
dev_info(&pdev->dev, "using legacy interrupts\n");
+ adapter->msix_entries[0].vector = pdev->irq;
}
}
@@ -671,8 +722,12 @@
netxen_nic_request_irq(struct netxen_adapter *adapter)
{
irq_handler_t handler;
+ struct nx_host_sds_ring *sds_ring;
+ int err, ring;
+
unsigned long flags = IRQF_SAMPLE_RANDOM;
struct net_device *netdev = adapter->netdev;
+ struct netxen_recv_context *recv_ctx = &adapter->recv_ctx;
if ((adapter->msi_mode != MSI_MODE_MULTIFUNC) ||
(adapter->intr_scheme != INTR_SCHEME_PERPORT)) {
@@ -693,8 +748,30 @@
}
adapter->irq = netdev->irq;
- return request_irq(adapter->irq, handler,
- flags, netdev->name, adapter);
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ sprintf(sds_ring->name, "%16s[%d]", netdev->name, ring);
+ err = request_irq(sds_ring->irq, handler,
+ flags, sds_ring->name, sds_ring);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static void
+netxen_nic_free_irq(struct netxen_adapter *adapter)
+{
+ int ring;
+ struct nx_host_sds_ring *sds_ring;
+
+ struct netxen_recv_context *recv_ctx = &adapter->recv_ctx;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ free_irq(sds_ring->irq, sds_ring);
+ }
}
static int
@@ -719,8 +796,10 @@
adapter->ahw.linkup = 0;
mod_timer(&adapter->watchdog_timer, jiffies);
- napi_enable(&adapter->napi);
- netxen_nic_enable_int(adapter);
+ netxen_napi_enable(adapter);
+
+ if (adapter->max_sds_rings > 1)
+ netxen_config_rss(adapter, 1);
return 0;
}
@@ -730,13 +809,11 @@
{
netif_carrier_off(netdev);
netif_stop_queue(netdev);
- napi_disable(&adapter->napi);
+ netxen_napi_disable(adapter);
if (adapter->stop_port)
adapter->stop_port(adapter);
- netxen_nic_disable_int(adapter);
-
netxen_release_tx_buffers(adapter);
FLUSH_SCHEDULED_WORK();
@@ -750,6 +827,7 @@
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
int err, ring;
+ struct nx_host_rds_ring *rds_ring;
err = netxen_init_firmware(adapter);
if (err != 0) {
@@ -788,8 +866,10 @@
netxen_nic_update_cmd_consumer(adapter, 0);
}
- for (ring = 0; ring < adapter->max_rds_rings; ring++)
- netxen_post_rx_buffers(adapter, ring);
+ for (ring = 0; ring < adapter->max_rds_rings; ring++) {
+ rds_ring = &adapter->recv_ctx.rds_rings[ring];
+ netxen_post_rx_buffers(adapter, ring, rds_ring);
+ }
err = netxen_nic_request_irq(adapter);
if (err) {
@@ -812,8 +892,7 @@
static void
netxen_nic_detach(struct netxen_adapter *adapter)
{
- if (adapter->irq)
- free_irq(adapter->irq, adapter);
+ netxen_nic_free_irq(adapter);
netxen_release_rx_buffers(adapter);
netxen_free_hw_resources(adapter);
@@ -883,14 +962,12 @@
goto err_out_free_netdev;
rwlock_init(&adapter->adapter_lock);
+ spin_lock_init(&adapter->tx_clean_lock);
err = netxen_setup_pci_map(adapter);
if (err)
goto err_out_free_netdev;
- netif_napi_add(netdev, &adapter->napi,
- netxen_nic_poll, NETXEN_NETDEV_WEIGHT);
-
/* This will be reset for mezz cards */
adapter->portnum = pci_func_id;
adapter->rx_csum = 1;
@@ -963,10 +1040,9 @@
netxen_setup_intr(adapter);
- if (adapter->flags & NETXEN_NIC_MSIX_ENABLED)
- netdev->irq = adapter->msix_entries[0].vector;
- else
- netdev->irq = pdev->irq;
+ netdev->irq = adapter->msix_entries[0].vector;
+
+ netxen_napi_add(adapter, netdev);
err = netxen_receive_peg_ready(adapter);
if (err)
@@ -1520,13 +1596,11 @@
printk(KERN_ERR "%s %s: transmit timeout, resetting.\n",
netxen_nic_driver_name, adapter->netdev->name);
- netxen_nic_disable_int(adapter);
- napi_disable(&adapter->napi);
+ netxen_napi_disable(adapter);
adapter->netdev->trans_start = jiffies;
- napi_enable(&adapter->napi);
- netxen_nic_enable_int(adapter);
+ netxen_napi_enable(adapter);
netif_wake_queue(adapter->netdev);
}
@@ -1564,7 +1638,8 @@
static irqreturn_t netxen_intr(int irq, void *data)
{
- struct netxen_adapter *adapter = data;
+ struct nx_host_sds_ring *sds_ring = data;
+ struct netxen_adapter *adapter = sds_ring->adapter;
u32 status = 0;
status = adapter->pci_read_immediate(adapter, ISR_INT_VECTOR);
@@ -1595,7 +1670,7 @@
/* clear interrupt */
if (adapter->fw_major < 4)
- netxen_nic_disable_int(adapter);
+ netxen_nic_disable_int(sds_ring);
adapter->pci_write_immediate(adapter,
adapter->legacy_intr.tgt_status_reg,
@@ -1604,45 +1679,49 @@
adapter->pci_read_immediate(adapter, ISR_INT_VECTOR);
adapter->pci_read_immediate(adapter, ISR_INT_VECTOR);
- napi_schedule(&adapter->napi);
+ napi_schedule(&sds_ring->napi);
return IRQ_HANDLED;
}
static irqreturn_t netxen_msi_intr(int irq, void *data)
{
- struct netxen_adapter *adapter = data;
+ struct nx_host_sds_ring *sds_ring = data;
+ struct netxen_adapter *adapter = sds_ring->adapter;
/* clear interrupt */
adapter->pci_write_immediate(adapter,
msi_tgt_status[adapter->ahw.pci_func], 0xffffffff);
- napi_schedule(&adapter->napi);
+ napi_schedule(&sds_ring->napi);
return IRQ_HANDLED;
}
static irqreturn_t netxen_msix_intr(int irq, void *data)
{
- struct netxen_adapter *adapter = data;
+ struct nx_host_sds_ring *sds_ring = data;
- napi_schedule(&adapter->napi);
+ napi_schedule(&sds_ring->napi);
return IRQ_HANDLED;
}
static int netxen_nic_poll(struct napi_struct *napi, int budget)
{
- struct netxen_adapter *adapter =
- container_of(napi, struct netxen_adapter, napi);
+ struct nx_host_sds_ring *sds_ring =
+ container_of(napi, struct nx_host_sds_ring, napi);
+
+ struct netxen_adapter *adapter = sds_ring->adapter;
+
int tx_complete;
int work_done;
tx_complete = netxen_process_cmd_ring(adapter);
- work_done = netxen_process_rcv_ring(adapter, budget);
+ work_done = netxen_process_rcv_ring(sds_ring, budget);
if ((work_done < budget) && tx_complete) {
- napi_complete(&adapter->napi);
- netxen_nic_enable_int(adapter);
+ napi_complete(&sds_ring->napi);
+ netxen_nic_enable_int(sds_ring);
}
return work_done;