ath11k: vdev delete synchronization with firmware
authorRitesh Singh <ritesi@codeaurora.org>
Tue, 24 Nov 2020 15:59:13 +0000 (17:59 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 24 Nov 2020 16:04:31 +0000 (18:04 +0200)
When the interface is added immediately after removing the
interface, vdev deletion in firmware might not have been
completed.

Hence, add vdev_delete_resp_event and wait_event_timeout
to synchronize with firmware.

Signed-off-by: Ritesh Singh <ritesi@codeaurora.org>
Signed-off-by: Maharaja Kennadyrajan <mkenna@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1605514143-17652-2-git-send-email-mkenna@codeaurora.org
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/core.h
drivers/net/wireless/ath/ath11k/mac.c
drivers/net/wireless/ath/ath11k/wmi.c
drivers/net/wireless/ath/ath11k/wmi.h

index c23a59a..f792825 100644 (file)
@@ -810,6 +810,7 @@ static void ath11k_core_restart(struct work_struct *work)
                complete(&ar->peer_assoc_done);
                complete(&ar->install_key_done);
                complete(&ar->vdev_setup_done);
+               complete(&ar->vdev_delete_done);
                complete(&ar->bss_survey_done);
                complete(&ar->thermal.wmi_sync);
 
index 8851cf8..b1b8afe 100644 (file)
@@ -430,6 +430,7 @@ struct ath11k_per_peer_tx_stats {
 };
 
 #define ATH11K_FLUSH_TIMEOUT (5 * HZ)
+#define ATH11K_VDEV_DELETE_TIMEOUT_HZ (5 * HZ)
 
 struct ath11k_vdev_stop_status {
        bool stop_in_progress;
@@ -512,6 +513,7 @@ struct ath11k {
        int last_wmi_vdev_start_status;
        struct ath11k_vdev_stop_status vdev_stop_status;
        struct completion vdev_setup_done;
+       struct completion vdev_delete_done;
 
        int num_peers;
        int max_num_peers;
index 70fc253..566e2a4 100644 (file)
@@ -4660,6 +4660,7 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw,
        struct ath11k *ar = hw->priv;
        struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
        struct ath11k_base *ab = ar->ab;
+       unsigned long time_left;
        int ret;
        int i;
 
@@ -4668,10 +4669,6 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw,
        ath11k_dbg(ab, ATH11K_DBG_MAC, "mac remove interface (vdev %d)\n",
                   arvif->vdev_id);
 
-       spin_lock_bh(&ar->data_lock);
-       list_del(&arvif->list);
-       spin_unlock_bh(&ar->data_lock);
-
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath11k_peer_delete(ar, arvif->vdev_id, vif->addr);
                if (ret)
@@ -4679,16 +4676,33 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw,
                                    arvif->vdev_id, ret);
        }
 
+       reinit_completion(&ar->vdev_delete_done);
+
        ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id);
-       if (ret)
+       if (ret) {
                ath11k_warn(ab, "failed to delete WMI vdev %d: %d\n",
                            arvif->vdev_id, ret);
+               goto err_vdev_del;
+       }
+
+       time_left = wait_for_completion_timeout(&ar->vdev_delete_done,
+                                               ATH11K_VDEV_DELETE_TIMEOUT_HZ);
+       if (time_left == 0) {
+               ath11k_warn(ab, "Timeout in receiving vdev delete response\n");
+               goto err_vdev_del;
+       }
 
+       ab->free_vdev_map |= 1LL << (arvif->vdev_id);
+       ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
        ar->num_created_vdevs--;
+
        ath11k_dbg(ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n",
                   vif->addr, arvif->vdev_id);
-       ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
-       ab->free_vdev_map |= 1LL << (arvif->vdev_id);
+
+err_vdev_del:
+       spin_lock_bh(&ar->data_lock);
+       list_del(&arvif->list);
+       spin_unlock_bh(&ar->data_lock);
 
        ath11k_peer_cleanup(ar, arvif->vdev_id);
 
@@ -6454,6 +6468,7 @@ int ath11k_mac_allocate(struct ath11k_base *ab)
                INIT_LIST_HEAD(&ar->ppdu_stats_info);
                mutex_init(&ar->conf_mutex);
                init_completion(&ar->vdev_setup_done);
