diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 0c18726fbf287..06b637ba8b8f3 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -221,8 +221,9 @@ struct ath12k_tx_conf { }; struct ath12k_key_conf { - bool changed; enum set_key_cmd cmd; + struct list_head list; + struct ieee80211_sta *sta; struct ieee80211_key_conf *key; }; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index c8a330eb0d4b7..7185cffe7dd42 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3519,14 +3519,30 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, static struct ath12k_vif_cache *ath12k_ahvif_get_link_cache(struct ath12k_vif *ahvif, u8 link_id) { - if (!ahvif->cache[link_id]) + if (!ahvif->cache[link_id]) { ahvif->cache[link_id] = kzalloc(sizeof(*ahvif->cache[0]), GFP_KERNEL); + if (ahvif->cache[link_id]) + INIT_LIST_HEAD(&ahvif->cache[link_id]->key_conf.list); + } return ahvif->cache[link_id]; } +static void ath12k_ahvif_put_link_key_cache(struct ath12k_vif_cache *cache) +{ + struct ath12k_key_conf *key_conf, *tmp; + + if (!cache || list_empty(&cache->key_conf.list)) + return; + list_for_each_entry_safe(key_conf, tmp, &cache->key_conf.list, list) { + list_del(&key_conf->list); + kfree(key_conf); + } +} + static void ath12k_ahvif_put_link_cache(struct ath12k_vif *ahvif, u8 link_id) { + ath12k_ahvif_put_link_key_cache(ahvif->cache[link_id]); kfree(ahvif->cache[link_id]); ahvif->cache[link_id] = NULL; } @@ -4170,6 +4186,39 @@ static int ath12k_mac_set_key(struct ath12k *ar, enum set_key_cmd cmd, return 0; } +static int ath12k_mac_update_key_cache(struct ath12k_vif_cache *cache, + enum set_key_cmd cmd, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath12k_key_conf *key_conf = NULL, *tmp; + + if (cmd == SET_KEY) { + key_conf = kzalloc(sizeof(*key_conf), GFP_KERNEL); + + if (!key_conf) + return -ENOMEM; + + key_conf->cmd = cmd; + key_conf->sta = sta; + key_conf->key = key; + list_add_tail(&key_conf->list, + &cache->key_conf.list); + } + if (list_empty(&cache->key_conf.list)) + return 0; + list_for_each_entry_safe(key_conf, tmp, &cache->key_conf.list, list) { + if (key_conf->key == key) { + /* DEL key for an old SET key which driver hasn't flushed yet. + */ + list_del(&key_conf->list); + kfree(key_conf); + break; + } + } + return 0; +} + static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) @@ -4179,13 +4228,12 @@ static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ath12k_link_sta *arsta = NULL; struct ath12k_vif_cache *cache; struct ath12k_sta *ahsta; - struct ath12k *ar; + unsigned long links; + u8 link_id; int ret; lockdep_assert_wiphy(hw->wiphy); - arvif = &ahvif->deflink; - /* BIP needs to be done in software */ if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC || key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || @@ -4197,36 +4245,63 @@ static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (key->keyidx > WMI_MAX_KEY_INDEX) return -ENOSPC; - ar = ath12k_get_ar_by_vif(hw, vif); - if (!ar) { - /* ar is expected to be valid when sta ptr is available */ - if (sta) { - WARN_ON_ONCE(1); - return -EINVAL; + if (sta) { + ahsta = ath12k_sta_to_ahsta(sta); + /* For an ML STA Pairwise key is same for all associated link Stations, + * hence do set key for all link STAs which are active. + */ + if (sta->mlo) { + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, + ahvif->link[link_id]); + arsta = wiphy_dereference(hw->wiphy, + ahsta->link[link_id]); + + if (WARN_ON(!arvif || !arsta)) + /* arvif and arsta are expected to be valid when + * STA is present. + */ + continue; + + ret = ath12k_mac_set_key(arvif->ar, cmd, arvif, + arsta, key); + if (ret) + break; + } + } else { + arsta = &ahsta->deflink; + arvif = arsta->arvif; + if (WARN_ON(!arvif)) { + ret = -EINVAL; + goto out; + } + + ret = ath12k_mac_set_key(arvif->ar, cmd, arvif, arsta, key); + } + } else { + if (key->link_id >= 0 && key->link_id < IEEE80211_MLD_MAX_NUM_LINKS) { + link_id = key->link_id; + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + } else { + link_id = 0; + arvif = &ahvif->deflink; } - cache = ath12k_ahvif_get_link_cache(ahvif, ATH12K_DEFAULT_LINK_ID); - if (!cache) - return -ENOSPC; + if (!arvif || !arvif->is_created) { + cache = ath12k_ahvif_get_link_cache(ahvif, link_id); + if (!cache) + return -ENOSPC; - cache->key_conf.cmd = cmd; - cache->key_conf.key = key; - cache->key_conf.changed = true; + ret = ath12k_mac_update_key_cache(cache, cmd, sta, key); - return 0; - } + return ret; + } - if (sta) { - ahsta = ath12k_sta_to_ahsta(sta); - arsta = &ahsta->deflink; + ret = ath12k_mac_set_key(arvif->ar, cmd, arvif, NULL, key); } - /* Note: Currently only deflink of ahvif and ahsta are used here, - * once MLO support is added the allocated links (i.e ahvif->links[]) - * should be use based on link id passed from mac80211 and such link - * access needs to be protected with ah->conf_mutex. - */ - ret = ath12k_mac_set_key(ar, cmd, arvif, arsta, key); +out: return ret; } @@ -6838,6 +6913,40 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) return ret; } +static void ath12k_mac_vif_flush_key_cache(struct ath12k_link_vif *arvif) +{ + struct ath12k_key_conf *key_conf, *tmp; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_hw *ah = ahvif->ah; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; + struct ath12k_vif_cache *cache = ahvif->cache[arvif->link_id]; + int ret; + + lockdep_assert_wiphy(ah->hw->wiphy); + + list_for_each_entry_safe(key_conf, tmp, &cache->key_conf.list, list) { + arsta = NULL; + if (key_conf->sta) { + ahsta = ath12k_sta_to_ahsta(key_conf->sta); + arsta = wiphy_dereference(ah->hw->wiphy, + ahsta->link[arvif->link_id]); + if (!arsta) + goto free_cache; + } + + ret = ath12k_mac_set_key(arvif->ar, key_conf->cmd, + arvif, arsta, + key_conf->key); + if (ret) + ath12k_warn(arvif->ar->ab, "unable to apply set key param to vdev %d ret %d\n", + arvif->vdev_id, ret); +free_cache: + list_del(&key_conf->list); + kfree(key_conf); + } +} + static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif *arvif) { struct ath12k_vif *ahvif = arvif->ahvif; @@ -6866,13 +6975,9 @@ static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif cache->bss_conf_changed); } - if (cache->key_conf.changed) { - ret = ath12k_mac_set_key(ar, cache->key_conf.cmd, arvif, NULL, - cache->key_conf.key); - if (ret) - ath12k_warn(ab, "unable to apply set key param to vdev %d ret %d\n", - arvif->vdev_id, ret); - } + if (!list_empty(&cache->key_conf.list)) + ath12k_mac_vif_flush_key_cache(arvif); + ath12k_ahvif_put_link_cache(ahvif, arvif->link_id); }