qlcnic: offload tx timeout recovery

Offload tx timeout recovery to fw recovery func(check_health).
In check_health, first check health of device, if it its ok, then
do tx timeout recovery otherwise device recovery.

Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Amit Kumar Salecha 2010-06-22 03:19:03 +00:00 committed by David S. Miller
parent 52486a3ac8
commit 68bf1c68a4
2 changed files with 32 additions and 36 deletions

View File

@ -934,6 +934,7 @@ struct qlcnic_adapter {
u8 rx_csum; u8 rx_csum;
u8 portnum; u8 portnum;
u8 physical_port; u8 physical_port;
u8 reset_context;
u8 mc_enabled; u8 mc_enabled;
u8 max_mc_count; u8 max_mc_count;
@ -1001,8 +1002,6 @@ struct qlcnic_adapter {
struct delayed_work fw_work; struct delayed_work fw_work;
struct work_struct tx_timeout_task;
struct qlcnic_nic_intr_coalesce coal; struct qlcnic_nic_intr_coalesce coal;
unsigned long state; unsigned long state;

View File

@ -75,7 +75,6 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev);
static int qlcnic_open(struct net_device *netdev); static int qlcnic_open(struct net_device *netdev);
static int qlcnic_close(struct net_device *netdev); static int qlcnic_close(struct net_device *netdev);
static void qlcnic_tx_timeout(struct net_device *netdev); static void qlcnic_tx_timeout(struct net_device *netdev);
static void qlcnic_tx_timeout_task(struct work_struct *work);
static void qlcnic_attach_work(struct work_struct *work); static void qlcnic_attach_work(struct work_struct *work);
static void qlcnic_fwinit_work(struct work_struct *work); static void qlcnic_fwinit_work(struct work_struct *work);
static void qlcnic_fw_poll_work(struct work_struct *work); static void qlcnic_fw_poll_work(struct work_struct *work);
@ -911,6 +910,7 @@ __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
qlcnic_linkevent_request(adapter, 1); qlcnic_linkevent_request(adapter, 1);
adapter->reset_context = 0;
set_bit(__QLCNIC_DEV_UP, &adapter->state); set_bit(__QLCNIC_DEV_UP, &adapter->state);
return 0; return 0;
} }
@ -1110,6 +1110,27 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
return 0; return 0;
} }
/* Reset context in hardware only */
static int
qlcnic_reset_hw_context(struct qlcnic_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
return -EBUSY;
netif_device_detach(netdev);
qlcnic_down(adapter, netdev);
qlcnic_up(adapter, netdev);
netif_device_attach(netdev);
clear_bit(__QLCNIC_RESETTING, &adapter->state);
return 0;
}
int int
qlcnic_reset_context(struct qlcnic_adapter *adapter) qlcnic_reset_context(struct qlcnic_adapter *adapter)
{ {
@ -1178,8 +1199,6 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter,
netdev->irq = adapter->msix_entries[0].vector; netdev->irq = adapter->msix_entries[0].vector;
INIT_WORK(&adapter->tx_timeout_task, qlcnic_tx_timeout_task);
if (qlcnic_read_mac_addr(adapter)) if (qlcnic_read_mac_addr(adapter))
dev_warn(&pdev->dev, "failed to read mac addr\n"); dev_warn(&pdev->dev, "failed to read mac addr\n");
@ -1350,8 +1369,6 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev)
unregister_netdev(netdev); unregister_netdev(netdev);
cancel_work_sync(&adapter->tx_timeout_task);
qlcnic_detach(adapter); qlcnic_detach(adapter);
if (adapter->npars != NULL) if (adapter->npars != NULL)
@ -1390,8 +1407,6 @@ static int __qlcnic_shutdown(struct pci_dev *pdev)
if (netif_running(netdev)) if (netif_running(netdev))
qlcnic_down(adapter, netdev); qlcnic_down(adapter, netdev);
cancel_work_sync(&adapter->tx_timeout_task);
qlcnic_clr_all_drv_state(adapter); qlcnic_clr_all_drv_state(adapter);
clear_bit(__QLCNIC_RESETTING, &adapter->state); clear_bit(__QLCNIC_RESETTING, &adapter->state);
@ -1854,35 +1869,11 @@ static void qlcnic_tx_timeout(struct net_device *netdev)
return; return;
dev_err(&netdev->dev, "transmit timeout, resetting.\n"); dev_err(&netdev->dev, "transmit timeout, resetting.\n");
schedule_work(&adapter->tx_timeout_task);
}
static void qlcnic_tx_timeout_task(struct work_struct *work)
{
struct qlcnic_adapter *adapter =
container_of(work, struct qlcnic_adapter, tx_timeout_task);
if (!netif_running(adapter->netdev))
return;
if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
return;
if (++adapter->tx_timeo_cnt >= QLCNIC_MAX_TX_TIMEOUTS) if (++adapter->tx_timeo_cnt >= QLCNIC_MAX_TX_TIMEOUTS)
goto request_reset; adapter->need_fw_reset = 1;
else
clear_bit(__QLCNIC_RESETTING, &adapter->state); adapter->reset_context = 1;
if (!qlcnic_reset_context(adapter)) {
adapter->netdev->trans_start = jiffies;
return;
/* context reset failed, fall through for fw reset */
}
request_reset:
adapter->need_fw_reset = 1;
clear_bit(__QLCNIC_RESETTING, &adapter->state);
QLCDB(adapter, DRV, "Resetting adapter\n");
} }
static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev) static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev)
@ -2540,6 +2531,12 @@ qlcnic_check_health(struct qlcnic_adapter *adapter)
adapter->fw_fail_cnt = 0; adapter->fw_fail_cnt = 0;
if (adapter->need_fw_reset) if (adapter->need_fw_reset)
goto detach; goto detach;
if (adapter->reset_context) {
qlcnic_reset_hw_context(adapter);
adapter->netdev->trans_start = jiffies;
}
return 0; return 0;
} }