AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/03 07:07 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Wangxun (ngbe/txgbe) physical PCIe ethernet drivers to add PCI error handling and Tx timeout recovery. These are vendor-specific hardware drivers that require physical PCIe cards not emulated in standard QEMU environments, making the modified code paths unreachable during fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/08/03 07:07 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 8b4b5791f72d40a4e9ebe606a39d39963e5cd6a8\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Aug 3 07:07:56 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/wangxun/libwx/Makefile b/drivers/net/ethernet/wangxun/libwx/Makefile\nindex a71b0ad77de3f..c8724bb129aa0 100644\n--- a/drivers/net/ethernet/wangxun/libwx/Makefile\n+++ b/drivers/net/ethernet/wangxun/libwx/Makefile\n@@ -4,5 +4,5 @@\n \n obj-$(CONFIG_LIBWX) += libwx.o\n \n-libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o\n+libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o wx_err.o\n libwx-objs += wx_vf.o wx_vf_lib.o wx_vf_common.o\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c\nnew file mode 100644\nindex 0000000000000..b56fbdc959dec\n--- /dev/null\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c\n@@ -0,0 +1,332 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd. */\n+/* Copyright (c) 1999 - 2026 Intel Corporation. */\n+\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/pci.h\u003e\n+#include \u003clinux/aer.h\u003e\n+\n+#include \"wx_type.h\"\n+#include \"wx_lib.h\"\n+#include \"wx_err.h\"\n+\n+/**\n+ * wx_io_error_detected - called when PCI error is detected\n+ * @pdev: Pointer to PCI device\n+ * @state: The current pci connection state\n+ *\n+ * Return: pci_ers_result_t.\n+ *\n+ * This function is called after a PCI bus error affecting\n+ * this device has been detected.\n+ */\n+static pci_ers_result_t wx_io_error_detected(struct pci_dev *pdev,\n+\t\t\t\t\t     pci_channel_state_t state)\n+{\n+\tstruct wx *wx = pci_get_drvdata(pdev);\n+\tstruct net_device *netdev;\n+\n+\tif (!wx)\n+\t\treturn PCI_ERS_RESULT_DISCONNECT;\n+\n+\tnetdev = wx-\u003enetdev;\n+\tif (!netif_device_present(netdev))\n+\t\treturn PCI_ERS_RESULT_DISCONNECT;\n+\n+\trtnl_lock();\n+\tnetif_device_detach(netdev);\n+\tset_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx-\u003eflags);\n+\twx_soft_quiesce(wx);\n+\n+\tif (state == pci_channel_io_perm_failure) {\n+\t\trtnl_unlock();\n+\t\treturn PCI_ERS_RESULT_DISCONNECT;\n+\t}\n+\n+\tif (!test_and_set_bit(WX_STATE_DISABLED, wx-\u003estate))\n+\t\tpci_disable_device(pdev);\n+\trtnl_unlock();\n+\n+\t/* Request a slot reset. */\n+\treturn PCI_ERS_RESULT_NEED_RESET;\n+}\n+\n+/**\n+ * wx_io_slot_reset - called after the pci bus has been reset.\n+ * @pdev: Pointer to PCI device\n+ *\n+ * Return: pci_ers_result_t.\n+ *\n+ * Restart the card from scratch, as if from a cold-boot.\n+ */\n+static pci_ers_result_t wx_io_slot_reset(struct pci_dev *pdev)\n+{\n+\tstruct wx *wx = pci_get_drvdata(pdev);\n+\n+\tif (pci_enable_device_mem(pdev)) {\n+\t\twx_err(wx, \"Cannot re-enable PCI device after reset.\\n\");\n+\t\treturn PCI_ERS_RESULT_DISCONNECT;\n+\t}\n+\n+\t/* make all memory operations done before clearing the flag */\n+\tsmp_mb__before_atomic();\n+\tclear_bit(WX_STATE_DISABLED, wx-\u003estate);\n+\tclear_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx-\u003eflags);\n+\tpci_set_master(pdev);\n+\tpci_restore_state(pdev);\n+\tpci_wake_from_d3(pdev, false);\n+\n+\trtnl_lock();\n+\tif (netif_running(wx-\u003enetdev) \u0026\u0026 wx-\u003edown_suspend)\n+\t\twx-\u003edown_suspend(wx);\n+\tif (wx-\u003edo_reset)\n+\t\twx-\u003edo_reset(wx-\u003enetdev, false);\n+\trtnl_unlock();\n+\n+\treturn PCI_ERS_RESULT_RECOVERED;\n+}\n+\n+/**\n+ * wx_io_resume - called when traffic can start flowing again.\n+ * @pdev: Pointer to PCI device\n+ *\n+ * This callback is called when the error recovery driver tells us that\n+ * its OK to resume normal operation.\n+ */\n+static void wx_io_resume(struct pci_dev *pdev)\n+{\n+\tstruct wx *wx = pci_get_drvdata(pdev);\n+\tstruct net_device *netdev;\n+\tint err;\n+\n+\tnetdev = wx-\u003enetdev;\n+\trtnl_lock();\n+\tif (netif_running(netdev)) {\n+\t\terr = netdev-\u003enetdev_ops-\u003endo_open(netdev);\n+\t\tif (err) {\n+\t\t\twx_err(wx, \"Failed to open netdev after reset\\n\");\n+\t\t\tgoto out;\n+\t\t}\n+\t}\n+\tnetif_device_attach(netdev);\n+out:\n+\trtnl_unlock();\n+}\n+\n+const struct pci_error_handlers wx_err_handler = {\n+\t.error_detected = wx_io_error_detected,\n+\t.slot_reset = wx_io_slot_reset,\n+\t.resume = wx_io_resume,\n+};\n+EXPORT_SYMBOL(wx_err_handler);\n+\n+static bool wx_check_pcie_error(struct wx *wx)\n+{\n+\tu16 vid, pci_cmd;\n+\n+\tpci_read_config_word(wx-\u003epdev, PCI_VENDOR_ID, \u0026vid);\n+\tpci_read_config_word(wx-\u003epdev, PCI_COMMAND, \u0026pci_cmd);\n+\n+\t/* PCIe link loss or memory space can't access */\n+\tif (vid == U16_MAX || !(pci_cmd \u0026 PCI_COMMAND_MEMORY))\n+\t\treturn true;\n+\n+\treturn false;\n+}\n+\n+static void wx_pf_reset_subtask(struct wx *wx)\n+{\n+\tif (!test_and_clear_bit(WX_FLAG_NEED_DO_RESET, wx-\u003eflags))\n+\t\treturn;\n+\n+\twx_warn(wx, \"Reset adapter.\\n\");\n+\tif (wx-\u003edo_reset)\n+\t\twx-\u003edo_reset(wx-\u003enetdev, true);\n+}\n+\n+static void wx_reset_task(struct work_struct *work)\n+{\n+\tstruct wx *wx = container_of(work, struct wx, reset_task);\n+\n+\trtnl_lock();\n+\n+\t/* If the device has been detached (e.g., due to AER error handling),\n+\t * abort the reset task to prevent operating on a dead or unmanaged\n+\t * hardware.\n+\t */\n+\tif (!netif_device_present(wx-\u003enetdev))\n+\t\tgoto out;\n+\n+\tif (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx-\u003eflags)) {\n+\t\t/* Double check: Verify if the PCIe error is still present. */\n+\t\tif (wx_check_pcie_error(wx))\n+\t\t\twx_soft_quiesce(wx);\n+\t\telse\n+\t\t\tclear_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx-\u003eflags);\n+\t\tgoto out;\n+\t}\n+\n+\tif (test_bit(WX_STATE_DOWN, wx-\u003estate) ||\n+\t    test_bit(WX_STATE_RESETTING, wx-\u003estate))\n+\t\tgoto out;\n+\n+\twx_pf_reset_subtask(wx);\n+\n+out:\n+\trtnl_unlock();\n+}\n+\n+void wx_check_err_subtask(struct wx *wx)\n+{\n+\tif (test_bit(WX_FLAG_NEED_DO_RESET, wx-\u003eflags))\n+\t\tqueue_work(wx-\u003ereset_wq, \u0026wx-\u003ereset_task);\n+}\n+EXPORT_SYMBOL(wx_check_err_subtask);\n+\n+int wx_init_err_task(struct wx *wx)\n+{\n+\twx-\u003ereset_wq = alloc_workqueue(\"%s_reset_wq_%x\", WQ_UNBOUND | WQ_HIGHPRI,\n+\t\t\t\t       1, wx-\u003edriver_name, pci_dev_id(wx-\u003epdev));\n+\tif (!wx-\u003ereset_wq) {\n+\t\twx_err(wx, \"Failed to create wx_reset_wq workqueue\\n\");\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tINIT_WORK(\u0026wx-\u003ereset_task, wx_reset_task);\n+\treturn 0;\n+}\n+EXPORT_SYMBOL(wx_init_err_task);\n+\n+static bool wx_ring_tx_pending(struct wx *wx)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c wx-\u003enum_tx_queues; i++) {\n+\t\tstruct wx_ring *tx_ring = wx-\u003etx_ring[i];\n+\n+\t\tif (tx_ring-\u003enext_to_use != tx_ring-\u003enext_to_clean)\n+\t\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+static bool wx_vf_tx_pending(struct wx *wx)\n+{\n+\tstruct wx_ring_feature *vmdq = \u0026wx-\u003ering_feature[RING_F_VMDQ];\n+\tu32 q_per_pool = __ALIGN_MASK(1, ~vmdq-\u003emask);\n+\tu32 i, j;\n+\n+\tif (!wx-\u003enum_vfs)\n+\t\treturn false;\n+\n+\tfor (i = 0; i \u003c wx-\u003enum_vfs; i++) {\n+\t\tfor (j = 0; j \u003c q_per_pool; j++) {\n+\t\t\tu32 h, t;\n+\n+\t\t\th = rd32(wx, WX_PX_TR_RP_PV(q_per_pool, i, j));\n+\t\t\tt = rd32(wx, WX_PX_TR_WP_PV(q_per_pool, i, j));\n+\n+\t\t\tif (h != t)\n+\t\t\t\treturn true;\n+\t\t}\n+\t}\n+\n+\treturn false;\n+}\n+\n+static void wx_watchdog_flush_tx(struct wx *wx)\n+{\n+\tif (!netif_running(wx-\u003enetdev))\n+\t\treturn;\n+\tif (netif_carrier_ok(wx-\u003enetdev))\n+\t\treturn;\n+\n+\tif (wx_ring_tx_pending(wx) || wx_vf_tx_pending(wx)) {\n+\t\t/* We've lost link, so the controller stops DMA,\n+\t\t * but we've got queued Tx work that's never going\n+\t\t * to get done, so reset controller to flush Tx.\n+\t\t * (Do the reset outside of interrupt context).\n+\t\t */\n+\t\twx_warn(wx, \"initiating reset due to lost link with pending Tx work\\n\");\n+\t\tset_bit(WX_FLAG_NEED_DO_RESET, wx-\u003eflags);\n+\t}\n+}\n+\n+static void wx_detect_tx_hang(struct wx *wx)\n+{\n+\tint i;\n+\n+\t/* If we're down or resetting, just bail */\n+\tif (!netif_running(wx-\u003enetdev) ||\n+\t    test_bit(WX_STATE_RESETTING, wx-\u003estate))\n+\t\treturn;\n+\n+\t/* Force detection of hung controller */\n+\tif (netif_carrier_ok(wx-\u003enetdev)) {\n+\t\tfor (i = 0; i \u003c wx-\u003enum_tx_queues; i++)\n+\t\t\tset_bit(WX_TX_DETECT_HANG, wx-\u003etx_ring[i]-\u003estate);\n+\t}\n+}\n+\n+void wx_check_hang_subtask(struct wx *wx)\n+{\n+\tif (test_bit(WX_STATE_DOWN, wx-\u003estate) ||\n+\t    test_bit(WX_STATE_RESETTING, wx-\u003estate))\n+\t\treturn;\n+\n+\twx_watchdog_flush_tx(wx);\n+\twx_detect_tx_hang(wx);\n+}\n+EXPORT_SYMBOL(wx_check_hang_subtask);\n+\n+static void wx_tx_timeout_recovery(struct wx *wx)\n+{\n+\t/*\n+\t * When a PCIe hardware error occurs, the driver should initiate a PCIe\n+\t * recovery mechanism. However, this recovery flow relies on the AER\n+\t * driver for current kernel policy. Therefore, a self-contained\n+\t * recovery mechanism is not implemented yet.\n+\t */\n+\tset_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx-\u003eflags);\n+\twx_err(wx, \"PCIe error detected during tx timeout\\n\");\n+\tqueue_work(wx-\u003ereset_wq, \u0026wx-\u003ereset_task);\n+}\n+\n+static void wx_tx_timeout_reset(struct wx *wx)\n+{\n+\tif (test_bit(WX_STATE_DOWN, wx-\u003estate))\n+\t\treturn;\n+\n+\tset_bit(WX_FLAG_NEED_DO_RESET, wx-\u003eflags);\n+\twx_warn(wx, \"initiating reset due to tx timeout\\n\");\n+\twx_service_event_schedule(wx);\n+}\n+\n+void wx_tx_timeout(struct net_device *netdev, unsigned int __always_unused txqueue)\n+{\n+\tstruct wx *wx = netdev_priv(netdev);\n+\n+\tif (wx_check_pcie_error(wx))\n+\t\twx_tx_timeout_recovery(wx);\n+\telse\n+\t\twx_tx_timeout_reset(wx);\n+}\n+EXPORT_SYMBOL(wx_tx_timeout);\n+\n+void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next)\n+{\n+\tstruct wx *wx = netdev_priv(tx_ring-\u003enetdev);\n+\n+\twx_warn(wx,\n+\t\t\"Detected Tx Unit Hang: Queue %d, TDH %x, TDT %x, ntu %x, ntc %x, ntc.time_stamp %lx, jiffies %lx\\n\",\n+\t\ttx_ring-\u003equeue_index,\n+\t\trd32(wx, WX_PX_TR_RP(tx_ring-\u003ereg_idx)),\n+\t\trd32(wx, WX_PX_TR_WP(tx_ring-\u003ereg_idx)),\n+\t\ttx_ring-\u003enext_to_use, next,\n+\t\ttx_ring-\u003etx_buffer_info[next].time_stamp, jiffies);\n+\n+\tnetif_stop_subqueue(tx_ring-\u003enetdev, tx_ring-\u003equeue_index);\n+\n+\twx_tx_timeout_reset(wx);\n+}\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h\nnew file mode 100644\nindex 0000000000000..a6a82a2635284\n--- /dev/null\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h\n@@ -0,0 +1,18 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * WangXun Gigabit PCI Express Linux driver\n+ * Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd.\n+ */\n+\n+#ifndef _WX_ERR_H_\n+#define _WX_ERR_H_\n+\n+extern const struct pci_error_handlers wx_err_handler;\n+\n+void wx_check_err_subtask(struct wx *wx);\n+int wx_init_err_task(struct wx *wx);\n+void wx_check_hang_subtask(struct wx *wx);\n+void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue);\n+void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next);\n+\n+#endif /* _WX_ERR_H_ */\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c\nindex 22037f015ded1..940d2e59876c7 100644\n--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c\n@@ -397,7 +397,7 @@ static void wx_update_rsc(struct wx *wx)\n \n \t/* reset the device to apply the new RSC setting */\n \tif (need_reset \u0026\u0026 wx-\u003edo_reset)\n-\t\twx-\u003edo_reset(netdev);\n+\t\twx-\u003edo_reset(netdev, true);\n }\n \n int wx_set_coalesce(struct net_device *netdev,\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c\nindex 260e14d5d5412..122c4952d203f 100644\n--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c\n@@ -1932,6 +1932,7 @@ static void wx_configure_tx_ring(struct wx *wx,\n \telse\n \t\tring-\u003eatr_sample_rate = 0;\n \n+\tbitmap_zero(ring-\u003estate, WX_RING_STATE_NBITS);\n \t/* reinitialize tx_buffer_info */\n \tmemset(ring-\u003etx_buffer_info, 0,\n \t       sizeof(struct wx_tx_buffer) * ring-\u003ecount);\n@@ -2851,16 +2852,26 @@ EXPORT_SYMBOL(wx_fc_enable);\n static void wx_update_xoff_rx_lfc(struct wx *wx)\n {\n \tstruct wx_hw_stats *hwstats = \u0026wx-\u003estats;\n+\tu64 data;\n+\tint i;\n \n \tif (wx-\u003efc.mode != wx_fc_full \u0026\u0026\n \t    wx-\u003efc.mode != wx_fc_rx_pause)\n \t\treturn;\n \n \tif (wx-\u003emac.type \u003e= wx_mac_aml)\n-\t\thwstats-\u003elxoffrxc += rd32_wrap(wx, WX_MAC_LXOFFRXC_AML,\n-\t\t\t\t\t       \u0026wx-\u003elast_stats.lxoffrxc);\n+\t\tdata = rd32_wrap(wx, WX_MAC_LXOFFRXC_AML,\n+\t\t\t\t \u0026wx-\u003elast_stats.lxoffrxc);\n \telse\n-\t\thwstats-\u003elxoffrxc += rd64(wx, WX_MAC_LXOFFRXC);\n+\t\tdata = rd64(wx, WX_MAC_LXOFFRXC);\n+\thwstats-\u003elxoffrxc += data;\n+\n+\t/* refill credits (no tx hang) if we received xoff */\n+\tif (!data)\n+\t\treturn;\n+\n+\tfor (i = 0; i \u003c wx-\u003enum_tx_queues; i++)\n+\t\tclear_bit(WX_HANG_CHECK_ARMED, wx-\u003etx_ring[i]-\u003estate);\n }\n \n /**\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c\nindex 476b71049f7e6..ed5aad7857bd9 100644\n--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c\n@@ -14,6 +14,7 @@\n \n #include \"wx_type.h\"\n #include \"wx_lib.h\"\n+#include \"wx_err.h\"\n #include \"wx_ptp.h\"\n #include \"wx_hw.h\"\n #include \"wx_vf_lib.h\"\n@@ -742,6 +743,37 @@ static struct netdev_queue *wx_txring_txq(const struct wx_ring *ring)\n \treturn netdev_get_tx_queue(ring-\u003enetdev, ring-\u003equeue_index);\n }\n \n+static u32 wx_get_tx_pending(struct wx_ring *ring)\n+{\n+\tunsigned int head, tail;\n+\n+\thead = ring-\u003enext_to_clean;\n+\ttail = ring-\u003enext_to_use;\n+\n+\treturn ((head \u003c= tail) ? tail : tail + ring-\u003ecount) - head;\n+}\n+\n+static bool wx_check_tx_hang(struct wx_ring *ring)\n+{\n+\tu32 tx_done_old = ring-\u003etx_stats.tx_done_old;\n+\tu32 tx_pending = wx_get_tx_pending(ring);\n+\tu32 tx_done = ring-\u003estats.packets;\n+\n+\tif (!test_and_clear_bit(WX_TX_DETECT_HANG, ring-\u003estate))\n+\t\treturn false;\n+\n+\tif (tx_done_old == tx_done \u0026\u0026 tx_pending)\n+\t\t/* make sure it is true for two checks in a row */\n+\t\treturn test_and_set_bit(WX_HANG_CHECK_ARMED, ring-\u003estate);\n+\n+\t/* update completed stats and continue */\n+\tring-\u003etx_stats.tx_done_old = tx_done;\n+\t/* reset the countdown */\n+\tclear_bit(WX_HANG_CHECK_ARMED, ring-\u003estate);\n+\n+\treturn false;\n+}\n+\n /**\n  * wx_clean_tx_irq - Reclaim resources after transmit completes\n  * @q_vector: structure containing interrupt and ring information\n@@ -866,6 +898,11 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector,\n \tnetdev_tx_completed_queue(wx_txring_txq(tx_ring),\n \t\t\t\t  total_packets, total_bytes);\n \n+\tif (wx_check_tx_hang(tx_ring)) {\n+\t\twx_handle_tx_hang(tx_ring, i);\n+\t\treturn true;\n+\t}\n+\n #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)\n \tif (unlikely(total_packets \u0026\u0026 netif_carrier_ok(tx_ring-\u003enetdev) \u0026\u0026\n \t\t     (wx_desc_unused(tx_ring) \u003e= TX_WAKE_THRESHOLD))) {\n@@ -3114,7 +3151,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)\n \tnetdev-\u003efeatures = features;\n \n \tif (changed \u0026 NETIF_F_HW_VLAN_CTAG_RX \u0026\u0026 wx-\u003edo_reset)\n-\t\twx-\u003edo_reset(netdev);\n+\t\twx-\u003edo_reset(netdev, true);\n \telse if (changed \u0026 (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER))\n \t\twx_set_rx_mode(netdev);\n \n@@ -3164,7 +3201,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)\n \n out:\n \tif (need_reset \u0026\u0026 wx-\u003edo_reset)\n-\t\twx-\u003edo_reset(netdev);\n+\t\twx-\u003edo_reset(netdev, true);\n \n \treturn 0;\n }\n@@ -3373,5 +3410,23 @@ void wx_service_timer(struct timer_list *t)\n }\n EXPORT_SYMBOL(wx_service_timer);\n \n+void wx_soft_quiesce(struct wx *wx)\n+{\n+\tif (!netif_running(wx-\u003enetdev) ||\n+\t    test_and_set_bit(WX_STATE_DOWN, wx-\u003estate))\n+\t\treturn;\n+\n+\tpci_clear_master(wx-\u003epdev);\n+\tnetif_tx_stop_all_queues(wx-\u003enetdev);\n+\tnetif_carrier_off(wx-\u003enetdev);\n+\tnetif_tx_disable(wx-\u003enetdev);\n+\twx_napi_disable_all(wx);\n+\twx_ptp_quiesce(wx);\n+\n+\tclear_bit(WX_FLAG_NEED_DO_RESET, wx-\u003eflags);\n+\ttimer_delete_sync(\u0026wx-\u003eservice_timer);\n+}\n+EXPORT_SYMBOL(wx_soft_quiesce);\n+\n MODULE_DESCRIPTION(\"Common library for Wangxun(R) Ethernet drivers.\");\n MODULE_LICENSE(\"GPL\");\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h\nindex bc671786978e5..9d85d399e17f4 100644\n--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h\n@@ -41,5 +41,6 @@ int wx_set_ring(struct wx *wx, u32 new_tx_count,\n void wx_service_event_schedule(struct wx *wx);\n void wx_service_event_complete(struct wx *wx);\n void wx_service_timer(struct timer_list *t);\n+void wx_soft_quiesce(struct wx *wx);\n \n #endif /* _WX_LIB_H_ */\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c\nindex 44f3e65052468..3eea647c4742c 100644\n--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c\n@@ -321,6 +321,9 @@ static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)\n \tstruct wx *wx = container_of(ptp, struct wx, ptp_caps);\n \tint ts_done;\n \n+\tif (!test_bit(WX_STATE_PTP_RUNNING, wx-\u003estate))\n+\t\treturn HZ;\n+\n \tts_done = wx_ptp_tx_hwtstamp_work(wx);\n \n \twx_ptp_overflow_check(wx);\n@@ -842,6 +845,30 @@ void wx_ptp_stop(struct wx *wx)\n }\n EXPORT_SYMBOL(wx_ptp_stop);\n \n+void wx_ptp_quiesce(struct wx *wx)\n+{\n+\tif (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx-\u003estate))\n+\t\treturn;\n+\n+\tclear_bit(WX_FLAG_PTP_PPS_ENABLED, wx-\u003eflags);\n+\n+\tif (wx-\u003eptp_clock)\n+\t\tptp_cancel_worker_sync(wx-\u003eptp_clock);\n+\n+\tif (wx-\u003eptp_tx_skb) {\n+\t\tdev_kfree_skb_any(wx-\u003eptp_tx_skb);\n+\t\twx-\u003eptp_tx_skb = NULL;\n+\t}\n+\tclear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx-\u003estate);\n+\n+\tif (wx-\u003eptp_clock) {\n+\t\tptp_clock_unregister(wx-\u003eptp_clock);\n+\t\twx-\u003eptp_clock = NULL;\n+\t\tdev_info(\u0026wx-\u003epdev-\u003edev, \"removed PHC on %s\\n\", wx-\u003enetdev-\u003ename);\n+\t}\n+}\n+EXPORT_SYMBOL(wx_ptp_quiesce);\n+\n /**\n  * wx_ptp_rx_hwtstamp - utility function which checks for RX time stamp\n  * @wx: pointer to wx struct\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h\nindex 50db90a6e3ee6..ad2f824875d51 100644\n--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h\n@@ -10,6 +10,7 @@ void wx_ptp_reset(struct wx *wx);\n void wx_ptp_init(struct wx *wx);\n void wx_ptp_suspend(struct wx *wx);\n void wx_ptp_stop(struct wx *wx);\n+void wx_ptp_quiesce(struct wx *wx);\n void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);\n int wx_hwtstamp_get(struct net_device *dev,\n \t\t    struct kernel_hwtstamp_config *cfg);\ndiff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h\nindex 65e3e55db1cff..2eba5ab599252 100644\n--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h\n@@ -450,6 +450,11 @@ enum WX_MSCA_CMD_value {\n #define WX_PX_TR_CFG_THRE_SHIFT      8\n #define WX_PX_TR_CFG_HEAD_WB         BIT(27)\n \n+#define WX_PX_TR_RP_PV(q_per_pool, vf_number, vf_q_index) \\\n+\t\t(WX_PX_TR_RP((q_per_pool) * (vf_number) + (vf_q_index)))\n+#define WX_PX_TR_WP_PV(q_per_pool, vf_number, vf_q_index) \\\n+\t\t(WX_PX_TR_WP((q_per_pool) * (vf_number) + (vf_q_index)))\n+\n /* Receive DMA Registers */\n #define WX_PX_RR_BAL(_i)             (0x01000 + ((_i) * 0x40))\n #define WX_PX_RR_BAH(_i)             (0x01004 + ((_i) * 0x40))\n@@ -1040,6 +1045,7 @@ struct wx_queue_stats {\n struct wx_tx_queue_stats {\n \tu64 restart_queue;\n \tu64 tx_busy;\n+\tu32 tx_done_old;\n };\n \n struct wx_rx_queue_stats {\n@@ -1055,6 +1061,12 @@ struct wx_rx_queue_stats {\n #define wx_for_each_ring(posm, headm) \\\n \tfor (posm = (headm).ring; posm; posm = posm-\u003enext)\n \n+enum wx_ring_state {\n+\tWX_TX_DETECT_HANG,\n+\tWX_HANG_CHECK_ARMED,\n+\tWX_RING_STATE_NBITS\n+};\n+\n struct wx_ring_container {\n \tstruct wx_ring *ring;           /* pointer to linked list of rings */\n \tunsigned int total_bytes;       /* total bytes processed this int */\n@@ -1074,6 +1086,7 @@ struct wx_ring {\n \t\tstruct wx_tx_buffer *tx_buffer_info;\n \t\tstruct wx_rx_buffer *rx_buffer_info;\n \t};\n+\tDECLARE_BITMAP(state, WX_RING_STATE_NBITS);\n \tu8 __iomem *tail;\n \tdma_addr_t dma;                 /* phys. address of descriptor ring */\n \tdma_addr_t headwb_dma;\n@@ -1209,6 +1222,8 @@ enum wx_state {\n \tWX_STATE_PTP_RUNNING,\n \tWX_STATE_PTP_TX_IN_PROGRESS,\n \tWX_STATE_SERVICE_SCHED,\n+\tWX_STATE_DISABLED,\n+\tWX_STATE_RES_FREED,\n \tWX_STATE_NBITS\t\t/* must be last */\n };\n \n@@ -1275,6 +1290,7 @@ enum wx_pf_flags {\n \tWX_FLAG_NEED_DO_RESET,\n \tWX_FLAG_RX_MERGE_ENABLED,\n \tWX_FLAG_TXHEAD_WB_ENABLED,\n+\tWX_FLAG_NEED_PCIE_RECOVERY,\n \tWX_PF_FLAGS_NBITS               /* must be last */\n };\n \n@@ -1395,7 +1411,8 @@ struct wx {\n \tvoid (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);\n \tvoid (*configure_fdir)(struct wx *wx);\n \tint (*setup_tc)(struct net_device *netdev, u8 tc);\n-\tvoid (*do_reset)(struct net_device *netdev);\n+\tvoid (*do_reset)(struct net_device *netdev, bool reinit);\n+\tvoid (*down_suspend)(struct wx *wx);\n \tint (*ptp_setup_sdp)(struct wx *wx);\n \tvoid (*set_num_queues)(struct wx *wx);\n \n@@ -1423,6 +1440,8 @@ struct wx {\n \n \tstruct timer_list service_timer;\n \tstruct work_struct service_task;\n+\tstruct work_struct reset_task;\n+\tstruct workqueue_struct *reset_wq;\n \tstruct mutex reset_lock; /* mutex for reset */\n };\n \n@@ -1505,7 +1524,8 @@ rd32_wrap(struct wx *wx, u32 reg, u32 *last)\n \n #define wx_err(wx, fmt, arg...) \\\n \tdev_err(\u0026(wx)-\u003epdev-\u003edev, fmt, ##arg)\n-\n+#define wx_warn(wx, fmt, arg...) \\\n+\tdev_warn(\u0026(wx)-\u003epdev-\u003edev, fmt, ##arg)\n #define wx_dbg(wx, fmt, arg...) \\\n \tdev_dbg(\u0026(wx)-\u003epdev-\u003edev, fmt, ##arg)\n \ndiff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c\nindex b2e1919828032..1960f7154151d 100644\n--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c\n+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c\n@@ -59,7 +59,6 @@ static int ngbe_set_ringparam(struct net_device *netdev,\n \twx_set_ring(wx, new_tx_count, new_rx_count, temp_ring);\n \tkvfree(temp_ring);\n \n-\twx_configure(wx);\n \tngbe_up(wx);\n \n clear_reset:\ndiff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c\nindex a16221995909f..14c00228cede4 100644\n--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c\n+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c\n@@ -14,6 +14,7 @@\n #include \"../libwx/wx_type.h\"\n #include \"../libwx/wx_hw.h\"\n #include \"../libwx/wx_lib.h\"\n+#include \"../libwx/wx_err.h\"\n #include \"../libwx/wx_ptp.h\"\n #include \"../libwx/wx_mbx.h\"\n #include \"../libwx/wx_sriov.h\"\n@@ -46,6 +47,20 @@ static const struct pci_device_id ngbe_pci_tbl[] = {\n \t{ }\n };\n \n+static void ngbe_down_suspend(struct wx *wx)\n+{\n+\tif (test_and_set_bit(WX_STATE_RES_FREED, wx-\u003estate))\n+\t\treturn;\n+\n+\tphylink_stop(wx-\u003ephylink);\n+\tphylink_disconnect_phy(wx-\u003ephylink);\n+\twx_clean_all_tx_rings(wx);\n+\twx_clean_all_rx_rings(wx);\n+\twx_free_irq(wx);\n+\twx_free_isb_resources(wx);\n+\twx_free_resources(wx);\n+}\n+\n /**\n  *  ngbe_init_type_code - Initialize the shared code\n  *  @wx: pointer to hardware structure\n@@ -133,6 +148,8 @@ static int ngbe_sw_init(struct wx *wx)\n \n \twx-\u003embx.size = WX_VXMAILBOX_SIZE;\n \twx-\u003esetup_tc = ngbe_setup_tc;\n+\twx-\u003edo_reset = ngbe_do_reset;\n+\twx-\u003edown_suspend = ngbe_down_suspend;\n \tset_bit(0, \u0026wx-\u003efwd_bitmask);\n \n \treturn 0;\n@@ -147,6 +164,8 @@ static void ngbe_service_task(struct work_struct *work)\n \tstruct wx *wx = container_of(work, struct wx, service_task);\n \n \twx_update_stats(wx);\n+\twx_check_hang_subtask(wx);\n+\twx_check_err_subtask(wx);\n \n \twx_service_event_complete(wx);\n }\n@@ -392,6 +411,7 @@ static void ngbe_disable_device(struct wx *wx)\n \tnetif_tx_stop_all_queues(netdev);\n \tnetif_tx_disable(netdev);\n \n+\tclear_bit(WX_FLAG_NEED_DO_RESET, wx-\u003eflags);\n \ttimer_delete_sync(\u0026wx-\u003eservice_timer);\n \tcancel_work_sync(\u0026wx-\u003eservice_task);\n \n@@ -408,6 +428,9 @@ static void ngbe_disable_device(struct wx *wx)\n \n static void ngbe_reset(struct wx *wx)\n {\n+\tif (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx-\u003eflags))\n+\t\treturn;\n+\n \twx_flush_sw_mac_table(wx);\n \twx_mac_set_default_filter(wx, wx-\u003emac.addr);\n \tif (test_bit(WX_STATE_PTP_RUNNING, wx-\u003estate))\n@@ -423,13 +446,14 @@ void ngbe_down(struct wx *wx)\n \twx_clean_all_rx_rings(wx);\n }\n \n-void ngbe_up(struct wx *wx)\n+static void ngbe_up_complete(struct wx *wx)\n {\n \twx_configure_vectors(wx);\n \n \t/* make sure to complete pre-operations */\n \tsmp_mb__before_atomic();\n \tclear_bit(WX_STATE_DOWN, wx-\u003estate);\n+\tclear_bit(WX_STATE_RES_FREED, wx-\u003estate);\n \twx_napi_enable_all(wx);\n \t/* enable transmits */\n \tnetif_tx_start_all_queues(wx-\u003enetdev);\n@@ -490,7 +514,7 @@ static int ngbe_open(struct net_device *netdev)\n \n \twx_ptp_init(wx);\n \n-\tngbe_up(wx);\n+\tngbe_up_complete(wx);\n \n \treturn 0;\n err_dis_phy:\n@@ -503,6 +527,12 @@ static int ngbe_open(struct net_device *netdev)\n \treturn err;\n }\n \n+void ngbe_up(struct wx *wx)\n+{\n+\twx_configure(wx);\n+\tngbe_up_complete(wx);\n+}\n+\n /**\n  * ngbe_close - Disables a network interface\n  * @netdev: network interface device structure\n@@ -518,12 +548,16 @@ static int ngbe_close(struct net_device *netdev)\n {\n \tstruct wx *wx = netdev_priv(netdev);\n \n+\tif (test_bit(WX_STATE_RES_FREED, wx-\u003estate))\n+\t\tgoto out;\n+\n \twx_ptp_stop(wx);\n \tngbe_down(wx);\n \twx_free_irq(wx);\n \twx_free_isb_resources(wx);\n \twx_free_resources(wx);\n \tphylink_disconnect_phy(wx-\u003ephylink);\n+out:\n \twx_control_hw(wx, false);\n \n \treturn 0;\n@@ -555,7 +589,8 @@ static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)\n \t*enable_wake = !!wufc;\n \twx_control_hw(wx, false);\n \n-\tpci_disable_device(pdev);\n+\tif (!test_and_set_bit(WX_STATE_DISABLED, wx-\u003estate))\n+\t\tpci_disable_device(pdev);\n }\n \n static void ngbe_shutdown(struct pci_dev *pdev)\n@@ -590,6 +625,8 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc)\n \t */\n \tif (netif_running(dev))\n \t\tngbe_close(dev);\n+\telse\n+\t\tngbe_reset(wx);\n \n \twx_clear_interrupt_scheme(wx);\n \n@@ -606,11 +643,36 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc)\n \treturn 0;\n }\n \n+static void ngbe_reinit_locked(struct wx *wx)\n+{\n+\tnetif_trans_update(wx-\u003enetdev);\n+\n+\tmutex_lock(\u0026wx-\u003ereset_lock);\n+\tset_bit(WX_STATE_RESETTING, wx-\u003estate);\n+\n+\tngbe_down(wx);\n+\tngbe_up(wx);\n+\n+\tclear_bit(WX_STATE_RESETTING, wx-\u003estate);\n+\tmutex_unlock(\u0026wx-\u003ereset_lock);\n+}\n+\n+void ngbe_do_reset(struct net_device *netdev, bool reinit)\n+{\n+\tstruct wx *wx = netdev_priv(netdev);\n+\n+\tif (netif_running(netdev) \u0026\u0026 reinit)\n+\t\tngbe_reinit_locked(wx);\n+\telse\n+\t\tngbe_reset(wx);\n+}\n+\n static const struct net_device_ops ngbe_netdev_ops = {\n \t.ndo_open               = ngbe_open,\n \t.ndo_stop               = ngbe_close,\n \t.ndo_change_mtu         = wx_change_mtu,\n \t.ndo_start_xmit         = wx_xmit_frame,\n+\t.ndo_tx_timeout         = wx_tx_timeout,\n \t.ndo_set_rx_mode        = wx_set_rx_mode,\n \t.ndo_set_features       = wx_set_features,\n \t.ndo_fix_features       = wx_fix_features,\n@@ -796,6 +858,10 @@ static int ngbe_probe(struct pci_dev *pdev,\n \teth_hw_addr_set(netdev, wx-\u003emac.perm_addr);\n \twx_mac_set_default_filter(wx, wx-\u003emac.perm_addr);\n \n+\terr = wx_init_err_task(wx);\n+\tif (err)\n+\t\tgoto err_free_mac_table;\n+\n \tngbe_init_service(wx);\n \n \terr = wx_init_interrupt_scheme(wx);\n@@ -812,6 +878,7 @@ static int ngbe_probe(struct pci_dev *pdev,\n \t\tgoto err_register;\n \n \tpci_set_drvdata(pdev, wx);\n+\tpci_save_state(pdev);\n \n \treturn 0;\n \n@@ -823,6 +890,8 @@ static int ngbe_probe(struct pci_dev *pdev,\n err_cancel_service:\n \ttimer_delete_sync(\u0026wx-\u003eservice_timer);\n \tcancel_work_sync(\u0026wx-\u003eservice_task);\n+\tcancel_work_sync(\u0026wx-\u003ereset_task);\n+\tdestroy_workqueue(wx-\u003ereset_wq);\n err_free_mac_table:\n \tkfree(wx-\u003erss_key);\n \tkfree(wx-\u003emac_table);\n@@ -854,6 +923,8 @@ static void ngbe_remove(struct pci_dev *pdev)\n \n \ttimer_shutdown_sync(\u0026wx-\u003eservice_timer);\n \tcancel_work_sync(\u0026wx-\u003eservice_task);\n+\tcancel_work_sync(\u0026wx-\u003ereset_task);\n+\tdestroy_workqueue(wx-\u003ereset_wq);\n \n \tphylink_destroy(wx-\u003ephylink);\n \tpci_release_selected_regions(pdev,\n@@ -863,7 +934,8 @@ static void ngbe_remove(struct pci_dev *pdev)\n \tkfree(wx-\u003emac_table);\n \twx_clear_interrupt_scheme(wx);\n \n-\tpci_disable_device(pdev);\n+\tif (!test_and_set_bit(WX_STATE_DISABLED, wx-\u003estate))\n+\t\tpci_disable_device(pdev);\n }\n \n static int ngbe_suspend(struct pci_dev *pdev, pm_message_t state)\n@@ -890,6 +962,7 @@ static int ngbe_resume(struct pci_dev *pdev)\n \t\twx_err(wx, \"Cannot enable PCI device from suspend\\n\");\n \t\treturn err;\n \t}\n+\tclear_bit(WX_STATE_DISABLED, wx-\u003estate);\n \tpci_set_master(pdev);\n \tdevice_wakeup_disable(\u0026pdev-\u003edev);\n \n@@ -914,6 +987,7 @@ static struct pci_driver ngbe_driver = {\n \t.resume   = ngbe_resume,\n \t.shutdown = ngbe_shutdown,\n \t.sriov_configure = wx_pci_sriov_configure,\n+\t.err_handler = \u0026wx_err_handler,\n };\n \n module_pci_driver(ngbe_driver);\ndiff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h\nindex 7077a0da4c986..c9233dc7ae505 100644\n--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h\n+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h\n@@ -125,5 +125,6 @@ extern char ngbe_driver_name[];\n void ngbe_down(struct wx *wx);\n void ngbe_up(struct wx *wx);\n int ngbe_setup_tc(struct net_device *dev, u8 tc);\n+void ngbe_do_reset(struct net_device *netdev, bool reinit);\n \n #endif /* _NGBE_TYPE_H_ */\ndiff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c\nindex c277863baf67a..eb91c4f28ecd4 100644\n--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c\n+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c\n@@ -14,6 +14,7 @@\n \n #include \"../libwx/wx_type.h\"\n #include \"../libwx/wx_lib.h\"\n+#include \"../libwx/wx_err.h\"\n #include \"../libwx/wx_ptp.h\"\n #include \"../libwx/wx_hw.h\"\n #include \"../libwx/wx_mbx.h\"\n@@ -93,12 +94,24 @@ static void txgbe_module_detection_subtask(struct wx *wx)\n {\n \tint err;\n \n+\tif (test_bit(WX_STATE_DOWN, wx-\u003estate) ||\n+\t    test_bit(WX_STATE_RESETTING, wx-\u003estate))\n+\t\treturn;\n+\n \tif (!test_and_clear_bit(WX_FLAG_NEED_MODULE_RESET, wx-\u003eflags))\n \t\treturn;\n \n \t/* wait for SFF module ready */\n \tmsleep(200);\n \n+\t/* Re-check state to avoid racing with down/reset paths.\n+\t * Module identification is deferred to the next up event,\n+\t * so it is safe to bail out here.\n+\t */\n+\tif (test_bit(WX_STATE_DOWN, wx-\u003estate) ||\n+\t    test_bit(WX_STATE_RESETTING, wx-\u003estate))\n+\t\treturn;\n+\n \terr = txgbe_identify_module(wx);\n \tif (err == -ENODEV)\n \t\tset_bit(WX_FLAG_NEED_MODULE_RESET, wx-\u003eflags);\n@@ -106,6 +119,10 @@ static void txgbe_module_detection_subtask(struct wx *wx)\n \n static void txgbe_link_config_subtask(struct wx *wx)\n {\n+\tif (test_bit(WX_STATE_DOWN, wx-\u003estate) ||\n+\t    test_bit(WX_STATE_RESETTING, wx-\u003estate))\n+\t\treturn;\n+\n \tif (!test_and_clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx-\u003eflags))\n \t\treturn;\n \n@@ -123,6 +140,8 @@ static void txgbe_service_task(struct work_struct *work)\n \ttxgbe_module_detection_subtask(wx);\n \ttxgbe_link_config_subtask(wx);\n \twx_update_stats(wx);\n+\twx_check_hang_subtask(wx);\n+\twx_check_err_subtask(wx);\n \n \twx_service_event_complete(wx);\n }\n@@ -144,6 +163,7 @@ static void txgbe_up_complete(struct wx *wx)\n \t/* make sure to complete pre-operations */\n \tsmp_mb__before_atomic();\n \tclear_bit(WX_STATE_DOWN, wx-\u003estate);\n+\tclear_bit(WX_STATE_RES_FREED, wx-\u003estate);\n \twx_napi_enable_all(wx);\n \n \tswitch (wx-\u003emac.type) {\n@@ -187,6 +207,9 @@ static void txgbe_reset(struct wx *wx)\n \tu8 old_addr[ETH_ALEN];\n \tint err;\n \n+\tif (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx-\u003eflags))\n+\t\treturn;\n+\n \terr = txgbe_reset_hw(wx);\n \tif (err != 0)\n \t\twx_err(wx, \"Hardware Error: %d\\n\", err);\n@@ -224,6 +247,7 @@ static void txgbe_disable_device(struct wx *wx)\n \twx_irq_disable(wx);\n \twx_napi_disable_all(wx);\n \n+\tclear_bit(WX_FLAG_NEED_DO_RESET, wx-\u003eflags);\n \ttimer_delete_sync(\u0026wx-\u003eservice_timer);\n \tcancel_work_sync(\u0026wx-\u003eservice_task);\n \n@@ -292,6 +316,20 @@ void txgbe_up(struct wx *wx)\n \ttxgbe_up_complete(wx);\n }\n \n+static void txgbe_down_suspend(struct wx *wx)\n+{\n+\tif (test_and_set_bit(WX_STATE_RES_FREED, wx-\u003estate))\n+\t\treturn;\n+\n+\tphylink_stop(wx-\u003ephylink);\n+\twx_clean_all_tx_rings(wx);\n+\twx_clean_all_rx_rings(wx);\n+\twx_free_irq(wx);\n+\ttxgbe_free_misc_irq(wx-\u003epriv);\n+\twx_free_resources(wx);\n+\ttxgbe_fdir_filter_exit(wx);\n+}\n+\n /**\n  *  txgbe_init_type_code - Initialize the shared code\n  *  @wx: pointer to hardware structure\n@@ -408,6 +446,7 @@ static int txgbe_sw_init(struct wx *wx)\n \n \twx-\u003esetup_tc = txgbe_setup_tc;\n \twx-\u003edo_reset = txgbe_do_reset;\n+\twx-\u003edown_suspend = txgbe_down_suspend;\n \tset_bit(0, \u0026wx-\u003efwd_bitmask);\n \n \tswitch (wx-\u003emac.type) {\n@@ -518,12 +557,16 @@ static int txgbe_close(struct net_device *netdev)\n {\n \tstruct wx *wx = netdev_priv(netdev);\n \n+\tif (test_bit(WX_STATE_RES_FREED, wx-\u003estate))\n+\t\tgoto out;\n+\n \twx_ptp_stop(wx);\n \ttxgbe_down(wx);\n \twx_free_irq(wx);\n \ttxgbe_free_misc_irq(wx-\u003epriv);\n \twx_free_resources(wx);\n \ttxgbe_fdir_filter_exit(wx);\n+out:\n \twx_control_hw(wx, false);\n \n \treturn 0;\n@@ -544,7 +587,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev)\n \n \twx_control_hw(wx, false);\n \n-\tpci_disable_device(pdev);\n+\tif (!test_and_set_bit(WX_STATE_DISABLED, wx-\u003estate))\n+\t\tpci_disable_device(pdev);\n }\n \n static void txgbe_shutdown(struct pci_dev *pdev)\n@@ -606,11 +650,11 @@ static void txgbe_reinit_locked(struct wx *wx)\n \tmutex_unlock(\u0026wx-\u003ereset_lock);\n }\n \n-void txgbe_do_reset(struct net_device *netdev)\n+void txgbe_do_reset(struct net_device *netdev, bool reinit)\n {\n \tstruct wx *wx = netdev_priv(netdev);\n \n-\tif (netif_running(netdev))\n+\tif (netif_running(netdev) \u0026\u0026 reinit)\n \t\ttxgbe_reinit_locked(wx);\n \telse\n \t\ttxgbe_reset(wx);\n@@ -654,6 +698,7 @@ static const struct net_device_ops txgbe_netdev_ops = {\n \t.ndo_stop               = txgbe_close,\n \t.ndo_change_mtu         = wx_change_mtu,\n \t.ndo_start_xmit         = wx_xmit_frame,\n+\t.ndo_tx_timeout         = wx_tx_timeout,\n \t.ndo_set_rx_mode        = wx_set_rx_mode,\n \t.ndo_set_features       = wx_set_features,\n \t.ndo_fix_features       = wx_fix_features,\n@@ -814,6 +859,10 @@ static int txgbe_probe(struct pci_dev *pdev,\n \teth_hw_addr_set(netdev, wx-\u003emac.perm_addr);\n \twx_mac_set_default_filter(wx, wx-\u003emac.perm_addr);\n \n+\terr = wx_init_err_task(wx);\n+\tif (err)\n+\t\tgoto err_free_mac_table;\n+\n \ttxgbe_init_service(wx);\n \n \terr = wx_init_interrupt_scheme(wx);\n@@ -889,6 +938,7 @@ static int txgbe_probe(struct pci_dev *pdev,\n \t\tgoto err_remove_phy;\n \n \tpci_set_drvdata(pdev, wx);\n+\tpci_save_state(pdev);\n \n \tnetif_tx_stop_all_queues(netdev);\n \n@@ -916,6 +966,8 @@ static int txgbe_probe(struct pci_dev *pdev,\n err_cancel_service:\n \ttimer_delete_sync(\u0026wx-\u003eservice_timer);\n \tcancel_work_sync(\u0026wx-\u003eservice_task);\n+\tcancel_work_sync(\u0026wx-\u003ereset_task);\n+\tdestroy_workqueue(wx-\u003ereset_wq);\n err_free_mac_table:\n \tkfree(wx-\u003erss_key);\n \tkfree(wx-\u003emac_table);\n@@ -949,6 +1001,8 @@ static void txgbe_remove(struct pci_dev *pdev)\n \n \ttimer_shutdown_sync(\u0026wx-\u003eservice_timer);\n \tcancel_work_sync(\u0026wx-\u003eservice_task);\n+\tcancel_work_sync(\u0026wx-\u003ereset_task);\n+\tdestroy_workqueue(wx-\u003ereset_wq);\n \n \ttxgbe_remove_phy(txgbe);\n \twx_free_isb_resources(wx);\n@@ -960,7 +1014,8 @@ static void txgbe_remove(struct pci_dev *pdev)\n \tkfree(wx-\u003emac_table);\n \twx_clear_interrupt_scheme(wx);\n \n-\tpci_disable_device(pdev);\n+\tif (!test_and_set_bit(WX_STATE_DISABLED, wx-\u003estate))\n+\t\tpci_disable_device(pdev);\n }\n \n static struct pci_driver txgbe_driver = {\n@@ -970,6 +1025,7 @@ static struct pci_driver txgbe_driver = {\n \t.remove   = txgbe_remove,\n \t.shutdown = txgbe_shutdown,\n \t.sriov_configure = wx_pci_sriov_configure,\n+\t.err_handler = \u0026wx_err_handler,\n };\n \n module_pci_driver(txgbe_driver);\ndiff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h\nindex 877234e3fdc2b..3e93a3f309c16 100644\n--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h\n+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h\n@@ -313,7 +313,7 @@ extern char txgbe_driver_name[];\n void txgbe_down(struct wx *wx);\n void txgbe_up(struct wx *wx);\n int txgbe_setup_tc(struct net_device *dev, u8 tc);\n-void txgbe_do_reset(struct net_device *netdev);\n+void txgbe_do_reset(struct net_device *netdev, bool reinit);\n \n #define DECLARE_PHY_INTERFACE_MASK_ZERO(name) \\\n \tunsigned long name[PHY_INTERFACE_MODE_MAX] = { 0, }\n"
}

