From 2c61c8a787fc3382edefd8c7ad3e0f74c81e5302 Mon Sep 17 00:00:00 2001 From: Robert Stonehouse Date: Fri, 2 Mar 2012 17:20:00 +0000 Subject: sfc: Fix calculation of vf_i in map_vi_index() This was broken during refactoring to use efx_vf_size(). [bwh: Keep using efx_vf_size()] Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/siena_sriov.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/siena_sriov.c b/drivers/net/ethernet/sfc/siena_sriov.c index 80976e84eee6..9cb3b84ecae9 100644 --- a/drivers/net/ethernet/sfc/siena_sriov.c +++ b/drivers/net/ethernet/sfc/siena_sriov.c @@ -514,7 +514,7 @@ static bool map_vi_index(struct efx_nic *efx, unsigned abs_index, if (abs_index < EFX_VI_BASE) return true; - vf_i = (abs_index - EFX_VI_BASE) * efx_vf_size(efx); + vf_i = (abs_index - EFX_VI_BASE) / efx_vf_size(efx); if (vf_i >= efx->vf_init_count) return true; -- cgit From bdca71ede7356e4a5838724a0a40be63ac549641 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 24 Feb 2012 21:29:40 +0000 Subject: sfc: Remove redundant function efx_nic_has_mc() This function is now used in only one place, where it always returns true. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/nic.h | 4 ---- drivers/net/ethernet/sfc/siena.c | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index 246c4140453c..ac12f7f7f35b 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -35,10 +35,6 @@ static inline int efx_nic_rev(struct efx_nic *efx) extern u32 efx_nic_fpga_ver(struct efx_nic *efx); -static inline bool efx_nic_has_mc(struct efx_nic *efx) -{ - return efx_nic_rev(efx) >= EFX_REV_SIENA_A0; -} /* NIC has two interlinked PCI functions for the same port. */ static inline bool efx_nic_is_dual_func(struct efx_nic *efx) { diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 7bea79017a05..9f8d7cea3967 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -409,8 +409,7 @@ static void siena_remove_nic(struct efx_nic *efx) siena_reset_hw(efx, RESET_TYPE_ALL); /* Relinquish the device back to the BMC */ - if (efx_nic_has_mc(efx)) - efx_mcdi_drv_attach(efx, false, NULL); + efx_mcdi_drv_attach(efx, false, NULL); /* Tear down the private nic state */ kfree(efx->nic_data); -- cgit From 61321d92fc3d89fea0a4deb83db4acb0a3dcda07 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 25 Feb 2012 01:58:35 +0000 Subject: sfc: Update comments on efx_rx_packet_gro() The in-tree driver has never supported Driverlink. The rest of the comments are rather redundant, but we can usefully state what the requirements are on the buffer state. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/rx.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 1ba290d0c21c..763fa2fe1a38 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -449,10 +449,8 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, efx_rx_queue_channel(rx_queue)->n_rx_overlength++; } -/* Pass a received packet up through the generic GRO stack - * - * Handles driverlink veto, and passes the fragment up via - * the appropriate GRO method +/* Pass a received packet up through GRO. GRO can handle pages + * regardless of checksum state and skbs with a good checksum. */ static void efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf, @@ -461,7 +459,6 @@ static void efx_rx_packet_gro(struct efx_channel *channel, struct napi_struct *napi = &channel->napi_str; gro_result_t gro_result; - /* Pass the skb/page into the GRO engine */ if (rx_buf->flags & EFX_RX_BUF_PAGE) { struct efx_nic *efx = channel->efx; struct page *page = rx_buf->u.page; -- cgit From e7bed9c8838c09518c02ea1a2ee74569836065ea Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 28 Feb 2012 18:44:13 +0000 Subject: sfc: Remove TX completions from adaptive IRQ scoring RX and TX completions on the same event queue are generally not associated with the same flows. The inclusion of TX completions in the adaptive IRQ score is more of a source of noise rather than useful feedback. Therefore, do not include them in the score, and adjust the default threshold scores down. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/efx.c | 4 ++-- drivers/net/ethernet/sfc/nic.c | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 1908ba7ca7e6..00e13ab080e6 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -163,12 +163,12 @@ static int phy_flash_cfg; module_param(phy_flash_cfg, int, 0644); MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); -static unsigned irq_adapt_low_thresh = 10000; +static unsigned irq_adapt_low_thresh = 8000; module_param(irq_adapt_low_thresh, uint, 0644); MODULE_PARM_DESC(irq_adapt_low_thresh, "Threshold score for reducing IRQ moderation"); -static unsigned irq_adapt_high_thresh = 20000; +static unsigned irq_adapt_high_thresh = 16000; module_param(irq_adapt_high_thresh, uint, 0644); MODULE_PARM_DESC(irq_adapt_high_thresh, "Threshold score for increasing IRQ moderation"); diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 2bf4283f05fe..5da8af5e7501 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -822,7 +822,6 @@ efx_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) channel, tx_ev_q_label % EFX_TXQ_TYPES); tx_packets = ((tx_ev_desc_ptr - tx_queue->read_count) & tx_queue->ptr_mask); - channel->irq_mod_score += tx_packets; efx_xmit_done(tx_queue, tx_ev_desc_ptr); } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_WQ_FF_FULL)) { /* Rewrite the FIFO write pointer */ -- cgit From 93e5dfa59b0e26a145a8adce5c9edf50bdaef4c7 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 28 Feb 2012 20:40:53 +0000 Subject: sfc: Raise self-test timeouts IRQ latency can be ridiculously high for various reasons, so our current timeouts of 100 ms or 10 ms are too short. Change the IRQ and event tests to use polling loops starting with a delay of 1 tick and doubling that if necessary up to a maximum total delay of approximately 1 second. Raise the loopback packet RX timeout to 1 second. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/selftest.c | 65 ++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c index febe2a9e6211..dc330b9d9168 100644 --- a/drivers/net/ethernet/sfc/selftest.c +++ b/drivers/net/ethernet/sfc/selftest.c @@ -25,6 +25,16 @@ #include "selftest.h" #include "workarounds.h" +/* IRQ latency can be enormous because: + * - All IRQs may be disabled on a CPU for a *long* time by e.g. a + * slow serial console or an old IDE driver doing error recovery + * - The PREEMPT_RT patches mostly deal with this, but also allow a + * tasklet or normal task to be given higher priority than our IRQ + * threads + * Try to avoid blaming the hardware for this. + */ +#define IRQ_TIMEOUT HZ + /* * Loopback test packet structure * @@ -77,6 +87,9 @@ struct efx_loopback_state { struct efx_loopback_payload payload; }; +/* How long to wait for all the packets to arrive (in ms) */ +#define LOOPBACK_TIMEOUT_MS 1000 + /************************************************************************** * * MII, NVRAM and register tests @@ -130,6 +143,7 @@ static int efx_test_chip(struct efx_nic *efx, struct efx_self_tests *tests) static int efx_test_interrupts(struct efx_nic *efx, struct efx_self_tests *tests) { + unsigned long timeout, wait; int cpu; netif_dbg(efx, drv, efx->net_dev, "testing interrupts\n"); @@ -140,13 +154,18 @@ static int efx_test_interrupts(struct efx_nic *efx, smp_wmb(); efx_nic_generate_interrupt(efx); + timeout = jiffies + IRQ_TIMEOUT; + wait = 1; /* Wait for arrival of test interrupt. */ netif_dbg(efx, drv, efx->net_dev, "waiting for test interrupt\n"); - schedule_timeout_uninterruptible(HZ / 10); - cpu = ACCESS_ONCE(efx->last_irq_cpu); - if (cpu >= 0) - goto success; + do { + schedule_timeout_uninterruptible(wait); + cpu = ACCESS_ONCE(efx->last_irq_cpu); + if (cpu >= 0) + goto success; + wait *= 2; + } while (time_before(jiffies, timeout)); netif_err(efx, drv, efx->net_dev, "timed out waiting for interrupt\n"); return -ETIMEDOUT; @@ -165,29 +184,37 @@ static int efx_test_eventq_irq(struct efx_channel *channel, struct efx_nic *efx = channel->efx; unsigned int read_ptr; bool napi_ran, dma_seen, int_seen; + unsigned long timeout, wait; read_ptr = channel->eventq_read_ptr; channel->last_irq_cpu = -1; smp_wmb(); efx_nic_generate_test_event(channel); + timeout = jiffies + IRQ_TIMEOUT; + wait = 1; /* Wait for arrival of interrupt. NAPI processing may or may * not complete in time, but we can cope in any case. */ - msleep(10); - napi_disable(&channel->napi_str); - if (channel->eventq_read_ptr != read_ptr) { - napi_ran = true; - dma_seen = true; - int_seen = true; - } else { - napi_ran = false; - dma_seen = efx_nic_event_present(channel); - int_seen = ACCESS_ONCE(channel->last_irq_cpu) >= 0; - } - napi_enable(&channel->napi_str); - efx_nic_eventq_read_ack(channel); + do { + schedule_timeout_uninterruptible(wait); + + napi_disable(&channel->napi_str); + if (channel->eventq_read_ptr != read_ptr) { + napi_ran = true; + dma_seen = true; + int_seen = true; + } else { + napi_ran = false; + dma_seen = efx_nic_event_present(channel); + int_seen = ACCESS_ONCE(channel->last_irq_cpu) >= 0; + } + napi_enable(&channel->napi_str); + efx_nic_eventq_read_ack(channel); + + wait *= 2; + } while (!(dma_seen && int_seen) && time_before(jiffies, timeout)); tests->eventq_dma[channel->channel] = dma_seen ? 1 : -1; tests->eventq_int[channel->channel] = int_seen ? 1 : -1; @@ -516,10 +543,10 @@ efx_test_loopback(struct efx_tx_queue *tx_queue, begin_rc = efx_begin_loopback(tx_queue); /* This will normally complete very quickly, but be - * prepared to wait up to 100 ms. */ + * prepared to wait much longer. */ msleep(1); if (!efx_poll_loopback(efx)) { - msleep(100); + msleep(LOOPBACK_TIMEOUT_MS); efx_poll_loopback(efx); } -- cgit From ed74f48087d1b1cf7cf534dad03bf1584eee783d Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 28 Feb 2012 20:40:54 +0000 Subject: sfc: Test all event queues in parallel In case all event queues are broken for some reason, this means it will only take about a second to check them all, rather than up to 32 seconds. This may also speed up testing in the successful case. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/selftest.c | 114 ++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c index dc330b9d9168..e4b4c8a3abc6 100644 --- a/drivers/net/ethernet/sfc/selftest.c +++ b/drivers/net/ethernet/sfc/selftest.c @@ -178,69 +178,88 @@ static int efx_test_interrupts(struct efx_nic *efx, } /* Test generation and receipt of interrupting events */ -static int efx_test_eventq_irq(struct efx_channel *channel, +static int efx_test_eventq_irq(struct efx_nic *efx, struct efx_self_tests *tests) { - struct efx_nic *efx = channel->efx; - unsigned int read_ptr; - bool napi_ran, dma_seen, int_seen; + struct efx_channel *channel; + unsigned int read_ptr[EFX_MAX_CHANNELS]; + unsigned long napi_ran = 0, dma_pend = 0, int_pend = 0; unsigned long timeout, wait; - read_ptr = channel->eventq_read_ptr; - channel->last_irq_cpu = -1; - smp_wmb(); + BUILD_BUG_ON(EFX_MAX_CHANNELS > BITS_PER_LONG); + + efx_for_each_channel(channel, efx) { + read_ptr[channel->channel] = channel->eventq_read_ptr; + set_bit(channel->channel, &dma_pend); + set_bit(channel->channel, &int_pend); + channel->last_irq_cpu = -1; + smp_wmb(); + efx_nic_generate_test_event(channel); + } - efx_nic_generate_test_event(channel); timeout = jiffies + IRQ_TIMEOUT; wait = 1; - /* Wait for arrival of interrupt. NAPI processing may or may + /* Wait for arrival of interrupts. NAPI processing may or may * not complete in time, but we can cope in any case. */ do { schedule_timeout_uninterruptible(wait); - napi_disable(&channel->napi_str); - if (channel->eventq_read_ptr != read_ptr) { - napi_ran = true; - dma_seen = true; - int_seen = true; - } else { - napi_ran = false; - dma_seen = efx_nic_event_present(channel); - int_seen = ACCESS_ONCE(channel->last_irq_cpu) >= 0; + efx_for_each_channel(channel, efx) { + napi_disable(&channel->napi_str); + if (channel->eventq_read_ptr != + read_ptr[channel->channel]) { + set_bit(channel->channel, &napi_ran); + clear_bit(channel->channel, &dma_pend); + clear_bit(channel->channel, &int_pend); + } else { + if (efx_nic_event_present(channel)) + clear_bit(channel->channel, &dma_pend); + if (ACCESS_ONCE(channel->last_irq_cpu) >= 0) + clear_bit(channel->channel, &int_pend); + } + napi_enable(&channel->napi_str); + efx_nic_eventq_read_ack(channel); } - napi_enable(&channel->napi_str); - efx_nic_eventq_read_ack(channel); wait *= 2; - } while (!(dma_seen && int_seen) && time_before(jiffies, timeout)); - - tests->eventq_dma[channel->channel] = dma_seen ? 1 : -1; - tests->eventq_int[channel->channel] = int_seen ? 1 : -1; + } while ((dma_pend || int_pend) && time_before(jiffies, timeout)); - if (dma_seen && int_seen) { - netif_dbg(efx, drv, efx->net_dev, - "channel %d event queue passed (with%s NAPI)\n", - channel->channel, napi_ran ? "" : "out"); - return 0; - } else { - /* Report failure and whether either interrupt or DMA worked */ - netif_err(efx, drv, efx->net_dev, - "channel %d timed out waiting for event queue\n", - channel->channel); - if (int_seen) - netif_err(efx, drv, efx->net_dev, - "channel %d saw interrupt " - "during event queue test\n", - channel->channel); - if (dma_seen) + efx_for_each_channel(channel, efx) { + bool dma_seen = !test_bit(channel->channel, &dma_pend); + bool int_seen = !test_bit(channel->channel, &int_pend); + + tests->eventq_dma[channel->channel] = dma_seen ? 1 : -1; + tests->eventq_int[channel->channel] = int_seen ? 1 : -1; + + if (dma_seen && int_seen) { + netif_dbg(efx, drv, efx->net_dev, + "channel %d event queue passed (with%s NAPI)\n", + channel->channel, + test_bit(channel->channel, &napi_ran) ? + "" : "out"); + } else { + /* Report failure and whether either interrupt or DMA + * worked + */ netif_err(efx, drv, efx->net_dev, - "channel %d event was generated, but " - "failed to trigger an interrupt\n", + "channel %d timed out waiting for event queue\n", channel->channel); - return -ETIMEDOUT; + if (int_seen) + netif_err(efx, drv, efx->net_dev, + "channel %d saw interrupt " + "during event queue test\n", + channel->channel); + if (dma_seen) + netif_err(efx, drv, efx->net_dev, + "channel %d event was generated, but " + "failed to trigger an interrupt\n", + channel->channel); + } } + + return (dma_pend || int_pend) ? -ETIMEDOUT : 0; } static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests, @@ -687,7 +706,6 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, enum efx_loopback_mode loopback_mode = efx->loopback_mode; int phy_mode = efx->phy_mode; enum reset_type reset_method = RESET_TYPE_INVISIBLE; - struct efx_channel *channel; int rc_test = 0, rc_reset = 0, rc; /* Online (i.e. non-disruptive) testing @@ -705,11 +723,9 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, if (rc && !rc_test) rc_test = rc; - efx_for_each_channel(channel, efx) { - rc = efx_test_eventq_irq(channel, tests); - if (rc && !rc_test) - rc_test = rc; - } + rc = efx_test_eventq_irq(efx, tests); + if (rc && !rc_test) + rc_test = rc; if (rc_test) return rc_test; -- cgit From eee6f6a9e0c83811de77a137989d4a3289e297cc Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 28 Feb 2012 23:37:35 +0000 Subject: sfc: Encapsulate access to efx_{channel,nic}::last_irq_cpu in self-test Cleanup in preparation for doing an event test on ifup. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/nic.c | 8 ++++++-- drivers/net/ethernet/sfc/nic.h | 13 +++++++++++-- drivers/net/ethernet/sfc/selftest.c | 14 ++++---------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 5da8af5e7501..4c47b7569145 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -1332,8 +1332,10 @@ void efx_nic_remove_eventq(struct efx_channel *channel) } -void efx_nic_generate_test_event(struct efx_channel *channel) +void efx_nic_event_test_start(struct efx_channel *channel) { + channel->last_irq_cpu = -1; + smp_wmb(); efx_magic_event(channel, EFX_CHANNEL_MAGIC_TEST(channel)); } @@ -1382,8 +1384,10 @@ void efx_nic_disable_interrupts(struct efx_nic *efx) * Interrupt must already have been enabled, otherwise nasty things * may happen. */ -void efx_nic_generate_interrupt(struct efx_nic *efx) +void efx_nic_irq_test_start(struct efx_nic *efx) { + efx->last_irq_cpu = -1; + smp_wmb(); efx_nic_interrupts(efx, true, true); } diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index ac12f7f7f35b..e0e8596f4d10 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -301,14 +301,23 @@ extern void falcon_update_stats_xmac(struct efx_nic *efx); /* Interrupts and test events */ extern int efx_nic_init_interrupt(struct efx_nic *efx); extern void efx_nic_enable_interrupts(struct efx_nic *efx); -extern void efx_nic_generate_test_event(struct efx_channel *channel); -extern void efx_nic_generate_interrupt(struct efx_nic *efx); +extern void efx_nic_event_test_start(struct efx_channel *channel); +extern void efx_nic_irq_test_start(struct efx_nic *efx); extern void efx_nic_disable_interrupts(struct efx_nic *efx); extern void efx_nic_fini_interrupt(struct efx_nic *efx); extern irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx); extern irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id); extern void falcon_irq_ack_a1(struct efx_nic *efx); +static inline int efx_nic_event_test_irq_cpu(struct efx_channel *channel) +{ + return ACCESS_ONCE(channel->last_irq_cpu); +} +static inline int efx_nic_irq_test_irq_cpu(struct efx_nic *efx) +{ + return ACCESS_ONCE(efx->last_irq_cpu); +} + /* Global Resources */ extern int efx_nic_flush_queues(struct efx_nic *efx); extern void falcon_start_nic_stats(struct efx_nic *efx); diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c index e4b4c8a3abc6..aa4ab53cac57 100644 --- a/drivers/net/ethernet/sfc/selftest.c +++ b/drivers/net/ethernet/sfc/selftest.c @@ -149,11 +149,7 @@ static int efx_test_interrupts(struct efx_nic *efx, netif_dbg(efx, drv, efx->net_dev, "testing interrupts\n"); tests->interrupt = -1; - /* Reset interrupt flag */ - efx->last_irq_cpu = -1; - smp_wmb(); - - efx_nic_generate_interrupt(efx); + efx_nic_irq_test_start(efx); timeout = jiffies + IRQ_TIMEOUT; wait = 1; @@ -161,7 +157,7 @@ static int efx_test_interrupts(struct efx_nic *efx, netif_dbg(efx, drv, efx->net_dev, "waiting for test interrupt\n"); do { schedule_timeout_uninterruptible(wait); - cpu = ACCESS_ONCE(efx->last_irq_cpu); + cpu = efx_nic_irq_test_irq_cpu(efx); if (cpu >= 0) goto success; wait *= 2; @@ -192,9 +188,7 @@ static int efx_test_eventq_irq(struct efx_nic *efx, read_ptr[channel->channel] = channel->eventq_read_ptr; set_bit(channel->channel, &dma_pend); set_bit(channel->channel, &int_pend); - channel->last_irq_cpu = -1; - smp_wmb(); - efx_nic_generate_test_event(channel); + efx_nic_event_test_start(channel); } timeout = jiffies + IRQ_TIMEOUT; @@ -216,7 +210,7 @@ static int efx_test_eventq_irq(struct efx_nic *efx, } else { if (efx_nic_event_present(channel)) clear_bit(channel->channel, &dma_pend); - if (ACCESS_ONCE(channel->last_irq_cpu) >= 0) + if (efx_nic_event_test_irq_cpu(channel) >= 0) clear_bit(channel->channel, &int_pend); } napi_enable(&channel->napi_str); -- cgit From dd40781e3a4e9d3177a548c389232ee9496dae8e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 28 Feb 2012 23:40:21 +0000 Subject: sfc: Run event/IRQ self-test asynchronously when interface is brought up Generate a test event on each event queue whenever the interface is brought up, then after 1 second check that we have either handled a test event or handled another IRQ for each event queue. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/efx.c | 6 +++++- drivers/net/ethernet/sfc/efx.h | 2 +- drivers/net/ethernet/sfc/net_driver.h | 6 ++++-- drivers/net/ethernet/sfc/nic.c | 4 ++-- drivers/net/ethernet/sfc/nic.h | 2 +- drivers/net/ethernet/sfc/selftest.c | 35 +++++++++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/selftest.h | 3 +++ 7 files changed, 51 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 00e13ab080e6..7683e53fda1d 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -25,6 +25,7 @@ #include "net_driver.h" #include "efx.h" #include "nic.h" +#include "selftest.h" #include "mcdi.h" #include "workarounds.h" @@ -1564,8 +1565,9 @@ static void efx_start_all(struct efx_nic *efx) * since we're holding the rtnl_lock at this point. */ static void efx_flush_all(struct efx_nic *efx) { - /* Make sure the hardware monitor is stopped */ + /* Make sure the hardware monitor and event self-test are stopped */ cancel_delayed_work_sync(&efx->monitor_work); + efx_selftest_async_cancel(efx); /* Stop scheduled port reconfigurations */ cancel_work_sync(&efx->mac_work); } @@ -1825,6 +1827,7 @@ static int efx_net_open(struct net_device *net_dev) efx_link_status_changed(efx); efx_start_all(efx); + efx_selftest_async_start(efx); return 0; } @@ -2375,6 +2378,7 @@ static int efx_init_struct(struct efx_nic *efx, const struct efx_nic_type *type, #endif INIT_WORK(&efx->reset_work, efx_reset_work); INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor); + INIT_DELAYED_WORK(&efx->selftest_work, efx_selftest_async_work); efx->pci_dev = pci_dev; efx->msg_enable = debug; efx->state = STATE_INIT; diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index 4debfe07fb88..be8f9158a714 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -148,7 +148,7 @@ static inline void efx_schedule_channel(struct efx_channel *channel) static inline void efx_schedule_channel_irq(struct efx_channel *channel) { - channel->last_irq_cpu = raw_smp_processor_id(); + channel->event_test_cpu = raw_smp_processor_id(); efx_schedule_channel(channel); } diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 0b95505e8968..27fe85fd4ba3 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -325,7 +325,7 @@ enum efx_rx_alloc_method { * @eventq_mask: Event queue pointer mask * @eventq_read_ptr: Event queue read pointer * @last_eventq_read_ptr: Last event queue read pointer value. - * @last_irq_cpu: Last CPU to handle interrupt for this channel + * @event_test_cpu: Last CPU to handle interrupt or test event for this channel * @irq_count: Number of IRQs since last adaptive moderation decision * @irq_mod_score: IRQ moderation score * @rx_alloc_level: Watermark based heuristic counter for pushing descriptors @@ -356,8 +356,8 @@ struct efx_channel { unsigned int eventq_mask; unsigned int eventq_read_ptr; unsigned int last_eventq_read_ptr; + int event_test_cpu; - int last_irq_cpu; unsigned int irq_count; unsigned int irq_mod_score; #ifdef CONFIG_RFS_ACCEL @@ -678,6 +678,7 @@ struct vfdi_status; * @irq_status: Interrupt status buffer * @irq_zero_count: Number of legacy IRQs seen with queue flags == 0 * @irq_level: IRQ level/index for IRQs not triggered by an event queue + * @selftest_work: Work item for asynchronous self-test * @mtd_list: List of MTDs attached to the NIC * @nic_data: Hardware dependent state * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode, @@ -791,6 +792,7 @@ struct efx_nic { struct efx_buffer irq_status; unsigned irq_zero_count; unsigned irq_level; + struct delayed_work selftest_work; #ifdef CONFIG_SFC_MTD struct list_head mtd_list; diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 4c47b7569145..4a9a5beec8fc 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -1083,7 +1083,7 @@ efx_handle_generated_event(struct efx_channel *channel, efx_qword_t *event) code = _EFX_CHANNEL_MAGIC_CODE(magic); if (magic == EFX_CHANNEL_MAGIC_TEST(channel)) { - /* ignore */ + channel->event_test_cpu = raw_smp_processor_id(); } else if (rx_queue && magic == EFX_CHANNEL_MAGIC_FILL(rx_queue)) { /* The queue must be empty, so we won't receive any rx * events, so efx_process_channel() won't refill the @@ -1334,7 +1334,7 @@ void efx_nic_remove_eventq(struct efx_channel *channel) void efx_nic_event_test_start(struct efx_channel *channel) { - channel->last_irq_cpu = -1; + channel->event_test_cpu = -1; smp_wmb(); efx_magic_event(channel, EFX_CHANNEL_MAGIC_TEST(channel)); } diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index e0e8596f4d10..f46e2cea8ca9 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -311,7 +311,7 @@ extern void falcon_irq_ack_a1(struct efx_nic *efx); static inline int efx_nic_event_test_irq_cpu(struct efx_channel *channel) { - return ACCESS_ONCE(channel->last_irq_cpu); + return ACCESS_ONCE(channel->event_test_cpu); } static inline int efx_nic_irq_test_irq_cpu(struct efx_nic *efx) { diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c index aa4ab53cac57..de4c0069f5b2 100644 --- a/drivers/net/ethernet/sfc/selftest.c +++ b/drivers/net/ethernet/sfc/selftest.c @@ -702,6 +702,8 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, enum reset_type reset_method = RESET_TYPE_INVISIBLE; int rc_test = 0, rc_reset = 0, rc; + efx_selftest_async_cancel(efx); + /* Online (i.e. non-disruptive) testing * This checks interrupt generation, event delivery and PHY presence. */ @@ -794,3 +796,36 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, return rc_test; } +void efx_selftest_async_start(struct efx_nic *efx) +{ + struct efx_channel *channel; + + efx_for_each_channel(channel, efx) + efx_nic_event_test_start(channel); + schedule_delayed_work(&efx->selftest_work, IRQ_TIMEOUT); +} + +void efx_selftest_async_cancel(struct efx_nic *efx) +{ + cancel_delayed_work_sync(&efx->selftest_work); +} + +void efx_selftest_async_work(struct work_struct *data) +{ + struct efx_nic *efx = container_of(data, struct efx_nic, + selftest_work.work); + struct efx_channel *channel; + int cpu; + + efx_for_each_channel(channel, efx) { + cpu = efx_nic_event_test_irq_cpu(channel); + if (cpu < 0) + netif_err(efx, ifup, efx->net_dev, + "channel %d failed to trigger an interrupt\n", + channel->channel); + else + netif_dbg(efx, ifup, efx->net_dev, + "channel %d triggered interrupt on CPU %d\n", + channel->channel, cpu); + } +} diff --git a/drivers/net/ethernet/sfc/selftest.h b/drivers/net/ethernet/sfc/selftest.h index 87abe2a53846..aed24b736059 100644 --- a/drivers/net/ethernet/sfc/selftest.h +++ b/drivers/net/ethernet/sfc/selftest.h @@ -48,5 +48,8 @@ extern void efx_loopback_rx_packet(struct efx_nic *efx, extern int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, unsigned flags); +extern void efx_selftest_async_start(struct efx_nic *efx); +extern void efx_selftest_async_cancel(struct efx_nic *efx); +extern void efx_selftest_async_work(struct work_struct *data); #endif /* EFX_SELFTEST_H */ -- cgit From 58f7e57d11b99575ddb0a3ee9baf7551f10bc3dd Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 29 Feb 2012 00:24:10 +0000 Subject: sfc: Remove efx_channel::last_eventq_read_ptr This member has never been used in a production version of the driver. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/net_driver.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 27fe85fd4ba3..f0385e1fb2d8 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -324,7 +324,6 @@ enum efx_rx_alloc_method { * @eventq: Event queue buffer * @eventq_mask: Event queue pointer mask * @eventq_read_ptr: Event queue read pointer - * @last_eventq_read_ptr: Last event queue read pointer value. * @event_test_cpu: Last CPU to handle interrupt or test event for this channel * @irq_count: Number of IRQs since last adaptive moderation decision * @irq_mod_score: IRQ moderation score @@ -355,7 +354,6 @@ struct efx_channel { struct efx_special_buffer eventq; unsigned int eventq_mask; unsigned int eventq_read_ptr; - unsigned int last_eventq_read_ptr; int event_test_cpu; unsigned int irq_count; -- cgit From 460eeaa03c5c0af32a388337a6e1f0f1acd043ce Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 5 Mar 2012 15:35:39 +0000 Subject: sfc: Log the part number on probe During probe of each port, read and log the part number from VPD. Remove the Falcon-specific board name lookup. Initial version by Stuart Hodgson . Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/efx.c | 53 ++++++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/falcon_boards.c | 13 -------- drivers/net/ethernet/sfc/nic.h | 4 --- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 7683e53fda1d..3cbfbffe3f00 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -2497,6 +2497,57 @@ static void efx_pci_remove(struct pci_dev *pci_dev) free_netdev(efx->net_dev); }; +/* NIC VPD information + * Called during probe to display the part number of the + * installed NIC. VPD is potentially very large but this should + * always appear within the first 512 bytes. + */ +#define SFC_VPD_LEN 512 +static void efx_print_product_vpd(struct efx_nic *efx) +{ + struct pci_dev *dev = efx->pci_dev; + char vpd_data[SFC_VPD_LEN]; + ssize_t vpd_size; + int i, j; + + /* Get the vpd data from the device */ + vpd_size = pci_read_vpd(dev, 0, sizeof(vpd_data), vpd_data); + if (vpd_size <= 0) { + netif_err(efx, drv, efx->net_dev, "Unable to read VPD\n"); + return; + } + + /* Get the Read only section */ + i = pci_vpd_find_tag(vpd_data, 0, vpd_size, PCI_VPD_LRDT_RO_DATA); + if (i < 0) { + netif_err(efx, drv, efx->net_dev, "VPD Read-only not found\n"); + return; + } + + j = pci_vpd_lrdt_size(&vpd_data[i]); + i += PCI_VPD_LRDT_TAG_SIZE; + if (i + j > vpd_size) + j = vpd_size - i; + + /* Get the Part number */ + i = pci_vpd_find_info_keyword(vpd_data, i, j, "PN"); + if (i < 0) { + netif_err(efx, drv, efx->net_dev, "Part number not found\n"); + return; + } + + j = pci_vpd_info_field_size(&vpd_data[i]); + i += PCI_VPD_INFO_FLD_HDR_SIZE; + if (i + j > vpd_size) { + netif_err(efx, drv, efx->net_dev, "Incomplete part number\n"); + return; + } + + netif_info(efx, drv, efx->net_dev, + "Part Number : %.*s\n", j, &vpd_data[i]); +} + + /* Main body of NIC initialisation * This is called at module load (or hotplug insertion, theoretically). */ @@ -2586,6 +2637,8 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, netif_info(efx, probe, efx->net_dev, "Solarflare NIC detected\n"); + efx_print_product_vpd(efx); + /* Set up basic I/O (BAR mappings etc) */ rc = efx_init_io(efx); if (rc) diff --git a/drivers/net/ethernet/sfc/falcon_boards.c b/drivers/net/ethernet/sfc/falcon_boards.c index 2084cc6ede52..8687a6c3db0d 100644 --- a/drivers/net/ethernet/sfc/falcon_boards.c +++ b/drivers/net/ethernet/sfc/falcon_boards.c @@ -709,8 +709,6 @@ static int sfe4003_init(struct efx_nic *efx) static const struct falcon_board_type board_types[] = { { .id = FALCON_BOARD_SFE4001, - .ref_model = "SFE4001", - .gen_type = "10GBASE-T adapter", .init = sfe4001_init, .init_phy = efx_port_dummy_op_void, .fini = sfe4001_fini, @@ -719,8 +717,6 @@ static const struct falcon_board_type board_types[] = { }, { .id = FALCON_BOARD_SFE4002, - .ref_model = "SFE4002", - .gen_type = "XFP adapter", .init = sfe4002_init, .init_phy = sfe4002_init_phy, .fini = efx_fini_lm87, @@ -729,8 +725,6 @@ static const struct falcon_board_type board_types[] = { }, { .id = FALCON_BOARD_SFE4003, - .ref_model = "SFE4003", - .gen_type = "10GBASE-CX4 adapter", .init = sfe4003_init, .init_phy = sfe4003_init_phy, .fini = efx_fini_lm87, @@ -739,8 +733,6 @@ static const struct falcon_board_type board_types[] = { }, { .id = FALCON_BOARD_SFN4112F, - .ref_model = "SFN4112F", - .gen_type = "SFP+ adapter", .init = sfn4112f_init, .init_phy = sfn4112f_init_phy, .fini = efx_fini_lm87, @@ -763,11 +755,6 @@ int falcon_probe_board(struct efx_nic *efx, u16 revision_info) board->type = &board_types[i]; if (board->type) { - netif_info(efx, probe, efx->net_dev, "board is %s rev %c%d\n", - (efx->pci_dev->subsystem_vendor == - PCI_VENDOR_ID_SOLARFLARE) - ? board->type->ref_model : board->type->gen_type, - 'A' + board->major, board->minor); return 0; } else { netif_err(efx, probe, efx->net_dev, "unknown board type %d\n", diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index f46e2cea8ca9..f48ccf6bb3b9 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -69,8 +69,6 @@ enum { /** * struct falcon_board_type - board operations and type information * @id: Board type id, as found in NVRAM - * @ref_model: Model number of Solarflare reference design - * @gen_type: Generic board type description * @init: Allocate resources and initialise peripheral hardware * @init_phy: Do board-specific PHY initialisation * @fini: Shut down hardware and free resources @@ -79,8 +77,6 @@ enum { */ struct falcon_board_type { u8 id; - const char *ref_model; - const char *gen_type; int (*init) (struct efx_nic *nic); void (*init_phy) (struct efx_nic *efx); void (*fini) (struct efx_nic *nic); -- cgit