diff options
author | Emil Tantilov <emil.s.tantilov@intel.com> | 2015-01-27 22:21:34 -0500 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2015-02-05 22:58:45 -0500 |
commit | 9ac5c5ccdbfd41c1dea802462a9b0abcfc106abc (patch) | |
tree | 57a985688edf80ef30efa9df0a373af08e63549e /drivers/net/ethernet | |
parent | e66c92ad5839ffd0ffd0ac7f7afd622151ef6272 (diff) |
ixgbevf: combine all of the tasks into a single service task
This change combines the reset and watchdog tasklets into a single task.
The advantage of this is that we can avoid multiple schedules of the reset
task when we have a reset event needed due to either the mailbox going down
or transmit packets being present on a link down.
CC: Alexander Duyck <alexander.h.duyck@redhat.com>
Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
Tested-by: Phil Schmitt <phillip.j.schmitt@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/intel/ixgbevf/ixgbevf.h | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 140 |
2 files changed, 87 insertions, 66 deletions
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index a41ab370278f..3a9b356dff01 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h | |||
@@ -367,8 +367,6 @@ struct ixgbevf_adapter { | |||
367 | /* this field must be first, see ixgbevf_process_skb_fields */ | 367 | /* this field must be first, see ixgbevf_process_skb_fields */ |
368 | unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; | 368 | unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; |
369 | 369 | ||
370 | struct timer_list watchdog_timer; | ||
371 | struct work_struct reset_task; | ||
372 | struct ixgbevf_q_vector *q_vector[MAX_MSIX_Q_VECTORS]; | 370 | struct ixgbevf_q_vector *q_vector[MAX_MSIX_Q_VECTORS]; |
373 | 371 | ||
374 | /* Interrupt Throttle Rate */ | 372 | /* Interrupt Throttle Rate */ |
@@ -398,8 +396,7 @@ struct ixgbevf_adapter { | |||
398 | * thus the additional *_CAPABLE flags. | 396 | * thus the additional *_CAPABLE flags. |
399 | */ | 397 | */ |
400 | u32 flags; | 398 | u32 flags; |
401 | #define IXGBE_FLAG_IN_WATCHDOG_TASK (u32)(1) | 399 | #define IXGBEVF_FLAG_RESET_REQUESTED (u32)(1) |
402 | |||
403 | #define IXGBEVF_FLAG_QUEUE_RESET_REQUESTED (u32)(1 << 2) | 400 | #define IXGBEVF_FLAG_QUEUE_RESET_REQUESTED (u32)(1 << 2) |
404 | 401 | ||
405 | struct msix_entry *msix_entries; | 402 | struct msix_entry *msix_entries; |
@@ -435,10 +432,11 @@ struct ixgbevf_adapter { | |||
435 | u32 link_speed; | 432 | u32 link_speed; |
436 | bool link_up; | 433 | bool link_up; |
437 | 434 | ||
435 | struct timer_list service_timer; | ||
436 | struct work_struct service_task; | ||
437 | |||
438 | spinlock_t mbx_lock; | 438 | spinlock_t mbx_lock; |
439 | unsigned long last_reset; | 439 | unsigned long last_reset; |
440 | |||
441 | struct work_struct watchdog_task; | ||
442 | }; | 440 | }; |
443 | 441 | ||
444 | enum ixbgevf_state_t { | 442 | enum ixbgevf_state_t { |
@@ -447,7 +445,8 @@ enum ixbgevf_state_t { | |||
447 | __IXGBEVF_DOWN, | 445 | __IXGBEVF_DOWN, |
448 | __IXGBEVF_DISABLED, | 446 | __IXGBEVF_DISABLED, |
449 | __IXGBEVF_REMOVING, | 447 | __IXGBEVF_REMOVING, |
450 | __IXGBEVF_WORK_INIT, | 448 | __IXGBEVF_SERVICE_SCHED, |
449 | __IXGBEVF_SERVICE_INITED, | ||
451 | }; | 450 | }; |
452 | 451 | ||
453 | enum ixgbevf_boards { | 452 | enum ixgbevf_boards { |
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index c1100654a4be..4186981e562d 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | |||
@@ -98,6 +98,23 @@ static int debug = -1; | |||
98 | module_param(debug, int, 0); | 98 | module_param(debug, int, 0); |
99 | MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); | 99 | MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); |
100 | 100 | ||
101 | static void ixgbevf_service_event_schedule(struct ixgbevf_adapter *adapter) | ||
102 | { | ||
103 | if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && | ||
104 | !test_bit(__IXGBEVF_REMOVING, &adapter->state) && | ||
105 | !test_and_set_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state)) | ||
106 | schedule_work(&adapter->service_task); | ||
107 | } | ||
108 | |||
109 | static void ixgbevf_service_event_complete(struct ixgbevf_adapter *adapter) | ||
110 | { | ||
111 | BUG_ON(!test_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state)); | ||
112 | |||
113 | /* flush memory to make sure state is correct before next watchdog */ | ||
114 | smp_mb__before_atomic(); | ||
115 | clear_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state); | ||
116 | } | ||
117 | |||
101 | /* forward decls */ | 118 | /* forward decls */ |
102 | static void ixgbevf_queue_reset_subtask(struct ixgbevf_adapter *adapter); | 119 | static void ixgbevf_queue_reset_subtask(struct ixgbevf_adapter *adapter); |
103 | static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector); | 120 | static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector); |
@@ -111,8 +128,8 @@ static void ixgbevf_remove_adapter(struct ixgbe_hw *hw) | |||
111 | return; | 128 | return; |
112 | hw->hw_addr = NULL; | 129 | hw->hw_addr = NULL; |
113 | dev_err(&adapter->pdev->dev, "Adapter removed\n"); | 130 | dev_err(&adapter->pdev->dev, "Adapter removed\n"); |
114 | if (test_bit(__IXGBEVF_WORK_INIT, &adapter->state)) | 131 | if (test_bit(__IXGBEVF_SERVICE_INITED, &adapter->state)) |
115 | schedule_work(&adapter->watchdog_task); | 132 | ixgbevf_service_event_schedule(adapter); |
116 | } | 133 | } |
117 | 134 | ||
118 | static void ixgbevf_check_remove(struct ixgbe_hw *hw, u32 reg) | 135 | static void ixgbevf_check_remove(struct ixgbe_hw *hw, u32 reg) |
@@ -246,6 +263,15 @@ static inline bool ixgbevf_check_tx_hang(struct ixgbevf_ring *tx_ring) | |||
246 | return false; | 263 | return false; |
247 | } | 264 | } |
248 | 265 | ||
266 | static void ixgbevf_tx_timeout_reset(struct ixgbevf_adapter *adapter) | ||
267 | { | ||
268 | /* Do the reset outside of interrupt context */ | ||
269 | if (!test_bit(__IXGBEVF_DOWN, &adapter->state)) { | ||
270 | adapter->flags |= IXGBEVF_FLAG_RESET_REQUESTED; | ||
271 | ixgbevf_service_event_schedule(adapter); | ||
272 | } | ||
273 | } | ||
274 | |||
249 | /** | 275 | /** |
250 | * ixgbevf_tx_timeout - Respond to a Tx Hang | 276 | * ixgbevf_tx_timeout - Respond to a Tx Hang |
251 | * @netdev: network interface device structure | 277 | * @netdev: network interface device structure |
@@ -254,8 +280,7 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) | |||
254 | { | 280 | { |
255 | struct ixgbevf_adapter *adapter = netdev_priv(netdev); | 281 | struct ixgbevf_adapter *adapter = netdev_priv(netdev); |
256 | 282 | ||
257 | /* Do the reset outside of interrupt context */ | 283 | ixgbevf_tx_timeout_reset(adapter); |
258 | schedule_work(&adapter->reset_task); | ||
259 | } | 284 | } |
260 | 285 | ||
261 | /** | 286 | /** |
@@ -387,7 +412,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, | |||
387 | netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); | 412 | netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); |
388 | 413 | ||
389 | /* schedule immediate reset if we believe we hung */ | 414 | /* schedule immediate reset if we believe we hung */ |
390 | schedule_work(&adapter->reset_task); | 415 | ixgbevf_tx_timeout_reset(adapter); |
391 | 416 | ||
392 | return true; | 417 | return true; |
393 | } | 418 | } |
@@ -1239,9 +1264,7 @@ static irqreturn_t ixgbevf_msix_other(int irq, void *data) | |||
1239 | 1264 | ||
1240 | hw->mac.get_link_status = 1; | 1265 | hw->mac.get_link_status = 1; |
1241 | 1266 | ||
1242 | if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && | 1267 | ixgbevf_service_event_schedule(adapter); |
1243 | !test_bit(__IXGBEVF_REMOVING, &adapter->state)) | ||
1244 | mod_timer(&adapter->watchdog_timer, jiffies); | ||
1245 | 1268 | ||
1246 | IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, adapter->eims_other); | 1269 | IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, adapter->eims_other); |
1247 | 1270 | ||
@@ -2051,7 +2074,7 @@ static void ixgbevf_up_complete(struct ixgbevf_adapter *adapter) | |||
2051 | ixgbevf_init_last_counter_stats(adapter); | 2074 | ixgbevf_init_last_counter_stats(adapter); |
2052 | 2075 | ||
2053 | hw->mac.get_link_status = 1; | 2076 | hw->mac.get_link_status = 1; |
2054 | mod_timer(&adapter->watchdog_timer, jiffies); | 2077 | mod_timer(&adapter->service_timer, jiffies); |
2055 | } | 2078 | } |
2056 | 2079 | ||
2057 | void ixgbevf_up(struct ixgbevf_adapter *adapter) | 2080 | void ixgbevf_up(struct ixgbevf_adapter *adapter) |
@@ -2177,13 +2200,7 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter) | |||
2177 | 2200 | ||
2178 | ixgbevf_napi_disable_all(adapter); | 2201 | ixgbevf_napi_disable_all(adapter); |
2179 | 2202 | ||
2180 | del_timer_sync(&adapter->watchdog_timer); | 2203 | del_timer_sync(&adapter->service_timer); |
2181 | |||
2182 | /* can't call flush scheduled work here because it can deadlock | ||
2183 | * if linkwatch_event tries to acquire the rtnl_lock which we are | ||
2184 | * holding */ | ||
2185 | while (adapter->flags & IXGBE_FLAG_IN_WATCHDOG_TASK) | ||
2186 | msleep(1); | ||
2187 | 2204 | ||
2188 | /* disable transmits in the hardware now that interrupts are off */ | 2205 | /* disable transmits in the hardware now that interrupts are off */ |
2189 | for (i = 0; i < adapter->num_tx_queues; i++) { | 2206 | for (i = 0; i < adapter->num_tx_queues; i++) { |
@@ -2711,22 +2728,25 @@ void ixgbevf_update_stats(struct ixgbevf_adapter *adapter) | |||
2711 | } | 2728 | } |
2712 | 2729 | ||
2713 | /** | 2730 | /** |
2714 | * ixgbevf_watchdog - Timer Call-back | 2731 | * ixgbevf_service_timer - Timer Call-back |
2715 | * @data: pointer to adapter cast into an unsigned long | 2732 | * @data: pointer to adapter cast into an unsigned long |
2716 | **/ | 2733 | **/ |
2717 | static void ixgbevf_watchdog(unsigned long data) | 2734 | static void ixgbevf_service_timer(unsigned long data) |
2718 | { | 2735 | { |
2719 | struct ixgbevf_adapter *adapter = (struct ixgbevf_adapter *)data; | 2736 | struct ixgbevf_adapter *adapter = (struct ixgbevf_adapter *)data; |
2720 | 2737 | ||
2721 | /* Do the reset outside of interrupt context */ | 2738 | /* Reset the timer */ |
2722 | schedule_work(&adapter->watchdog_task); | 2739 | mod_timer(&adapter->service_timer, (HZ * 2) + jiffies); |
2740 | |||
2741 | ixgbevf_service_event_schedule(adapter); | ||
2723 | } | 2742 | } |
2724 | 2743 | ||
2725 | static void ixgbevf_reset_task(struct work_struct *work) | 2744 | static void ixgbevf_reset_subtask(struct ixgbevf_adapter *adapter) |
2726 | { | 2745 | { |
2727 | struct ixgbevf_adapter *adapter; | 2746 | if (!(adapter->flags & IXGBEVF_FLAG_RESET_REQUESTED)) |
2747 | return; | ||
2728 | 2748 | ||
2729 | adapter = container_of(work, struct ixgbevf_adapter, reset_task); | 2749 | adapter->flags &= ~IXGBEVF_FLAG_RESET_REQUESTED; |
2730 | 2750 | ||
2731 | /* If we're already down or resetting, just bail */ | 2751 | /* If we're already down or resetting, just bail */ |
2732 | if (test_bit(__IXGBEVF_DOWN, &adapter->state) || | 2752 | if (test_bit(__IXGBEVF_DOWN, &adapter->state) || |
@@ -2766,6 +2786,7 @@ static void ixgbevf_check_hang_subtask(struct ixgbevf_adapter *adapter) | |||
2766 | /* get one bit for every active tx/rx interrupt vector */ | 2786 | /* get one bit for every active tx/rx interrupt vector */ |
2767 | for (i = 0; i < adapter->num_msix_vectors - NON_Q_VECTORS; i++) { | 2787 | for (i = 0; i < adapter->num_msix_vectors - NON_Q_VECTORS; i++) { |
2768 | struct ixgbevf_q_vector *qv = adapter->q_vector[i]; | 2788 | struct ixgbevf_q_vector *qv = adapter->q_vector[i]; |
2789 | |||
2769 | if (qv->rx.ring || qv->tx.ring) | 2790 | if (qv->rx.ring || qv->tx.ring) |
2770 | eics |= 1 << i; | 2791 | eics |= 1 << i; |
2771 | } | 2792 | } |
@@ -2793,7 +2814,7 @@ static void ixgbevf_watchdog_update_link(struct ixgbevf_adapter *adapter) | |||
2793 | 2814 | ||
2794 | /* if check for link returns error we will need to reset */ | 2815 | /* if check for link returns error we will need to reset */ |
2795 | if (err && time_after(jiffies, adapter->last_reset + (10 * HZ))) { | 2816 | if (err && time_after(jiffies, adapter->last_reset + (10 * HZ))) { |
2796 | schedule_work(&adapter->reset_task); | 2817 | adapter->flags |= IXGBEVF_FLAG_RESET_REQUESTED; |
2797 | link_up = false; | 2818 | link_up = false; |
2798 | } | 2819 | } |
2799 | 2820 | ||
@@ -2847,14 +2868,35 @@ static void ixgbevf_watchdog_link_is_down(struct ixgbevf_adapter *adapter) | |||
2847 | } | 2868 | } |
2848 | 2869 | ||
2849 | /** | 2870 | /** |
2850 | * ixgbevf_watchdog_task - worker thread to bring link up | 2871 | * ixgbevf_watchdog_subtask - worker thread to bring link up |
2872 | * @work: pointer to work_struct containing our data | ||
2873 | **/ | ||
2874 | static void ixgbevf_watchdog_subtask(struct ixgbevf_adapter *adapter) | ||
2875 | { | ||
2876 | /* if interface is down do nothing */ | ||
2877 | if (test_bit(__IXGBEVF_DOWN, &adapter->state) || | ||
2878 | test_bit(__IXGBEVF_RESETTING, &adapter->state)) | ||
2879 | return; | ||
2880 | |||
2881 | ixgbevf_watchdog_update_link(adapter); | ||
2882 | |||
2883 | if (adapter->link_up) | ||
2884 | ixgbevf_watchdog_link_is_up(adapter); | ||
2885 | else | ||
2886 | ixgbevf_watchdog_link_is_down(adapter); | ||
2887 | |||
2888 | ixgbevf_update_stats(adapter); | ||
2889 | } | ||
2890 | |||
2891 | /** | ||
2892 | * ixgbevf_service_task - manages and runs subtasks | ||
2851 | * @work: pointer to work_struct containing our data | 2893 | * @work: pointer to work_struct containing our data |
2852 | **/ | 2894 | **/ |
2853 | static void ixgbevf_watchdog_task(struct work_struct *work) | 2895 | static void ixgbevf_service_task(struct work_struct *work) |
2854 | { | 2896 | { |
2855 | struct ixgbevf_adapter *adapter = container_of(work, | 2897 | struct ixgbevf_adapter *adapter = container_of(work, |
2856 | struct ixgbevf_adapter, | 2898 | struct ixgbevf_adapter, |
2857 | watchdog_task); | 2899 | service_task); |
2858 | struct ixgbe_hw *hw = &adapter->hw; | 2900 | struct ixgbe_hw *hw = &adapter->hw; |
2859 | 2901 | ||
2860 | if (IXGBE_REMOVED(hw->hw_addr)) { | 2902 | if (IXGBE_REMOVED(hw->hw_addr)) { |
@@ -2867,27 +2909,11 @@ static void ixgbevf_watchdog_task(struct work_struct *work) | |||
2867 | } | 2909 | } |
2868 | 2910 | ||
2869 | ixgbevf_queue_reset_subtask(adapter); | 2911 | ixgbevf_queue_reset_subtask(adapter); |
2870 | 2912 | ixgbevf_reset_subtask(adapter); | |
2871 | adapter->flags |= IXGBE_FLAG_IN_WATCHDOG_TASK; | 2913 | ixgbevf_watchdog_subtask(adapter); |
2872 | |||
2873 | ixgbevf_watchdog_update_link(adapter); | ||
2874 | |||
2875 | if (adapter->link_up) | ||
2876 | ixgbevf_watchdog_link_is_up(adapter); | ||
2877 | else | ||
2878 | ixgbevf_watchdog_link_is_down(adapter); | ||
2879 | |||
2880 | ixgbevf_update_stats(adapter); | ||
2881 | |||
2882 | ixgbevf_check_hang_subtask(adapter); | 2914 | ixgbevf_check_hang_subtask(adapter); |
2883 | 2915 | ||
2884 | /* Reset the timer */ | 2916 | ixgbevf_service_event_complete(adapter); |
2885 | if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && | ||
2886 | !test_bit(__IXGBEVF_REMOVING, &adapter->state)) | ||
2887 | mod_timer(&adapter->watchdog_timer, | ||
2888 | round_jiffies(jiffies + (2 * HZ))); | ||
2889 | |||
2890 | adapter->flags &= ~IXGBE_FLAG_IN_WATCHDOG_TASK; | ||
2891 | } | 2917 | } |
2892 | 2918 | ||
2893 | /** | 2919 | /** |
@@ -3994,17 +4020,17 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
3994 | 4020 | ||
3995 | netdev->priv_flags |= IFF_UNICAST_FLT; | 4021 | netdev->priv_flags |= IFF_UNICAST_FLT; |
3996 | 4022 | ||
3997 | init_timer(&adapter->watchdog_timer); | ||
3998 | adapter->watchdog_timer.function = ixgbevf_watchdog; | ||
3999 | adapter->watchdog_timer.data = (unsigned long)adapter; | ||
4000 | |||
4001 | if (IXGBE_REMOVED(hw->hw_addr)) { | 4023 | if (IXGBE_REMOVED(hw->hw_addr)) { |
4002 | err = -EIO; | 4024 | err = -EIO; |
4003 | goto err_sw_init; | 4025 | goto err_sw_init; |
4004 | } | 4026 | } |
4005 | INIT_WORK(&adapter->reset_task, ixgbevf_reset_task); | 4027 | |
4006 | INIT_WORK(&adapter->watchdog_task, ixgbevf_watchdog_task); | 4028 | setup_timer(&adapter->service_timer, &ixgbevf_service_timer, |
4007 | set_bit(__IXGBEVF_WORK_INIT, &adapter->state); | 4029 | (unsigned long)adapter); |
4030 | |||
4031 | INIT_WORK(&adapter->service_task, ixgbevf_service_task); | ||
4032 | set_bit(__IXGBEVF_SERVICE_INITED, &adapter->state); | ||
4033 | clear_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state); | ||
4008 | 4034 | ||
4009 | err = ixgbevf_init_interrupt_scheme(adapter); | 4035 | err = ixgbevf_init_interrupt_scheme(adapter); |
4010 | if (err) | 4036 | if (err) |
@@ -4078,11 +4104,7 @@ static void ixgbevf_remove(struct pci_dev *pdev) | |||
4078 | adapter = netdev_priv(netdev); | 4104 | adapter = netdev_priv(netdev); |
4079 | 4105 | ||
4080 | set_bit(__IXGBEVF_REMOVING, &adapter->state); | 4106 | set_bit(__IXGBEVF_REMOVING, &adapter->state); |
4081 | 4107 | cancel_work_sync(&adapter->service_task); | |
4082 | del_timer_sync(&adapter->watchdog_timer); | ||
4083 | |||
4084 | cancel_work_sync(&adapter->reset_task); | ||
4085 | cancel_work_sync(&adapter->watchdog_task); | ||
4086 | 4108 | ||
4087 | if (netdev->reg_state == NETREG_REGISTERED) | 4109 | if (netdev->reg_state == NETREG_REGISTERED) |
4088 | unregister_netdev(netdev); | 4110 | unregister_netdev(netdev); |
@@ -4116,7 +4138,7 @@ static pci_ers_result_t ixgbevf_io_error_detected(struct pci_dev *pdev, | |||
4116 | struct net_device *netdev = pci_get_drvdata(pdev); | 4138 | struct net_device *netdev = pci_get_drvdata(pdev); |
4117 | struct ixgbevf_adapter *adapter = netdev_priv(netdev); | 4139 | struct ixgbevf_adapter *adapter = netdev_priv(netdev); |
4118 | 4140 | ||
4119 | if (!test_bit(__IXGBEVF_WORK_INIT, &adapter->state)) | 4141 | if (!test_bit(__IXGBEVF_SERVICE_INITED, &adapter->state)) |
4120 | return PCI_ERS_RESULT_DISCONNECT; | 4142 | return PCI_ERS_RESULT_DISCONNECT; |
4121 | 4143 | ||
4122 | rtnl_lock(); | 4144 | rtnl_lock(); |