]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
rtw88: handle and recover when firmware crash
authorTzu-En Huang <tehuang@realtek.com>
Fri, 25 Sep 2020 06:12:16 +0000 (14:12 +0800)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 29 Sep 2020 08:22:19 +0000 (11:22 +0300)
This handles the situation when firmware crashes.
When firmware crashes, it will send an interrupt, and driver will queue
a work for recovery.
In the work, driver will reset it's internal association state, which
includes removing associated sta's macid, resetting vifs' states
and removing keys. After resetting the driver's state, driver will call
rtw_enter_ips() to force the chipset power off to reset the chip.
Finally, driver calls ieee80211_restart_hw() to inform mac80211 stack
to restart.
Since only 8822c firmware supports this feature, the interrupt will only
be triggered when 8822c chipset is loaded.

Signed-off-by: Tzu-En Huang <tehuang@realtek.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200925061219.23754-3-tehuang@realtek.com
drivers/net/wireless/realtek/rtw88/fw.c
drivers/net/wireless/realtek/rtw88/fw.h
drivers/net/wireless/realtek/rtw88/mac80211.c
drivers/net/wireless/realtek/rtw88/main.c
drivers/net/wireless/realtek/rtw88/main.h
drivers/net/wireless/realtek/rtw88/pci.c
drivers/net/wireless/realtek/rtw88/reg.h
drivers/net/wireless/realtek/rtw88/util.h

index 63b00bc190007f598d1703952104734a0bb00754..6a50bb993cafa1059ac923d201872b4115fc7940 100644 (file)
@@ -193,6 +193,15 @@ void rtw_fw_c2h_cmd_rx_irqsafe(struct rtw_dev *rtwdev, u32 pkt_offset,
 }
 EXPORT_SYMBOL(rtw_fw_c2h_cmd_rx_irqsafe);
 
+void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev)
+{
+       if (rtw_read8(rtwdev, REG_MCU_TST_CFG) == VAL_FW_TRIGGER)
+               rtw_fw_recovery(rtwdev);
+       else
+               rtw_warn(rtwdev, "unhandled firmware c2h interrupt\n");
+}
+EXPORT_SYMBOL(rtw_fw_c2h_cmd_isr);
+
 static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev,
                                    u8 *h2c)
 {
index 686dcd3bbda6043aa1854418a062b8e39fd5f6a5..b4e3f755e8fbc0ed9e50daf908615dcb5cc79779 100644 (file)
@@ -564,5 +564,6 @@ void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev,
                                 struct cfg80211_ssid *ssid);
 void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable);
 void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c);
+void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev);
 
 #endif
index 6b199152abcfb09e6b7d207852d17f221d25d009..c92fba2fa4808f2981f591ea01834b9c0ee3d663 100644 (file)
@@ -358,13 +358,10 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
        rtw_leave_lps_deep(rtwdev);
 
        if (changed & BSS_CHANGED_ASSOC) {
-               enum rtw_net_type net_type;
-
+               rtw_vif_assoc_changed(rtwvif, conf);
                if (conf->assoc) {
                        rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_FINISH);
-                       net_type = RTW_NET_MGD_LINKED;
 
-                       rtwvif->aid = conf->aid;
                        rtw_fw_download_rsvd_page(rtwdev);
                        rtw_send_rsvd_page_h2c(rtwdev);
                        rtw_coex_media_status_notify(rtwdev, conf->assoc);
@@ -372,12 +369,9 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
                                rtw_bf_assoc(rtwdev, vif, conf);
                } else {
                        rtw_leave_lps(rtwdev);
-                       net_type = RTW_NET_NO_LINK;
-                       rtwvif->aid = 0;
                        rtw_bf_disassoc(rtwdev, vif, conf);
                }
 
-               rtwvif->net_type = net_type;
                config |= PORT_SET_NET_TYPE;
                config |= PORT_SET_AID;
        }
@@ -429,56 +423,17 @@ static int rtw_ops_conf_tx(struct ieee80211_hw *hw,
        return 0;
 }
 
