Path: blob/main/sys/contrib/dev/athk/ath11k/peer.c
48378 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.3* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.4*/56#include "core.h"7#include "peer.h"8#include "debug.h"910static struct ath11k_peer *ath11k_peer_find_list_by_id(struct ath11k_base *ab,11int peer_id)12{13struct ath11k_peer *peer;1415lockdep_assert_held(&ab->base_lock);1617list_for_each_entry(peer, &ab->peers, list) {18if (peer->peer_id != peer_id)19continue;2021return peer;22}2324return NULL;25}2627struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id,28const u8 *addr)29{30struct ath11k_peer *peer;3132lockdep_assert_held(&ab->base_lock);3334list_for_each_entry(peer, &ab->peers, list) {35if (peer->vdev_id != vdev_id)36continue;37if (!ether_addr_equal(peer->addr, addr))38continue;3940return peer;41}4243return NULL;44}4546struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab,47const u8 *addr)48{49struct ath11k_peer *peer;5051lockdep_assert_held(&ab->base_lock);5253if (!ab->rhead_peer_addr)54return NULL;5556peer = rhashtable_lookup_fast(ab->rhead_peer_addr, addr,57ab->rhash_peer_addr_param);5859return peer;60}6162struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab,63int peer_id)64{65struct ath11k_peer *peer;6667lockdep_assert_held(&ab->base_lock);6869if (!ab->rhead_peer_id)70return NULL;7172peer = rhashtable_lookup_fast(ab->rhead_peer_id, &peer_id,73ab->rhash_peer_id_param);7475return peer;76}7778struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,79int vdev_id)80{81struct ath11k_peer *peer;8283spin_lock_bh(&ab->base_lock);8485list_for_each_entry(peer, &ab->peers, list) {86if (vdev_id == peer->vdev_id) {87spin_unlock_bh(&ab->base_lock);88return peer;89}90}91spin_unlock_bh(&ab->base_lock);92return NULL;93}9495void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id)96{97struct ath11k_peer *peer;9899spin_lock_bh(&ab->base_lock);100101peer = ath11k_peer_find_list_by_id(ab, peer_id);102if (!peer) {103ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n",104peer_id);105goto exit;106}107108ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "peer unmap vdev %d peer %pM id %d\n",109peer->vdev_id, peer->addr, peer_id);110111list_del(&peer->list);112kfree(peer);113wake_up(&ab->peer_mapping_wq);114115exit:116spin_unlock_bh(&ab->base_lock);117}118119void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id,120u8 *mac_addr, u16 ast_hash, u16 hw_peer_id)121{122struct ath11k_peer *peer;123124spin_lock_bh(&ab->base_lock);125peer = ath11k_peer_find(ab, vdev_id, mac_addr);126if (!peer) {127peer = kzalloc(sizeof(*peer), GFP_ATOMIC);128if (!peer)129goto exit;130131peer->vdev_id = vdev_id;132peer->peer_id = peer_id;133peer->ast_hash = ast_hash;134peer->hw_peer_id = hw_peer_id;135ether_addr_copy(peer->addr, mac_addr);136list_add(&peer->list, &ab->peers);137wake_up(&ab->peer_mapping_wq);138}139140ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "peer map vdev %d peer %pM id %d\n",141vdev_id, mac_addr, peer_id);142143exit:144spin_unlock_bh(&ab->base_lock);145}146147static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id,148const u8 *addr, bool expect_mapped)149{150int ret;151152ret = wait_event_timeout(ab->peer_mapping_wq, ({153bool mapped;154155spin_lock_bh(&ab->base_lock);156mapped = !!ath11k_peer_find(ab, vdev_id, addr);157spin_unlock_bh(&ab->base_lock);158159(mapped == expect_mapped ||160test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags));161}), 3 * HZ);162163if (ret <= 0)164return -ETIMEDOUT;165166return 0;167}168169static inline int ath11k_peer_rhash_insert(struct ath11k_base *ab,170struct rhashtable *rtbl,171struct rhash_head *rhead,172struct rhashtable_params *params,173void *key)174{175struct ath11k_peer *tmp;176177lockdep_assert_held(&ab->tbl_mtx_lock);178179tmp = rhashtable_lookup_get_insert_fast(rtbl, rhead, *params);180181if (!tmp)182return 0;183else if (IS_ERR(tmp))184return PTR_ERR(tmp);185else186return -EEXIST;187}188189static inline int ath11k_peer_rhash_remove(struct ath11k_base *ab,190struct rhashtable *rtbl,191struct rhash_head *rhead,192struct rhashtable_params *params)193{194int ret;195196lockdep_assert_held(&ab->tbl_mtx_lock);197198ret = rhashtable_remove_fast(rtbl, rhead, *params);199if (ret && ret != -ENOENT)200return ret;201202return 0;203}204205static int ath11k_peer_rhash_add(struct ath11k_base *ab, struct ath11k_peer *peer)206{207int ret;208209lockdep_assert_held(&ab->base_lock);210lockdep_assert_held(&ab->tbl_mtx_lock);211212if (!ab->rhead_peer_id || !ab->rhead_peer_addr)213return -EPERM;214215ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_id, &peer->rhash_id,216&ab->rhash_peer_id_param, &peer->peer_id);217if (ret) {218ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_id ret %d\n",219peer->addr, peer->peer_id, ret);220return ret;221}222223ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_addr, &peer->rhash_addr,224&ab->rhash_peer_addr_param, &peer->addr);225if (ret) {226ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_addr ret %d\n",227peer->addr, peer->peer_id, ret);228goto err_clean;229}230231return 0;232233err_clean:234ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,235&ab->rhash_peer_id_param);236return ret;237}238239void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id)240{241struct ath11k_peer *peer, *tmp;242struct ath11k_base *ab = ar->ab;243244lockdep_assert_held(&ar->conf_mutex);245246mutex_lock(&ab->tbl_mtx_lock);247spin_lock_bh(&ab->base_lock);248list_for_each_entry_safe(peer, tmp, &ab->peers, list) {249if (peer->vdev_id != vdev_id)250continue;251252ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n",253peer->addr, vdev_id);254255ath11k_peer_rhash_delete(ab, peer);256list_del(&peer->list);257kfree(peer);258ar->num_peers--;259}260261spin_unlock_bh(&ab->base_lock);262mutex_unlock(&ab->tbl_mtx_lock);263}264265static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr)266{267return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, false);268}269270int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,271const u8 *addr)272{273int ret;274unsigned long time_left;275276ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr);277if (ret) {278ath11k_warn(ar->ab, "failed wait for peer deleted");279return ret;280}281282time_left = wait_for_completion_timeout(&ar->peer_delete_done,2833 * HZ);284if (time_left == 0) {285ath11k_warn(ar->ab, "Timeout in receiving peer delete response\n");286return -ETIMEDOUT;287}288289return 0;290}291292static int __ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr)293{294int ret;295struct ath11k_peer *peer;296struct ath11k_base *ab = ar->ab;297298lockdep_assert_held(&ar->conf_mutex);299300mutex_lock(&ab->tbl_mtx_lock);301spin_lock_bh(&ab->base_lock);302303peer = ath11k_peer_find_by_addr(ab, addr);304/* Check if the found peer is what we want to remove.305* While the sta is transitioning to another band we may306* have 2 peer with the same addr assigned to different307* vdev_id. Make sure we are deleting the correct peer.308*/309if (peer && peer->vdev_id == vdev_id)310ath11k_peer_rhash_delete(ab, peer);311312/* Fallback to peer list search if the correct peer can't be found.313* Skip the deletion of the peer from the rhash since it has already314* been deleted in peer add.315*/316if (!peer)317peer = ath11k_peer_find(ab, vdev_id, addr);318319if (!peer) {320spin_unlock_bh(&ab->base_lock);321mutex_unlock(&ab->tbl_mtx_lock);322323ath11k_warn(ab,324"failed to find peer vdev_id %d addr %pM in delete\n",325vdev_id, addr);326return -EINVAL;327}328329spin_unlock_bh(&ab->base_lock);330mutex_unlock(&ab->tbl_mtx_lock);331332reinit_completion(&ar->peer_delete_done);333334ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id);335if (ret) {336ath11k_warn(ab,337"failed to delete peer vdev_id %d addr %pM ret %d\n",338vdev_id, addr, ret);339return ret;340}341342ret = ath11k_wait_for_peer_delete_done(ar, vdev_id, addr);343if (ret)344return ret;345346return 0;347}348349int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr)350{351int ret;352353lockdep_assert_held(&ar->conf_mutex);354355ret = __ath11k_peer_delete(ar, vdev_id, addr);356if (ret)357return ret;358359ar->num_peers--;360361return 0;362}363364static int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8 *addr)365{366return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, true);367}368369int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,370struct ieee80211_sta *sta, struct peer_create_params *param)371{372struct ath11k_peer *peer;373struct ath11k_sta *arsta;374int ret, fbret;375376lockdep_assert_held(&ar->conf_mutex);377378if (ar->num_peers > (ar->max_num_peers - 1)) {379ath11k_warn(ar->ab,380"failed to create peer due to insufficient peer entry resource in firmware\n");381return -ENOBUFS;382}383384mutex_lock(&ar->ab->tbl_mtx_lock);385spin_lock_bh(&ar->ab->base_lock);386peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr);387if (peer) {388if (peer->vdev_id == param->vdev_id) {389spin_unlock_bh(&ar->ab->base_lock);390mutex_unlock(&ar->ab->tbl_mtx_lock);391return -EINVAL;392}393394/* Assume sta is transitioning to another band.395* Remove here the peer from rhash.396*/397ath11k_peer_rhash_delete(ar->ab, peer);398}399spin_unlock_bh(&ar->ab->base_lock);400mutex_unlock(&ar->ab->tbl_mtx_lock);401402ret = ath11k_wmi_send_peer_create_cmd(ar, param);403if (ret) {404ath11k_warn(ar->ab,405"failed to send peer create vdev_id %d ret %d\n",406param->vdev_id, ret);407return ret;408}409410ret = ath11k_wait_for_peer_created(ar, param->vdev_id,411param->peer_addr);412if (ret)413return ret;414415mutex_lock(&ar->ab->tbl_mtx_lock);416spin_lock_bh(&ar->ab->base_lock);417418peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr);419if (!peer) {420spin_unlock_bh(&ar->ab->base_lock);421mutex_unlock(&ar->ab->tbl_mtx_lock);422ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n",423param->peer_addr, param->vdev_id);424425ret = -ENOENT;426goto cleanup;427}428429ret = ath11k_peer_rhash_add(ar->ab, peer);430if (ret) {431spin_unlock_bh(&ar->ab->base_lock);432mutex_unlock(&ar->ab->tbl_mtx_lock);433goto cleanup;434}435436peer->pdev_idx = ar->pdev_idx;437peer->sta = sta;438439if (arvif->vif->type == NL80211_IFTYPE_STATION) {440arvif->ast_hash = peer->ast_hash;441arvif->ast_idx = peer->hw_peer_id;442}443444peer->sec_type = HAL_ENCRYPT_TYPE_OPEN;445peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN;446447if (sta) {448arsta = (struct ath11k_sta *)sta->drv_priv;449arsta->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 0) |450FIELD_PREP(HTT_TCL_META_DATA_PEER_ID,451peer->peer_id);452453/* set HTT extension valid bit to 0 by default */454arsta->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT;455}456457ar->num_peers++;458459spin_unlock_bh(&ar->ab->base_lock);460mutex_unlock(&ar->ab->tbl_mtx_lock);461462return 0;463464cleanup:465fbret = __ath11k_peer_delete(ar, param->vdev_id, param->peer_addr);466if (fbret)467ath11k_warn(ar->ab, "failed peer %pM delete vdev_id %d fallback ret %d\n",468param->peer_addr, param->vdev_id, fbret);469470return ret;471}472473int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer)474{475int ret;476477lockdep_assert_held(&ab->base_lock);478lockdep_assert_held(&ab->tbl_mtx_lock);479480if (!ab->rhead_peer_id || !ab->rhead_peer_addr)481return -EPERM;482483ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_addr, &peer->rhash_addr,484&ab->rhash_peer_addr_param);485if (ret) {486ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_addr ret %d\n",487peer->addr, peer->peer_id, ret);488return ret;489}490491ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,492&ab->rhash_peer_id_param);493if (ret) {494ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_id ret %d\n",495peer->addr, peer->peer_id, ret);496return ret;497}498499return 0;500}501502static int ath11k_peer_rhash_id_tbl_init(struct ath11k_base *ab)503{504struct rhashtable_params *param;505struct rhashtable *rhash_id_tbl;506int ret;507size_t size;508509lockdep_assert_held(&ab->tbl_mtx_lock);510511if (ab->rhead_peer_id)512return 0;513514size = sizeof(*ab->rhead_peer_id);515rhash_id_tbl = kzalloc(size, GFP_KERNEL);516if (!rhash_id_tbl) {517ath11k_warn(ab, "failed to init rhash id table due to no mem (size %zu)\n",518size);519return -ENOMEM;520}521522param = &ab->rhash_peer_id_param;523524param->key_offset = offsetof(struct ath11k_peer, peer_id);525param->head_offset = offsetof(struct ath11k_peer, rhash_id);526param->key_len = sizeof_field(struct ath11k_peer, peer_id);527param->automatic_shrinking = true;528param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab);529530ret = rhashtable_init(rhash_id_tbl, param);531if (ret) {532ath11k_warn(ab, "failed to init peer id rhash table %d\n", ret);533goto err_free;534}535536spin_lock_bh(&ab->base_lock);537538if (!ab->rhead_peer_id) {539ab->rhead_peer_id = rhash_id_tbl;540} else {541spin_unlock_bh(&ab->base_lock);542goto cleanup_tbl;543}544545spin_unlock_bh(&ab->base_lock);546547return 0;548549cleanup_tbl:550rhashtable_destroy(rhash_id_tbl);551err_free:552kfree(rhash_id_tbl);553554return ret;555}556557static int ath11k_peer_rhash_addr_tbl_init(struct ath11k_base *ab)558{559struct rhashtable_params *param;560struct rhashtable *rhash_addr_tbl;561int ret;562size_t size;563564lockdep_assert_held(&ab->tbl_mtx_lock);565566if (ab->rhead_peer_addr)567return 0;568569size = sizeof(*ab->rhead_peer_addr);570rhash_addr_tbl = kzalloc(size, GFP_KERNEL);571if (!rhash_addr_tbl) {572ath11k_warn(ab, "failed to init rhash addr table due to no mem (size %zu)\n",573size);574return -ENOMEM;575}576577param = &ab->rhash_peer_addr_param;578579param->key_offset = offsetof(struct ath11k_peer, addr);580param->head_offset = offsetof(struct ath11k_peer, rhash_addr);581param->key_len = sizeof_field(struct ath11k_peer, addr);582param->automatic_shrinking = true;583param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab);584585ret = rhashtable_init(rhash_addr_tbl, param);586if (ret) {587ath11k_warn(ab, "failed to init peer addr rhash table %d\n", ret);588goto err_free;589}590591spin_lock_bh(&ab->base_lock);592593if (!ab->rhead_peer_addr) {594ab->rhead_peer_addr = rhash_addr_tbl;595} else {596spin_unlock_bh(&ab->base_lock);597goto cleanup_tbl;598}599600spin_unlock_bh(&ab->base_lock);601602return 0;603604cleanup_tbl:605rhashtable_destroy(rhash_addr_tbl);606err_free:607kfree(rhash_addr_tbl);608609return ret;610}611612static inline void ath11k_peer_rhash_id_tbl_destroy(struct ath11k_base *ab)613{614lockdep_assert_held(&ab->tbl_mtx_lock);615616if (!ab->rhead_peer_id)617return;618619rhashtable_destroy(ab->rhead_peer_id);620kfree(ab->rhead_peer_id);621ab->rhead_peer_id = NULL;622}623624static inline void ath11k_peer_rhash_addr_tbl_destroy(struct ath11k_base *ab)625{626lockdep_assert_held(&ab->tbl_mtx_lock);627628if (!ab->rhead_peer_addr)629return;630631rhashtable_destroy(ab->rhead_peer_addr);632kfree(ab->rhead_peer_addr);633ab->rhead_peer_addr = NULL;634}635636int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab)637{638int ret;639640mutex_lock(&ab->tbl_mtx_lock);641642ret = ath11k_peer_rhash_id_tbl_init(ab);643if (ret)644goto out;645646ret = ath11k_peer_rhash_addr_tbl_init(ab);647if (ret)648goto cleanup_tbl;649650mutex_unlock(&ab->tbl_mtx_lock);651652return 0;653654cleanup_tbl:655ath11k_peer_rhash_id_tbl_destroy(ab);656out:657mutex_unlock(&ab->tbl_mtx_lock);658return ret;659}660661void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab)662{663mutex_lock(&ab->tbl_mtx_lock);664665ath11k_peer_rhash_addr_tbl_destroy(ab);666ath11k_peer_rhash_id_tbl_destroy(ab);667668mutex_unlock(&ab->tbl_mtx_lock);669}670671672