aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Kazior <michal.kazior@tieto.com>2013-07-16 03:54:35 -0400
committerKalle Valo <kvalo@qca.qualcomm.com>2013-07-30 11:01:20 -0400
commitaffd321733eebc92b12cd329505f63e94ae80c93 (patch)
tree2e743c8a0a89f6e6057cd2c8f4b96769dfcdde17
parent87571bf0b8b27d4a97848ce48de34fa6d3b12db8 (diff)
ath10k: implement device recovery
Restart the hardware if FW crashes. If FW crashes during recovery we leave the hardware in a "wedged" state to avoid recursive recoveries. When in "wedged" state userspace may bring interfaces down (to issue stop()) and then bring one interface (to issue start()) to reload hardware manually. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c30
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h19
-rw-r--r--drivers/net/wireless/ath/ath10k/htc.c3
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c101
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.h1
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c7
7 files changed, 144 insertions, 19 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index c37f79f6f8ce..7226c23b9569 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -476,6 +476,34 @@ static int ath10k_init_hw_params(struct ath10k *ar)
476 return 0; 476 return 0;
477} 477}
478 478
479static void ath10k_core_restart(struct work_struct *work)
480{
481 struct ath10k *ar = container_of(work, struct ath10k, restart_work);
482
483 mutex_lock(&ar->conf_mutex);
484
485 switch (ar->state) {
486 case ATH10K_STATE_ON:
487 ath10k_halt(ar);
488 ar->state = ATH10K_STATE_RESTARTING;
489 ieee80211_restart_hw(ar->hw);
490 break;
491 case ATH10K_STATE_OFF:
492 /* this can happen if driver is being unloaded */
493 ath10k_warn("cannot restart a device that hasn't been started\n");
494 break;
495 case ATH10K_STATE_RESTARTING:
496 case ATH10K_STATE_RESTARTED:
497 ar->state = ATH10K_STATE_WEDGED;
498 /* fall through */
499 case ATH10K_STATE_WEDGED:
500 ath10k_warn("device is wedged, will not restart\n");
501 break;
502 }
503
504 mutex_unlock(&ar->conf_mutex);
505}
506
479struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, 507struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
480 const struct ath10k_hif_ops *hif_ops) 508 const struct ath10k_hif_ops *hif_ops)
481{ 509{
@@ -519,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
519 547
520 init_waitqueue_head(&ar->event_queue); 548 init_waitqueue_head(&ar->event_queue);
521 549
550 INIT_WORK(&ar->restart_work, ath10k_core_restart);
551
522 return ar; 552 return ar;
523 553
524err_wq: 554err_wq:
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 413f1c5a8119..9f21ecb239d7 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -250,6 +250,23 @@ struct ath10k_debug {
250enum ath10k_state { 250enum ath10k_state {
251 ATH10K_STATE_OFF = 0, 251 ATH10K_STATE_OFF = 0,
252 ATH10K_STATE_ON, 252 ATH10K_STATE_ON,
253
254 /* When doing firmware recovery the device is first powered down.
255 * mac80211 is supposed to call in to start() hook later on. It is
256 * however possible that driver unloading and firmware crash overlap.
257 * mac80211 can wait on conf_mutex in stop() while the device is
258 * stopped in ath10k_core_restart() work holding conf_mutex. The state
259 * RESTARTED means that the device is up and mac80211 has started hw
260 * reconfiguration. Once mac80211 is done with the reconfiguration we
261 * set the state to STATE_ON in restart_complete(). */
262 ATH10K_STATE_RESTARTING,
263 ATH10K_STATE_RESTARTED,
264
265 /* The device has crashed while restarting hw. This state is like ON
266 * but commands are blocked in HTC and -ECOMM response is given. This
267 * prevents completion timeouts and makes the driver more responsive to
268 * userspace commands. This is also prevents recursive recovery. */
269 ATH10K_STATE_WEDGED,
253}; 270};
254 271
255struct ath10k { 272struct ath10k {
@@ -355,6 +372,8 @@ struct ath10k {
355 372
356 enum ath10k_state state; 373 enum ath10k_state state;
357 374
375 struct work_struct restart_work;
376
358#ifdef CONFIG_ATH10K_DEBUGFS 377#ifdef CONFIG_ATH10K_DEBUGFS
359 struct ath10k_debug debug; 378 struct ath10k_debug debug;
360#endif 379#endif
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 7d5a36616b71..72e072c97588 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -246,6 +246,9 @@ int ath10k_htc_send(struct ath10k_htc *htc,
246{ 246{
247 struct ath10k_htc_ep *ep = &htc->endpoint[eid]; 247 struct ath10k_htc_ep *ep = &htc->endpoint[eid];
248 248
249 if (htc->ar->state == ATH10K_STATE_WEDGED)
250 return -ECOMM;
251
249 if (eid >= ATH10K_HTC_EP_COUNT) { 252 if (eid >= ATH10K_HTC_EP_COUNT) {
250 ath10k_warn("Invalid endpoint id: %d\n", eid); 253 ath10k_warn("Invalid endpoint id: %d\n", eid);
251 return -ENOENT; 254 return -ENOENT;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 2dfd446251b1..b1bb318d153f 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1738,7 +1738,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
1738/* 1738/*
1739 * Initialize various parameters with default vaules. 1739 * Initialize various parameters with default vaules.
1740 */ 1740 */
1741static void ath10k_halt(struct ath10k *ar) 1741void ath10k_halt(struct ath10k *ar)
1742{ 1742{
1743 lockdep_assert_held(&ar->conf_mutex); 1743 lockdep_assert_held(&ar->conf_mutex);
1744 1744
@@ -1764,7 +1764,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
1764 1764
1765 mutex_lock(&ar->conf_mutex); 1765 mutex_lock(&ar->conf_mutex);
1766 1766
1767 if (ar->state != ATH10K_STATE_OFF) { 1767 if (ar->state != ATH10K_STATE_OFF &&
1768 ar->state != ATH10K_STATE_RESTARTING) {
1768 ret = -EINVAL; 1769 ret = -EINVAL;
1769 goto exit; 1770 goto exit;
1770 } 1771 }
@@ -1784,6 +1785,11 @@ static int ath10k_start(struct ieee80211_hw *hw)
1784 goto exit; 1785 goto exit;
1785 } 1786 }
1786 1787
1788 if (ar->state == ATH10K_STATE_OFF)
1789 ar->state = ATH10K_STATE_ON;
1790 else if (ar->state == ATH10K_STATE_RESTARTING)
1791 ar->state = ATH10K_STATE_RESTARTED;
1792
1787 ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); 1793 ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
1788 if (ret) 1794 if (ret)
1789 ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", 1795 ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
@@ -1806,22 +1812,47 @@ static void ath10k_stop(struct ieee80211_hw *hw)
1806 struct ath10k *ar = hw->priv; 1812 struct ath10k *ar = hw->priv;
1807 1813
1808 mutex_lock(&ar->conf_mutex); 1814 mutex_lock(&ar->conf_mutex);
1809 if (ar->state == ATH10K_STATE_ON) 1815 if (ar->state == ATH10K_STATE_ON ||
1816 ar->state == ATH10K_STATE_RESTARTED ||
1817 ar->state == ATH10K_STATE_WEDGED)
1810 ath10k_halt(ar); 1818 ath10k_halt(ar);
1811 1819
1812 ar->state = ATH10K_STATE_OFF; 1820 ar->state = ATH10K_STATE_OFF;
1813 mutex_unlock(&ar->conf_mutex); 1821 mutex_unlock(&ar->conf_mutex);
1814 1822
1815 cancel_work_sync(&ar->offchan_tx_work); 1823 cancel_work_sync(&ar->offchan_tx_work);
1824 cancel_work_sync(&ar->restart_work);
1816} 1825}
1817 1826
1818static int ath10k_config(struct ieee80211_hw *hw, u32 changed) 1827static void ath10k_config_ps(struct ath10k *ar)
1819{ 1828{
1820 struct ath10k_generic_iter ar_iter; 1829 struct ath10k_generic_iter ar_iter;
1830
1831 lockdep_assert_held(&ar->conf_mutex);
1832
1833 /* During HW reconfiguration mac80211 reports all interfaces that were
1834 * running until reconfiguration was started. Since FW doesn't have any
1835 * vdevs at this point we must not iterate over this interface list.
1836 * This setting will be updated upon add_interface(). */
1837 if (ar->state == ATH10K_STATE_RESTARTED)
1838 return;
1839
1840 memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
1841 ar_iter.ar = ar;
1842
1843 ieee80211_iterate_active_interfaces_atomic(
1844 ar->hw, IEEE80211_IFACE_ITER_NORMAL,
1845 ath10k_ps_iter, &ar_iter);
1846
1847 if (ar_iter.ret)
1848 ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
1849}
1850
1851static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
1852{
1821 struct ath10k *ar = hw->priv; 1853 struct ath10k *ar = hw->priv;
1822 struct ieee80211_conf *conf = &hw->conf; 1854 struct ieee80211_conf *conf = &hw->conf;
1823 int ret = 0; 1855 int ret = 0;
1824 u32 flags;
1825 1856
1826 mutex_lock(&ar->conf_mutex); 1857 mutex_lock(&ar->conf_mutex);
1827 1858
@@ -1833,18 +1864,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
1833 spin_unlock_bh(&ar->data_lock); 1864 spin_unlock_bh(&ar->data_lock);
1834 } 1865 }
1835 1866
1836 if (changed & IEEE80211_CONF_CHANGE_PS) { 1867 if (changed & IEEE80211_CONF_CHANGE_PS)
1837 memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); 1868 ath10k_config_ps(ar);
1838 ar_iter.ar = ar;
1839 flags = IEEE80211_IFACE_ITER_RESUME_ALL;
1840
1841 ieee80211_iterate_active_interfaces_atomic(hw,
1842 flags,
1843 ath10k_ps_iter,
1844 &ar_iter);
1845
1846 ret = ar_iter.ret;
1847 }
1848 1869
1849 if (changed & IEEE80211_CONF_CHANGE_MONITOR) { 1870 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1850 if (conf->flags & IEEE80211_CONF_MONITOR) 1871 if (conf->flags & IEEE80211_CONF_MONITOR)
@@ -1853,6 +1874,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
1853 ret = ath10k_monitor_destroy(ar); 1874 ret = ath10k_monitor_destroy(ar);
1854 } 1875 }
1855 1876
1877 ath10k_wmi_flush_tx(ar);
1856 mutex_unlock(&ar->conf_mutex); 1878 mutex_unlock(&ar->conf_mutex);
1857 return ret; 1879 return ret;
1858} 1880}
@@ -2695,6 +2717,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
2695 2717
2696 lockdep_assert_held(&arvif->ar->conf_mutex); 2718 lockdep_assert_held(&arvif->ar->conf_mutex);
2697 2719
2720 /* During HW reconfiguration mac80211 reports all interfaces that were
2721 * running until reconfiguration was started. Since FW doesn't have any
2722 * vdevs at this point we must not iterate over this interface list.
2723 * This setting will be updated upon add_interface(). */
2724 if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
2725 return;
2726
2698 rts = min_t(u32, rts, ATH10K_RTS_MAX); 2727 rts = min_t(u32, rts, ATH10K_RTS_MAX);
2699 2728
2700 ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, 2729 ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
@@ -2735,6 +2764,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
2735 2764
2736 lockdep_assert_held(&arvif->ar->conf_mutex); 2765 lockdep_assert_held(&arvif->ar->conf_mutex);
2737 2766
2767 /* During HW reconfiguration mac80211 reports all interfaces that were
2768 * running until reconfiguration was started. Since FW doesn't have any
2769 * vdevs at this point we must not iterate over this interface list.
2770 * This setting will be updated upon add_interface(). */
2771 if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
2772 return;
2773
2738 frag = clamp_t(u32, frag, 2774 frag = clamp_t(u32, frag,
2739 ATH10K_FRAGMT_THRESHOLD_MIN, 2775 ATH10K_FRAGMT_THRESHOLD_MIN,
2740 ATH10K_FRAGMT_THRESHOLD_MAX); 2776 ATH10K_FRAGMT_THRESHOLD_MAX);
@@ -2773,6 +2809,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
2773static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) 2809static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
2774{ 2810{
2775 struct ath10k *ar = hw->priv; 2811 struct ath10k *ar = hw->priv;
2812 bool skip;
2776 int ret; 2813 int ret;
2777 2814
2778 /* mac80211 doesn't care if we really xmit queued frames or not 2815 /* mac80211 doesn't care if we really xmit queued frames or not
@@ -2782,17 +2819,26 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
2782 2819
2783 mutex_lock(&ar->conf_mutex); 2820 mutex_lock(&ar->conf_mutex);
2784 2821
2822 if (ar->state == ATH10K_STATE_WEDGED)
2823 goto skip;
2824
2785 ret = wait_event_timeout(ar->htt.empty_tx_wq, ({ 2825 ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
2786 bool empty; 2826 bool empty;
2827
2787 spin_lock_bh(&ar->htt.tx_lock); 2828 spin_lock_bh(&ar->htt.tx_lock);
2788 empty = bitmap_empty(ar->htt.used_msdu_ids, 2829 empty = bitmap_empty(ar->htt.used_msdu_ids,
2789 ar->htt.max_num_pending_tx); 2830 ar->htt.max_num_pending_tx);
2790 spin_unlock_bh(&ar->htt.tx_lock); 2831 spin_unlock_bh(&ar->htt.tx_lock);
2791 (empty); 2832
2833 skip = (ar->state == ATH10K_STATE_WEDGED);
2834
2835 (empty || skip);
2792 }), ATH10K_FLUSH_TIMEOUT_HZ); 2836 }), ATH10K_FLUSH_TIMEOUT_HZ);
2793 if (ret <= 0) 2837
2838 if (ret <= 0 || skip)
2794 ath10k_warn("tx not flushed\n"); 2839 ath10k_warn("tx not flushed\n");
2795 2840
2841skip:
2796 mutex_unlock(&ar->conf_mutex); 2842 mutex_unlock(&ar->conf_mutex);
2797} 2843}
2798 2844
@@ -2866,6 +2912,22 @@ static int ath10k_resume(struct ieee80211_hw *hw)
2866} 2912}
2867#endif 2913#endif
2868 2914
2915static void ath10k_restart_complete(struct ieee80211_hw *hw)
2916{
2917 struct ath10k *ar = hw->priv;
2918
2919 mutex_lock(&ar->conf_mutex);
2920
2921 /* If device failed to restart it will be in a different state, e.g.
2922 * ATH10K_STATE_WEDGED */
2923 if (ar->state == ATH10K_STATE_RESTARTED) {
2924 ath10k_info("device successfully recovered\n");
2925 ar->state = ATH10K_STATE_ON;
2926 }
2927
2928 mutex_unlock(&ar->conf_mutex);
2929}
2930
2869static const struct ieee80211_ops ath10k_ops = { 2931static const struct ieee80211_ops ath10k_ops = {
2870 .tx = ath10k_tx, 2932 .tx = ath10k_tx,
2871 .start = ath10k_start, 2933 .start = ath10k_start,
@@ -2886,6 +2948,7 @@ static const struct ieee80211_ops ath10k_ops = {
2886 .set_frag_threshold = ath10k_set_frag_threshold, 2948 .set_frag_threshold = ath10k_set_frag_threshold,
2887 .flush = ath10k_flush, 2949 .flush = ath10k_flush,
2888 .tx_last_beacon = ath10k_tx_last_beacon, 2950 .tx_last_beacon = ath10k_tx_last_beacon,
2951 .restart_complete = ath10k_restart_complete,
2889#ifdef CONFIG_PM 2952#ifdef CONFIG_PM
2890 .suspend = ath10k_suspend, 2953 .suspend = ath10k_suspend,
2891 .resume = ath10k_resume, 2954 .resume = ath10k_resume,
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 27fc92e58829..6fce9bfb19a5 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
34void ath10k_reset_scan(unsigned long ptr); 34void ath10k_reset_scan(unsigned long ptr);
35void ath10k_offchan_tx_purge(struct ath10k *ar); 35void ath10k_offchan_tx_purge(struct ath10k *ar);
36void ath10k_offchan_tx_work(struct work_struct *work); 36void ath10k_offchan_tx_work(struct work_struct *work);
37void ath10k_halt(struct ath10k *ar);
37 38
38static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) 39static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
39{ 40{
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index bfe856166bb0..c71b488eba9f 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -720,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
720 reg_dump_values[i + 1], 720 reg_dump_values[i + 1],
721 reg_dump_values[i + 2], 721 reg_dump_values[i + 2],
722 reg_dump_values[i + 3]); 722 reg_dump_values[i + 3]);
723
724 ieee80211_queue_work(ar->hw, &ar->restart_work);
723} 725}
724 726
725static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, 727static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index b7e7e456b5de..0d25cd733afd 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar)
27{ 27{
28 int ret; 28 int ret;
29 29
30 lockdep_assert_held(&ar->conf_mutex);
31
32 if (ar->state == ATH10K_STATE_WEDGED) {
33 ath10k_warn("wmi flush skipped - device is wedged anyway\n");
34 return;
35 }
36
30 ret = wait_event_timeout(ar->wmi.wq, 37 ret = wait_event_timeout(ar->wmi.wq,
31 atomic_read(&ar->wmi.pending_tx_count) == 0, 38 atomic_read(&ar->wmi.pending_tx_count) == 0,
32 5*HZ); 39 5*HZ);