Print this page
2869 duplicate packets with vnics over aggrs
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/io/aggr/aggr_port.c
+++ new/usr/src/uts/common/io/aggr/aggr_port.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 + * Copyright 2012 OmniTI Computer Consulting, Inc All rights reserved.
24 25 */
25 26
26 27 /*
27 28 * IEEE 802.3ad Link Aggregation - Link Aggregation MAC ports.
28 29 *
29 30 * Implements the functions needed to manage the MAC ports that are
30 31 * part of Link Aggregation groups.
31 32 */
32 33
33 34 #include <sys/types.h>
34 35 #include <sys/sysmacros.h>
35 36 #include <sys/conf.h>
36 37 #include <sys/cmn_err.h>
37 38 #include <sys/id_space.h>
38 39 #include <sys/list.h>
39 40 #include <sys/ksynch.h>
40 41 #include <sys/kmem.h>
41 42 #include <sys/stream.h>
42 43 #include <sys/modctl.h>
43 44 #include <sys/ddi.h>
44 45 #include <sys/sunddi.h>
45 46 #include <sys/atomic.h>
46 47 #include <sys/stat.h>
47 48 #include <sys/sdt.h>
48 49 #include <sys/dlpi.h>
49 50 #include <sys/dls.h>
50 51 #include <sys/aggr.h>
51 52 #include <sys/aggr_impl.h>
52 53
53 54 static kmem_cache_t *aggr_port_cache;
54 55 static id_space_t *aggr_portids;
55 56
56 57 static void aggr_port_notify_cb(void *, mac_notify_type_t);
57 58
58 59 /*ARGSUSED*/
59 60 static int
60 61 aggr_port_constructor(void *buf, void *arg, int kmflag)
61 62 {
62 63 bzero(buf, sizeof (aggr_port_t));
63 64 return (0);
64 65 }
65 66
66 67 /*ARGSUSED*/
67 68 static void
68 69 aggr_port_destructor(void *buf, void *arg)
69 70 {
70 71 aggr_port_t *port = buf;
71 72
72 73 ASSERT(port->lp_mnh == NULL);
73 74 ASSERT(port->lp_mphp == NULL);
74 75 ASSERT(!port->lp_rx_grp_added && !port->lp_tx_grp_added);
75 76 ASSERT(port->lp_hwgh == NULL);
76 77 }
77 78
78 79 void
79 80 aggr_port_init(void)
80 81 {
81 82 aggr_port_cache = kmem_cache_create("aggr_port_cache",
82 83 sizeof (aggr_port_t), 0, aggr_port_constructor,
83 84 aggr_port_destructor, NULL, NULL, NULL, 0);
84 85
85 86 /*
86 87 * Allocate a id space to manage port identification. The range of
87 88 * the arena will be from 1 to UINT16_MAX, because the LACP protocol
88 89 * specifies 16-bit unique identification.
89 90 */
90 91 aggr_portids = id_space_create("aggr_portids", 1, UINT16_MAX);
91 92 ASSERT(aggr_portids != NULL);
92 93 }
93 94
94 95 void
95 96 aggr_port_fini(void)
96 97 {
97 98 /*
98 99 * This function is called only after all groups have been
99 100 * freed. This ensures that there are no remaining allocated
100 101 * ports when this function is invoked.
101 102 */
102 103 kmem_cache_destroy(aggr_port_cache);
103 104 id_space_destroy(aggr_portids);
104 105 }
105 106
106 107 /* ARGSUSED */
107 108 void
108 109 aggr_port_init_callbacks(aggr_port_t *port)
109 110 {
110 111 /* add the port's receive callback */
111 112 port->lp_mnh = mac_notify_add(port->lp_mh, aggr_port_notify_cb, port);
112 113 /*
113 114 * Hold a reference of the grp and the port and this reference will
114 115 * be released when the thread exits.
115 116 *
116 117 * The reference on the port is used for aggr_port_delete() to
117 118 * continue without waiting for the thread to exit; the reference
118 119 * on the grp is used for aggr_grp_delete() to wait for the thread
119 120 * to exit before calling mac_unregister().
120 121 *
121 122 * Note that these references will be released either in
122 123 * aggr_port_delete() when mac_notify_remove() succeeds, or in
123 124 * the aggr_port_notify_cb() callback when the port is deleted
124 125 * (lp_closing is set).
125 126 */
126 127 aggr_grp_port_hold(port);
127 128 }
128 129
129 130 /* ARGSUSED */
130 131 int
131 132 aggr_port_create(aggr_grp_t *grp, const datalink_id_t linkid, boolean_t force,
132 133 aggr_port_t **pp)
133 134 {
134 135 int err;
135 136 mac_handle_t mh;
136 137 mac_client_handle_t mch = NULL;
137 138 aggr_port_t *port;
138 139 uint16_t portid;
139 140 uint_t i;
140 141 boolean_t no_link_update = B_FALSE;
141 142 const mac_info_t *mip;
142 143 uint32_t note;
143 144 uint32_t margin;
144 145 char client_name[MAXNAMELEN];
145 146 char aggr_name[MAXNAMELEN];
146 147 char port_name[MAXNAMELEN];
147 148 mac_diag_t diag;
148 149 mac_unicast_handle_t mah;
149 150
150 151 *pp = NULL;
151 152
152 153 if ((err = mac_open_by_linkid(linkid, &mh)) != 0)
153 154 return (err);
154 155
155 156 mip = mac_info(mh);
156 157 if (mip->mi_media != DL_ETHER || mip->mi_nativemedia != DL_ETHER) {
157 158 err = EINVAL;
158 159 goto fail;
159 160 }
160 161
161 162 /*
162 163 * If the underlying MAC does not support link update notification, it
163 164 * can only be aggregated if `force' is set. This is because aggr
164 165 * depends on link notifications to attach ports whose link is up.
165 166 */
166 167 note = mac_no_notification(mh);
167 168 if ((note & (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN)) != 0) {
168 169 no_link_update = B_TRUE;
169 170 if (!force) {
170 171 /*
171 172 * We borrow this error code to indicate that link
172 173 * notification is not supported.
173 174 */
174 175 err = ENETDOWN;
175 176 goto fail;
176 177 }
177 178 }
178 179
179 180 if (((err = dls_mgmt_get_linkinfo(grp->lg_linkid,
180 181 aggr_name, NULL, NULL, NULL)) != 0) ||
181 182 ((err = dls_mgmt_get_linkinfo(linkid, port_name,
182 183 NULL, NULL, NULL)) != 0)) {
183 184 goto fail;
184 185 }
185 186
186 187 (void) snprintf(client_name, MAXNAMELEN, "%s-%s", aggr_name, port_name);
187 188 if ((err = mac_client_open(mh, &mch, client_name,
188 189 MAC_OPEN_FLAGS_IS_AGGR_PORT | MAC_OPEN_FLAGS_EXCLUSIVE)) != 0) {
189 190 goto fail;
190 191 }
191 192
192 193 if ((portid = (uint16_t)id_alloc(aggr_portids)) == 0) {
193 194 err = ENOMEM;
194 195 goto fail;
195 196 }
196 197
197 198 /*
198 199 * As the underlying mac's current margin size is used to determine
199 200 * the margin size of the aggregation itself, request the underlying
200 201 * mac not to change to a smaller size.
201 202 */
202 203 if ((err = mac_margin_add(mh, &margin, B_TRUE)) != 0) {
203 204 id_free(aggr_portids, portid);
204 205 goto fail;
205 206 }
206 207
207 208 if ((err = mac_unicast_add(mch, NULL, MAC_UNICAST_PRIMARY |
208 209 MAC_UNICAST_DISABLE_TX_VID_CHECK, &mah, 0, &diag)) != 0) {
209 210 VERIFY(mac_margin_remove(mh, margin) == 0);
210 211 id_free(aggr_portids, portid);
211 212 goto fail;
212 213 }
213 214
214 215 port = kmem_cache_alloc(aggr_port_cache, KM_SLEEP);
215 216
216 217 port->lp_refs = 1;
217 218 port->lp_next = NULL;
218 219 port->lp_mh = mh;
219 220 port->lp_mch = mch;
220 221 port->lp_mip = mip;
221 222 port->lp_linkid = linkid;
222 223 port->lp_closing = B_FALSE;
223 224 port->lp_mah = mah;
224 225
225 226 /* get the port's original MAC address */
226 227 mac_unicast_primary_get(port->lp_mh, port->lp_addr);
227 228
228 229 /* initialize state */
229 230 port->lp_state = AGGR_PORT_STATE_STANDBY;
230 231 port->lp_link_state = LINK_STATE_UNKNOWN;
231 232 port->lp_ifspeed = 0;
232 233 port->lp_link_duplex = LINK_DUPLEX_UNKNOWN;
233 234 port->lp_started = B_FALSE;
234 235 port->lp_tx_enabled = B_FALSE;
235 236 port->lp_promisc_on = B_FALSE;
236 237 port->lp_no_link_update = no_link_update;
237 238 port->lp_portid = portid;
238 239 port->lp_margin = margin;
239 240 port->lp_prom_addr = NULL;
240 241
241 242 /*
242 243 * Save the current statistics of the port. They will be used
243 244 * later by aggr_m_stats() when aggregating the statistics of
244 245 * the constituent ports.
245 246 */
246 247 for (i = 0; i < MAC_NSTAT; i++) {
247 248 port->lp_stat[i] =
248 249 aggr_port_stat(port, i + MAC_STAT_MIN);
249 250 }
250 251 for (i = 0; i < ETHER_NSTAT; i++) {
251 252 port->lp_ether_stat[i] =
252 253 aggr_port_stat(port, i + MACTYPE_STAT_MIN);
253 254 }
254 255
255 256 /* LACP related state */
256 257 port->lp_collector_enabled = B_FALSE;
257 258
258 259 *pp = port;
259 260 return (0);
260 261
261 262 fail:
262 263 if (mch != NULL)
263 264 mac_client_close(mch, MAC_CLOSE_FLAGS_EXCLUSIVE);
264 265 mac_close(mh);
265 266 return (err);
266 267 }
267 268
268 269 void
269 270 aggr_port_delete(aggr_port_t *port)
270 271 {
271 272 aggr_lacp_port_t *pl = &port->lp_lacp;
272 273
273 274 ASSERT(port->lp_mphp == NULL);
274 275 ASSERT(!port->lp_promisc_on);
275 276
276 277 port->lp_closing = B_TRUE;
277 278
278 279 VERIFY(mac_margin_remove(port->lp_mh, port->lp_margin) == 0);
279 280 mac_rx_clear(port->lp_mch);
280 281 /*
281 282 * If the notification callback is already in process and waiting for
282 283 * the aggr grp's mac perimeter, don't wait (otherwise there would be
283 284 * deadlock). Otherwise, if mac_notify_remove() succeeds, we can
284 285 * release the reference held when mac_notify_add() is called.
285 286 */
286 287 if ((port->lp_mnh != NULL) &&
287 288 (mac_notify_remove(port->lp_mnh, B_FALSE) == 0)) {
288 289 aggr_grp_port_rele(port);
289 290 }
290 291 port->lp_mnh = NULL;
291 292
292 293 /*
293 294 * Inform the the port lacp timer thread to exit. Note that waiting
294 295 * for the thread to exit may cause deadlock since that thread may
295 296 * need to enter into the mac perimeter which we are currently in.
296 297 * It is fine to continue without waiting though since that thread
297 298 * is holding a reference of the port.
298 299 */
299 300 mutex_enter(&pl->lacp_timer_lock);
300 301 pl->lacp_timer_bits |= LACP_THREAD_EXIT;
301 302 cv_broadcast(&pl->lacp_timer_cv);
302 303 mutex_exit(&pl->lacp_timer_lock);
303 304
304 305 /*
305 306 * Restore the port MAC address. Note it is called after the
306 307 * port's notification callback being removed. This prevent
307 308 * port's MAC_NOTE_UNICST notify callback function being called.
308 309 */
309 310 (void) mac_unicast_primary_set(port->lp_mh, port->lp_addr);
310 311 if (port->lp_mah != NULL)
311 312 (void) mac_unicast_remove(port->lp_mch, port->lp_mah);
312 313 mac_client_close(port->lp_mch, MAC_CLOSE_FLAGS_EXCLUSIVE);
313 314 mac_close(port->lp_mh);
314 315 AGGR_PORT_REFRELE(port);
315 316 }
316 317
317 318 void
318 319 aggr_port_free(aggr_port_t *port)
319 320 {
320 321 ASSERT(port->lp_refs == 0);
321 322 if (port->lp_grp != NULL)
322 323 AGGR_GRP_REFRELE(port->lp_grp);
323 324 port->lp_grp = NULL;
324 325 id_free(aggr_portids, port->lp_portid);
325 326 port->lp_portid = 0;
326 327 mutex_destroy(&port->lp_lacp.lacp_timer_lock);
327 328 cv_destroy(&port->lp_lacp.lacp_timer_cv);
328 329 kmem_cache_free(aggr_port_cache, port);
329 330 }
330 331
331 332 /*
332 333 * Invoked upon receiving a MAC_NOTE_LINK notification for
333 334 * one of the constituent ports.
334 335 */
335 336 boolean_t
336 337 aggr_port_notify_link(aggr_grp_t *grp, aggr_port_t *port)
337 338 {
338 339 boolean_t do_attach = B_FALSE;
339 340 boolean_t do_detach = B_FALSE;
340 341 boolean_t link_state_changed = B_TRUE;
341 342 uint64_t ifspeed;
342 343 link_state_t link_state;
343 344 link_duplex_t link_duplex;
344 345 mac_perim_handle_t mph;
345 346
346 347 ASSERT(MAC_PERIM_HELD(grp->lg_mh));
347 348 mac_perim_enter_by_mh(port->lp_mh, &mph);
348 349
349 350 /*
350 351 * link state change? For links that do not support link state
351 352 * notification, always assume the link is up.
352 353 */
353 354 link_state = port->lp_no_link_update ? LINK_STATE_UP :
354 355 mac_link_get(port->lp_mh);
355 356 if (port->lp_link_state != link_state) {
356 357 if (link_state == LINK_STATE_UP)
357 358 do_attach = (port->lp_link_state != LINK_STATE_UP);
358 359 else
359 360 do_detach = (port->lp_link_state == LINK_STATE_UP);
360 361 }
361 362 port->lp_link_state = link_state;
362 363
363 364 /* link duplex change? */
364 365 link_duplex = aggr_port_stat(port, ETHER_STAT_LINK_DUPLEX);
365 366 if (port->lp_link_duplex != link_duplex) {
366 367 if (link_duplex == LINK_DUPLEX_FULL)
367 368 do_attach |= (port->lp_link_duplex != LINK_DUPLEX_FULL);
368 369 else
369 370 do_detach |= (port->lp_link_duplex == LINK_DUPLEX_FULL);
370 371 }
371 372 port->lp_link_duplex = link_duplex;
372 373
373 374 /* link speed changes? */
374 375 ifspeed = aggr_port_stat(port, MAC_STAT_IFSPEED);
375 376 if (port->lp_ifspeed != ifspeed) {
376 377 if (port->lp_state == AGGR_PORT_STATE_ATTACHED)
377 378 do_detach |= (ifspeed != grp->lg_ifspeed);
378 379 else
379 380 do_attach |= (ifspeed == grp->lg_ifspeed);
380 381 }
381 382 port->lp_ifspeed = ifspeed;
382 383
383 384 if (do_attach) {
384 385 /* attempt to attach the port to the aggregation */
385 386 link_state_changed = aggr_grp_attach_port(grp, port);
386 387 } else if (do_detach) {
387 388 /* detach the port from the aggregation */
388 389 link_state_changed = aggr_grp_detach_port(grp, port);
389 390 }
390 391
391 392 mac_perim_exit(mph);
392 393 return (link_state_changed);
393 394 }
394 395
395 396 /*
396 397 * Invoked upon receiving a MAC_NOTE_UNICST for one of the constituent
397 398 * ports of a group.
398 399 */
399 400 static void
400 401 aggr_port_notify_unicst(aggr_grp_t *grp, aggr_port_t *port,
401 402 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp)
402 403 {
403 404 boolean_t mac_addr_changed = B_FALSE;
404 405 boolean_t link_state_changed = B_FALSE;
405 406 uint8_t mac_addr[ETHERADDRL];
406 407 mac_perim_handle_t mph;
407 408
408 409 ASSERT(MAC_PERIM_HELD(grp->lg_mh));
409 410 ASSERT(mac_addr_changedp != NULL);
410 411 ASSERT(link_state_changedp != NULL);
411 412 mac_perim_enter_by_mh(port->lp_mh, &mph);
412 413
413 414 /*
414 415 * If it is called when setting the MAC address to the
415 416 * aggregation group MAC address, do nothing.
416 417 */
417 418 mac_unicast_primary_get(port->lp_mh, mac_addr);
418 419 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) {
419 420 mac_perim_exit(mph);
420 421 goto done;
421 422 }
422 423
423 424 /* save the new port MAC address */
424 425 bcopy(mac_addr, port->lp_addr, ETHERADDRL);
425 426
426 427 aggr_grp_port_mac_changed(grp, port, &mac_addr_changed,
427 428 &link_state_changed);
428 429
429 430 mac_perim_exit(mph);
430 431
431 432 /*
432 433 * If this port was used to determine the MAC address of
433 434 * the group, update the MAC address of the constituent
434 435 * ports.
435 436 */
436 437 if (mac_addr_changed && aggr_grp_update_ports_mac(grp))
437 438 link_state_changed = B_TRUE;
438 439
439 440 done:
440 441 *mac_addr_changedp = mac_addr_changed;
441 442 *link_state_changedp = link_state_changed;
442 443 }
443 444
444 445 /*
445 446 * Notification callback invoked by the MAC service module for
446 447 * a particular MAC port.
447 448 */
448 449 static void
449 450 aggr_port_notify_cb(void *arg, mac_notify_type_t type)
450 451 {
451 452 aggr_port_t *port = arg;
452 453 aggr_grp_t *grp = port->lp_grp;
453 454 boolean_t mac_addr_changed, link_state_changed;
454 455 mac_perim_handle_t mph;
455 456
456 457 mac_perim_enter_by_mh(grp->lg_mh, &mph);
457 458 if (port->lp_closing) {
458 459 mac_perim_exit(mph);
459 460
460 461 /*
461 462 * Release the reference so it is safe for aggr to call
462 463 * mac_unregister() now.
463 464 */
464 465 aggr_grp_port_rele(port);
465 466 return;
466 467 }
467 468
468 469 switch (type) {
469 470 case MAC_NOTE_TX:
470 471 mac_tx_update(grp->lg_mh);
471 472 break;
472 473 case MAC_NOTE_LINK:
473 474 if (aggr_port_notify_link(grp, port))
474 475 mac_link_update(grp->lg_mh, grp->lg_link_state);
475 476 break;
476 477 case MAC_NOTE_UNICST:
477 478 aggr_port_notify_unicst(grp, port, &mac_addr_changed,
478 479 &link_state_changed);
479 480 if (mac_addr_changed)
480 481 mac_unicst_update(grp->lg_mh, grp->lg_addr);
481 482 if (link_state_changed)
482 483 mac_link_update(grp->lg_mh, grp->lg_link_state);
483 484 break;
484 485 default:
485 486 break;
486 487 }
487 488
488 489 mac_perim_exit(mph);
489 490 }
490 491
491 492 int
492 493 aggr_port_start(aggr_port_t *port)
493 494 {
494 495 ASSERT(MAC_PERIM_HELD(port->lp_mh));
495 496
496 497 if (port->lp_started)
497 498 return (0);
498 499
499 500 port->lp_started = B_TRUE;
500 501 aggr_grp_multicst_port(port, B_TRUE);
501 502 return (0);
502 503 }
503 504
504 505 void
505 506 aggr_port_stop(aggr_port_t *port)
506 507 {
507 508 ASSERT(MAC_PERIM_HELD(port->lp_mh));
508 509
509 510 if (!port->lp_started)
510 511 return;
511 512
512 513 aggr_grp_multicst_port(port, B_FALSE);
513 514
514 515 /* update the port state */
515 516 port->lp_started = B_FALSE;
516 517 }
517 518
518 519 int
519 520 aggr_port_promisc(aggr_port_t *port, boolean_t on)
520 521 {
↓ open down ↓ |
487 lines elided |
↑ open up ↑ |
521 522 int rc;
522 523
523 524 ASSERT(MAC_PERIM_HELD(port->lp_mh));
524 525
525 526 if (on == port->lp_promisc_on)
526 527 /* already in desired promiscous mode */
527 528 return (0);
528 529
529 530 if (on) {
530 531 mac_rx_clear(port->lp_mch);
532 + /* We use the promisc callback because without hardware
533 + * rings, we deliver through flows that will cause duplicate
534 + * delivery of packets when we've flipped into this mode
535 + * to compensate for the lack of hardware MAC matching
536 + */
531 537 rc = mac_promisc_add(port->lp_mch, MAC_CLIENT_PROMISC_ALL,
532 - aggr_recv_cb, port, &port->lp_mphp,
538 + aggr_recv_promisc_cb, port, &port->lp_mphp,
533 539 MAC_PROMISC_FLAGS_NO_TX_LOOP);
534 540 if (rc != 0) {
535 541 mac_rx_set(port->lp_mch, aggr_recv_cb, port);
536 542 return (rc);
537 543 }
538 544 } else {
539 545 mac_promisc_remove(port->lp_mphp);
540 546 port->lp_mphp = NULL;
541 547 mac_rx_set(port->lp_mch, aggr_recv_cb, port);
542 548 }
543 549
544 550 port->lp_promisc_on = on;
545 551
546 552 return (0);
547 553 }
548 554
549 555 /*
550 556 * Set the MAC address of a port.
551 557 */
552 558 int
553 559 aggr_port_unicst(aggr_port_t *port)
554 560 {
555 561 aggr_grp_t *grp = port->lp_grp;
556 562
557 563 ASSERT(MAC_PERIM_HELD(grp->lg_mh));
558 564 ASSERT(MAC_PERIM_HELD(port->lp_mh));
559 565
560 566 return (mac_unicast_primary_set(port->lp_mh, grp->lg_addr));
561 567 }
562 568
563 569 /*
564 570 * Add or remove a multicast address to/from a port.
565 571 */
566 572 int
567 573 aggr_port_multicst(void *arg, boolean_t add, const uint8_t *addrp)
568 574 {
569 575 aggr_port_t *port = arg;
570 576
571 577 if (add) {
572 578 return (mac_multicast_add(port->lp_mch, addrp));
573 579 } else {
574 580 mac_multicast_remove(port->lp_mch, addrp);
575 581 return (0);
576 582 }
577 583 }
578 584
579 585 uint64_t
580 586 aggr_port_stat(aggr_port_t *port, uint_t stat)
581 587 {
582 588 return (mac_stat_get(port->lp_mh, stat));
583 589 }
584 590
585 591 /*
586 592 * Add a non-primary unicast address to the underlying port. If the port
587 593 * supports HW Rx group, try to add the address into the HW Rx group of
588 594 * the port first. If that fails, or if the port does not support HW Rx
589 595 * group, enable the port's promiscous mode.
590 596 */
591 597 int
592 598 aggr_port_addmac(aggr_port_t *port, const uint8_t *mac_addr)
593 599 {
594 600 aggr_unicst_addr_t *addr, **pprev;
595 601 mac_perim_handle_t pmph;
596 602 int err;
597 603
598 604 ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh));
599 605 mac_perim_enter_by_mh(port->lp_mh, &pmph);
600 606
601 607 /*
602 608 * If the underlying port support HW Rx group, add the mac to its
603 609 * RX group directly.
604 610 */
605 611 if ((port->lp_hwgh != NULL) &&
606 612 ((mac_hwgroup_addmac(port->lp_hwgh, mac_addr)) == 0)) {
607 613 mac_perim_exit(pmph);
608 614 return (0);
609 615 }
610 616
611 617 /*
612 618 * If that fails, or if the port does not support HW Rx group, enable
613 619 * the port's promiscous mode. (Note that we turn on the promiscous
614 620 * mode only if the port is already started.
615 621 */
616 622 if (port->lp_started &&
617 623 ((err = aggr_port_promisc(port, B_TRUE)) != 0)) {
618 624 mac_perim_exit(pmph);
619 625 return (err);
620 626 }
621 627
622 628 /*
623 629 * Walk through the unicast addresses that requires promiscous mode
624 630 * enabled on this port, and add this address to the end of the list.
625 631 */
626 632 pprev = &port->lp_prom_addr;
627 633 while ((addr = *pprev) != NULL) {
628 634 ASSERT(bcmp(mac_addr, addr->aua_addr, ETHERADDRL) != 0);
629 635 pprev = &addr->aua_next;
630 636 }
631 637 addr = kmem_alloc(sizeof (aggr_unicst_addr_t), KM_SLEEP);
632 638 bcopy(mac_addr, addr->aua_addr, ETHERADDRL);
633 639 addr->aua_next = NULL;
634 640 *pprev = addr;
635 641 mac_perim_exit(pmph);
636 642 return (0);
637 643 }
638 644
639 645 /*
640 646 * Remove a non-primary unicast address from the underlying port. This address
641 647 * must has been added by aggr_port_addmac(). As a result, we probably need to
642 648 * remove the address from the port's HW Rx group, or to disable the port's
643 649 * promiscous mode.
644 650 */
645 651 void
646 652 aggr_port_remmac(aggr_port_t *port, const uint8_t *mac_addr)
647 653 {
648 654 aggr_grp_t *grp = port->lp_grp;
649 655 aggr_unicst_addr_t *addr, **pprev;
650 656 mac_perim_handle_t pmph;
651 657
652 658 ASSERT(MAC_PERIM_HELD(grp->lg_mh));
653 659 mac_perim_enter_by_mh(port->lp_mh, &pmph);
654 660
655 661 /*
656 662 * See whether this address is in the list of addresses that requires
657 663 * the port being promiscous mode.
658 664 */
659 665 pprev = &port->lp_prom_addr;
660 666 while ((addr = *pprev) != NULL) {
661 667 if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) == 0)
662 668 break;
663 669 pprev = &addr->aua_next;
664 670 }
665 671 if (addr != NULL) {
666 672 /*
667 673 * This unicast address put the port into the promiscous mode,
668 674 * delete this address from the lp_prom_addr list. If this is
669 675 * the last address in that list, disable the promiscous mode
670 676 * if the aggregation is not in promiscous mode.
671 677 */
672 678 *pprev = addr->aua_next;
673 679 kmem_free(addr, sizeof (aggr_unicst_addr_t));
674 680 if (port->lp_prom_addr == NULL && !grp->lg_promisc)
675 681 (void) aggr_port_promisc(port, B_FALSE);
676 682 } else {
677 683 ASSERT(port->lp_hwgh != NULL);
678 684 (void) mac_hwgroup_remmac(port->lp_hwgh, mac_addr);
679 685 }
680 686 mac_perim_exit(pmph);
681 687 }
↓ open down ↓ |
139 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX