1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/errno.h> 29 #include <sys/debug.h> 30 #include <sys/time.h> 31 #include <sys/sysmacros.h> 32 #include <sys/systm.h> 33 #include <sys/user.h> 34 #include <sys/stropts.h> 35 #include <sys/stream.h> 36 #include <sys/strlog.h> 37 #include <sys/strsubr.h> 38 #include <sys/cmn_err.h> 39 #include <sys/cpu.h> 40 #include <sys/kmem.h> 41 #include <sys/conf.h> 42 #include <sys/ddi.h> 43 #include <sys/sunddi.h> 44 #include <sys/ksynch.h> 45 #include <sys/stat.h> 46 #include <sys/kstat.h> 47 #include <sys/vtrace.h> 48 #include <sys/strsun.h> 49 #include <sys/dlpi.h> 50 #include <sys/ethernet.h> 51 #include <net/if.h> 52 #include <netinet/arp.h> 53 #include <inet/arp.h> 54 #include <sys/varargs.h> 55 #include <sys/machsystm.h> 56 #include <sys/modctl.h> 57 #include <sys/modhash.h> 58 #include <sys/mac_client.h> 59 #include <sys/mac_provider.h> 60 #include <sys/mac_client_priv.h> 61 #include <sys/mac_ether.h> 62 #include <sys/taskq.h> 63 #include <sys/note.h> 64 #include <sys/mach_descrip.h> 65 #include <sys/mac.h> 66 #include <sys/mdeg.h> 67 #include <sys/vsw.h> 68 #include <sys/vlan.h> 69 70 /* MAC Ring table functions. */ 71 static void vsw_port_rx_cb(void *, mac_resource_handle_t, mblk_t *, 72 boolean_t); 73 static void vsw_if_rx_cb(void *, mac_resource_handle_t, mblk_t *, boolean_t); 74 75 /* MAC layer routines */ 76 static int vsw_set_port_hw_addr(vsw_port_t *port); 77 static int vsw_set_if_hw_addr(vsw_t *vswp); 78 static void vsw_unset_hw_addr(vsw_t *, vsw_port_t *, int); 79 static int vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type); 80 static void vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type); 81 static void vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type); 82 static void vsw_mac_multicast_remove_all(vsw_t *vswp, 83 vsw_port_t *portp, int type); 84 static void vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch, 85 uint8_t *macaddr, uint16_t flags, vsw_vlanid_t *vids, int nvids); 86 static void vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids, 87 int nvids); 88 static void vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu); 89 static int vsw_notify_add(vsw_t *vswp); 90 static int vsw_notify_rem(vsw_t *vswp); 91 static void vsw_notify_cb(void *arg, mac_notify_type_t type); 92 static void vsw_notify_link(vsw_t *vswp); 93 94 /* Support functions */ 95 int vsw_set_hw(vsw_t *, vsw_port_t *, int); 96 void vsw_unset_hw(vsw_t *, vsw_port_t *, int); 97 void vsw_reconfig_hw(vsw_t *); 98 int vsw_mac_open(vsw_t *vswp); 99 void vsw_mac_close(vsw_t *vswp); 100 int vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p, 101 int type); 102 void vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port, 103 mcst_addr_t *mcst_p, int type); 104 int vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type); 105 void vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type); 106 void vsw_mac_cleanup_ports(vsw_t *vswp); 107 void vsw_unset_addrs(vsw_t *vswp); 108 void vsw_set_addrs(vsw_t *vswp); 109 mblk_t *vsw_tx_msg(vsw_t *, mblk_t *, int, vsw_port_t *); 110 void vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp); 111 void vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans, 112 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids); 113 void vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid, 114 vsw_vlanid_t *new_vids, int new_nvids); 115 void vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans, 116 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids); 117 118 /* 119 * Functions imported from other files. 120 */ 121 extern int vsw_portsend(vsw_port_t *port, mblk_t *mp); 122 extern void vsw_hio_stop_port(vsw_port_t *portp); 123 extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate); 124 extern uint32_t vsw_publish_macaddr_count; 125 extern uint32_t vsw_vlan_frame_untag(void *arg, int type, mblk_t **np, 126 mblk_t **npt); 127 extern void vsw_physlink_state_update(vsw_t *vswp); 128 static char mac_mtu_propname[] = "mtu"; 129 130 /* 131 * Tunables used in this file. 132 */ 133 extern int vsw_mac_open_retries; 134 135 136 #define WRITE_MACCL_ENTER(vswp, port, type) \ 137 (type == VSW_LOCALDEV) ? rw_enter(&vswp->maccl_rwlock, RW_WRITER) :\ 138 rw_enter(&port->maccl_rwlock, RW_WRITER) 139 140 #define READ_MACCL_ENTER(vswp, port, type) \ 141 (type == VSW_LOCALDEV) ? rw_enter(&vswp->maccl_rwlock, RW_READER) :\ 142 rw_enter(&port->maccl_rwlock, RW_READER) 143 144 #define RW_MACCL_EXIT(vswp, port, type) \ 145 (type == VSW_LOCALDEV) ? rw_exit(&vswp->maccl_rwlock) : \ 146 rw_exit(&port->maccl_rwlock) 147 148 149 /* 150 * Locking strategy in this file is explained as follows: 151 * - A global lock(vswp->mac_lock) is used to protect the 152 * MAC calls that deal with entire device. That is, the 153 * operations that deal with mac_handle which include 154 * mac_open()/close() and mac_client_open(). 155 * 156 * - A per port/interface RW lock(maccl_rwlock) is used protect 157 * the operations that deal with the MAC client. 158 * 159 * When both mac_lock and maccl_rwlock need to be held, the 160 * mac_lock need be acquired first and then maccl_rwlock. That is, 161 * mac_lock---->maccl_rwlock 162 * 163 * The 'mca_lock' that protects the mcast list is also acquired 164 * within the context of maccl_rwlock. The hierarchy for this 165 * one is as below: 166 * maccl_rwlock---->mca_lock 167 */ 168 169 170 /* 171 * Program unicast and multicast addresses of vsw interface and the ports 172 * into the network device. 173 */ 174 void 175 vsw_set_addrs(vsw_t *vswp) 176 { 177 vsw_port_list_t *plist = &vswp->plist; 178 vsw_port_t *port; 179 int rv; 180 181 READ_ENTER(&vswp->if_lockrw); 182 183 if (vswp->if_state & VSW_IF_UP) { 184 185 /* Open a mac client and program addresses */ 186 rv = vsw_mac_client_init(vswp, NULL, VSW_LOCALDEV); 187 if (rv != 0) { 188 cmn_err(CE_NOTE, 189 "!vsw%d: failed to program interface " 190 "unicast address\n", vswp->instance); 191 } 192 193 /* 194 * Notify the MAC layer of the changed address. 195 */ 196 if (rv == 0) { 197 mac_unicst_update(vswp->if_mh, 198 (uint8_t *)&vswp->if_addr); 199 } 200 201 } 202 203 RW_EXIT(&vswp->if_lockrw); 204 205 WRITE_ENTER(&plist->lockrw); 206 207 /* program unicast address of ports in the network device */ 208 for (port = plist->head; port != NULL; port = port->p_next) { 209 if (port->addr_set) /* addr already set */ 210 continue; 211 212 /* Open a mac client and program addresses */ 213 rv = vsw_mac_client_init(vswp, port, VSW_VNETPORT); 214 if (rv != 0) { 215 cmn_err(CE_NOTE, 216 "!vsw%d: failed to program port(%d) " 217 "unicast address\n", vswp->instance, 218 port->p_instance); 219 } 220 } 221 /* announce macaddr of vnets to the physical switch */ 222 if (vsw_publish_macaddr_count != 0) { /* enabled */ 223 for (port = plist->head; port != NULL; port = port->p_next) { 224 vsw_publish_macaddr(vswp, port); 225 } 226 } 227 228 RW_EXIT(&plist->lockrw); 229 } 230 231 /* 232 * Remove unicast, multicast addresses and close mac clients 233 * for the vsw interface and all ports. 234 */ 235 void 236 vsw_unset_addrs(vsw_t *vswp) 237 { 238 READ_ENTER(&vswp->if_lockrw); 239 if (vswp->if_state & VSW_IF_UP) { 240 241 /* Cleanup and close the mac client for the interface */ 242 vsw_mac_client_cleanup(vswp, NULL, VSW_LOCALDEV); 243 } 244 RW_EXIT(&vswp->if_lockrw); 245 246 /* Cleanup and close the mac clients for all ports */ 247 vsw_mac_cleanup_ports(vswp); 248 } 249 250 /* 251 * Open the underlying network device for access in layer2 mode. 252 * Returns: 253 * 0 on success 254 * EAGAIN if mac_open() fails due to the device being not available yet. 255 * EIO on any other failures. 256 */ 257 int 258 vsw_mac_open(vsw_t *vswp) 259 { 260 int rv; 261 262 ASSERT(MUTEX_HELD(&vswp->mac_lock)); 263 264 if (vswp->mh != NULL) { 265 /* already open */ 266 return (0); 267 } 268 269 if (vswp->mac_open_retries++ >= vsw_mac_open_retries) { 270 /* exceeded max retries */ 271 return (EIO); 272 } 273 274 if ((rv = mac_open_by_linkname(vswp->physname, &vswp->mh)) != 0) { 275 /* 276 * If mac_open() failed and the error indicates that either 277 * the dlmgmtd door or the device is not available yet, we 278 * return EAGAIN to indicate that mac_open() needs to be 279 * retried. For example, this may happen during boot up, if 280 * the required link aggregation groups(devices) have not 281 * been created yet. 282 */ 283 if (rv == ENOENT || rv == EBADF) { 284 return (EAGAIN); 285 } else { 286 cmn_err(CE_WARN, "!vsw%d: mac_open %s failed rv:%x\n", 287 vswp->instance, vswp->physname, rv); 288 return (EIO); 289 } 290 } 291 vswp->mac_open_retries = 0; 292 293 vsw_mac_set_mtu(vswp, vswp->mtu); 294 295 rv = vsw_notify_add(vswp); 296 if (rv != 0) { 297 cmn_err(CE_CONT, "!vsw%d: mac_notify_add %s failed rv:%x\n", 298 vswp->instance, vswp->physname, rv); 299 } 300 301 return (0); 302 } 303 304 /* 305 * Close the underlying physical device. 306 */ 307 void 308 vsw_mac_close(vsw_t *vswp) 309 { 310 ASSERT(MUTEX_HELD(&vswp->mac_lock)); 311 312 if (vswp->mh != NULL) { 313 if (vswp->mnh != 0) { 314 (void) vsw_notify_rem(vswp); 315 vswp->mnh = 0; 316 } 317 if (vswp->mtu != vswp->mtu_physdev_orig) { 318 vsw_mac_set_mtu(vswp, vswp->mtu_physdev_orig); 319 } 320 mac_close(vswp->mh); 321 vswp->mh = NULL; 322 } 323 } 324 325 /* 326 * Add multicast addr. 327 */ 328 int 329 vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p, 330 int type) 331 { 332 int ret = 0; 333 mac_client_handle_t mch; 334 335 WRITE_MACCL_ENTER(vswp, port, type); 336 337 mch = (type == VSW_LOCALDEV) ? vswp->mch : port->p_mch; 338 339 if (mch != NULL) { 340 ret = mac_multicast_add(mch, mcst_p->mca.ether_addr_octet); 341 if (ret != 0) { 342 cmn_err(CE_WARN, "!vsw%d: unable to " 343 "program multicast address(%s) err=%d", 344 vswp->instance, 345 ether_sprintf((void *)&mcst_p->mca), ret); 346 RW_MACCL_EXIT(vswp, port, type); 347 return (ret); 348 } 349 mcst_p->mac_added = B_TRUE; 350 } 351 352 RW_MACCL_EXIT(vswp, port, type); 353 return (ret); 354 } 355 356 /* 357 * Remove multicast addr. 358 */ 359 void 360 vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p, 361 int type) 362 { 363 mac_client_handle_t mch; 364 365 WRITE_MACCL_ENTER(vswp, port, type); 366 mch = (type == VSW_LOCALDEV) ? vswp->mch : port->p_mch; 367 368 if (mch != NULL && mcst_p->mac_added) { 369 mac_multicast_remove(mch, mcst_p->mca.ether_addr_octet); 370 mcst_p->mac_added = B_FALSE; 371 } 372 RW_MACCL_EXIT(vswp, port, type); 373 } 374 375 376 /* 377 * Add all multicast addresses of the port. 378 */ 379 static void 380 vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type) 381 { 382 mcst_addr_t *mcap; 383 mac_client_handle_t mch; 384 kmutex_t *mca_lockp; 385 int rv; 386 387 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); 388 if (type == VSW_LOCALDEV) { 389 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock)); 390 mch = vswp->mch; 391 mcap = vswp->mcap; 392 mca_lockp = &vswp->mca_lock; 393 } else { 394 ASSERT(RW_WRITE_HELD(&portp->maccl_rwlock)); 395 mch = portp->p_mch; 396 mcap = portp->mcap; 397 mca_lockp = &portp->mca_lock; 398 } 399 400 if (mch == NULL) 401 return; 402 403 mutex_enter(mca_lockp); 404 for (mcap = mcap; mcap != NULL; mcap = mcap->nextp) { 405 if (mcap->mac_added) 406 continue; 407 rv = mac_multicast_add(mch, (uchar_t *)&mcap->mca); 408 if (rv == 0) { 409 mcap->mac_added = B_TRUE; 410 } else { 411 cmn_err(CE_WARN, "!vsw%d: unable to program " 412 "multicast address(%s) err=%d", vswp->instance, 413 ether_sprintf((void *)&mcap->mca), rv); 414 } 415 } 416 mutex_exit(mca_lockp); 417 } 418 419 /* 420 * Remove all multicast addresses of the port. 421 */ 422 static void 423 vsw_mac_multicast_remove_all(vsw_t *vswp, vsw_port_t *portp, int type) 424 { 425 mac_client_handle_t mch; 426 mcst_addr_t *mcap; 427 kmutex_t *mca_lockp; 428 429 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); 430 if (type == VSW_LOCALDEV) { 431 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock)); 432 mch = vswp->mch; 433 mcap = vswp->mcap; 434 mca_lockp = &vswp->mca_lock; 435 } else { 436 ASSERT(RW_WRITE_HELD(&portp->maccl_rwlock)); 437 mch = portp->p_mch; 438 mcap = portp->mcap; 439 mca_lockp = &portp->mca_lock; 440 } 441 442 if (mch == NULL) 443 return; 444 445 mutex_enter(mca_lockp); 446 for (; mcap != NULL; mcap = mcap->nextp) { 447 if (!mcap->mac_added) 448 continue; 449 (void) mac_multicast_remove(mch, (uchar_t *)&mcap->mca); 450 mcap->mac_added = B_FALSE; 451 } 452 mutex_exit(mca_lockp); 453 } 454 455 /* 456 * Open a mac client and program uncast and multicast addresses 457 * for a port or the interface. 458 * Returns: 459 * 0 on success 460 * non-zero for failure. 461 */ 462 int 463 vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type) 464 { 465 int rv; 466 467 mutex_enter(&vswp->mac_lock); 468 WRITE_MACCL_ENTER(vswp, port, type); 469 rv = vsw_maccl_open(vswp, port, type); 470 471 /* Release mac_lock now */ 472 mutex_exit(&vswp->mac_lock); 473 474 if (rv == 0) { 475 (void) vsw_set_hw(vswp, port, type); 476 vsw_mac_multicast_add_all(vswp, port, type); 477 } 478 RW_MACCL_EXIT(vswp, port, type); 479 return (rv); 480 } 481 482 /* 483 * Open a MAC client for a port or an interface. 484 * The flags and their purpose as below: 485 * 486 * MAC_OPEN_FLAGS_NO_HWRINGS -- This flag is used by default 487 * for all ports/interface so that they are associated with 488 * default group & resources. It will not be used for the 489 * ports that have HybridIO is enabled so that the h/w resources 490 * assigned to it. 491 * 492 * MAC_OPEN_FLAGS_SHARES_DESIRED -- This flag is used to indicate 493 * that a port desires a Share. This will be the case with the 494 * the ports that have hybrid mode enabled. This will only cause 495 * MAC layer to allocate a share and corresponding resources 496 * ahead of time. 497 * 498 * MAC_UNICAST_TAG_DISABLE -- This flag is used for VLAN 499 * support. It will cause MAC to not add any tags, but expect 500 * vsw to tag the packets. 501 * 502 * MAC_UNICAST_STRIP_DISABLE -- This flag is used for VLAN 503 * support. It will case the MAC layer to not strip the tags. 504 * Vsw may have to strip the tag for pvid case. 505 */ 506 static int 507 vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type) 508 { 509 int rv = 0; 510 int instance; 511 char mac_cl_name[MAXNAMELEN]; 512 const char *dev_name; 513 mac_client_handle_t *mchp; 514 uint64_t flags = MAC_OPEN_FLAGS_NO_HWRINGS; 515 516 ASSERT(MUTEX_HELD(&vswp->mac_lock)); 517 if (vswp->mh == NULL) { 518 /* 519 * In case net-dev is changed (either set to nothing or 520 * using aggregation device), return success here as the 521 * timeout mechanism will handle it. 522 */ 523 return (0); 524 } 525 526 mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch; 527 if (*mchp != NULL) { 528 /* already open */ 529 return (0); 530 } 531 dev_name = ddi_driver_name(vswp->dip); 532 instance = ddi_get_instance(vswp->dip); 533 if (type == VSW_VNETPORT) { 534 if (port->p_hio_enabled == B_TRUE) { 535 flags &= ~MAC_OPEN_FLAGS_NO_HWRINGS; 536 flags |= MAC_OPEN_FLAGS_SHARES_DESIRED; 537 } 538 (void) snprintf(mac_cl_name, MAXNAMELEN, "%s%d%s%d", dev_name, 539 instance, "_port", port->p_instance); 540 } else { 541 (void) snprintf(mac_cl_name, MAXNAMELEN, "%s%s%d", 542 dev_name, "_if", instance); 543 } 544 545 rv = mac_client_open(vswp->mh, mchp, mac_cl_name, flags); 546 if (rv != 0) { 547 cmn_err(CE_NOTE, "!vsw%d:%s mac_client_open() failed\n", 548 vswp->instance, mac_cl_name); 549 } 550 return (rv); 551 } 552 553 /* 554 * Clean up by removing uncast, multicast addresses and 555 * closing the MAC client for a port or the interface. 556 */ 557 void 558 vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type) 559 { 560 WRITE_MACCL_ENTER(vswp, port, type); 561 vsw_unset_hw(vswp, port, type); 562 vsw_maccl_close(vswp, port, type); 563 vsw_mac_multicast_remove_all(vswp, port, type); 564 RW_MACCL_EXIT(vswp, port, type); 565 } 566 567 /* 568 * Close a MAC client for a port or an interface. 569 */ 570 static void 571 vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type) 572 { 573 mac_client_handle_t *mchp; 574 575 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); 576 577 mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch; 578 if (*mchp != NULL) { 579 mac_client_close(*mchp, 0); 580 *mchp = NULL; 581 } 582 } 583 584 /* 585 * Cleanup MAC client related stuff for all ports. 586 */ 587 void 588 vsw_mac_cleanup_ports(vsw_t *vswp) 589 { 590 vsw_port_list_t *plist = &vswp->plist; 591 vsw_port_t *port; 592 593 READ_ENTER(&plist->lockrw); 594 for (port = plist->head; port != NULL; port = port->p_next) { 595 vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT); 596 } 597 RW_EXIT(&plist->lockrw); 598 } 599 600 /* 601 * Depending on the mode specified, the capabilites and capacity 602 * of the underlying device setup the physical device. 603 * 604 * If in layer 3 mode, then do nothing. 605 * 606 * If in layer 2 mode, open a mac client and program the mac-address 607 * and vlan-ids. The MAC layer will take care of programming 608 * the address into h/w or set the h/w into promiscuous mode. 609 * 610 * Returns 0 success, 1 on failure. 611 */ 612 int 613 vsw_set_hw(vsw_t *vswp, vsw_port_t *port, int type) 614 { 615 int err = 1; 616 617 D1(vswp, "%s: enter", __func__); 618 619 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); 620 621 if (vswp->smode == VSW_LAYER3) 622 return (0); 623 624 if (type == VSW_VNETPORT) { 625 ASSERT(port != NULL); 626 err = vsw_set_port_hw_addr(port); 627 } else { 628 err = vsw_set_if_hw_addr(vswp); 629 } 630 631 D1(vswp, "%s: exit", __func__); 632 return (err); 633 } 634 635 /* 636 * If in layer 3 mode do nothing. 637 * 638 * If in layer 2 switched mode remove the address from the physical 639 * device. 640 * 641 * If in layer 2 promiscuous mode disable promisc mode. 642 * 643 * Returns 0 on success. 644 */ 645 void 646 vsw_unset_hw(vsw_t *vswp, vsw_port_t *port, int type) 647 { 648 D1(vswp, "%s: enter", __func__); 649 650 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); 651 652 if (vswp->smode == VSW_LAYER3) 653 return; 654 655 if (type == VSW_VNETPORT) { 656 ASSERT(port != NULL); 657 vsw_unset_hw_addr(vswp, port, type); 658 } else { 659 vsw_unset_hw_addr(vswp, NULL, type); 660 } 661 662 D1(vswp, "%s: exit", __func__); 663 } 664 665 /* 666 * Program the macaddress and vlans of a port. 667 * 668 * Returns 0 on sucess, 1 on failure. 669 */ 670 static int 671 vsw_set_port_hw_addr(vsw_port_t *port) 672 { 673 vsw_t *vswp = port->p_vswp; 674 mac_diag_t diag; 675 uint8_t *macaddr; 676 uint16_t vid = VLAN_ID_NONE; 677 int rv; 678 uint16_t mac_flags = MAC_UNICAST_TAG_DISABLE | 679 MAC_UNICAST_STRIP_DISABLE; 680 681 D1(vswp, "%s: enter", __func__); 682 683 ASSERT(RW_WRITE_HELD(&port->maccl_rwlock)); 684 if (port->p_mch == NULL) 685 return (0); 686 687 /* 688 * If the port has a specific 'pvid', then 689 * register with that vlan-id, otherwise register 690 * with VLAN_ID_NONE. 691 */ 692 if (port->pvid != vswp->default_vlan_id) { 693 vid = port->pvid; 694 } 695 macaddr = (uint8_t *)port->p_macaddr.ether_addr_octet; 696 697 if (!(vswp->smode & VSW_LAYER2_PROMISC)) { 698 mac_flags |= MAC_UNICAST_HW; 699 } 700 701 if (port->addr_set == B_FALSE) { 702 port->p_muh = NULL; 703 rv = mac_unicast_add(port->p_mch, macaddr, mac_flags, 704 &port->p_muh, vid, &diag); 705 706 if (rv != 0) { 707 cmn_err(CE_WARN, "vsw%d: Failed to program" 708 "macaddr,vid(%s, %d) err=%d", 709 vswp->instance, ether_sprintf((void *)macaddr), 710 vid, rv); 711 return (rv); 712 } 713 port->addr_set = B_TRUE; 714 715 D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s", 716 __func__, ether_sprintf((void *)macaddr), vid, 717 vswp->physname); 718 } 719 720 /* Add vlans to the MAC layer */ 721 vsw_mac_add_vlans(vswp, port->p_mch, macaddr, 722 mac_flags, port->vids, port->nvids); 723 724 mac_rx_set(port->p_mch, vsw_port_rx_cb, (void *)port); 725 726 D1(vswp, "%s: exit", __func__); 727 return (rv); 728 } 729 730 /* 731 * Program the macaddress and vlans of a port. 732 * 733 * Returns 0 on sucess, 1 on failure. 734 */ 735 static int 736 vsw_set_if_hw_addr(vsw_t *vswp) 737 { 738 mac_diag_t diag; 739 uint8_t *macaddr; 740 uint8_t primary_addr[ETHERADDRL]; 741 uint16_t vid = VLAN_ID_NONE; 742 int rv; 743 uint16_t mac_flags = MAC_UNICAST_TAG_DISABLE | 744 MAC_UNICAST_STRIP_DISABLE; 745 746 D1(vswp, "%s: enter", __func__); 747 748 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock)); 749 if (vswp->mch == NULL) 750 return (0); 751 752 macaddr = (uint8_t *)vswp->if_addr.ether_addr_octet; 753 754 /* check if it is the primary macaddr of the card. */ 755 mac_unicast_primary_get(vswp->mh, primary_addr); 756 if (ether_cmp((void *)primary_addr, (void*)macaddr) == 0) { 757 mac_flags |= MAC_UNICAST_PRIMARY; 758 } 759 760 /* 761 * If the interface has a specific 'pvid', then 762 * register with that vlan-id, otherwise register 763 * with VLAN_ID_NONE. 764 */ 765 if (vswp->pvid != vswp->default_vlan_id) { 766 vid = vswp->pvid; 767 } 768 769 if (!(vswp->smode & VSW_LAYER2_PROMISC)) { 770 mac_flags |= MAC_UNICAST_HW; 771 } 772 773 if (vswp->addr_set == B_FALSE) { 774 vswp->muh = NULL; 775 rv = mac_unicast_add(vswp->mch, macaddr, mac_flags, 776 &vswp->muh, vid, &diag); 777 778 if (rv != 0) { 779 cmn_err(CE_WARN, "vsw%d: Failed to program" 780 "macaddr,vid(%s, %d) err=%d", 781 vswp->instance, ether_sprintf((void *)macaddr), 782 vid, rv); 783 return (rv); 784 } 785 vswp->addr_set = B_TRUE; 786 787 D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s", 788 __func__, ether_sprintf((void *)macaddr), vid, 789 vswp->physname); 790 } 791 792 vsw_mac_add_vlans(vswp, vswp->mch, macaddr, mac_flags, 793 vswp->vids, vswp->nvids); 794 795 mac_rx_set(vswp->mch, vsw_if_rx_cb, (void *)vswp); 796 797 D1(vswp, "%s: exit", __func__); 798 return (rv); 799 } 800 801 /* 802 * Remove a unicast mac address which has previously been programmed 803 * into HW. 804 * 805 * Returns 0 on sucess, 1 on failure. 806 */ 807 static void 808 vsw_unset_hw_addr(vsw_t *vswp, vsw_port_t *port, int type) 809 { 810 vsw_vlanid_t *vids; 811 int nvids; 812 mac_client_handle_t mch = NULL; 813 814 D1(vswp, "%s: enter", __func__); 815 816 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); 817 818 if (type == VSW_VNETPORT) { 819 ASSERT(port != NULL); 820 ASSERT(RW_WRITE_HELD(&port->maccl_rwlock)); 821 vids = port->vids; 822 nvids = port->nvids; 823 } else { 824 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock)); 825 vids = vswp->vids; 826 nvids = vswp->nvids; 827 } 828 829 /* First clear the callback */ 830 if (type == VSW_LOCALDEV) { 831 mch = vswp->mch; 832 } else if (type == VSW_VNETPORT) { 833 mch = port->p_mch; 834 } 835 836 837 if (mch == NULL) { 838 return; 839 } 840 841 mac_rx_clear(mch); 842 843 /* Remove vlans */ 844 vsw_mac_remove_vlans(mch, vids, nvids); 845 846 if ((type == VSW_LOCALDEV) && (vswp->addr_set == B_TRUE)) { 847 (void) mac_unicast_remove(vswp->mch, vswp->muh); 848 vswp->muh = NULL; 849 D2(vswp, "removed vsw interface mac-addr from " 850 "the device %s", vswp->physname); 851 vswp->addr_set = B_FALSE; 852 853 } else if ((type == VSW_VNETPORT) && (port->addr_set == B_TRUE)) { 854 (void) mac_unicast_remove(port->p_mch, port->p_muh); 855 port->p_muh = NULL; 856 D2(vswp, "removed port(0x%p) mac-addr from " 857 "the device %s", port, vswp->physname); 858 port->addr_set = B_FALSE; 859 } 860 861 D1(vswp, "%s: exit", __func__); 862 } 863 864 /* 865 * receive callback routine for vsw interface. Invoked by MAC layer when there 866 * are pkts being passed up from physical device for this vsw interface. 867 */ 868 /* ARGSUSED */ 869 static void 870 vsw_if_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 871 boolean_t loopback) 872 { 873 _NOTE(ARGUNUSED(mrh)) 874 875 vsw_t *vswp = (vsw_t *)arg; 876 mblk_t *mpt; 877 int count; 878 879 ASSERT(vswp != NULL); 880 881 D1(vswp, "%s: enter", __func__); 882 883 READ_ENTER(&vswp->if_lockrw); 884 if (vswp->if_state & VSW_IF_UP) { 885 RW_EXIT(&vswp->if_lockrw); 886 count = vsw_vlan_frame_untag(vswp, VSW_LOCALDEV, &mp, &mpt); 887 if (count != 0) { 888 mac_rx(vswp->if_mh, NULL, mp); 889 } 890 } else { 891 RW_EXIT(&vswp->if_lockrw); 892 freemsgchain(mp); 893 } 894 895 D1(vswp, "%s: exit", __func__); 896 } 897 898 /* 899 * receive callback routine for port. Invoked by MAC layer when there 900 * are pkts being passed up from physical device for this port. 901 */ 902 /* ARGSUSED */ 903 static void 904 vsw_port_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 905 boolean_t loopback) 906 { 907 _NOTE(ARGUNUSED(mrh)) 908 909 vsw_t *vswp; 910 vsw_port_t *port = arg; 911 912 ASSERT(port != NULL); 913 914 vswp = port->p_vswp; 915 916 D1(vswp, "vsw_port_rx_cb: enter"); 917 918 /* 919 * Send the packets to the peer directly. 920 */ 921 (void) vsw_portsend(port, mp); 922 923 D1(vswp, "vsw_port_rx_cb: exit"); 924 } 925 926 /* 927 * Send a message out over the physical device 928 * via the MAC layer. 929 * 930 * Returns any mblks that it was unable to transmit. 931 */ 932 mblk_t * 933 vsw_tx_msg(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port) 934 { 935 mac_client_handle_t mch; 936 mac_unicast_handle_t muh; 937 938 READ_MACCL_ENTER(vswp, port, caller); 939 940 mch = (caller == VSW_LOCALDEV) ? vswp->mch : port->p_mch; 941 muh = (caller == VSW_LOCALDEV) ? vswp->muh : port->p_muh; 942 943 if (mch == NULL || muh == NULL) { 944 RW_MACCL_EXIT(vswp, port, caller); 945 return (mp); 946 } 947 948 /* packets are sent or dropped */ 949 (void) mac_tx(mch, mp, 0, MAC_DROP_ON_NO_DESC, NULL); 950 RW_MACCL_EXIT(vswp, port, caller); 951 return (NULL); 952 } 953 954 /* 955 * vsw_port_mac_reconfig -- Cleanup and close the MAC client 956 * and reopen and re-configure the MAC client with new flags etc. 957 * This function is useful for two different purposes: 958 * 1) To update the MAC client with new vlan-ids. This is done 959 * by freeing the existing vlan-ids and reopen with the new 960 * vlan-ids. 961 * 962 * 2) If the Hybrid mode status of a port changes, then the 963 * MAC client need to be closed and re-opened, otherwise, 964 * Share related resources may not be freed(hybird mode disabled) 965 * or assigned(hybrid mode enabled). To accomplish this, 966 * this function simply closes and reopens the MAC client. 967 * The reopen will result in using the flags based on the 968 * new hybrid mode of the port. 969 */ 970 void 971 vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans, 972 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids) 973 { 974 vsw_t *vswp = portp->p_vswp; 975 int rv; 976 977 D1(vswp, "%s: enter", __func__); 978 /* 979 * Remove the multi-cast addresses, unicast address 980 * and close the mac-client. 981 */ 982 mutex_enter(&vswp->mac_lock); 983 WRITE_ENTER(&portp->maccl_rwlock); 984 vsw_mac_multicast_remove_all(vswp, portp, VSW_VNETPORT); 985 vsw_unset_hw(vswp, portp, VSW_VNETPORT); 986 vsw_maccl_close(vswp, portp, VSW_VNETPORT); 987 988 if (update_vlans == B_TRUE) { 989 if (portp->nvids != 0) { 990 kmem_free(portp->vids, 991 sizeof (vsw_vlanid_t) * portp->nvids); 992 portp->vids = NULL; 993 portp->nvids = 0; 994 } 995 portp->vids = new_vids; 996 portp->nvids = new_nvids; 997 portp->pvid = new_pvid; 998 } 999 1000 /* 1001 * Now re-open the mac-client and 1002 * configure unicast addr and multicast addrs. 1003 */ 1004 rv = vsw_maccl_open(vswp, portp, VSW_VNETPORT); 1005 if (rv != 0) { 1006 goto recret; 1007 } 1008 1009 if (vsw_set_hw(vswp, portp, VSW_VNETPORT)) { 1010 cmn_err(CE_NOTE, "!vsw%d: port:%d failed to " 1011 "set unicast address\n", vswp->instance, portp->p_instance); 1012 goto recret; 1013 } 1014 1015 vsw_mac_multicast_add_all(vswp, portp, VSW_VNETPORT); 1016 1017 recret: 1018 RW_EXIT(&portp->maccl_rwlock); 1019 mutex_exit(&vswp->mac_lock); 1020 D1(vswp, "%s: exit", __func__); 1021 } 1022 1023 /* 1024 * vsw_if_mac_reconfig -- Reconfigure the vsw interfaace's mac-client 1025 * by closing and re-opening it. This function is used handle the 1026 * following two cases: 1027 * 1028 * 1) Handle the MAC address change for the interface. 1029 * 2) Handle vlan update. 1030 */ 1031 void 1032 vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans, 1033 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids) 1034 { 1035 int rv; 1036 1037 D1(vswp, "%s: enter", __func__); 1038 /* 1039 * Remove the multi-cast addresses, unicast address 1040 * and close the mac-client. 1041 */ 1042 mutex_enter(&vswp->mac_lock); 1043 WRITE_ENTER(&vswp->maccl_rwlock); 1044 vsw_mac_multicast_remove_all(vswp, NULL, VSW_LOCALDEV); 1045 vsw_unset_hw(vswp, NULL, VSW_LOCALDEV); 1046 vsw_maccl_close(vswp, NULL, VSW_LOCALDEV); 1047 1048 if (update_vlans == B_TRUE) { 1049 if (vswp->nvids != 0) { 1050 kmem_free(vswp->vids, 1051 sizeof (vsw_vlanid_t) * vswp->nvids); 1052 vswp->vids = NULL; 1053 vswp->nvids = 0; 1054 } 1055 vswp->vids = new_vids; 1056 vswp->nvids = new_nvids; 1057 vswp->pvid = new_pvid; 1058 } 1059 1060 /* 1061 * Now re-open the mac-client and 1062 * configure unicast addr and multicast addrs. 1063 */ 1064 rv = vsw_maccl_open(vswp, NULL, VSW_LOCALDEV); 1065 if (rv != 0) { 1066 goto ifrecret; 1067 } 1068 1069 if (vsw_set_hw(vswp, NULL, VSW_LOCALDEV)) { 1070 cmn_err(CE_NOTE, "!vsw%d:failed to set unicast address\n", 1071 vswp->instance); 1072 goto ifrecret; 1073 } 1074 1075 vsw_mac_multicast_add_all(vswp, NULL, VSW_LOCALDEV); 1076 1077 ifrecret: 1078 RW_EXIT(&vswp->maccl_rwlock); 1079 mutex_exit(&vswp->mac_lock); 1080 D1(vswp, "%s: exit", __func__); 1081 } 1082 1083 /* 1084 * vsw_mac_port_reconfig_vlans -- Reconfigure a port to handle 1085 * vlan configuration update. As the removal of the last unicast-address,vid 1086 * from the MAC client results in releasing all resources, it expects 1087 * no Shares to be associated with such MAC client. 1088 * 1089 * To handle vlan configuration update for a port that already has 1090 * a Share bound, then we need to free that share prior to reconfiguration. 1091 * Initiate the hybrdIO setup again after the completion of reconfiguration. 1092 */ 1093 void 1094 vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid, 1095 vsw_vlanid_t *new_vids, int new_nvids) 1096 { 1097 /* 1098 * As the reconfiguration involves the close of 1099 * mac client, cleanup HybridIO and later restart 1100 * HybridIO setup again. 1101 */ 1102 if (portp->p_hio_enabled == B_TRUE) { 1103 vsw_hio_stop_port(portp); 1104 } 1105 vsw_port_mac_reconfig(portp, B_TRUE, new_pvid, new_vids, new_nvids); 1106 if (portp->p_hio_enabled == B_TRUE) { 1107 /* reset to setup the HybridIO again. */ 1108 vsw_hio_port_reset(portp, B_FALSE); 1109 } 1110 } 1111 1112 /* Add vlans to MAC client */ 1113 static void 1114 vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch, uint8_t *macaddr, 1115 uint16_t flags, vsw_vlanid_t *vids, int nvids) 1116 { 1117 vsw_vlanid_t *vidp; 1118 mac_diag_t diag; 1119 int rv; 1120 int i; 1121 1122 flags |= MAC_UNICAST_TAG_DISABLE | MAC_UNICAST_STRIP_DISABLE; 1123 1124 /* Add vlans to the MAC layer */ 1125 for (i = 0; i < nvids; i++) { 1126 vidp = &vids[i]; 1127 1128 if (vidp->vl_set == B_TRUE) { 1129 continue; 1130 } 1131 1132 rv = mac_unicast_add(mch, macaddr, flags, 1133 &vidp->vl_muh, vidp->vl_vid, &diag); 1134 if (rv != 0) { 1135 cmn_err(CE_WARN, "vsw%d: Failed to program" 1136 "macaddr,vid(%s, %d) err=%d", 1137 vswp->instance, ether_sprintf((void *)macaddr), 1138 vidp->vl_vid, rv); 1139 } else { 1140 vidp->vl_set = B_TRUE; 1141 D2(vswp, "%s:programmed macaddr(%s) vid(%d) " 1142 "into device %s", __func__, 1143 ether_sprintf((void *)macaddr), 1144 vidp->vl_vid, vswp->physname); 1145 } 1146 } 1147 } 1148 1149 /* Remove vlans from the MAC client */ 1150 static void 1151 vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids, int nvids) 1152 { 1153 int i; 1154 vsw_vlanid_t *vidp; 1155 1156 for (i = 0; i < nvids; i++) { 1157 vidp = &vids[i]; 1158 if (vidp->vl_set == B_FALSE) { 1159 continue; 1160 } 1161 mac_unicast_remove(mch, vidp->vl_muh); 1162 vidp->vl_set = B_FALSE; 1163 } 1164 } 1165 1166 #define ARH_FIXED_LEN 8 /* Length of fixed part of ARP header(see arp.h) */ 1167 1168 /* 1169 * Send a gratuitous RARP packet to notify the physical switch to update its 1170 * Layer2 forwarding table for the given mac address. This is done to allow the 1171 * switch to quickly learn the macaddr-port association when a guest is live 1172 * migrated or when vsw's physical device is changed dynamically. Any protocol 1173 * packet would serve this purpose, but we choose RARP, as it allows us to 1174 * accomplish this within L2 (ie, no need to specify IP addr etc in the packet) 1175 * The macaddr of vnet is retained across migration. Hence, we don't need to 1176 * update the arp cache of other hosts within the broadcast domain. Note that 1177 * it is harmless to send these RARP packets during normal port attach of a 1178 * client vnet. This can can be turned off if needed, by setting 1179 * vsw_publish_macaddr_count to zero in /etc/system. 1180 */ 1181 void 1182 vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp) 1183 { 1184 mblk_t *mp; 1185 mblk_t *bp; 1186 struct arphdr *arh; 1187 struct ether_header *ehp; 1188 int count = 0; 1189 int plen = 4; 1190 uint8_t *cp; 1191 1192 mp = allocb(ETHERMIN, BPRI_MED); 1193 if (mp == NULL) { 1194 return; 1195 } 1196 1197 /* Initialize eth header */ 1198 ehp = (struct ether_header *)mp->b_rptr; 1199 bcopy(ðerbroadcastaddr, &ehp->ether_dhost, ETHERADDRL); 1200 bcopy(&portp->p_macaddr, &ehp->ether_shost, ETHERADDRL); 1201 ehp->ether_type = htons(ETHERTYPE_REVARP); 1202 1203 /* Initialize arp packet */ 1204 arh = (struct arphdr *)(mp->b_rptr + sizeof (struct ether_header)); 1205 cp = (uint8_t *)arh; 1206 1207 arh->ar_hrd = htons(ARPHRD_ETHER); /* Hardware type: ethernet */ 1208 arh->ar_pro = htons(ETHERTYPE_IP); /* Protocol type: IP */ 1209 arh->ar_hln = ETHERADDRL; /* Length of hardware address: 6 */ 1210 arh->ar_pln = plen; /* Length of protocol address: 4 */ 1211 arh->ar_op = htons(REVARP_REQUEST); /* Opcode: REVARP Request */ 1212 1213 cp += ARH_FIXED_LEN; 1214 1215 /* Sender's hardware address and protocol address */ 1216 bcopy(&portp->p_macaddr, cp, ETHERADDRL); 1217 cp += ETHERADDRL; 1218 bzero(cp, plen); /* INADDR_ANY */ 1219 cp += plen; 1220 1221 /* Target hardware address and protocol address */ 1222 bcopy(&portp->p_macaddr, cp, ETHERADDRL); 1223 cp += ETHERADDRL; 1224 bzero(cp, plen); /* INADDR_ANY */ 1225 cp += plen; 1226 1227 mp->b_wptr += ETHERMIN; /* total size is 42; round up to ETHERMIN */ 1228 1229 for (count = 0; count < vsw_publish_macaddr_count; count++) { 1230 1231 bp = dupmsg(mp); 1232 if (bp == NULL) { 1233 continue; 1234 } 1235 1236 /* transmit the packet */ 1237 bp = vsw_tx_msg(vswp, bp, VSW_VNETPORT, portp); 1238 if (bp != NULL) { 1239 freemsg(bp); 1240 } 1241 } 1242 1243 freemsg(mp); 1244 } 1245 1246 static void 1247 vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu) 1248 { 1249 uint_t mtu_orig; 1250 int rv; 1251 1252 rv = mac_set_mtu(vswp->mh, mtu, &mtu_orig); 1253 if (rv != 0) { 1254 cmn_err(CE_NOTE, 1255 "!vsw%d: Unable to set the mtu:%d, in the " 1256 "physical device:%s\n", 1257 vswp->instance, mtu, vswp->physname); 1258 return; 1259 } 1260 1261 /* save the original mtu of physdev to reset it back later if needed */ 1262 vswp->mtu_physdev_orig = mtu_orig; 1263 } 1264 1265 /* 1266 * Register a callback with underlying mac layer for notifications. 1267 * We are currently interested in only link-state events. 1268 */ 1269 static int 1270 vsw_notify_add(vsw_t *vswp) 1271 { 1272 mac_notify_handle_t mnh; 1273 uint32_t note; 1274 1275 /* 1276 * Check if the underlying MAC supports link update notification. 1277 */ 1278 note = mac_no_notification(vswp->mh); 1279 if ((note & (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN)) != 0) { 1280 vswp->phys_no_link_update = B_TRUE; 1281 } else { 1282 vswp->phys_no_link_update = B_FALSE; 1283 } 1284 1285 /* 1286 * Read the current link state of the device and cache it. 1287 */ 1288 vswp->phys_link_state = vswp->phys_no_link_update ? LINK_STATE_UP : 1289 mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE); 1290 1291 /* 1292 * Add notify callback function, if link update is supported. 1293 */ 1294 if (vswp->phys_no_link_update == B_TRUE) { 1295 return (0); 1296 } 1297 1298 mnh = mac_notify_add(vswp->mh, vsw_notify_cb, vswp); 1299 if (mnh == 0) { 1300 /* failed */ 1301 return (1); 1302 } 1303 1304 vswp->mnh = mnh; 1305 return (0); 1306 } 1307 1308 /* 1309 * Remove notify callback. 1310 */ 1311 static int 1312 vsw_notify_rem(vsw_t *vswp) 1313 { 1314 int rv; 1315 1316 rv = mac_notify_remove(vswp->mnh, B_FALSE); 1317 return (rv); 1318 } 1319 1320 /* 1321 * Notification callback invoked by the MAC service 1322 * module. Note that we process only link state updates. 1323 */ 1324 static void 1325 vsw_notify_cb(void *arg, mac_notify_type_t type) 1326 { 1327 vsw_t *vswp = arg; 1328 1329 switch (type) { 1330 1331 case MAC_NOTE_LINK: 1332 vsw_notify_link(vswp); 1333 break; 1334 1335 default: 1336 break; 1337 1338 } 1339 } 1340 1341 /* 1342 * Invoked upon receiving a MAC_NOTE_LINK 1343 * notification for the underlying physical device. 1344 */ 1345 static void 1346 vsw_notify_link(vsw_t *vswp) 1347 { 1348 link_state_t link_state; 1349 1350 /* link state change notification */ 1351 link_state = mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE); 1352 1353 if (vswp->phys_link_state != link_state) { 1354 D3(vswp, "%s: phys_link_state(%d)\n", 1355 __func__, vswp->phys_link_state); 1356 1357 vswp->phys_link_state = link_state; 1358 vsw_physlink_state_update(vswp); 1359 } 1360 } 1361