• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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