-static u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
-{
-       unsigned long mac_id;
-
-       mac_id = find_first_zero_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
-       if (mac_id < RTW_MAX_MAC_ID_NUM)
-               set_bit(mac_id, rtwdev->mac_id_map);
-
-       return mac_id;
-}
-
-static void rtw_release_macid(struct rtw_dev *rtwdev, u8 mac_id)
-{
-       clear_bit(mac_id, rtwdev->mac_id_map);
-}
-
 static int rtw_ops_sta_add(struct ieee80211_hw *hw,
                           struct ieee80211_vif *vif,
                           struct ieee80211_sta *sta)
 {
        struct rtw_dev *rtwdev = hw->priv;
-       struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
-       int i;
        int ret = 0;
 
        mutex_lock(&rtwdev->mutex);
-
-       si->mac_id = rtw_acquire_macid(rtwdev);
-       if (si->mac_id >= RTW_MAX_MAC_ID_NUM) {
-               ret = -ENOSPC;
-               goto out;
-       }
-
-       si->sta = sta;
-       si->vif = vif;
-       si->init_ra_lv = 1;
-       ewma_rssi_init(&si->avg_rssi);
-       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
-               rtw_txq_init(rtwdev, sta->txq[i]);
-
-       rtw_update_sta_info(rtwdev, si);
-       rtw_fw_media_status_report(rtwdev, si->mac_id, true);
-
-       rtwdev->sta_cnt++;
-
-       rtw_info(rtwdev, "sta %pM joined with macid %d\n",
-                sta->addr, si->mac_id);
-
-out:
+       ret = rtw_sta_add(rtwdev, sta, vif);
        mutex_unlock(&rtwdev->mutex);
+
        return ret;
 }
 
@@ -487,25 +442,11 @@ static int rtw_ops_sta_remove(struct ieee80211_hw *hw,
                              struct ieee80211_sta *sta)
 {
        struct rtw_dev *rtwdev = hw->priv;
-       struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
-       int i;
 
        mutex_lock(&rtwdev->mutex);
-
-       rtw_release_macid(rtwdev, si->mac_id);
-       rtw_fw_media_status_report(rtwdev, si->mac_id, false);
-
-       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
-               rtw_txq_cleanup(rtwdev, sta->txq[i]);
-
-       kfree(si->mask);
-
-       rtwdev->sta_cnt--;
-
-       rtw_info(rtwdev, "sta %pM with macid %d left\n",
-                sta->addr, si->mac_id);
-
+       rtw_sta_remove(rtwdev, sta, true);
        mutex_unlock(&rtwdev->mutex);
+
        return 0;
 }
 
@@ -845,6 +786,17 @@ static void rtw_ops_set_wakeup(struct ieee80211_hw *hw, bool enabled)
 }
 #endif
 
+static void rtw_reconfig_complete(struct ieee80211_hw *hw,
+                                 enum ieee80211_reconfig_type reconfig_type)
+{
+       struct rtw_dev *rtwdev = hw->priv;
+
+       mutex_lock(&rtwdev->mutex);
+       if (reconfig_type == IEEE80211_RECONFIG_TYPE_RESTART)
+               clear_bit(RTW_FLAG_RESTARTING, rtwdev->flags);
+       mutex_unlock(&rtwdev->mutex);
+}
+
 const struct ieee80211_ops rtw_ops = {
        .tx                     = rtw_ops_tx,
        .wake_tx_queue          = rtw_ops_wake_tx_queue,
@@ -871,6 +823,7 @@ const struct ieee80211_ops rtw_ops = {
        .set_bitrate_mask       = rtw_ops_set_bitrate_mask,
        .set_antenna            = rtw_ops_set_antenna,
        .get_antenna            = rtw_ops_get_antenna,
+       .reconfig_complete      = rtw_reconfig_complete,
 #ifdef CONFIG_PM
        .suspend                = rtw_ops_suspend,
        .resume                 = rtw_ops_resume,
index cc82c80f0433254d998ef49b4642b7ac538bbbfb..420853eaf270c8eb844066e5edb765f7e7e1affc 100644 (file)
@@ -259,6 +259,137 @@ static void rtw_c2h_work(struct work_struct *work)
        }
 }
 
