Commit db219973 authored by Matt Carlson's avatar Matt Carlson Committed by David S. Miller

tg3: Schedule at most one tg3_reset_task run

It is possible for multiple threads in the tg3 driver to each attempt to
schedule a run of tg3_reset_task().  The multiple tg3_reset_task
executions could all wind up on the same queue (and execute serially) or
wind up on the queues of another processor (which could execute in
parallel).  Either scenario is not what was truly desired.

This patch adds a new flag, TG3_FLAG_RESET_TASK_PENDING, and uses it to
determine whether or not to schedule another run of tg3_reset_task().
With the new flag comes two new functions to facilitate scheduling and
descheduling of tg3_reset_task().
Signed-off-by: default avatarMatt Carlson <mcarlson@broadcom.com>
Reviewed-by: default avatarMichael Chan <mchan@broadcom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9dc5e342
...@@ -5929,6 +5929,18 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget) ...@@ -5929,6 +5929,18 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget)
return work_done; return work_done;
} }
static inline void tg3_reset_task_schedule(struct tg3 *tp)
{
if (!test_and_set_bit(TG3_FLAG_RESET_TASK_PENDING, tp->tg3_flags))
schedule_work(&tp->reset_task);
}
static inline void tg3_reset_task_cancel(struct tg3 *tp)
{
cancel_work_sync(&tp->reset_task);
tg3_flag_clear(tp, RESET_TASK_PENDING);
}
static int tg3_poll_msix(struct napi_struct *napi, int budget) static int tg3_poll_msix(struct napi_struct *napi, int budget)
{ {
struct tg3_napi *tnapi = container_of(napi, struct tg3_napi, napi); struct tg3_napi *tnapi = container_of(napi, struct tg3_napi, napi);
...@@ -5969,7 +5981,7 @@ static int tg3_poll_msix(struct napi_struct *napi, int budget) ...@@ -5969,7 +5981,7 @@ static int tg3_poll_msix(struct napi_struct *napi, int budget)
tx_recovery: tx_recovery:
/* work_done is guaranteed to be less than budget. */ /* work_done is guaranteed to be less than budget. */
napi_complete(napi); napi_complete(napi);
schedule_work(&tp->reset_task); tg3_reset_task_schedule(tp);
return work_done; return work_done;
} }
...@@ -6004,7 +6016,7 @@ static void tg3_process_error(struct tg3 *tp) ...@@ -6004,7 +6016,7 @@ static void tg3_process_error(struct tg3 *tp)
tg3_dump_state(tp); tg3_dump_state(tp);
tg3_flag_set(tp, ERROR_PROCESSED); tg3_flag_set(tp, ERROR_PROCESSED);
schedule_work(&tp->reset_task); tg3_reset_task_schedule(tp);
} }
static int tg3_poll(struct napi_struct *napi, int budget) static int tg3_poll(struct napi_struct *napi, int budget)
...@@ -6051,7 +6063,7 @@ static int tg3_poll(struct napi_struct *napi, int budget) ...@@ -6051,7 +6063,7 @@ static int tg3_poll(struct napi_struct *napi, int budget)
tx_recovery: tx_recovery:
/* work_done is guaranteed to be less than budget. */ /* work_done is guaranteed to be less than budget. */
napi_complete(napi); napi_complete(napi);
schedule_work(&tp->reset_task); tg3_reset_task_schedule(tp);
return work_done; return work_done;
} }
...@@ -6345,6 +6357,7 @@ static void tg3_reset_task(struct work_struct *work) ...@@ -6345,6 +6357,7 @@ static void tg3_reset_task(struct work_struct *work)
tg3_full_lock(tp, 0); tg3_full_lock(tp, 0);
if (!netif_running(tp->dev)) { if (!netif_running(tp->dev)) {
tg3_flag_clear(tp, RESET_TASK_PENDING);
tg3_full_unlock(tp); tg3_full_unlock(tp);
return; return;
} }
...@@ -6382,6 +6395,8 @@ static void tg3_reset_task(struct work_struct *work) ...@@ -6382,6 +6395,8 @@ static void tg3_reset_task(struct work_struct *work)
if (!err) if (!err)
tg3_phy_start(tp); tg3_phy_start(tp);
tg3_flag_clear(tp, RESET_TASK_PENDING);
} }
static void tg3_tx_timeout(struct net_device *dev) static void tg3_tx_timeout(struct net_device *dev)
...@@ -6393,7 +6408,7 @@ static void tg3_tx_timeout(struct net_device *dev) ...@@ -6393,7 +6408,7 @@ static void tg3_tx_timeout(struct net_device *dev)
tg3_dump_state(tp); tg3_dump_state(tp);
} }
schedule_work(&tp->reset_task); tg3_reset_task_schedule(tp);
} }
/* Test for DMA buffers crossing any 4GB boundaries: 4G, 8G, etc */ /* Test for DMA buffers crossing any 4GB boundaries: 4G, 8G, etc */
...@@ -9228,7 +9243,7 @@ static void tg3_timer(unsigned long __opaque) ...@@ -9228,7 +9243,7 @@ static void tg3_timer(unsigned long __opaque)
if (!(tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) { if (!(tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) {
tg3_flag_set(tp, RESTART_TIMER); tg3_flag_set(tp, RESTART_TIMER);
spin_unlock(&tp->lock); spin_unlock(&tp->lock);
schedule_work(&tp->reset_task); tg3_reset_task_schedule(tp);
return; return;
} }
} }
...@@ -9785,7 +9800,7 @@ static int tg3_close(struct net_device *dev) ...@@ -9785,7 +9800,7 @@ static int tg3_close(struct net_device *dev)
struct tg3 *tp = netdev_priv(dev); struct tg3 *tp = netdev_priv(dev);
tg3_napi_disable(tp); tg3_napi_disable(tp);
cancel_work_sync(&tp->reset_task); tg3_reset_task_cancel(tp);
netif_tx_stop_all_queues(dev); netif_tx_stop_all_queues(dev);
...@@ -15685,7 +15700,7 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev) ...@@ -15685,7 +15700,7 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev)
if (tp->fw) if (tp->fw)
release_firmware(tp->fw); release_firmware(tp->fw);
cancel_work_sync(&tp->reset_task); tg3_reset_task_cancel(tp);
if (tg3_flag(tp, USE_PHYLIB)) { if (tg3_flag(tp, USE_PHYLIB)) {
tg3_phy_fini(tp); tg3_phy_fini(tp);
...@@ -15719,7 +15734,7 @@ static int tg3_suspend(struct device *device) ...@@ -15719,7 +15734,7 @@ static int tg3_suspend(struct device *device)
if (!netif_running(dev)) if (!netif_running(dev))
return 0; return 0;
flush_work_sync(&tp->reset_task); tg3_reset_task_cancel(tp);
tg3_phy_stop(tp); tg3_phy_stop(tp);
tg3_netif_stop(tp); tg3_netif_stop(tp);
...@@ -15835,7 +15850,7 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev, ...@@ -15835,7 +15850,7 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
tg3_flag_clear(tp, RESTART_TIMER); tg3_flag_clear(tp, RESTART_TIMER);
/* Want to make sure that the reset task doesn't run */ /* Want to make sure that the reset task doesn't run */
cancel_work_sync(&tp->reset_task); tg3_reset_task_cancel(tp);
tg3_flag_clear(tp, TX_RECOVERY_PENDING); tg3_flag_clear(tp, TX_RECOVERY_PENDING);
tg3_flag_clear(tp, RESTART_TIMER); tg3_flag_clear(tp, RESTART_TIMER);
......
...@@ -2922,6 +2922,7 @@ enum TG3_FLAGS { ...@@ -2922,6 +2922,7 @@ enum TG3_FLAGS {
TG3_FLAG_APE_HAS_NCSI, TG3_FLAG_APE_HAS_NCSI,
TG3_FLAG_5717_PLUS, TG3_FLAG_5717_PLUS,
TG3_FLAG_4K_FIFO_LIMIT, TG3_FLAG_4K_FIFO_LIMIT,
TG3_FLAG_RESET_TASK_PENDING,
/* Add new flags before this comment and TG3_FLAG_NUMBER_OF_FLAGS */ /* Add new flags before this comment and TG3_FLAG_NUMBER_OF_FLAGS */
TG3_FLAG_NUMBER_OF_FLAGS, /* Last entry in enum TG3_FLAGS */ TG3_FLAG_NUMBER_OF_FLAGS, /* Last entry in enum TG3_FLAGS */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment