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