+static u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
+{
+       unsigned long mac_id;
+
+       mac_id = find_first_zero_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
+       if (mac_id < RTW_MAX_MAC_ID_NUM)
+               set_bit(mac_id, rtwdev->mac_id_map);
+
+       return mac_id;
+}
+
+int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
+               struct ieee80211_vif *vif)
+{
+       struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+       int i;
+
+       si->mac_id = rtw_acquire_macid(rtwdev);
+       if (si->mac_id >= RTW_MAX_MAC_ID_NUM)
+               return -ENOSPC;
+
+       si->sta = sta;
+       si->vif = vif;
+       si->init_ra_lv = 1;
+       ewma_rssi_init(&si->avg_rssi);
+       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+               rtw_txq_init(rtwdev, sta->txq[i]);
+
+       rtw_update_sta_info(rtwdev, si);
+       rtw_fw_media_status_report(rtwdev, si->mac_id, true);
+
+       rtwdev->sta_cnt++;
+       rtw_info(rtwdev, "sta %pM joined with macid %d\n",
+                sta->addr, si->mac_id);
+
+       return 0;
+}
+
+void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
+                   bool fw_exist)
+{
+       struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+       int i;
+
+       rtw_release_macid(rtwdev, si->mac_id);
+       if (fw_exist)
+               rtw_fw_media_status_report(rtwdev, si->mac_id, false);
+
+       for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+               rtw_txq_cleanup(rtwdev, sta->txq[i]);
+
+       kfree(si->mask);
+
+       rtwdev->sta_cnt--;
+       rtw_info(rtwdev, "sta %pM with macid %d left\n",
+                sta->addr, si->mac_id);
+}
+
+void rtw_vif_assoc_changed(struct rtw_vif *rtwvif,
+                          struct ieee80211_bss_conf *conf)
+{
+       if (conf && conf->assoc) {
+               rtwvif->aid = conf->aid;
+               rtwvif->net_type = RTW_NET_MGD_LINKED;
+       } else {
+               rtwvif->aid = 0;
+               rtwvif->net_type = RTW_NET_NO_LINK;
+       }
+}
+
+static void rtw_reset_key_iter(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_sta *sta,
+                              struct ieee80211_key_conf *key,
+                              void *data)
+{
+       struct rtw_dev *rtwdev = (struct rtw_dev *)data;
+       struct rtw_sec_desc *sec = &rtwdev->sec;
+
+       rtw_sec_clear_cam(rtwdev, sec, key->hw_key_idx);
+}
+
+static void rtw_reset_sta_iter(void *data, struct ieee80211_sta *sta)
+{
+       struct rtw_dev *rtwdev = (struct rtw_dev *)data;
+
+       if (rtwdev->sta_cnt == 0) {
+               rtw_warn(rtwdev, "sta count before reset should not be 0\n");
+               return;
+       }
+       rtw_sta_remove(rtwdev, sta, false);
+}
+
+static void rtw_reset_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct rtw_dev *rtwdev = (struct rtw_dev *)data;
+       struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
+
+       rtw_bf_disassoc(rtwdev, vif, NULL);
+       rtw_vif_assoc_changed(rtwvif, NULL);
+       rtw_txq_cleanup(rtwdev, vif->txq);
+}
+
+void rtw_fw_recovery(struct rtw_dev *rtwdev)
+{
+       if (!test_bit(RTW_FLAG_RESTARTING, rtwdev->flags))
+               ieee80211_queue_work(rtwdev->hw, &rtwdev->fw_recovery_work);
+}
+
+static void rtw_fw_recovery_work(struct work_struct *work)
+{
+       struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,
+                                             fw_recovery_work);
+
+       WARN(1, "firmware crash, start reset and recover\n");
+
+       mutex_lock(&rtwdev->mutex);
+
+       set_bit(RTW_FLAG_RESTARTING, rtwdev->flags);
+       rcu_read_lock();
+       rtw_iterate_keys_rcu(rtwdev, NULL, rtw_reset_key_iter, rtwdev);
+       rcu_read_unlock();
+       rtw_iterate_stas_atomic(rtwdev, rtw_reset_sta_iter, rtwdev);
+       rtw_iterate_vifs_atomic(rtwdev, rtw_reset_vif_iter, rtwdev);
+       rtw_enter_ips(rtwdev);
+
+       mutex_unlock(&rtwdev->mutex);
+
+       ieee80211_restart_hw(rtwdev->hw);
+}
+
 struct rtw_txq_ba_iter_data {
 };
 
@@ -1431,6 +1562,7 @@ int rtw_core_init(struct rtw_dev *rtwdev)
        INIT_DELAYED_WORK(&coex->wl_remain_work, rtw_coex_wl_remain_work);
        INIT_DELAYED_WORK(&coex->bt_remain_work, rtw_coex_bt_remain_work);
        INIT_WORK(&rtwdev->c2h_work, rtw_c2h_work);
