@@ -35,6 +35,8 @@ struct txgbe_adapter {
struct net_device *netdev;
struct pci_dev *pdev;
+ unsigned long state;
+
/* Tx fast path data */
int num_tx_queues;
@@ -45,6 +47,9 @@ struct txgbe_adapter {
struct txgbe_hw hw;
u16 msg_enable;
+ struct timer_list service_timer;
+ struct work_struct service_task;
+
char eeprom_id[32];
bool netdev_registered;
@@ -52,7 +57,19 @@ struct txgbe_adapter {
};
+enum txgbe_state_t {
+ __TXGBE_TESTING,
+ __TXGBE_RESETTING,
+ __TXGBE_DOWN,
+ __TXGBE_HANGING,
+ __TXGBE_DISABLED,
+ __TXGBE_REMOVING,
+ __TXGBE_SERVICE_SCHED,
+ __TXGBE_SERVICE_INITED,
+};
+
/* needed by txgbe_main.c */
+void txgbe_service_event_schedule(struct txgbe_adapter *adapter);
void txgbe_assign_netdev_ops(struct net_device *netdev);
int txgbe_open(struct net_device *netdev);
@@ -33,6 +33,8 @@ static const struct pci_device_id txgbe_pci_tbl[] = {
#define DEFAULT_DEBUG_LEVEL_SHIFT 3
+static struct workqueue_struct *txgbe_wq;
+
static bool txgbe_is_sfp(struct txgbe_hw *hw);
static void txgbe_check_minimum_link(struct txgbe_adapter *adapter)
@@ -73,6 +75,24 @@ static int txgbe_enumerate_functions(struct txgbe_adapter *adapter)
return physfns;
}
+void txgbe_service_event_schedule(struct txgbe_adapter *adapter)
+{
+ if (!test_bit(__TXGBE_DOWN, &adapter->state) &&
+ !test_bit(__TXGBE_REMOVING, &adapter->state) &&
+ !test_and_set_bit(__TXGBE_SERVICE_SCHED, &adapter->state))
+ queue_work(txgbe_wq, &adapter->service_task);
+}
+
+static void txgbe_service_event_complete(struct txgbe_adapter *adapter)
+{
+ if (WARN_ON(!test_bit(__TXGBE_SERVICE_SCHED, &adapter->state)))
+ return;
+
+ /* flush memory to make sure state is correct before next watchdog */
+ smp_mb__before_atomic();
+ clear_bit(__TXGBE_SERVICE_SCHED, &adapter->state);
+}
+
static void txgbe_release_hw_control(struct txgbe_adapter *adapter)
{
/* Let firmware take over control of hw */
@@ -215,6 +235,10 @@ void txgbe_disable_device(struct txgbe_adapter *adapter)
struct txgbe_hw *hw = &adapter->hw;
u32 i;
+ /* signal that we are down to the interrupt handler */
+ if (test_and_set_bit(__TXGBE_DOWN, &adapter->state))
+ return; /* do nothing if already down */
+
txgbe_disable_pcie_master(hw);
/* disable receives */
hw->mac.ops.disable_rx(hw);
@@ -222,6 +246,8 @@ void txgbe_disable_device(struct txgbe_adapter *adapter)
netif_carrier_off(netdev);
netif_tx_disable(netdev);
+ del_timer_sync(&adapter->service_timer);
+
if (hw->bus.lan_id == 0)
wr32m(hw, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN0_UP, 0);
else if (hw->bus.lan_id == 1)
@@ -297,6 +323,8 @@ static int txgbe_sw_init(struct txgbe_adapter *adapter)
return -ENOMEM;
}
+ set_bit(__TXGBE_DOWN, &adapter->state);
+
return 0;
}
@@ -368,7 +396,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
txgbe_release_hw_control(adapter);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__TXGBE_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
}
static void txgbe_shutdown(struct pci_dev *pdev)
@@ -383,6 +412,32 @@ static void txgbe_shutdown(struct pci_dev *pdev)
}
}
+static void txgbe_service_timer(struct timer_list *t)
+{
+ struct txgbe_adapter *adapter = from_timer(adapter, t, service_timer);
+ unsigned long next_event_offset;
+
+ next_event_offset = HZ * 2;
+
+ /* Reset the timer */
+ mod_timer(&adapter->service_timer, next_event_offset + jiffies);
+
+ txgbe_service_event_schedule(adapter);
+}
+
+/**
+ * txgbe_service_task - manages and runs subtasks
+ * @work: pointer to work_struct containing our data
+ **/
+static void txgbe_service_task(struct work_struct *work)
+{
+ struct txgbe_adapter *adapter = container_of(work,
+ struct txgbe_adapter,
+ service_task);
+
+ txgbe_service_event_complete(adapter);
+}
+
/**
* txgbe_set_mac - Change the Ethernet Address of the NIC
* @netdev: network interface device structure
@@ -483,6 +538,7 @@ static int txgbe_probe(struct pci_dev *pdev,
struct txgbe_adapter *adapter = NULL;
struct txgbe_hw *hw = NULL;
struct net_device *netdev;
+ bool disable_dev = false;
int err, expected_gts;
u16 eeprom_verh = 0, eeprom_verl = 0, offset = 0;
@@ -541,6 +597,7 @@ static int txgbe_probe(struct pci_dev *pdev,
hw->hw_addr = adapter->io_addr;
txgbe_assign_netdev_ops(netdev);
+ netdev->watchdog_timeo = 5 * HZ;
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
/* setup the private structure */
@@ -595,6 +652,12 @@ static int txgbe_probe(struct pci_dev *pdev,
txgbe_mac_set_default_filter(adapter, hw->mac.perm_addr);
+ timer_setup(&adapter->service_timer, txgbe_service_timer, 0);
+
+ INIT_WORK(&adapter->service_task, txgbe_service_task);
+ set_bit(__TXGBE_SERVICE_INITED, &adapter->state);
+ clear_bit(__TXGBE_SERVICE_SCHED, &adapter->state);
+
/* Save off EEPROM version number and Option Rom version which
* together make a unique identify for the eeprom
*/
@@ -693,11 +756,13 @@ static int txgbe_probe(struct pci_dev *pdev,
err_free_mac_table:
kfree(adapter->mac_table);
err_pci_release_regions:
+ disable_dev = !test_and_set_bit(__TXGBE_DISABLED, &adapter->state);
pci_disable_pcie_error_reporting(pdev);
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
err_pci_disable_dev:
- pci_disable_device(pdev);
+ if (!adapter || disable_dev)
+ pci_disable_device(pdev);
return err;
}
@@ -714,8 +779,11 @@ static void txgbe_remove(struct pci_dev *pdev)
{
struct txgbe_adapter *adapter = pci_get_drvdata(pdev);
struct net_device *netdev;
+ bool disable_dev;
netdev = adapter->netdev;
+ set_bit(__TXGBE_REMOVING, &adapter->state);
+ cancel_work_sync(&adapter->service_task);
/* remove the added san mac */
txgbe_del_sanmac_netdev(netdev);
@@ -731,10 +799,12 @@ static void txgbe_remove(struct pci_dev *pdev)
pci_select_bars(pdev, IORESOURCE_MEM));
kfree(adapter->mac_table);
+ disable_dev = !test_and_set_bit(__TXGBE_DISABLED, &adapter->state);
pci_disable_pcie_error_reporting(pdev);
- pci_disable_device(pdev);
+ if (disable_dev)
+ pci_disable_device(pdev);
}
static struct pci_driver txgbe_driver = {
@@ -745,7 +815,42 @@ static struct pci_driver txgbe_driver = {
.shutdown = txgbe_shutdown,
};
-module_pci_driver(txgbe_driver);
+/**
+ * txgbe_init_module - Driver Registration Routine
+ *
+ * txgbe_init_module is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ **/
+static int __init txgbe_init_module(void)
+{
+ int ret;
+
+ txgbe_wq = create_singlethread_workqueue(txgbe_driver_name);
+ if (!txgbe_wq) {
+ pr_err("%s: Failed to create workqueue\n", txgbe_driver_name);
+ return -ENOMEM;
+ }
+
+ ret = pci_register_driver(&txgbe_driver);
+ return ret;
+}
+
+module_init(txgbe_init_module);
+
+/**
+ * txgbe_exit_module - Driver Exit Cleanup Routine
+ *
+ * txgbe_exit_module is called just before the driver is removed
+ * from memory.
+ **/
+static void __exit txgbe_exit_module(void)
+{
+ pci_unregister_driver(&txgbe_driver);
+ if (txgbe_wq)
+ destroy_workqueue(txgbe_wq);
+}
+
+module_exit(txgbe_exit_module);
MODULE_DEVICE_TABLE(pci, txgbe_pci_tbl);
MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@trustnetic.com>");
Setup work queue, and initialize service task to process the following tasks. Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com> --- drivers/net/ethernet/wangxun/txgbe/txgbe.h | 17 +++ .../net/ethernet/wangxun/txgbe/txgbe_main.c | 113 +++++++++++++++++- 2 files changed, 126 insertions(+), 4 deletions(-)