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 2008 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/param.h> 30 #include <sys/stream.h> 31 #include <sys/kmem.h> 32 #include <sys/conf.h> 33 #include <sys/devops.h> 34 #include <sys/ksynch.h> 35 #include <sys/stat.h> 36 #include <sys/modctl.h> 37 #include <sys/modhash.h> 38 #include <sys/debug.h> 39 #include <sys/ethernet.h> 40 #include <sys/dlpi.h> 41 #include <net/if.h> 42 #include <sys/mac.h> 43 #include <sys/mac_ether.h> 44 #include <sys/ddi.h> 45 #include <sys/sunddi.h> 46 #include <sys/strsun.h> 47 #include <sys/note.h> 48 #include <sys/atomic.h> 49 #include <sys/vnet.h> 50 #include <sys/vlan.h> 51 #include <sys/vnet_mailbox.h> 52 #include <sys/vnet_common.h> 53 #include <sys/dds.h> 54 #include <sys/strsubr.h> 55 #include <sys/taskq.h> 56 57 /* 58 * Function prototypes. 59 */ 60 61 /* DDI entrypoints */ 62 static int vnetdevinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 63 static int vnetattach(dev_info_t *, ddi_attach_cmd_t); 64 static int vnetdetach(dev_info_t *, ddi_detach_cmd_t); 65 66 /* MAC entrypoints */ 67 static int vnet_m_stat(void *, uint_t, uint64_t *); 68 static int vnet_m_start(void *); 69 static void vnet_m_stop(void *); 70 static int vnet_m_promisc(void *, boolean_t); 71 static int vnet_m_multicst(void *, boolean_t, const uint8_t *); 72 static int vnet_m_unicst(void *, const uint8_t *); 73 mblk_t *vnet_m_tx(void *, mblk_t *); 74 75 /* vnet internal functions */ 76 static int vnet_mac_register(vnet_t *); 77 static int vnet_read_mac_address(vnet_t *vnetp); 78 79 /* Forwarding database (FDB) routines */ 80 static void vnet_fdb_create(vnet_t *vnetp); 81 static void vnet_fdb_destroy(vnet_t *vnetp); 82 static vnet_res_t *vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp); 83 static void vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val); 84 void vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp); 85 static void vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp); 86 87 static void vnet_rx(vio_net_handle_t vrh, mblk_t *mp); 88 static void vnet_tx_update(vio_net_handle_t vrh); 89 static void vnet_res_start_task(void *arg); 90 static void vnet_start_resources(vnet_t *vnetp); 91 static void vnet_stop_resources(vnet_t *vnetp); 92 static void vnet_dispatch_res_task(vnet_t *vnetp); 93 static void vnet_res_start_task(void *arg); 94 static void vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err); 95 int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu); 96 97 /* Exported to to vnet_dds */ 98 int vnet_send_dds_msg(vnet_t *vnetp, void *dmsg); 99 100 /* Externs that are imported from vnet_gen */ 101 extern int vgen_init(void *vnetp, uint64_t regprop, dev_info_t *vnetdip, 102 const uint8_t *macaddr, void **vgenhdl); 103 extern int vgen_uninit(void *arg); 104 extern int vgen_dds_tx(void *arg, void *dmsg); 105 106 /* Externs that are imported from vnet_dds */ 107 extern void vdds_mod_init(void); 108 extern void vdds_mod_fini(void); 109 extern int vdds_init(vnet_t *vnetp); 110 extern void vdds_cleanup(vnet_t *vnetp); 111 extern void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg); 112 extern void vdds_cleanup_hybrid_res(void *arg); 113 114 #define VNET_FDBE_REFHOLD(p) \ 115 { \ 116 atomic_inc_32(&(p)->refcnt); \ 117 ASSERT((p)->refcnt != 0); \ 118 } 119 120 #define VNET_FDBE_REFRELE(p) \ 121 { \ 122 ASSERT((p)->refcnt != 0); \ 123 atomic_dec_32(&(p)->refcnt); \ 124 } 125 126 static mac_callbacks_t vnet_m_callbacks = { 127 0, 128 vnet_m_stat, 129 vnet_m_start, 130 vnet_m_stop, 131 vnet_m_promisc, 132 vnet_m_multicst, 133 vnet_m_unicst, 134 vnet_m_tx, 135 NULL, 136 NULL, 137 NULL 138 }; 139 140 /* 141 * Linked list of "vnet_t" structures - one per instance. 142 */ 143 static vnet_t *vnet_headp = NULL; 144 static krwlock_t vnet_rw; 145 146 /* Tunables */ 147 uint32_t vnet_ntxds = VNET_NTXDS; /* power of 2 transmit descriptors */ 148 uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */ 149 uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT; /* tx timeout in msec */ 150 uint32_t vnet_ldc_mtu = VNET_LDC_MTU; /* ldc mtu */ 151 152 /* 153 * Set this to non-zero to enable additional internal receive buffer pools 154 * based on the MTU of the device for better performance at the cost of more 155 * memory consumption. This is turned off by default, to use allocb(9F) for 156 * receive buffer allocations of sizes > 2K. 157 */ 158 boolean_t vnet_jumbo_rxpools = B_FALSE; 159 160 /* # of chains in fdb hash table */ 161 uint32_t vnet_fdb_nchains = VNET_NFDB_HASH; 162 163 /* Internal tunables */ 164 uint32_t vnet_ethermtu = 1500; /* mtu of the device */ 165 166 /* 167 * Default vlan id. This is only used internally when the "default-vlan-id" 168 * property is not present in the MD device node. Therefore, this should not be 169 * used as a tunable; if this value is changed, the corresponding variable 170 * should be updated to the same value in vsw and also other vnets connected to 171 * the same vsw. 172 */ 173 uint16_t vnet_default_vlan_id = 1; 174 175 /* delay in usec to wait for all references on a fdb entry to be dropped */ 176 uint32_t vnet_fdbe_refcnt_delay = 10; 177 178 static struct ether_addr etherbroadcastaddr = { 179 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 180 }; 181 182 183 /* 184 * Property names 185 */ 186 static char macaddr_propname[] = "local-mac-address"; 187 188 /* 189 * This is the string displayed by modinfo(1m). 190 */ 191 static char vnet_ident[] = "vnet driver"; 192 extern struct mod_ops mod_driverops; 193 static struct cb_ops cb_vnetops = { 194 nulldev, /* cb_open */ 195 nulldev, /* cb_close */ 196 nodev, /* cb_strategy */ 197 nodev, /* cb_print */ 198 nodev, /* cb_dump */ 199 nodev, /* cb_read */ 200 nodev, /* cb_write */ 201 nodev, /* cb_ioctl */ 202 nodev, /* cb_devmap */ 203 nodev, /* cb_mmap */ 204 nodev, /* cb_segmap */ 205 nochpoll, /* cb_chpoll */ 206 ddi_prop_op, /* cb_prop_op */ 207 NULL, /* cb_stream */ 208 (int)(D_MP) /* cb_flag */ 209 }; 210 211 static struct dev_ops vnetops = { 212 DEVO_REV, /* devo_rev */ 213 0, /* devo_refcnt */ 214 NULL, /* devo_getinfo */ 215 nulldev, /* devo_identify */ 216 nulldev, /* devo_probe */ 217 vnetattach, /* devo_attach */ 218 vnetdetach, /* devo_detach */ 219 nodev, /* devo_reset */ 220 &cb_vnetops, /* devo_cb_ops */ 221 (struct bus_ops *)NULL, /* devo_bus_ops */ 222 NULL, /* devo_power */ 223 ddi_quiesce_not_supported, /* devo_quiesce */ 224 }; 225 226 static struct modldrv modldrv = { 227 &mod_driverops, /* Type of module. This one is a driver */ 228 vnet_ident, /* ID string */ 229 &vnetops /* driver specific ops */ 230 }; 231 232 static struct modlinkage modlinkage = { 233 MODREV_1, (void *)&modldrv, NULL 234 }; 235 236 #ifdef DEBUG 237 238 /* 239 * Print debug messages - set to 0xf to enable all msgs 240 */ 241 int vnet_dbglevel = 0x8; 242 243 static void 244 debug_printf(const char *fname, void *arg, const char *fmt, ...) 245 { 246 char buf[512]; 247 va_list ap; 248 vnet_t *vnetp = (vnet_t *)arg; 249 char *bufp = buf; 250 251 if (vnetp == NULL) { 252 (void) sprintf(bufp, "%s: ", fname); 253 bufp += strlen(bufp); 254 } else { 255 (void) sprintf(bufp, "vnet%d:%s: ", vnetp->instance, fname); 256 bufp += strlen(bufp); 257 } 258 va_start(ap, fmt); 259 (void) vsprintf(bufp, fmt, ap); 260 va_end(ap); 261 cmn_err(CE_CONT, "%s\n", buf); 262 } 263 264 #endif 265 266 /* _init(9E): initialize the loadable module */ 267 int 268 _init(void) 269 { 270 int status; 271 272 DBG1(NULL, "enter\n"); 273 274 mac_init_ops(&vnetops, "vnet"); 275 status = mod_install(&modlinkage); 276 if (status != 0) { 277 mac_fini_ops(&vnetops); 278 } 279 vdds_mod_init(); 280 DBG1(NULL, "exit(%d)\n", status); 281 return (status); 282 } 283 284 /* _fini(9E): prepare the module for unloading. */ 285 int 286 _fini(void) 287 { 288 int status; 289 290 DBG1(NULL, "enter\n"); 291 292 status = mod_remove(&modlinkage); 293 if (status != 0) 294 return (status); 295 mac_fini_ops(&vnetops); 296 vdds_mod_fini(); 297 298 DBG1(NULL, "exit(%d)\n", status); 299 return (status); 300 } 301 302 /* _info(9E): return information about the loadable module */ 303 int 304 _info(struct modinfo *modinfop) 305 { 306 return (mod_info(&modlinkage, modinfop)); 307 } 308 309 /* 310 * attach(9E): attach a device to the system. 311 * called once for each instance of the device on the system. 312 */ 313 static int 314 vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd) 315 { 316 vnet_t *vnetp; 317 int status; 318 int instance; 319 uint64_t reg; 320 char qname[TASKQ_NAMELEN]; 321 enum { AST_init = 0x0, AST_vnet_alloc = 0x1, 322 AST_mac_alloc = 0x2, AST_read_macaddr = 0x4, 323 AST_vgen_init = 0x8, AST_fdbh_alloc = 0x10, 324 AST_vdds_init = 0x20, AST_taskq_create = 0x40, 325 AST_vnet_list = 0x80 } attach_state; 326 327 attach_state = AST_init; 328 329 switch (cmd) { 330 case DDI_ATTACH: 331 break; 332 case DDI_RESUME: 333 case DDI_PM_RESUME: 334 default: 335 goto vnet_attach_fail; 336 } 337 338 instance = ddi_get_instance(dip); 339 DBG1(NULL, "instance(%d) enter\n", instance); 340 341 /* allocate vnet_t and mac_t structures */ 342 vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP); 343 vnetp->dip = dip; 344 vnetp->instance = instance; 345 rw_init(&vnetp->vrwlock, NULL, RW_DRIVER, NULL); 346 rw_init(&vnetp->vsw_fp_rw, NULL, RW_DRIVER, NULL); 347 attach_state |= AST_vnet_alloc; 348 349 status = vdds_init(vnetp); 350 if (status != 0) { 351 goto vnet_attach_fail; 352 } 353 attach_state |= AST_vdds_init; 354 355 /* setup links to vnet_t from both devinfo and mac_t */ 356 ddi_set_driver_private(dip, (caddr_t)vnetp); 357 358 /* read the mac address */ 359 status = vnet_read_mac_address(vnetp); 360 if (status != DDI_SUCCESS) { 361 goto vnet_attach_fail; 362 } 363 attach_state |= AST_read_macaddr; 364 365 reg = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 366 DDI_PROP_DONTPASS, "reg", -1); 367 if (reg == -1) { 368 goto vnet_attach_fail; 369 } 370 vnetp->reg = reg; 371 372 vnet_fdb_create(vnetp); 373 attach_state |= AST_fdbh_alloc; 374 375 (void) snprintf(qname, TASKQ_NAMELEN, "vnet_taskq%d", instance); 376 if ((vnetp->taskqp = ddi_taskq_create(dip, qname, 1, 377 TASKQ_DEFAULTPRI, 0)) == NULL) { 378 cmn_err(CE_WARN, "!vnet%d: Unable to create task queue", 379 instance); 380 goto vnet_attach_fail; 381 } 382 attach_state |= AST_taskq_create; 383 384 /* add to the list of vnet devices */ 385 WRITE_ENTER(&vnet_rw); 386 vnetp->nextp = vnet_headp; 387 vnet_headp = vnetp; 388 RW_EXIT(&vnet_rw); 389 390 attach_state |= AST_vnet_list; 391 392 /* 393 * Initialize the generic vnet plugin which provides 394 * communication via sun4v LDC (logical domain channel) based 395 * resources. It will register the LDC resources as and when 396 * they become available. 397 */ 398 status = vgen_init(vnetp, reg, vnetp->dip, 399 (uint8_t *)vnetp->curr_macaddr, &vnetp->vgenhdl); 400 if (status != DDI_SUCCESS) { 401 DERR(vnetp, "vgen_init() failed\n"); 402 goto vnet_attach_fail; 403 } 404 attach_state |= AST_vgen_init; 405 406 /* register with MAC layer */ 407 status = vnet_mac_register(vnetp); 408 if (status != DDI_SUCCESS) { 409 goto vnet_attach_fail; 410 } 411 412 DBG1(NULL, "instance(%d) exit\n", instance); 413 return (DDI_SUCCESS); 414 415 vnet_attach_fail: 416 417 if (attach_state & AST_vnet_list) { 418 vnet_t **vnetpp; 419 /* unlink from instance(vnet_t) list */ 420 WRITE_ENTER(&vnet_rw); 421 for (vnetpp = &vnet_headp; *vnetpp; 422 vnetpp = &(*vnetpp)->nextp) { 423 if (*vnetpp == vnetp) { 424 *vnetpp = vnetp->nextp; 425 break; 426 } 427 } 428 RW_EXIT(&vnet_rw); 429 } 430 431 if (attach_state & AST_vdds_init) { 432 vdds_cleanup(vnetp); 433 } 434 if (attach_state & AST_taskq_create) { 435 ddi_taskq_destroy(vnetp->taskqp); 436 } 437 if (attach_state & AST_fdbh_alloc) { 438 vnet_fdb_destroy(vnetp); 439 } 440 if (attach_state & AST_vgen_init) { 441 (void) vgen_uninit(vnetp->vgenhdl); 442 } 443 if (attach_state & AST_vnet_alloc) { 444 rw_destroy(&vnetp->vrwlock); 445 rw_destroy(&vnetp->vsw_fp_rw); 446 KMEM_FREE(vnetp); 447 } 448 return (DDI_FAILURE); 449 } 450 451 /* 452 * detach(9E): detach a device from the system. 453 */ 454 static int 455 vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) 456 { 457 vnet_t *vnetp; 458 vnet_t **vnetpp; 459 int instance; 460 int rv; 461 462 instance = ddi_get_instance(dip); 463 DBG1(NULL, "instance(%d) enter\n", instance); 464 465 vnetp = ddi_get_driver_private(dip); 466 if (vnetp == NULL) { 467 goto vnet_detach_fail; 468 } 469 470 switch (cmd) { 471 case DDI_DETACH: 472 break; 473 case DDI_SUSPEND: 474 case DDI_PM_SUSPEND: 475 default: 476 goto vnet_detach_fail; 477 } 478 479 (void) vdds_cleanup(vnetp); 480 rv = vgen_uninit(vnetp->vgenhdl); 481 if (rv != DDI_SUCCESS) { 482 goto vnet_detach_fail; 483 } 484 485 /* 486 * Unregister from the MAC subsystem. This can fail, in 487 * particular if there are DLPI style-2 streams still open - 488 * in which case we just return failure. 489 */ 490 if (mac_unregister(vnetp->mh) != 0) 491 goto vnet_detach_fail; 492 493 /* unlink from instance(vnet_t) list */ 494 WRITE_ENTER(&vnet_rw); 495 for (vnetpp = &vnet_headp; *vnetpp; vnetpp = &(*vnetpp)->nextp) { 496 if (*vnetpp == vnetp) { 497 *vnetpp = vnetp->nextp; 498 break; 499 } 500 } 501 RW_EXIT(&vnet_rw); 502 503 ddi_taskq_destroy(vnetp->taskqp); 504 /* destroy fdb */ 505 vnet_fdb_destroy(vnetp); 506 507 rw_destroy(&vnetp->vrwlock); 508 rw_destroy(&vnetp->vsw_fp_rw); 509 KMEM_FREE(vnetp); 510 511 return (DDI_SUCCESS); 512 513 vnet_detach_fail: 514 return (DDI_FAILURE); 515 } 516 517 /* enable the device for transmit/receive */ 518 static int 519 vnet_m_start(void *arg) 520 { 521 vnet_t *vnetp = arg; 522 523 DBG1(vnetp, "enter\n"); 524 525 WRITE_ENTER(&vnetp->vrwlock); 526 vnetp->flags |= VNET_STARTED; 527 vnet_start_resources(vnetp); 528 RW_EXIT(&vnetp->vrwlock); 529 530 DBG1(vnetp, "exit\n"); 531 return (VNET_SUCCESS); 532 533 } 534 535 /* stop transmit/receive for the device */ 536 static void 537 vnet_m_stop(void *arg) 538 { 539 vnet_t *vnetp = arg; 540 541 DBG1(vnetp, "enter\n"); 542 543 WRITE_ENTER(&vnetp->vrwlock); 544 if (vnetp->flags & VNET_STARTED) { 545 vnet_stop_resources(vnetp); 546 vnetp->flags &= ~VNET_STARTED; 547 } 548 RW_EXIT(&vnetp->vrwlock); 549 550 DBG1(vnetp, "exit\n"); 551 } 552 553 /* set the unicast mac address of the device */ 554 static int 555 vnet_m_unicst(void *arg, const uint8_t *macaddr) 556 { 557 _NOTE(ARGUNUSED(macaddr)) 558 559 vnet_t *vnetp = arg; 560 561 DBG1(vnetp, "enter\n"); 562 /* 563 * NOTE: setting mac address dynamically is not supported. 564 */ 565 DBG1(vnetp, "exit\n"); 566 567 return (VNET_FAILURE); 568 } 569 570 /* enable/disable a multicast address */ 571 static int 572 vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 573 { 574 _NOTE(ARGUNUSED(add, mca)) 575 576 vnet_t *vnetp = arg; 577 vnet_res_t *vresp; 578 mac_register_t *macp; 579 mac_callbacks_t *cbp; 580 int rv = VNET_SUCCESS; 581 582 DBG1(vnetp, "enter\n"); 583 584 READ_ENTER(&vnetp->vrwlock); 585 for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) { 586 if (vresp->type == VIO_NET_RES_LDC_SERVICE) { 587 macp = &vresp->macreg; 588 cbp = macp->m_callbacks; 589 rv = cbp->mc_multicst(macp->m_driver, add, mca); 590 } 591 } 592 RW_EXIT(&vnetp->vrwlock); 593 594 DBG1(vnetp, "exit(%d)\n", rv); 595 return (rv); 596 } 597 598 /* set or clear promiscuous mode on the device */ 599 static int 600 vnet_m_promisc(void *arg, boolean_t on) 601 { 602 _NOTE(ARGUNUSED(on)) 603 604 vnet_t *vnetp = arg; 605 DBG1(vnetp, "enter\n"); 606 /* 607 * NOTE: setting promiscuous mode is not supported, just return success. 608 */ 609 DBG1(vnetp, "exit\n"); 610 return (VNET_SUCCESS); 611 } 612 613 /* 614 * Transmit a chain of packets. This function provides switching functionality 615 * based on the destination mac address to reach other guests (within ldoms) or 616 * external hosts. 617 */ 618 mblk_t * 619 vnet_m_tx(void *arg, mblk_t *mp) 620 { 621 vnet_t *vnetp; 622 vnet_res_t *vresp; 623 mblk_t *next; 624 mblk_t *resid_mp; 625 mac_register_t *macp; 626 struct ether_header *ehp; 627 boolean_t is_unicast; 628 629 vnetp = (vnet_t *)arg; 630 DBG1(vnetp, "enter\n"); 631 ASSERT(mp != NULL); 632 633 while (mp != NULL) { 634 635 next = mp->b_next; 636 mp->b_next = NULL; 637 638 /* 639 * Find fdb entry for the destination 640 * and hold a reference to it. 641 */ 642 ehp = (struct ether_header *)mp->b_rptr; 643 vresp = vnet_fdbe_find(vnetp, &ehp->ether_dhost); 644 if (vresp != NULL) { 645 646 /* 647 * Destination found in FDB. 648 * The destination is a vnet device within ldoms 649 * and directly reachable, invoke the tx function 650 * in the fdb entry. 651 */ 652 macp = &vresp->macreg; 653 resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp); 654 655 /* tx done; now release ref on fdb entry */ 656 VNET_FDBE_REFRELE(vresp); 657 658 if (resid_mp != NULL) { 659 /* m_tx failed */ 660 mp->b_next = next; 661 break; 662 } 663 } else { 664 is_unicast = !(IS_BROADCAST(ehp) || 665 (IS_MULTICAST(ehp))); 666 /* 667 * Destination is not in FDB. 668 * If the destination is broadcast or multicast, 669 * then forward the packet to vswitch. 670 * If a Hybrid resource avilable, then send the 671 * unicast packet via hybrid resource, otherwise 672 * forward it to vswitch. 673 */ 674 READ_ENTER(&vnetp->vsw_fp_rw); 675 676 if ((is_unicast) && (vnetp->hio_fp != NULL)) { 677 vresp = vnetp->hio_fp; 678 } else { 679 vresp = vnetp->vsw_fp; 680 } 681 if (vresp == NULL) { 682 /* 683 * no fdb entry to vsw? drop the packet. 684 */ 685 RW_EXIT(&vnetp->vsw_fp_rw); 686 freemsg(mp); 687 mp = next; 688 continue; 689 } 690 691 /* ref hold the fdb entry to vsw */ 692 VNET_FDBE_REFHOLD(vresp); 693 694 RW_EXIT(&vnetp->vsw_fp_rw); 695 696 macp = &vresp->macreg; 697 resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp); 698 699 /* tx done; now release ref on fdb entry */ 700 VNET_FDBE_REFRELE(vresp); 701 702 if (resid_mp != NULL) { 703 /* m_tx failed */ 704 mp->b_next = next; 705 break; 706 } 707 } 708 709 mp = next; 710 } 711 712 DBG1(vnetp, "exit\n"); 713 return (mp); 714 } 715 716 /* get statistics from the device */ 717 int 718 vnet_m_stat(void *arg, uint_t stat, uint64_t *val) 719 { 720 vnet_t *vnetp = arg; 721 vnet_res_t *vresp; 722 mac_register_t *macp; 723 mac_callbacks_t *cbp; 724 uint64_t val_total = 0; 725 726 DBG1(vnetp, "enter\n"); 727 728 /* 729 * get the specified statistic from each transport and return the 730 * aggregate val. This obviously only works for counters. 731 */ 732 if ((IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat)) || 733 (IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat))) { 734 return (ENOTSUP); 735 } 736 737 READ_ENTER(&vnetp->vrwlock); 738 for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) { 739 macp = &vresp->macreg; 740 cbp = macp->m_callbacks; 741 if (cbp->mc_getstat(macp->m_driver, stat, val) == 0) 742 val_total += *val; 743 } 744 RW_EXIT(&vnetp->vrwlock); 745 746 *val = val_total; 747 748 DBG1(vnetp, "exit\n"); 749 return (0); 750 } 751 752 /* wrapper function for mac_register() */ 753 static int 754 vnet_mac_register(vnet_t *vnetp) 755 { 756 mac_register_t *macp; 757 int err; 758 759 if ((macp = mac_alloc(MAC_VERSION)) == NULL) 760 return (DDI_FAILURE); 761 macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 762 macp->m_driver = vnetp; 763 macp->m_dip = vnetp->dip; 764 macp->m_src_addr = vnetp->curr_macaddr; 765 macp->m_callbacks = &vnet_m_callbacks; 766 macp->m_min_sdu = 0; 767 macp->m_max_sdu = vnetp->mtu; 768 macp->m_margin = VLAN_TAGSZ; 769 770 /* 771 * Finally, we're ready to register ourselves with the MAC layer 772 * interface; if this succeeds, we're all ready to start() 773 */ 774 err = mac_register(macp, &vnetp->mh); 775 mac_free(macp); 776 return (err == 0 ? DDI_SUCCESS : DDI_FAILURE); 777 } 778 779 /* read the mac address of the device */ 780 static int 781 vnet_read_mac_address(vnet_t *vnetp) 782 { 783 uchar_t *macaddr; 784 uint32_t size; 785 int rv; 786 787 rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip, 788 DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size); 789 if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) { 790 DWARN(vnetp, "prop_lookup failed(%s) err(%d)\n", 791 macaddr_propname, rv); 792 return (DDI_FAILURE); 793 } 794 bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL); 795 bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL); 796 ddi_prop_free(macaddr); 797 798 return (DDI_SUCCESS); 799 } 800 801 static void 802 vnet_fdb_create(vnet_t *vnetp) 803 { 804 char hashname[MAXNAMELEN]; 805 806 (void) snprintf(hashname, MAXNAMELEN, "vnet%d-fdbhash", 807 vnetp->instance); 808 vnetp->fdb_nchains = vnet_fdb_nchains; 809 vnetp->fdb_hashp = mod_hash_create_ptrhash(hashname, vnetp->fdb_nchains, 810 mod_hash_null_valdtor, sizeof (void *)); 811 } 812 813 static void 814 vnet_fdb_destroy(vnet_t *vnetp) 815 { 816 /* destroy fdb-hash-table */ 817 if (vnetp->fdb_hashp != NULL) { 818 mod_hash_destroy_hash(vnetp->fdb_hashp); 819 vnetp->fdb_hashp = NULL; 820 vnetp->fdb_nchains = 0; 821 } 822 } 823 824 /* 825 * Add an entry into the fdb. 826 */ 827 void 828 vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp) 829 { 830 uint64_t addr = 0; 831 int rv; 832 833 KEY_HASH(addr, vresp->rem_macaddr); 834 835 /* 836 * If the entry being added corresponds to LDC_SERVICE resource, 837 * that is, vswitch connection, it is added to the hash and also 838 * the entry is cached, an additional reference count reflects 839 * this. The HYBRID resource is not added to the hash, but only 840 * cached, as it is only used for sending out packets for unknown 841 * unicast destinations. 842 */ 843 (vresp->type == VIO_NET_RES_LDC_SERVICE) ? 844 (vresp->refcnt = 1) : (vresp->refcnt = 0); 845 846 /* 847 * Note: duplicate keys will be rejected by mod_hash. 848 */ 849 if (vresp->type != VIO_NET_RES_HYBRID) { 850 rv = mod_hash_insert(vnetp->fdb_hashp, (mod_hash_key_t)addr, 851 (mod_hash_val_t)vresp); 852 if (rv != 0) { 853 DWARN(vnetp, "Duplicate macaddr key(%lx)\n", addr); 854 return; 855 } 856 } 857 858 if (vresp->type == VIO_NET_RES_LDC_SERVICE) { 859 /* Cache the fdb entry to vsw-port */ 860 WRITE_ENTER(&vnetp->vsw_fp_rw); 861 if (vnetp->vsw_fp == NULL) 862 vnetp->vsw_fp = vresp; 863 RW_EXIT(&vnetp->vsw_fp_rw); 864 } else if (vresp->type == VIO_NET_RES_HYBRID) { 865 /* Cache the fdb entry to hybrid resource */ 866 WRITE_ENTER(&vnetp->vsw_fp_rw); 867 if (vnetp->hio_fp == NULL) 868 vnetp->hio_fp = vresp; 869 RW_EXIT(&vnetp->vsw_fp_rw); 870 } 871 } 872 873 /* 874 * Remove an entry from fdb. 875 */ 876 static void 877 vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp) 878 { 879 uint64_t addr = 0; 880 int rv; 881 uint32_t refcnt; 882 vnet_res_t *tmp; 883 884 KEY_HASH(addr, vresp->rem_macaddr); 885 886 /* 887 * Remove the entry from fdb hash table. 888 * This prevents further references to this fdb entry. 889 */ 890 if (vresp->type != VIO_NET_RES_HYBRID) { 891 rv = mod_hash_remove(vnetp->fdb_hashp, (mod_hash_key_t)addr, 892 (mod_hash_val_t *)&tmp); 893 if (rv != 0) { 894 /* 895 * As the resources are added to the hash only 896 * after they are started, this can occur if 897 * a resource unregisters before it is ever started. 898 */ 899 return; 900 } 901 } 902 903 if (vresp->type == VIO_NET_RES_LDC_SERVICE) { 904 WRITE_ENTER(&vnetp->vsw_fp_rw); 905 906 ASSERT(tmp == vnetp->vsw_fp); 907 vnetp->vsw_fp = NULL; 908 909 RW_EXIT(&vnetp->vsw_fp_rw); 910 } else if (vresp->type == VIO_NET_RES_HYBRID) { 911 WRITE_ENTER(&vnetp->vsw_fp_rw); 912 913 vnetp->hio_fp = NULL; 914 915 RW_EXIT(&vnetp->vsw_fp_rw); 916 } 917 918 /* 919 * If there are threads already ref holding before the entry was 920 * removed from hash table, then wait for ref count to drop to zero. 921 */ 922 (vresp->type == VIO_NET_RES_LDC_SERVICE) ? 923 (refcnt = 1) : (refcnt = 0); 924 while (vresp->refcnt > refcnt) { 925 delay(drv_usectohz(vnet_fdbe_refcnt_delay)); 926 } 927 } 928 929 /* 930 * Search fdb for a given mac address. If an entry is found, hold 931 * a reference to it and return the entry; else returns NULL. 932 */ 933 static vnet_res_t * 934 vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp) 935 { 936 uint64_t key = 0; 937 vnet_res_t *vresp; 938 int rv; 939 940 KEY_HASH(key, addrp->ether_addr_octet); 941 942 rv = mod_hash_find_cb(vnetp->fdb_hashp, (mod_hash_key_t)key, 943 (mod_hash_val_t *)&vresp, vnet_fdbe_find_cb); 944 945 if (rv != 0) 946 return (NULL); 947 948 return (vresp); 949 } 950 951 /* 952 * Callback function provided to mod_hash_find_cb(). After finding the fdb 953 * entry corresponding to the key (macaddr), this callback will be invoked by 954 * mod_hash_find_cb() to atomically increment the reference count on the fdb 955 * entry before returning the found entry. 956 */ 957 static void 958 vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val) 959 { 960 _NOTE(ARGUNUSED(key)) 961 VNET_FDBE_REFHOLD((vnet_res_t *)val); 962 } 963 964 static void 965 vnet_rx(vio_net_handle_t vrh, mblk_t *mp) 966 { 967 vnet_res_t *vresp = (vnet_res_t *)vrh; 968 vnet_t *vnetp = vresp->vnetp; 969 970 if ((vnetp != NULL) && (vnetp->mh)) { 971 mac_rx(vnetp->mh, NULL, mp); 972 } else { 973 freemsgchain(mp); 974 } 975 } 976 977 void 978 vnet_tx_update(vio_net_handle_t vrh) 979 { 980 vnet_res_t *vresp = (vnet_res_t *)vrh; 981 vnet_t *vnetp = vresp->vnetp; 982 983 if ((vnetp != NULL) && (vnetp->mh != NULL)) { 984 mac_tx_update(vnetp->mh); 985 } 986 } 987 988 /* 989 * Update the new mtu of vnet into the mac layer. First check if the device has 990 * been plumbed and if so fail the mtu update. Returns 0 on success. 991 */ 992 int 993 vnet_mtu_update(vnet_t *vnetp, uint32_t mtu) 994 { 995 int rv; 996 997 if (vnetp == NULL || vnetp->mh == NULL) { 998 return (EINVAL); 999 } 1000 1001 WRITE_ENTER(&vnetp->vrwlock); 1002 1003 if (vnetp->flags & VNET_STARTED) { 1004 RW_EXIT(&vnetp->vrwlock); 1005 cmn_err(CE_NOTE, "!vnet%d: Unable to process mtu " 1006 "update as the device is plumbed\n", 1007 vnetp->instance); 1008 return (EBUSY); 1009 } 1010 1011 /* update mtu in the mac layer */ 1012 rv = mac_maxsdu_update(vnetp->mh, mtu); 1013 if (rv != 0) { 1014 RW_EXIT(&vnetp->vrwlock); 1015 cmn_err(CE_NOTE, 1016 "!vnet%d: Unable to update mtu with mac layer\n", 1017 vnetp->instance); 1018 return (EIO); 1019 } 1020 1021 vnetp->mtu = mtu; 1022 1023 RW_EXIT(&vnetp->vrwlock); 1024 1025 return (0); 1026 } 1027 1028 /* 1029 * vio_net_resource_reg -- An interface called to register a resource 1030 * with vnet. 1031 * macp -- a GLDv3 mac_register that has all the details of 1032 * a resource and its callbacks etc. 1033 * type -- resource type. 1034 * local_macaddr -- resource's MAC address. This is used to 1035 * associate a resource with a corresponding vnet. 1036 * remote_macaddr -- remote side MAC address. This is ignored for 1037 * the Hybrid resources. 1038 * vhp -- A handle returned to the caller. 1039 * vcb -- A set of callbacks provided to the callers. 1040 */ 1041 int vio_net_resource_reg(mac_register_t *macp, vio_net_res_type_t type, 1042 ether_addr_t local_macaddr, ether_addr_t rem_macaddr, vio_net_handle_t *vhp, 1043 vio_net_callbacks_t *vcb) 1044 { 1045 vnet_t *vnetp; 1046 vnet_res_t *vresp; 1047 1048 vresp = kmem_zalloc(sizeof (vnet_res_t), KM_SLEEP); 1049 ether_copy(local_macaddr, vresp->local_macaddr); 1050 ether_copy(rem_macaddr, vresp->rem_macaddr); 1051 vresp->type = type; 1052 bcopy(macp, &vresp->macreg, sizeof (mac_register_t)); 1053 1054 DBG1(NULL, "Resource Registerig type=0%X\n", type); 1055 1056 READ_ENTER(&vnet_rw); 1057 vnetp = vnet_headp; 1058 while (vnetp != NULL) { 1059 if (VNET_MATCH_RES(vresp, vnetp)) { 1060 WRITE_ENTER(&vnetp->vrwlock); 1061 vresp->vnetp = vnetp; 1062 vresp->nextp = vnetp->vres_list; 1063 vnetp->vres_list = vresp; 1064 RW_EXIT(&vnetp->vrwlock); 1065 break; 1066 } 1067 vnetp = vnetp->nextp; 1068 } 1069 RW_EXIT(&vnet_rw); 1070 if (vresp->vnetp == NULL) { 1071 DWARN(NULL, "No vnet instance"); 1072 kmem_free(vresp, sizeof (vnet_res_t)); 1073 return (ENXIO); 1074 } 1075 1076 *vhp = vresp; 1077 vcb->vio_net_rx_cb = vnet_rx; 1078 vcb->vio_net_tx_update = vnet_tx_update; 1079 vcb->vio_net_report_err = vnet_handle_res_err; 1080 1081 /* Dispatch a task to start resources */ 1082 vnet_dispatch_res_task(vnetp); 1083 return (0); 1084 } 1085 1086 /* 1087 * vio_net_resource_unreg -- An interface to unregister a resource. 1088 */ 1089 void 1090 vio_net_resource_unreg(vio_net_handle_t vhp) 1091 { 1092 vnet_res_t *vresp = (vnet_res_t *)vhp; 1093 vnet_t *vnetp = vresp->vnetp; 1094 vnet_res_t *vrp; 1095 1096 DBG1(NULL, "Resource Registerig hdl=0x%p", vhp); 1097 1098 ASSERT(vnetp != NULL); 1099 vnet_fdbe_del(vnetp, vresp); 1100 1101 WRITE_ENTER(&vnetp->vrwlock); 1102 if (vresp == vnetp->vres_list) { 1103 vnetp->vres_list = vresp->nextp; 1104 } else { 1105 vrp = vnetp->vres_list; 1106 while (vrp->nextp != NULL) { 1107 if (vrp->nextp == vresp) { 1108 vrp->nextp = vresp->nextp; 1109 break; 1110 } 1111 vrp = vrp->nextp; 1112 } 1113 } 1114 vresp->vnetp = NULL; 1115 vresp->nextp = NULL; 1116 RW_EXIT(&vnetp->vrwlock); 1117 KMEM_FREE(vresp); 1118 } 1119 1120 /* 1121 * vnet_dds_rx -- an interface called by vgen to DDS messages. 1122 */ 1123 void 1124 vnet_dds_rx(void *arg, void *dmsg) 1125 { 1126 vnet_t *vnetp = arg; 1127 vdds_process_dds_msg(vnetp, dmsg); 1128 } 1129 1130 /* 1131 * vnet_send_dds_msg -- An interface provided to DDS to send 1132 * DDS messages. This simply sends meessages via vgen. 1133 */ 1134 int 1135 vnet_send_dds_msg(vnet_t *vnetp, void *dmsg) 1136 { 1137 int rv; 1138 1139 if (vnetp->vgenhdl != NULL) { 1140 rv = vgen_dds_tx(vnetp->vgenhdl, dmsg); 1141 } 1142 return (rv); 1143 } 1144 1145 /* 1146 * vnet_handle_res_err -- A callback function called by a resource 1147 * to report an error. For example, vgen can call to report 1148 * an LDC down/reset event. This will trigger cleanup of associated 1149 * Hybrid resource. 1150 */ 1151 /* ARGSUSED */ 1152 static void 1153 vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err) 1154 { 1155 vnet_res_t *vresp = (vnet_res_t *)vrh; 1156 vnet_t *vnetp = vresp->vnetp; 1157 int rv; 1158 1159 if (vnetp == NULL) { 1160 return; 1161 } 1162 if ((vresp->type != VIO_NET_RES_LDC_SERVICE) && 1163 (vresp->type != VIO_NET_RES_HYBRID)) { 1164 return; 1165 } 1166 rv = ddi_taskq_dispatch(vnetp->taskqp, vdds_cleanup_hybrid_res, 1167 vnetp, DDI_NOSLEEP); 1168 if (rv != DDI_SUCCESS) { 1169 cmn_err(CE_WARN, 1170 "vnet%d:Failed to dispatch task to cleanup hybrid resource", 1171 vnetp->instance); 1172 } 1173 } 1174 1175 /* 1176 * vnet_dispatch_res_task -- A function to dispatch tasks start resources. 1177 */ 1178 static void 1179 vnet_dispatch_res_task(vnet_t *vnetp) 1180 { 1181 int rv; 1182 1183 WRITE_ENTER(&vnetp->vrwlock); 1184 if (vnetp->flags & VNET_STARTED) { 1185 rv = ddi_taskq_dispatch(vnetp->taskqp, vnet_res_start_task, 1186 vnetp, DDI_NOSLEEP); 1187 if (rv != DDI_SUCCESS) { 1188 cmn_err(CE_WARN, 1189 "vnet%d:Can't dispatch start resource task", 1190 vnetp->instance); 1191 } 1192 } 1193 RW_EXIT(&vnetp->vrwlock); 1194 } 1195 1196 /* 1197 * vnet_res_start_task -- A taskq callback function that starts a resource. 1198 */ 1199 static void 1200 vnet_res_start_task(void *arg) 1201 { 1202 vnet_t *vnetp = arg; 1203 1204 WRITE_ENTER(&vnetp->vrwlock); 1205 if (vnetp->flags & VNET_STARTED) { 1206 vnet_start_resources(vnetp); 1207 } 1208 RW_EXIT(&vnetp->vrwlock); 1209 } 1210 1211 /* 1212 * vnet_start_resources -- starts all resources associated with 1213 * a vnet. 1214 */ 1215 static void 1216 vnet_start_resources(vnet_t *vnetp) 1217 { 1218 mac_register_t *macp; 1219 mac_callbacks_t *cbp; 1220 vnet_res_t *vresp; 1221 int rv; 1222 1223 DBG1(vnetp, "enter\n"); 1224 1225 for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) { 1226 /* skip if it is already started */ 1227 if (vresp->flags & VNET_STARTED) { 1228 continue; 1229 } 1230 macp = &vresp->macreg; 1231 cbp = macp->m_callbacks; 1232 rv = cbp->mc_start(macp->m_driver); 1233 if (rv == 0) { 1234 /* 1235 * Successfully started the resource, so now 1236 * add it to the fdb. 1237 */ 1238 vresp->flags |= VNET_STARTED; 1239 vnet_fdbe_add(vnetp, vresp); 1240 } 1241 } 1242 1243 DBG1(vnetp, "exit\n"); 1244 1245 } 1246 1247 /* 1248 * vnet_stop_resources -- stop all resources associated with a vnet. 1249 */ 1250 static void 1251 vnet_stop_resources(vnet_t *vnetp) 1252 { 1253 vnet_res_t *vresp; 1254 vnet_res_t *nvresp; 1255 mac_register_t *macp; 1256 mac_callbacks_t *cbp; 1257 1258 DBG1(vnetp, "enter\n"); 1259 1260 for (vresp = vnetp->vres_list; vresp != NULL; ) { 1261 nvresp = vresp->nextp; 1262 if (vresp->flags & VNET_STARTED) { 1263 macp = &vresp->macreg; 1264 cbp = macp->m_callbacks; 1265 cbp->mc_stop(macp->m_driver); 1266 vresp->flags &= ~VNET_STARTED; 1267 } 1268 vresp = nvresp; 1269 } 1270 DBG1(vnetp, "exit\n"); 1271 } 1272