+               init_completion(&ar->vdev_delete_done);
                init_completion(&ar->peer_assoc_done);
                init_completion(&ar->install_key_done);
                init_completion(&ar->bss_survey_done);
index bca66c1..d1175a1 100644 (file)
@@ -126,6 +126,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
                .min_len = sizeof(struct wmi_fils_discovery_event) },
        [WMI_TAG_OFFLOAD_PRB_RSP_TX_STATUS_EVENT] = {
                .min_len = sizeof(struct wmi_probe_resp_tx_status_event) },
+       [WMI_TAG_VDEV_DELETE_RESP_EVENT] = {
+               .min_len = sizeof(struct wmi_vdev_delete_resp_event) },
 };
 
 #define PRIMAP(_hw_mode_) \
@@ -4379,6 +4381,34 @@ static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *
        return 0;
 }
 
+static int ath11k_pull_vdev_del_resp_ev(struct ath11k_base *ab,
+                                       struct sk_buff *skb,
+                                       u32 *vdev_id)
+{
+       const void **tb;
+       const struct wmi_vdev_delete_resp_event *ev;
+       int ret;
+
+       tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+       if (IS_ERR(tb)) {
+               ret = PTR_ERR(tb);
+               ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
+               return ret;
+       }
+
+       ev = tb[WMI_TAG_VDEV_DELETE_RESP_EVENT];
+       if (!ev) {
+               ath11k_warn(ab, "failed to fetch vdev delete resp ev");
+               kfree(tb);
+               return -EPROTO;
+       }
+
+       *vdev_id = ev->vdev_id;
+
+       kfree(tb);
+       return 0;
+}
+
 static int ath11k_pull_bcn_tx_status_ev(struct ath11k_base *ab, void *evt_buf,
                                        u32 len, u32 *vdev_id,
                                        u32 *tx_status)
@@ -5711,6 +5741,34 @@ static void ath11k_peer_delete_resp_event(struct ath11k_base *ab, struct sk_buff
         */
 }
 
+static void ath11k_vdev_delete_resp_event(struct ath11k_base *ab,
+                                         struct sk_buff *skb)
+{
+       struct ath11k *ar;
+       u32 vdev_id = 0;
+
+       if (ath11k_pull_vdev_del_resp_ev(ab, skb, &vdev_id) != 0) {
+               ath11k_warn(ab, "failed to extract vdev delete resp");
+               return;
+       }
+
+       rcu_read_lock();
+       ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id);
+       if (!ar) {
+               ath11k_warn(ab, "invalid vdev id in vdev delete resp ev %d",
+                           vdev_id);
+               rcu_read_unlock();
+               return;
+       }
+
+       complete(&ar->vdev_delete_done);
+
+       rcu_read_unlock();
+
+       ath11k_dbg(ab, ATH11K_DBG_WMI, "vdev delete resp for vdev id %d\n",
+                  vdev_id);
+}
+
 static inline const char *ath11k_wmi_vdev_resp_print(u32 vdev_resp_status)
 {
        switch (vdev_resp_status) {
@@ -6722,7 +6780,6 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
                break;
        /* add Unsupported events here */
        case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
-       case WMI_VDEV_DELETE_RESP_EVENTID:
        case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
        case WMI_TWT_ENABLE_EVENTID:
        case WMI_TWT_DISABLE_EVENTID:
@@ -6733,6 +6790,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
        case WMI_PDEV_DFS_RADAR_DETECTION_EVENTID:
                ath11k_wmi_pdev_dfs_radar_detected_event(ab, skb);
                break;
+       case WMI_VDEV_DELETE_RESP_EVENTID:
+               ath11k_vdev_delete_resp_event(ab, skb);
+               break;
        /* TODO: Add remaining events */
        default:
                ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
index e4cc159..4c802c5 100644 (file)
@@ -4018,6 +4018,10 @@ struct wmi_regulatory_rule_struct {
        u32  flag_info;
 };
 
+struct wmi_vdev_delete_resp_event {
+       u32 vdev_id;
+} __packed;
+
 struct wmi_peer_delete_resp_event {
        u32 vdev_id;
        struct wmi_mac_addr peer_macaddr;