1 | // SPDX-License-Identifier: BSD-3-Clause-Clear |
2 | /* |
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 | |
11 | static 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 | |
28 | struct 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(addr1: peer->addr, addr2: addr)) |
39 | continue; |
40 | |
41 | return peer; |
42 | } |
43 | |
44 | return NULL; |
45 | } |
46 | |
47 | struct 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(ht: ab->rhead_peer_addr, key: addr, |
58 | params: ab->rhash_peer_addr_param); |
59 | |
60 | return peer; |
61 | } |
62 | |
63 | struct 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(ht: ab->rhead_peer_id, key: &peer_id, |
74 | params: ab->rhash_peer_id_param); |
75 | |
76 | return peer; |
77 | } |
78 | |
79 | struct 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(lock: &ab->base_lock); |
85 | |
86 | list_for_each_entry(peer, &ab->peers, list) { |
87 | if (vdev_id == peer->vdev_id) { |
88 | spin_unlock_bh(lock: &ab->base_lock); |
89 | return peer; |
90 | } |
91 | } |
92 | spin_unlock_bh(lock: &ab->base_lock); |
93 | return NULL; |
94 | } |
95 | |
96 | void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id) |
97 | { |
98 | struct ath11k_peer *peer; |
99 | |
100 | spin_lock_bh(lock: &ab->base_lock); |
101 | |
102 | peer = ath11k_peer_find_list_by_id(ab, peer_id); |
103 | if (!peer) { |
104 | ath11k_warn(ab, fmt: "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(entry: &peer->list); |
113 | kfree(objp: peer); |
114 | wake_up(&ab->peer_mapping_wq); |
115 | |
116 | exit: |
117 | spin_unlock_bh(lock: &ab->base_lock); |
118 | } |
119 | |
120 | void 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(lock: &ab->base_lock); |
126 | peer = ath11k_peer_find(ab, vdev_id, addr: mac_addr); |
127 | if (!peer) { |
128 | peer = kzalloc(size: 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(dst: peer->addr, src: mac_addr); |
137 | list_add(new: &peer->list, head: &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 | |
144 | exit: |
145 | spin_unlock_bh(lock: &ab->base_lock); |
146 | } |
147 | |
148 | static 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 | |
170 | static 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(ht: rtbl, obj: rhead, params: *params); |
181 | |
182 | if (!tmp) |
183 | return 0; |
184 | else if (IS_ERR(ptr: tmp)) |
185 | return PTR_ERR(ptr: tmp); |
186 | else |
187 | return -EEXIST; |
188 | } |
189 | |
190 | static 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(ht: rtbl, obj: rhead, params: *params); |
200 | if (ret && ret != -ENOENT) |
201 | return ret; |
202 | |
203 | return 0; |
204 | } |
205 | |
206 | static 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, rtbl: ab->rhead_peer_id, rhead: &peer->rhash_id, |
217 | params: &ab->rhash_peer_id_param, key: &peer->peer_id); |
218 | if (ret) { |
219 | ath11k_warn(ab, fmt: "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, rtbl: ab->rhead_peer_addr, rhead: &peer->rhash_addr, |
225 | params: &ab->rhash_peer_addr_param, key: &peer->addr); |
226 | if (ret) { |
227 | ath11k_warn(ab, fmt: "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 | |
234 | err_clean: |
235 | ath11k_peer_rhash_remove(ab, rtbl: ab->rhead_peer_id, rhead: &peer->rhash_id, |
236 | params: &ab->rhash_peer_id_param); |
237 | return ret; |
238 | } |
239 | |
240 | void 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(lock: &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, fmt: "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(entry: &peer->list); |
258 | kfree(objp: peer); |
259 | ar->num_peers--; |
260 | } |
261 | |
262 | spin_unlock_bh(lock: &ab->base_lock); |
263 | mutex_unlock(lock: &ab->tbl_mtx_lock); |
264 | } |
265 | |
266 | static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) |
267 | { |
268 | return ath11k_wait_for_peer_common(ab: ar->ab, vdev_id, addr, expect_mapped: false); |
269 | } |
270 | |
271 | int 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(ab: ar->ab, fmt: "failed wait for peer deleted" ); |
280 | return ret; |
281 | } |
282 | |
283 | time_left = wait_for_completion_timeout(x: &ar->peer_delete_done, |
284 | timeout: 3 * HZ); |
285 | if (time_left == 0) { |
286 | ath11k_warn(ab: ar->ab, fmt: "Timeout in receiving peer delete response\n" ); |
287 | return -ETIMEDOUT; |
288 | } |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | static 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(lock: &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 may |
307 | * have 2 peer with the same addr assigned to different |
308 | * 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 already |
315 | * 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(lock: &ab->base_lock); |
322 | mutex_unlock(lock: &ab->tbl_mtx_lock); |
323 | |
324 | ath11k_warn(ab, |
325 | fmt: "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(lock: &ab->base_lock); |
331 | mutex_unlock(lock: &ab->tbl_mtx_lock); |
332 | |
333 | reinit_completion(x: &ar->peer_delete_done); |
334 | |
335 | ret = ath11k_wmi_send_peer_delete_cmd(ar, peer_addr: addr, vdev_id); |
336 | if (ret) { |
337 | ath11k_warn(ab, |
338 | fmt: "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 | |
350 | int 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 | |
365 | static int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8 *addr) |
366 | { |
367 | return ath11k_wait_for_peer_common(ab: ar->ab, vdev_id, addr, expect_mapped: true); |
368 | } |
369 | |
370 | int 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(ab: ar->ab, |
381 | fmt: "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(lock: &ar->ab->base_lock); |
387 | peer = ath11k_peer_find_by_addr(ab: ar->ab, addr: param->peer_addr); |
388 | if (peer) { |
389 | if (peer->vdev_id == param->vdev_id) { |
390 | spin_unlock_bh(lock: &ar->ab->base_lock); |
391 | mutex_unlock(lock: &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(ab: ar->ab, peer); |
399 | } |
400 | spin_unlock_bh(lock: &ar->ab->base_lock); |
401 | mutex_unlock(lock: &ar->ab->tbl_mtx_lock); |
402 | |
403 | ret = ath11k_wmi_send_peer_create_cmd(ar, param); |
404 | if (ret) { |
405 | ath11k_warn(ab: ar->ab, |
406 | fmt: "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, vdev_id: param->vdev_id, |
412 | addr: param->peer_addr); |
413 | if (ret) |
414 | return ret; |
415 | |
416 | mutex_lock(&ar->ab->tbl_mtx_lock); |
417 | spin_lock_bh(lock: &ar->ab->base_lock); |
418 | |
419 | peer = ath11k_peer_find(ab: ar->ab, vdev_id: param->vdev_id, addr: param->peer_addr); |
420 | if (!peer) { |
421 | spin_unlock_bh(lock: &ar->ab->base_lock); |
422 | mutex_unlock(lock: &ar->ab->tbl_mtx_lock); |
423 | ath11k_warn(ab: ar->ab, fmt: "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(ab: ar->ab, peer); |
431 | if (ret) { |
432 | spin_unlock_bh(lock: &ar->ab->base_lock); |
433 | mutex_unlock(lock: &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(lock: &ar->ab->base_lock); |
461 | mutex_unlock(lock: &ar->ab->tbl_mtx_lock); |
462 | |
463 | return 0; |
464 | |
465 | cleanup: |
466 | fbret = __ath11k_peer_delete(ar, vdev_id: param->vdev_id, addr: param->peer_addr); |
467 | if (fbret) |
468 | ath11k_warn(ab: ar->ab, fmt: "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 | |
474 | int 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, rtbl: ab->rhead_peer_addr, rhead: &peer->rhash_addr, |
485 | params: &ab->rhash_peer_addr_param); |
486 | if (ret) { |
487 | ath11k_warn(ab, fmt: "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, rtbl: ab->rhead_peer_id, rhead: &peer->rhash_id, |
493 | params: &ab->rhash_peer_id_param); |
494 | if (ret) { |
495 | ath11k_warn(ab, fmt: "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 | |
503 | static 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, fmt: "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(ht: rhash_id_tbl, params: param); |
532 | if (ret) { |
533 | ath11k_warn(ab, fmt: "failed to init peer id rhash table %d\n" , ret); |
534 | goto err_free; |
535 | } |
536 | |
537 | spin_lock_bh(lock: &ab->base_lock); |
538 | |
539 | if (!ab->rhead_peer_id) { |
540 | ab->rhead_peer_id = rhash_id_tbl; |
541 | } else { |
542 | spin_unlock_bh(lock: &ab->base_lock); |
543 | goto cleanup_tbl; |
544 | } |
545 | |
546 | spin_unlock_bh(lock: &ab->base_lock); |
547 | |
548 | return 0; |
549 | |
550 | cleanup_tbl: |
551 | rhashtable_destroy(ht: rhash_id_tbl); |
552 | err_free: |
553 | kfree(objp: rhash_id_tbl); |
554 | |
555 | return ret; |
556 | } |
557 | |
558 | static 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, fmt: "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(ht: rhash_addr_tbl, params: param); |
587 | if (ret) { |
588 | ath11k_warn(ab, fmt: "failed to init peer addr rhash table %d\n" , ret); |
589 | goto err_free; |
590 | } |
591 | |
592 | spin_lock_bh(lock: &ab->base_lock); |
593 | |
594 | if (!ab->rhead_peer_addr) { |
595 | ab->rhead_peer_addr = rhash_addr_tbl; |
596 | } else { |
597 | spin_unlock_bh(lock: &ab->base_lock); |
598 | goto cleanup_tbl; |
599 | } |
600 | |
601 | spin_unlock_bh(lock: &ab->base_lock); |
602 | |
603 | return 0; |
604 | |
605 | cleanup_tbl: |
606 | rhashtable_destroy(ht: rhash_addr_tbl); |
607 | err_free: |
608 | kfree(objp: rhash_addr_tbl); |
609 | |
610 | return ret; |
611 | } |
612 | |
613 | static 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(ht: ab->rhead_peer_id); |
621 | kfree(objp: ab->rhead_peer_id); |
622 | ab->rhead_peer_id = NULL; |
623 | } |
624 | |
625 | static 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(ht: ab->rhead_peer_addr); |
633 | kfree(objp: ab->rhead_peer_addr); |
634 | ab->rhead_peer_addr = NULL; |
635 | } |
636 | |
637 | int 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(lock: &ab->tbl_mtx_lock); |
652 | |
653 | return 0; |
654 | |
655 | cleanup_tbl: |
656 | ath11k_peer_rhash_id_tbl_destroy(ab); |
657 | out: |
658 | mutex_unlock(lock: &ab->tbl_mtx_lock); |
659 | return ret; |
660 | } |
661 | |
662 | void 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(lock: &ab->tbl_mtx_lock); |
670 | } |
671 | |