--- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -521,6 +521,7 @@ struct ath11k { u8 lmac_id; struct completion peer_assoc_done; + struct completion peer_delete_done; int install_key_status; struct completion install_key_done; --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -364,7 +364,7 @@ static u8 ath11k_mac_bitrate_to_rate(int (ath11k_mac_bitrate_is_cck(bitrate) ? BIT(7) : 0); } -static void ath11k_get_arvif_iter(void *data, u8 *mac, +static void ath11k_mac_get_arvif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath11k_vif_iter *arvif_iter = data; @@ -385,7 +385,7 @@ struct ath11k_vif *ath11k_mac_get_arvif( flags = IEEE80211_IFACE_ITER_RESUME_ALL; ieee80211_iterate_active_interfaces_atomic(ar->hw, flags, - ath11k_get_arvif_iter, + ath11k_mac_get_arvif_iter, &arvif_iter); if (!arvif_iter.arvif) { ath11k_warn(ar->ab, "No VIF found for vdev %d\n", vdev_id); @@ -479,6 +479,36 @@ struct ath11k *ath11k_mac_get_ar_vdev_st return NULL; } +struct ath11k *ath11k_mac_get_ar_peer_delete_status(struct ath11k_base *ab, u8 *peer_addr, + u32 vdev_id) +{ + int i; + struct ath11k_pdev *pdev; + struct ath11k *ar; + struct ath11k_vif *arvif; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) { + ar = pdev->ar; + + spin_lock_bh(&ar->data_lock); + if (ether_addr_equal(ar->mac_addr, peer_addr)) { + spin_unlock_bh(&ar->data_lock); + return ar; + } else { + arvif = ath11k_mac_get_arvif(pdev->ar, vdev_id); + if (arvif) { + spin_unlock_bh(&ar->data_lock); + return arvif->ar; + } + } + spin_unlock_bh(&ar->data_lock); + } + } + return NULL; +} + static void ath11k_pdev_caps_update(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; @@ -673,6 +703,28 @@ static int ath11k_monitor_vdev_up(struct return 0; } +int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id, + const u8 *addr) +{ + int ret; + unsigned long time_left; + + ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr); + if(ret) { + ath11k_warn(ar->ab, "failed wait for peer deleted"); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->peer_delete_done, + 3 * HZ); + if(time_left == 0) { + ath11k_warn(ar->ab, "Timeout in receiving peer delete response\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int ath11k_mac_op_config(struct ieee80211_hw *hw, u32 changed) { struct ath11k *ar = hw->priv; @@ -4386,8 +4438,25 @@ static int ath11k_mac_op_add_interface(s err_peer_del: if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + reinit_completion(&ar->peer_delete_done); + + ret = ath11k_wmi_send_peer_delete_cmd(ar, vif->addr, + arvif->vdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", + arvif->vdev_id, vif->addr); + return ret; + } + ret = ath11k_wait_for_peer_delete_done(ar, arvif->vdev_id, + vif->addr); + if (ret) + return ret; + + ret = ath11k_wait_for_peer_delete_done(ar, arvif->vdev_id, vif->addr); + if (ret) + return ret; + ar->num_peers--; - ath11k_wmi_send_peer_delete_cmd(ar, vif->addr, arvif->vdev_id); } err_vdev_del: @@ -6194,6 +6263,7 @@ int ath11k_mac_allocate(struct ath11k_ba init_completion(&ar->vdev_setup_done); init_completion(&ar->vdev_delete_done); init_completion(&ar->peer_assoc_done); + init_completion(&ar->peer_delete_done); init_completion(&ar->install_key_done); init_completion(&ar->bss_survey_done); init_completion(&ar->scan.started); --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -153,17 +153,41 @@ void ath11k_peer_cleanup(struct ath11k * spin_unlock_bh(&ab->base_lock); } -static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) +int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) { return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, false); } +int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id, + const u8 *addr) +{ + int ret; + unsigned long time_left; + + ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr); + if (ret) { + ath11k_warn(ar->ab, "failed wait for peer deleted"); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->peer_delete_done, + 3 * HZ); + if (time_left == 0) { + ath11k_warn(ar->ab, "Timeout in receiving peer delete response\n"); + return -ETIMEDOUT; + } + + return 0; +} + int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr) { int ret; lockdep_assert_held(&ar->conf_mutex); + reinit_completion(&ar->peer_delete_done); + ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); if (ret) { ath11k_warn(ar->ab, @@ -172,7 +196,7 @@ int ath11k_peer_delete(struct ath11k *ar return ret; } - ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr); + ret = ath11k_wait_for_peer_delete_done(ar, vdev_id, addr); if (ret) return ret; @@ -220,8 +244,22 @@ int ath11k_peer_create(struct ath11k *ar spin_unlock_bh(&ar->ab->base_lock); ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", param->peer_addr, param->vdev_id); - ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, - param->vdev_id); + + reinit_completion(&ar->peer_delete_done); + + ret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, + param->vdev_id); + if(ret) { + ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", + param->vdev_id, param->peer_addr); + return ret; + } + + ret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, + param->peer_addr); + if(ret) + return ret; + return -ENOENT; } --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -5569,15 +5569,29 @@ static int ath11k_ready_event(struct ath static void ath11k_peer_delete_resp_event(struct ath11k_base *ab, struct sk_buff *skb) { struct wmi_peer_delete_resp_event peer_del_resp; + struct ath11k *ar; if (ath11k_pull_peer_del_resp_ev(ab, skb, &peer_del_resp) != 0) { ath11k_warn(ab, "failed to extract peer delete resp"); return; } - /* TODO: Do we need to validate whether ath11k_peer_find() return NULL - * Why this is needed when there is HTT event for peer delete - */ + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, peer_del_resp.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in peer delete resp ev %d", + peer_del_resp.vdev_id); + rcu_read_unlock(); + return; + } + + complete(&ar->peer_delete_done); + + rcu_read_unlock(); + + ath11k_dbg(ab, ATH11K_DBG_WMI, "peer delete resp for vdev id %d addr %pM\n", + peer_del_resp.vdev_id, peer_del_resp.peer_macaddr.addr); + } static void ath11k_vdev_delete_resp_event(struct ath11k_base *ab, struct sk_buff *skb) --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -147,6 +147,8 @@ void ath11k_mac_drain_tx(struct ath11k * void ath11k_mac_peer_cleanup_all(struct ath11k *ar); int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); +struct ath11k *ath11k_mac_get_ar_peer_delete_status(struct ath11k_base *ab, u8 *peer_addr, + u32 vdev_id); enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw); enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher); #endif --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -686,6 +686,7 @@ static void ath11k_core_restart(struct w complete(&ar->scan.started); complete(&ar->scan.completed); complete(&ar->peer_assoc_done); + complete(&ar->peer_delete_done); complete(&ar->install_key_done); complete(&ar->vdev_setup_done); complete(&ar->vdev_delete_done); --- a/drivers/net/wireless/ath/ath11k/peer.h +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -40,5 +40,7 @@ void ath11k_peer_cleanup(struct ath11k * int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr); int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, struct ieee80211_sta *sta, struct peer_create_params *param); +int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr); +int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id, const u8 *addr); #endif /* _PEER_H_ */