diff options
author | Martin Habets <mhabets@solarflare.com> | 2018-05-24 12:14:00 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-05-25 21:49:37 +0300 |
commit | 0c235113b3c42197dba66baf76697359b03a5046 (patch) | |
tree | 7434564e95de1ef46a2981f233f8f5076bf2dcb2 /drivers/net/ethernet/sfc | |
parent | 7d850abd5f4edb1b1ca4b4141a4453305736f564 (diff) | |
download | linux-0c235113b3c42197dba66baf76697359b03a5046.tar.xz |
sfc: stop the TX queue before pushing new buffers
efx_enqueue_skb() can push new buffers for the xmit_more functionality.
We must stops the TX queue before this or else the TX queue does not get
restarted and we get a netdev watchdog.
In the error handling we may now need to unwind more than 1 packet, and
we may need to push the new buffers onto the partner queue.
v2: In the error leg also push this queue if xmit_more is set
Fixes: e9117e5099ea ("sfc: Firmware-Assisted TSO version 2")
Reported-by: Jarod Wilson <jarod@redhat.com>
Tested-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Martin Habets <mhabets@solarflare.com>
Acked-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/sfc')
-rw-r--r-- | drivers/net/ethernet/sfc/tx.c | 33 |
1 files changed, 25 insertions, 8 deletions
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index cece961f2e82..c3ad564ac4c0 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -435,17 +435,18 @@ static int efx_tx_map_data(struct efx_tx_queue *tx_queue, struct sk_buff *skb, } while (1); } -/* Remove buffers put into a tx_queue. None of the buffers must have - * an skb attached. +/* Remove buffers put into a tx_queue for the current packet. + * None of the buffers must have an skb attached. */ -static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue) +static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue, + unsigned int insert_count) { struct efx_tx_buffer *buffer; unsigned int bytes_compl = 0; unsigned int pkts_compl = 0; /* Work backwards until we hit the original insert pointer value */ - while (tx_queue->insert_count != tx_queue->write_count) { + while (tx_queue->insert_count != insert_count) { --tx_queue->insert_count; buffer = __efx_tx_queue_get_insert_buffer(tx_queue); efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl); @@ -504,6 +505,8 @@ static int efx_tx_tso_fallback(struct efx_tx_queue *tx_queue, */ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) { + unsigned int old_insert_count = tx_queue->insert_count; + bool xmit_more = skb->xmit_more; bool data_mapped = false; unsigned int segments; unsigned int skb_len; @@ -553,8 +556,10 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) /* Update BQL */ netdev_tx_sent_queue(tx_queue->core_txq, skb_len); + efx_tx_maybe_stop_queue(tx_queue); + /* Pass off to hardware */ - if (!skb->xmit_more || netif_xmit_stopped(tx_queue->core_txq)) { + if (!xmit_more || netif_xmit_stopped(tx_queue->core_txq)) { struct efx_tx_queue *txq2 = efx_tx_queue_partner(tx_queue); /* There could be packets left on the partner queue if those @@ -577,14 +582,26 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) tx_queue->tx_packets++; } - efx_tx_maybe_stop_queue(tx_queue); - return NETDEV_TX_OK; err: - efx_enqueue_unwind(tx_queue); + efx_enqueue_unwind(tx_queue, old_insert_count); dev_kfree_skb_any(skb); + + /* If we're not expecting another transmit and we had something to push + * on this queue or a partner queue then we need to push here to get the + * previous packets out. + */ + if (!xmit_more) { + struct efx_tx_queue *txq2 = efx_tx_queue_partner(tx_queue); + + if (txq2->xmit_more_available) + efx_nic_push_buffers(txq2); + + efx_nic_push_buffers(tx_queue); + } + return NETDEV_TX_OK; } |