net: qede: Implement ndo_tx_timeout
authorDenis Bolotin <dbolotin@marvell.com>
Thu, 14 May 2020 09:57:24 +0000 (12:57 +0300)
committerDavid S. Miller <davem@davemloft.net>
Thu, 14 May 2020 20:25:46 +0000 (13:25 -0700)
Upon tx timeout detection we do disable carrier and print TX queue
info on TX timeout. We then raise hw error condition and trigger
service task to handle this.

This handler will capture extra debug info and then optionally
trigger recovery procedure to try restore function.

Signed-off-by: Denis Bolotin <dbolotin@marvell.com>
Signed-off-by: Ariel Elior <aelior@marvell.com>
Signed-off-by: Igor Russkikh <irusskikh@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/qede/qede.h
drivers/net/ethernet/qlogic/qede/qede_main.c

index 695d645..8857da1 100644 (file)
@@ -533,7 +533,6 @@ u16 qede_select_queue(struct net_device *dev, struct sk_buff *skb,
 netdev_features_t qede_features_check(struct sk_buff *skb,
                                      struct net_device *dev,
                                      netdev_features_t features);
-void qede_tx_log_print(struct qede_dev *edev, struct qede_fastpath *fp);
 int qede_alloc_rx_buffer(struct qede_rx_queue *rxq, bool allow_lazy);
 int qede_free_tx_pkt(struct qede_dev *edev,
                     struct qede_tx_queue *txq, int *len);
index ee7662d..f50d9a9 100644 (file)
@@ -539,6 +539,51 @@ static int qede_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        return 0;
 }
 
+static void qede_tx_log_print(struct qede_dev *edev, struct qede_tx_queue *txq)
+{
+       DP_NOTICE(edev,
+                 "Txq[%d]: FW cons [host] %04x, SW cons %04x, SW prod %04x [Jiffies %lu]\n",
+                 txq->index, le16_to_cpu(*txq->hw_cons_ptr),
+                 qed_chain_get_cons_idx(&txq->tx_pbl),
+                 qed_chain_get_prod_idx(&txq->tx_pbl),
+                 jiffies);
+}
+
+static void qede_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+       struct qede_tx_queue *txq;
+       int cos;
+
+       netif_carrier_off(dev);
+       DP_NOTICE(edev, "TX timeout on queue %u!\n", txqueue);
+
+       if (!(edev->fp_array[txqueue].type & QEDE_FASTPATH_TX))
+               return;
+
+       for_each_cos_in_txq(edev, cos) {
+               txq = &edev->fp_array[txqueue].txq[cos];
+
+               if (qed_chain_get_cons_idx(&txq->tx_pbl) !=
+                   qed_chain_get_prod_idx(&txq->tx_pbl))
+                       qede_tx_log_print(edev, txq);
+       }
+
+       if (IS_VF(edev))
+               return;
+
+       if (test_and_set_bit(QEDE_ERR_IS_HANDLED, &edev->err_flags) ||
+           edev->state == QEDE_STATE_RECOVERY) {
+               DP_INFO(edev,
+                       "Avoid handling a Tx timeout while another HW error is being handled\n");
+               return;
+       }
+
+       set_bit(QEDE_ERR_GET_DBG_INFO, &edev->err_flags);
+       set_bit(QEDE_SP_HW_ERR, &edev->sp_flags);
+       schedule_delayed_work(&edev->sp_task, 0);
+}
+
 static int qede_setup_tc(struct net_device *ndev, u8 num_tc)
 {
        struct qede_dev *edev = netdev_priv(ndev);
@@ -626,6 +671,7 @@ static const struct net_device_ops qede_netdev_ops = {
        .ndo_validate_addr = eth_validate_addr,
        .ndo_change_mtu = qede_change_mtu,
        .ndo_do_ioctl = qede_ioctl,
+       .ndo_tx_timeout = qede_tx_timeout,
 #ifdef CONFIG_QED_SRIOV
        .ndo_set_vf_mac = qede_set_vf_mac,
        .ndo_set_vf_vlan = qede_set_vf_vlan,