671 lines · c
1// SPDX-License-Identifier: BSD-3-Clause-Clear2/*3 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.4 * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.5 */6 7#include "core.h"8#include "peer.h"9#include "debug.h"10 11static struct ath11k_peer *ath11k_peer_find_list_by_id(struct ath11k_base *ab,12 int peer_id)13{14 struct ath11k_peer *peer;15 16 lockdep_assert_held(&ab->base_lock);17 18 list_for_each_entry(peer, &ab->peers, list) {19 if (peer->peer_id != peer_id)20 continue;21 22 return peer;23 }24 25 return NULL;26}27 28struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id,29 const u8 *addr)30{31 struct ath11k_peer *peer;32 33 lockdep_assert_held(&ab->base_lock);34 35 list_for_each_entry(peer, &ab->peers, list) {36 if (peer->vdev_id != vdev_id)37 continue;38 if (!ether_addr_equal(peer->addr, addr))39 continue;40 41 return peer;42 }43 44 return NULL;45}46 47struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab,48 const u8 *addr)49{50 struct ath11k_peer *peer;51 52 lockdep_assert_held(&ab->base_lock);53 54 if (!ab->rhead_peer_addr)55 return NULL;56 57 peer = rhashtable_lookup_fast(ab->rhead_peer_addr, addr,58 ab->rhash_peer_addr_param);59 60 return peer;61}62 63struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab,64 int peer_id)65{66 struct ath11k_peer *peer;67 68 lockdep_assert_held(&ab->base_lock);69 70 if (!ab->rhead_peer_id)71 return NULL;72 73 peer = rhashtable_lookup_fast(ab->rhead_peer_id, &peer_id,74 ab->rhash_peer_id_param);75 76 return peer;77}78 79struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,80 int vdev_id)81{82 struct ath11k_peer *peer;83 84 spin_lock_bh(&ab->base_lock);85 86 list_for_each_entry(peer, &ab->peers, list) {87 if (vdev_id == peer->vdev_id) {88 spin_unlock_bh(&ab->base_lock);89 return peer;90 }91 }92 spin_unlock_bh(&ab->base_lock);93 return NULL;94}95 96void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id)97{98 struct ath11k_peer *peer;99 100 spin_lock_bh(&ab->base_lock);101 102 peer = ath11k_peer_find_list_by_id(ab, peer_id);103 if (!peer) {104 ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n",105 peer_id);106 goto exit;107 }108 109 ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "peer unmap vdev %d peer %pM id %d\n",110 peer->vdev_id, peer->addr, peer_id);111 112 list_del(&peer->list);113 kfree(peer);114 wake_up(&ab->peer_mapping_wq);115 116exit:117 spin_unlock_bh(&ab->base_lock);118}119 120void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id,121 u8 *mac_addr, u16 ast_hash, u16 hw_peer_id)122{123 struct ath11k_peer *peer;124 125 spin_lock_bh(&ab->base_lock);126 peer = ath11k_peer_find(ab, vdev_id, mac_addr);127 if (!peer) {128 peer = kzalloc(sizeof(*peer), GFP_ATOMIC);129 if (!peer)130 goto exit;131 132 peer->vdev_id = vdev_id;133 peer->peer_id = peer_id;134 peer->ast_hash = ast_hash;135 peer->hw_peer_id = hw_peer_id;136 ether_addr_copy(peer->addr, mac_addr);137 list_add(&peer->list, &ab->peers);138 wake_up(&ab->peer_mapping_wq);139 }140 141 ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "peer map vdev %d peer %pM id %d\n",142 vdev_id, mac_addr, peer_id);143 144exit:145 spin_unlock_bh(&ab->base_lock);146}147 148static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id,149 const u8 *addr, bool expect_mapped)150{151 int ret;152 153 ret = wait_event_timeout(ab->peer_mapping_wq, ({154 bool mapped;155 156 spin_lock_bh(&ab->base_lock);157 mapped = !!ath11k_peer_find(ab, vdev_id, addr);158 spin_unlock_bh(&ab->base_lock);159 160 (mapped == expect_mapped ||161 test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags));162 }), 3 * HZ);163 164 if (ret <= 0)165 return -ETIMEDOUT;166 167 return 0;168}169 170static inline int ath11k_peer_rhash_insert(struct ath11k_base *ab,171 struct rhashtable *rtbl,172 struct rhash_head *rhead,173 struct rhashtable_params *params,174 void *key)175{176 struct ath11k_peer *tmp;177 178 lockdep_assert_held(&ab->tbl_mtx_lock);179 180 tmp = rhashtable_lookup_get_insert_fast(rtbl, rhead, *params);181 182 if (!tmp)183 return 0;184 else if (IS_ERR(tmp))185 return PTR_ERR(tmp);186 else187 return -EEXIST;188}189 190static inline int ath11k_peer_rhash_remove(struct ath11k_base *ab,191 struct rhashtable *rtbl,192 struct rhash_head *rhead,193 struct rhashtable_params *params)194{195 int ret;196 197 lockdep_assert_held(&ab->tbl_mtx_lock);198 199 ret = rhashtable_remove_fast(rtbl, rhead, *params);200 if (ret && ret != -ENOENT)201 return ret;202 203 return 0;204}205 206static int ath11k_peer_rhash_add(struct ath11k_base *ab, struct ath11k_peer *peer)207{208 int ret;209 210 lockdep_assert_held(&ab->base_lock);211 lockdep_assert_held(&ab->tbl_mtx_lock);212 213 if (!ab->rhead_peer_id || !ab->rhead_peer_addr)214 return -EPERM;215 216 ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_id, &peer->rhash_id,217 &ab->rhash_peer_id_param, &peer->peer_id);218 if (ret) {219 ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_id ret %d\n",220 peer->addr, peer->peer_id, ret);221 return ret;222 }223 224 ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_addr, &peer->rhash_addr,225 &ab->rhash_peer_addr_param, &peer->addr);226 if (ret) {227 ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_addr ret %d\n",228 peer->addr, peer->peer_id, ret);229 goto err_clean;230 }231 232 return 0;233 234err_clean:235 ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,236 &ab->rhash_peer_id_param);237 return ret;238}239 240void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id)241{242 struct ath11k_peer *peer, *tmp;243 struct ath11k_base *ab = ar->ab;244 245 lockdep_assert_held(&ar->conf_mutex);246 247 mutex_lock(&ab->tbl_mtx_lock);248 spin_lock_bh(&ab->base_lock);249 list_for_each_entry_safe(peer, tmp, &ab->peers, list) {250 if (peer->vdev_id != vdev_id)251 continue;252 253 ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n",254 peer->addr, vdev_id);255 256 ath11k_peer_rhash_delete(ab, peer);257 list_del(&peer->list);258 kfree(peer);259 ar->num_peers--;260 }261 262 spin_unlock_bh(&ab->base_lock);263 mutex_unlock(&ab->tbl_mtx_lock);264}265 266static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr)267{268 return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, false);269}270 271int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,272 const u8 *addr)273{274 int ret;275 unsigned long time_left;276 277 ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr);278 if (ret) {279 ath11k_warn(ar->ab, "failed wait for peer deleted");280 return ret;281 }282 283 time_left = wait_for_completion_timeout(&ar->peer_delete_done,284 3 * HZ);285 if (time_left == 0) {286 ath11k_warn(ar->ab, "Timeout in receiving peer delete response\n");287 return -ETIMEDOUT;288 }289 290 return 0;291}292 293static int __ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr)294{295 int ret;296 struct ath11k_peer *peer;297 struct ath11k_base *ab = ar->ab;298 299 lockdep_assert_held(&ar->conf_mutex);300 301 mutex_lock(&ab->tbl_mtx_lock);302 spin_lock_bh(&ab->base_lock);303 304 peer = ath11k_peer_find_by_addr(ab, addr);305 /* Check if the found peer is what we want to remove.306 * While the sta is transitioning to another band we may307 * have 2 peer with the same addr assigned to different308 * vdev_id. Make sure we are deleting the correct peer.309 */310 if (peer && peer->vdev_id == vdev_id)311 ath11k_peer_rhash_delete(ab, peer);312 313 /* Fallback to peer list search if the correct peer can't be found.314 * Skip the deletion of the peer from the rhash since it has already315 * been deleted in peer add.316 */317 if (!peer)318 peer = ath11k_peer_find(ab, vdev_id, addr);319 320 if (!peer) {321 spin_unlock_bh(&ab->base_lock);322 mutex_unlock(&ab->tbl_mtx_lock);323 324 ath11k_warn(ab,325 "failed to find peer vdev_id %d addr %pM in delete\n",326 vdev_id, addr);327 return -EINVAL;328 }329 330 spin_unlock_bh(&ab->base_lock);331 mutex_unlock(&ab->tbl_mtx_lock);332 333 reinit_completion(&ar->peer_delete_done);334 335 ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id);336 if (ret) {337 ath11k_warn(ab,338 "failed to delete peer vdev_id %d addr %pM ret %d\n",339 vdev_id, addr, ret);340 return ret;341 }342 343 ret = ath11k_wait_for_peer_delete_done(ar, vdev_id, addr);344 if (ret)345 return ret;346 347 return 0;348}349 350int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr)351{352 int ret;353 354 lockdep_assert_held(&ar->conf_mutex);355 356 ret = __ath11k_peer_delete(ar, vdev_id, addr);357 if (ret)358 return ret;359 360 ar->num_peers--;361 362 return 0;363}364 365static int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8 *addr)366{367 return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, true);368}369 370int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,371 struct ieee80211_sta *sta, struct peer_create_params *param)372{373 struct ath11k_peer *peer;374 struct ath11k_sta *arsta;375 int ret, fbret;376 377 lockdep_assert_held(&ar->conf_mutex);378 379 if (ar->num_peers > (ar->max_num_peers - 1)) {380 ath11k_warn(ar->ab,381 "failed to create peer due to insufficient peer entry resource in firmware\n");382 return -ENOBUFS;383 }384 385 mutex_lock(&ar->ab->tbl_mtx_lock);386 spin_lock_bh(&ar->ab->base_lock);387 peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr);388 if (peer) {389 if (peer->vdev_id == param->vdev_id) {390 spin_unlock_bh(&ar->ab->base_lock);391 mutex_unlock(&ar->ab->tbl_mtx_lock);392 return -EINVAL;393 }394 395 /* Assume sta is transitioning to another band.396 * Remove here the peer from rhash.397 */398 ath11k_peer_rhash_delete(ar->ab, peer);399 }400 spin_unlock_bh(&ar->ab->base_lock);401 mutex_unlock(&ar->ab->tbl_mtx_lock);402 403 ret = ath11k_wmi_send_peer_create_cmd(ar, param);404 if (ret) {405 ath11k_warn(ar->ab,406 "failed to send peer create vdev_id %d ret %d\n",407 param->vdev_id, ret);408 return ret;409 }410 411 ret = ath11k_wait_for_peer_created(ar, param->vdev_id,412 param->peer_addr);413 if (ret)414 return ret;415 416 mutex_lock(&ar->ab->tbl_mtx_lock);417 spin_lock_bh(&ar->ab->base_lock);418 419 peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr);420 if (!peer) {421 spin_unlock_bh(&ar->ab->base_lock);422 mutex_unlock(&ar->ab->tbl_mtx_lock);423 ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n",424 param->peer_addr, param->vdev_id);425 426 ret = -ENOENT;427 goto cleanup;428 }429 430 ret = ath11k_peer_rhash_add(ar->ab, peer);431 if (ret) {432 spin_unlock_bh(&ar->ab->base_lock);433 mutex_unlock(&ar->ab->tbl_mtx_lock);434 goto cleanup;435 }436 437 peer->pdev_idx = ar->pdev_idx;438 peer->sta = sta;439 440 if (arvif->vif->type == NL80211_IFTYPE_STATION) {441 arvif->ast_hash = peer->ast_hash;442 arvif->ast_idx = peer->hw_peer_id;443 }444 445 peer->sec_type = HAL_ENCRYPT_TYPE_OPEN;446 peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN;447 448 if (sta) {449 arsta = ath11k_sta_to_arsta(sta);450 arsta->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 0) |451 FIELD_PREP(HTT_TCL_META_DATA_PEER_ID,452 peer->peer_id);453 454 /* set HTT extension valid bit to 0 by default */455 arsta->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT;456 }457 458 ar->num_peers++;459 460 spin_unlock_bh(&ar->ab->base_lock);461 mutex_unlock(&ar->ab->tbl_mtx_lock);462 463 return 0;464 465cleanup:466 fbret = __ath11k_peer_delete(ar, param->vdev_id, param->peer_addr);467 if (fbret)468 ath11k_warn(ar->ab, "failed peer %pM delete vdev_id %d fallback ret %d\n",469 param->peer_addr, param->vdev_id, fbret);470 471 return ret;472}473 474int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer)475{476 int ret;477 478 lockdep_assert_held(&ab->base_lock);479 lockdep_assert_held(&ab->tbl_mtx_lock);480 481 if (!ab->rhead_peer_id || !ab->rhead_peer_addr)482 return -EPERM;483 484 ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_addr, &peer->rhash_addr,485 &ab->rhash_peer_addr_param);486 if (ret) {487 ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_addr ret %d\n",488 peer->addr, peer->peer_id, ret);489 return ret;490 }491 492 ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,493 &ab->rhash_peer_id_param);494 if (ret) {495 ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_id ret %d\n",496 peer->addr, peer->peer_id, ret);497 return ret;498 }499 500 return 0;501}502 503static int ath11k_peer_rhash_id_tbl_init(struct ath11k_base *ab)504{505 struct rhashtable_params *param;506 struct rhashtable *rhash_id_tbl;507 int ret;508 size_t size;509 510 lockdep_assert_held(&ab->tbl_mtx_lock);511 512 if (ab->rhead_peer_id)513 return 0;514 515 size = sizeof(*ab->rhead_peer_id);516 rhash_id_tbl = kzalloc(size, GFP_KERNEL);517 if (!rhash_id_tbl) {518 ath11k_warn(ab, "failed to init rhash id table due to no mem (size %zu)\n",519 size);520 return -ENOMEM;521 }522 523 param = &ab->rhash_peer_id_param;524 525 param->key_offset = offsetof(struct ath11k_peer, peer_id);526 param->head_offset = offsetof(struct ath11k_peer, rhash_id);527 param->key_len = sizeof_field(struct ath11k_peer, peer_id);528 param->automatic_shrinking = true;529 param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab);530 531 ret = rhashtable_init(rhash_id_tbl, param);532 if (ret) {533 ath11k_warn(ab, "failed to init peer id rhash table %d\n", ret);534 goto err_free;535 }536 537 spin_lock_bh(&ab->base_lock);538 539 if (!ab->rhead_peer_id) {540 ab->rhead_peer_id = rhash_id_tbl;541 } else {542 spin_unlock_bh(&ab->base_lock);543 goto cleanup_tbl;544 }545 546 spin_unlock_bh(&ab->base_lock);547 548 return 0;549 550cleanup_tbl:551 rhashtable_destroy(rhash_id_tbl);552err_free:553 kfree(rhash_id_tbl);554 555 return ret;556}557 558static int ath11k_peer_rhash_addr_tbl_init(struct ath11k_base *ab)559{560 struct rhashtable_params *param;561 struct rhashtable *rhash_addr_tbl;562 int ret;563 size_t size;564 565 lockdep_assert_held(&ab->tbl_mtx_lock);566 567 if (ab->rhead_peer_addr)568 return 0;569 570 size = sizeof(*ab->rhead_peer_addr);571 rhash_addr_tbl = kzalloc(size, GFP_KERNEL);572 if (!rhash_addr_tbl) {573 ath11k_warn(ab, "failed to init rhash addr table due to no mem (size %zu)\n",574 size);575 return -ENOMEM;576 }577 578 param = &ab->rhash_peer_addr_param;579 580 param->key_offset = offsetof(struct ath11k_peer, addr);581 param->head_offset = offsetof(struct ath11k_peer, rhash_addr);582 param->key_len = sizeof_field(struct ath11k_peer, addr);583 param->automatic_shrinking = true;584 param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab);585 586 ret = rhashtable_init(rhash_addr_tbl, param);587 if (ret) {588 ath11k_warn(ab, "failed to init peer addr rhash table %d\n", ret);589 goto err_free;590 }591 592 spin_lock_bh(&ab->base_lock);593 594 if (!ab->rhead_peer_addr) {595 ab->rhead_peer_addr = rhash_addr_tbl;596 } else {597 spin_unlock_bh(&ab->base_lock);598 goto cleanup_tbl;599 }600 601 spin_unlock_bh(&ab->base_lock);602 603 return 0;604 605cleanup_tbl:606 rhashtable_destroy(rhash_addr_tbl);607err_free:608 kfree(rhash_addr_tbl);609 610 return ret;611}612 613static inline void ath11k_peer_rhash_id_tbl_destroy(struct ath11k_base *ab)614{615 lockdep_assert_held(&ab->tbl_mtx_lock);616 617 if (!ab->rhead_peer_id)618 return;619 620 rhashtable_destroy(ab->rhead_peer_id);621 kfree(ab->rhead_peer_id);622 ab->rhead_peer_id = NULL;623}624 625static inline void ath11k_peer_rhash_addr_tbl_destroy(struct ath11k_base *ab)626{627 lockdep_assert_held(&ab->tbl_mtx_lock);628 629 if (!ab->rhead_peer_addr)630 return;631 632 rhashtable_destroy(ab->rhead_peer_addr);633 kfree(ab->rhead_peer_addr);634 ab->rhead_peer_addr = NULL;635}636 637int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab)638{639 int ret;640 641 mutex_lock(&ab->tbl_mtx_lock);642 643 ret = ath11k_peer_rhash_id_tbl_init(ab);644 if (ret)645 goto out;646 647 ret = ath11k_peer_rhash_addr_tbl_init(ab);648 if (ret)649 goto cleanup_tbl;650 651 mutex_unlock(&ab->tbl_mtx_lock);652 653 return 0;654 655cleanup_tbl:656 ath11k_peer_rhash_id_tbl_destroy(ab);657out:658 mutex_unlock(&ab->tbl_mtx_lock);659 return ret;660}661 662void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab)663{664 mutex_lock(&ab->tbl_mtx_lock);665 666 ath11k_peer_rhash_addr_tbl_destroy(ab);667 ath11k_peer_rhash_id_tbl_destroy(ab);668 669 mutex_unlock(&ab->tbl_mtx_lock);670}671