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