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