--- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -688,6 +688,7 @@ static void ath11k_core_restart(struct w 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); --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -430,9 +430,11 @@ 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 { +struct ath11k_vdev_stop_delete_status { bool stop_in_progress; + bool delete_in_progress; u32 vdev_id; }; @@ -524,8 +526,9 @@ struct ath11k { struct completion install_key_done; int last_wmi_vdev_start_status; - struct ath11k_vdev_stop_status vdev_stop_status; + struct ath11k_vdev_stop_delete_status vdev_stop_delete_status; struct completion vdev_setup_done; + struct completion vdev_delete_done; int num_peers; int max_num_peers; --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -448,8 +448,8 @@ struct ath11k *ath11k_mac_get_ar_by_pdev return NULL; } -struct ath11k *ath11k_mac_get_ar_vdev_stop_status(struct ath11k_base *ab, - u32 vdev_id) +struct ath11k *ath11k_mac_get_ar_vdev_stop_delete_status(struct ath11k_base *ab, + u32 vdev_id) { int i; struct ath11k_pdev *pdev; @@ -461,9 +461,15 @@ struct ath11k *ath11k_mac_get_ar_vdev_st ar = pdev->ar; spin_lock_bh(&ar->data_lock); - if (ar->vdev_stop_status.stop_in_progress && - ar->vdev_stop_status.vdev_id == vdev_id) { - ar->vdev_stop_status.stop_in_progress = false; + if (ar->vdev_stop_delete_status.stop_in_progress && + ar->vdev_stop_delete_status.vdev_id == vdev_id) { + ar->vdev_stop_delete_status.stop_in_progress = false; + spin_unlock_bh(&ar->data_lock); + return ar; + } + if (ar->vdev_stop_delete_status.delete_in_progress && + ar->vdev_stop_delete_status.vdev_id == vdev_id) { + ar->vdev_stop_delete_status.delete_in_progress = false; spin_unlock_bh(&ar->data_lock); return ar; } @@ -4416,6 +4422,7 @@ static void ath11k_mac_op_remove_interfa 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; @@ -4424,10 +4431,6 @@ static void ath11k_mac_op_remove_interfa 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) @@ -4435,14 +4438,29 @@ static void ath11k_mac_op_remove_interfa 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; + } - ar->num_created_vdevs--; - ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); - ab->free_vdev_map |= 1LL << (arvif->vdev_id); + 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"); + } else { + ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); + ab->free_vdev_map |= 1LL << (arvif->vdev_id); + ar->num_created_vdevs--; + } + +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); @@ -4640,6 +4658,7 @@ ath11k_mac_vdev_start_restart(struct ath lockdep_assert_held(&ar->conf_mutex); reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); arg.vdev_id = arvif->vdev_id; arg.dtim_period = arvif->dtim_period; @@ -4740,11 +4759,12 @@ static int ath11k_mac_vdev_stop(struct a lockdep_assert_held(&ar->conf_mutex); reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); spin_lock_bh(&ar->data_lock); - ar->vdev_stop_status.stop_in_progress = true; - ar->vdev_stop_status.vdev_id = arvif->vdev_id; + ar->vdev_stop_delete_status.stop_in_progress = true; + ar->vdev_stop_delete_status.vdev_id = arvif->vdev_id; spin_unlock_bh(&ar->data_lock); @@ -4775,7 +4795,7 @@ static int ath11k_mac_vdev_stop(struct a return 0; err: spin_lock_bh(&ar->data_lock); - ar->vdev_stop_status.stop_in_progress = false; + ar->vdev_stop_delete_status.stop_in_progress = false; spin_unlock_bh(&ar->data_lock); return ret; @@ -6172,6 +6192,7 @@ int ath11k_mac_allocate(struct ath11k_ba 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); --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -123,6 +123,8 @@ static const struct wmi_tlv_policy wmi_t = { .min_len = sizeof(struct wmi_stats_event) }, [WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT] = { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) }, + [WMI_TAG_VDEV_DELETE_RESP_EVENT] + = { .min_len = sizeof(struct wmi_vdev_delete_resp_event) }, }; #define PRIMAP(_hw_mode_) \ @@ -4207,6 +4209,33 @@ static int ath11k_pull_peer_del_resp_ev( 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) @@ -5551,6 +5580,32 @@ static void ath11k_peer_delete_resp_even */ } +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_vdev_stop_delete_status(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", vdev_id); +} + static inline const char *ath11k_wmi_vdev_resp_print(u32 vdev_resp_status) { switch (vdev_resp_status) { @@ -5627,7 +5682,7 @@ static void ath11k_vdev_stopped_event(st } rcu_read_lock(); - ar = ath11k_mac_get_ar_vdev_stop_status(ab, vdev_id); + ar = ath11k_mac_get_ar_vdev_stop_delete_status(ab, vdev_id); if (!ar) { ath11k_warn(ab, "invalid vdev id in vdev stopped ev %d", vdev_id); @@ -6521,7 +6576,6 @@ static void ath11k_wmi_tlv_op_rx(struct 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: @@ -6532,6 +6586,9 @@ static void ath11k_wmi_tlv_op_rx(struct 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_warn(ab, "Unknown eventid: 0x%x\n", id); --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -3919,6 +3919,10 @@ struct wmi_peer_delete_resp_event { struct wmi_mac_addr peer_macaddr; } __packed; +struct wmi_vdev_delete_resp_event { + u32 vdev_id; +} __packed; + struct wmi_bcn_tx_status_event { u32 vdev_id; u32 tx_status; --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -141,7 +141,8 @@ struct ath11k *ath11k_mac_get_ar_by_vdev struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id); struct ath11k *ath11k_mac_get_ar_vdev_stop_status(struct ath11k_base *ab, u32 vdev_id); - +struct ath11k *ath11k_mac_get_ar_vdev_stop_delete_status(struct ath11k_base *ab, + u32 vdev_id); void ath11k_mac_drain_tx(struct ath11k *ar); void ath11k_mac_peer_cleanup_all(struct ath11k *ar); int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);