Path: blob/main/sys/contrib/dev/athk/ath12k/dp_peer.c
283314 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.3* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.4*/56#include "core.h"7#include "dp_peer.h"8#include "debug.h"9#include "debugfs.h"1011void ath12k_dp_link_peer_free(struct ath12k_dp_link_peer *peer)12{13list_del(&peer->list);1415kfree(peer->peer_stats.rx_stats);16kfree(peer);17}1819struct ath12k_dp_link_peer *20ath12k_dp_link_peer_find_by_vdev_and_addr(struct ath12k_dp *dp,21int vdev_id, const u8 *addr)22{23struct ath12k_dp_link_peer *peer;2425lockdep_assert_held(&dp->dp_lock);2627list_for_each_entry(peer, &dp->peers, list) {28if (peer->vdev_id != vdev_id)29continue;30if (!ether_addr_equal(peer->addr, addr))31continue;3233return peer;34}3536return NULL;37}3839struct ath12k_dp_link_peer *40ath12k_dp_link_peer_find_by_pdev_and_addr(struct ath12k_dp *dp, u8 pdev_idx,41const u8 *addr)42{43struct ath12k_dp_link_peer *peer;4445lockdep_assert_held(&dp->dp_lock);4647list_for_each_entry(peer, &dp->peers, list) {48if (peer->pdev_idx != pdev_idx)49continue;50if (!ether_addr_equal(peer->addr, addr))51continue;5253return peer;54}5556return NULL;57}5859struct ath12k_dp_link_peer *60ath12k_dp_link_peer_find_by_addr(struct ath12k_dp *dp, const u8 *addr)61{62lockdep_assert_held(&dp->dp_lock);6364return rhashtable_lookup_fast(dp->rhead_peer_addr, addr,65dp->rhash_peer_addr_param);66}67EXPORT_SYMBOL(ath12k_dp_link_peer_find_by_addr);6869static struct ath12k_dp_link_peer *70ath12k_dp_link_peer_find_by_ml_id(struct ath12k_dp *dp, int ml_peer_id)71{72struct ath12k_dp_link_peer *peer;7374lockdep_assert_held(&dp->dp_lock);7576list_for_each_entry(peer, &dp->peers, list)77if (ml_peer_id == peer->ml_id)78return peer;7980return NULL;81}8283static struct ath12k_dp_link_peer *84ath12k_dp_link_peer_search_by_id(struct ath12k_dp *dp, int peer_id)85{86struct ath12k_dp_link_peer *peer;8788lockdep_assert_held(&dp->dp_lock);8990if (peer_id == HAL_INVALID_PEERID)91return NULL;9293if (peer_id & ATH12K_PEER_ML_ID_VALID)94return ath12k_dp_link_peer_find_by_ml_id(dp, peer_id);9596list_for_each_entry(peer, &dp->peers, list)97if (peer_id == peer->peer_id)98return peer;99100return NULL;101}102103bool ath12k_dp_link_peer_exist_by_vdev_id(struct ath12k_dp *dp, int vdev_id)104{105struct ath12k_dp_link_peer *peer;106107spin_lock_bh(&dp->dp_lock);108109list_for_each_entry(peer, &dp->peers, list) {110if (vdev_id == peer->vdev_id) {111spin_unlock_bh(&dp->dp_lock);112return true;113}114}115spin_unlock_bh(&dp->dp_lock);116return false;117}118119struct ath12k_dp_link_peer *120ath12k_dp_link_peer_find_by_ast(struct ath12k_dp *dp, int ast_hash)121{122struct ath12k_dp_link_peer *peer;123124lockdep_assert_held(&dp->dp_lock);125126list_for_each_entry(peer, &dp->peers, list)127if (ast_hash == peer->ast_hash)128return peer;129130return NULL;131}132133void ath12k_dp_link_peer_unmap_event(struct ath12k_base *ab, u16 peer_id)134{135struct ath12k_dp_link_peer *peer;136struct ath12k_dp *dp = ath12k_ab_to_dp(ab);137138spin_lock_bh(&dp->dp_lock);139140peer = ath12k_dp_link_peer_search_by_id(dp, peer_id);141if (!peer) {142ath12k_warn(ab, "peer-unmap-event: unknown peer id %d\n",143peer_id);144goto exit;145}146147ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt peer unmap vdev %d peer %pM id %d\n",148peer->vdev_id, peer->addr, peer_id);149150ath12k_dp_link_peer_free(peer);151wake_up(&ab->peer_mapping_wq);152153exit:154spin_unlock_bh(&dp->dp_lock);155}156157void ath12k_dp_link_peer_map_event(struct ath12k_base *ab, u8 vdev_id, u16 peer_id,158u8 *mac_addr, u16 ast_hash, u16 hw_peer_id)159{160struct ath12k_dp_link_peer *peer;161struct ath12k_dp *dp = ath12k_ab_to_dp(ab);162struct ath12k *ar;163164spin_lock_bh(&dp->dp_lock);165peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, mac_addr);166if (!peer) {167peer = kzalloc_obj(*peer, GFP_ATOMIC);168if (!peer)169goto exit;170171peer->vdev_id = vdev_id;172peer->peer_id = peer_id;173peer->ast_hash = ast_hash;174peer->hw_peer_id = hw_peer_id;175ether_addr_copy(peer->addr, mac_addr);176177rcu_read_lock();178ar = ath12k_mac_get_ar_by_vdev_id(ab, vdev_id);179if (ar && ath12k_debugfs_is_extd_rx_stats_enabled(ar) &&180!peer->peer_stats.rx_stats) {181peer->peer_stats.rx_stats = kzalloc_obj(*peer->peer_stats.rx_stats,182GFP_ATOMIC);183}184rcu_read_unlock();185186list_add(&peer->list, &dp->peers);187wake_up(&ab->peer_mapping_wq);188ewma_avg_rssi_init(&peer->avg_rssi);189}190ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt peer map vdev %d peer %pM id %d\n",191vdev_id, mac_addr, peer_id);192193exit:194spin_unlock_bh(&dp->dp_lock);195}196197struct ath12k_link_sta *ath12k_dp_link_peer_to_link_sta(struct ath12k_base *ab,198struct ath12k_dp_link_peer *peer)199{200struct ath12k_sta *ahsta;201struct ath12k_link_sta *arsta;202203RCU_LOCKDEP_WARN(!rcu_read_lock_held(),204"ath12k_dp_link_peer to ath12k_link_sta called without rcu lock");205206if (!peer->sta)207return NULL;208209ahsta = ath12k_sta_to_ahsta(peer->sta);210if (peer->ml_id & ATH12K_PEER_ML_ID_VALID) {211if (!(ahsta->links_map & BIT(peer->link_id))) {212ath12k_warn(ab, "peer %pM id %d link_id %d can't found in STA link_map 0x%x\n",213peer->addr, peer->peer_id, peer->link_id,214ahsta->links_map);215return NULL;216}217arsta = rcu_dereference(ahsta->link[peer->link_id]);218if (!arsta)219return NULL;220} else {221arsta = &ahsta->deflink;222}223return arsta;224}225226static int ath12k_dp_link_peer_rhash_addr_tbl_init(struct ath12k_dp *dp)227{228struct ath12k_base *ab = dp->ab;229struct rhashtable_params *param;230struct rhashtable *rhash_addr_tbl;231int ret;232233lockdep_assert_held(&dp->link_peer_rhash_tbl_lock);234235rhash_addr_tbl = kzalloc_obj(*dp->rhead_peer_addr);236if (!rhash_addr_tbl)237return -ENOMEM;238239param = &dp->rhash_peer_addr_param;240241param->key_offset = offsetof(struct ath12k_dp_link_peer, addr);242param->head_offset = offsetof(struct ath12k_dp_link_peer, rhash_addr);243param->key_len = sizeof_field(struct ath12k_dp_link_peer, addr);244param->automatic_shrinking = true;245param->nelem_hint = ab->num_radios * ath12k_core_get_max_peers_per_radio(ab);246247ret = rhashtable_init(rhash_addr_tbl, param);248if (ret) {249ath12k_warn(ab, "failed to init peer addr rhash table %d\n", ret);250goto err_free;251}252253dp->rhead_peer_addr = rhash_addr_tbl;254255return 0;256257err_free:258kfree(rhash_addr_tbl);259260return ret;261}262263int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp)264{265int ret;266267mutex_lock(&dp->link_peer_rhash_tbl_lock);268ret = ath12k_dp_link_peer_rhash_addr_tbl_init(dp);269mutex_unlock(&dp->link_peer_rhash_tbl_lock);270271return ret;272}273274void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp)275{276mutex_lock(&dp->link_peer_rhash_tbl_lock);277rhashtable_destroy(dp->rhead_peer_addr);278kfree(dp->rhead_peer_addr);279dp->rhead_peer_addr = NULL;280mutex_unlock(&dp->link_peer_rhash_tbl_lock);281}282283static int ath12k_dp_link_peer_rhash_insert(struct ath12k_dp *dp,284struct ath12k_dp_link_peer *peer)285{286struct ath12k_dp_link_peer *tmp;287288lockdep_assert_held(&dp->dp_lock);289290tmp = rhashtable_lookup_get_insert_fast(dp->rhead_peer_addr, &peer->rhash_addr,291dp->rhash_peer_addr_param);292if (!tmp)293return 0;294else if (IS_ERR(tmp))295return PTR_ERR(tmp);296else297return -EEXIST;298}299300static int ath12k_dp_link_peer_rhash_remove(struct ath12k_dp *dp,301struct ath12k_dp_link_peer *peer)302{303int ret;304305lockdep_assert_held(&dp->dp_lock);306307ret = rhashtable_remove_fast(dp->rhead_peer_addr, &peer->rhash_addr,308dp->rhash_peer_addr_param);309if (ret && ret != -ENOENT)310return ret;311312return 0;313}314315int ath12k_dp_link_peer_rhash_add(struct ath12k_dp *dp,316struct ath12k_dp_link_peer *peer)317{318int ret;319320lockdep_assert_held(&dp->dp_lock);321322ret = ath12k_dp_link_peer_rhash_insert(dp, peer);323if (ret)324ath12k_warn(dp, "failed to add peer %pM with id %d in rhash_addr ret %d\n",325peer->addr, peer->peer_id, ret);326327return ret;328}329330void ath12k_dp_link_peer_rhash_delete(struct ath12k_dp *dp,331struct ath12k_dp_link_peer *peer)332{333/* No failure handling and hence return type is void */334int ret;335336lockdep_assert_held(&dp->dp_lock);337338ret = ath12k_dp_link_peer_rhash_remove(dp, peer);339if (ret)340ath12k_warn(dp, "failed to remove peer %pM with id %d in rhash_addr ret %d\n",341peer->addr, peer->peer_id, ret);342}343344struct ath12k_dp_peer *ath12k_dp_peer_find_by_addr(struct ath12k_dp_hw *dp_hw, u8 *addr)345{346struct ath12k_dp_peer *peer;347348lockdep_assert_held(&dp_hw->peer_lock);349350list_for_each_entry(peer, &dp_hw->dp_peers_list, list) {351if (ether_addr_equal(peer->addr, addr))352return peer;353}354355return NULL;356}357EXPORT_SYMBOL(ath12k_dp_peer_find_by_addr);358359struct ath12k_dp_peer *ath12k_dp_peer_find_by_addr_and_sta(struct ath12k_dp_hw *dp_hw,360u8 *addr,361struct ieee80211_sta *sta)362{363struct ath12k_dp_peer *dp_peer;364365lockdep_assert_held(&dp_hw->peer_lock);366367list_for_each_entry(dp_peer, &dp_hw->dp_peers_list, list) {368if (ether_addr_equal(dp_peer->addr, addr) && (dp_peer->sta == sta))369return dp_peer;370}371372return NULL;373}374375static struct ath12k_dp_peer *ath12k_dp_peer_create_find(struct ath12k_dp_hw *dp_hw,376u8 *addr,377struct ieee80211_sta *sta,378bool mlo_peer)379{380struct ath12k_dp_peer *dp_peer;381382lockdep_assert_held(&dp_hw->peer_lock);383384list_for_each_entry(dp_peer, &dp_hw->dp_peers_list, list) {385if (ether_addr_equal(dp_peer->addr, addr)) {386if (!sta || mlo_peer || dp_peer->is_mlo ||387dp_peer->sta == sta)388return dp_peer;389}390}391392return NULL;393}394395/*396* Index of ath12k_dp_peer for MLO client is same as peer id of ath12k_dp_peer,397* while for ath12k_dp_link_peer(mlo and non-mlo) and ath12k_dp_peer for398* Non-MLO client it is derived as ((DEVICE_ID << 10) | (10 bits of peer id)).399*400* This is done because ml_peer_id and peer_id_table are at hw granularity,401* while link_peer_id is at device granularity, hence in order to avoid402* conflict this approach is followed.403*/404#define ATH12K_DP_PEER_TABLE_DEVICE_ID_SHIFT 10405406u16 ath12k_dp_peer_get_peerid_index(struct ath12k_dp *dp, u16 peer_id)407{408return (peer_id & ATH12K_PEER_ML_ID_VALID) ? peer_id :409((dp->device_id << ATH12K_DP_PEER_TABLE_DEVICE_ID_SHIFT) | peer_id);410}411412struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_pdev,413u16 peer_id)414{415u16 index;416struct ath12k_dp *dp = dp_pdev->dp;417418RCU_LOCKDEP_WARN(!rcu_read_lock_held(),419"ath12k dp peer find by peerid index called without rcu lock");420421if (!peer_id || peer_id >= ATH12K_DP_PEER_ID_INVALID)422return NULL;423424index = ath12k_dp_peer_get_peerid_index(dp, peer_id);425426return rcu_dereference(dp_pdev->dp_hw->dp_peers[index]);427}428EXPORT_SYMBOL(ath12k_dp_peer_find_by_peerid);429430struct ath12k_dp_link_peer *431ath12k_dp_link_peer_find_by_peerid(struct ath12k_pdev_dp *dp_pdev, u16 peer_id)432{433struct ath12k_dp_peer *dp_peer = NULL;434u8 link_id;435436RCU_LOCKDEP_WARN(!rcu_read_lock_held(),437"ath12k dp link peer find by peerid index called without rcu lock");438439if (dp_pdev->hw_link_id >= ATH12K_GROUP_MAX_RADIO)440return NULL;441442dp_peer = ath12k_dp_peer_find_by_peerid(dp_pdev, peer_id);443if (!dp_peer)444return NULL;445446link_id = dp_peer->hw_links[dp_pdev->hw_link_id];447448return rcu_dereference(dp_peer->link_peers[link_id]);449}450EXPORT_SYMBOL(ath12k_dp_link_peer_find_by_peerid);451452int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,453struct ath12k_dp_peer_create_params *params)454{455struct ath12k_dp_peer *dp_peer;456457spin_lock_bh(&dp_hw->peer_lock);458dp_peer = ath12k_dp_peer_create_find(dp_hw, addr, params->sta, params->is_mlo);459if (dp_peer) {460spin_unlock_bh(&dp_hw->peer_lock);461return -EEXIST;462}463spin_unlock_bh(&dp_hw->peer_lock);464465dp_peer = kzalloc_obj(*dp_peer, GFP_ATOMIC);466if (!dp_peer)467return -ENOMEM;468469ether_addr_copy(dp_peer->addr, addr);470dp_peer->sta = params->sta;471dp_peer->is_mlo = params->is_mlo;472473/*474* For MLO client, the host assigns the ML peer ID, so set peer_id in dp_peer475* For non-MLO client, host gets link peer ID from firmware and will be476* assigned at the time of link peer creation477*/478dp_peer->peer_id = params->is_mlo ? params->peer_id : ATH12K_DP_PEER_ID_INVALID;479dp_peer->ucast_ra_only = params->ucast_ra_only;480481dp_peer->sec_type = HAL_ENCRYPT_TYPE_OPEN;482dp_peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN;483dp_peer->ucast_ra_only = params->ucast_ra_only;484485spin_lock_bh(&dp_hw->peer_lock);486487list_add(&dp_peer->list, &dp_hw->dp_peers_list);488489/*490* For MLO client, the peer_id for ath12k_dp_peer is allocated by host491* and that peer_id is known at this point, and hence this ath12k_dp_peer492* can be added to the RCU table using the peer_id.493* For non-MLO client, this addition to RCU table shall be done at the494* time of assignment of ath12k_dp_link_peer to ath12k_dp_peer.495*/496if (dp_peer->is_mlo)497rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], dp_peer);498499spin_unlock_bh(&dp_hw->peer_lock);500501return 0;502}503504void ath12k_dp_peer_delete(struct ath12k_dp_hw *dp_hw, u8 *addr,505struct ieee80211_sta *sta)506{507struct ath12k_dp_peer *dp_peer;508509spin_lock_bh(&dp_hw->peer_lock);510511dp_peer = ath12k_dp_peer_find_by_addr_and_sta(dp_hw, addr, sta);512if (!dp_peer) {513spin_unlock_bh(&dp_hw->peer_lock);514return;515}516517if (dp_peer->is_mlo)518rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);519520list_del(&dp_peer->list);521522spin_unlock_bh(&dp_hw->peer_lock);523524synchronize_rcu();525kfree(dp_peer);526}527528int ath12k_dp_link_peer_assign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_hw,529u8 vdev_id, struct ieee80211_sta *sta, u8 *addr,530u8 link_id, u32 hw_link_id)531{532struct ath12k_dp_peer *dp_peer;533struct ath12k_dp_link_peer *peer, *temp_peer;534u16 peerid_index;535int ret = -EINVAL;536u8 *dp_peer_mac = !sta ? addr : sta->addr;537538spin_lock_bh(&dp->dp_lock);539540peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, addr);541if (!peer) {542ath12k_warn(dp, "failed to find dp_link_peer with mac %pM on vdev %u\n",543addr, vdev_id);544ret = -ENOENT;545goto err_peer;546}547548spin_lock_bh(&dp_hw->peer_lock);549550dp_peer = ath12k_dp_peer_find_by_addr_and_sta(dp_hw, dp_peer_mac, sta);551if (!dp_peer) {552ath12k_warn(dp, "failed to find dp_peer with mac %pM\n", dp_peer_mac);553ret = -ENOENT;554goto err_dp_peer;555}556557/*558* Set peer_id in dp_peer for non-mlo client, peer_id for mlo client is559* set during dp_peer create560*/561if (!dp_peer->is_mlo)562dp_peer->peer_id = peer->peer_id;563564peer->dp_peer = dp_peer;565peer->hw_link_id = hw_link_id;566567dp_peer->hw_links[peer->hw_link_id] = link_id;568569peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);570571rcu_assign_pointer(dp_peer->link_peers[peer->link_id], peer);572573rcu_assign_pointer(dp_hw->dp_peers[peerid_index], dp_peer);574575spin_unlock_bh(&dp_hw->peer_lock);576577/*578* In case of Split PHY and roaming scenario, pdev idx579* might differ but both the pdev will share same rhash580* table. In that case update the rhash table if link_peer is581* already present582*/583temp_peer = ath12k_dp_link_peer_find_by_addr(dp, addr);584if (temp_peer && temp_peer->hw_link_id != hw_link_id)585ath12k_dp_link_peer_rhash_delete(dp, temp_peer);586587ret = ath12k_dp_link_peer_rhash_add(dp, peer);588if (ret) {589/*590* If new entry addition failed, add back old entry591* If old entry addition also fails, then nothing592* can be done, simply proceed593*/594if (temp_peer)595ath12k_dp_link_peer_rhash_add(dp, temp_peer);596}597598spin_unlock_bh(&dp->dp_lock);599600return ret;601602err_dp_peer:603spin_unlock_bh(&dp_hw->peer_lock);604605err_peer:606spin_unlock_bh(&dp->dp_lock);607608return ret;609}610611void ath12k_dp_link_peer_unassign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_hw,612u8 vdev_id, u8 *addr, u32 hw_link_id)613{614struct ath12k_dp_peer *dp_peer;615struct ath12k_dp_link_peer *peer, *temp_peer;616u16 peerid_index;617618spin_lock_bh(&dp->dp_lock);619620peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, addr);621if (!peer || !peer->dp_peer) {622spin_unlock_bh(&dp->dp_lock);623return;624}625626spin_lock_bh(&dp_hw->peer_lock);627628dp_peer = peer->dp_peer;629dp_peer->hw_links[peer->hw_link_id] = 0;630631peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);632633rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL);634635rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL);636637spin_unlock_bh(&dp_hw->peer_lock);638639/* To handle roaming and split phy scenario */640temp_peer = ath12k_dp_link_peer_find_by_addr(dp, addr);641if (temp_peer && temp_peer->hw_link_id == hw_link_id)642ath12k_dp_link_peer_rhash_delete(dp, peer);643644spin_unlock_bh(&dp->dp_lock);645646synchronize_rcu();647}648649void650ath12k_dp_link_peer_get_sta_rate_info_stats(struct ath12k_dp *dp, const u8 *addr,651struct ath12k_dp_link_peer_rate_info *info)652{653struct ath12k_dp_link_peer *link_peer;654655guard(spinlock_bh)(&dp->dp_lock);656657link_peer = ath12k_dp_link_peer_find_by_addr(dp, addr);658if (!link_peer)659return;660661info->rx_duration = link_peer->rx_duration;662info->tx_duration = link_peer->tx_duration;663info->txrate.legacy = link_peer->txrate.legacy;664info->txrate.mcs = link_peer->txrate.mcs;665info->txrate.nss = link_peer->txrate.nss;666info->txrate.bw = link_peer->txrate.bw;667info->txrate.he_gi = link_peer->txrate.he_gi;668info->txrate.he_dcm = link_peer->txrate.he_dcm;669info->txrate.he_ru_alloc = link_peer->txrate.he_ru_alloc;670info->txrate.flags = link_peer->txrate.flags;671info->rssi_comb = link_peer->rssi_comb;672info->signal_avg = ewma_avg_rssi_read(&link_peer->avg_rssi);673}674675void ath12k_dp_link_peer_reset_rx_stats(struct ath12k_dp *dp, const u8 *addr)676{677struct ath12k_rx_peer_stats *rx_stats;678struct ath12k_dp_link_peer *link_peer;679680guard(spinlock_bh)(&dp->dp_lock);681682link_peer = ath12k_dp_link_peer_find_by_addr(dp, addr);683if (!link_peer || !link_peer->peer_stats.rx_stats)684return;685686rx_stats = link_peer->peer_stats.rx_stats;687if (rx_stats)688memset(rx_stats, 0, sizeof(*rx_stats));689}690691692