aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2011-05-23 12:18:45 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-28 12:06:00 -0700
commit442933f2b6a4c0e1d4a3b216c55c720a01c032be (patch)
tree16b635eac56c1bfead31ab1df1d61d4f4e5ae8cb
parent886033e132d6d83d6e7738e3edcd1598a7b66cf0 (diff)
sfc: Fix two causes of flush failure
[ Upstream commits a606f4325dca6950996abbae452d33f2af095f39, d5e8cc6c946e0857826dcfbb3585068858445bfe, 525d9e824018cd7cc8d8d44832ddcd363abfe6e1 ] The TX DMA engine issues upstream read requests when there is room in the TX FIFO for the completion. However, the fetches for the rest of the packet might be delayed by any back pressure. Since a flush must wait for an EOP, the entire flush may be delayed by back pressure. Mitigate this by disabling flow control before the flushes are started. Since PF and VF flushes run in parallel introduce fc_disable, a reference count of the number of flushes outstanding. The same principle could be applied to Falcon, but that would bring with it its own testing. We sometimes hit a "failed to flush" timeout on some TX queues, but the flushes have completed and the flush completion events seem to go missing. In this case, we can check the TX_DESC_PTR_TBL register and drain the queues if the flushes had finished. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> [bwh: Backported to 3.0: - Call efx_nic_type::finish_flush() on both success and failure paths - Check the TX_DESC_PTR_TBL registers in the polling loop - Declare efx_mcdi_set_mac() extern] Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/net/sfc/falcon.c2
-rw-r--r--drivers/net/sfc/mcdi.h1
-rw-r--r--drivers/net/sfc/mcdi_mac.c4
-rw-r--r--drivers/net/sfc/net_driver.h6
-rw-r--r--drivers/net/sfc/nic.c21
-rw-r--r--drivers/net/sfc/nic.h2
-rw-r--r--drivers/net/sfc/siena.c15
7 files changed, 46 insertions, 5 deletions
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 60176e873d62..19b996b33d42 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -1714,6 +1714,7 @@ const struct efx_nic_type falcon_a1_nic_type = {
.remove_port = falcon_remove_port,
.handle_global_event = falcon_handle_global_event,
.prepare_flush = falcon_prepare_flush,
+ .finish_flush = efx_port_dummy_op_void,
.update_stats = falcon_update_nic_stats,
.start_stats = falcon_start_nic_stats,
.stop_stats = falcon_stop_nic_stats,
@@ -1755,6 +1756,7 @@ const struct efx_nic_type falcon_b0_nic_type = {
.remove_port = falcon_remove_port,
.handle_global_event = falcon_handle_global_event,
.prepare_flush = falcon_prepare_flush,
+ .finish_flush = efx_port_dummy_op_void,
.update_stats = falcon_update_nic_stats,
.start_stats = falcon_start_nic_stats,
.stop_stats = falcon_stop_nic_stats,
diff --git a/drivers/net/sfc/mcdi.h b/drivers/net/sfc/mcdi.h
index aced2a7856fc..b61eea046160 100644
--- a/drivers/net/sfc/mcdi.h
+++ b/drivers/net/sfc/mcdi.h
@@ -126,5 +126,6 @@ extern int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx,
extern int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out);
extern int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id);
extern int efx_mcdi_wol_filter_reset(struct efx_nic *efx);
+extern int efx_mcdi_set_mac(struct efx_nic *efx);
#endif /* EFX_MCDI_H */
diff --git a/drivers/net/sfc/mcdi_mac.c b/drivers/net/sfc/mcdi_mac.c
index 50c20777a564..da269d7076ce 100644
--- a/drivers/net/sfc/mcdi_mac.c
+++ b/drivers/net/sfc/mcdi_mac.c
@@ -13,7 +13,7 @@
#include "mcdi.h"
#include "mcdi_pcol.h"
-static int efx_mcdi_set_mac(struct efx_nic *efx)
+int efx_mcdi_set_mac(struct efx_nic *efx)
{
u32 reject, fcntl;
u8 cmdbytes[MC_CMD_SET_MAC_IN_LEN];
@@ -45,6 +45,8 @@ static int efx_mcdi_set_mac(struct efx_nic *efx)
}
if (efx->wanted_fc & EFX_FC_AUTO)
fcntl = MC_CMD_FCNTL_AUTO;
+ if (efx->fc_disable)
+ fcntl = MC_CMD_FCNTL_OFF;
MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_FCNTL, fcntl);
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index e8d5f03a89fe..47aff8ec7205 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -690,6 +690,9 @@ struct efx_filter_state;
* @promiscuous: Promiscuous flag. Protected by netif_tx_lock.
* @multicast_hash: Multicast hash table
* @wanted_fc: Wanted flow control flags
+ * @fc_disable: When non-zero flow control is disabled. Typically used to
+ * ensure that network back pressure doesn't delay dma queue flushes.
+ * Serialised by the rtnl lock.
* @mac_work: Work item for changing MAC promiscuity and multicast hash
* @loopback_mode: Loopback status
* @loopback_modes: Supported loopback mode bitmask
@@ -783,6 +786,7 @@ struct efx_nic {
bool promiscuous;
union efx_multicast_hash multicast_hash;
u8 wanted_fc;
+ unsigned fc_disable;
atomic_t rx_reset;
enum efx_loopback_mode loopback_mode;
@@ -834,6 +838,7 @@ static inline unsigned int efx_port_num(struct efx_nic *efx)
* @remove_port: Free resources allocated by probe_port()
* @handle_global_event: Handle a "global" event (may be %NULL)
* @prepare_flush: Prepare the hardware for flushing the DMA queues
+ * @finish_flush: Clean up after flushing the DMA queues
* @update_stats: Update statistics not provided by event handling
* @start_stats: Start the regular fetching of statistics
* @stop_stats: Stop the regular fetching of statistics
@@ -879,6 +884,7 @@ struct efx_nic_type {
void (*remove_port)(struct efx_nic *efx);
bool (*handle_global_event)(struct efx_channel *channel, efx_qword_t *);
void (*prepare_flush)(struct efx_nic *efx);
+ void (*finish_flush)(struct efx_nic *efx);
void (*update_stats)(struct efx_nic *efx);
void (*start_stats)(struct efx_nic *efx);
void (*stop_stats)(struct efx_nic *efx);
diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c
index 5ac9fa2cd3bc..854df4e7d55e 100644
--- a/drivers/net/sfc/nic.c
+++ b/drivers/net/sfc/nic.c
@@ -1260,13 +1260,27 @@ int efx_nic_flush_queues(struct efx_nic *efx)
}
efx_for_each_possible_channel_tx_queue(tx_queue, channel) {
if (tx_queue->initialised &&
- tx_queue->flushed != FLUSH_DONE)
- ++tx_pending;
+ tx_queue->flushed != FLUSH_DONE) {
+ efx_oword_t txd_ptr_tbl;
+
+ efx_reado_table(efx, &txd_ptr_tbl,
+ FR_BZ_TX_DESC_PTR_TBL,
+ tx_queue->queue);
+ if (EFX_OWORD_FIELD(txd_ptr_tbl,
+ FRF_AZ_TX_DESCQ_FLUSH) ||
+ EFX_OWORD_FIELD(txd_ptr_tbl,
+ FRF_AZ_TX_DESCQ_EN))
+ ++tx_pending;
+ else
+ tx_queue->flushed = FLUSH_DONE;
+ }
}
}
- if (rx_pending == 0 && tx_pending == 0)
+ if (rx_pending == 0 && tx_pending == 0) {
+ efx->type->finish_flush(efx);
return 0;
+ }
msleep(EFX_FLUSH_INTERVAL);
efx_poll_flush_events(efx);
@@ -1292,6 +1306,7 @@ int efx_nic_flush_queues(struct efx_nic *efx)
}
}
+ efx->type->finish_flush(efx);
return -ETIMEDOUT;
}
diff --git a/drivers/net/sfc/nic.h b/drivers/net/sfc/nic.h
index d2405ceb2539..8a2c4f5aa29a 100644
--- a/drivers/net/sfc/nic.h
+++ b/drivers/net/sfc/nic.h
@@ -211,6 +211,8 @@ extern void falcon_irq_ack_a1(struct efx_nic *efx);
/* Global Resources */
extern int efx_nic_flush_queues(struct efx_nic *efx);
+extern void siena_prepare_flush(struct efx_nic *efx);
+extern void siena_finish_flush(struct efx_nic *efx);
extern void falcon_start_nic_stats(struct efx_nic *efx);
extern void falcon_stop_nic_stats(struct efx_nic *efx);
extern void falcon_setup_xaui(struct efx_nic *efx);
diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c
index 407f50bf2fcb..062494a5097a 100644
--- a/drivers/net/sfc/siena.c
+++ b/drivers/net/sfc/siena.c
@@ -135,6 +135,18 @@ static void siena_remove_port(struct efx_nic *efx)
efx_nic_free_buffer(efx, &efx->stats_buffer);
}
+void siena_prepare_flush(struct efx_nic *efx)
+{
+ if (efx->fc_disable++ == 0)
+ efx_mcdi_set_mac(efx);
+}
+
+void siena_finish_flush(struct efx_nic *efx)
+{
+ if (--efx->fc_disable == 0)
+ efx_mcdi_set_mac(efx);
+}
+
static const struct efx_nic_register_test siena_register_tests[] = {
{ FR_AZ_ADR_REGION,
EFX_OWORD32(0x0003FFFF, 0x0003FFFF, 0x0003FFFF, 0x0003FFFF) },
@@ -590,7 +602,8 @@ const struct efx_nic_type siena_a0_nic_type = {
.reset = siena_reset_hw,
.probe_port = siena_probe_port,
.remove_port = siena_remove_port,
- .prepare_flush = efx_port_dummy_op_void,
+ .prepare_flush = siena_prepare_flush,
+ .finish_flush = siena_finish_flush,
.update_stats = siena_update_nic_stats,
.start_stats = siena_start_nic_stats,
.stop_stats = siena_stop_nic_stats,