1 /******************************************************************************
2 *
3 * Copyright(c) 2007 - 2017 Realtek Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 *****************************************************************************/
15 #define _RTW_WDS_C_
16
17 #include <drv_types.h>
18
19 #if defined(CONFIG_RTW_WDS)
20 #include <linux/jhash.h>
21
22 #if defined(CONFIG_AP_MODE)
23
24 #ifdef PLATFORM_LINUX
25 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
rtw_wpath_free_rcu(struct rtw_wds_path * wpath)26 static void rtw_wpath_free_rcu(struct rtw_wds_path *wpath)
27 {
28 kfree_rcu(wpath, rcu);
29 rtw_mstat_update(MSTAT_TYPE_PHY, MSTAT_FREE, sizeof(struct rtw_wds_path));
30 }
31 #else
rtw_wpath_free_rcu_callback(rtw_rcu_head * head)32 static void rtw_wpath_free_rcu_callback(rtw_rcu_head *head)
33 {
34 struct rtw_wds_path *wpath;
35
36 wpath = container_of(head, struct rtw_wds_path, rcu);
37 rtw_mfree(wpath, sizeof(struct rtw_wds_path));
38 }
39
rtw_wpath_free_rcu(struct rtw_wds_path * wpath)40 static void rtw_wpath_free_rcu(struct rtw_wds_path *wpath)
41 {
42 call_rcu(&wpath->rcu, rtw_wpath_free_rcu_callback);
43 }
44 #endif
45 #endif /* PLATFORM_LINUX */
46
47 static void rtw_wds_path_free_rcu(struct rtw_wds_table *tbl, struct rtw_wds_path *wpath);
48
rtw_wds_table_hash(const void * addr,u32 len,u32 seed)49 static u32 rtw_wds_table_hash(const void *addr, u32 len, u32 seed)
50 {
51 /* Use last four bytes of hw addr as hash index */
52 return jhash_1word(*(u32 *)(addr+2), seed);
53 }
54
55 static const rtw_rhashtable_params rtw_wds_rht_params = {
56 .nelem_hint = 2,
57 .automatic_shrinking = true,
58 .key_len = ETH_ALEN,
59 .key_offset = offsetof(struct rtw_wds_path, dst),
60 .head_offset = offsetof(struct rtw_wds_path, rhash),
61 .hashfn = rtw_wds_table_hash,
62 };
63
rtw_wds_path_rht_free(void * ptr,void * tblptr)64 static void rtw_wds_path_rht_free(void *ptr, void *tblptr)
65 {
66 struct rtw_wds_path *wpath = ptr;
67 struct rtw_wds_table *tbl = tblptr;
68
69 rtw_wds_path_free_rcu(tbl, wpath);
70 }
71
rtw_wds_table_alloc(void)72 static struct rtw_wds_table *rtw_wds_table_alloc(void)
73 {
74 struct rtw_wds_table *newtbl;
75
76 newtbl = rtw_malloc(sizeof(struct rtw_wds_table));
77 if (!newtbl)
78 return NULL;
79
80 return newtbl;
81 }
82
rtw_wds_table_free(struct rtw_wds_table * tbl)83 static void rtw_wds_table_free(struct rtw_wds_table *tbl)
84 {
85 rtw_rhashtable_free_and_destroy(&tbl->rhead,
86 rtw_wds_path_rht_free, tbl);
87 rtw_mfree(tbl, sizeof(struct rtw_wds_table));
88 }
89
rtw_wds_path_assign_nexthop(struct rtw_wds_path * wpath,struct sta_info * sta)90 void rtw_wds_path_assign_nexthop(struct rtw_wds_path *wpath, struct sta_info *sta)
91 {
92 rtw_rcu_assign_pointer(wpath->next_hop, sta);
93 }
94
rtw_wpath_lookup(struct rtw_wds_table * tbl,const u8 * dst)95 static struct rtw_wds_path *rtw_wpath_lookup(struct rtw_wds_table *tbl, const u8 *dst)
96 {
97 struct rtw_wds_path *wpath;
98
99 if (!tbl)
100 return NULL;
101
102 wpath = rtw_rhashtable_lookup_fast(&tbl->rhead, dst, rtw_wds_rht_params);
103
104 return wpath;
105 }
106
rtw_wds_path_lookup(_adapter * adapter,const u8 * dst)107 struct rtw_wds_path *rtw_wds_path_lookup(_adapter *adapter, const u8 *dst)
108 {
109 return rtw_wpath_lookup(adapter->wds_paths, dst);
110 }
111
112 static struct rtw_wds_path *
__rtw_wds_path_lookup_by_idx(struct rtw_wds_table * tbl,int idx)113 __rtw_wds_path_lookup_by_idx(struct rtw_wds_table *tbl, int idx)
114 {
115 int i = 0, ret;
116 struct rtw_wds_path *wpath = NULL;
117 rtw_rhashtable_iter iter;
118
119 if (!tbl)
120 return NULL;
121
122 ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
123 if (ret)
124 return NULL;
125
126 ret = rtw_rhashtable_walk_start(&iter);
127 if (ret && ret != -EAGAIN)
128 goto err;
129
130 while ((wpath = rtw_rhashtable_walk_next(&iter))) {
131 if (IS_ERR(wpath) && PTR_ERR(wpath) == -EAGAIN)
132 continue;
133 if (IS_ERR(wpath))
134 break;
135 if (i++ == idx)
136 break;
137 }
138 err:
139 rtw_rhashtable_walk_stop(&iter);
140 rtw_rhashtable_walk_exit(&iter);
141
142 if (IS_ERR(wpath) || !wpath)
143 return NULL;
144
145 return wpath;
146 }
147
148 /**
149 * Locking: must be called within a read rcu section.
150 */
151 struct rtw_wds_path *
rtw_wds_path_lookup_by_idx(_adapter * adapter,int idx)152 rtw_wds_path_lookup_by_idx(_adapter *adapter, int idx)
153 {
154 return __rtw_wds_path_lookup_by_idx(adapter->wds_paths, idx);
155 }
156
dump_wpath(void * sel,_adapter * adapter)157 void dump_wpath(void *sel, _adapter *adapter)
158 {
159 struct rtw_wds_path *wpath;
160 int idx = 0;
161 char dst[ETH_ALEN];
162 char next_hop[ETH_ALEN];
163 u32 age_ms;
164
165 RTW_PRINT_SEL(sel, "num:%d\n", ATOMIC_READ(&adapter->wds_path_num));
166 RTW_PRINT_SEL(sel, "%-17s %-17s %-6s\n"
167 , "dst", "next_hop", "age"
168 );
169
170 do {
171 rtw_rcu_read_lock();
172
173 wpath = rtw_wds_path_lookup_by_idx(adapter, idx);
174 if (wpath) {
175 _rtw_memcpy(dst, wpath->dst, ETH_ALEN);
176 _rtw_memcpy(next_hop, wpath->next_hop->cmn.mac_addr, ETH_ALEN);
177 age_ms = rtw_get_passing_time_ms(wpath->last_update);
178 }
179
180 rtw_rcu_read_unlock();
181
182 if (wpath) {
183 RTW_PRINT_SEL(sel, MAC_FMT" "MAC_FMT" %6u\n"
184 , MAC_ARG(dst), MAC_ARG(next_hop)
185 , age_ms < 999999 ? age_ms : 999999
186 );
187 }
188
189 idx++;
190 } while (wpath);
191 }
192
193 static
rtw_wds_path_new(_adapter * adapter,const u8 * dst)194 struct rtw_wds_path *rtw_wds_path_new(_adapter *adapter,
195 const u8 *dst)
196 {
197 struct rtw_wds_path *new_wpath;
198
199 new_wpath = rtw_zmalloc(sizeof(struct rtw_wds_path));
200 if (!new_wpath)
201 return NULL;
202
203 new_wpath->adapter = adapter;
204 _rtw_memcpy(new_wpath->dst, dst, ETH_ALEN);
205 new_wpath->last_update = rtw_get_current_time();
206
207 return new_wpath;
208 }
209
210 /**
211 * Returns: 0 on success
212 *
213 * State: the initial state of the new path is set to 0
214 */
rtw_wds_path_add(_adapter * adapter,const u8 * dst,struct sta_info * next_hop)215 struct rtw_wds_path *rtw_wds_path_add(_adapter *adapter,
216 const u8 *dst, struct sta_info *next_hop)
217 {
218 struct rtw_wds_table *tbl = adapter->wds_paths;
219 struct rtw_wds_path *wpath, *new_wpath;
220 int ret;
221
222 if (!tbl)
223 return ERR_PTR(-ENOTSUPP);
224
225 if (_rtw_memcmp(dst, adapter_mac_addr(adapter), ETH_ALEN) == _TRUE)
226 /* never add ourselves as neighbours */
227 return ERR_PTR(-ENOTSUPP);
228
229 if (IS_MCAST(dst))
230 return ERR_PTR(-ENOTSUPP);
231
232 if (ATOMIC_INC_UNLESS(&adapter->wds_path_num, RTW_WDS_MAX_PATHS) == 0)
233 return ERR_PTR(-ENOSPC);
234
235 new_wpath = rtw_wds_path_new(adapter, dst);
236 if (!new_wpath)
237 return ERR_PTR(-ENOMEM);
238
239 do {
240 ret = rtw_rhashtable_lookup_insert_fast(&tbl->rhead,
241 &new_wpath->rhash,
242 rtw_wds_rht_params);
243
244 if (ret == -EEXIST)
245 wpath = rtw_rhashtable_lookup_fast(&tbl->rhead,
246 dst,
247 rtw_wds_rht_params);
248
249 } while (unlikely(ret == -EEXIST && !wpath));
250
251 if (ret && ret != -EEXIST)
252 return ERR_PTR(ret);
253
254 /* At this point either new_wpath was added, or we found a
255 * matching entry already in the table; in the latter case
256 * free the unnecessary new entry.
257 */
258 if (ret == -EEXIST) {
259 rtw_mfree(new_wpath, sizeof(struct rtw_wds_path));
260 new_wpath = wpath;
261 }
262 rtw_wds_path_assign_nexthop(new_wpath, next_hop);
263
264 return new_wpath;
265 }
266
rtw_wds_path_free_rcu(struct rtw_wds_table * tbl,struct rtw_wds_path * wpath)267 static void rtw_wds_path_free_rcu(struct rtw_wds_table *tbl,
268 struct rtw_wds_path *wpath)
269 {
270 _adapter *adapter = wpath->adapter;
271
272 ATOMIC_DEC(&adapter->wds_path_num);
273
274 rtw_wpath_free_rcu(wpath);
275 }
276
__rtw_wds_path_del(struct rtw_wds_table * tbl,struct rtw_wds_path * wpath)277 static void __rtw_wds_path_del(struct rtw_wds_table *tbl, struct rtw_wds_path *wpath)
278 {
279 rtw_rhashtable_remove_fast(&tbl->rhead, &wpath->rhash, rtw_wds_rht_params);
280 rtw_wds_path_free_rcu(tbl, wpath);
281 }
282
rtw_wds_path_flush_by_nexthop(struct sta_info * sta)283 void rtw_wds_path_flush_by_nexthop(struct sta_info *sta)
284 {
285 _adapter *adapter = sta->padapter;
286 struct rtw_wds_table *tbl = adapter->wds_paths;
287 struct rtw_wds_path *wpath;
288 rtw_rhashtable_iter iter;
289 int ret;
290
291 if (!tbl)
292 return;
293
294 ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
295 if (ret)
296 return;
297
298 ret = rtw_rhashtable_walk_start(&iter);
299 if (ret && ret != -EAGAIN)
300 goto out;
301
302 while ((wpath = rtw_rhashtable_walk_next(&iter))) {
303 if (IS_ERR(wpath) && PTR_ERR(wpath) == -EAGAIN)
304 continue;
305 if (IS_ERR(wpath))
306 break;
307
308 if (rtw_rcu_access_pointer(wpath->next_hop) == sta)
309 __rtw_wds_path_del(tbl, wpath);
310 }
311 out:
312 rtw_rhashtable_walk_stop(&iter);
313 rtw_rhashtable_walk_exit(&iter);
314 }
315
rtw_wds_table_flush_by_iface(struct rtw_wds_table * tbl)316 static void rtw_wds_table_flush_by_iface(struct rtw_wds_table *tbl)
317 {
318 struct rtw_wds_path *wpath;
319 rtw_rhashtable_iter iter;
320 int ret;
321
322 if (!tbl)
323 return;
324
325 ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
326 if (ret)
327 return;
328
329 ret = rtw_rhashtable_walk_start(&iter);
330 if (ret && ret != -EAGAIN)
331 goto out;
332
333 while ((wpath = rtw_rhashtable_walk_next(&iter))) {
334 if (IS_ERR(wpath) && PTR_ERR(wpath) == -EAGAIN)
335 continue;
336 if (IS_ERR(wpath))
337 break;
338 __rtw_wds_path_del(tbl, wpath);
339 }
340 out:
341 rtw_rhashtable_walk_stop(&iter);
342 rtw_rhashtable_walk_exit(&iter);
343 }
344
rtw_wds_path_flush_by_iface(_adapter * adapter)345 void rtw_wds_path_flush_by_iface(_adapter *adapter)
346 {
347 rtw_wds_table_flush_by_iface(adapter->wds_paths);
348 }
349
rtw_wds_table_path_del(struct rtw_wds_table * tbl,const u8 * addr)350 static int rtw_wds_table_path_del(struct rtw_wds_table *tbl,
351 const u8 *addr)
352 {
353 struct rtw_wds_path *wpath;
354
355 if (!tbl)
356 return -ENXIO;
357
358 rtw_rcu_read_lock();
359 wpath = rtw_rhashtable_lookup_fast(&tbl->rhead, addr, rtw_wds_rht_params);
360 if (!wpath) {
361 rtw_rcu_read_unlock();
362 return -ENXIO;
363 }
364
365 __rtw_wds_path_del(tbl, wpath);
366 rtw_rcu_read_unlock();
367 return 0;
368 }
369
rtw_wds_path_del(_adapter * adapter,const u8 * addr)370 int rtw_wds_path_del(_adapter *adapter, const u8 *addr)
371 {
372 int err;
373
374 err = rtw_wds_table_path_del(adapter->wds_paths, addr);
375 return err;
376 }
377
rtw_wds_pathtbl_init(_adapter * adapter)378 int rtw_wds_pathtbl_init(_adapter *adapter)
379 {
380 struct rtw_wds_table *tbl_path;
381 int ret;
382
383 tbl_path = rtw_wds_table_alloc();
384 if (!tbl_path)
385 return -ENOMEM;
386
387 rtw_rhashtable_init(&tbl_path->rhead, &rtw_wds_rht_params);
388
389 ATOMIC_SET(&adapter->wds_path_num, 0);
390 adapter->wds_paths = tbl_path;
391
392 return 0;
393 }
394
395 static
rtw_wds_path_tbl_expire(_adapter * adapter,struct rtw_wds_table * tbl)396 void rtw_wds_path_tbl_expire(_adapter *adapter,
397 struct rtw_wds_table *tbl)
398 {
399 struct rtw_wds_path *wpath;
400 rtw_rhashtable_iter iter;
401 int ret;
402
403 if (!tbl)
404 return;
405
406 ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
407 if (ret)
408 return;
409
410 ret = rtw_rhashtable_walk_start(&iter);
411 if (ret && ret != -EAGAIN)
412 goto out;
413
414 while ((wpath = rtw_rhashtable_walk_next(&iter))) {
415 if (IS_ERR(wpath) && PTR_ERR(wpath) == -EAGAIN)
416 continue;
417 if (IS_ERR(wpath))
418 break;
419 if (rtw_time_after(rtw_get_current_time(), wpath->last_update + RTW_WDS_PATH_EXPIRE))
420 __rtw_wds_path_del(tbl, wpath);
421 }
422
423 out:
424 rtw_rhashtable_walk_stop(&iter);
425 rtw_rhashtable_walk_exit(&iter);
426 }
427
rtw_wds_path_expire(_adapter * adapter)428 void rtw_wds_path_expire(_adapter *adapter)
429 {
430 rtw_wds_path_tbl_expire(adapter, adapter->wds_paths);
431 }
432
rtw_wds_pathtbl_unregister(_adapter * adapter)433 void rtw_wds_pathtbl_unregister(_adapter *adapter)
434 {
435 if (adapter->wds_paths) {
436 rtw_wds_table_free(adapter->wds_paths);
437 adapter->wds_paths = NULL;
438 }
439 }
440
rtw_wds_nexthop_lookup(_adapter * adapter,const u8 * da,u8 * ra)441 int rtw_wds_nexthop_lookup(_adapter *adapter, const u8 *da, u8 *ra)
442 {
443 struct rtw_wds_path *wpath;
444 struct sta_info *next_hop;
445 int err = -ENOENT;
446
447 rtw_rcu_read_lock();
448 wpath = rtw_wds_path_lookup(adapter, da);
449
450 if (!wpath)
451 goto endlookup;
452
453 next_hop = rtw_rcu_dereference(wpath->next_hop);
454 if (next_hop) {
455 _rtw_memcpy(ra, next_hop->cmn.mac_addr, ETH_ALEN);
456 err = 0;
457 }
458
459 endlookup:
460 rtw_rcu_read_unlock();
461 return err;
462 }
463
464 #endif /* defined(CONFIG_AP_MODE) */
465
466 /* WDS group adddressed proxy TX record */
467 struct rtw_wds_gptr {
468 u8 src[ETH_ALEN];
469 systime last_update;
470 rtw_rhash_head rhash;
471 _adapter *adapter;
472 rtw_rcu_head rcu;
473 };
474
475 #define RTW_WDS_GPTR_EXPIRE (2 * HZ)
476
477 /* Maximum number of gptrs per interface */
478 #define RTW_WDS_MAX_GPTRS 1024
479
480 #ifdef PLATFORM_LINUX
481 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
rtw_wgptr_free_rcu(struct rtw_wds_gptr * wgptr)482 static void rtw_wgptr_free_rcu(struct rtw_wds_gptr *wgptr)
483 {
484 kfree_rcu(wgptr, rcu);
485 rtw_mstat_update(MSTAT_TYPE_PHY, MSTAT_FREE, sizeof(struct rtw_wds_gptr));
486 }
487 #else
rtw_wgptr_free_rcu_callback(rtw_rcu_head * head)488 static void rtw_wgptr_free_rcu_callback(rtw_rcu_head *head)
489 {
490 struct rtw_wds_gptr *wgptr;
491
492 wgptr = container_of(head, struct rtw_wds_gptr, rcu);
493 rtw_mfree(wgptr, sizeof(struct rtw_wds_gptr));
494 }
495
rtw_wgptr_free_rcu(struct rtw_wds_gptr * wgptr)496 static void rtw_wgptr_free_rcu(struct rtw_wds_gptr *wgptr)
497 {
498 call_rcu(&wgptr->rcu, rtw_wgptr_free_rcu_callback);
499 }
500 #endif
501 #endif /* PLATFORM_LINUX */
502
rtw_wds_gptr_free_rcu(struct rtw_wds_gptr_table * tbl,struct rtw_wds_gptr * wgptr)503 static void rtw_wds_gptr_free_rcu(struct rtw_wds_gptr_table *tbl, struct rtw_wds_gptr *wgptr)
504 {
505 _adapter *adapter = wgptr->adapter;
506
507 ATOMIC_DEC(&adapter->wds_gpt_record_num);
508
509 rtw_wgptr_free_rcu(wgptr);
510 }
511
rtw_wds_gptr_table_hash(const void * addr,u32 len,u32 seed)512 static u32 rtw_wds_gptr_table_hash(const void *addr, u32 len, u32 seed)
513 {
514 /* Use last four bytes of hw addr as hash index */
515 return jhash_1word(*(u32 *)(addr+2), seed);
516 }
517
518 static const rtw_rhashtable_params rtw_wds_gptr_rht_params = {
519 .nelem_hint = 2,
520 .automatic_shrinking = true,
521 .key_len = ETH_ALEN,
522 .key_offset = offsetof(struct rtw_wds_gptr, src),
523 .head_offset = offsetof(struct rtw_wds_gptr, rhash),
524 .hashfn = rtw_wds_gptr_table_hash,
525 };
526
rtw_wds_gptr_rht_free(void * ptr,void * tblptr)527 static void rtw_wds_gptr_rht_free(void *ptr, void *tblptr)
528 {
529 struct rtw_wds_gptr *wgptr = ptr;
530 struct rtw_wds_gptr_table *tbl = tblptr;
531
532 rtw_wds_gptr_free_rcu(tbl, wgptr);
533 }
534
rtw_wds_gptr_table_alloc(void)535 static struct rtw_wds_gptr_table *rtw_wds_gptr_table_alloc(void)
536 {
537 struct rtw_wds_gptr_table *newtbl;
538
539 newtbl = rtw_malloc(sizeof(struct rtw_wds_gptr_table));
540 if (!newtbl)
541 return NULL;
542
543 return newtbl;
544 }
545
rtw_wds_gptr_table_free(struct rtw_wds_gptr_table * tbl)546 static void rtw_wds_gptr_table_free(struct rtw_wds_gptr_table *tbl)
547 {
548 rtw_rhashtable_free_and_destroy(&tbl->rhead,
549 rtw_wds_gptr_rht_free, tbl);
550 rtw_mfree(tbl, sizeof(struct rtw_wds_gptr_table));
551 }
552
rtw_wds_gptr_lookup(_adapter * adapter,const u8 * src)553 static struct rtw_wds_gptr *rtw_wds_gptr_lookup(_adapter *adapter, const u8 *src)
554 {
555 struct rtw_wds_gptr_table *tbl = adapter->wds_gpt_records;
556
557 if (!tbl)
558 return NULL;
559
560 return rtw_rhashtable_lookup_fast(&tbl->rhead, src, rtw_wds_gptr_rht_params);
561 }
562
563 /**
564 * Locking: must be called within a read rcu section.
565 */
rtw_wds_gptr_lookup_by_idx(_adapter * adapter,int idx)566 static struct rtw_wds_gptr *rtw_wds_gptr_lookup_by_idx(_adapter *adapter, int idx)
567 {
568 int i = 0, ret;
569 struct rtw_wds_gptr_table *tbl = adapter->wds_gpt_records;
570 struct rtw_wds_gptr *wgptr = NULL;
571 rtw_rhashtable_iter iter;
572
573 if (!tbl)
574 return NULL;
575
576 ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
577 if (ret)
578 return NULL;
579
580 ret = rtw_rhashtable_walk_start(&iter);
581 if (ret && ret != -EAGAIN)
582 goto err;
583
584 while ((wgptr = rtw_rhashtable_walk_next(&iter))) {
585 if (IS_ERR(wgptr) && PTR_ERR(wgptr) == -EAGAIN)
586 continue;
587 if (IS_ERR(wgptr))
588 break;
589 if (i++ == idx)
590 break;
591 }
592 err:
593 rtw_rhashtable_walk_stop(&iter);
594 rtw_rhashtable_walk_exit(&iter);
595
596 if (IS_ERR(wgptr) || !wgptr)
597 return NULL;
598
599 return wgptr;
600 }
601
dump_wgptr(void * sel,_adapter * adapter)602 void dump_wgptr(void *sel, _adapter *adapter)
603 {
604 struct rtw_wds_gptr *wgptr;
605 int idx = 0;
606 char src[ETH_ALEN];
607 u32 age_ms;
608
609 RTW_PRINT_SEL(sel, "num:%d\n", ATOMIC_READ(&adapter->wds_gpt_record_num));
610 RTW_PRINT_SEL(sel, "%-17s %-6s\n"
611 , "src", "age"
612 );
613
614 do {
615 rtw_rcu_read_lock();
616
617 wgptr = rtw_wds_gptr_lookup_by_idx(adapter, idx);
618 if (wgptr) {
619 _rtw_memcpy(src, wgptr->src, ETH_ALEN);
620 age_ms = rtw_get_passing_time_ms(wgptr->last_update);
621 }
622
623 rtw_rcu_read_unlock();
624
625 if (wgptr) {
626 RTW_PRINT_SEL(sel, MAC_FMT" %6u\n"
627 , MAC_ARG(src)
628 , age_ms < 999999 ? age_ms : 999999
629 );
630 }
631
632 idx++;
633 } while (wgptr);
634 }
635
rtw_wds_gptr_new(_adapter * adapter,const u8 * src)636 static struct rtw_wds_gptr *rtw_wds_gptr_new(_adapter *adapter, const u8 *src)
637 {
638 struct rtw_wds_gptr *new_wgptr;
639
640 new_wgptr = rtw_zmalloc(sizeof(struct rtw_wds_gptr));
641 if (!new_wgptr)
642 return NULL;
643
644 new_wgptr->adapter = adapter;
645 _rtw_memcpy(new_wgptr->src, src, ETH_ALEN);
646 new_wgptr->last_update = rtw_get_current_time();
647
648 return new_wgptr;
649 }
650
rtw_wds_gptr_add(_adapter * adapter,const u8 * src)651 static struct rtw_wds_gptr *rtw_wds_gptr_add(_adapter *adapter, const u8 *src)
652 {
653 struct rtw_wds_gptr_table *tbl = adapter->wds_gpt_records;
654 struct rtw_wds_gptr *wgptr, *new_wgptr;
655 int ret;
656
657 if (!tbl)
658 return ERR_PTR(-ENOTSUPP);
659
660 if (ATOMIC_INC_UNLESS(&adapter->wds_gpt_record_num, RTW_WDS_MAX_PATHS) == 0)
661 return ERR_PTR(-ENOSPC);
662
663 new_wgptr = rtw_wds_gptr_new(adapter, src);
664 if (!new_wgptr)
665 return ERR_PTR(-ENOMEM);
666
667 do {
668 ret = rtw_rhashtable_lookup_insert_fast(&tbl->rhead,
669 &new_wgptr->rhash,
670 rtw_wds_gptr_rht_params);
671
672 if (ret == -EEXIST)
673 wgptr = rtw_rhashtable_lookup_fast(&tbl->rhead,
674 src,
675 rtw_wds_gptr_rht_params);
676
677 } while (unlikely(ret == -EEXIST && !wgptr));
678
679 if (ret && ret != -EEXIST)
680 return ERR_PTR(ret);
681
682 /* At this point either new_wgptr was added, or we found a
683 * matching entry already in the table; in the latter case
684 * free the unnecessary new entry.
685 */
686 if (ret == -EEXIST) {
687 rtw_mfree(new_wgptr, sizeof(struct rtw_wds_gptr));
688 new_wgptr = wgptr;
689 }
690
691 return new_wgptr;
692 }
693
rtw_rx_wds_gptr_check(_adapter * adapter,const u8 * src)694 bool rtw_rx_wds_gptr_check(_adapter *adapter, const u8 *src)
695 {
696 struct rtw_wds_gptr *wgptr;
697 bool ret = 0;
698
699 rtw_rcu_read_lock();
700
701 wgptr = rtw_wds_gptr_lookup(adapter, src);
702 if (wgptr)
703 ret = rtw_time_after(wgptr->last_update + RTW_WDS_GPTR_EXPIRE, rtw_get_current_time());
704
705 rtw_rcu_read_unlock();
706
707 return ret;
708 }
709
rtw_tx_wds_gptr_update(_adapter * adapter,const u8 * src)710 void rtw_tx_wds_gptr_update(_adapter *adapter, const u8 *src)
711 {
712 struct rtw_wds_gptr *wgptr;
713
714 rtw_rcu_read_lock();
715 wgptr = rtw_wds_gptr_lookup(adapter, src);
716 if (!wgptr)
717 rtw_wds_gptr_add(adapter, src);
718 else
719 wgptr->last_update = rtw_get_current_time();
720 rtw_rcu_read_unlock();
721 }
722
__rtw_wds_gptr_del(struct rtw_wds_gptr_table * tbl,struct rtw_wds_gptr * wgptr)723 static void __rtw_wds_gptr_del(struct rtw_wds_gptr_table *tbl, struct rtw_wds_gptr *wgptr)
724 {
725 rtw_rhashtable_remove_fast(&tbl->rhead, &wgptr->rhash, rtw_wds_gptr_rht_params);
726 rtw_wds_gptr_free_rcu(tbl, wgptr);
727 }
728
rtw_wds_gptr_expire(_adapter * adapter)729 void rtw_wds_gptr_expire(_adapter *adapter)
730 {
731 struct rtw_wds_gptr_table *tbl = adapter->wds_gpt_records;
732 struct rtw_wds_gptr *wgptr;
733 rtw_rhashtable_iter iter;
734 int ret;
735
736 if (!tbl)
737 return;
738
739 ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
740 if (ret)
741 return;
742
743 ret = rtw_rhashtable_walk_start(&iter);
744 if (ret && ret != -EAGAIN)
745 goto out;
746
747 while ((wgptr = rtw_rhashtable_walk_next(&iter))) {
748 if (IS_ERR(wgptr) && PTR_ERR(wgptr) == -EAGAIN)
749 continue;
750 if (IS_ERR(wgptr))
751 break;
752 if (rtw_time_after(rtw_get_current_time(), wgptr->last_update + RTW_WDS_GPTR_EXPIRE))
753 __rtw_wds_gptr_del(tbl, wgptr);
754 }
755
756 out:
757 rtw_rhashtable_walk_stop(&iter);
758 rtw_rhashtable_walk_exit(&iter);
759 }
760
rtw_wds_gptr_tbl_init(_adapter * adapter)761 int rtw_wds_gptr_tbl_init(_adapter *adapter)
762 {
763 struct rtw_wds_gptr_table *tbl;
764 int ret;
765
766 tbl = rtw_wds_gptr_table_alloc();
767 if (!tbl)
768 return -ENOMEM;
769
770 rtw_rhashtable_init(&tbl->rhead, &rtw_wds_gptr_rht_params);
771
772 ATOMIC_SET(&adapter->wds_gpt_record_num, 0);
773 adapter->wds_gpt_records = tbl;
774
775 return 0;
776 }
777
rtw_wds_gptr_tbl_unregister(_adapter * adapter)778 void rtw_wds_gptr_tbl_unregister(_adapter *adapter)
779 {
780 if (adapter->wds_gpt_records) {
781 rtw_wds_gptr_table_free(adapter->wds_gpt_records);
782 adapter->wds_gpt_records = NULL;
783 }
784 }
785 #endif /* defined(CONFIG_RTW_WDS) */
786
787