Commit 5e971287 authored by Karthikeyan Periyasamy's avatar Karthikeyan Periyasamy Committed by Kalle Valo

ath11k: fix resource leak in ath11k_mac_sta_state

Handled the error case with proper resource cleanup and
moved the handling into a separate function from
ath11k_mac_sta_state.
Signed-off-by: default avatarKarthikeyan Periyasamy <periyasa@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 86d4def8
...@@ -2682,34 +2682,21 @@ static void ath11k_mac_dec_num_stations(struct ath11k_vif *arvif, ...@@ -2682,34 +2682,21 @@ static void ath11k_mac_dec_num_stations(struct ath11k_vif *arvif,
ar->num_stations--; ar->num_stations--;
} }
static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, static int ath11k_mac_station_add(struct ath11k *ar,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_sta *sta)
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state)
{ {
struct ath11k *ar = hw->priv; struct ath11k_base *ab = ar->ab;
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
struct peer_create_params peer_param; struct peer_create_params peer_param;
int ret = 0; int ret;
/* cancel must be done outside the mutex to avoid deadlock */
if ((old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST))
cancel_work_sync(&arsta->update_wk);
mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST && lockdep_assert_held(&ar->conf_mutex);
new_state == IEEE80211_STA_NONE) {
memset(arsta, 0, sizeof(*arsta));
arsta->arvif = arvif;
INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk);
ret = ath11k_mac_inc_num_stations(arvif, sta); ret = ath11k_mac_inc_num_stations(arvif, sta);
if (ret) { if (ret) {
ath11k_warn(ar->ab, "refusing to associate station: too many connected already (%d)\n", ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n",
ar->max_num_stations); ar->max_num_stations);
goto exit; goto exit;
} }
...@@ -2717,29 +2704,28 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ...@@ -2717,29 +2704,28 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL);
if (!arsta->rx_stats) { if (!arsta->rx_stats) {
ret = -ENOMEM; ret = -ENOMEM;
goto exit; goto dec_num_station;
} }
peer_param.vdev_id = arvif->vdev_id; peer_param.vdev_id = arvif->vdev_id;
peer_param.peer_addr = sta->addr; peer_param.peer_addr = sta->addr;
peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; peer_param.peer_type = WMI_PEER_TYPE_DEFAULT;
ret = ath11k_peer_create(ar, arvif, sta, &peer_param); ret = ath11k_peer_create(ar, arvif, sta, &peer_param);
if (ret) { if (ret) {
ath11k_warn(ar->ab, "Failed to add peer: %pM for VDEV: %d\n", ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id); sta->addr, arvif->vdev_id);
ath11k_mac_dec_num_stations(arvif, sta); goto free_rx_stats;
goto exit;
} }
ath11k_info(ar->ab, "Added peer: %pM for VDEV: %d\n", ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id); sta->addr, arvif->vdev_id);
if (ath11k_debug_is_extd_tx_stats_enabled(ar)) { if (ath11k_debug_is_extd_tx_stats_enabled(ar)) {
arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL);
GFP_KERNEL);
if (!arsta->tx_stats) { if (!arsta->tx_stats) {
ret = -ENOMEM; ret = -ENOMEM;
goto exit; goto free_peer;
} }
} }
...@@ -2748,19 +2734,63 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ...@@ -2748,19 +2734,63 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
arvif->vdev_id, arvif->vdev_id,
WMI_PEER_USE_4ADDR, 1); WMI_PEER_USE_4ADDR, 1);
if (ret) { if (ret) {
ath11k_warn(ar->ab, "failed to STA %pM 4addr capability: %d\n", ath11k_warn(ab, "failed to STA %pM 4addr capability: %d\n",
sta->addr, ret); sta->addr, ret);
goto exit; goto free_tx_stats;
} }
} }
ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr);
if (ret) { if (ret) {
ath11k_warn(ar->ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n",
sta->addr, arvif->vdev_id, ret); sta->addr, arvif->vdev_id, ret);
goto free_tx_stats;
}
return 0;
free_tx_stats:
kfree(arsta->tx_stats);
arsta->tx_stats = NULL;
free_peer:
ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
free_rx_stats:
kfree(arsta->rx_stats);
arsta->rx_stats = NULL;
dec_num_station:
ath11k_mac_dec_num_stations(arvif, sta); ath11k_mac_dec_num_stations(arvif, sta);
} exit:
return ret;
}
static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state)
{
struct ath11k *ar = hw->priv;
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
int ret = 0;
/* cancel must be done outside the mutex to avoid deadlock */
if ((old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST))
cancel_work_sync(&arsta->update_wk);
mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
memset(arsta, 0, sizeof(*arsta));
arsta->arvif = arvif;
INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk);
ret = ath11k_mac_station_add(ar, vif, sta);
if (ret)
ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id);
} else if ((old_state == IEEE80211_STA_NONE && } else if ((old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST)) { new_state == IEEE80211_STA_NOTEXIST)) {
ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr);
...@@ -2770,8 +2800,7 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ...@@ -2770,8 +2800,7 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id); sta->addr, arvif->vdev_id);
else else
ath11k_info(ar->ab, ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n",
"Removed peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id); sta->addr, arvif->vdev_id);
ath11k_mac_dec_num_stations(arvif, sta); ath11k_mac_dec_num_stations(arvif, sta);
...@@ -2809,7 +2838,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ...@@ -2809,7 +2838,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
sta->addr); sta->addr);
} }
exit:
mutex_unlock(&ar->conf_mutex); mutex_unlock(&ar->conf_mutex);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment