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