1/** HND GMAC Forwarder Implementation: LAN(GMAC) <--FWD--> WLAN
2 * Include WOFA dictionary with 3 stage lookup.
3 *
4 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * $Id$
18 *
19 * vim: set ts=4 noet sw=4 tw=80:
20 * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
21 *
22 */
23
24#if defined(BCM_GMAC3)
25
26#include <linux/if.h>
27#include <linux/skbuff.h>
28#include <linux/netdevice.h>
29#include <linux/interrupt.h>
30#include <osl.h>
31#include <hndfwd.h>
32#include <bcmutils.h>
33#include <proto/vlan.h>
34#include <proto/ethernet.h>
35
36/* forward declaration */
37struct fwder_if;
38struct fwder_cpumap;
39
40#define FWDER_WOFA_NULL         ((fwder_wofa_t *)NULL)
41#define FWDER_NET_DEVICE_NULL   ((struct net_device *)NULL)
42#define FWDER_BYPASS_FN_NULL    ((fwder_bypass_fn_t)NULL)
43
44#define FWDER_ALIGN16(sym)      FWDER_ASSERT((((uintptr)sym) & 1) == 0)
45
46/* Safe fetch of a string member given a structure pointer. */
47#define __SSTR(_struct_ptr, _member) \
48	(((_struct_ptr) != NULL) ? (_struct_ptr)->_member : "null")
49
50/* Formatted Etherned Mac Address display */
51#define __EFMT                      "%02X:%02X:%02X:%02X:%02X:%02X "
52#define __EVAL(e)                   *((e) + 0), *((e) + 1), *((e) + 2), \
53	                                *((e) + 3), *((e) + 4), *((e) + 5)
54
55/** WOFA Dictionary manipulation helper static function declarations. */
56/** WOFA Dictionary symbol manipulation. */
57static inline uint16 __wofa_sym48_hash16(const uint16 * key);
58static inline uint16 __wofa_sym48_cmp16(const uint16 * key, const uint16 * sym);
59static inline void   __wofa_sym48_set16(const uint16 * key, uint16 * sym);
60/** WOFA Dictionary bloom filter manipulation. */
61static inline uint32 __wofa_bloom_lkup16(const uint32 * bfilter, const uint16 hash16);
62static inline void   __wofa_bloom_set16(uint32 * bfilter, const uint16 hash16);
63static inline void   __wofa_bloom_clr16(uint32 * bfilter, const uint16 hash16);
64
65/** WOFA based Forwarding static function declarations. */
66/** WOFA Accessors. */
67static inline uint32 __fwder_wofa_syms(struct fwder_wofa * fwder_wofa);
68#if defined(FWDER_STATS)
69static inline uint32 __fwder_wofa_hits(struct fwder_wofa * fwder_wofa);
70static inline uint32 __fwder_wofa_miss(struct fwder_wofa * fwder_wofa);
71#endif /* FWDER_STATS */
72/** WOFA Constructor and Destructor. */
73static struct fwder_wofa * _fwder_wofa_init(const int funit);
74static void          _fwder_wofa_fini(struct fwder_wofa * fwder_wofa);
75/** WOFA Address Resolution Logic. */
76static inline int    __fwder_wofa_add(struct fwder_wofa * fwder_wofa,
77                                      uint16 * symbol, wofa_t wofa);
78static inline int    __fwder_wofa_del(struct fwder_wofa * fwder_wofa,
79                                      uint16 * symbol, wofa_t wofa);
80static inline int    __fwder_wofa_clr(struct fwder_wofa * fwder_wofa,
81                                      wofa_t wofa);
82static inline uintptr_t __fwder_wofa_lkup(struct fwder_wofa * fwder_wofa,
83                                          uint16 * symbol, const int port);
84/** WOFA Debug dump and audit. */
85static void          _fwder_wofa_dump(struct bcmstrbuf *b,
86                                      struct fwder_wofa * fwder_wofa);
87
88/** Forwarder static function delcarations. */
89/** Forwarder accessor string formating. */
90static inline const char * __fwder_dir(const int dir);
91static inline const char * __fwder_mode(const int mode);
92static inline const char * __fwder_chan(const int chan);
93/** Forwarder default bypass handler. */
94static int _fwder_bypass_fn(fwder_t * fwder, struct sk_buff * skbs, int skb_cnt,
95                            struct net_device * rx_dev);
96/** Forwarder cpumap configuration. */
97static int _fwder_cpumap_config(int radio, struct fwder_cpumap *map);
98static int _fwder_cpumap_parse(const char *fwder_cpumap_nvar_str);
99/** Forwarder instance accessor. */
100static inline fwder_t * ___fwder_self(const fwder_dir_t dir, int funit);
101static inline fwder_t * __fwder_self(const fwder_dir_t dir, int funit);
102static inline void      __fwder_sync_devs_cnt(fwder_t * fwder_dn);
103static inline struct fwder_if * __fwder_if(int unit, int subunit);
104
105/** Forwarder Debug and audit. */
106static void _fwder_dump(struct bcmstrbuf *b, const fwder_t * fwder);
107static void _fwder_devs_dump(struct bcmstrbuf *b, dll_t * fwder_if_dll);
108
109
110/** WOFA Dictionary sizing. */
111#define WOFA_DICT_SYMBOL_SIZE   (6) /* Size of a Mac Address */
112#define WOFA_DICT_BKT_MAX       (1 << NBBY)
113#define WOFA_DICT_BIN_MAX       (4) /* collision list */
114#define WOFA_DICT_SYMBOLS_MAX   (WOFA_DICT_BKT_MAX * WOFA_DICT_BIN_MAX)
115
116#if (WOFA_DICT_SYMBOL_SIZE != 6)
117#error "Validated for 6Byte symbols"
118#endif
119
120#if (WOFA_DICT_BKT_MAX != 256)
121#error "Dictionary hash table size uses a uint8 index"
122#endif
123
124/** WOFA Bloom Filter sizing. */
125#define WOFA_BLOOMFILTER_BITS   (64 * 1024) /* 16bit hash index */
126#define WOFA_BLOOMFILTER_WORDS  (WOFA_BLOOMFILTER_BITS / 32)
127
128/** WOFA Cached recent hit per LAN port|WLAN Interface. */
129#define WOFA_MAX_PORTS          (FWDER_MAX_IF + 1)
130
131/** WOFA Symbol: key, 32bit associated metadata, and runtime stats */
132typedef struct wofa_sym {           /* 16Byte symbol + metadata */
133	uint16         hash16;
134	union {                         /* dictionary symbol ethernet MAC address */
135		uint8       u8[WOFA_DICT_SYMBOL_SIZE];
136		uint16     u16[WOFA_DICT_SYMBOL_SIZE / sizeof(uint16)];
137	} key;
138	wofa_t         wofa;            /* 32bit meta data */
139#if defined(FWDER_STATS)
140	uint32         hits;            /* Per symbol hit statistics */
141#endif
142} wofa_sym_t;
143
144/** WOFA Fast lookup of cached last hit hash16. */
145typedef struct wofa_cached {
146	uint16         hash16;          /* Cached recent hit ix */
147	wofa_sym_t     * sym;           /* Cache recent symbol */
148} wofa_cached_t;
149
150/* WOFA Hash table bin. */
151typedef struct wofa_bin {
152	wofa_sym_t     bin[WOFA_DICT_BIN_MAX];
153} wofa_bin_t;
154
155/* WOFA Hash table bkt of bins. */
156typedef struct wofa_bkt {
157	wofa_bin_t     bkt[WOFA_DICT_BKT_MAX];
158} wofa_bkt_t;
159
160/** WOFA Dictionary of MAC Address symbols for lookup based forwarding.
161 * WOFA Dictionary constituted of a cached, bloom filter and hash table lkup.
162 */
163typedef struct wofa_dict {
164	wofa_cached_t  cached[WOFA_MAX_PORTS];
165	uint32         bloomfilter[WOFA_BLOOMFILTER_WORDS];
166	wofa_bkt_t     table;           /* Hash table: buckets and bins */
167} wofa_dict_t;
168
169/** Forwarder instance of a WOFA lkup system. */
170typedef struct fwder_wofa {
171	wofa_dict_t    dict;            /* Dictionary of 6B symbols and metadata */
172	int            syms;            /* Number of symbols in dictionary */
173#if defined(FWDER_STATS)
174	uint32         hits;            /* Lookup successful statistics counter */
175	uint32         miss;            /* Lookup failure statistics counter */
176#endif /* FWDER_STATS */
177} fwder_wofa_t;
178
179static fwder_wofa_t * _fwder_wofa[FWDER_MAX_UNIT] =
180{ FWDER_WOFA_NULL, FWDER_WOFA_NULL };
181
182/** Hash table and symbol manipulation helper routines. */
183
184/** Compute 16bit hash, on a 16bit aligned 6Byte key. */
185static inline uint16
186__wofa_sym48_hash16(const uint16 * key)
187{
188	uint16 hash16;
189	hash16 = (*(key + 0)) ^ (*(key + 1)) ^ (*(key + 2));
190	return hash16;
191}
192
193/** Compare two 6Byte values that are 2Byte aligned. */
194static inline uint16
195__wofa_sym48_cmp16(const uint16 * key, const uint16 * sym)
196{
197	/* returns 0 if equal */
198	return ((*(key + 0) ^ *(sym + 0)) |
199	        (*(key + 1) ^ *(sym + 1)) |
200	        (*(key + 2) ^ *(sym + 2)));
201}
202
203/** Set a 6Byte 16bit aligned symbol from a given 6B key. */
204static inline void
205__wofa_sym48_set16(const uint16 * key, uint16 * sym)
206{
207	*(sym + 0) = *(key + 0);
208	*(sym + 1) = *(key + 1);
209	*(sym + 2) = *(key + 2);
210}
211
212/** Single hash bloom filter manipulation helper routines. */
213
214/* Lkup a bloom filter for a 16bit hash_key */
215static inline uint32
216__wofa_bloom_lkup16(const uint32 * bloom_filter, const uint16 hash16)
217{
218	return ((*(bloom_filter + (hash16 >> 5))) & (1U << (hash16 & 31)));
219}
220
221static inline void
222__wofa_bloom_set16(uint32 * bloom_filter, const uint16 hash16)
223{
224	*(bloom_filter + (hash16 >> 5)) |= (1U << (hash16 & 31));
225}
226
227static inline void
228__wofa_bloom_clr16(uint32 * bloom_filter, const uint16 hash16)
229{
230	*(bloom_filter + (hash16 >> 5)) &= ~(1U << (hash16 & 31));
231}
232
233#define DECLARE_WOFA_FN(FIELD)                                                 \
234static inline uint32 __fwder_wofa_ ## FIELD(fwder_wofa_t * fwder_wofa)         \
235{ return fwder_wofa->FIELD; }
236DECLARE_WOFA_FN(syms) /* __fwder_wofa_syms(): Return num active symbols */
237#if defined(FWDER_STATS)
238DECLARE_WOFA_FN(hits) /* __fwder_wofa_hits() Return num of successful lkups */
239DECLARE_WOFA_FN(miss) /* __fwder_wofa_miss() Return num of unsuccessful lkups */
240#endif /* FWDER_STATS */
241
242/** Allocate and reset storage for dictionary and bloom filter. */
243static fwder_wofa_t * __init
244_fwder_wofa_init(const int funit)
245{
246	int bin, bkt, port;
247	gfp_t flags;
248	wofa_dict_t * dict;
249	fwder_wofa_t * fwder_wofa;
250	const int fwder_wofa_sz = sizeof(fwder_wofa_t);
251
252	FWDER_ASSERT(funit < FWDER_MAX_UNIT);
253	FWDER_ASSERT(_fwder_wofa[funit] == FWDER_WOFA_NULL);
254
255	flags = CAN_SLEEP() ? GFP_KERNEL : GFP_ATOMIC;
256	fwder_wofa = (fwder_wofa_t *)kmalloc(fwder_wofa_sz, flags);
257
258	if (fwder_wofa == FWDER_WOFA_NULL) {
259		FWDER_WARN(("%s Failed allocate wofa size<%u>\n",
260		            __FUNCTION__, fwder_wofa_sz));
261		ASSERT(fwder_wofa != FWDER_WOFA_NULL);
262		return FWDER_WOFA_NULL;
263	}
264
265	bzero(fwder_wofa, fwder_wofa_sz); /* Initializes bloomfilter state */
266	dict = &fwder_wofa->dict;
267
268	/* Initialize cache lkup state */
269	for (port = 0; port < WOFA_MAX_PORTS; port++) {
270		dict->cached[port].hash16 = 0;
271		dict->cached[port].sym = &dict->table.bkt[0].bin[0];
272	}
273
274	/* Initialize hash lkup state */
275	for (bkt = 0; bkt < WOFA_DICT_BKT_MAX; bkt++) {
276		for (bin = 0; bin < WOFA_DICT_BIN_MAX; bin++) {
277			dict->table.bkt[bkt].bin[bin].wofa = FWDER_WOFA_INVALID;
278		}
279	}
280
281	FWDER_STATS_CLR(fwder_wofa->hits);
282	FWDER_STATS_CLR(fwder_wofa->miss);
283
284	FWDER_TRACE(("Fwder WOFA[%d]:<%p> size<%uKB:%uB>\n",
285	             funit, fwder_wofa, fwder_wofa_sz/1024, fwder_wofa_sz));
286
287	_fwder_wofa[funit] = fwder_wofa;
288
289	return fwder_wofa;
290}
291
292/** Free storage for dictionary and bloom filter. */
293static void
294_fwder_wofa_fini(fwder_wofa_t * fwder_wofa)
295{
296	if (fwder_wofa == FWDER_WOFA_NULL)
297		return;
298
299	kfree(fwder_wofa);
300}
301
302/** Add a symbol to WOFA dictionary: hash table and bloom-filter. */
303static inline int
304__fwder_wofa_add(fwder_wofa_t * fwder_wofa, uint16 * symbol, wofa_t wofa)
305{
306	uint16 hash16;
307	wofa_dict_t * dict;
308
309	FWDER_ASSERT(fwder_wofa != FWDER_WOFA_NULL);
310	FWDER_ASSERT(symbol != NULL);
311	FWDER_ASSERT(wofa != FWDER_WOFA_INVALID);
312
313	dict = &fwder_wofa->dict;
314	hash16 = __wofa_sym48_hash16(symbol);
315
316	{   /* Search the exact match hash table */
317		int added = 0;
318		uint8 hash8;      /* CAUTION: 8bit hash index for 258 buckets */
319		wofa_sym_t * sym; /* walk bins (collision list) in a bucket */
320		wofa_sym_t * end; /* last bin in the bucket collision list */
321
322		/* Fold the 16bit hash into a 8bit hash table index */
323		hash8 = ((uint8)(hash16) ^ (uint8)(hash16 >> 8));
324
325		/* Fetch the first bin in the hashed bucket */
326		sym = &dict->table.bkt[hash8].bin[0];
327		end = sym + WOFA_DICT_BIN_MAX; /* after last bin in collision list */
328
329		while ((uintptr)sym < (uintptr)end) {
330
331			/* Check for duplicates */
332			if (sym->wofa != FWDER_WOFA_INVALID) { /* valid entry */
333
334				if (__wofa_sym48_cmp16(symbol, sym->key.u16) == 0) {
335					/* Found a previous entry */
336					if (added == 0) { /* overwrite duplicate */
337						FWDER_ASSERT(sym->hash16 == hash16);
338						sym->wofa = wofa;
339						FWDER_STATS_CLR(sym->hits);
340						added = 1;
341					} else {  /* duplicate? */
342						FWDER_WARN(("Found several duplicate symbol"));
343						sym->wofa = FWDER_WOFA_INVALID;
344						sym->hash16 = 0; /* 0 may be a valid value */
345						fwder_wofa->syms--;
346					}
347				}
348
349			} else if (added == 0) { /* found an empty bin, insert here */
350
351				__wofa_bloom_set16(dict->bloomfilter, hash16);
352
353				__wofa_sym48_set16(symbol, sym->key.u16);
354
355				sym->hash16 = hash16;
356				sym->wofa = wofa;
357				FWDER_STATS_CLR(sym->hits);
358				FWDER_TRACE(("WOFA add " __EFMT "wofa<0x%08x>\n",
359				             __EVAL((uint8*)symbol), (uint)wofa));
360				fwder_wofa->syms++;
361				added = 1; /* used to clear duplicates */
362			}
363
364			sym++; /* next bin; for duplicates/overwrite */
365		}
366
367		if (added == 0) {
368			FWDER_WARN(("WOFA add " __EFMT "wofa<0x%08x> : bkt<%u> overflow\n",
369			            __EVAL((uint8*)symbol), (uint)wofa, hash8));
370		}
371	}
372
373	return BCME_OK;
374}
375
376/** Delete a symbol from the WOFA dictionary and clear the bloom filter if no
377 * other elements have a matching hash.
378 */
379static inline int
380__fwder_wofa_del(fwder_wofa_t * fwder_wofa, uint16 * symbol, wofa_t wofa)
381{
382	uint16 hash16;
383	wofa_dict_t * dict;
384
385	FWDER_ASSERT(fwder_wofa != FWDER_WOFA_NULL);
386	FWDER_ASSERT(symbol != NULL);
387	FWDER_ASSERT(wofa != FWDER_WOFA_INVALID);
388
389	dict = &fwder_wofa->dict;
390	hash16 = __wofa_sym48_hash16(symbol);
391
392	{   /* Clear cached entry */
393		wofa_cached_t * cached = &dict->cached[0];
394		wofa_cached_t * end    = cached + WOFA_MAX_PORTS;
395		while ((uintptr)cached < (uintptr)end) {
396			if (cached->hash16 == hash16) {
397				cached->hash16 = 0; /* 0 is a valid value */
398				cached->sym = &dict->table.bkt[0].bin[0];
399			}
400			cached++; /* next port's cached entry */
401		}
402	}
403
404	{   /* Clear matching bins for the found bucket, in hash table */
405		uint8 hash8;      /* CAUTION: 8bit hash index for 258 buckets */
406		wofa_sym_t * sym; /* walk bins (collision list) in a bucket */
407		wofa_sym_t * end; /* last bin in the bucket collision list */
408
409		/* Fold the 16bit hash into a 8bit hash table index */
410		hash8 = ((uint8)(hash16) ^ (uint8)(hash16 >> 8));
411
412		sym = &dict->table.bkt[hash8].bin[0];
413		end = sym + WOFA_DICT_BIN_MAX; /* after last bin in collision list */
414
415		while ((uintptr)sym < (uintptr)end) {
416			if ((sym->wofa != FWDER_WOFA_INVALID) &&
417			       (sym->hash16 == hash16)) {
418				if (__wofa_sym48_cmp16(symbol, sym->key.u16) == 0) {
419					if (sym->wofa != wofa) {
420						FWDER_WARN((
421						   "%s sym->wofa<0x%08x> != wofa<0x%08x>\n",
422						   __FUNCTION__, (uint)sym->wofa, (uint)wofa));
423					}
424					sym->wofa = FWDER_WOFA_INVALID;
425					sym->hash16 = 0; /* 0 is a valid value */
426					fwder_wofa->syms--;
427				}
428			}
429			sym++; /* next bin in bkt collision list */
430		}
431	}
432
433	/*
434	 * If hash8 was computed from lower 8bits of hash16, then do not need to
435	 * walk entire table.
436	 */
437	{   /* Update bloom_filter */
438		int bkt, bin;
439		int found_hash16 = 0;
440
441		for (bkt = 0; bkt < WOFA_DICT_BKT_MAX; bkt++) {
442			for (bin = 0; bin < WOFA_DICT_BIN_MAX; bin++) {
443				const wofa_sym_t * sym = &dict->table.bkt[bkt].bin[bin];
444				if ((sym->wofa != FWDER_WOFA_INVALID) &&
445				        (sym->hash16 == hash16)) {
446					found_hash16 = 1;
447					break;
448				}
449			}
450		}
451
452		if (found_hash16 == 0) {
453			__wofa_bloom_clr16(dict->bloomfilter, hash16);
454		}
455	}
456
457	return BCME_OK;
458}
459
460
461/** Delete all symbols in the WOFA dictionary that match the wofa metadata and
462 * clear the bloom filter.
463 */
464static inline int
465__fwder_wofa_clr(struct fwder_wofa * fwder_wofa, wofa_t wofa)
466{
467	wofa_dict_t * dict;
468
469	FWDER_ASSERT(fwder_wofa != FWDER_WOFA_NULL);
470	FWDER_ASSERT(wofa != FWDER_WOFA_INVALID);
471
472	dict = &fwder_wofa->dict;
473
474	{   /* Reset cache entries */
475		int port;
476		for (port = 0; port < WOFA_MAX_PORTS; port++) {
477			dict->cached[port].hash16 = 0;
478			dict->cached[port].sym = &dict->table.bkt[0].bin[0];
479		}
480	}
481
482	/* Reset bloom filter */
483	bzero(dict->bloomfilter, sizeof(uint32) * WOFA_BLOOMFILTER_WORDS);
484
485	{	/* Rebuild bloom filter after flushing symbols. */
486		uint16 hash16;
487		int bkt, bin;
488		for (bkt = 0; bkt < WOFA_DICT_BKT_MAX; bkt++) {
489			for (bin = 0; bin < WOFA_DICT_BIN_MAX; bin++) {
490				if (dict->table.bkt[bkt].bin[bin].wofa == wofa) {
491					hash16 = dict->table.bkt[bkt].bin[bin].hash16;
492					__wofa_bloom_set16(dict->bloomfilter, hash16);
493				}
494			}
495		}
496	}
497
498	return FWDER_SUCCESS;
499}
500
501/** Lookup a symbol in the WOFA dictionary returning the wofa metadata.
502 * 3 Step lookup is performed. First the last hit cached entry is tested.
503 */
504static inline wofa_t
505__fwder_wofa_lkup(struct fwder_wofa * fwder_wofa, uint16 * symbol,
506                  const int port)
507{
508	uint16 hash16;
509	wofa_sym_t * sym;
510	wofa_dict_t * dict;
511
512	FWDER_ASSERT(fwder_wofa != FWDER_WOFA_NULL);
513	FWDER_ASSERT(symbol != NULL);
514
515	dict = &fwder_wofa->dict;
516	hash16 = __wofa_sym48_hash16(symbol);
517
518	/* Lkup the cached hit entry first */
519	if (hash16 == dict->cached[port].hash16) {
520		sym = dict->cached[port].sym;
521		if (__wofa_sym48_cmp16(symbol, sym->key.u16) == 0) {
522			goto found_symbol;
523		}
524	}
525
526	/* Now Lkup bloom filter and quickly exit on failure */
527	if (__wofa_bloom_lkup16(dict->bloomfilter, hash16) == 0U) {
528		goto bloomfilter_lkup_failure;
529	}
530
531	/* Single hash bloom filter susceptible to false positives, exact match */
532
533	{   /* Now search the exact match hash table: testing all bins in bkt */
534		uint8 hash8;
535		wofa_sym_t * end; /* last bin in the bucket collision list */
536
537		/* CAUTION: 8bit hash index for 258 buckets */
538		/* Fold the 16bit hash into a 8bit hash table index */
539		hash8 = ((uint8)(hash16) ^ (uint8)(hash16 >> 8));
540
541		sym = &dict->table.bkt[hash8].bin[0];
542		end = sym + WOFA_DICT_BIN_MAX; /* after last bin in collision list */
543
544		/* Walk the bins for the hashed bucket */
545		while ((uintptr)sym < (uintptr)end) {
546
547			if (sym->hash16 == hash16) {
548				/* Test exact match */
549				if (__wofa_sym48_cmp16(symbol, sym->key.u16) == 0) {
550
551					/* Cache the new hit index */
552					dict->cached[port].hash16 = hash16;
553					dict->cached[port].sym = sym;
554
555					goto found_symbol;
556				}
557			}
558
559			sym++; /* next bin in collision list */
560		}
561	}
562
563bloomfilter_lkup_failure:
564	FWDER_STATS_INCR(fwder_wofa->miss);
565
566	return FWDER_WOFA_INVALID;
567
568found_symbol:
569	FWDER_STATS_INCR(fwder_wofa->hits);
570	FWDER_STATS_INCR(sym->hits);
571
572	FWDER_ASSERT(sym->wofa != FWDER_WOFA_INVALID);
573
574	return sym->wofa; /* return associated wofa metadata */
575}
576
577/** Debug dump a forwaring unit's WOFA table. */
578static void
579_fwder_wofa_dump(struct bcmstrbuf *b, fwder_wofa_t * fwder_wofa)
580{
581	int dump, word, port, bkt, bin;
582	wofa_sym_t * sym;
583	wofa_dict_t * dict;
584
585	if (fwder_wofa == FWDER_WOFA_NULL)
586		return;
587
588	dict = &fwder_wofa->dict;
589
590	bcm_bprintf(b, "WOFA Symbols<%u>\n", __fwder_wofa_syms(fwder_wofa));
591
592#if defined(FWDER_STATS)
593	bcm_bprintf(b, "WOFA Statistics: hits<%u> miss<%u>\n",
594	            __fwder_wofa_hits(fwder_wofa), __fwder_wofa_miss(fwder_wofa));
595#endif /* FWDER_STATS */
596
597	bcm_bprintf(b, "WOFA Cached Table Dump:\n");
598	for (port = 0; port < WOFA_MAX_PORTS; port++) {
599		bcm_bprintf(b, "\tPort<%2d> : hash<0x%04x> sym<%p>\n",
600		            port, dict->cached[port].hash16, dict->cached[port].sym);
601	}
602
603	bcm_bprintf(b, "WOFA Bloom Filter Dump\n");
604	for (word = 0; word < WOFA_BLOOMFILTER_WORDS; word++) {
605		if (dict->bloomfilter[word] != 0) {
606			bcm_bprintf(b, "\tBF[%04u] = 0x%08x\n",
607			            word, dict->bloomfilter[word]);
608		}
609	}
610
611	dump = 0;
612
613	bcm_bprintf(b, "WOFA Hash Table Dump\n");
614	for (bkt = 0; bkt < WOFA_DICT_BKT_MAX; bkt++) {
615		for (bin = 0; bin < WOFA_DICT_BIN_MAX; bin++) {
616			sym = &dict->table.bkt[bkt].bin[bin];
617			if (sym->wofa != FWDER_WOFA_INVALID) {
618				bcm_bprintf(b, "\t" __EFMT "wofa<0x%08x> "
619#if defined(FWDER_STATS)
620				            "hits<%u> "
621#endif
622				            "BktBin<%03d:%d] sym<%p> hash<0x%04X:%05u>\n",
623				            __EVAL(sym->key.u8), (uint)sym->wofa,
624#if defined(FWDER_STATS)
625				            sym->hits,
626#endif
627				            bkt, bin, sym, sym->hash16, (int)sym->hash16);
628
629				if (++dump > 32) {
630					bcm_bprintf(b, "... too many to dump ...\n");
631					break;
632				}
633			}
634		}
635	}
636
637	bcm_bprintf(b, "\n\n");
638}
639
640/* fwder enum to string conversions for debug dump: fwder_dir(), fwder_mode() */
641static const char * _fwder_dir_g[FWDER_MAX_DIR] = { "UP", "DN" };
642static const char * _fwder_mode_g[FWDER_MAX_MODE] = { "NIC", "DGL" };
643static const char * _fwder_chan_g[FWDER_MAX_CHAN] = { "UPPER", "LOWER", "UNDEF" };
644
645#define DECLARE_FWDER_ENUM_STR(name, max)                                      \
646	static inline const char * __fwder_##name(const int name)                  \
647	{ return (name < max) ? _fwder_##name##_g[name] : "invalid"; }
648DECLARE_FWDER_ENUM_STR(dir, FWDER_MAX_DIR)   /* __fwder_dir(int) */
649DECLARE_FWDER_ENUM_STR(mode, FWDER_MAX_MODE) /* __fwder_mode(int) */
650DECLARE_FWDER_ENUM_STR(chan, FWDER_MAX_CHAN) /* __fwder_chan(int) */
651
652
653/** Default dummy xmit handler bound to the fwder. */
654static int
655_fwder_bypass_fn(fwder_t * fwder, struct sk_buff * skbs, int skb_cnt,
656                 struct net_device * rx_dev)
657{
658	ASSERT(fwder != FWDER_NULL);
659
660	FWDER_PTRACE(("%s fwder<%p:%s> error skbs<%p> skb_cnt<%d> rx_dev<%p:%s>\n",
661	              __FUNCTION__, fwder, __SSTR(fwder, name), skbs, skb_cnt,
662	              rx_dev, __SSTR(rx_dev, name)));
663
664	FWDER_STATS_ADD(fwder->dropped, skb_cnt);
665
666	return FWDER_FAILURE;
667}
668
669/** HND Forwarder Radio to CPU/Fwder unit mapping. */
670typedef struct fwder_cpumap {
671	fwder_mode_t mode;
672	fwder_chan_t chan;
673	int band, irq, cpu;
674	int unit; /* fwder unit assigned from FWDER_CPUMAP_NVAR nvram settings */
675} fwder_cpumap_t;
676
677#define _FWDER_CPUMAP_INI_(ix)                                                 \
678	{ .mode = FWDER_NIC_MODE, .chan = FWDER_UNDEF_CHAN,                        \
679	  .band = 0, .irq = 0, .cpu = -1, .unit = ix }
680
681static fwder_cpumap_t fwder_cpumap_g[FWDER_MAX_RADIO] = {
682	_FWDER_CPUMAP_INI_(0),
683	_FWDER_CPUMAP_INI_(1),
684	_FWDER_CPUMAP_INI_(2),
685	_FWDER_CPUMAP_INI_(3)
686};
687
688/** Assign a fwder_unit number: [0 .. FWDER_MAX_RADIO) */
689int
690fwder_assign(fwder_mode_t mode, int radio_unit)
691{
692	FWDER_TRACE(("%s mode<%s> radio_unit<%d>\n",
693	             __FUNCTION__, __fwder_mode(mode), radio_unit));
694	ASSERT(fwder_cpumap_g[radio_unit].mode == mode);
695	ASSERT(fwder_cpumap_g[radio_unit].cpu != -1);
696
697	return fwder_cpumap_g[radio_unit].unit;
698}
699
700/** Assign the IRQ affinity for a radio identified by its assigned fwder_unit */
701int
702fwder_affinity(fwder_dir_t dir, int fwder_unit, int irq)
703{
704	int cpu_core;
705
706	FWDER_TRACE(("%s dir<%s> fwder_unit<%d> irq<%d>\n",
707	             __FUNCTION__, __fwder_dir(dir), fwder_unit, irq));
708	FWDER_ASSERT((dir < FWDER_MAX_DIR));
709	FWDER_ASSERT((fwder_unit < FWDER_MAX_RADIO));
710
711	if (dir == FWDER_UPSTREAM) { /* Radio Interface */
712		int radio_unit;
713		fwder_cpumap_t * cpumap = &fwder_cpumap_g[0];
714
715		for (radio_unit = 0; radio_unit < FWDER_MAX_RADIO; radio_unit++) {
716			cpumap = &fwder_cpumap_g[radio_unit];
717			if (cpumap->unit == fwder_unit)
718				break;
719		}
720
721		FWDER_ASSERT((cpumap->unit == fwder_unit) && (cpumap->irq == irq));
722
723		cpu_core = cpumap->cpu;
724	} else { /* GMAC forwarder */
725		cpu_core = fwder_unit;
726	}
727
728	FWDER_ASSERT((cpu_core < NR_CPUS));
729
730	FWDER_TRACE(("%s irq_set_affinity irq<%d> cpu_core<%d>\n",
731	             __FUNCTION__, irq, cpu_core));
732
733	/* Setup IRQ affinity */
734	irq_set_affinity(irq, cpumask_of(cpu_core));
735
736	return FWDER_SUCCESS;
737}
738
739/** Assign a fwder_unit number given a CPU map configurtion.
740 * When multiple radios share the same IRQ, they will be assigned to the same
741 * CPU by allocating a fwder_unit number to allow modulo-2 CPU mapping.
742 * The radio unit (probe sequence) and its configuration is used to allocate a
743 * fwder_unit : [0 .. FWDER_MAX_RADIO)
744 */
745static int
746_fwder_cpumap_config(int radio_unit, struct fwder_cpumap *map)
747{
748	int radio, fwder_unit;
749	fwder_cpumap_t * cpumap;
750
751	FWDER_TRACE(("%s %d. mode<%s> chan<%s> band<%d> irq<%d> cpu<%d>",
752	             __FUNCTION__, radio_unit, __fwder_mode(map->mode),
753	             __fwder_chan(map->chan), map->band, map->irq, map->cpu));
754	FWDER_ASSERT((radio_unit < FWDER_MAX_RADIO));
755
756	cpumap = &fwder_cpumap_g[radio_unit]; /* radio to be configured */
757	FWDER_ASSERT((cpumap->cpu == -1));
758
759	/* First radio sharing the IRQ will use the cpu core as fwder_unit. */
760	fwder_unit = map->cpu;
761
762	/* Traverse previously assigned cpumap and fetch the fwder_unit */
763	for (radio = 0; radio < radio_unit; radio++) {
764		if (fwder_cpumap_g[radio].irq == map->irq) {
765			if (fwder_cpumap_g[radio].cpu != map->cpu) {
766				FWDER_ERROR(("ERROR %s: unit:irq:cpu<%d:%d:%d> "
767				           "mismatch <%d:%d:%d>\n", __FUNCTION__, radio,
768				           fwder_cpumap_g[radio].irq, fwder_cpumap_g[radio].cpu,
769				           radio_unit, map->irq, map->cpu));
770				return FWDER_FAILURE;
771			}
772
773			/* Subsequent radios sharing IRQ will use first previous radio's
774			 * fwder_unit + max forwarder(s) allowing a module-FWDER_MAX_UNIT
775			 */
776			fwder_unit = fwder_cpumap_g[radio].unit + FWDER_MAX_UNIT;
777		}
778	}
779
780	cpumap->unit = fwder_unit; /* allocate fwder_unit for this radio. */
781	cpumap->mode = map->mode;
782	cpumap->chan = map->chan;
783	cpumap->band = map->band;
784	cpumap->irq  = map->irq;
785	cpumap->cpu  = map->cpu;
786
787	FWDER_TRACE((" fwder_unit<%d>\n", cpumap->unit));
788
789	return cpumap->unit;
790}
791
792/** Parse the FWDER_CPUMAP_NVAR nvram variable and apply configuration. */
793static int
794_fwder_cpumap_parse(const char *fwder_cpumap_nvar_str)
795{
796	int radio_unit = 0;
797	char parse_nvar_str[128]; /* local copy for strtok parsing */
798	char *cpumap_all_str = parse_nvar_str; /* cpumap str for all radios */
799	char *cpumap_per_str; /* cpumap str per radio */
800
801	if (fwder_cpumap_nvar_str == NULL)
802		return FWDER_FAILURE;
803
804	FWDER_TRACE(("%s = [%s]\n", FWDER_CPUMAP_NVAR, fwder_cpumap_nvar_str));
805
806	/* Make a local copy */
807	strncpy(parse_nvar_str, fwder_cpumap_nvar_str, sizeof(parse_nvar_str));
808	parse_nvar_str[sizeof(parse_nvar_str)-1] = '\0';
809
810	/* Fetch each radio's cpumap substr */
811	while ((cpumap_per_str = bcmstrtok((char **)&cpumap_all_str, " ", NULL))
812		    != NULL) { /* per radio cpumap substr parsing */
813		fwder_cpumap_t cpumap;
814		char mode, chan;
815
816		if (sscanf(cpumap_per_str, "%c:%c:%d:%d:%d", &mode, &chan,
817		                       &cpumap.band, &cpumap.irq, &cpumap.cpu) != 5) {
818			FWDER_ERROR(("ERROR %s: parsing radio cpumap %s\n",
819			             __FUNCTION__, cpumap_per_str));
820			return FWDER_FAILURE;
821		}
822		FWDER_ASSERT((cpumap.cpu < NR_CPUS));
823
824		cpumap.mode = (mode == 'd') ? FWDER_DNG_MODE : FWDER_NIC_MODE;
825		if (chan == 'u')
826			cpumap.chan = FWDER_UPPER_CHAN;
827		else if (chan == 'l')
828			cpumap.chan = FWDER_LOWER_CHAN;
829		else
830			cpumap.chan = FWDER_UNDEF_CHAN;
831
832		if (_fwder_cpumap_config(radio_unit, &cpumap) == FWDER_FAILURE) {
833			FWDER_ERROR(("ERROR %s: configuring radio cpumap\n", __FUNCTION__));
834			return FWDER_FAILURE;
835		}
836
837		radio_unit++; /* next radio unit */
838	}
839
840	return FWDER_SUCCESS;
841}
842
843
844/** HND Forwarder runtime state:
845 * Two (one per radio) sets of <upstream,dnstream> forwarder's are maintained.
846 */
847#if defined(CONFIG_SMP)
848
849#define _FWDER_INI_(NAME)                                                      \
850	{                                                                          \
851		.lock      = __SPIN_LOCK_UNLOCKED(.lock),                              \
852		.lflags    = 0UL,                                                      \
853		.mate      = FWDER_NULL,                                               \
854		.bypass_fn = _fwder_bypass_fn,                                         \
855		.dev_def   = FWDER_NET_DEVICE_NULL,                                    \
856		.devs_cnt  = 0,                                                        \
857		.devs_dll  = { .next_p = NULL, .prev_p = NULL },                       \
858		.wofa      = FWDER_WOFA_NULL,                                          \
859		.mode      = FWDER_NIC_MODE,                                           \
860		.dataoff   = 0,                                                        \
861		.osh       = NULL,                                                     \
862		.name      = #NAME                                                     \
863	}
864
865/** Static declaration of fwder objects per cpu, accessed via per_cpu, */
866DEFINE_PER_CPU(struct fwder, fwder_upstream_g) = _FWDER_INI_(upstream);
867DEFINE_PER_CPU(struct fwder, fwder_dnstream_g) = _FWDER_INI_(dnstream);
868
869/** Fetch an upstream or dnstream forwarder instance, by CPU# as unit. */
870#define FWDER_GET(fwder, funit)	        &per_cpu((fwder), (funit))
871
872#else  /* ! CONFIG_SMP */
873
874#define _FWDER_INI_(NAME, FUNIT)                                               \
875	{                                                                          \
876		.lflags    = 0UL,                                                      \
877		.mate      = FWDER_NULL,                                               \
878		.bypass_fn = _fwder_bypass_fn,                                         \
879		.dev_def   = FWDER_NET_DEVICE_NULL,                                    \
880		.devs_cnt  = 0,                                                        \
881		.devs_dll  = { .next_p = NULL, .prev_p = NULL },                       \
882		.wofa      = FWDER_WOFA_NULL,                                          \
883		.mode      = FWDER_NIC_MODE,                                           \
884		.dataoff   = 0,                                                        \
885		.osh       = (void*)NULL,                                              \
886		.unit      = FUNIT,                                                    \
887		.name      = #NAME                                                     \
888	}
889
890
891/** Static declaration of a set of upstream fwder indexed by GMAC/radio unit. */
892fwder_t fwder_upstream_g[FWDER_MAX_UNIT] = {
893	_FWDER_INI_(upstream, 0),
894	_FWDER_INI_(upstream, 1)
895};
896
897/** Static declaration of a set of dnstream fwder indexed by GMAC/radio unit. */
898fwder_t fwder_dnstream_g[FWDER_MAX_UNIT] = {
899	_FWDER_INI_(dnstream, 0),
900	_FWDER_INI_(dnstream, 1)
901};
902
903/** Fetch an upstream or dnstream forwarder instance, indexed by unit. */
904#define FWDER_GET(fwder, funit)         &fwder[funit]
905
906#endif /* ! CONFIG_SMP */
907
908
909/** Mapping a interface unit to a fwedr unit. */
910#define FWDER_UNIT(unit)                ((unit) % FWDER_MAX_UNIT)
911
912/** Used to instantiate a pool of virtual fwder if per fwder.
913 * When multiple WLAN interfaces bind to a fwder, a list of all actively bound
914 * WLAN interfaces is maintained in the upstream fwder. This list will be used
915 * to broadcast packets to all active WLAN interfaces (excluding the WLAN
916 * interface on which the broadcast packet was received).
917 */
918typedef struct fwder_if {
919	dll_t             node;         /* Double Linked List node */
920	struct net_device * dev;        /* Network device associated with wlif */
921} fwder_if_t;
922
923/** Free pool of fwder_if objects. */
924typedef struct fwder_if_pool {
925	fwder_if_t     pool[FWDER_MAX_RADIO][FWDER_MAX_IF];
926} fwder_if_pool_t;
927
928/** Static declaration of a global if pool: an array of fwder_if objects.
929 * Accessible from either CPU core (lock free).
930 */
931struct fwder_if_pool _fwder_if_pool_g;
932
933/** Fetch the forwarder object for a given a direction and unit. */
934static inline fwder_t *
935___fwder_self(const fwder_dir_t dir, int funit)
936{
937	fwder_t * fwder;
938
939	if (dir == (int)FWDER_UPSTREAM)
940		fwder = FWDER_GET(fwder_upstream_g, funit);
941	else
942		fwder = FWDER_GET(fwder_dnstream_g, funit);
943
944	/* fwder's unit may not be set yet. */
945	return fwder;
946}
947
948static inline fwder_t *
949__fwder_self(const fwder_dir_t dir, int funit)
950{
951	fwder_t * fwder = ___fwder_self(dir, funit);
952	FWDER_ASSERT(fwder->unit == funit);
953	return fwder;
954}
955
956/** Update number of devices in upstream forwarder. */
957static inline void
958__fwder_sync_devs_cnt(fwder_t * fwder_dn)
959{
960	fwder_t * fwder_up = fwder_dn->mate;
961	fwder_up->devs_cnt = fwder_dn->devs_cnt + 1; /* upstream has one fwdXX */
962}
963
964/** Fetch the fwder if object, give a unit and subunit. */
965static inline fwder_if_t *
966__fwder_if(int unit, int subunit)
967{
968	FWDER_ASSERT(unit < FWDER_MAX_RADIO);
969	FWDER_ASSERT(subunit < FWDER_MAX_IF);
970	return &_fwder_if_pool_g.pool[unit][subunit];
971}
972
973/** Instantiate and initialize forwarder, WOFA and free if pool. */
974int
975fwder_init(void)
976{
977	int dir, funit, unit, subunit;
978	fwder_if_t * fwder_if;
979	fwder_t * fwder;
980
981	if (_fwder_wofa[0] != FWDER_WOFA_NULL)
982		return FWDER_SUCCESS;
983
984	FWDER_TRACE(("%s: WOFA dictionary, fwder_if pool, fwder, cpumap\n",
985	             __FUNCTION__));
986
987	/* Instantiate WOFA dictionary. */
988	for (funit = 0; funit < FWDER_MAX_UNIT; funit++) {
989		_fwder_wofa[funit] = _fwder_wofa_init(funit);
990		if (_fwder_wofa[funit] == FWDER_WOFA_NULL)
991			return FWDER_FAILURE;
992	}
993
994	/* Initialize the free pool of fwder_if */
995	for (unit = 0; unit < FWDER_MAX_RADIO; unit++) {
996		for (subunit = 0; subunit < FWDER_MAX_IF; subunit++) {
997			fwder_if = __fwder_if(unit, subunit);
998			fwder_if->dev = FWDER_NET_DEVICE_NULL;
999		}
1000	}
1001
1002#if defined(CONFIG_SMP)
1003	FWDER_TRACE(("%s SMP Per CPU objects.\n", __FUNCTION__));
1004#else
1005	FWDER_TRACE(("%s global pair.\n", __FUNCTION__));
1006#endif
1007
1008#if defined(CONFIG_SMP)
1009	for_each_online_cpu(funit)
1010#else
1011	for (funit = 0; funit < FWDER_MAX_UNIT; funit++)
1012#endif /* ! CONFIG_SMP */
1013	{
1014		/* Initialize the fwder instances */
1015		for (dir = (int)FWDER_UPSTREAM; dir < (int)FWDER_MAX_DIR; dir++) {
1016
1017			fwder = ___fwder_self(dir, funit);
1018			fwder->mate = ___fwder_self((dir + 1) % FWDER_MAX_DIR, funit);
1019
1020			fwder->bypass_fn = _fwder_bypass_fn;
1021
1022			fwder->devs_cnt = 0;
1023			fwder->dev_def = FWDER_NET_DEVICE_NULL;
1024			dll_init(&fwder->devs_dll);
1025
1026			fwder->wofa = _fwder_wofa[funit];
1027
1028			FWDER_STATS_CLR(fwder->transmit);
1029			FWDER_STATS_CLR(fwder->dropped);
1030			FWDER_STATS_CLR(fwder->flooded);
1031
1032			fwder->mode = FWDER_NIC_MODE;
1033			fwder->dataoff = 0;
1034			fwder->unit = funit;
1035		}
1036
1037	} /* for_each_online_cpu | for funit = 0 .. FWDER_MAX_UNIT */
1038
1039	{   /* Initialize the radio to fwder_unit mapping from FWDER_CPUMAP_NVAR */
1040		const char * fwder_cpumap_nvar;
1041		fwder_cpumap_nvar = getvar(NULL, FWDER_CPUMAP_NVAR);
1042
1043		if (fwder_cpumap_nvar == NULL) {
1044			fwder_cpumap_nvar = FWDER_CPUMAP_DEFAULT; /* default cpumap */
1045			FWDER_ERROR(("ERROR %s: %s nvram not present, using default\n",
1046			             __FUNCTION__, FWDER_CPUMAP_NVAR));
1047		}
1048
1049		if (_fwder_cpumap_parse(fwder_cpumap_nvar) == FWDER_FAILURE) {
1050			FWDER_ERROR(("ERROR %s: failure parsing %s\n",
1051			             __FUNCTION__, fwder_cpumap_nvar));
1052			return FWDER_FAILURE;
1053		}
1054	}
1055	return FWDER_SUCCESS;
1056}
1057
1058/** Destruct forwarder. */
1059int
1060fwder_exit(void)
1061{
1062	int funit;
1063	fwder_t * fwder;
1064
1065#if defined(CONFIG_SMP)
1066	for_each_online_cpu(funit)
1067#else
1068	for (funit = 0; funit < FWDER_MAX_UNIT; funit++)
1069#endif /* ! CONFIG_SMP */
1070	{
1071		fwder = __fwder_self(FWDER_UPSTREAM, funit);
1072		_fwder_wofa_fini(fwder->wofa);
1073		fwder->mate->wofa = fwder->wofa = FWDER_WOFA_NULL;
1074		_fwder_wofa[funit] = FWDER_WOFA_NULL;
1075	}
1076
1077	return FWDER_SUCCESS;
1078}
1079
1080
1081/** Register a bypass handler and return the reverse dir forwarder object.
1082 * fwder_attach is called by the GMAC forwarder and the primary WLAN interface.
1083 *
1084 * The primary WLAN interface will attach a NULL dev.
1085 * WLAN interfaces, including the primary interface, needs to explicitly invoke
1086 * fwder_bind().
1087 */
1088fwder_t *
1089fwder_attach(fwder_dir_t dir, int unit, fwder_mode_t mode,
1090             fwder_bypass_fn_t bypass_fn, struct net_device * dev, void * osh)
1091{
1092	int funit;
1093	fwder_t * self;
1094	fwder_t * mate; /* Reverse direction forwarder, returned */
1095
1096	FWDER_TRACE(("%s: dir<%s> unit<%d> mode<%s> <%p,%pS> dev<%p:%s>\n",
1097	             __FUNCTION__, __fwder_dir(dir), unit, __fwder_mode(mode),
1098	             bypass_fn, bypass_fn, dev, __SSTR(dev, name)));
1099
1100	ASSERT((int)dir < (int)FWDER_MAX_DIR);
1101	ASSERT((int)mode < (int)FWDER_MAX_MODE);
1102	ASSERT(bypass_fn != FWDER_BYPASS_FN_NULL);
1103	if (dir == FWDER_DNSTREAM) {
1104		ASSERT(dev == FWDER_NET_DEVICE_NULL);
1105	} else {
1106		ASSERT(dev != FWDER_NET_DEVICE_NULL);
1107	}
1108
1109	/* Use the unit # and direction to fetch the fwder and mate's fwder */
1110	funit = FWDER_UNIT(unit);
1111	self = __fwder_self(dir, funit);
1112
1113	_FWDER_LOCK(self);                                     /* ++LOCK */
1114
1115	/* Configure self */
1116	self->mode = mode; /* in dnstream dir, mode will be used by fwd#0, fwd#1 */
1117	self->bypass_fn = bypass_fn;
1118	if (dir == FWDER_UPSTREAM) {
1119		self->devs_cnt = 1;
1120		self->dev_def = dev;
1121		FWDER_ASSERT(dev != FWDER_NET_DEVICE_NULL);
1122	}
1123	self->osh = osh;
1124
1125	/* Return the mate's fwder */
1126	mate = self->mate;
1127	FWDER_ASSERT(mate->unit == self->unit);
1128
1129	_FWDER_UNLOCK(self);                                   /* --LOCK */
1130
1131	return mate;
1132}
1133
1134/** Dettach an interface from the forwarder by dettaching the bypass handler.
1135 * fwder_dettach is called by the GMAC forwarder and the primary WLAN interface.
1136 */
1137fwder_t *
1138fwder_dettach(fwder_t * mate, fwder_dir_t dir, int unit)
1139{
1140	int funit;
1141	fwder_t * self;
1142
1143	FWDER_TRACE(("%s: mate<%p> dir<%s> unit<%d>\n",
1144	            __FUNCTION__, mate, __fwder_dir(dir), unit));
1145
1146	ASSERT(dir < FWDER_MAX_DIR);
1147	ASSERT(unit < FWDER_MAX_RADIO);
1148
1149	if (mate == FWDER_NULL)
1150		return FWDER_NULL;
1151
1152	funit = FWDER_UNIT(unit);
1153	self = __fwder_self(dir, funit);
1154
1155	ASSERT(self == mate->mate);
1156	FWDER_ASSERT(self->unit == mate->unit);
1157
1158	_FWDER_LOCK(self);                                     /* ++LOCK */
1159
1160	/* A WLAN unit dettached */
1161	if (dir == (int)FWDER_DNSTREAM) {
1162		int subunit;
1163		fwder_if_t * fwder_if;
1164
1165		for (subunit = 0; subunit < FWDER_MAX_IF; subunit++) {
1166			fwder_if = __fwder_if(unit, subunit);
1167
1168			/* Ensure no interfaces are still bound to the forwarder */
1169			if (fwder_if->dev != FWDER_NET_DEVICE_NULL) {
1170				/* Move to pool's free list */
1171				FWDER_ASSERT(!dll_empty(&self->devs_dll));
1172				fwder_flush(self, (wofa_t)fwder_if->dev);
1173				self->devs_cnt--;
1174				dll_delete(&fwder_if->node);
1175				fwder_if->dev = FWDER_NET_DEVICE_NULL;
1176			}
1177		}
1178
1179		__fwder_sync_devs_cnt(self); /* sync upstream fwder devs_cnt */
1180	}
1181
1182	if (self->devs_cnt == 0) {
1183		self->bypass_fn = _fwder_bypass_fn;
1184		self->dev_def = FWDER_NET_DEVICE_NULL;
1185		self->mode = FWDER_NIC_MODE;
1186	}
1187
1188	if (dir == FWDER_UPSTREAM) {
1189		self->dev_def = FWDER_NET_DEVICE_NULL;
1190	}
1191
1192	_FWDER_UNLOCK(self);                                   /* --LOCK */
1193
1194	return FWDER_NULL;
1195}
1196
1197/** Given an upstream fwder handle, register a default interface
1198 * to mate downstream fwder. Deregister bu using a NULL net_device.
1199 */
1200int
1201fwder_register(fwder_t * fwder, struct net_device * dev)
1202{
1203	FWDER_TRACE(("%s fwder<%p> dev<%p:%s>\n", __FUNCTION__,
1204	             fwder, dev, __SSTR(dev, name)));
1205
1206	if (fwder == FWDER_NULL)
1207		return FWDER_FAILURE;
1208
1209	ASSERT(fwder == __fwder_self(FWDER_UPSTREAM, fwder->unit));
1210
1211	/* register with downstream fwder */
1212	fwder->mate->dev_def = dev;
1213
1214	return FWDER_SUCCESS;
1215}
1216
1217/** Given a downstream fwder handle, fetch the default registered device. */
1218struct net_device *
1219fwder_default(fwder_t * fwder)
1220{
1221	if (fwder == FWDER_NULL)
1222		return FWDER_NET_DEVICE_NULL;
1223
1224	ASSERT(fwder == __fwder_self(FWDER_DNSTREAM, fwder->unit));
1225
1226	return fwder->dev_def;
1227}
1228
1229/** Bind/Unbind HW switching to a primary/virtual interface.
1230 * WLAN interfaces use the fwder_bind to attach a primary or virtual interface
1231 * once the net_device is registered. The WLAN interface name will be fetched
1232 * from the nvram fwd_wlandevs to determine eligibility for HW switching.
1233 *
1234 * Note: The interface is added to the downstream forwarder.
1235 *       mate points to upstream forwarder.
1236 */
1237fwder_t *
1238fwder_bind(fwder_t * mate, int unit, int subunit, struct net_device * dev,
1239	bool attach)
1240{
1241	int funit;
1242	fwder_t * self;
1243	fwder_if_t * fwder_if;
1244
1245	FWDER_TRACE(("%s unit<%d> subunit<%d> dev<%p:%s>\n", __FUNCTION__,
1246	             unit, subunit, dev, __SSTR(dev, name)));
1247
1248	ASSERT(unit < FWDER_MAX_RADIO);
1249	ASSERT(subunit < FWDER_MAX_IF);
1250	ASSERT(dev != FWDER_NET_DEVICE_NULL);
1251
1252	/* Primary did not attach a fwder */
1253	if (mate == FWDER_NULL)
1254		return FWDER_NULL;
1255
1256	{	/* Check if this interface is eligible for HW switching */
1257		const char * fwder_wlifs_nvar;
1258		fwder_wlifs_nvar = getvar(NULL, FWDER_WLIFS_NVAR);
1259		if (bcmstrstr(fwder_wlifs_nvar, dev->name) == NULL) {
1260			return FWDER_NULL;
1261		}
1262	}
1263
1264	funit = FWDER_UNIT(unit);
1265	fwder_if = __fwder_if(unit, subunit);
1266
1267	/* Fetch the fwder and the fwder_if */
1268	self = mate->mate; /* downstream direction forwarder */
1269	FWDER_ASSERT(mate == __fwder_self(FWDER_UPSTREAM, funit));
1270	FWDER_ASSERT(self == __fwder_self(FWDER_DNSTREAM, funit));
1271	FWDER_ASSERT(mate->unit == self->unit);
1272
1273	/* Add the interface to the downstream forwarder (used by et). */
1274	_FWDER_LOCK(self);                                     /* ++LOCK */
1275
1276	if (attach == TRUE) {
1277
1278		/* Check if wlif<subunit> is re-binding. */
1279		if (fwder_if->dev != FWDER_NET_DEVICE_NULL) {
1280			FWDER_WARN(("%s re bind at<%d,%d> new dev<%p:%s> old dev<%p:%s>\n",
1281			            __FUNCTION__, unit, subunit, dev, __SSTR(dev, name),
1282			            fwder_if->dev, __SSTR(fwder_if->dev, name)));
1283			ASSERT(dev == fwder_if->dev);
1284			fwder_if->dev = dev;
1285			goto unlock_ret;
1286		}
1287
1288		/* Move to active if list. */
1289		fwder_if->dev = dev;
1290		dll_append(&self->devs_dll, &fwder_if->node);
1291
1292		self->devs_cnt++;
1293
1294	} else { /* attach == FALSE */
1295
1296		if (fwder_if->dev != FWDER_NET_DEVICE_NULL) {
1297			/* Move to pool's free list */
1298			dll_delete(&fwder_if->node);
1299			fwder_if->dev = FWDER_NET_DEVICE_NULL;
1300
1301			/* Is attached, so dettach */
1302			self->devs_cnt--;
1303			FWDER_ASSERT(self->devs_cnt >= 0);
1304
1305		} else { /* already dettached, do nothing. */
1306			FWDER_WARN(("%s unbind NULL dev at<%d,%d>\n",
1307			            __FUNCTION__, unit, subunit));
1308		}
1309	}
1310
1311	__fwder_sync_devs_cnt(self); /* sync upstream fwder devs_cnt */
1312
1313unlock_ret:
1314
1315	_FWDER_UNLOCK(self);                                   /* --LOCK */
1316
1317	return mate;
1318}
1319
1320/** Add a station to a forwarder's WOFA on association or reassociation. */
1321int
1322fwder_reassoc(fwder_t * fwder, uint16 * symbol, wofa_t wofa)
1323{
1324	int err;
1325	if (fwder == FWDER_NULL)
1326		return FWDER_FAILURE;
1327
1328	FWDER_TRACE(("%s fwder<%p,%s> " __EFMT "wofa<0x%08x>\n", __FUNCTION__,
1329	           fwder, __SSTR(fwder, name), __EVAL((uint8*)symbol), (uint)wofa));
1330	FWDER_ASSERT(fwder->wofa == fwder->mate->wofa);
1331	FWDER_ALIGN16(symbol);
1332
1333	err = __fwder_wofa_add(fwder->wofa, symbol, wofa);
1334
1335	return err;
1336}
1337
1338/** Delete a station from a forwarder's WOFA on deassociation. */
1339int
1340fwder_deassoc(fwder_t * fwder, uint16 * symbol, wofa_t wofa)
1341{
1342	int err;
1343	if (fwder == FWDER_NULL)
1344		return FWDER_FAILURE;
1345
1346	FWDER_TRACE(("%s fwder<%p,%s> " __EFMT "wofa<0x%08x>\n", __FUNCTION__,
1347	       fwder, __SSTR(fwder, name), __EVAL((uint8*)symbol), (uint)wofa));
1348	FWDER_ALIGN16(symbol);
1349	FWDER_ASSERT(fwder->wofa == fwder->mate->wofa);
1350
1351	err = __fwder_wofa_del(fwder->wofa, symbol, wofa);
1352
1353	return err;
1354}
1355
1356/** Flush all entries in the forwarder's WOFA containing the metadata */
1357int
1358fwder_flush(fwder_t * fwder, wofa_t wofa)
1359{
1360	int err;
1361	if ((fwder == FWDER_NULL) || (wofa == FWDER_WOFA_INVALID))
1362		return FWDER_FAILURE;
1363
1364	FWDER_TRACE(("%s fwder<%p,%s> wofa<0x%08x>\n", __FUNCTION__,
1365	            fwder, __SSTR(fwder, name), (uint)wofa));
1366	FWDER_ASSERT(fwder->wofa == fwder->mate->wofa);
1367
1368	err = __fwder_wofa_clr(fwder->wofa, wofa);
1369
1370	return err;
1371}
1372
1373/** Lookup WOFA for a station (by Mac Address) assocatied with a forwarder. */
1374wofa_t
1375fwder_lookup(fwder_t * fwder, uint16 * symbol, const int port)
1376{
1377	wofa_t wofa;
1378	FWDER_ASSERT(fwder != FWDER_NULL);
1379	FWDER_PTRACE(("%s fwder<%p,%s> " __EFMT "port<%d>\n", __FUNCTION__,
1380	              fwder, __SSTR(fwder, name), __EVAL((uint8*)symbol), port));
1381	FWDER_ALIGN16(symbol);
1382	wofa = __fwder_wofa_lkup(fwder->wofa, symbol, port);
1383	return wofa;
1384}
1385
1386/** Flood a packet to all interfaces. Free original if clone is FALSE. */
1387int
1388fwder_flood(fwder_t * fwder, struct sk_buff * skb, void * osh, bool clone,
1389            fwder_flood_fn_t dev_start_xmit)
1390{
1391	int ret = FWDER_SUCCESS;
1392	struct sk_buff * nskb;
1393	struct net_device * dev;
1394	dll_t * item, * next, * list;
1395
1396	FWDER_PTRACE(("%s fwder<%p,%s> skb<0x%p>, osh<%p>\n", __FUNCTION__,
1397	              fwder, __SSTR(fwder, name), skb, osh));
1398
1399	/* Traverse the list of bound interfaces in the downstream forwarder. */
1400	list = &fwder->devs_dll;
1401	for (item = dll_head_p(list); !dll_end(list, item); item = next) {
1402		next = dll_next_p(item);
1403
1404		dev = ((fwder_if_t *)item)->dev; /* fetch fwder interface's netdevice */
1405
1406		/* do not flood back to self, so skb->dev must be properly set */
1407		if ((dev != skb->dev) && (dev->flags & IFF_UP)) {
1408
1409			/* Use PKTDUP, which will manipulate the appropriate CTF fields */
1410			if ((nskb = (struct sk_buff *)PKTDUP(osh, skb)) == NULL) {
1411				ret = FWDER_FAILURE;
1412				break;
1413			}
1414
1415			FWDER_PTRACE(("%s skb<0x%p> %pS\n", __FUNCTION__,
1416			              nskb, dev_start_xmit));
1417
1418			/* dispatch to either NIC or DHD start xmit */
1419			nskb->dev = dev;
1420			dev_start_xmit(nskb, dev, TRUE);
1421
1422			FWDER_STATS_ADD(fwder->flooded, 1);
1423		}
1424	}
1425
1426	if (clone == FALSE) { /* free original skb */
1427		PKTFREE(osh, skb, FALSE);
1428	}
1429
1430	return ret;
1431}
1432
1433/** Fixup a packet received from a GMAC forwarder. */
1434void
1435fwder_fixup(fwder_t * fwder, struct sk_buff * skb)
1436{
1437	FWDER_PTRACE(("%s fwder<%p,%s> skb<%p>\n", __FUNCTION__,
1438	              fwder, __SSTR(fwder, name), skb));
1439	/* strip off rxhdr and popping of BRCM TAG */
1440	__skb_pull(skb, fwder->dataoff);
1441	ASSERT(((ulong)skb->data & 3) == 2); /* aligned 2-mod-4 */
1442
1443	/* strip off 4Byte CRC32 at tail end */
1444	skb_trim(skb, skb->len - ETHER_CRC_LEN);
1445
1446	PKTCLRFWDERBUF(fwder->osh, skb); /* redundant, but safe */
1447}
1448
1449/** Downstream forwarder discarding a packet in the context of GMAC forwarder */
1450void
1451fwder_discard(fwder_t * fwder, struct sk_buff * skb)
1452{
1453	FWDER_PTRACE(("%s fwder<%p,%s> skb<%p>\n", __FUNCTION__,
1454	              fwder, __SSTR(fwder, name), skb));
1455	PKTFRMFWDER(fwder->osh, skb, 1);
1456	PKTFREE(fwder->osh, skb, FALSE);
1457}
1458
1459/** Debug dump the Radio to forwarder cpu mapping. */
1460static void
1461_fwder_cpumap_dump(struct bcmstrbuf *b)
1462{
1463	int ix;
1464	fwder_cpumap_t * cpumap;
1465	for (ix = 0; ix < FWDER_MAX_RADIO; ix++) {
1466		cpumap = &fwder_cpumap_g[ix];
1467		if (cpumap->cpu == -1) continue;
1468		bcm_bprintf(b,
1469		    "%d. mode<%s> chan<%s> band<%d> irq<%d> cpu<%d> unit<%d>\n",
1470		    ix, __fwder_mode(cpumap->mode), __fwder_chan(cpumap->chan),
1471		    cpumap->band, cpumap->irq, cpumap->cpu, cpumap->unit);
1472	}
1473}
1474
1475/** Debug dump a forwarding object. */
1476static void
1477_fwder_dump(struct bcmstrbuf *b, const fwder_t * fwder)
1478{
1479	if (fwder == FWDER_NULL)
1480		return;
1481
1482	bcm_bprintf(b, "Fwder[%p,%s]: unit<%u> mate<%p:%s> mode<%s>"
1483#if defined(FWDER_STATS)
1484	            " transmit<%u> dropped<%u> flooded<%u>\n"
1485#endif
1486	            "\tdevs_cnt<%u> dev<%p:%s> bypass<%p:%pS>\n", fwder,
1487	            __SSTR(fwder, name), fwder->unit, fwder->mate,
1488	            __SSTR(fwder->mate, name), __fwder_mode(fwder->mode),
1489#if defined(FWDER_STATS)
1490	            fwder->transmit, fwder->dropped, fwder->flooded,
1491#endif
1492	            fwder->devs_cnt, fwder->dev_def, __SSTR(fwder->dev_def, name),
1493	            fwder->bypass_fn, fwder->bypass_fn);
1494}
1495
1496/** Debug dump the list of bound interfaces (net_device). */
1497static void
1498_fwder_devs_dump(struct bcmstrbuf *b, dll_t * fwder_if_dll)
1499{
1500	struct net_device * dev;
1501	dll_t * item, * next;
1502	bcm_bprintf(b, "\tBound devices:\n");
1503	/* Traverse the list of active interfaces */
1504	/* Do additional checking for item because fwder_if_dll doesn't be initialized
1505	 * by dll_init when gmac_fwd is FALSE.
1506	 */
1507	for (item = dll_head_p(fwder_if_dll);
1508	     item && !dll_end(fwder_if_dll, item);
1509	     item = next)
1510	{
1511		next = dll_next_p(item);
1512		dev = ((fwder_if_t *)item)->dev;
1513		bcm_bprintf(b, "\t\tdev<%p,%s>\n", dev, __SSTR(dev, name));
1514	}
1515}
1516
1517/** Debug dump all forwarding objects units<0,1>: <Upstream,Dnstream> pairs. */
1518void
1519fwder_dump(struct bcmstrbuf *b)
1520{
1521	int unit;
1522	fwder_t * fwder;
1523
1524	ASSERT(b != NULL);
1525
1526	bcm_bprintf(b, "Fwder Dump default bypass<%p,%pS>\n",
1527	            _fwder_bypass_fn, _fwder_bypass_fn);
1528
1529	/* Dump the Radio to Fwder unit cpu map */
1530	_fwder_cpumap_dump(b);
1531
1532	/* Traverse the two bidirectional forwarders. */
1533#if defined(CONFIG_SMP)
1534	for_each_online_cpu(unit)
1535#else
1536	for (unit = 0; unit < FWDER_MAX_UNIT; unit++)
1537#endif  /* !CONFIG_SMP */
1538	{
1539		fwder = FWDER_GET(fwder_upstream_g, unit);
1540		_fwder_dump(b, fwder); /* dump upstream forwarder */
1541		_fwder_dump(b, fwder->mate); /* dump mate downstream forwarder */
1542		_fwder_devs_dump(b, &fwder->devs_dll); /* dump bound interfaces */
1543		_fwder_wofa_dump(b, fwder->wofa); /* dump WOFA ARL */
1544	}	/* for_each_online_cpu | for unit = 0 .. FWDER_MAX_UNIT */
1545}
1546#endif  /*  BCM_GMAC3 */
1547