diff options
author | Michal Kazior <michal.kazior@tieto.com> | 2013-07-16 03:54:35 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2013-07-30 11:01:20 -0400 |
commit | affd321733eebc92b12cd329505f63e94ae80c93 (patch) | |
tree | 2e743c8a0a89f6e6057cd2c8f4b96769dfcdde17 /drivers/net/wireless/ath/ath10k/mac.c | |
parent | 87571bf0b8b27d4a97848ce48de34fa6d3b12db8 (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>
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/mac.c')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.c | 101 |
1 files changed, 82 insertions, 19 deletions
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 | */ |
1741 | static void ath10k_halt(struct ath10k *ar) | 1741 | void 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 | ||
1818 | static int ath10k_config(struct ieee80211_hw *hw, u32 changed) | 1827 | static 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 | |||
1851 | static 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) | |||
2773 | static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) | 2809 | static 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 | ||
2841 | skip: | ||
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 | ||
2915 | static 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 | |||
2869 | static const struct ieee80211_ops ath10k_ops = { | 2931 | static 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, |