From: Chiqijun chiqijun@huawei.com
driver inclusion category: bugfix bugzilla: 4472
-----------------------------------------------------------------------
VF does not switch interrupts on the virtual machine. If the hardware sends the entire tx queue packets between tx_poll processed all the packets and calling napi_complete, it will cause the hardware to no longer report the interrupt, napi will not be called again, and driver unable free up tx resources. Eventually, the queue is full and packets cannot be sent.
By calling tx_poll again before napi_complete, to ensure that the queue resources can be released in time.
When clearing queue resources during ifconfig down processing, determine whether the packet has been sent more accurately by judging hardware CI and software PI.
Signed-off-by: Chiqijun chiqijun@huawei.com Reviewed-by: Zengweiliang zengweiliang.zengweiliang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/net/ethernet/huawei/hinic/hinic_main.c | 3 ++- drivers/net/ethernet/huawei/hinic/hinic_tx.c | 26 +++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 910fed9..f367dfb 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -468,7 +468,8 @@ static int hinic_poll(struct napi_struct *napi, int budget)
set_bit(HINIC_RESEND_ON, &irq_cfg->intr_flag); rx_pkts += hinic_rx_poll(irq_cfg->rxq, budget - rx_pkts); - if (rx_pkts >= budget) { + tx_pkts += hinic_tx_poll(irq_cfg->txq, budget - tx_pkts); + if (rx_pkts >= budget || tx_pkts >= budget) { clear_bit(HINIC_RESEND_ON, &irq_cfg->intr_flag); return budget; } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index c8492ae..3d9d9d0 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -39,6 +39,7 @@ #include "hinic_nic_dev.h" #include "hinic_qp.h" #include "hinic_tx.h" +#include "hinic_dbg.h"
#define MIN_SKB_LEN 32 #define MAX_PAYLOAD_OFFSET 221 @@ -1291,17 +1292,25 @@ void hinic_free_txqs(struct net_device *netdev) /* should stop transmit any packets before calling this function */ #define HINIC_FLUSH_QUEUE_TIMEOUT 1000
+static bool hinic_get_hw_handle_status(void *hwdev, u16 q_id) +{ + u16 sw_pi = 0, hw_ci = 0; + + sw_pi = hinic_dbg_get_sq_pi(hwdev, q_id); + hw_ci = hinic_get_sq_hw_ci(hwdev, q_id); + + return sw_pi == hw_ci; +} + int hinic_stop_sq(struct hinic_txq *txq) { struct hinic_nic_dev *nic_dev = netdev_priv(txq->netdev); unsigned long timeout; - int free_wqebbs, err; + int err;
timeout = msecs_to_jiffies(HINIC_FLUSH_QUEUE_TIMEOUT) + jiffies; do { - free_wqebbs = hinic_get_sq_free_wqebbs(nic_dev->hwdev, - txq->q_id) + 1; - if (free_wqebbs == txq->q_depth) + if (hinic_get_hw_handle_status(nic_dev->hwdev, txq->q_id)) return 0;
usleep_range(900, 1000); @@ -1310,9 +1319,7 @@ int hinic_stop_sq(struct hinic_txq *txq) /* force hardware to drop packets */ timeout = msecs_to_jiffies(HINIC_FLUSH_QUEUE_TIMEOUT) + jiffies; do { - free_wqebbs = hinic_get_sq_free_wqebbs(nic_dev->hwdev, - txq->q_id) + 1; - if (free_wqebbs == txq->q_depth) + if (hinic_get_hw_handle_status(nic_dev->hwdev, txq->q_id)) return 0;
err = hinic_force_drop_tx_pkt(nic_dev->hwdev); @@ -1323,8 +1330,7 @@ int hinic_stop_sq(struct hinic_txq *txq) } while (time_before(jiffies, timeout));
/* Avoid msleep takes too long and get a fake result */ - free_wqebbs = hinic_get_sq_free_wqebbs(nic_dev->hwdev, txq->q_id) + 1; - if (free_wqebbs == txq->q_depth) + if (hinic_get_hw_handle_status(nic_dev->hwdev, txq->q_id)) return 0;
return -EFAULT; @@ -1342,6 +1348,4 @@ void hinic_flush_txqs(struct net_device *netdev) nicif_err(nic_dev, drv, netdev, "Failed to stop sq%d\n", qid); } - - return; } /*lint -e766*/