diff options
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-1000.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-5000.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 18 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 68 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-shared.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h | 16 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | 27 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 77 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.h | 12 |
11 files changed, 91 insertions, 141 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 95c59e39b803..3787f845cbd6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c | |||
@@ -165,9 +165,8 @@ static const struct iwl_base_params iwl1000_base_params = { | |||
165 | .support_ct_kill_exit = true, | 165 | .support_ct_kill_exit = true, |
166 | .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, | 166 | .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, |
167 | .chain_noise_scale = 1000, | 167 | .chain_noise_scale = 1000, |
168 | .wd_timeout = IWL_DEF_WD_TIMEOUT, | 168 | .wd_timeout = IWL_WATCHHDOG_DISABLED, |
169 | .max_event_log_size = 128, | 169 | .max_event_log_size = 128, |
170 | .wd_disable = true, | ||
171 | }; | 170 | }; |
172 | 171 | ||
173 | static const struct iwl_ht_params iwl1000_ht_params = { | 172 | static const struct iwl_ht_params iwl1000_ht_params = { |
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 34bc8dd0064b..9f379d3dad18 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c | |||
@@ -312,10 +312,9 @@ static const struct iwl_base_params iwl5000_base_params = { | |||
312 | .led_compensation = 51, | 312 | .led_compensation = 51, |
313 | .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, | 313 | .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, |
314 | .chain_noise_scale = 1000, | 314 | .chain_noise_scale = 1000, |
315 | .wd_timeout = IWL_LONG_WD_TIMEOUT, | 315 | .wd_timeout = IWL_WATCHHDOG_DISABLED, |
316 | .max_event_log_size = 512, | 316 | .max_event_log_size = 512, |
317 | .no_idle_support = true, | 317 | .no_idle_support = true, |
318 | .wd_disable = true, | ||
319 | }; | 318 | }; |
320 | 319 | ||
321 | static const struct iwl_ht_params iwl5000_ht_params = { | 320 | static const struct iwl_ht_params iwl5000_ht_params = { |
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 3d920f92a3f3..514719957919 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c | |||
@@ -741,9 +741,6 @@ int iwl_alive_start(struct iwl_priv *priv) | |||
741 | /* After the ALIVE response, we can send host commands to the uCode */ | 741 | /* After the ALIVE response, we can send host commands to the uCode */ |
742 | set_bit(STATUS_ALIVE, &priv->status); | 742 | set_bit(STATUS_ALIVE, &priv->status); |
743 | 743 | ||
744 | /* Enable watchdog to monitor the driver tx queues */ | ||
745 | iwl_setup_watchdog(priv); | ||
746 | |||
747 | if (iwl_is_rfkill(priv)) | 744 | if (iwl_is_rfkill(priv)) |
748 | return -ERFKILL; | 745 | return -ERFKILL; |
749 | 746 | ||
@@ -887,10 +884,6 @@ void iwl_down(struct iwl_priv *priv) | |||
887 | exit_pending = | 884 | exit_pending = |
888 | test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); | 885 | test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); |
889 | 886 | ||
890 | /* Stop TX queues watchdog. We need to have STATUS_EXIT_PENDING bit set | ||
891 | * to prevent rearm timer */ | ||
892 | del_timer_sync(&priv->watchdog); | ||
893 | |||
894 | iwl_clear_ucode_stations(priv, NULL); | 887 | iwl_clear_ucode_stations(priv, NULL); |
895 | iwl_dealloc_bcast_stations(priv); | 888 | iwl_dealloc_bcast_stations(priv); |
896 | iwl_clear_driver_stations(priv); | 889 | iwl_clear_driver_stations(priv); |
@@ -1092,10 +1085,6 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) | |||
1092 | init_timer(&priv->ucode_trace); | 1085 | init_timer(&priv->ucode_trace); |
1093 | priv->ucode_trace.data = (unsigned long)priv; | 1086 | priv->ucode_trace.data = (unsigned long)priv; |
1094 | priv->ucode_trace.function = iwl_bg_ucode_trace; | 1087 | priv->ucode_trace.function = iwl_bg_ucode_trace; |
1095 | |||
1096 | init_timer(&priv->watchdog); | ||
1097 | priv->watchdog.data = (unsigned long)priv; | ||
1098 | priv->watchdog.function = iwl_bg_watchdog; | ||
1099 | } | 1088 | } |
1100 | 1089 | ||
1101 | void iwl_cancel_deferred_work(struct iwl_priv *priv) | 1090 | void iwl_cancel_deferred_work(struct iwl_priv *priv) |
@@ -1410,8 +1399,6 @@ static void iwl_set_hw_params(struct iwl_priv *priv) | |||
1410 | if (iwlagn_mod_params.disable_11n & IWL_DISABLE_HT_ALL) | 1399 | if (iwlagn_mod_params.disable_11n & IWL_DISABLE_HT_ALL) |
1411 | hw_params(priv).sku &= ~EEPROM_SKU_CAP_11N_ENABLE; | 1400 | hw_params(priv).sku &= ~EEPROM_SKU_CAP_11N_ENABLE; |
1412 | 1401 | ||
1413 | hw_params(priv).wd_timeout = cfg(priv)->base_params->wd_timeout; | ||
1414 | |||
1415 | /* Device-specific setup */ | 1402 | /* Device-specific setup */ |
1416 | cfg(priv)->lib->set_hw_params(priv); | 1403 | cfg(priv)->lib->set_hw_params(priv); |
1417 | } | 1404 | } |
@@ -1498,6 +1485,11 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, | |||
1498 | trans_cfg.no_reclaim_cmds = no_reclaim_cmds; | 1485 | trans_cfg.no_reclaim_cmds = no_reclaim_cmds; |
1499 | trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); | 1486 | trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); |
1500 | trans_cfg.rx_buf_size_8k = iwlagn_mod_params.amsdu_size_8K; | 1487 | trans_cfg.rx_buf_size_8k = iwlagn_mod_params.amsdu_size_8K; |
1488 | if (!iwlagn_mod_params.wd_disable) | ||
1489 | trans_cfg.queue_watchdog_timeout = | ||
1490 | cfg(priv)->base_params->wd_timeout; | ||
1491 | else | ||
1492 | trans_cfg.queue_watchdog_timeout = IWL_WATCHHDOG_DISABLED; | ||
1501 | 1493 | ||
1502 | ucode_flags = fw->ucode_capa.flags; | 1494 | ucode_flags = fw->ucode_capa.flags; |
1503 | 1495 | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 88ea31d9eb75..6fc1841ff536 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c | |||
@@ -837,74 +837,6 @@ int iwl_cmd_echo_test(struct iwl_priv *priv) | |||
837 | return ret; | 837 | return ret; |
838 | } | 838 | } |
839 | 839 | ||
840 | static inline int iwl_check_stuck_queue(struct iwl_priv *priv, int txq) | ||
841 | { | ||
842 | if (iwl_trans_check_stuck_queue(trans(priv), txq)) { | ||
843 | int ret; | ||
844 | ret = iwl_force_reset(priv, IWL_FW_RESET, false); | ||
845 | return (ret == -EAGAIN) ? 0 : 1; | ||
846 | } | ||
847 | return 0; | ||
848 | } | ||
849 | |||
850 | /* | ||
851 | * Making watchdog tick be a quarter of timeout assure we will | ||
852 | * discover the queue hung between timeout and 1.25*timeout | ||
853 | */ | ||
854 | #define IWL_WD_TICK(timeout) ((timeout) / 4) | ||
855 | |||
856 | /* | ||
857 | * Watchdog timer callback, we check each tx queue for stuck, if if hung | ||
858 | * we reset the firmware. If everything is fine just rearm the timer. | ||
859 | */ | ||
860 | void iwl_bg_watchdog(unsigned long data) | ||
861 | { | ||
862 | struct iwl_priv *priv = (struct iwl_priv *)data; | ||
863 | int cnt; | ||
864 | unsigned long timeout; | ||
865 | |||
866 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | ||
867 | return; | ||
868 | |||
869 | if (iwl_is_rfkill(priv)) | ||
870 | return; | ||
871 | |||
872 | timeout = hw_params(priv).wd_timeout; | ||
873 | if (timeout == 0) | ||
874 | return; | ||
875 | |||
876 | /* monitor and check for stuck queues */ | ||
877 | for (cnt = 0; cnt < cfg(priv)->base_params->num_of_queues; cnt++) | ||
878 | if (iwl_check_stuck_queue(priv, cnt)) | ||
879 | return; | ||
880 | |||
881 | mod_timer(&priv->watchdog, jiffies + | ||
882 | msecs_to_jiffies(IWL_WD_TICK(timeout))); | ||
883 | } | ||
884 | |||
885 | void iwl_setup_watchdog(struct iwl_priv *priv) | ||
886 | { | ||
887 | unsigned int timeout = hw_params(priv).wd_timeout; | ||
888 | |||
889 | if (!iwlagn_mod_params.wd_disable) { | ||
890 | /* use system default */ | ||
891 | if (timeout && !cfg(priv)->base_params->wd_disable) | ||
892 | mod_timer(&priv->watchdog, | ||
893 | jiffies + | ||
894 | msecs_to_jiffies(IWL_WD_TICK(timeout))); | ||
895 | else | ||
896 | del_timer(&priv->watchdog); | ||
897 | } else { | ||
898 | /* module parameter overwrite default configuration */ | ||
899 | if (timeout && iwlagn_mod_params.wd_disable == 2) | ||
900 | mod_timer(&priv->watchdog, | ||
901 | jiffies + | ||
902 | msecs_to_jiffies(IWL_WD_TICK(timeout))); | ||
903 | else | ||
904 | del_timer(&priv->watchdog); | ||
905 | } | ||
906 | } | ||
907 | |||
908 | /** | 840 | /** |
909 | * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time | 841 | * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time |
910 | * @priv -- pointer to iwl_priv data structure | 842 | * @priv -- pointer to iwl_priv data structure |
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 7aa3060fc6b5..f388dc4474da 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h | |||
@@ -151,7 +151,6 @@ static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx, | |||
151 | ******************************************************/ | 151 | ******************************************************/ |
152 | void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); | 152 | void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); |
153 | 153 | ||
154 | void iwl_setup_watchdog(struct iwl_priv *priv); | ||
155 | /***************************************************** | 154 | /***************************************************** |
156 | * TX power | 155 | * TX power |
157 | ****************************************************/ | 156 | ****************************************************/ |
@@ -193,7 +192,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv, | |||
193 | * S e n d i n g H o s t C o m m a n d s * | 192 | * S e n d i n g H o s t C o m m a n d s * |
194 | *****************************************************/ | 193 | *****************************************************/ |
195 | 194 | ||
196 | void iwl_bg_watchdog(unsigned long data); | ||
197 | u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval); | 195 | u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval); |
198 | __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, | 196 | __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, |
199 | u32 addon, u32 beacon_interval); | 197 | u32 addon, u32 beacon_interval); |
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 99be58940e27..780bcf3f6ff1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h | |||
@@ -585,6 +585,7 @@ struct iwl_event_log { | |||
585 | #define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) | 585 | #define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) |
586 | 586 | ||
587 | /* TX queue watchdog timeouts in mSecs */ | 587 | /* TX queue watchdog timeouts in mSecs */ |
588 | #define IWL_WATCHHDOG_DISABLED (0) | ||
588 | #define IWL_DEF_WD_TIMEOUT (2000) | 589 | #define IWL_DEF_WD_TIMEOUT (2000) |
589 | #define IWL_LONG_WD_TIMEOUT (10000) | 590 | #define IWL_LONG_WD_TIMEOUT (10000) |
590 | #define IWL_MAX_WD_TIMEOUT (120000) | 591 | #define IWL_MAX_WD_TIMEOUT (120000) |
@@ -973,7 +974,6 @@ struct iwl_priv { | |||
973 | struct work_struct run_time_calib_work; | 974 | struct work_struct run_time_calib_work; |
974 | struct timer_list statistics_periodic; | 975 | struct timer_list statistics_periodic; |
975 | struct timer_list ucode_trace; | 976 | struct timer_list ucode_trace; |
976 | struct timer_list watchdog; | ||
977 | 977 | ||
978 | struct iwl_event_log event_log; | 978 | struct iwl_event_log event_log; |
979 | 979 | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h index c2e5ce995fb4..c6049cfb653d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-shared.h +++ b/drivers/net/wireless/iwlwifi/iwl-shared.h | |||
@@ -169,7 +169,6 @@ struct iwl_mod_params { | |||
169 | * @ct_kill_threshold: temperature threshold - in hw dependent unit | 169 | * @ct_kill_threshold: temperature threshold - in hw dependent unit |
170 | * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit | 170 | * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit |
171 | * relevant for 1000, 6000 and up | 171 | * relevant for 1000, 6000 and up |
172 | * @wd_timeout: TX queues watchdog timeout | ||
173 | * @struct iwl_sensitivity_ranges: range of sensitivity values | 172 | * @struct iwl_sensitivity_ranges: range of sensitivity values |
174 | * @use_rts_for_aggregation: use rts/cts protection for HT traffic | 173 | * @use_rts_for_aggregation: use rts/cts protection for HT traffic |
175 | */ | 174 | */ |
@@ -183,7 +182,6 @@ struct iwl_hw_params { | |||
183 | u16 sku; | 182 | u16 sku; |
184 | u32 ct_kill_threshold; | 183 | u32 ct_kill_threshold; |
185 | u32 ct_kill_exit_threshold; | 184 | u32 ct_kill_exit_threshold; |
186 | unsigned int wd_timeout; | ||
187 | 185 | ||
188 | const struct iwl_sensitivity_ranges *sens; | 186 | const struct iwl_sensitivity_ranges *sens; |
189 | }; | 187 | }; |
@@ -221,7 +219,6 @@ enum iwl_led_mode { | |||
221 | * @shadow_reg_enable: HW shadhow register bit | 219 | * @shadow_reg_enable: HW shadhow register bit |
222 | * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up | 220 | * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up |
223 | * @no_idle_support: do not support idle mode | 221 | * @no_idle_support: do not support idle mode |
224 | * wd_disable: disable watchdog timer | ||
225 | */ | 222 | */ |
226 | struct iwl_base_params { | 223 | struct iwl_base_params { |
227 | int eeprom_size; | 224 | int eeprom_size; |
@@ -241,7 +238,6 @@ struct iwl_base_params { | |||
241 | const bool shadow_reg_enable; | 238 | const bool shadow_reg_enable; |
242 | const bool hd_v2; | 239 | const bool hd_v2; |
243 | const bool no_idle_support; | 240 | const bool no_idle_support; |
244 | const bool wd_disable; | ||
245 | }; | 241 | }; |
246 | 242 | ||
247 | /* | 243 | /* |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h index a1fc439aafd0..731d2750439c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/skbuff.h> | 34 | #include <linux/skbuff.h> |
35 | #include <linux/wait.h> | 35 | #include <linux/wait.h> |
36 | #include <linux/pci.h> | 36 | #include <linux/pci.h> |
37 | #include <linux/timer.h> | ||
37 | 38 | ||
38 | #include "iwl-fh.h" | 39 | #include "iwl-fh.h" |
39 | #include "iwl-csr.h" | 40 | #include "iwl-csr.h" |
@@ -204,7 +205,8 @@ struct iwl_tx_queue { | |||
204 | struct iwl_cmd_meta *meta; | 205 | struct iwl_cmd_meta *meta; |
205 | struct sk_buff **skbs; | 206 | struct sk_buff **skbs; |
206 | spinlock_t lock; | 207 | spinlock_t lock; |
207 | unsigned long time_stamp; | 208 | struct timer_list stuck_timer; |
209 | struct iwl_trans_pcie *trans_pcie; | ||
208 | u8 need_update; | 210 | u8 need_update; |
209 | u8 active; | 211 | u8 active; |
210 | }; | 212 | }; |
@@ -227,6 +229,7 @@ struct iwl_tx_queue { | |||
227 | * @cmd_queue - command queue number | 229 | * @cmd_queue - command queue number |
228 | * @rx_buf_size_8k: 8 kB RX buffer size | 230 | * @rx_buf_size_8k: 8 kB RX buffer size |
229 | * @rx_page_order: page order for receive buffer size | 231 | * @rx_page_order: page order for receive buffer size |
232 | * @wd_timeout: queue watchdog timeout (jiffies) | ||
230 | */ | 233 | */ |
231 | struct iwl_trans_pcie { | 234 | struct iwl_trans_pcie { |
232 | struct iwl_rx_queue rxq; | 235 | struct iwl_rx_queue rxq; |
@@ -269,11 +272,22 @@ struct iwl_trans_pcie { | |||
269 | 272 | ||
270 | bool rx_buf_size_8k; | 273 | bool rx_buf_size_8k; |
271 | u32 rx_page_order; | 274 | u32 rx_page_order; |
275 | |||
276 | |||
277 | /* queue watchdog */ | ||
278 | unsigned long wd_timeout; | ||
272 | }; | 279 | }; |
273 | 280 | ||
274 | #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ | 281 | #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ |
275 | ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) | 282 | ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) |
276 | 283 | ||
284 | static inline struct iwl_trans * | ||
285 | iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) | ||
286 | { | ||
287 | return container_of((void *)trans_pcie, struct iwl_trans, | ||
288 | trans_specific); | ||
289 | } | ||
290 | |||
277 | /***************************************************** | 291 | /***************************************************** |
278 | * RX | 292 | * RX |
279 | ******************************************************/ | 293 | ******************************************************/ |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index d35d0b81fd28..c34eac062762 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | |||
@@ -668,6 +668,10 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) | |||
668 | trace_bufs[2], trace_lens[2]); | 668 | trace_bufs[2], trace_lens[2]); |
669 | #endif | 669 | #endif |
670 | 670 | ||
671 | /* start timer if queue currently empty */ | ||
672 | if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) | ||
673 | mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); | ||
674 | |||
671 | /* Increment and update queue's write index */ | 675 | /* Increment and update queue's write index */ |
672 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); | 676 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); |
673 | iwl_txq_update_write_ptr(trans, txq); | 677 | iwl_txq_update_write_ptr(trans, txq); |
@@ -677,6 +681,22 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) | |||
677 | return idx; | 681 | return idx; |
678 | } | 682 | } |
679 | 683 | ||
684 | static inline void iwl_queue_progress(struct iwl_trans_pcie *trans_pcie, | ||
685 | struct iwl_tx_queue *txq) | ||
686 | { | ||
687 | if (!trans_pcie->wd_timeout) | ||
688 | return; | ||
689 | |||
690 | /* | ||
691 | * if empty delete timer, otherwise move timer forward | ||
692 | * since we're making progress on this queue | ||
693 | */ | ||
694 | if (txq->q.read_ptr == txq->q.write_ptr) | ||
695 | del_timer(&txq->stuck_timer); | ||
696 | else | ||
697 | mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); | ||
698 | } | ||
699 | |||
680 | /** | 700 | /** |
681 | * iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd | 701 | * iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd |
682 | * | 702 | * |
@@ -711,6 +731,8 @@ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, | |||
711 | } | 731 | } |
712 | 732 | ||
713 | } | 733 | } |
734 | |||
735 | iwl_queue_progress(trans_pcie, txq); | ||
714 | } | 736 | } |
715 | 737 | ||
716 | /** | 738 | /** |
@@ -754,8 +776,6 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, | |||
754 | cmd = txq->cmd[cmd_index]; | 776 | cmd = txq->cmd[cmd_index]; |
755 | meta = &txq->meta[cmd_index]; | 777 | meta = &txq->meta[cmd_index]; |
756 | 778 | ||
757 | txq->time_stamp = jiffies; | ||
758 | |||
759 | iwlagn_unmap_tfd(trans, meta, &txq->tfds[index], | 779 | iwlagn_unmap_tfd(trans, meta, &txq->tfds[index], |
760 | DMA_BIDIRECTIONAL); | 780 | DMA_BIDIRECTIONAL); |
761 | 781 | ||
@@ -949,5 +969,8 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, | |||
949 | iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr, DMA_TO_DEVICE); | 969 | iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr, DMA_TO_DEVICE); |
950 | freed++; | 970 | freed++; |
951 | } | 971 | } |
972 | |||
973 | iwl_queue_progress(trans_pcie, txq); | ||
974 | |||
952 | return freed; | 975 | return freed; |
953 | } | 976 | } |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index 1d100491be4c..f3695fea45ef 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | |||
@@ -299,6 +299,33 @@ static inline void iwlagn_free_dma_ptr(struct iwl_trans *trans, | |||
299 | memset(ptr, 0, sizeof(*ptr)); | 299 | memset(ptr, 0, sizeof(*ptr)); |
300 | } | 300 | } |
301 | 301 | ||
302 | static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) | ||
303 | { | ||
304 | struct iwl_tx_queue *txq = (void *)data; | ||
305 | struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; | ||
306 | struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); | ||
307 | |||
308 | spin_lock(&txq->lock); | ||
309 | /* check if triggered erroneously */ | ||
310 | if (txq->q.read_ptr == txq->q.write_ptr) { | ||
311 | spin_unlock(&txq->lock); | ||
312 | return; | ||
313 | } | ||
314 | spin_unlock(&txq->lock); | ||
315 | |||
316 | |||
317 | IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, | ||
318 | jiffies_to_msecs(trans_pcie->wd_timeout)); | ||
319 | IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", | ||
320 | txq->q.read_ptr, txq->q.write_ptr); | ||
321 | IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n", | ||
322 | iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq->q.id)) | ||
323 | & (TFD_QUEUE_SIZE_MAX - 1), | ||
324 | iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq->q.id))); | ||
325 | |||
326 | iwl_op_mode_nic_error(trans->op_mode); | ||
327 | } | ||
328 | |||
302 | static int iwl_trans_txq_alloc(struct iwl_trans *trans, | 329 | static int iwl_trans_txq_alloc(struct iwl_trans *trans, |
303 | struct iwl_tx_queue *txq, int slots_num, | 330 | struct iwl_tx_queue *txq, int slots_num, |
304 | u32 txq_id) | 331 | u32 txq_id) |
@@ -310,6 +337,10 @@ static int iwl_trans_txq_alloc(struct iwl_trans *trans, | |||
310 | if (WARN_ON(txq->meta || txq->cmd || txq->skbs || txq->tfds)) | 337 | if (WARN_ON(txq->meta || txq->cmd || txq->skbs || txq->tfds)) |
311 | return -EINVAL; | 338 | return -EINVAL; |
312 | 339 | ||
340 | setup_timer(&txq->stuck_timer, iwl_trans_pcie_queue_stuck_timer, | ||
341 | (unsigned long)txq); | ||
342 | txq->trans_pcie = trans_pcie; | ||
343 | |||
313 | txq->q.n_window = slots_num; | 344 | txq->q.n_window = slots_num; |
314 | 345 | ||
315 | txq->meta = kcalloc(slots_num, sizeof(txq->meta[0]), GFP_KERNEL); | 346 | txq->meta = kcalloc(slots_num, sizeof(txq->meta[0]), GFP_KERNEL); |
@@ -472,6 +503,8 @@ static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) | |||
472 | txq->cmd = NULL; | 503 | txq->cmd = NULL; |
473 | txq->meta = NULL; | 504 | txq->meta = NULL; |
474 | 505 | ||
506 | del_timer_sync(&txq->stuck_timer); | ||
507 | |||
475 | /* 0-fill queue descriptor structure */ | 508 | /* 0-fill queue descriptor structure */ |
476 | memset(txq, 0, sizeof(*txq)); | 509 | memset(txq, 0, sizeof(*txq)); |
477 | } | 510 | } |
@@ -1347,6 +1380,10 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, | |||
1347 | &dev_cmd->hdr, firstlen, | 1380 | &dev_cmd->hdr, firstlen, |
1348 | skb->data + hdr_len, secondlen); | 1381 | skb->data + hdr_len, secondlen); |
1349 | 1382 | ||
1383 | /* start timer if queue currently empty */ | ||
1384 | if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) | ||
1385 | mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); | ||
1386 | |||
1350 | /* Tell device the write index *just past* this latest filled TFD */ | 1387 | /* Tell device the write index *just past* this latest filled TFD */ |
1351 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); | 1388 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); |
1352 | iwl_txq_update_write_ptr(trans, txq); | 1389 | iwl_txq_update_write_ptr(trans, txq); |
@@ -1442,8 +1479,6 @@ static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, | |||
1442 | 1479 | ||
1443 | spin_lock(&txq->lock); | 1480 | spin_lock(&txq->lock); |
1444 | 1481 | ||
1445 | txq->time_stamp = jiffies; | ||
1446 | |||
1447 | if (txq->q.read_ptr != tfd_num) { | 1482 | if (txq->q.read_ptr != tfd_num) { |
1448 | IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", | 1483 | IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", |
1449 | txq_id, txq->q.read_ptr, tfd_num, ssn); | 1484 | txq_id, txq->q.read_ptr, tfd_num, ssn); |
@@ -1500,6 +1535,9 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, | |||
1500 | trans_pcie->rx_page_order = get_order(8 * 1024); | 1535 | trans_pcie->rx_page_order = get_order(8 * 1024); |
1501 | else | 1536 | else |
1502 | trans_pcie->rx_page_order = get_order(4 * 1024); | 1537 | trans_pcie->rx_page_order = get_order(4 * 1024); |
1538 | |||
1539 | trans_pcie->wd_timeout = | ||
1540 | msecs_to_jiffies(trans_cfg->queue_watchdog_timeout); | ||
1503 | } | 1541 | } |
1504 | 1542 | ||
1505 | static void iwl_trans_pcie_free(struct iwl_trans *trans) | 1543 | static void iwl_trans_pcie_free(struct iwl_trans *trans) |
@@ -1589,40 +1627,6 @@ static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans) | |||
1589 | return ret; | 1627 | return ret; |
1590 | } | 1628 | } |
1591 | 1629 | ||
1592 | /* | ||
1593 | * On every watchdog tick we check (latest) time stamp. If it does not | ||
1594 | * change during timeout period and queue is not empty we reset firmware. | ||
1595 | */ | ||
1596 | static int iwl_trans_pcie_check_stuck_queue(struct iwl_trans *trans, int cnt) | ||
1597 | { | ||
1598 | struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); | ||
1599 | struct iwl_tx_queue *txq = &trans_pcie->txq[cnt]; | ||
1600 | struct iwl_queue *q = &txq->q; | ||
1601 | unsigned long timeout; | ||
1602 | |||
1603 | if (q->read_ptr == q->write_ptr) { | ||
1604 | txq->time_stamp = jiffies; | ||
1605 | return 0; | ||
1606 | } | ||
1607 | |||
1608 | timeout = txq->time_stamp + | ||
1609 | msecs_to_jiffies(hw_params(trans).wd_timeout); | ||
1610 | |||
1611 | if (time_after(jiffies, timeout)) { | ||
1612 | IWL_ERR(trans, "Queue %d stuck for %u ms.\n", q->id, | ||
1613 | hw_params(trans).wd_timeout); | ||
1614 | IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", | ||
1615 | q->read_ptr, q->write_ptr); | ||
1616 | IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n", | ||
1617 | iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) | ||
1618 | & (TFD_QUEUE_SIZE_MAX - 1), | ||
1619 | iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); | ||
1620 | return 1; | ||
1621 | } | ||
1622 | |||
1623 | return 0; | ||
1624 | } | ||
1625 | |||
1626 | static const char *get_fh_string(int cmd) | 1630 | static const char *get_fh_string(int cmd) |
1627 | { | 1631 | { |
1628 | switch (cmd) { | 1632 | switch (cmd) { |
@@ -2039,7 +2043,6 @@ const struct iwl_trans_ops trans_ops_pcie = { | |||
2039 | .dbgfs_register = iwl_trans_pcie_dbgfs_register, | 2043 | .dbgfs_register = iwl_trans_pcie_dbgfs_register, |
2040 | 2044 | ||
2041 | .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty, | 2045 | .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty, |
2042 | .check_stuck_queue = iwl_trans_pcie_check_stuck_queue, | ||
2043 | 2046 | ||
2044 | #ifdef CONFIG_PM_SLEEP | 2047 | #ifdef CONFIG_PM_SLEEP |
2045 | .suspend = iwl_trans_pcie_suspend, | 2048 | .suspend = iwl_trans_pcie_suspend, |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 46be59f5ef39..a6598a29ef59 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h | |||
@@ -307,6 +307,8 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) | |||
307 | * @n_no_reclaim_cmds: # of commands in list | 307 | * @n_no_reclaim_cmds: # of commands in list |
308 | * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs, | 308 | * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs, |
309 | * if unset 4k will be the RX buffer size | 309 | * if unset 4k will be the RX buffer size |
310 | * @queue_watchdog_timeout: time (in ms) after which queues | ||
311 | * are considered stuck and will trigger device restart | ||
310 | */ | 312 | */ |
311 | struct iwl_trans_config { | 313 | struct iwl_trans_config { |
312 | struct iwl_op_mode *op_mode; | 314 | struct iwl_op_mode *op_mode; |
@@ -318,6 +320,7 @@ struct iwl_trans_config { | |||
318 | int n_no_reclaim_cmds; | 320 | int n_no_reclaim_cmds; |
319 | 321 | ||
320 | bool rx_buf_size_8k; | 322 | bool rx_buf_size_8k; |
323 | unsigned int queue_watchdog_timeout; | ||
321 | }; | 324 | }; |
322 | 325 | ||
323 | /** | 326 | /** |
@@ -355,7 +358,6 @@ struct iwl_trans_config { | |||
355 | * irq, tasklet etc... From this point on, the device may not issue | 358 | * irq, tasklet etc... From this point on, the device may not issue |
356 | * any interrupt (incl. RFKILL). | 359 | * any interrupt (incl. RFKILL). |
357 | * May sleep | 360 | * May sleep |
358 | * @check_stuck_queue: check if a specific queue is stuck | ||
359 | * @wait_tx_queue_empty: wait until all tx queues are empty | 361 | * @wait_tx_queue_empty: wait until all tx queues are empty |
360 | * May sleep | 362 | * May sleep |
361 | * @dbgfs_register: add the dbgfs files under this directory. Files will be | 363 | * @dbgfs_register: add the dbgfs files under this directory. Files will be |
@@ -394,7 +396,6 @@ struct iwl_trans_ops { | |||
394 | void (*free)(struct iwl_trans *trans); | 396 | void (*free)(struct iwl_trans *trans); |
395 | 397 | ||
396 | int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); | 398 | int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); |
397 | int (*check_stuck_queue)(struct iwl_trans *trans, int q); | ||
398 | int (*wait_tx_queue_empty)(struct iwl_trans *trans); | 399 | int (*wait_tx_queue_empty)(struct iwl_trans *trans); |
399 | #ifdef CONFIG_PM_SLEEP | 400 | #ifdef CONFIG_PM_SLEEP |
400 | int (*suspend)(struct iwl_trans *trans); | 401 | int (*suspend)(struct iwl_trans *trans); |
@@ -577,13 +578,6 @@ static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans) | |||
577 | return trans->ops->wait_tx_queue_empty(trans); | 578 | return trans->ops->wait_tx_queue_empty(trans); |
578 | } | 579 | } |
579 | 580 | ||
580 | static inline int iwl_trans_check_stuck_queue(struct iwl_trans *trans, int q) | ||
581 | { | ||
582 | WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, | ||
583 | "%s bad state = %d", __func__, trans->state); | ||
584 | |||
585 | return trans->ops->check_stuck_queue(trans, q); | ||
586 | } | ||
587 | static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, | 581 | static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, |
588 | struct dentry *dir) | 582 | struct dentry *dir) |
589 | { | 583 | { |