2/1 2026/08/03 07:07 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Wangxun (ngbe/txgbe) physical PCIe ethernet drivers to add PCI error handling and Tx timeout recovery. These are vendor-specific hardware drivers that require physical PCIe cards not emulated in standard QEMU environments, making the modified code paths unreachable during fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 8b4b5791f72d40a4e9ebe606a39d39963e5cd6a8
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 3 07:07:56 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/ethernet/wangxun/libwx/Makefile b/drivers/net/ethernet/wangxun/libwx/Makefile
index a71b0ad77de3f..c8724bb129aa0 100644
--- a/drivers/net/ethernet/wangxun/libwx/Makefile
+++ b/drivers/net/ethernet/wangxun/libwx/Makefile
@@ -4,5 +4,5 @@
 
 obj-$(CONFIG_LIBWX) += libwx.o
 
-libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o
+libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o wx_err.o
 libwx-objs += wx_vf.o wx_vf_lib.o wx_vf_common.o
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
new file mode 100644
index 0000000000000..b56fbdc959dec
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd. */
+/* Copyright (c) 1999 - 2026 Intel Corporation. */
+
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/aer.h>
+
+#include "wx_type.h"
+#include "wx_lib.h"
+#include "wx_err.h"
+
+/**
+ * wx_io_error_detected - called when PCI error is detected
+ * @pdev: Pointer to PCI device
+ * @state: The current pci connection state
+ *
+ * Return: pci_ers_result_t.
+ *
+ * This function is called after a PCI bus error affecting
+ * this device has been detected.
+ */
+static pci_ers_result_t wx_io_error_detected(struct pci_dev *pdev,
+					     pci_channel_state_t state)
+{
+	struct wx *wx = pci_get_drvdata(pdev);
+	struct net_device *netdev;
+
+	if (!wx)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	netdev = wx->netdev;
+	if (!netif_device_present(netdev))
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	rtnl_lock();
+	netif_device_detach(netdev);
+	set_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
+	wx_soft_quiesce(wx);
+
+	if (state == pci_channel_io_perm_failure) {
+		rtnl_unlock();
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+		pci_disable_device(pdev);
+	rtnl_unlock();
+
+	/* Request a slot reset. */
+	return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * wx_io_slot_reset - called after the pci bus has been reset.
+ * @pdev: Pointer to PCI device
+ *
+ * Return: pci_ers_result_t.
+ *
+ * Restart the card from scratch, as if from a cold-boot.
+ */
+static pci_ers_result_t wx_io_slot_reset(struct pci_dev *pdev)
+{
+	struct wx *wx = pci_get_drvdata(pdev);
+
+	if (pci_enable_device_mem(pdev)) {
+		wx_err(wx, "Cannot re-enable PCI device after reset.\n");
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	/* make all memory operations done before clearing the flag */
+	smp_mb__before_atomic();
+	clear_bit(WX_STATE_DISABLED, wx->state);
+	clear_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
+	pci_set_master(pdev);
+	pci_restore_state(pdev);
+	pci_wake_from_d3(pdev, false);
+
+	rtnl_lock();
+	if (netif_running(wx->netdev) && wx->down_suspend)
+		wx->down_suspend(wx);
+	if (wx->do_reset)
+		wx->do_reset(wx->netdev, false);
+	rtnl_unlock();
+
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * wx_io_resume - called when traffic can start flowing again.
+ * @pdev: Pointer to PCI device
+ *
+ * This callback is called when the error recovery driver tells us that
+ * its OK to resume normal operation.
+ */
+static void wx_io_resume(struct pci_dev *pdev)
+{
+	struct wx *wx = pci_get_drvdata(pdev);
+	struct net_device *netdev;
+	int err;
+
+	netdev = wx->netdev;
+	rtnl_lock();
+	if (netif_running(netdev)) {
+		err = netdev->netdev_ops->ndo_open(netdev);
+		if (err) {
+			wx_err(wx, "Failed to open netdev after reset\n");
+			goto out;
+		}
+	}
+	netif_device_attach(netdev);
+out:
+	rtnl_unlock();
+}
+
+const struct pci_error_handlers wx_err_handler = {
+	.error_detected = wx_io_error_detected,
+	.slot_reset = wx_io_slot_reset,
+	.resume = wx_io_resume,
+};
+EXPORT_SYMBOL(wx_err_handler);
+
+static bool wx_check_pcie_error(struct wx *wx)
+{
+	u16 vid, pci_cmd;
+
+	pci_read_config_word(wx->pdev, PCI_VENDOR_ID, &vid);
+	pci_read_config_word(wx->pdev, PCI_COMMAND, &pci_cmd);
+
+	/* PCIe link loss or memory space can't access */
+	if (vid == U16_MAX || !(pci_cmd & PCI_COMMAND_MEMORY))
+		return true;
+
+	return false;
+}
+
+static void wx_pf_reset_subtask(struct wx *wx)
+{
+	if (!test_and_clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags))
+		return;
+
+	wx_warn(wx, "Reset adapter.\n");
+	if (wx->do_reset)
+		wx->do_reset(wx->netdev, true);
+}
+
+static void wx_reset_task(struct work_struct *work)
+{
+	struct wx *wx = container_of(work, struct wx, reset_task);
+
+	rtnl_lock();
+
+	/* If the device has been detached (e.g., due to AER error handling),
+	 * abort the reset task to prevent operating on a dead or unmanaged
+	 * hardware.
+	 */
+	if (!netif_device_present(wx->netdev))
+		goto out;
+
+	if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags)) {
+		/* Double check: Verify if the PCIe error is still present. */
+		if (wx_check_pcie_error(wx))
+			wx_soft_quiesce(wx);
+		else
+			clear_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
+		goto out;
+	}
+
+	if (test_bit(WX_STATE_DOWN, wx->state) ||
+	    test_bit(WX_STATE_RESETTING, wx->state))
+		goto out;
+
+	wx_pf_reset_subtask(wx);
+
+out:
+	rtnl_unlock();
+}
+
+void wx_check_err_subtask(struct wx *wx)
+{
+	if (test_bit(WX_FLAG_NEED_DO_RESET, wx->flags))
+		queue_work(wx->reset_wq, &wx->reset_task);
+}
+EXPORT_SYMBOL(wx_check_err_subtask);
+
+int wx_init_err_task(struct wx *wx)
+{
+	wx->reset_wq = alloc_workqueue("%s_reset_wq_%x", WQ_UNBOUND | WQ_HIGHPRI,
+				       1, wx->driver_name, pci_dev_id(wx->pdev));
+	if (!wx->reset_wq) {
+		wx_err(wx, "Failed to create wx_reset_wq workqueue\n");
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&wx->reset_task, wx_reset_task);
+	return 0;
+}
+EXPORT_SYMBOL(wx_init_err_task);
+
+static bool wx_ring_tx_pending(struct wx *wx)
+{
+	int i;
+
+	for (i = 0; i < wx->num_tx_queues; i++) {
+		struct wx_ring *tx_ring = wx->tx_ring[i];
+
+		if (tx_ring->next_to_use != tx_ring->next_to_clean)
+			return true;
+	}
+
+	return false;
+}
+
+static bool wx_vf_tx_pending(struct wx *wx)
+{
+	struct wx_ring_feature *vmdq = &wx->ring_feature[RING_F_VMDQ];
+	u32 q_per_pool = __ALIGN_MASK(1, ~vmdq->mask);
+	u32 i, j;
+
+	if (!wx->num_vfs)
+		return false;
+
+	for (i = 0; i < wx->num_vfs; i++) {
+		for (j = 0; j < q_per_pool; j++) {
+			u32 h, t;
+
+			h = rd32(wx, WX_PX_TR_RP_PV(q_per_pool, i, j));
+			t = rd32(wx, WX_PX_TR_WP_PV(q_per_pool, i, j));
+
+			if (h != t)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+static void wx_watchdog_flush_tx(struct wx *wx)
+{
+	if (!netif_running(wx->netdev))
+		return;
+	if (netif_carrier_ok(wx->netdev))
+		return;
+
+	if (wx_ring_tx_pending(wx) || wx_vf_tx_pending(wx)) {
+		/* We've lost link, so the controller stops DMA,
+		 * but we've got queued Tx work that's never going
+		 * to get done, so reset controller to flush Tx.
+		 * (Do the reset outside of interrupt context).
+		 */
+		wx_warn(wx, "initiating reset due to lost link with pending Tx work\n");
+		set_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
+	}
+}
+
+static void wx_detect_tx_hang(struct wx *wx)
+{
+	int i;
+
+	/* If we're down or resetting, just bail */
+	if (!netif_running(wx->netdev) ||
+	    test_bit(WX_STATE_RESETTING, wx->state))
+		return;
+
+	/* Force detection of hung controller */
+	if (netif_carrier_ok(wx->netdev)) {
+		for (i = 0; i < wx->num_tx_queues; i++)
+			set_bit(WX_TX_DETECT_HANG, wx->tx_ring[i]->state);
+	}
+}
+
+void wx_check_hang_subtask(struct wx *wx)
+{
+	if (test_bit(WX_STATE_DOWN, wx->state) ||
+	    test_bit(WX_STATE_RESETTING, wx->state))
+		return;
+
+	wx_watchdog_flush_tx(wx);
+	wx_detect_tx_hang(wx);
+}
+EXPORT_SYMBOL(wx_check_hang_subtask);
+
+static void wx_tx_timeout_recovery(struct wx *wx)
+{
+	/*
+	 * When a PCIe hardware error occurs, the driver should initiate a PCIe
+	 * recovery mechanism. However, this recovery flow relies on the AER
+	 * driver for current kernel policy. Therefore, a self-contained
+	 * recovery mechanism is not implemented yet.
+	 */
+	set_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
+	wx_err(wx, "PCIe error detected during tx timeout\n");
+	queue_work(wx->reset_wq, &wx->reset_task);
+}
+
+static void wx_tx_timeout_reset(struct wx *wx)
+{
+	if (test_bit(WX_STATE_DOWN, wx->state))
+		return;
+
+	set_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
+	wx_warn(wx, "initiating reset due to tx timeout\n");
+	wx_service_event_schedule(wx);
+}
+
+void wx_tx_timeout(struct net_device *netdev, unsigned int __always_unused txqueue)
+{
+	struct wx *wx = netdev_priv(netdev);
+
+	if (wx_check_pcie_error(wx))
+		wx_tx_timeout_recovery(wx);
+	else
+		wx_tx_timeout_reset(wx);
+}
+EXPORT_SYMBOL(wx_tx_timeout);
+
+void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next)
+{
+	struct wx *wx = netdev_priv(tx_ring->netdev);
+
+	wx_warn(wx,
+		"Detected Tx Unit Hang: Queue %d, TDH %x, TDT %x, ntu %x, ntc %x, ntc.time_stamp %lx, jiffies %lx\n",
+		tx_ring->queue_index,
+		rd32(wx, WX_PX_TR_RP(tx_ring->reg_idx)),
+		rd32(wx, WX_PX_TR_WP(tx_ring->reg_idx)),
+		tx_ring->next_to_use, next,
+		tx_ring->tx_buffer_info[next].time_stamp, jiffies);
+
+	netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
+
+	wx_tx_timeout_reset(wx);
+}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h
new file mode 100644
index 0000000000000..a6a82a2635284
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * WangXun Gigabit PCI Express Linux driver
+ * Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd.
+ */
+
+#ifndef _WX_ERR_H_
+#define _WX_ERR_H_
+
+extern const struct pci_error_handlers wx_err_handler;
+
+void wx_check_err_subtask(struct wx *wx);
+int wx_init_err_task(struct wx *wx);
+void wx_check_hang_subtask(struct wx *wx);
+void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue);
+void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next);
+
+#endif /* _WX_ERR_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index 22037f015ded1..940d2e59876c7 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -397,7 +397,7 @@ static void wx_update_rsc(struct wx *wx)
 
 	/* reset the device to apply the new RSC setting */
 	if (need_reset && wx->do_reset)
-		wx->do_reset(netdev);
+		wx->do_reset(netdev, true);
 }
 
 int wx_set_coalesce(struct net_device *netdev,
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 260e14d5d5412..122c4952d203f 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -1932,6 +1932,7 @@ static void wx_configure_tx_ring(struct wx *wx,
 	else
 		ring->atr_sample_rate = 0;
 
+	bitmap_zero(ring->state, WX_RING_STATE_NBITS);
 	/* reinitialize tx_buffer_info */
 	memset(ring->tx_buffer_info, 0,
 	       sizeof(struct wx_tx_buffer) * ring->count);
@@ -2851,16 +2852,26 @@ EXPORT_SYMBOL(wx_fc_enable);
 static void wx_update_xoff_rx_lfc(struct wx *wx)
 {
 	struct wx_hw_stats *hwstats = &wx->stats;
+	u64 data;
+	int i;
 
 	if (wx->fc.mode != wx_fc_full &&
 	    wx->fc.mode != wx_fc_rx_pause)
 		return;
 
 	if (wx->mac.type >= wx_mac_aml)
-		hwstats->lxoffrxc += rd32_wrap(wx, WX_MAC_LXOFFRXC_AML,
-					       &wx->last_stats.lxoffrxc);
+		data = rd32_wrap(wx, WX_MAC_LXOFFRXC_AML,
+				 &wx->last_stats.lxoffrxc);
 	else
-		hwstats->lxoffrxc += rd64(wx, WX_MAC_LXOFFRXC);
+		data = rd64(wx, WX_MAC_LXOFFRXC);
+	hwstats->lxoffrxc += data;
+
+	/* refill credits (no tx hang) if we received xoff */
+	if (!data)
+		return;
+
+	for (i = 0; i < wx->num_tx_queues; i++)
+		clear_bit(WX_HANG_CHECK_ARMED, wx->tx_ring[i]->state);
 }
 
 /**
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 476b71049f7e6..ed5aad7857bd9 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -14,6 +14,7 @@
 
 #include "wx_type.h"
 #include "wx_lib.h"
+#include "wx_err.h"
 #include "wx_ptp.h"
 #include "wx_hw.h"
 #include "wx_vf_lib.h"
@@ -742,6 +743,37 @@ static struct netdev_queue *wx_txring_txq(const struct wx_ring *ring)
 	return netdev_get_tx_queue(ring->netdev, ring->queue_index);
 }
 
+static u32 wx_get_tx_pending(struct wx_ring *ring)
+{
+	unsigned int head, tail;
+
+	head = ring->next_to_clean;
+	tail = ring->next_to_use;
+
+	return ((head <= tail) ? tail : tail + ring->count) - head;
+}
+
+static bool wx_check_tx_hang(struct wx_ring *ring)
+{
+	u32 tx_done_old = ring->tx_stats.tx_done_old;
+	u32 tx_pending = wx_get_tx_pending(ring);
+	u32 tx_done = ring->stats.packets;
+
+	if (!test_and_clear_bit(WX_TX_DETECT_HANG, ring->state))
+		return false;
+
+	if (tx_done_old == tx_done && tx_pending)
+		/* make sure it is true for two checks in a row */
+		return test_and_set_bit(WX_HANG_CHECK_ARMED, ring->state);
+
+	/* update completed stats and continue */
+	ring->tx_stats.tx_done_old = tx_done;
+	/* reset the countdown */
+	clear_bit(WX_HANG_CHECK_ARMED, ring->state);
+
+	return false;
+}
+
 /**
  * wx_clean_tx_irq - Reclaim resources after transmit completes
  * @q_vector: structure containing interrupt and ring information
@@ -866,6 +898,11 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector,
 	netdev_tx_completed_queue(wx_txring_txq(tx_ring),
 				  total_packets, total_bytes);
 
+	if (wx_check_tx_hang(tx_ring)) {
+		wx_handle_tx_hang(tx_ring, i);
+		return true;
+	}
+
 #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
 	if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) &&
 		     (wx_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) {
@@ -3114,7 +3151,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
 	netdev->features = features;
 
 	if (changed & NETIF_F_HW_VLAN_CTAG_RX && wx->do_reset)
-		wx->do_reset(netdev);
+		wx->do_reset(netdev, true);
 	else if (changed & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER))
 		wx_set_rx_mode(netdev);
 
@@ -3164,7 +3201,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
 
 out:
 	if (need_reset && wx->do_reset)
-		wx->do_reset(netdev);
+		wx->do_reset(netdev, true);
 
 	return 0;
 }
@@ -3373,5 +3410,23 @@ void wx_service_timer(struct timer_list *t)
 }
 EXPORT_SYMBOL(wx_service_timer);
 
+void wx_soft_quiesce(struct wx *wx)
+{
+	if (!netif_running(wx->netdev) ||
+	    test_and_set_bit(WX_STATE_DOWN, wx->state))
+		return;
+
+	pci_clear_master(wx->pdev);
+	netif_tx_stop_all_queues(wx->netdev);
+	netif_carrier_off(wx->netdev);
+	netif_tx_disable(wx->netdev);
+	wx_napi_disable_all(wx);
+	wx_ptp_quiesce(wx);
+
+	clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
+	timer_delete_sync(&wx->service_timer);
+}
+EXPORT_SYMBOL(wx_soft_quiesce);
+
 MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
 MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
index bc671786978e5..9d85d399e17f4 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
@@ -41,5 +41,6 @@ int wx_set_ring(struct wx *wx, u32 new_tx_count,
 void wx_service_event_schedule(struct wx *wx);
 void wx_service_event_complete(struct wx *wx);
 void wx_service_timer(struct timer_list *t);
+void wx_soft_quiesce(struct wx *wx);
 
 #endif /* _WX_LIB_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
index 44f3e65052468..3eea647c4742c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
@@ -321,6 +321,9 @@ static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
 	struct wx *wx = container_of(ptp, struct wx, ptp_caps);
 	int ts_done;
 
+	if (!test_bit(WX_STATE_PTP_RUNNING, wx->state))
+		return HZ;
+
 	ts_done = wx_ptp_tx_hwtstamp_work(wx);
 
 	wx_ptp_overflow_check(wx);
@@ -842,6 +845,30 @@ void wx_ptp_stop(struct wx *wx)
 }
 EXPORT_SYMBOL(wx_ptp_stop);
 
+void wx_ptp_quiesce(struct wx *wx)
+{
+	if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
+		return;
+
+	clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
+
+	if (wx->ptp_clock)
+		ptp_cancel_worker_sync(wx->ptp_clock);
+
+	if (wx->ptp_tx_skb) {
+		dev_kfree_skb_any(wx->ptp_tx_skb);
+		wx->ptp_tx_skb = NULL;
+	}
+	clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
+
+	if (wx->ptp_clock) {
+		ptp_clock_unregister(wx->ptp_clock);
+		wx->ptp_clock = NULL;
+		dev_info(&wx->pdev->dev, "removed PHC on %s\n", wx->netdev->name);
+	}
+}
+EXPORT_SYMBOL(wx_ptp_quiesce);
+
 /**
  * wx_ptp_rx_hwtstamp - utility function which checks for RX time stamp
  * @wx: pointer to wx struct
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
index 50db90a6e3ee6..ad2f824875d51 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
@@ -10,6 +10,7 @@ void wx_ptp_reset(struct wx *wx);
 void wx_ptp_init(struct wx *wx);
 void wx_ptp_suspend(struct wx *wx);
 void wx_ptp_stop(struct wx *wx);
+void wx_ptp_quiesce(struct wx *wx);
 void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);
 int wx_hwtstamp_get(struct net_device *dev,
 		    struct kernel_hwtstamp_config *cfg);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 65e3e55db1cff..2eba5ab599252 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -450,6 +450,11 @@ enum WX_MSCA_CMD_value {
 #define WX_PX_TR_CFG_THRE_SHIFT      8
 #define WX_PX_TR_CFG_HEAD_WB         BIT(27)
 
+#define WX_PX_TR_RP_PV(q_per_pool, vf_number, vf_q_index) \
+		(WX_PX_TR_RP((q_per_pool) * (vf_number) + (vf_q_index)))
+#define WX_PX_TR_WP_PV(q_per_pool, vf_number, vf_q_index) \
+		(WX_PX_TR_WP((q_per_pool) * (vf_number) + (vf_q_index)))
+
 /* Receive DMA Registers */
 #define WX_PX_RR_BAL(_i)             (0x01000 + ((_i) * 0x40))
 #define WX_PX_RR_BAH(_i)             (0x01004 + ((_i) * 0x40))
@@ -1040,6 +1045,7 @@ struct wx_queue_stats {
 struct wx_tx_queue_stats {
 	u64 restart_queue;
 	u64 tx_busy;
+	u32 tx_done_old;
 };
 
 struct wx_rx_queue_stats {
@@ -1055,6 +1061,12 @@ struct wx_rx_queue_stats {
 #define wx_for_each_ring(posm, headm) \
 	for (posm = (headm).ring; posm; posm = posm->next)
 
+enum wx_ring_state {
+	WX_TX_DETECT_HANG,
+	WX_HANG_CHECK_ARMED,
+	WX_RING_STATE_NBITS
+};
+
 struct wx_ring_container {
 	struct wx_ring *ring;           /* pointer to linked list of rings */
 	unsigned int total_bytes;       /* total bytes processed this int */
@@ -1074,6 +1086,7 @@ struct wx_ring {
 		struct wx_tx_buffer *tx_buffer_info;
 		struct wx_rx_buffer *rx_buffer_info;
 	};
+	DECLARE_BITMAP(state, WX_RING_STATE_NBITS);
 	u8 __iomem *tail;
 	dma_addr_t dma;                 /* phys. address of descriptor ring */
 	dma_addr_t headwb_dma;
@@ -1209,6 +1222,8 @@ enum wx_state {
 	WX_STATE_PTP_RUNNING,
 	WX_STATE_PTP_TX_IN_PROGRESS,
 	WX_STATE_SERVICE_SCHED,
+	WX_STATE_DISABLED,
+	WX_STATE_RES_FREED,
 	WX_STATE_NBITS		/* must be last */
 };
 
@@ -1275,6 +1290,7 @@ enum wx_pf_flags {
 	WX_FLAG_NEED_DO_RESET,
 	WX_FLAG_RX_MERGE_ENABLED,
 	WX_FLAG_TXHEAD_WB_ENABLED,
+	WX_FLAG_NEED_PCIE_RECOVERY,
 	WX_PF_FLAGS_NBITS               /* must be last */
 };
 
@@ -1395,7 +1411,8 @@ struct wx {
 	void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
 	void (*configure_fdir)(struct wx *wx);
 	int (*setup_tc)(struct net_device *netdev, u8 tc);
-	void (*do_reset)(struct net_device *netdev);
+	void (*do_reset)(struct net_device *netdev, bool reinit);
+	void (*down_suspend)(struct wx *wx);
 	int (*ptp_setup_sdp)(struct wx *wx);
 	void (*set_num_queues)(struct wx *wx);
 
@@ -1423,6 +1440,8 @@ struct wx {
 
 	struct timer_list service_timer;
 	struct work_struct service_task;
+	struct work_struct reset_task;
+	struct workqueue_struct *reset_wq;
 	struct mutex reset_lock; /* mutex for reset */
 };
 
@@ -1505,7 +1524,8 @@ rd32_wrap(struct wx *wx, u32 reg, u32 *last)
 
 #define wx_err(wx, fmt, arg...) \
 	dev_err(&(wx)->pdev->dev, fmt, ##arg)
-
+#define wx_warn(wx, fmt, arg...) \
+	dev_warn(&(wx)->pdev->dev, fmt, ##arg)
 #define wx_dbg(wx, fmt, arg...) \
 	dev_dbg(&(wx)->pdev->dev, fmt, ##arg)
 
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
index b2e1919828032..1960f7154151d 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
@@ -59,7 +59,6 @@ static int ngbe_set_ringparam(struct net_device *netdev,
 	wx_set_ring(wx, new_tx_count, new_rx_count, temp_ring);
 	kvfree(temp_ring);
 
-	wx_configure(wx);
 	ngbe_up(wx);
 
 clear_reset:
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index a16221995909f..14c00228cede4 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -14,6 +14,7 @@
 #include "../libwx/wx_type.h"
 #include "../libwx/wx_hw.h"
 #include "../libwx/wx_lib.h"
+#include "../libwx/wx_err.h"
 #include "../libwx/wx_ptp.h"
 #include "../libwx/wx_mbx.h"
 #include "../libwx/wx_sriov.h"
@@ -46,6 +47,20 @@ static const struct pci_device_id ngbe_pci_tbl[] = {
 	{ }
 };
 
+static void ngbe_down_suspend(struct wx *wx)
+{
+	if (test_and_set_bit(WX_STATE_RES_FREED, wx->state))
+		return;
+
+	phylink_stop(wx->phylink);
+	phylink_disconnect_phy(wx->phylink);
+	wx_clean_all_tx_rings(wx);
+	wx_clean_all_rx_rings(wx);
+	wx_free_irq(wx);
+	wx_free_isb_resources(wx);
+	wx_free_resources(wx);
+}
+
 /**
  *  ngbe_init_type_code - Initialize the shared code
  *  @wx: pointer to hardware structure
@@ -133,6 +148,8 @@ static int ngbe_sw_init(struct wx *wx)
 
 	wx->mbx.size = WX_VXMAILBOX_SIZE;
 	wx->setup_tc = ngbe_setup_tc;
+	wx->do_reset = ngbe_do_reset;
+	wx->down_suspend = ngbe_down_suspend;
 	set_bit(0, &wx->fwd_bitmask);
 
 	return 0;
@@ -147,6 +164,8 @@ static void ngbe_service_task(struct work_struct *work)
 	struct wx *wx = container_of(work, struct wx, service_task);
 
 	wx_update_stats(wx);
+	wx_check_hang_subtask(wx);
+	wx_check_err_subtask(wx);
 
 	wx_service_event_complete(wx);
 }
@@ -392,6 +411,7 @@ static void ngbe_disable_device(struct wx *wx)
 	netif_tx_stop_all_queues(netdev);
 	netif_tx_disable(netdev);
 
+	clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
 	timer_delete_sync(&wx->service_timer);
 	cancel_work_sync(&wx->service_task);
 
@@ -408,6 +428,9 @@ static void ngbe_disable_device(struct wx *wx)
 
 static void ngbe_reset(struct wx *wx)
 {
+	if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
+		return;
+
 	wx_flush_sw_mac_table(wx);
 	wx_mac_set_default_filter(wx, wx->mac.addr);
 	if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
@@ -423,13 +446,14 @@ void ngbe_down(struct wx *wx)
 	wx_clean_all_rx_rings(wx);
 }
 
-void ngbe_up(struct wx *wx)
+static void ngbe_up_complete(struct wx *wx)
 {
 	wx_configure_vectors(wx);
 
 	/* make sure to complete pre-operations */
 	smp_mb__before_atomic();
 	clear_bit(WX_STATE_DOWN, wx->state);
+	clear_bit(WX_STATE_RES_FREED, wx->state);
 	wx_napi_enable_all(wx);
 	/* enable transmits */
 	netif_tx_start_all_queues(wx->netdev);
@@ -490,7 +514,7 @@ static int ngbe_open(struct net_device *netdev)
 
 	wx_ptp_init(wx);
 
-	ngbe_up(wx);
+	ngbe_up_complete(wx);
 
 	return 0;
 err_dis_phy:
@@ -503,6 +527,12 @@ static int ngbe_open(struct net_device *netdev)
 	return err;
 }
 
+void ngbe_up(struct wx *wx)
+{
+	wx_configure(wx);
+	ngbe_up_complete(wx);
+}
+
 /**
  * ngbe_close - Disables a network interface
  * @netdev: network interface device structure
@@ -518,12 +548,16 @@ static int ngbe_close(struct net_device *netdev)
 {
 	struct wx *wx = netdev_priv(netdev);
 
+	if (test_bit(WX_STATE_RES_FREED, wx->state))
+		goto out;
+
 	wx_ptp_stop(wx);
 	ngbe_down(wx);
 	wx_free_irq(wx);
 	wx_free_isb_resources(wx);
 	wx_free_resources(wx);
 	phylink_disconnect_phy(wx->phylink);
+out:
 	wx_control_hw(wx, false);
 
 	return 0;
@@ -555,7 +589,8 @@ static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
 	*enable_wake = !!wufc;
 	wx_control_hw(wx, false);
 
-	pci_disable_device(pdev);
+	if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+		pci_disable_device(pdev);
 }
 
 static void ngbe_shutdown(struct pci_dev *pdev)
@@ -590,6 +625,8 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc)
 	 */
 	if (netif_running(dev))
 		ngbe_close(dev);
+	else
+		ngbe_reset(wx);
 
 	wx_clear_interrupt_scheme(wx);
 
@@ -606,11 +643,36 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc)
 	return 0;
 }
 
+static void ngbe_reinit_locked(struct wx *wx)
+{
+	netif_trans_update(wx->netdev);
+
+	mutex_lock(&wx->reset_lock);
+	set_bit(WX_STATE_RESETTING, wx->state);
+
+	ngbe_down(wx);
+	ngbe_up(wx);
+
+	clear_bit(WX_STATE_RESETTING, wx->state);
+	mutex_unlock(&wx->reset_lock);
+}
+
+void ngbe_do_reset(struct net_device *netdev, bool reinit)
+{
+	struct wx *wx = netdev_priv(netdev);
+
+	if (netif_running(netdev) && reinit)
+		ngbe_reinit_locked(wx);
+	else
+		ngbe_reset(wx);
+}
+
 static const struct net_device_ops ngbe_netdev_ops = {
 	.ndo_open               = ngbe_open,
 	.ndo_stop               = ngbe_close,
 	.ndo_change_mtu         = wx_change_mtu,
 	.ndo_start_xmit         = wx_xmit_frame,
+	.ndo_tx_timeout         = wx_tx_timeout,
 	.ndo_set_rx_mode        = wx_set_rx_mode,
 	.ndo_set_features       = wx_set_features,
 	.ndo_fix_features       = wx_fix_features,
@@ -796,6 +858,10 @@ static int ngbe_probe(struct pci_dev *pdev,
 	eth_hw_addr_set(netdev, wx->mac.perm_addr);
 	wx_mac_set_default_filter(wx, wx->mac.perm_addr);
 
+	err = wx_init_err_task(wx);
+	if (err)
+		goto err_free_mac_table;
+
 	ngbe_init_service(wx);
 
 	err = wx_init_interrupt_scheme(wx);
@@ -812,6 +878,7 @@ static int ngbe_probe(struct pci_dev *pdev,
 		goto err_register;
 
 	pci_set_drvdata(pdev, wx);
+	pci_save_state(pdev);
 
 	return 0;
 
@@ -823,6 +890,8 @@ static int ngbe_probe(struct pci_dev *pdev,
 err_cancel_service:
 	timer_delete_sync(&wx->service_timer);
 	cancel_work_sync(&wx->service_task);
+	cancel_work_sync(&wx->reset_task);
+	destroy_workqueue(wx->reset_wq);
 err_free_mac_table:
 	kfree(wx->rss_key);
 	kfree(wx->mac_table);
@@ -854,6 +923,8 @@ static void ngbe_remove(struct pci_dev *pdev)
 
 	timer_shutdown_sync(&wx->service_timer);
 	cancel_work_sync(&wx->service_task);
+	cancel_work_sync(&wx->reset_task);
+	destroy_workqueue(wx->reset_wq);
 
 	phylink_destroy(wx->phylink);
 	pci_release_selected_regions(pdev,
@@ -863,7 +934,8 @@ static void ngbe_remove(struct pci_dev *pdev)
 	kfree(wx->mac_table);
 	wx_clear_interrupt_scheme(wx);
 
-	pci_disable_device(pdev);
+	if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+		pci_disable_device(pdev);
 }
 
 static int ngbe_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -890,6 +962,7 @@ static int ngbe_resume(struct pci_dev *pdev)
 		wx_err(wx, "Cannot enable PCI device from suspend\n");
 		return err;
 	}
+	clear_bit(WX_STATE_DISABLED, wx->state);
 	pci_set_master(pdev);
 	device_wakeup_disable(&pdev->dev);
 
@@ -914,6 +987,7 @@ static struct pci_driver ngbe_driver = {
 	.resume   = ngbe_resume,
 	.shutdown = ngbe_shutdown,
 	.sriov_configure = wx_pci_sriov_configure,
+	.err_handler = &wx_err_handler,
 };
 
 module_pci_driver(ngbe_driver);
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index 7077a0da4c986..c9233dc7ae505 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -125,5 +125,6 @@ extern char ngbe_driver_name[];
 void ngbe_down(struct wx *wx);
 void ngbe_up(struct wx *wx);
 int ngbe_setup_tc(struct net_device *dev, u8 tc);
+void ngbe_do_reset(struct net_device *netdev, bool reinit);
 
 #endif /* _NGBE_TYPE_H_ */
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index c277863baf67a..eb91c4f28ecd4 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -14,6 +14,7 @@
 
 #include "../libwx/wx_type.h"
 #include "../libwx/wx_lib.h"
+#include "../libwx/wx_err.h"
 #include "../libwx/wx_ptp.h"
 #include "../libwx/wx_hw.h"
 #include "../libwx/wx_mbx.h"
@@ -93,12 +94,24 @@ static void txgbe_module_detection_subtask(struct wx *wx)
 {
 	int err;
 
+	if (test_bit(WX_STATE_DOWN, wx->state) ||
+	    test_bit(WX_STATE_RESETTING, wx->state))
+		return;
+
 	if (!test_and_clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags))
 		return;
 
 	/* wait for SFF module ready */
 	msleep(200);
 
+	/* Re-check state to avoid racing with down/reset paths.
+	 * Module identification is deferred to the next up event,
+	 * so it is safe to bail out here.
+	 */
+	if (test_bit(WX_STATE_DOWN, wx->state) ||
+	    test_bit(WX_STATE_RESETTING, wx->state))
+		return;
+
 	err = txgbe_identify_module(wx);
 	if (err == -ENODEV)
 		set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
@@ -106,6 +119,10 @@ static void txgbe_module_detection_subtask(struct wx *wx)
 
 static void txgbe_link_config_subtask(struct wx *wx)
 {
+	if (test_bit(WX_STATE_DOWN, wx->state) ||
+	    test_bit(WX_STATE_RESETTING, wx->state))
+		return;
+
 	if (!test_and_clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags))
 		return;
 
@@ -123,6 +140,8 @@ static void txgbe_service_task(struct work_struct *work)
 	txgbe_module_detection_subtask(wx);
 	txgbe_link_config_subtask(wx);
 	wx_update_stats(wx);
+	wx_check_hang_subtask(wx);
+	wx_check_err_subtask(wx);
 
 	wx_service_event_complete(wx);
 }
@@ -144,6 +163,7 @@ static void txgbe_up_complete(struct wx *wx)
 	/* make sure to complete pre-operations */
 	smp_mb__before_atomic();
 	clear_bit(WX_STATE_DOWN, wx->state);
+	clear_bit(WX_STATE_RES_FREED, wx->state);
 	wx_napi_enable_all(wx);
 
 	switch (wx->mac.type) {
@@ -187,6 +207,9 @@ static void txgbe_reset(struct wx *wx)
 	u8 old_addr[ETH_ALEN];
 	int err;
 
+	if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
+		return;
+
 	err = txgbe_reset_hw(wx);
 	if (err != 0)
 		wx_err(wx, "Hardware Error: %d\n", err);
@@ -224,6 +247,7 @@ static void txgbe_disable_device(struct wx *wx)
 	wx_irq_disable(wx);
 	wx_napi_disable_all(wx);
 
+	clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
 	timer_delete_sync(&wx->service_timer);
 	cancel_work_sync(&wx->service_task);
 
@@ -292,6 +316,20 @@ void txgbe_up(struct wx *wx)
 	txgbe_up_complete(wx);
 }
 
+static void txgbe_down_suspend(struct wx *wx)
+{
+	if (test_and_set_bit(WX_STATE_RES_FREED, wx->state))
+		return;
+
+	phylink_stop(wx->phylink);
+	wx_clean_all_tx_rings(wx);
+	wx_clean_all_rx_rings(wx);
+	wx_free_irq(wx);
+	txgbe_free_misc_irq(wx->priv);
+	wx_free_resources(wx);
+	txgbe_fdir_filter_exit(wx);
+}
+
 /**
  *  txgbe_init_type_code - Initialize the shared code
  *  @wx: pointer to hardware structure
@@ -408,6 +446,7 @@ static int txgbe_sw_init(struct wx *wx)
 
 	wx->setup_tc = txgbe_setup_tc;
 	wx->do_reset = txgbe_do_reset;
+	wx->down_suspend = txgbe_down_suspend;
 	set_bit(0, &wx->fwd_bitmask);
 
 	switch (wx->mac.type) {
@@ -518,12 +557,16 @@ static int txgbe_close(struct net_device *netdev)
 {
 	struct wx *wx = netdev_priv(netdev);
 
+	if (test_bit(WX_STATE_RES_FREED, wx->state))
+		goto out;
+
 	wx_ptp_stop(wx);
 	txgbe_down(wx);
 	wx_free_irq(wx);
 	txgbe_free_misc_irq(wx->priv);
 	wx_free_resources(wx);
 	txgbe_fdir_filter_exit(wx);
+out:
 	wx_control_hw(wx, false);
 
 	return 0;
@@ -544,7 +587,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev)
 
 	wx_control_hw(wx, false);
 
-	pci_disable_device(pdev);
+	if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+		pci_disable_device(pdev);
 }
 
 static void txgbe_shutdown(struct pci_dev *pdev)
@@ -606,11 +650,11 @@ static void txgbe_reinit_locked(struct wx *wx)
 	mutex_unlock(&wx->reset_lock);
 }
 
-void txgbe_do_reset(struct net_device *netdev)
+void txgbe_do_reset(struct net_device *netdev, bool reinit)
 {
 	struct wx *wx = netdev_priv(netdev);
 
-	if (netif_running(netdev))
+	if (netif_running(netdev) && reinit)
 		txgbe_reinit_locked(wx);
 	else
 		txgbe_reset(wx);
@@ -654,6 +698,7 @@ static const struct net_device_ops txgbe_netdev_ops = {
 	.ndo_stop               = txgbe_close,
 	.ndo_change_mtu         = wx_change_mtu,
 	.ndo_start_xmit         = wx_xmit_frame,
+	.ndo_tx_timeout         = wx_tx_timeout,
 	.ndo_set_rx_mode        = wx_set_rx_mode,
 	.ndo_set_features       = wx_set_features,
 	.ndo_fix_features       = wx_fix_features,
@@ -814,6 +859,10 @@ static int txgbe_probe(struct pci_dev *pdev,
 	eth_hw_addr_set(netdev, wx->mac.perm_addr);
 	wx_mac_set_default_filter(wx, wx->mac.perm_addr);
 
+	err = wx_init_err_task(wx);
+	if (err)
+		goto err_free_mac_table;
+
 	txgbe_init_service(wx);
 
 	err = wx_init_interrupt_scheme(wx);
@@ -889,6 +938,7 @@ static int txgbe_probe(struct pci_dev *pdev,
 		goto err_remove_phy;
 
 	pci_set_drvdata(pdev, wx);
+	pci_save_state(pdev);
 
 	netif_tx_stop_all_queues(netdev);
 
@@ -916,6 +966,8 @@ static int txgbe_probe(struct pci_dev *pdev,
 err_cancel_service:
 	timer_delete_sync(&wx->service_timer);
 	cancel_work_sync(&wx->service_task);
+	cancel_work_sync(&wx->reset_task);
+	destroy_workqueue(wx->reset_wq);
 err_free_mac_table:
 	kfree(wx->rss_key);
 	kfree(wx->mac_table);
@@ -949,6 +1001,8 @@ static void txgbe_remove(struct pci_dev *pdev)
 
 	timer_shutdown_sync(&wx->service_timer);
 	cancel_work_sync(&wx->service_task);
+	cancel_work_sync(&wx->reset_task);
+	destroy_workqueue(wx->reset_wq);
 
 	txgbe_remove_phy(txgbe);
 	wx_free_isb_resources(wx);
@@ -960,7 +1014,8 @@ static void txgbe_remove(struct pci_dev *pdev)
 	kfree(wx->mac_table);
 	wx_clear_interrupt_scheme(wx);
 
-	pci_disable_device(pdev);
+	if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+		pci_disable_device(pdev);
 }
 
 static struct pci_driver txgbe_driver = {
@@ -970,6 +1025,7 @@ static struct pci_driver txgbe_driver = {
 	.remove   = txgbe_remove,
 	.shutdown = txgbe_shutdown,
 	.sriov_configure = wx_pci_sriov_configure,
+	.err_handler = &wx_err_handler,
 };
 
 module_pci_driver(txgbe_driver);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 877234e3fdc2b..3e93a3f309c16 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -313,7 +313,7 @@ extern char txgbe_driver_name[];
 void txgbe_down(struct wx *wx);
 void txgbe_up(struct wx *wx);
 int txgbe_setup_tc(struct net_device *dev, u8 tc);
-void txgbe_do_reset(struct net_device *netdev);
+void txgbe_do_reset(struct net_device *netdev, bool reinit);
 
 #define DECLARE_PHY_INTERFACE_MASK_ZERO(name) \
 	unsigned long name[PHY_INTERFACE_MODE_MAX] = { 0, }

3/2 2026/08/03 07:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17506
							output: 92
							thoughts: 1497
						

Thoughts:
**WorthFuzzing Assessment for Wangxun Ethernet Driver Patches**

Okay, so I've got this patch series, and it's touching the Wangxun ethernet drivers – specifically, the `ngbe` and `txgbe` drivers. It looks like they're adding error handling, particularly around PCI errors and Tx timeouts, with some hang detection logic as well. The modifications are across a few files in the `wangxun` directory. My initial thought is, "Wangxun... isn't that a vendor-specific PCIe NIC?" I've seen these before; they're not exactly generic.

My next step is to confirm my suspicion. Are these drivers dependent on physical hardware that QEMU wouldn't emulate? I pull up some documentation, and yep, it's a "WangXun Gigabit PCI Express Linux driver." Confirmed. They're physical NIC drivers. The question is, can we fuzz these meaningfully?

Now, I go back to the instructions, and I see the crucial line: "Return WorthFuzzing=false if the patch only contains... code in vendor-specific PCIe switch, SmartNIC, or GPU drivers... that require physical PCIe hardware cards not emulated in standard QEMU." Bingo.

Let's look at QEMU's supported devices. I know QEMU emulates the standard NICs. But it doesn't emulate these Wangxun cards. So the code paths being modified here are fundamentally unreachable in environments like GCE or standard QEMU. Can Syzkaller's PCI injection help? Syzkaller *can* inject devices, but without physical hardware, the driver will likely fail when it tries to talk to the hardware registers. The hardware state machine of these cards (DMA rings, PHY link status) just won't be there. And the instructions explicitly say that these sorts of drivers are not fuzzable.

I need to confirm that these aren't just regular NICs. They are 1G/10G NICs from Beijing WangXun Technology, confirming that they are not a standard NIC, or SmartNIC that is emulated.

So, since these drivers depend on physical hardware, and the code paths are unreachable in a virtualized environment, it's a no-go for functional fuzzing. The probe might trigger via syzkaller, but nothing will work without the physical card. The `ndo_open`, `ndo_start_xmit`, `ndo_tx_timeout`, and PCI error handlers, all modified in the patch, are completely dead code in a QEMU environment.

Let's check if syzkaller currently fuzzes it. I can't search syslang files directly, per instructions, but I can reason about it. Physical PCIe NICs not emulated by QEMU cannot be functionally fuzzed.

I've reviewed the patch again. All the modifications are within `drivers/net/ethernet/wangxun/`. This confirms my initial assessment: hardware-specific ethernet driver for Wangxun NICs that's unreachable.

**Final Verdict:** `WorthFuzzing: False`. The patch's core modifications are in vendor-specific PCIe ethernet drivers, which require physical hardware and are not emulated, making the code paths inaccessible for functional fuzzing.



4/2 2026/08/03 07:08 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Wangxun (ngbe/txgbe) physical PCIe ethernet drivers to add PCI error handling and Tx timeout recovery. These are vendor-specific hardware drivers that require physical PCIe cards not emulated in standard QEMU environments, making the modified code paths unreachable during fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Wangxun (ngbe/txgbe) physical PCIe ethernet drivers to add PCI error handling and Tx timeout recovery. These are vendor-specific hardware drivers that require physical PCIe cards not emulated in standard QEMU environments, making the modified code paths unreachable during fuzzing.",
  "WorthFuzzing": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)