AI Trajectory


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

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Wangxun (ngbe, txgbe) ethernet drivers, which are for physical vendor-specific PCIe network cards. These devices are not emulated in standard QEMU or GCE environments, making the code unreachable during fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/07/25 14:37 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit b3a3732ec157167eac98b9bb7c546171ef1faf75\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sat Jul 25 14:37:02 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 a71b0ad77de3f7..c8724bb129aa06 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 00000000000000..7936845c2894c1\n--- /dev/null\n+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c\n@@ -0,0 +1,319 @@\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 void wx_pf_reset_subtask(struct wx *wx)\n+{\n+\tif (!test_and_clear_bit(WX_FLAG_NEED_PF_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+\tif (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx-\u003eflags))\n+\t\twx_soft_quiesce(wx);\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_PF_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_PF_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 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_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_PF_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 00000000000000..a6a82a26352849\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 22037f015ded19..940d2e59876c7c 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 260e14d5d54121..122c4952d203f0 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 31572f59b9ce2c..f32f1246386357 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@@ -3112,7 +3149,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@@ -3162,7 +3199,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@@ -3371,5 +3408,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_PF_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 bc671786978e54..9d85d399e17f4b 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 44f3e65052468f..3eea647c4742c0 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 50db90a6e3ee66..ad2f824875d51b 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 65e3e55db1cffb..aa357e260c4c51 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,8 @@ 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_PF_RESET,\n+\tWX_FLAG_NEED_PCIE_RECOVERY,\n \tWX_PF_FLAGS_NBITS               /* must be last */\n };\n \n@@ -1395,7 +1412,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 +1441,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 +1525,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 b2e19198280326..1960f7154151df 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 a16221995909f9..56d4b63387fd6e 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_PF_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@@ -700,6 +762,7 @@ static int ngbe_probe(struct pci_dev *pdev,\n \twx-\u003edriver_name = ngbe_driver_name;\n \tngbe_set_ethtool_ops(netdev);\n \tnetdev-\u003enetdev_ops = \u0026ngbe_netdev_ops;\n+\tnetdev-\u003ewatchdog_timeo = 5 * HZ;\n \n \tnetdev-\u003efeatures = NETIF_F_SG | NETIF_F_IP_CSUM |\n \t\t\t   NETIF_F_TSO | NETIF_F_TSO6 |\n@@ -796,6 +859,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 +879,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 +891,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 +924,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 +935,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 +963,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 +988,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 7077a0da4c986a..c9233dc7ae5052 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 c277863baf67a2..582ee567e6b9c0 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_PF_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@@ -745,6 +790,7 @@ static int txgbe_probe(struct pci_dev *pdev,\n \twx-\u003edriver_name = txgbe_driver_name;\n \ttxgbe_set_ethtool_ops(netdev);\n \tnetdev-\u003enetdev_ops = \u0026txgbe_netdev_ops;\n+\tnetdev-\u003ewatchdog_timeo = 5 * HZ;\n \tnetdev-\u003eudp_tunnel_nic_info = \u0026txgbe_udp_tunnels;\n \n \t/* setup the private structure */\n@@ -814,6 +860,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 +939,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 +967,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 +1002,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 +1015,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 +1026,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 877234e3fdc2b6..3e93a3f309c166 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/07/25 14:37 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Wangxun (ngbe, txgbe) ethernet drivers, which are for physical vendor-specific PCIe network cards. These devices are not emulated in standard QEMU or GCE environments, making the code 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 b3a3732ec157167eac98b9bb7c546171ef1faf75
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Jul 25 14:37:02 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 a71b0ad77de3f7..c8724bb129aa06 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 00000000000000..7936845c2894c1
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
@@ -0,0 +1,319 @@
+// 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 void wx_pf_reset_subtask(struct wx *wx)
+{
+	if (!test_and_clear_bit(WX_FLAG_NEED_PF_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 (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
+		wx_soft_quiesce(wx);
+
+	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_PF_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_PF_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 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_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_PF_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 00000000000000..a6a82a26352849
--- /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 22037f015ded19..940d2e59876c7c 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 260e14d5d54121..122c4952d203f0 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 31572f59b9ce2c..f32f1246386357 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))) {
@@ -3112,7 +3149,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);
 
@@ -3162,7 +3199,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;
 }
@@ -3371,5 +3408,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_PF_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 bc671786978e54..9d85d399e17f4b 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 44f3e65052468f..3eea647c4742c0 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 50db90a6e3ee66..ad2f824875d51b 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 65e3e55db1cffb..aa357e260c4c51 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,8 @@ enum wx_pf_flags {
 	WX_FLAG_NEED_DO_RESET,
 	WX_FLAG_RX_MERGE_ENABLED,
 	WX_FLAG_TXHEAD_WB_ENABLED,
+	WX_FLAG_NEED_PF_RESET,
+	WX_FLAG_NEED_PCIE_RECOVERY,
 	WX_PF_FLAGS_NBITS               /* must be last */
 };
 
@@ -1395,7 +1412,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 +1441,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 +1525,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 b2e19198280326..1960f7154151df 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 a16221995909f9..56d4b63387fd6e 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_PF_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,
@@ -700,6 +762,7 @@ static int ngbe_probe(struct pci_dev *pdev,
 	wx->driver_name = ngbe_driver_name;
 	ngbe_set_ethtool_ops(netdev);
 	netdev->netdev_ops = &ngbe_netdev_ops;
+	netdev->watchdog_timeo = 5 * HZ;
 
 	netdev->features = NETIF_F_SG | NETIF_F_IP_CSUM |
 			   NETIF_F_TSO | NETIF_F_TSO6 |
@@ -796,6 +859,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 +879,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 +891,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 +924,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 +935,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 +963,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 +988,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 7077a0da4c986a..c9233dc7ae5052 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 c277863baf67a2..582ee567e6b9c0 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_PF_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,
@@ -745,6 +790,7 @@ static int txgbe_probe(struct pci_dev *pdev,
 	wx->driver_name = txgbe_driver_name;
 	txgbe_set_ethtool_ops(netdev);
 	netdev->netdev_ops = &txgbe_netdev_ops;
+	netdev->watchdog_timeo = 5 * HZ;
 	netdev->udp_tunnel_nic_info = &txgbe_udp_tunnels;
 
 	/* setup the private structure */
@@ -814,6 +860,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 +939,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 +967,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 +1002,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 +1015,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 +1026,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 877234e3fdc2b6..3e93a3f309c166 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/07/25 14:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17698
							output: 35
							thoughts: 383
						

Thoughts:
**Analyzing the Wangxun Ethernet Driver Patch**

Okay, so I'm looking at this patch for the Wangxun Ethernet drivers – `libwx`, `ngbe`, and `txgbe`. It looks like it's adding some critical error handling and reset logic, which is good. Specifically, a new `wx_err.c` file implements PCIe error handlers (`wx_io_error_detected`, etc.), Tx timeout handling (`wx_tx_timeout`), Tx hang detection (`wx_handle_tx_hang`), and some reset tasks. The patch modifies `ngbe` and `txgbe` to use these handlers by adding the error handler and timeout handler function pointers to the relevant driver structures.

Now, a red flag popped up. We're talking about Wangxun drivers, and these are for *physical* PCIe cards, right? The question is, are Wangxun cards even emulated in QEMU? That's the critical question here for fuzzer use. I don't have the QEMU source right in front of me to check, and it's not looking like a standard driver I would expect to find in QEMU based on previous experience.

Ah, the prompt reminds me that the target is "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". This confirms my suspicion. Let's see if we can fuzz these.

Most of the standard Ethernet drivers I work with, like Intel (e1000e, igb) or Realtek (rtl8139), are generally well-supported and emulated in QEMU. Wangxun? Probably not. I need to confirm if these drivers are even candidates for fuzzing, and if so, how. Let's start with a check on the `Kconfig` file in the driver directory to see how they're built.



4/2 2026/07/25 14:37 tool grepper
0m Args:
{
  "Expression": "config NGBE",
  "PathPrefix": "drivers/net/ethernet/wangxun/"
}

Results:
{
  "Output": "drivers/net/ethernet/wangxun/Kconfig=19=config LIBWX\n--\ndrivers/net/ethernet/wangxun/Kconfig-27-\ndrivers/net/ethernet/wangxun/Kconfig:28:config NGBE\ndrivers/net/ethernet/wangxun/Kconfig-29-\ttristate \"Wangxun(R) GbE PCI Express adapters support\"\n--\ndrivers/net/ethernet/wangxun/Kconfig=67=config TXGBEVF\n--\ndrivers/net/ethernet/wangxun/Kconfig-84-\ndrivers/net/ethernet/wangxun/Kconfig:85:config NGBEVF\ndrivers/net/ethernet/wangxun/Kconfig-86-\ttristate \"Wangxun(R) GbE Virtual Function Ethernet support\"\n"
}

5/2 2026/07/25 14:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18760
							output: 34
							thoughts: 237
						

Thoughts:
**Analyzing the Feasibility of Fuzzing Wangxun GbE PCI Express Adapters**

Okay, so the question is whether a patch involving Wangxun GbE PCI Express adapters is worth fuzzing. My initial thought is almost certainly not. Wangxun is a Chinese vendor; they're not a widely emulated hardware platform, especially not within a standard QEMU environment. The prompt is very clear on this point. It specifically flags code paths that are inaccessible in virtualization, even with software emulated hardware, and cites vendor-specific PCIe drivers as examples. Wangxun, with their ngbe and txgbe drivers, clearly falls into that category of a vendor-specific physical PCIe network card driver. It's almost guaranteed to require a physical card, which means the code is unreachable in QEMU.

Just to be absolutely sure, I'll quickly check if there's any mention of software emulation for Wangxun hardware within the kernel sources, but I'm not optimistic. Based on my experience, it's highly unlikely.



6/2 2026/07/25 14:37 tool grepper
0m Args:
{
  "Expression": "wangxun",
  "PathPrefix": "drivers/net/ethernet/wangxun/"
}

Results:
{
  "Output": "drivers/net/ethernet/wangxun/Kconfig=28=config NGBE\n--\ndrivers/net/ethernet/wangxun/Kconfig-37-\t  More specific information on configuring the driver is in\ndrivers/net/ethernet/wangxun/Kconfig:38:\t  \u003cfile:Documentation/networking/device_drivers/ethernet/wangxun/ngbe.rst\u003e.\ndrivers/net/ethernet/wangxun/Kconfig-39-\n--\ndrivers/net/ethernet/wangxun/Kconfig=43=config TXGBE\n--\ndrivers/net/ethernet/wangxun/Kconfig-61-\t  More specific information on configuring the driver is in\ndrivers/net/ethernet/wangxun/Kconfig:62:\t  \u003cfile:Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst\u003e.\ndrivers/net/ethernet/wangxun/Kconfig-63-\n--\ndrivers/net/ethernet/wangxun/Kconfig=67=config TXGBEVF\n--\ndrivers/net/ethernet/wangxun/Kconfig-79-\t  More specific information on configuring the driver is in\ndrivers/net/ethernet/wangxun/Kconfig:80:\t  \u003cfile:Documentation/networking/device_drivers/ethernet/wangxun/txgbevf.rst\u003e.\ndrivers/net/ethernet/wangxun/Kconfig-81-\n--\ndrivers/net/ethernet/wangxun/Kconfig=85=config NGBEVF\n--\ndrivers/net/ethernet/wangxun/Kconfig-95-\t  More specific information on configuring the driver is in\ndrivers/net/ethernet/wangxun/Kconfig:96:\t  \u003cfile:Documentation/networking/device_drivers/ethernet/wangxun/ngbevf.rst\u003e.\ndrivers/net/ethernet/wangxun/Kconfig-97-\n"
}

7/2 2026/07/25 14:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19730
							output: 84
							thoughts: 43
						

Thoughts:
Okay, here's my summary, taking on the persona you've described:

**My Assessment of the PCIe Network Card Driver**

Right, let me see... this is straightforward. This is *just* a physical PCIe network card driver. Nothing fancy, nothing emulated. It's vendor-specific, which means it's designed to interact directly with the hardware on a physical machine, not a virtual one. My expertise tells me it's not the sort of thing I'd expect to find emulated in something like QEMU. Given its nature and its target environment, there's no real benefit in fuzzing this. Therefore, I'm comfortable returning `WorthFuzzing=false`. The reasoning is clear: it's a vendor-specific PCIe driver *not* emulated in QEMU. That's the key.



8/2 2026/07/25 14:37 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Wangxun (ngbe, txgbe) ethernet drivers, which are for physical vendor-specific PCIe network cards. These devices are not emulated in standard QEMU or GCE environments, making the code unreachable during fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Wangxun (ngbe, txgbe) ethernet drivers, which are for physical vendor-specific PCIe network cards. These devices are not emulated in standard QEMU or GCE environments, making the code 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)