+       INIT_WORK(&rtwdev->fw_recovery_work, rtw_fw_recovery_work);
        INIT_WORK(&rtwdev->ba_work, rtw_txq_ba_work);
        skb_queue_head_init(&rtwdev->c2h_queue);
        skb_queue_head_init(&rtwdev->coex.queue);
index 276b5d38146784d366516ee89be231330edf3d57..292336387b89c319078aad63676c286ee25f52ce 100644 (file)
@@ -359,6 +359,7 @@ enum rtw_flags {
        RTW_FLAG_DIG_DISABLE,
        RTW_FLAG_BUSY_TRAFFIC,
        RTW_FLAG_WOWLAN,
+       RTW_FLAG_RESTARTING,
 
        NUM_OF_RTW_FLAGS,
 };
@@ -1699,6 +1700,7 @@ struct rtw_dev {
        /* c2h cmd queue & handler work */
        struct sk_buff_head c2h_queue;
        struct work_struct c2h_work;
+       struct work_struct fw_recovery_work;
 
        /* used to protect txqs list */
        spinlock_t txq_lock;
@@ -1799,6 +1801,11 @@ static inline bool rtw_chip_has_rx_ldpc(struct rtw_dev *rtwdev)
        return rtwdev->chip->rx_ldpc;
 }
 
+static inline void rtw_release_macid(struct rtw_dev *rtwdev, u8 mac_id)
+{
+       clear_bit(mac_id, rtwdev->mac_id_map);
+}
+
 void rtw_get_channel_params(struct cfg80211_chan_def *chandef,
                            struct rtw_channel_params *ch_param);
 bool check_hw_ready(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target);
@@ -1821,5 +1828,12 @@ void rtw_core_deinit(struct rtw_dev *rtwdev);
 int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw);
 void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw);
 u16 rtw_desc_to_bitrate(u8 desc_rate);
+void rtw_vif_assoc_changed(struct rtw_vif *rtwvif,
+                          struct ieee80211_bss_conf *conf);
+int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
+               struct ieee80211_vif *vif);
+void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
+                   bool fw_exist);
+void rtw_fw_recovery(struct rtw_dev *rtwdev);
 
 #endif
index 135dd331691cb2ac4ce420e8f0db413c28a8def5..e72ea99d94302090c1fda16080b7a41b6fdcefc7 100644 (file)
@@ -389,6 +389,7 @@ static int rtw_pci_init(struct rtw_dev *rtwdev)
                              IMR_VODOK |
                              IMR_ROK |
                              IMR_BCNDMAINT_E |
+                             IMR_C2HCMD |
                              0;
        rtwpci->irq_mask[1] = IMR_TXFOVW |
                              0;
@@ -1079,6 +1080,8 @@ static irqreturn_t rtw_pci_interrupt_threadfn(int irq, void *dev)
                rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_H2C);
        if (irq_status[0] & IMR_ROK)
                rtw_pci_rx_isr(rtwdev, rtwpci, RTW_RX_QUEUE_MPDU);
+       if (unlikely(irq_status[0] & IMR_C2HCMD))
+               rtw_fw_c2h_cmd_isr(rtwdev);
 
        /* all of the jobs for this interrupt have been done */
        rtw_pci_enable_interrupt(rtwdev, rtwpci);
index 8f468d6b5f788c18af129248f8991dd6265876d0..9a696ac17d691170584c8b15b58f9542103cde31 100644 (file)
                                 BIT_WINTINI_RDY | BIT_RAM_DL_SEL)
 #define FW_READY_MASK          0xffff
 
+#define REG_MCU_TST_CFG                0x84
+#define VAL_FW_TRIGGER         0x1
+
 #define REG_EFUSE_ACCESS       0x00CF
 #define EFUSE_ACCESS_ON                0x69
 #define EFUSE_ACCESS_OFF       0x00
index 41c10e7144dff868786842749ce405c2009d117f..0c23b5069be0badfe8599a5d78647cc7d0c26bf1 100644 (file)
@@ -17,6 +17,8 @@ struct rtw_dev;
        ieee80211_iterate_stations_atomic(rtwdev->hw, iterator, data)
 #define rtw_iterate_keys(rtwdev, vif, iterator, data)                         \
        ieee80211_iter_keys(rtwdev->hw, vif, iterator, data)
+#define rtw_iterate_keys_rcu(rtwdev, vif, iterator, data)                     \
+       ieee80211_iter_keys_rcu((rtwdev)->hw, vif, iterator, data)
 
 static inline u8 *get_hdr_bssid(struct ieee80211_hdr *hdr)
 {