* [RFC net-next PATCH 1/4] qlge: Remove workqueue usage from data path.
2009-08-17 21:08 [RFC net-next PATCH 0/4] qlge: Performance changes for qlge Ron Mercer
@ 2009-08-17 21:08 ` Ron Mercer
2009-08-17 21:08 ` [RFC net-next PATCH 2/4] qlge: Move TX completion processing to send path Ron Mercer
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Ron Mercer @ 2009-08-17 21:08 UTC (permalink / raw)
To: davem; +Cc: netdev, ron.mercer
Driver was using workqueues for TX completions and for default
(broadcast/multicast) RX completions. This change moves default
RX to NAPI context and TX to interrupt context.
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
---
drivers/net/qlge/qlge.h | 2 -
drivers/net/qlge/qlge_main.c | 115 +++++++++---------------------------------
2 files changed, 24 insertions(+), 93 deletions(-)
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h
index 6ed5317..e0b9330 100644
--- a/drivers/net/qlge/qlge.h
+++ b/drivers/net/qlge/qlge.h
@@ -1292,7 +1292,6 @@ struct rx_ring {
u32 cpu; /* Which CPU this should run on. */
char name[IFNAMSIZ + 5];
struct napi_struct napi;
- struct delayed_work rx_work;
u8 reserved;
struct ql_adapter *qdev;
};
@@ -1519,7 +1518,6 @@ struct ql_adapter {
union flash_params flash;
struct net_device_stats stats;
- struct workqueue_struct *q_workqueue;
struct workqueue_struct *workqueue;
struct delayed_work asic_reset_work;
struct delayed_work mpi_reset_work;
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c
index 3a271af..c551ac3 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/qlge/qlge_main.c
@@ -1682,7 +1682,7 @@ static void ql_process_mac_tx_intr(struct ql_adapter *qdev,
ql_unmap_send(qdev, tx_ring_desc, tx_ring_desc->map_cnt);
qdev->stats.tx_bytes += (tx_ring_desc->skb)->len;
qdev->stats.tx_packets++;
- dev_kfree_skb(tx_ring_desc->skb);
+ dev_kfree_skb_any(tx_ring_desc->skb);
tx_ring_desc->skb = NULL;
if (unlikely(mac_rsp->flags1 & (OB_MAC_IOCB_RSP_E |
@@ -1928,35 +1928,11 @@ static void ql_vlan_rx_kill_vid(struct net_device *ndev, u16 vid)
}
-/* Worker thread to process a given rx_ring that is dedicated
- * to outbound completions.
- */
-static void ql_tx_clean(struct work_struct *work)
-{
- struct rx_ring *rx_ring =
- container_of(work, struct rx_ring, rx_work.work);
- ql_clean_outbound_rx_ring(rx_ring);
- ql_enable_completion_interrupt(rx_ring->qdev, rx_ring->irq);
-
-}
-
-/* Worker thread to process a given rx_ring that is dedicated
- * to inbound completions.
- */
-static void ql_rx_clean(struct work_struct *work)
-{
- struct rx_ring *rx_ring =
- container_of(work, struct rx_ring, rx_work.work);
- ql_clean_inbound_rx_ring(rx_ring, 64);
- ql_enable_completion_interrupt(rx_ring->qdev, rx_ring->irq);
-}
-
/* MSI-X Multiple Vector Interrupt Handler for outbound completions. */
static irqreturn_t qlge_msix_tx_isr(int irq, void *dev_id)
{
struct rx_ring *rx_ring = dev_id;
- queue_delayed_work_on(rx_ring->cpu, rx_ring->qdev->q_workqueue,
- &rx_ring->rx_work, 0);
+ ql_clean_outbound_rx_ring(rx_ring);
return IRQ_HANDLED;
}
@@ -1978,7 +1954,7 @@ static irqreturn_t qlge_isr(int irq, void *dev_id)
struct rx_ring *rx_ring = dev_id;
struct ql_adapter *qdev = rx_ring->qdev;
struct intr_context *intr_context = &qdev->intr_context[0];
- u32 var;
+ u32 var, mask;
int i;
int work_done = 0;
@@ -2020,41 +1996,26 @@ static irqreturn_t qlge_isr(int irq, void *dev_id)
}
/*
- * Check the default queue and wake handler if active.
+ * Start NAPI for rx or handler for TX for each active queue.
*/
- rx_ring = &qdev->rx_ring[0];
- if (ql_read_sh_reg(rx_ring->prod_idx_sh_reg) != rx_ring->cnsmr_idx) {
- QPRINTK(qdev, INTR, INFO, "Waking handler for rx_ring[0].\n");
- ql_disable_completion_interrupt(qdev, intr_context->intr);
- queue_delayed_work_on(smp_processor_id(), qdev->q_workqueue,
- &rx_ring->rx_work, 0);
- work_done++;
- }
-
- if (!test_bit(QL_MSIX_ENABLED, &qdev->flags)) {
- /*
- * Start the DPC for each active queue.
- */
- for (i = 1; i < qdev->rx_ring_count; i++) {
+ mask = ql_read32(qdev, ISR1);
+ for (i = 0; i < qdev->rx_ring_count; i++) {
+ if (mask & (1 << i)) {
rx_ring = &qdev->rx_ring[i];
- if (ql_read_sh_reg(rx_ring->prod_idx_sh_reg) !=
- rx_ring->cnsmr_idx) {
- QPRINTK(qdev, INTR, INFO,
- "Waking handler for rx_ring[%d].\n", i);
+ QPRINTK(qdev, INTR, INFO,
+ "Waking handler for rx_ring[%d].\n", i);
+ if (rx_ring->type == TX_Q)
+ ql_clean_outbound_rx_ring(rx_ring);
+ else {
ql_disable_completion_interrupt(qdev,
- intr_context->
- intr);
- if (i < qdev->rss_ring_first_cq_id)
- queue_delayed_work_on(rx_ring->cpu,
- qdev->q_workqueue,
- &rx_ring->rx_work,
- 0);
- else
- napi_schedule(&rx_ring->napi);
- work_done++;
+ intr_context->
+ intr);
+ napi_schedule(&rx_ring->napi);
}
+ work_done++;
}
}
+
ql_enable_completion_interrupt(qdev, intr_context->intr);
return work_done ? IRQ_HANDLED : IRQ_NONE;
}
@@ -2706,32 +2667,15 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
}
switch (rx_ring->type) {
case TX_Q:
- /* If there's only one interrupt, then we use
- * worker threads to process the outbound
- * completion handling rx_rings. We do this so
- * they can be run on multiple CPUs. There is
- * room to play with this more where we would only
- * run in a worker if there are more than x number
- * of outbound completions on the queue and more
- * than one queue active. Some threshold that
- * would indicate a benefit in spite of the cost
- * of a context switch.
- * If there's more than one interrupt, then the
- * outbound completions are processed in the ISR.
- */
- if (!test_bit(QL_MSIX_ENABLED, &qdev->flags))
- INIT_DELAYED_WORK(&rx_ring->rx_work, ql_tx_clean);
- else {
- /* With all debug warnings on we see a WARN_ON message
- * when we free the skb in the interrupt context.
- */
- INIT_DELAYED_WORK(&rx_ring->rx_work, ql_tx_clean);
- }
cqicb->irq_delay = cpu_to_le16(qdev->tx_coalesce_usecs);
cqicb->pkt_delay = cpu_to_le16(qdev->tx_max_coalesced_frames);
break;
case DEFAULT_Q:
- INIT_DELAYED_WORK(&rx_ring->rx_work, ql_rx_clean);
+ /* Inbound completion handling rx_rings run in
+ * separate NAPI contexts.
+ */
+ netif_napi_add(qdev->ndev, &rx_ring->napi, ql_napi_poll_msix,
+ 64);
cqicb->irq_delay = 0;
cqicb->pkt_delay = 0;
break;
@@ -3340,16 +3284,11 @@ static int ql_adapter_down(struct ql_adapter *qdev)
cancel_delayed_work_sync(&qdev->mpi_idc_work);
cancel_delayed_work_sync(&qdev->mpi_port_cfg_work);
- /* The default queue at index 0 is always processed in
- * a workqueue.
- */
- cancel_delayed_work_sync(&qdev->rx_ring[0].rx_work);
-
/* The rest of the rx_rings are processed in
* a workqueue only if it's a single interrupt
* environment (MSI/Legacy).
*/
- for (i = 1; i < qdev->rx_ring_count; i++) {
+ for (i = 0; i < qdev->rx_ring_count; i++) {
rx_ring = &qdev->rx_ring[i];
/* Only the RSS rings use NAPI on multi irq
* environment. Outbound completion processing
@@ -3357,8 +3296,6 @@ static int ql_adapter_down(struct ql_adapter *qdev)
*/
if (i >= qdev->rss_ring_first_cq_id) {
napi_disable(&rx_ring->napi);
- } else {
- cancel_delayed_work_sync(&rx_ring->rx_work);
}
}
@@ -3845,10 +3782,7 @@ static void ql_release_all(struct pci_dev *pdev)
destroy_workqueue(qdev->workqueue);
qdev->workqueue = NULL;
}
- if (qdev->q_workqueue) {
- destroy_workqueue(qdev->q_workqueue);
- qdev->q_workqueue = NULL;
- }
+
if (qdev->reg_base)
iounmap(qdev->reg_base);
if (qdev->doorbell_area)
@@ -3962,7 +3896,6 @@ static int __devinit ql_init_device(struct pci_dev *pdev,
*/
qdev->rx_csum = 1;
- qdev->q_workqueue = create_workqueue(ndev->name);
qdev->workqueue = create_singlethread_workqueue(ndev->name);
INIT_DELAYED_WORK(&qdev->asic_reset_work, ql_asic_reset_work);
INIT_DELAYED_WORK(&qdev->mpi_reset_work, ql_mpi_reset_work);
--
1.6.0.2
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC net-next PATCH 2/4] qlge: Move TX completion processing to send path.
2009-08-17 21:08 [RFC net-next PATCH 0/4] qlge: Performance changes for qlge Ron Mercer
2009-08-17 21:08 ` [RFC net-next PATCH 1/4] qlge: Remove workqueue usage from data path Ron Mercer
@ 2009-08-17 21:08 ` Ron Mercer
2009-08-17 21:08 ` [RFC net-next PATCH 3/4] qlge: Change rx_ring to MSIX vector mapping Ron Mercer
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Ron Mercer @ 2009-08-17 21:08 UTC (permalink / raw)
To: davem; +Cc: netdev, ron.mercer
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
---
drivers/net/qlge/qlge.h | 5 ++-
drivers/net/qlge/qlge_main.c | 59 +++++++++++++++++++++++++++++++++++++-----
2 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h
index e0b9330..975590c 100644
--- a/drivers/net/qlge/qlge.h
+++ b/drivers/net/qlge/qlge.h
@@ -1205,6 +1205,7 @@ struct bq_desc {
};
#define QL_TXQ_IDX(qdev, skb) (smp_processor_id()%(qdev->tx_ring_count))
+#define TXQ_CLEAN_TIME (HZ/4)
struct tx_ring {
/*
@@ -1224,11 +1225,11 @@ struct tx_ring {
u8 wq_id; /* queue id for this entry */
u8 reserved1[3];
struct tx_ring_desc *q; /* descriptor list for the queue */
- spinlock_t lock;
atomic_t tx_count; /* counts down for every outstanding IO */
atomic_t queue_stopped; /* Turns queue off when full. */
- struct delayed_work tx_work;
+ struct netdev_queue *txq;
struct ql_adapter *qdev;
+ struct timer_list txq_clean_timer;
};
/*
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c
index c551ac3..d9b22da 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/qlge/qlge_main.c
@@ -1682,7 +1682,7 @@ static void ql_process_mac_tx_intr(struct ql_adapter *qdev,
ql_unmap_send(qdev, tx_ring_desc, tx_ring_desc->map_cnt);
qdev->stats.tx_bytes += (tx_ring_desc->skb)->len;
qdev->stats.tx_packets++;
- dev_kfree_skb_any(tx_ring_desc->skb);
+ dev_kfree_skb(tx_ring_desc->skb);
tx_ring_desc->skb = NULL;
if (unlikely(mac_rsp->flags1 & (OB_MAC_IOCB_RSP_E |
@@ -1797,22 +1797,40 @@ static int ql_clean_outbound_rx_ring(struct rx_ring *rx_ring)
ql_update_cq(rx_ring);
prod = ql_read_sh_reg(rx_ring->prod_idx_sh_reg);
}
+ if (!count)
+ return count;
ql_write_cq_idx(rx_ring);
tx_ring = &qdev->tx_ring[net_rsp->txq_idx];
- if (__netif_subqueue_stopped(qdev->ndev, tx_ring->wq_id) &&
- net_rsp != NULL) {
+ if (netif_tx_queue_stopped(tx_ring->txq)) {
if (atomic_read(&tx_ring->queue_stopped) &&
(atomic_read(&tx_ring->tx_count) > (tx_ring->wq_len / 4)))
/*
* The queue got stopped because the tx_ring was full.
* Wake it up, because it's now at least 25% empty.
*/
- netif_wake_subqueue(qdev->ndev, tx_ring->wq_id);
+ if (netif_running(qdev->ndev)) {
+ netif_tx_wake_queue(tx_ring->txq);
+ atomic_dec(&tx_ring->queue_stopped);
+ }
}
return count;
}
+static void ql_txq_clean_timer(unsigned long data)
+{
+ struct tx_ring *tx_ring = (struct tx_ring *)data;
+ struct ql_adapter *qdev = tx_ring->qdev;
+ struct rx_ring *rx_ring = &qdev->rx_ring[tx_ring->cq_id];
+
+ if (__netif_tx_trylock(tx_ring->txq)) {
+ ql_clean_outbound_rx_ring(rx_ring);
+ __netif_tx_unlock(tx_ring->txq);
+ }
+ mod_timer(&tx_ring->txq_clean_timer, jiffies + TXQ_CLEAN_TIME);
+
+}
+
static int ql_clean_inbound_rx_ring(struct rx_ring *rx_ring, int budget)
{
struct ql_adapter *qdev = rx_ring->qdev;
@@ -2005,7 +2023,7 @@ static irqreturn_t qlge_isr(int irq, void *dev_id)
QPRINTK(qdev, INTR, INFO,
"Waking handler for rx_ring[%d].\n", i);
if (rx_ring->type == TX_Q)
- ql_clean_outbound_rx_ring(rx_ring);
+ continue;
else {
ql_disable_completion_interrupt(qdev,
intr_context->
@@ -2107,11 +2125,17 @@ static int qlge_send(struct sk_buff *skb, struct net_device *ndev)
if (skb_padto(skb, ETH_ZLEN))
return NETDEV_TX_OK;
+ /* If there is at least 16 entries to clean then
+ * go do it.
+ */
+ if (tx_ring->wq_len - atomic_read(&tx_ring->tx_count) > 16)
+ ql_clean_outbound_rx_ring(&qdev->rx_ring[tx_ring->cq_id]);
+
if (unlikely(atomic_read(&tx_ring->tx_count) < 2)) {
QPRINTK(qdev, TX_QUEUED, INFO,
"%s: shutting down tx queue %d du to lack of resources.\n",
__func__, tx_ring_idx);
- netif_stop_subqueue(ndev, tx_ring->wq_id);
+ netif_tx_stop_queue(tx_ring->txq);
atomic_inc(&tx_ring->queue_stopped);
return NETDEV_TX_BUSY;
}
@@ -2128,6 +2152,8 @@ static int qlge_send(struct sk_buff *skb, struct net_device *ndev)
tx_ring_desc->skb = skb;
mac_iocb_ptr->frame_len = cpu_to_le16((u16) skb->len);
+ /* Disable completion interrupt for this packet. */
+ mac_iocb_ptr->flags1 |= OB_MAC_IOCB_REQ_I;
if (qdev->vlgrp && vlan_tx_tag_present(skb)) {
QPRINTK(qdev, TX_QUEUED, DEBUG, "Adding a vlan tag %d.\n",
@@ -2153,13 +2179,20 @@ static int qlge_send(struct sk_buff *skb, struct net_device *ndev)
tx_ring->prod_idx++;
if (tx_ring->prod_idx == tx_ring->wq_len)
tx_ring->prod_idx = 0;
+ atomic_dec(&tx_ring->tx_count);
wmb();
+ /* Run the destructor before telling the DMA engine about
+ * the packet to make sure it doesn't complete and get
+ * freed prematurely.
+ */
+ if (likely(!skb_shared(skb)))
+ skb_orphan(skb);
+
ql_write_db_reg(tx_ring->prod_idx, tx_ring->prod_idx_db_reg);
QPRINTK(qdev, TX_QUEUED, DEBUG, "tx queued, slot %d, len %d\n",
tx_ring->prod_idx, skb->len);
- atomic_dec(&tx_ring->tx_count);
return NETDEV_TX_OK;
}
@@ -2727,6 +2760,8 @@ static int ql_start_tx_ring(struct ql_adapter *qdev, struct tx_ring *tx_ring)
*/
tx_ring->cnsmr_idx_sh_reg = shadow_reg;
tx_ring->cnsmr_idx_sh_reg_dma = shadow_reg_dma;
+ *tx_ring->cnsmr_idx_sh_reg = 0;
+ tx_ring->txq = netdev_get_tx_queue(qdev->ndev, tx_ring->wq_id);
wqicb->len = cpu_to_le16(tx_ring->wq_len | Q_LEN_V | Q_LEN_CPP_CONT);
wqicb->flags = cpu_to_le16(Q_FLAGS_LC |
@@ -2746,6 +2781,7 @@ static int ql_start_tx_ring(struct ql_adapter *qdev, struct tx_ring *tx_ring)
return err;
}
QPRINTK(qdev, IFUP, DEBUG, "Successfully loaded WQICB.\n");
+ mod_timer(&tx_ring->txq_clean_timer, jiffies + TXQ_CLEAN_TIME);
return err;
}
@@ -3299,6 +3335,12 @@ static int ql_adapter_down(struct ql_adapter *qdev)
}
}
+ /* Delete the timers used for cleaning up
+ * TX completions.
+ */
+ for (i = 0; i < qdev->tx_ring_count; i++)
+ del_timer_sync(&qdev->tx_ring[i].txq_clean_timer);
+
clear_bit(QL_ADAPTER_UP, &qdev->flags);
ql_disable_interrupts(qdev);
@@ -3438,6 +3480,9 @@ static int ql_configure_rings(struct ql_adapter *qdev)
* immediately after the default Q ID, which is zero.
*/
tx_ring->cq_id = i + 1;
+ init_timer(&tx_ring->txq_clean_timer);
+ tx_ring->txq_clean_timer.data = (unsigned long)tx_ring;
+ tx_ring->txq_clean_timer.function = ql_txq_clean_timer;
}
for (i = 0; i < qdev->rx_ring_count; i++) {
--
1.6.0.2
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC net-next PATCH 3/4] qlge: Change rx_ring to MSIX vector mapping.
2009-08-17 21:08 [RFC net-next PATCH 0/4] qlge: Performance changes for qlge Ron Mercer
2009-08-17 21:08 ` [RFC net-next PATCH 1/4] qlge: Remove workqueue usage from data path Ron Mercer
2009-08-17 21:08 ` [RFC net-next PATCH 2/4] qlge: Move TX completion processing to send path Ron Mercer
@ 2009-08-17 21:08 ` Ron Mercer
2009-08-17 21:08 ` [RFC net-next PATCH 4/4] qlge: Change large rx buffer logic Ron Mercer
2009-08-18 0:57 ` [RFC net-next PATCH 0/4] qlge: Performance changes for qlge David Miller
4 siblings, 0 replies; 7+ messages in thread
From: Ron Mercer @ 2009-08-17 21:08 UTC (permalink / raw)
To: davem; +Cc: netdev, ron.mercer
Currently there is one default ring, one outbound rx ring for each
CPU, and one RSS ring for each CPU.
This patch allocates one RSS ring for each MSIx vector,
and adds the default ring's functionality to the first
RSS ring.
Using up an MSIX vector on sideband work is expensive on platforms
that have limited resources.
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
---
drivers/net/qlge/qlge.h | 10 +-
drivers/net/qlge/qlge_dbg.c | 8 +-
drivers/net/qlge/qlge_ethtool.c | 40 +++---
drivers/net/qlge/qlge_main.c | 266 +++++++++++++++++----------------------
4 files changed, 141 insertions(+), 183 deletions(-)
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h
index 975590c..5559c80 100644
--- a/drivers/net/qlge/qlge.h
+++ b/drivers/net/qlge/qlge.h
@@ -34,7 +34,7 @@
#define QLGE_DEVICE_ID_8000 0x8000
#define MAX_CPUS 8
#define MAX_TX_RINGS MAX_CPUS
-#define MAX_RX_RINGS ((MAX_CPUS * 2) + 1)
+#define MAX_RX_RINGS (MAX_CPUS * 2)
#define NUM_TX_RING_ENTRIES 256
#define NUM_RX_RING_ENTRIES 256
@@ -1236,7 +1236,6 @@ struct tx_ring {
* Type of inbound queue.
*/
enum {
- DEFAULT_Q = 2, /* Handles slow queue and chip/MPI events. */
TX_Q = 3, /* Handles outbound completions. */
RX_Q = 4, /* Handles inbound completions. */
};
@@ -1288,7 +1287,7 @@ struct rx_ring {
u32 sbq_free_cnt; /* free buffer desc cnt */
/* Misc. handler elements. */
- u32 type; /* Type of queue, tx, rx, or default. */
+ u32 type; /* Type of queue, tx, rx. */
u32 irq; /* Which vector this ring is assigned. */
u32 cpu; /* Which CPU this should run on. */
char name[IFNAMSIZ + 5];
@@ -1486,11 +1485,9 @@ struct ql_adapter {
struct intr_context intr_context[MAX_RX_RINGS];
int tx_ring_count; /* One per online CPU. */
- u32 rss_ring_first_cq_id;/* index of first inbound (rss) rx_ring */
- u32 rss_ring_count; /* One per online CPU. */
+ u32 rss_ring_count; /* One per irq vector. */
/*
* rx_ring_count =
- * one default queue +
* (CPU count * outbound completion rx_ring) +
* (CPU count * inbound (RSS) completion rx_ring)
*/
@@ -1502,7 +1499,6 @@ struct ql_adapter {
struct tx_ring tx_ring[MAX_TX_RINGS];
int rx_csum;
- u32 default_rx_queue;
u16 rx_coalesce_usecs; /* cqicb->int_delay */
u16 rx_max_coalesced_frames; /* cqicb->pkt_int_delay */
diff --git a/drivers/net/qlge/qlge_dbg.c b/drivers/net/qlge/qlge_dbg.c
index 40a70c3..c6d4db5 100644
--- a/drivers/net/qlge/qlge_dbg.c
+++ b/drivers/net/qlge/qlge_dbg.c
@@ -418,13 +418,9 @@ void ql_dump_qdev(struct ql_adapter *qdev)
printk(KERN_ERR PFX "qdev->intr_count = %d.\n", qdev->intr_count);
printk(KERN_ERR PFX "qdev->tx_ring = %p.\n",
qdev->tx_ring);
- printk(KERN_ERR PFX "qdev->rss_ring_first_cq_id = %d.\n",
- qdev->rss_ring_first_cq_id);
printk(KERN_ERR PFX "qdev->rss_ring_count = %d.\n",
qdev->rss_ring_count);
printk(KERN_ERR PFX "qdev->rx_ring = %p.\n", qdev->rx_ring);
- printk(KERN_ERR PFX "qdev->default_rx_queue = %d.\n",
- qdev->default_rx_queue);
printk(KERN_ERR PFX "qdev->xg_sem_mask = 0x%08x.\n",
qdev->xg_sem_mask);
printk(KERN_ERR PFX "qdev->port_link_up = 0x%08x.\n",
@@ -545,8 +541,8 @@ void ql_dump_rx_ring(struct rx_ring *rx_ring)
printk(KERN_ERR PFX
"===================== Dumping rx_ring %d ===============.\n",
rx_ring->cq_id);
- printk(KERN_ERR PFX "Dumping rx_ring %d, type = %s%s%s.\n",
- rx_ring->cq_id, rx_ring->type == DEFAULT_Q ? "DEFAULT" : "",
+ printk(KERN_ERR PFX "Dumping rx_ring %d, type = %s%s.\n",
+ rx_ring->cq_id,
rx_ring->type == TX_Q ? "OUTBOUND COMPLETIONS" : "",
rx_ring->type == RX_Q ? "INBOUND_COMPLETIONS" : "");
printk(KERN_ERR PFX "rx_ring->cqicb = %p.\n", &rx_ring->cqicb);
diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c
index eb6a9ee..57faa54 100644
--- a/drivers/net/qlge/qlge_ethtool.c
+++ b/drivers/net/qlge/qlge_ethtool.c
@@ -46,20 +46,21 @@ static int ql_update_ring_coalescing(struct ql_adapter *qdev)
return status;
spin_lock(&qdev->hw_lock);
- /* Skip the default queue, and update the outbound handler
+ /* Update the inbound (RSS) handler
* queues if they changed.
*/
- cqicb = (struct cqicb *)&qdev->rx_ring[1];
- if (le16_to_cpu(cqicb->irq_delay) != qdev->tx_coalesce_usecs ||
- le16_to_cpu(cqicb->pkt_delay) != qdev->tx_max_coalesced_frames) {
- for (i = 1; i < qdev->rss_ring_first_cq_id; i++, rx_ring++) {
+ cqicb = (struct cqicb *)&qdev->rx_ring[0];
+ if (le16_to_cpu(cqicb->irq_delay) != qdev->rx_coalesce_usecs ||
+ le16_to_cpu(cqicb->pkt_delay) !=
+ qdev->rx_max_coalesced_frames) {
+ for (i = 0; i < qdev->rss_ring_count; i++, rx_ring++) {
rx_ring = &qdev->rx_ring[i];
- cqicb = (struct cqicb *)rx_ring;
- cqicb->irq_delay = cpu_to_le16(qdev->tx_coalesce_usecs);
+ cqicb = &rx_ring->cqicb;
+ cqicb->irq_delay = cpu_to_le16(qdev->rx_coalesce_usecs);
cqicb->pkt_delay =
- cpu_to_le16(qdev->tx_max_coalesced_frames);
+ cpu_to_le16(qdev->rx_max_coalesced_frames);
cqicb->flags = FLAGS_LI;
- status = ql_write_cfg(qdev, cqicb, sizeof(*cqicb),
+ status = ql_write_cfg(qdev, cqicb, sizeof(cqicb),
CFG_LCQ, rx_ring->cq_id);
if (status) {
QPRINTK(qdev, IFUP, ERR,
@@ -69,20 +70,21 @@ static int ql_update_ring_coalescing(struct ql_adapter *qdev)
}
}
- /* Update the inbound (RSS) handler queues if they changed. */
- cqicb = (struct cqicb *)&qdev->rx_ring[qdev->rss_ring_first_cq_id];
- if (le16_to_cpu(cqicb->irq_delay) != qdev->rx_coalesce_usecs ||
- le16_to_cpu(cqicb->pkt_delay) != qdev->rx_max_coalesced_frames) {
- for (i = qdev->rss_ring_first_cq_id;
- i <= qdev->rss_ring_first_cq_id + qdev->rss_ring_count;
+ /* Update the outbound handler queues if they changed. */
+ cqicb = (struct cqicb *)&qdev->rx_ring[qdev->rss_ring_count];
+ if (le16_to_cpu(cqicb->irq_delay) != qdev->tx_coalesce_usecs ||
+ le16_to_cpu(cqicb->pkt_delay) !=
+ qdev->tx_max_coalesced_frames) {
+ for (i = qdev->rss_ring_count;
+ i < qdev->rx_ring_count;
i++) {
rx_ring = &qdev->rx_ring[i];
- cqicb = (struct cqicb *)rx_ring;
- cqicb->irq_delay = cpu_to_le16(qdev->rx_coalesce_usecs);
+ cqicb = &rx_ring->cqicb;
+ cqicb->irq_delay = cpu_to_le16(qdev->tx_coalesce_usecs);
cqicb->pkt_delay =
- cpu_to_le16(qdev->rx_max_coalesced_frames);
+ cpu_to_le16(qdev->tx_max_coalesced_frames);
cqicb->flags = FLAGS_LI;
- status = ql_write_cfg(qdev, cqicb, sizeof(*cqicb),
+ status = ql_write_cfg(qdev, cqicb, sizeof(cqicb),
CFG_LCQ, rx_ring->cq_id);
if (status) {
QPRINTK(qdev, IFUP, ERR,
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c
index d9b22da..dcac9f2 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/qlge/qlge_main.c
@@ -370,9 +370,7 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type,
cam_output = (CAM_OUT_ROUTE_NIC |
(qdev->
func << CAM_OUT_FUNC_SHIFT) |
- (qdev->
- rss_ring_first_cq_id <<
- CAM_OUT_CQ_ID_SHIFT));
+ (0 << CAM_OUT_CQ_ID_SHIFT));
if (qdev->vlgrp)
cam_output |= CAM_OUT_RV;
/* route to NIC core */
@@ -616,9 +614,8 @@ u32 ql_enable_completion_interrupt(struct ql_adapter *qdev, u32 intr)
unsigned long hw_flags = 0;
struct intr_context *ctx = qdev->intr_context + intr;
- if (likely(test_bit(QL_MSIX_ENABLED, &qdev->flags) && intr)) {
- /* Always enable if we're MSIX multi interrupts and
- * it's not the default (zeroeth) interrupt.
+ if (likely(test_bit(QL_MSIX_ENABLED, &qdev->flags))) {
+ /* Always enable if we're MSIX multi interrupts.
*/
ql_write32(qdev, INTR_EN,
ctx->intr_en_mask);
@@ -644,7 +641,7 @@ static u32 ql_disable_completion_interrupt(struct ql_adapter *qdev, u32 intr)
/* HW disables for us if we're MSIX multi interrupts and
* it's not the default (zeroeth) interrupt.
*/
- if (likely(test_bit(QL_MSIX_ENABLED, &qdev->flags) && intr))
+ if (likely(test_bit(QL_MSIX_ENABLED, &qdev->flags)))
return 0;
ctx = qdev->intr_context + intr;
@@ -1649,8 +1646,7 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
qdev->stats.rx_packets++;
qdev->stats.rx_bytes += skb->len;
- skb_record_rx_queue(skb,
- rx_ring->cq_id - qdev->rss_ring_first_cq_id);
+ skb_record_rx_queue(skb, rx_ring->cq_id);
if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
if (qdev->vlgrp &&
(ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) &&
@@ -1946,19 +1942,55 @@ static void ql_vlan_rx_kill_vid(struct net_device *ndev, u16 vid)
}
-/* MSI-X Multiple Vector Interrupt Handler for outbound completions. */
-static irqreturn_t qlge_msix_tx_isr(int irq, void *dev_id)
+/* MSI-X Multiple Vector Interrupt Handler for inbound completions. */
+static irqreturn_t qlge_msix_rx_isr(int irq, void *dev_id)
{
struct rx_ring *rx_ring = dev_id;
- ql_clean_outbound_rx_ring(rx_ring);
+ napi_schedule(&rx_ring->napi);
return IRQ_HANDLED;
}
/* MSI-X Multiple Vector Interrupt Handler for inbound completions. */
-static irqreturn_t qlge_msix_rx_isr(int irq, void *dev_id)
+static irqreturn_t qlge_msix_dflt_rx_isr(int irq, void *dev_id)
{
struct rx_ring *rx_ring = dev_id;
- napi_schedule(&rx_ring->napi);
+ struct ql_adapter *qdev = rx_ring->qdev;
+ u32 var;
+
+ var = ql_read32(qdev, STS);
+ /*
+ * Check for fatal error.
+ */
+ if (var & STS_FE) {
+ ql_queue_asic_error(qdev);
+ QPRINTK(qdev, INTR, ERR, "Got fatal error, STS = %x.\n", var);
+ var = ql_read32(qdev, ERR_STS);
+ QPRINTK(qdev, INTR, ERR,
+ "Resetting chip. Error Status Register = 0x%x\n", var);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Check MPI processor activity.
+ */
+ if ((var & STS_PI) &&
+ (ql_read32(qdev, INTR_MASK) & INTR_MASK_PI)) {
+ /*
+ * We've got an async event or mailbox completion.
+ * Handle it and clear the source of the interrupt.
+ */
+ QPRINTK(qdev, INTR, ERR, "Got MPI processor interrupt.\n");
+ queue_delayed_work(qdev->workqueue,
+ &qdev->mpi_work, 0);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Start NAPI if there's work to do.
+ */
+ if (ql_read_sh_reg(rx_ring->prod_idx_sh_reg) !=
+ rx_ring->cnsmr_idx)
+ napi_schedule(&rx_ring->napi);
return IRQ_HANDLED;
}
@@ -1972,8 +2004,7 @@ static irqreturn_t qlge_isr(int irq, void *dev_id)
struct rx_ring *rx_ring = dev_id;
struct ql_adapter *qdev = rx_ring->qdev;
struct intr_context *intr_context = &qdev->intr_context[0];
- u32 var, mask;
- int i;
+ u32 var;
int work_done = 0;
spin_lock(&qdev->hw_lock);
@@ -2016,22 +2047,14 @@ static irqreturn_t qlge_isr(int irq, void *dev_id)
/*
* Start NAPI for rx or handler for TX for each active queue.
*/
- mask = ql_read32(qdev, ISR1);
- for (i = 0; i < qdev->rx_ring_count; i++) {
- if (mask & (1 << i)) {
- rx_ring = &qdev->rx_ring[i];
- QPRINTK(qdev, INTR, INFO,
- "Waking handler for rx_ring[%d].\n", i);
- if (rx_ring->type == TX_Q)
- continue;
- else {
- ql_disable_completion_interrupt(qdev,
- intr_context->
- intr);
- napi_schedule(&rx_ring->napi);
- }
- work_done++;
- }
+ var = ql_read32(qdev, ISR1);
+ if (var) {
+ QPRINTK(qdev, INTR, INFO,
+ "Waking handler for rx_ring[0].\n");
+ ql_disable_completion_interrupt(qdev,
+ intr_context->intr);
+ napi_schedule(&rx_ring->napi);
+ work_done++;
}
ql_enable_completion_interrupt(qdev, intr_context->intr);
@@ -2612,6 +2635,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
/* Set up the shadow registers for this ring. */
rx_ring->prod_idx_sh_reg = shadow_reg;
rx_ring->prod_idx_sh_reg_dma = shadow_reg_dma;
+ *rx_ring->prod_idx_sh_reg = 0;
shadow_reg += sizeof(u64);
shadow_reg_dma += sizeof(u64);
rx_ring->lbq_base_indirect = shadow_reg;
@@ -2702,15 +2726,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
case TX_Q:
cqicb->irq_delay = cpu_to_le16(qdev->tx_coalesce_usecs);
cqicb->pkt_delay = cpu_to_le16(qdev->tx_max_coalesced_frames);
- break;
- case DEFAULT_Q:
- /* Inbound completion handling rx_rings run in
- * separate NAPI contexts.
- */
- netif_napi_add(qdev->ndev, &rx_ring->napi, ql_napi_poll_msix,
- 64);
- cqicb->irq_delay = 0;
- cqicb->pkt_delay = 0;
+ cqicb->msix_vect = 0;
break;
case RX_Q:
/* Inbound completion handling rx_rings run in
@@ -2800,15 +2816,14 @@ static void ql_disable_msix(struct ql_adapter *qdev)
static void ql_enable_msix(struct ql_adapter *qdev)
{
- int i;
+ int i, err;
- qdev->intr_count = 1;
/* Get the MSIX vectors. */
if (irq_type == MSIX_IRQ) {
/* Try to alloc space for the msix struct,
* if it fails then go to MSI/legacy.
*/
- qdev->msi_x_entry = kcalloc(qdev->rx_ring_count,
+ qdev->msi_x_entry = kcalloc(qdev->intr_count,
sizeof(struct msix_entry),
GFP_KERNEL);
if (!qdev->msi_x_entry) {
@@ -2816,26 +2831,39 @@ static void ql_enable_msix(struct ql_adapter *qdev)
goto msi;
}
- for (i = 0; i < qdev->rx_ring_count; i++)
+ for (i = 0; i < qdev->intr_count; i++) {
qdev->msi_x_entry[i].entry = i;
+ qdev->msi_x_entry[i].vector = 0;
+ }
- if (!pci_enable_msix
- (qdev->pdev, qdev->msi_x_entry, qdev->rx_ring_count)) {
- set_bit(QL_MSIX_ENABLED, &qdev->flags);
- qdev->intr_count = qdev->rx_ring_count;
- QPRINTK(qdev, IFUP, DEBUG,
- "MSI-X Enabled, got %d vectors.\n",
- qdev->intr_count);
- return;
- } else {
+ /* Loop to get our vectors. We start with
+ * what we want and settle for what we get.
+ */
+ do {
+ err = pci_enable_msix(qdev->pdev,
+ qdev->msi_x_entry, qdev->intr_count);
+ if (err > 0)
+ qdev->intr_count = err;
+ } while (err > 0);
+
+ if (err < 0) {
+ pci_disable_msix(qdev->pdev);
kfree(qdev->msi_x_entry);
qdev->msi_x_entry = NULL;
QPRINTK(qdev, IFUP, WARNING,
"MSI-X Enable failed, trying MSI.\n");
+ qdev->intr_count = 1;
irq_type = MSI_IRQ;
+ } else if (err == 0) {
+ set_bit(QL_MSIX_ENABLED, &qdev->flags);
+ QPRINTK(qdev, IFUP, INFO,
+ "MSI-X Enabled, got %d vectors.\n",
+ qdev->intr_count);
+ return;
}
}
msi:
+ qdev->intr_count = 1;
if (irq_type == MSI_IRQ) {
if (!pci_enable_msi(qdev->pdev)) {
set_bit(QL_MSI_ENABLED, &qdev->flags);
@@ -2845,7 +2873,7 @@ msi:
}
}
irq_type = LEG_IRQ;
- QPRINTK(qdev, IFUP, DEBUG, "Running with legacy interrupts.\n");
+ QPRINTK(qdev, IFUP, INFO, "Running with legacy interrupts.\n");
}
/*
@@ -2859,13 +2887,10 @@ static void ql_resolve_queues_to_irqs(struct ql_adapter *qdev)
int i = 0;
struct intr_context *intr_context = &qdev->intr_context[0];
- ql_enable_msix(qdev);
-
if (likely(test_bit(QL_MSIX_ENABLED, &qdev->flags))) {
/* Each rx_ring has it's
* own intr_context since we have separate
* vectors for each queue.
- * This only true when MSI-X is enabled.
*/
for (i = 0; i < qdev->intr_count; i++, intr_context++) {
qdev->rx_ring[i].irq = i;
@@ -2887,25 +2912,18 @@ static void ql_resolve_queues_to_irqs(struct ql_adapter *qdev)
INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK |
INTR_EN_TYPE_READ | INTR_EN_IHD_MASK | INTR_EN_IHD |
i;
-
if (i == 0) {
- /*
- * Default queue handles bcast/mcast plus
- * async events. Needs buffers.
+ /* The first vector/queue handles
+ * broadcast/multicast, fatal errors,
+ * and firmware events. This in addition
+ * to normal inbound NAPI processing.
*/
- intr_context->handler = qlge_isr;
- sprintf(intr_context->name, "%s-default-queue",
- qdev->ndev->name);
- } else if (i < qdev->rss_ring_first_cq_id) {
- /*
- * Outbound queue is for outbound completions only.
- */
- intr_context->handler = qlge_msix_tx_isr;
- sprintf(intr_context->name, "%s-tx-%d",
+ intr_context->handler = qlge_msix_dflt_rx_isr;
+ sprintf(intr_context->name, "%s-rx-%d",
qdev->ndev->name, i);
} else {
/*
- * Inbound queues handle unicast frames only.
+ * Inbound queues handle unicast frames.
*/
intr_context->handler = qlge_msix_rx_isr;
sprintf(intr_context->name, "%s-rx-%d",
@@ -2935,7 +2953,7 @@ static void ql_resolve_queues_to_irqs(struct ql_adapter *qdev)
*/
intr_context->handler = qlge_isr;
sprintf(intr_context->name, "%s-single_irq", qdev->ndev->name);
- for (i = 0; i < qdev->rx_ring_count; i++)
+ for (i = 0; i < qdev->rss_ring_count; i++)
qdev->rx_ring[i].irq = 0;
}
}
@@ -2986,11 +3004,10 @@ static int ql_request_irq(struct ql_adapter *qdev)
goto err_irq;
} else {
QPRINTK(qdev, IFUP, DEBUG,
- "Hooked intr %d, queue type %s%s%s, with name %s.\n",
+ "Hooked intr %d, queue type %s%s, "
+ "with name %s.\n",
i,
qdev->rx_ring[i].type ==
- DEFAULT_Q ? "DEFAULT_Q" : "",
- qdev->rx_ring[i].type ==
TX_Q ? "TX_Q" : "",
qdev->rx_ring[i].type ==
RX_Q ? "RX_Q" : "", intr_context->name);
@@ -3016,10 +3033,8 @@ static int ql_request_irq(struct ql_adapter *qdev)
goto err_irq;
QPRINTK(qdev, IFUP, ERR,
- "Hooked intr %d, queue type %s%s%s, with name %s.\n",
+ "Hooked intr %d, queue type %s%s, with name %s.\n",
i,
- qdev->rx_ring[0].type ==
- DEFAULT_Q ? "DEFAULT_Q" : "",
qdev->rx_ring[0].type == TX_Q ? "TX_Q" : "",
qdev->rx_ring[0].type == RX_Q ? "RX_Q" : "",
intr_context->name);
@@ -3042,7 +3057,7 @@ static int ql_start_rss(struct ql_adapter *qdev)
memset((void *)ricb, 0, sizeof(*ricb));
- ricb->base_cq = qdev->rss_ring_first_cq_id | RSS_L4K;
+ ricb->base_cq = RSS_L4K;
ricb->flags =
(RSS_L6K | RSS_LI | RSS_LB | RSS_LM | RSS_RI4 | RSS_RI6 | RSS_RT4 |
RSS_RT6);
@@ -3244,7 +3259,7 @@ static int ql_adapter_initialize(struct ql_adapter *qdev)
}
/* Start NAPI for the RSS queues. */
- for (i = qdev->rss_ring_first_cq_id; i < qdev->rx_ring_count; i++) {
+ for (i = 0; i < qdev->rss_ring_count; i++) {
QPRINTK(qdev, IFUP, DEBUG, "Enabling NAPI for rx_ring[%d].\n",
i);
napi_enable(&qdev->rx_ring[i].napi);
@@ -3306,7 +3321,6 @@ static void ql_display_dev_info(struct net_device *ndev)
static int ql_adapter_down(struct ql_adapter *qdev)
{
int i, status = 0;
- struct rx_ring *rx_ring;
ql_link_off(qdev);
@@ -3320,20 +3334,12 @@ static int ql_adapter_down(struct ql_adapter *qdev)
cancel_delayed_work_sync(&qdev->mpi_idc_work);
cancel_delayed_work_sync(&qdev->mpi_port_cfg_work);
- /* The rest of the rx_rings are processed in
- * a workqueue only if it's a single interrupt
- * environment (MSI/Legacy).
- */
- for (i = 0; i < qdev->rx_ring_count; i++) {
- rx_ring = &qdev->rx_ring[i];
/* Only the RSS rings use NAPI on multi irq
* environment. Outbound completion processing
* is done in interrupt context.
*/
- if (i >= qdev->rss_ring_first_cq_id) {
- napi_disable(&rx_ring->napi);
- }
- }
+ for (i = 0; i < qdev->rss_ring_count; i++)
+ napi_disable(&qdev->rx_ring[i].napi);
/* Delete the timers used for cleaning up
* TX completions.
@@ -3349,7 +3355,7 @@ static int ql_adapter_down(struct ql_adapter *qdev)
/* Call netif_napi_del() from common point.
*/
- for (i = qdev->rss_ring_first_cq_id; i < qdev->rx_ring_count; i++)
+ for (i = 0; i < qdev->rss_ring_count; i++)
netif_napi_del(&qdev->rx_ring[i].napi);
ql_free_rx_buffers(qdev);
@@ -3428,43 +3434,20 @@ static int ql_configure_rings(struct ql_adapter *qdev)
int i;
struct rx_ring *rx_ring;
struct tx_ring *tx_ring;
- int cpu_cnt = num_online_cpus();
-
- /*
- * For each processor present we allocate one
- * rx_ring for outbound completions, and one
- * rx_ring for inbound completions. Plus there is
- * always the one default queue. For the CPU
- * counts we end up with the following rx_rings:
- * rx_ring count =
- * one default queue +
- * (CPU count * outbound completion rx_ring) +
- * (CPU count * inbound (RSS) completion rx_ring)
- * To keep it simple we limit the total number of
- * queues to < 32, so we truncate CPU to 8.
- * This limitation can be removed when requested.
- */
-
- if (cpu_cnt > MAX_CPUS)
- cpu_cnt = MAX_CPUS;
+ int cpu_cnt = min(MAX_CPUS, (int)num_online_cpus());
- /*
- * rx_ring[0] is always the default queue.
- */
/* Allocate outbound completion ring for each CPU. */
qdev->tx_ring_count = cpu_cnt;
- /* Allocate inbound completion (RSS) ring for each CPU. */
qdev->rss_ring_count = cpu_cnt;
- /* cq_id for the first inbound ring handler. */
- qdev->rss_ring_first_cq_id = cpu_cnt + 1;
+ qdev->intr_count = qdev->rss_ring_count;
+ /* Try to get a vector for each RSS queue (per CPU). */
+ ql_enable_msix(qdev);
/*
- * qdev->rx_ring_count:
- * Total number of rx_rings. This includes the one
- * default queue, a number of outbound completion
- * handler rx_rings, and the number of inbound
- * completion handler rx_rings.
+ * We might not have gotten what we wanted.
+ * Downshift to what we were actually given
*/
- qdev->rx_ring_count = qdev->tx_ring_count + qdev->rss_ring_count + 1;
+ qdev->rss_ring_count = qdev->intr_count;
+ qdev->rx_ring_count = qdev->tx_ring_count + qdev->rss_ring_count;
for (i = 0; i < qdev->tx_ring_count; i++) {
tx_ring = &qdev->tx_ring[i];
@@ -3477,9 +3460,9 @@ static int ql_configure_rings(struct ql_adapter *qdev)
/*
* The completion queue ID for the tx rings start
- * immediately after the default Q ID, which is zero.
+ * immediately after the rss rings.
*/
- tx_ring->cq_id = i + 1;
+ tx_ring->cq_id = qdev->rss_ring_count + i;
init_timer(&tx_ring->txq_clean_timer);
tx_ring->txq_clean_timer.data = (unsigned long)tx_ring;
tx_ring->txq_clean_timer.function = ql_txq_clean_timer;
@@ -3487,14 +3470,12 @@ static int ql_configure_rings(struct ql_adapter *qdev)
for (i = 0; i < qdev->rx_ring_count; i++) {
rx_ring = &qdev->rx_ring[i];
- memset((void *)rx_ring, 0, sizeof(*rx_ring));
+ memset(rx_ring, 0, sizeof(*rx_ring));
rx_ring->qdev = qdev;
rx_ring->cq_id = i;
- rx_ring->cpu = i % cpu_cnt; /* CPU to run handler on. */
- if (i == 0) { /* Default queue at index 0. */
+ if (i < qdev->rss_ring_count) {
/*
- * Default queue handles bcast/mcast plus
- * async events. Needs buffers.
+ * Inbound (RSS) queues.
*/
rx_ring->cq_len = qdev->rx_ring_size;
rx_ring->cq_size =
@@ -3507,12 +3488,11 @@ static int ql_configure_rings(struct ql_adapter *qdev)
rx_ring->sbq_size =
rx_ring->sbq_len * sizeof(__le64);
rx_ring->sbq_buf_size = SMALL_BUFFER_SIZE * 2;
- rx_ring->type = DEFAULT_Q;
- } else if (i < qdev->rss_ring_first_cq_id) {
+ rx_ring->type = RX_Q;
+ } else {
/*
- * Outbound queue handles outbound completions only.
+ * Outbound queues handling outbound completions only.
*/
- /* outbound cq is same size as tx_ring it services. */
rx_ring->cq_len = qdev->tx_ring_size;
rx_ring->cq_size =
rx_ring->cq_len * sizeof(struct ql_net_rsp_iocb);
@@ -3523,22 +3503,6 @@ static int ql_configure_rings(struct ql_adapter *qdev)
rx_ring->sbq_size = 0;
rx_ring->sbq_buf_size = 0;
rx_ring->type = TX_Q;
- } else { /* Inbound completions (RSS) queues */
- /*
- * Inbound queues handle unicast frames only.
- */
- rx_ring->cq_len = qdev->rx_ring_size;
- rx_ring->cq_size =
- rx_ring->cq_len * sizeof(struct ql_net_rsp_iocb);
- rx_ring->lbq_len = NUM_LARGE_BUFFERS;
- rx_ring->lbq_size =
- rx_ring->lbq_len * sizeof(__le64);
- rx_ring->lbq_buf_size = LARGE_BUFFER_SIZE;
- rx_ring->sbq_len = NUM_SMALL_BUFFERS;
- rx_ring->sbq_size =
- rx_ring->sbq_len * sizeof(__le64);
- rx_ring->sbq_buf_size = SMALL_BUFFER_SIZE * 2;
- rx_ring->type = RX_Q;
}
}
return 0;
--
1.6.0.2
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC net-next PATCH 4/4] qlge: Change large rx buffer logic.
2009-08-17 21:08 [RFC net-next PATCH 0/4] qlge: Performance changes for qlge Ron Mercer
` (2 preceding siblings ...)
2009-08-17 21:08 ` [RFC net-next PATCH 3/4] qlge: Change rx_ring to MSIX vector mapping Ron Mercer
@ 2009-08-17 21:08 ` Ron Mercer
2009-08-18 0:57 ` [RFC net-next PATCH 0/4] qlge: Performance changes for qlge David Miller
4 siblings, 0 replies; 7+ messages in thread
From: Ron Mercer @ 2009-08-17 21:08 UTC (permalink / raw)
To: davem; +Cc: netdev, ron.mercer
Currently we use a single page per large rx buffer. This patch changes
this to use either chunks of pages for small MTU or multiple pages for
jumbo MTU depending on the page size.
Examples:
64k pages at 1500 MTU:
large buffers will be 2048 bytes in length and there will be 32
per page.
4k pages at 9000 MTU:
large buffer will use 4 pages as one large buffer.
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
---
drivers/net/qlge/qlge.h | 16 ++-
drivers/net/qlge/qlge_main.c | 302 +++++++++++++++++++++++++++++-------------
2 files changed, 224 insertions(+), 94 deletions(-)
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h
index 5559c80..c731a05 100644
--- a/drivers/net/qlge/qlge.h
+++ b/drivers/net/qlge/qlge.h
@@ -53,8 +53,8 @@
#define RX_RING_SHADOW_SPACE (sizeof(u64) + \
MAX_DB_PAGES_PER_BQ(NUM_SMALL_BUFFERS) * sizeof(u64) + \
MAX_DB_PAGES_PER_BQ(NUM_LARGE_BUFFERS) * sizeof(u64))
-#define SMALL_BUFFER_SIZE 256
-#define LARGE_BUFFER_SIZE PAGE_SIZE
+#define SMALL_BUFFER_SIZE 512
+#define SMALL_BUF_MAP_SIZE (SMALL_BUFFER_SIZE / 2)
#define MAX_SPLIT_SIZE 1023
#define QLGE_SB_PAD 32
@@ -1193,9 +1193,17 @@ struct tx_ring_desc {
struct tx_ring_desc *next;
};
+struct page_chunk {
+ struct page *page; /* master page */
+ char *va; /* virt addr for this chunk */
+ u64 map; /* mapping for master */
+ unsigned int offset; /* offset for this chunk */
+ unsigned int last_flag; /* flag set for last chunk in page */
+};
+
struct bq_desc {
union {
- struct page *lbq_page;
+ struct page_chunk pg_chunk;
struct sk_buff *skb;
} p;
__le64 *addr;
@@ -1264,6 +1272,7 @@ struct rx_ring {
dma_addr_t lbq_base_dma;
void *lbq_base_indirect;
dma_addr_t lbq_base_indirect_dma;
+ struct page_chunk pg_chunk; /* current page for chunks */
struct bq_desc *lbq; /* array of control blocks */
void __iomem *lbq_prod_idx_db_reg; /* PCI doorbell mem area + 0x18 */
u32 lbq_prod_idx; /* current sw prod idx */
@@ -1497,6 +1506,7 @@ struct ql_adapter {
struct rx_ring rx_ring[MAX_RX_RINGS];
struct tx_ring tx_ring[MAX_TX_RINGS];
+ unsigned int lbq_buf_order;
int rx_csum;
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c
index dcac9f2..a057ae1 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/qlge/qlge_main.c
@@ -996,6 +996,11 @@ end:
return status;
}
+static inline unsigned int ql_lbq_block_size(struct ql_adapter *qdev)
+{
+ return PAGE_SIZE << qdev->lbq_buf_order;
+}
+
/* Get the next large buffer. */
static struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring)
{
@@ -1007,6 +1012,28 @@ static struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring)
return lbq_desc;
}
+static struct bq_desc *ql_get_curr_lchunk(struct ql_adapter *qdev,
+ struct rx_ring *rx_ring)
+{
+ struct bq_desc *lbq_desc = ql_get_curr_lbuf(rx_ring);
+
+ pci_dma_sync_single_for_cpu(qdev->pdev,
+ pci_unmap_addr(lbq_desc, mapaddr),
+ rx_ring->lbq_buf_size,
+ PCI_DMA_FROMDEVICE);
+
+ /* If it's the last chunk of our master page then
+ * we unmap it.
+ */
+ if ((lbq_desc->p.pg_chunk.offset + rx_ring->lbq_buf_size)
+ == ql_lbq_block_size(qdev))
+ pci_unmap_page(qdev->pdev,
+ lbq_desc->p.pg_chunk.map,
+ ql_lbq_block_size(qdev),
+ PCI_DMA_FROMDEVICE);
+ return lbq_desc;
+}
+
/* Get the next small buffer. */
static struct bq_desc *ql_get_curr_sbuf(struct rx_ring *rx_ring)
{
@@ -1034,6 +1061,53 @@ static void ql_write_cq_idx(struct rx_ring *rx_ring)
ql_write_db_reg(rx_ring->cnsmr_idx, rx_ring->cnsmr_idx_db_reg);
}
+static int ql_get_next_chunk(struct ql_adapter *qdev, struct rx_ring *rx_ring,
+ struct bq_desc *lbq_desc)
+{
+ if (!rx_ring->pg_chunk.page) {
+ u64 map;
+ rx_ring->pg_chunk.page = alloc_pages(__GFP_COLD | __GFP_COMP |
+ GFP_ATOMIC,
+ qdev->lbq_buf_order);
+ if (unlikely(!rx_ring->pg_chunk.page)) {
+ QPRINTK(qdev, DRV, ERR,
+ "page allocation failed.\n");
+ return -ENOMEM;
+ }
+ rx_ring->pg_chunk.offset = 0;
+ map = pci_map_page(qdev->pdev, rx_ring->pg_chunk.page,
+ 0, ql_lbq_block_size(qdev),
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(qdev->pdev, map)) {
+ __free_pages(rx_ring->pg_chunk.page,
+ qdev->lbq_buf_order);
+ QPRINTK(qdev, DRV, ERR,
+ "PCI mapping failed.\n");
+ return -ENOMEM;
+ }
+ rx_ring->pg_chunk.map = map;
+ rx_ring->pg_chunk.va = page_address(rx_ring->pg_chunk.page);
+ }
+
+ /* Copy the current master pg_chunk info
+ * to the current descriptor.
+ */
+ lbq_desc->p.pg_chunk = rx_ring->pg_chunk;
+
+ /* Adjust the master page chunk for next
+ * buffer get.
+ */
+ rx_ring->pg_chunk.offset += rx_ring->lbq_buf_size;
+ if (rx_ring->pg_chunk.offset == ql_lbq_block_size(qdev)) {
+ rx_ring->pg_chunk.page = NULL;
+ lbq_desc->p.pg_chunk.last_flag = 1;
+ } else {
+ rx_ring->pg_chunk.va += rx_ring->lbq_buf_size;
+ get_page(rx_ring->pg_chunk.page);
+ lbq_desc->p.pg_chunk.last_flag = 0;
+ }
+ return 0;
+}
/* Process (refill) a large buffer queue. */
static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
{
@@ -1043,39 +1117,30 @@ static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
u64 map;
int i;
- while (rx_ring->lbq_free_cnt > 16) {
+ while (rx_ring->lbq_free_cnt > 32) {
for (i = 0; i < 16; i++) {
QPRINTK(qdev, RX_STATUS, DEBUG,
"lbq: try cleaning clean_idx = %d.\n",
clean_idx);
lbq_desc = &rx_ring->lbq[clean_idx];
- if (lbq_desc->p.lbq_page == NULL) {
- QPRINTK(qdev, RX_STATUS, DEBUG,
- "lbq: getting new page for index %d.\n",
- lbq_desc->index);
- lbq_desc->p.lbq_page = alloc_page(GFP_ATOMIC);
- if (lbq_desc->p.lbq_page == NULL) {
- rx_ring->lbq_clean_idx = clean_idx;
- QPRINTK(qdev, RX_STATUS, ERR,
- "Couldn't get a page.\n");
- return;
- }
- map = pci_map_page(qdev->pdev,
- lbq_desc->p.lbq_page,
- 0, PAGE_SIZE,
- PCI_DMA_FROMDEVICE);
- if (pci_dma_mapping_error(qdev->pdev, map)) {
- rx_ring->lbq_clean_idx = clean_idx;
- put_page(lbq_desc->p.lbq_page);
- lbq_desc->p.lbq_page = NULL;
- QPRINTK(qdev, RX_STATUS, ERR,
- "PCI mapping failed.\n");
+ if (ql_get_next_chunk(qdev, rx_ring, lbq_desc)) {
+ QPRINTK(qdev, IFUP, ERR,
+ "Could not get a page chunk.\n");
return;
}
- pci_unmap_addr_set(lbq_desc, mapaddr, map);
- pci_unmap_len_set(lbq_desc, maplen, PAGE_SIZE);
- *lbq_desc->addr = cpu_to_le64(map);
- }
+
+ map = lbq_desc->p.pg_chunk.map +
+ lbq_desc->p.pg_chunk.offset;
+ pci_unmap_addr_set(lbq_desc, mapaddr, map);
+ pci_unmap_len_set(lbq_desc,
+ maplen,
+ rx_ring->lbq_buf_size);
+
+ *lbq_desc->addr = cpu_to_le64(map);
+
+ pci_dma_sync_single_for_device(qdev->pdev, map,
+ rx_ring->lbq_buf_size,
+ PCI_DMA_FROMDEVICE);
clean_idx++;
if (clean_idx == rx_ring->lbq_len)
clean_idx = 0;
@@ -1118,7 +1183,7 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
sbq_desc->index);
sbq_desc->p.skb =
netdev_alloc_skb(qdev->ndev,
- rx_ring->sbq_buf_size);
+ SMALL_BUFFER_SIZE);
if (sbq_desc->p.skb == NULL) {
QPRINTK(qdev, PROBE, ERR,
"Couldn't get an skb.\n");
@@ -1128,8 +1193,8 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
skb_reserve(sbq_desc->p.skb, QLGE_SB_PAD);
map = pci_map_single(qdev->pdev,
sbq_desc->p.skb->data,
- rx_ring->sbq_buf_size /
- 2, PCI_DMA_FROMDEVICE);
+ rx_ring->sbq_buf_size,
+ PCI_DMA_FROMDEVICE);
if (pci_dma_mapping_error(qdev->pdev, map)) {
QPRINTK(qdev, IFUP, ERR, "PCI mapping failed.\n");
rx_ring->sbq_clean_idx = clean_idx;
@@ -1139,7 +1204,7 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
}
pci_unmap_addr_set(sbq_desc, mapaddr, map);
pci_unmap_len_set(sbq_desc, maplen,
- rx_ring->sbq_buf_size / 2);
+ rx_ring->sbq_buf_size);
*sbq_desc->addr = cpu_to_le64(map);
}
@@ -1451,48 +1516,41 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
* chain it to the header buffer's skb and let
* it rip.
*/
- lbq_desc = ql_get_curr_lbuf(rx_ring);
- pci_unmap_page(qdev->pdev,
- pci_unmap_addr(lbq_desc,
- mapaddr),
- pci_unmap_len(lbq_desc, maplen),
- PCI_DMA_FROMDEVICE);
+ lbq_desc = ql_get_curr_lchunk(qdev, rx_ring);
QPRINTK(qdev, RX_STATUS, DEBUG,
- "Chaining page to skb.\n");
- skb_fill_page_desc(skb, 0, lbq_desc->p.lbq_page,
- 0, length);
+ "Chaining page at offset = %d,"
+ "for %d bytes to skb.\n",
+ lbq_desc->p.pg_chunk.offset, length);
+ skb_fill_page_desc(skb, 0, lbq_desc->p.pg_chunk.page,
+ lbq_desc->p.pg_chunk.offset,
+ length);
skb->len += length;
skb->data_len += length;
skb->truesize += length;
- lbq_desc->p.lbq_page = NULL;
} else {
/*
* The headers and data are in a single large buffer. We
* copy it to a new skb and let it go. This can happen with
* jumbo mtu on a non-TCP/UDP frame.
*/
- lbq_desc = ql_get_curr_lbuf(rx_ring);
+ lbq_desc = ql_get_curr_lchunk(qdev, rx_ring);
skb = netdev_alloc_skb(qdev->ndev, length);
if (skb == NULL) {
- QPRINTK(qdev, PROBE, DEBUG,
+ QPRINTK(qdev, PROBE, ERR,
"No skb available, drop the packet.\n");
return NULL;
}
- pci_unmap_page(qdev->pdev,
- pci_unmap_addr(lbq_desc,
- mapaddr),
- pci_unmap_len(lbq_desc, maplen),
- PCI_DMA_FROMDEVICE);
skb_reserve(skb, NET_IP_ALIGN);
QPRINTK(qdev, RX_STATUS, DEBUG,
"%d bytes of headers and data in large. Chain page to new skb and pull tail.\n", length);
- skb_fill_page_desc(skb, 0, lbq_desc->p.lbq_page,
- 0, length);
+ skb_fill_page_desc(skb, 0,
+ lbq_desc->p.pg_chunk.page,
+ lbq_desc->p.pg_chunk.offset,
+ length);
skb->len += length;
skb->data_len += length;
skb->truesize += length;
length -= length;
- lbq_desc->p.lbq_page = NULL;
__pskb_pull_tail(skb,
(ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ?
VLAN_ETH_HLEN : ETH_HLEN);
@@ -1509,8 +1567,7 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
* frames. If the MTU goes up we could
* eventually be in trouble.
*/
- int size, offset, i = 0;
- __le64 *bq, bq_array[8];
+ int size, i = 0;
sbq_desc = ql_get_curr_sbuf(rx_ring);
pci_unmap_single(qdev->pdev,
pci_unmap_addr(sbq_desc, mapaddr),
@@ -1529,37 +1586,25 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
QPRINTK(qdev, RX_STATUS, DEBUG,
"%d bytes of headers & data in chain of large.\n", length);
skb = sbq_desc->p.skb;
- bq = &bq_array[0];
- memcpy(bq, skb->data, sizeof(bq_array));
sbq_desc->p.skb = NULL;
skb_reserve(skb, NET_IP_ALIGN);
- } else {
- QPRINTK(qdev, RX_STATUS, DEBUG,
- "Headers in small, %d bytes of data in chain of large.\n", length);
- bq = (__le64 *)sbq_desc->p.skb->data;
}
while (length > 0) {
- lbq_desc = ql_get_curr_lbuf(rx_ring);
- pci_unmap_page(qdev->pdev,
- pci_unmap_addr(lbq_desc,
- mapaddr),
- pci_unmap_len(lbq_desc,
- maplen),
- PCI_DMA_FROMDEVICE);
- size = (length < PAGE_SIZE) ? length : PAGE_SIZE;
- offset = 0;
+ lbq_desc = ql_get_curr_lchunk(qdev, rx_ring);
+ size = (length < rx_ring->lbq_buf_size) ? length :
+ rx_ring->lbq_buf_size;
QPRINTK(qdev, RX_STATUS, DEBUG,
"Adding page %d to skb for %d bytes.\n",
i, size);
- skb_fill_page_desc(skb, i, lbq_desc->p.lbq_page,
- offset, size);
+ skb_fill_page_desc(skb, i,
+ lbq_desc->p.pg_chunk.page,
+ lbq_desc->p.pg_chunk.offset,
+ size);
skb->len += size;
skb->data_len += size;
skb->truesize += size;
length -= size;
- lbq_desc->p.lbq_page = NULL;
- bq++;
i++;
}
__pskb_pull_tail(skb, (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ?
@@ -1576,7 +1621,7 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
struct net_device *ndev = qdev->ndev;
struct sk_buff *skb = NULL;
u16 vlan_id = (le16_to_cpu(ib_mac_rsp->vlan_id) &
- IB_MAC_IOCB_RSP_VLAN_MASK)
+ IB_MAC_IOCB_RSP_VLAN_MASK);
QL_DUMP_IB_MAC_RSP(ib_mac_rsp);
@@ -2322,25 +2367,34 @@ err:
return -ENOMEM;
}
-static void ql_free_lbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring)
+static void ql_free_lbq_buffers(struct ql_adapter *qdev,
+ struct rx_ring *rx_ring)
{
- int i;
struct bq_desc *lbq_desc;
- for (i = 0; i < rx_ring->lbq_len; i++) {
- lbq_desc = &rx_ring->lbq[i];
- if (lbq_desc->p.lbq_page) {
- pci_unmap_page(qdev->pdev,
- pci_unmap_addr(lbq_desc, mapaddr),
- pci_unmap_len(lbq_desc, maplen),
- PCI_DMA_FROMDEVICE);
+ uint32_t curr_idx, clean_idx;
+
+ curr_idx = rx_ring->lbq_curr_idx;
+ clean_idx = rx_ring->lbq_clean_idx;
+ while (curr_idx != clean_idx) {
+ lbq_desc = &rx_ring->lbq[curr_idx];
- put_page(lbq_desc->p.lbq_page);
- lbq_desc->p.lbq_page = NULL;
+ if (lbq_desc->p.pg_chunk.last_flag) {
+ pci_unmap_page(qdev->pdev,
+ lbq_desc->p.pg_chunk.map,
+ ql_lbq_block_size(qdev),
+ PCI_DMA_FROMDEVICE);
+ lbq_desc->p.pg_chunk.last_flag = 0;
}
+
+ put_page(lbq_desc->p.pg_chunk.page);
+ lbq_desc->p.pg_chunk.page = NULL;
+
+ if (++curr_idx == rx_ring->lbq_len)
+ curr_idx = 0;
+
}
}
-
static void ql_free_sbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring)
{
int i;
@@ -2713,7 +2767,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
cqicb->sbq_addr =
cpu_to_le64(rx_ring->sbq_base_indirect_dma);
cqicb->sbq_buf_size =
- cpu_to_le16((u16)(rx_ring->sbq_buf_size/2));
+ cpu_to_le16((u16)(rx_ring->sbq_buf_size));
bq_len = (rx_ring->sbq_len == 65536) ? 0 :
(u16) rx_ring->sbq_len;
cqicb->sbq_len = cpu_to_le16(bq_len);
@@ -2732,6 +2786,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
/* Inbound completion handling rx_rings run in
* separate NAPI contexts.
*/
+ rx_ring->napi.dev = qdev->ndev;
netif_napi_add(qdev->ndev, &rx_ring->napi, ql_napi_poll_msix,
64);
cqicb->irq_delay = cpu_to_le16(qdev->rx_coalesce_usecs);
@@ -3210,7 +3265,7 @@ static int ql_adapter_initialize(struct ql_adapter *qdev)
ql_write32(qdev, FSC, mask | value);
ql_write32(qdev, SPLT_HDR, SPLT_HDR_EP |
- min(SMALL_BUFFER_SIZE, MAX_SPLIT_SIZE));
+ min(SMALL_BUF_MAP_SIZE, MAX_SPLIT_SIZE));
/* Start up the rx queues. */
for (i = 0; i < qdev->rx_ring_count; i++) {
@@ -3435,6 +3490,10 @@ static int ql_configure_rings(struct ql_adapter *qdev)
struct rx_ring *rx_ring;
struct tx_ring *tx_ring;
int cpu_cnt = min(MAX_CPUS, (int)num_online_cpus());
+ unsigned int lbq_buf_len =
+ (qdev->ndev->mtu > 1500) ? 16384 : 2048;
+
+ qdev->lbq_buf_order = get_order(lbq_buf_len);
/* Allocate outbound completion ring for each CPU. */
qdev->tx_ring_count = cpu_cnt;
@@ -3483,11 +3542,11 @@ static int ql_configure_rings(struct ql_adapter *qdev)
rx_ring->lbq_len = NUM_LARGE_BUFFERS;
rx_ring->lbq_size =
rx_ring->lbq_len * sizeof(__le64);
- rx_ring->lbq_buf_size = LARGE_BUFFER_SIZE;
+ rx_ring->lbq_buf_size = (u16)lbq_buf_len;
rx_ring->sbq_len = NUM_SMALL_BUFFERS;
rx_ring->sbq_size =
rx_ring->sbq_len * sizeof(__le64);
- rx_ring->sbq_buf_size = SMALL_BUFFER_SIZE * 2;
+ rx_ring->sbq_buf_size = SMALL_BUF_MAP_SIZE;
rx_ring->type = RX_Q;
} else {
/*
@@ -3532,14 +3591,64 @@ error_up:
return err;
}
+static int ql_change_rx_buffers(struct ql_adapter *qdev)
+{
+ struct rx_ring *rx_ring;
+ int i, status;
+ u32 lbq_buf_len;
+
+ /* Wait for an oustanding reset to complete. */
+ if (!test_bit(QL_ADAPTER_UP, &qdev->flags)) {
+ int i = 3;
+ while (i-- && !test_bit(QL_ADAPTER_UP, &qdev->flags)) {
+ QPRINTK(qdev, IFUP, ERR,
+ "Waiting for adapter UP...\n");
+ ssleep(1);
+ }
+
+ if (!i) {
+ QPRINTK(qdev, IFUP, ERR,
+ "Timed out waiting for adapter UP\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ status = ql_adapter_down(qdev);
+ if (status)
+ goto error;
+
+ /* Get the new rx buffer size. */
+ lbq_buf_len = (qdev->ndev->mtu > 1500) ? 16384 : 2048;
+ qdev->lbq_buf_order = get_order(lbq_buf_len);
+
+ for (i = 0; i < qdev->rss_ring_count; i++) {
+ rx_ring = &qdev->rx_ring[i];
+ /* Set the new size. */
+ rx_ring->lbq_buf_size = lbq_buf_len;
+ }
+
+ status = ql_adapter_up(qdev);
+ if (status)
+ goto error;
+
+ return status;
+error:
+ QPRINTK(qdev, IFUP, ALERT,
+ "Driver up/down cycle failed, closing device.\n");
+ rtnl_lock();
+ set_bit(QL_ADAPTER_UP, &qdev->flags);
+ dev_close(qdev->ndev);
+ rtnl_unlock();
+ return status;
+}
+
static int qlge_change_mtu(struct net_device *ndev, int new_mtu)
{
struct ql_adapter *qdev = netdev_priv(ndev);
+ int status = 0;
if (ndev->mtu == 1500 && new_mtu == 9000) {
QPRINTK(qdev, IFUP, ERR, "Changing to jumbo MTU.\n");
- queue_delayed_work(qdev->workqueue,
- &qdev->mpi_port_cfg_work, 0);
} else if (ndev->mtu == 9000 && new_mtu == 1500) {
QPRINTK(qdev, IFUP, ERR, "Changing to normal MTU.\n");
} else if ((ndev->mtu == 1500 && new_mtu == 1500) ||
@@ -3547,8 +3656,19 @@ static int qlge_change_mtu(struct net_device *ndev, int new_mtu)
return 0;
} else
return -EINVAL;
+
+ if (!netif_running(qdev->ndev)) {
+ ndev->mtu = new_mtu;
+ return 0;
+ }
+
ndev->mtu = new_mtu;
- return 0;
+ status = ql_change_rx_buffers(qdev);
+ if (status)
+ QPRINTK(qdev, IFUP, ERR,
+ "Changing MTU failed.\n");
+
+ return status;
}
static struct net_device_stats *qlge_get_stats(struct net_device
--
1.6.0.2
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [RFC net-next PATCH 0/4] qlge: Performance changes for qlge.
2009-08-17 21:08 [RFC net-next PATCH 0/4] qlge: Performance changes for qlge Ron Mercer
` (3 preceding siblings ...)
2009-08-17 21:08 ` [RFC net-next PATCH 4/4] qlge: Change large rx buffer logic Ron Mercer
@ 2009-08-18 0:57 ` David Miller
2009-08-18 21:26 ` Ron Mercer
4 siblings, 1 reply; 7+ messages in thread
From: David Miller @ 2009-08-18 0:57 UTC (permalink / raw)
To: ron.mercer; +Cc: netdev
From: Ron Mercer <ron.mercer@qlogic.com>
Date: Mon, 17 Aug 2009 14:08:45 -0700
> 1) Do TX completions in send path (with cleaner timer).
You should really do them in NAPI context.
When you do them from hardware interrupt context, they all
get rescheduled into a softirq for the real SKB freeing
work anyways.
So by doing it in NAPI poll, you're avoiding some needless
overhead.
BTW, it's insanely confusing that there is a function called
qlge_msix_tx_isr() that of all things does RX work :-/
> 2) Change RSS queue count to match MSIx vector count instead
> of CPU count. Some platforms didn't offer enough vectors
> for our previous approach.
Ideally you want "max(num_msix_vectors, num_cpus)" because
if you hook up more MSIX vectors than you have cpus it's just
extra overhead and depending upon the descrepency between the
two counts it might unevenly distribute traffic work amongst
the cpus.
> 3) Change large RX buffer logic to use either multiple pages
> or chunks of pages based on MTU and system page size.
>
> Examples:
>
> 64k Pages with 1500 MTU. The RX buffers size would be
> 2048 bytes and there would be 32 per page.
>
> 4k pages with 9000 MTU. The RX buffer size would be 16k,
> or 4 pages per buffer.
This is wasteful, does the card have a mechnism by which it
can dynamically carve up pages depending upon the actual
frame size?
If anything, make sure that skb->truesize gets set to something
reasonable, or else TCP is going to reallocate SKBs when the
receive queue limits are hit.
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [RFC net-next PATCH 0/4] qlge: Performance changes for qlge.
2009-08-18 0:57 ` [RFC net-next PATCH 0/4] qlge: Performance changes for qlge David Miller
@ 2009-08-18 21:26 ` Ron Mercer
0 siblings, 0 replies; 7+ messages in thread
From: Ron Mercer @ 2009-08-18 21:26 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org
Dave,
Thanks for the quick feedback. I will re-spin per my comments below.
>
> > 1) Do TX completions in send path (with cleaner timer).
>
> You should really do them in NAPI context.
>
> When you do them from hardware interrupt context, they all
> get rescheduled into a softirq for the real SKB freeing
> work anyways.
>
> So by doing it in NAPI poll, you're avoiding some needless
> overhead.
>
> BTW, it's insanely confusing that there is a function called
> qlge_msix_tx_isr() that of all things does RX work :-/
>
I tried to do the patch series as a logical progression but might have
made it more confusing. Patch 1 moves TX completion processing to the
hardware interrupt context (as you pointed out). Patch 2 moves it from
interrupt context to the send path as many drivers do.
It wasn't my intention to do the processinging in the ISR. Sorry about
the confusion.
> > 2) Change RSS queue count to match MSIx vector count instead
> > of CPU count. Some platforms didn't offer enough vectors
> > for our previous approach.
>
> Ideally you want "max(num_msix_vectors, num_cpus)" because
> if you hook up more MSIX vectors than you have cpus it's just
> extra overhead and depending upon the descrepency between the
> two counts it might unevenly distribute traffic work amongst
> the cpus.
I think you mean "min(num_msix_vectors, num_cpus)". That is what I'm
trying to do in the patch. I will clean it up and improve comments before I
resubmit.
>
> > 3) Change large RX buffer logic to use either multiple pages
> > or chunks of pages based on MTU and system page size.
> >
> > Examples:
> >
> > 64k Pages with 1500 MTU. The RX buffers size would be
> > 2048 bytes and there would be 32 per page.
> >
> > 4k pages with 9000 MTU. The RX buffer size would be 16k,
> > or 4 pages per buffer.
>
> This is wasteful, does the card have a mechnism by which it
> can dynamically carve up pages depending upon the actual
> frame size?
>
> If anything, make sure that skb->truesize gets set to something
> reasonable, or else TCP is going to reallocate SKBs when the
> receive queue limits are hit.
^ permalink raw reply [flat|nested] 7+ messages in thread