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/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_provider.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 static void vnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp); 75 #ifdef VNET_IOC_DEBUG 76 static void vnet_force_link_state(vnet_t *vnetp, queue_t *q, mblk_t *mp); 77 #endif 78 79 /* vnet internal functions */ 80 static int vnet_unattach(vnet_t *vnetp); 81 static int vnet_mac_register(vnet_t *); 82 static int vnet_read_mac_address(vnet_t *vnetp); 83 84 /* Forwarding database (FDB) routines */ 85 static void vnet_fdb_create(vnet_t *vnetp); 86 static void vnet_fdb_destroy(vnet_t *vnetp); 87 static vnet_res_t *vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp); 88 static void vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val); 89 void vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp); 90 static void vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp); 91 92 static void vnet_rx_frames_untag(uint16_t pvid, mblk_t **mp); 93 static void vnet_rx(vio_net_handle_t vrh, mblk_t *mp); 94 static void vnet_tx_update(vio_net_handle_t vrh); 95 static void vnet_res_start_task(void *arg); 96 static void vnet_start_resources(vnet_t *vnetp); 97 static void vnet_stop_resources(vnet_t *vnetp); 98 static void vnet_dispatch_res_task(vnet_t *vnetp); 99 static void vnet_res_start_task(void *arg); 100 static void vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err); 101 102 /* Exported to vnet_gen */ 103 int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu); 104 void vnet_link_update(vnet_t *vnetp, link_state_t link_state); 105 void vnet_dds_cleanup_hio(vnet_t *vnetp); 106 107 static kstat_t *vnet_hio_setup_kstats(char *ks_mod, char *ks_name, 108 vnet_res_t *vresp); 109 static int vnet_hio_update_kstats(kstat_t *ksp, int rw); 110 static void vnet_hio_get_stats(vnet_res_t *vresp, vnet_hio_stats_t *statsp); 111 static void vnet_hio_destroy_kstats(kstat_t *ksp); 112 113 /* Exported to to vnet_dds */ 114 int vnet_send_dds_msg(vnet_t *vnetp, void *dmsg); 115 116 /* Externs that are imported from vnet_gen */ 117 extern int vgen_init(void *vnetp, uint64_t regprop, dev_info_t *vnetdip, 118 const uint8_t *macaddr, void **vgenhdl); 119 extern void vgen_uninit(void *arg); 120 extern int vgen_dds_tx(void *arg, void *dmsg); 121 extern void vgen_mod_init(void); 122 extern int vgen_mod_cleanup(void); 123 extern void vgen_mod_fini(void); 124 125 /* Externs that are imported from vnet_dds */ 126 extern void vdds_mod_init(void); 127 extern void vdds_mod_fini(void); 128 extern int vdds_init(vnet_t *vnetp); 129 extern void vdds_cleanup(vnet_t *vnetp); 130 extern void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg); 131 extern void vdds_cleanup_hybrid_res(void *arg); 132 extern void vdds_cleanup_hio(vnet_t *vnetp); 133 134 #define DRV_NAME "vnet" 135 #define VNET_FDBE_REFHOLD(p) \ 136 { \ 137 atomic_inc_32(&(p)->refcnt); \ 138 ASSERT((p)->refcnt != 0); \ 139 } 140 141 #define VNET_FDBE_REFRELE(p) \ 142 { \ 143 ASSERT((p)->refcnt != 0); \ 144 atomic_dec_32(&(p)->refcnt); \ 145 } 146 147 #ifdef VNET_IOC_DEBUG 148 #define VNET_M_CALLBACK_FLAGS (MC_IOCTL) 149 #else 150 #define VNET_M_CALLBACK_FLAGS (0) 151 #endif 152 153 static mac_callbacks_t vnet_m_callbacks = { 154 VNET_M_CALLBACK_FLAGS, 155 vnet_m_stat, 156 vnet_m_start, 157 vnet_m_stop, 158 vnet_m_promisc, 159 vnet_m_multicst, 160 vnet_m_unicst, 161 vnet_m_tx, 162 vnet_m_ioctl, 163 NULL, 164 NULL 165 }; 166 167 /* 168 * Linked list of "vnet_t" structures - one per instance. 169 */ 170 static vnet_t *vnet_headp = NULL; 171 static krwlock_t vnet_rw; 172 173 /* Tunables */ 174 uint32_t vnet_ntxds = VNET_NTXDS; /* power of 2 transmit descriptors */ 175 uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */ 176 uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT; /* tx timeout in msec */ 177 uint32_t vnet_ldc_mtu = VNET_LDC_MTU; /* ldc mtu */ 178 179 /* 180 * Set this to non-zero to enable additional internal receive buffer pools 181 * based on the MTU of the device for better performance at the cost of more 182 * memory consumption. This is turned off by default, to use allocb(9F) for 183 * receive buffer allocations of sizes > 2K. 184 */ 185 boolean_t vnet_jumbo_rxpools = B_FALSE; 186 187 /* # of chains in fdb hash table */ 188 uint32_t vnet_fdb_nchains = VNET_NFDB_HASH; 189 190 /* Internal tunables */ 191 uint32_t vnet_ethermtu = 1500; /* mtu of the device */ 192 193 /* 194 * Default vlan id. This is only used internally when the "default-vlan-id" 195 * property is not present in the MD device node. Therefore, this should not be 196 * used as a tunable; if this value is changed, the corresponding variable 197 * should be updated to the same value in vsw and also other vnets connected to 198 * the same vsw. 199 */ 200 uint16_t vnet_default_vlan_id = 1; 201 202 /* delay in usec to wait for all references on a fdb entry to be dropped */ 203 uint32_t vnet_fdbe_refcnt_delay = 10; 204 205 static struct ether_addr etherbroadcastaddr = { 206 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 207 }; 208 209 210 /* 211 * Property names 212 */ 213 static char macaddr_propname[] = "local-mac-address"; 214 215 /* 216 * This is the string displayed by modinfo(1m). 217 */ 218 static char vnet_ident[] = "vnet driver"; 219 extern struct mod_ops mod_driverops; 220 static struct cb_ops cb_vnetops = { 221 nulldev, /* cb_open */ 222 nulldev, /* cb_close */ 223 nodev, /* cb_strategy */ 224 nodev, /* cb_print */ 225 nodev, /* cb_dump */ 226 nodev, /* cb_read */ 227 nodev, /* cb_write */ 228 nodev, /* cb_ioctl */ 229 nodev, /* cb_devmap */ 230 nodev, /* cb_mmap */ 231 nodev, /* cb_segmap */ 232 nochpoll, /* cb_chpoll */ 233 ddi_prop_op, /* cb_prop_op */ 234 NULL, /* cb_stream */ 235 (int)(D_MP) /* cb_flag */ 236 }; 237 238 static struct dev_ops vnetops = { 239 DEVO_REV, /* devo_rev */ 240 0, /* devo_refcnt */ 241 NULL, /* devo_getinfo */ 242 nulldev, /* devo_identify */ 243 nulldev, /* devo_probe */ 244 vnetattach, /* devo_attach */ 245 vnetdetach, /* devo_detach */ 246 nodev, /* devo_reset */ 247 &cb_vnetops, /* devo_cb_ops */ 248 (struct bus_ops *)NULL, /* devo_bus_ops */ 249 NULL, /* devo_power */ 250 ddi_quiesce_not_supported, /* devo_quiesce */ 251 }; 252 253 static struct modldrv modldrv = { 254 &mod_driverops, /* Type of module. This one is a driver */ 255 vnet_ident, /* ID string */ 256 &vnetops /* driver specific ops */ 257 }; 258 259 static struct modlinkage modlinkage = { 260 MODREV_1, (void *)&modldrv, NULL 261 }; 262 263 #ifdef DEBUG 264 265 /* 266 * Print debug messages - set to 0xf to enable all msgs 267 */ 268 int vnet_dbglevel = 0x8; 269 270 static void 271 debug_printf(const char *fname, void *arg, const char *fmt, ...) 272 { 273 char buf[512]; 274 va_list ap; 275 vnet_t *vnetp = (vnet_t *)arg; 276 char *bufp = buf; 277 278 if (vnetp == NULL) { 279 (void) sprintf(bufp, "%s: ", fname); 280 bufp += strlen(bufp); 281 } else { 282 (void) sprintf(bufp, "vnet%d:%s: ", vnetp->instance, fname); 283 bufp += strlen(bufp); 284 } 285 va_start(ap, fmt); 286 (void) vsprintf(bufp, fmt, ap); 287 va_end(ap); 288 cmn_err(CE_CONT, "%s\n", buf); 289 } 290 291 #endif 292 293 /* _init(9E): initialize the loadable module */ 294 int 295 _init(void) 296 { 297 int status; 298 299 DBG1(NULL, "enter\n"); 300 301 mac_init_ops(&vnetops, "vnet"); 302 status = mod_install(&modlinkage); 303 if (status != 0) { 304 mac_fini_ops(&vnetops); 305 } 306 vdds_mod_init(); 307 vgen_mod_init(); 308 DBG1(NULL, "exit(%d)\n", status); 309 return (status); 310 } 311 312 /* _fini(9E): prepare the module for unloading. */ 313 int 314 _fini(void) 315 { 316 int status; 317 318 DBG1(NULL, "enter\n"); 319 320 status = vgen_mod_cleanup(); 321 if (status != 0) 322 return (status); 323 324 status = mod_remove(&modlinkage); 325 if (status != 0) 326 return (status); 327 mac_fini_ops(&vnetops); 328 vgen_mod_fini(); 329 vdds_mod_fini(); 330 331 DBG1(NULL, "exit(%d)\n", status); 332 return (status); 333 } 334 335 /* _info(9E): return information about the loadable module */ 336 int 337 _info(struct modinfo *modinfop) 338 { 339 return (mod_info(&modlinkage, modinfop)); 340 } 341 342 /* 343 * attach(9E): attach a device to the system. 344 * called once for each instance of the device on the system. 345 */ 346 static int 347 vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd) 348 { 349 vnet_t *vnetp; 350 int status; 351 int instance; 352 uint64_t reg; 353 char qname[TASKQ_NAMELEN]; 354 vnet_attach_progress_t attach_progress; 355 356 attach_progress = AST_init; 357 358 switch (cmd) { 359 case DDI_ATTACH: 360 break; 361 case DDI_RESUME: 362 case DDI_PM_RESUME: 363 default: 364 goto vnet_attach_fail; 365 } 366 367 instance = ddi_get_instance(dip); 368 DBG1(NULL, "instance(%d) enter\n", instance); 369 370 /* allocate vnet_t and mac_t structures */ 371 vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP); 372 vnetp->dip = dip; 373 vnetp->instance = instance; 374 rw_init(&vnetp->vrwlock, NULL, RW_DRIVER, NULL); 375 rw_init(&vnetp->vsw_fp_rw, NULL, RW_DRIVER, NULL); 376 attach_progress |= AST_vnet_alloc; 377 378 status = vdds_init(vnetp); 379 if (status != 0) { 380 goto vnet_attach_fail; 381 } 382 attach_progress |= AST_vdds_init; 383 384 /* setup links to vnet_t from both devinfo and mac_t */ 385 ddi_set_driver_private(dip, (caddr_t)vnetp); 386 387 /* read the mac address */ 388 status = vnet_read_mac_address(vnetp); 389 if (status != DDI_SUCCESS) { 390 goto vnet_attach_fail; 391 } 392 attach_progress |= AST_read_macaddr; 393 394 reg = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 395 DDI_PROP_DONTPASS, "reg", -1); 396 if (reg == -1) { 397 goto vnet_attach_fail; 398 } 399 vnetp->reg = reg; 400 401 vnet_fdb_create(vnetp); 402 attach_progress |= AST_fdbh_alloc; 403 404 (void) snprintf(qname, TASKQ_NAMELEN, "vnet_taskq%d", instance); 405 if ((vnetp->taskqp = ddi_taskq_create(dip, qname, 1, 406 TASKQ_DEFAULTPRI, 0)) == NULL) { 407 cmn_err(CE_WARN, "!vnet%d: Unable to create task queue", 408 instance); 409 goto vnet_attach_fail; 410 } 411 attach_progress |= AST_taskq_create; 412 413 /* add to the list of vnet devices */ 414 WRITE_ENTER(&vnet_rw); 415 vnetp->nextp = vnet_headp; 416 vnet_headp = vnetp; 417 RW_EXIT(&vnet_rw); 418 419 attach_progress |= AST_vnet_list; 420 421 /* 422 * Initialize the generic vnet plugin which provides 423 * communication via sun4v LDC (logical domain channel) based 424 * resources. It will register the LDC resources as and when 425 * they become available. 426 */ 427 status = vgen_init(vnetp, reg, vnetp->dip, 428 (uint8_t *)vnetp->curr_macaddr, &vnetp->vgenhdl); 429 if (status != DDI_SUCCESS) { 430 DERR(vnetp, "vgen_init() failed\n"); 431 goto vnet_attach_fail; 432 } 433 attach_progress |= AST_vgen_init; 434 435 /* register with MAC layer */ 436 status = vnet_mac_register(vnetp); 437 if (status != DDI_SUCCESS) { 438 goto vnet_attach_fail; 439 } 440 vnetp->link_state = LINK_STATE_UNKNOWN; 441 442 attach_progress |= AST_macreg; 443 444 vnetp->attach_progress = attach_progress; 445 446 DBG1(NULL, "instance(%d) exit\n", instance); 447 return (DDI_SUCCESS); 448 449 vnet_attach_fail: 450 vnetp->attach_progress = attach_progress; 451 status = vnet_unattach(vnetp); 452 ASSERT(status == 0); 453 return (DDI_FAILURE); 454 } 455 456 /* 457 * detach(9E): detach a device from the system. 458 */ 459 static int 460 vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) 461 { 462 vnet_t *vnetp; 463 int instance; 464 465 instance = ddi_get_instance(dip); 466 DBG1(NULL, "instance(%d) enter\n", instance); 467 468 vnetp = ddi_get_driver_private(dip); 469 if (vnetp == NULL) { 470 goto vnet_detach_fail; 471 } 472 473 switch (cmd) { 474 case DDI_DETACH: 475 break; 476 case DDI_SUSPEND: 477 case DDI_PM_SUSPEND: 478 default: 479 goto vnet_detach_fail; 480 } 481 482 if (vnet_unattach(vnetp) != 0) { 483 goto vnet_detach_fail; 484 } 485 486 return (DDI_SUCCESS); 487 488 vnet_detach_fail: 489 return (DDI_FAILURE); 490 } 491 492 /* 493 * Common routine to handle vnetattach() failure and vnetdetach(). Note that 494 * the only reason this function could fail is if mac_unregister() fails. 495 * Otherwise, this function must ensure that all resources are freed and return 496 * success. 497 */ 498 static int 499 vnet_unattach(vnet_t *vnetp) 500 { 501 vnet_attach_progress_t attach_progress; 502 503 attach_progress = vnetp->attach_progress; 504 505 /* 506 * Unregister from the gldv3 subsystem. This can fail, in particular 507 * if there are still any open references to this mac device; in which 508 * case we just return failure without continuing to detach further. 509 */ 510 if (attach_progress & AST_macreg) { 511 if (mac_unregister(vnetp->mh) != 0) { 512 return (1); 513 } 514 attach_progress &= ~AST_macreg; 515 } 516 517 /* 518 * Now that we have unregistered from gldv3, we must finish all other 519 * steps and successfully return from this function; otherwise we will 520 * end up leaving the device in a broken/unusable state. 521 * 522 * First, release any hybrid resources assigned to this vnet device. 523 */ 524 if (attach_progress & AST_vdds_init) { 525 vdds_cleanup(vnetp); 526 attach_progress &= ~AST_vdds_init; 527 } 528 529 /* 530 * Uninit vgen. This stops further mdeg callbacks to this vnet 531 * device and/or its ports; and detaches any existing ports. 532 */ 533 if (attach_progress & AST_vgen_init) { 534 vgen_uninit(vnetp->vgenhdl); 535 attach_progress &= ~AST_vgen_init; 536 } 537 538 /* Destroy the taskq. */ 539 if (attach_progress & AST_taskq_create) { 540 ddi_taskq_destroy(vnetp->taskqp); 541 attach_progress &= ~AST_taskq_create; 542 } 543 544 /* Destroy fdb. */ 545 if (attach_progress & AST_fdbh_alloc) { 546 vnet_fdb_destroy(vnetp); 547 attach_progress &= ~AST_fdbh_alloc; 548 } 549 550 /* Remove from the device list */ 551 if (attach_progress & AST_vnet_list) { 552 vnet_t **vnetpp; 553 /* unlink from instance(vnet_t) list */ 554 WRITE_ENTER(&vnet_rw); 555 for (vnetpp = &vnet_headp; *vnetpp; 556 vnetpp = &(*vnetpp)->nextp) { 557 if (*vnetpp == vnetp) { 558 *vnetpp = vnetp->nextp; 559 break; 560 } 561 } 562 RW_EXIT(&vnet_rw); 563 attach_progress &= ~AST_vnet_list; 564 } 565 566 if (attach_progress & AST_vnet_alloc) { 567 rw_destroy(&vnetp->vrwlock); 568 rw_destroy(&vnetp->vsw_fp_rw); 569 attach_progress &= ~AST_vnet_list; 570 KMEM_FREE(vnetp); 571 } 572 573 return (0); 574 } 575 576 /* enable the device for transmit/receive */ 577 static int 578 vnet_m_start(void *arg) 579 { 580 vnet_t *vnetp = arg; 581 582 DBG1(vnetp, "enter\n"); 583 584 WRITE_ENTER(&vnetp->vrwlock); 585 vnetp->flags |= VNET_STARTED; 586 vnet_start_resources(vnetp); 587 RW_EXIT(&vnetp->vrwlock); 588 589 DBG1(vnetp, "exit\n"); 590 return (VNET_SUCCESS); 591 592 } 593 594 /* stop transmit/receive for the device */ 595 static void 596 vnet_m_stop(void *arg) 597 { 598 vnet_t *vnetp = arg; 599 600 DBG1(vnetp, "enter\n"); 601 602 WRITE_ENTER(&vnetp->vrwlock); 603 if (vnetp->flags & VNET_STARTED) { 604 /* 605 * Set the flags appropriately; this should prevent starting of 606 * any new resources that are added(see vnet_res_start_task()), 607 * while we release the vrwlock in vnet_stop_resources() before 608 * stopping each resource. 609 */ 610 vnetp->flags &= ~VNET_STARTED; 611 vnetp->flags |= VNET_STOPPING; 612 vnet_stop_resources(vnetp); 613 vnetp->flags &= ~VNET_STOPPING; 614 } 615 RW_EXIT(&vnetp->vrwlock); 616 617 DBG1(vnetp, "exit\n"); 618 } 619 620 /* set the unicast mac address of the device */ 621 static int 622 vnet_m_unicst(void *arg, const uint8_t *macaddr) 623 { 624 _NOTE(ARGUNUSED(macaddr)) 625 626 vnet_t *vnetp = arg; 627 628 DBG1(vnetp, "enter\n"); 629 /* 630 * NOTE: setting mac address dynamically is not supported. 631 */ 632 DBG1(vnetp, "exit\n"); 633 634 return (VNET_FAILURE); 635 } 636 637 /* enable/disable a multicast address */ 638 static int 639 vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 640 { 641 _NOTE(ARGUNUSED(add, mca)) 642 643 vnet_t *vnetp = arg; 644 vnet_res_t *vresp; 645 mac_register_t *macp; 646 mac_callbacks_t *cbp; 647 int rv = VNET_SUCCESS; 648 649 DBG1(vnetp, "enter\n"); 650 651 READ_ENTER(&vnetp->vrwlock); 652 for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) { 653 if (vresp->type == VIO_NET_RES_LDC_SERVICE) { 654 macp = &vresp->macreg; 655 cbp = macp->m_callbacks; 656 rv = cbp->mc_multicst(macp->m_driver, add, mca); 657 } 658 } 659 RW_EXIT(&vnetp->vrwlock); 660 661 DBG1(vnetp, "exit(%d)\n", rv); 662 return (rv); 663 } 664 665 /* set or clear promiscuous mode on the device */ 666 static int 667 vnet_m_promisc(void *arg, boolean_t on) 668 { 669 _NOTE(ARGUNUSED(on)) 670 671 vnet_t *vnetp = arg; 672 DBG1(vnetp, "enter\n"); 673 /* 674 * NOTE: setting promiscuous mode is not supported, just return success. 675 */ 676 DBG1(vnetp, "exit\n"); 677 return (VNET_SUCCESS); 678 } 679 680 /* 681 * Transmit a chain of packets. This function provides switching functionality 682 * based on the destination mac address to reach other guests (within ldoms) or 683 * external hosts. 684 */ 685 mblk_t * 686 vnet_m_tx(void *arg, mblk_t *mp) 687 { 688 vnet_t *vnetp; 689 vnet_res_t *vresp; 690 mblk_t *next; 691 mblk_t *resid_mp; 692 mac_register_t *macp; 693 struct ether_header *ehp; 694 boolean_t is_unicast; 695 boolean_t is_pvid; /* non-default pvid ? */ 696 boolean_t hres; /* Hybrid resource ? */ 697 698 vnetp = (vnet_t *)arg; 699 DBG1(vnetp, "enter\n"); 700 ASSERT(mp != NULL); 701 702 is_pvid = (vnetp->pvid != vnetp->default_vlan_id) ? B_TRUE : B_FALSE; 703 704 while (mp != NULL) { 705 706 next = mp->b_next; 707 mp->b_next = NULL; 708 709 /* 710 * Find fdb entry for the destination 711 * and hold a reference to it. 712 */ 713 ehp = (struct ether_header *)mp->b_rptr; 714 vresp = vnet_fdbe_find(vnetp, &ehp->ether_dhost); 715 if (vresp != NULL) { 716 717 /* 718 * Destination found in FDB. 719 * The destination is a vnet device within ldoms 720 * and directly reachable, invoke the tx function 721 * in the fdb entry. 722 */ 723 macp = &vresp->macreg; 724 resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp); 725 726 /* tx done; now release ref on fdb entry */ 727 VNET_FDBE_REFRELE(vresp); 728 729 if (resid_mp != NULL) { 730 /* m_tx failed */ 731 mp->b_next = next; 732 break; 733 } 734 } else { 735 is_unicast = !(IS_BROADCAST(ehp) || 736 (IS_MULTICAST(ehp))); 737 /* 738 * Destination is not in FDB. 739 * If the destination is broadcast or multicast, 740 * then forward the packet to vswitch. 741 * If a Hybrid resource avilable, then send the 742 * unicast packet via hybrid resource, otherwise 743 * forward it to vswitch. 744 */ 745 READ_ENTER(&vnetp->vsw_fp_rw); 746 747 if ((is_unicast) && (vnetp->hio_fp != NULL)) { 748 vresp = vnetp->hio_fp; 749 hres = B_TRUE; 750 } else { 751 vresp = vnetp->vsw_fp; 752 hres = B_FALSE; 753 } 754 if (vresp == NULL) { 755 /* 756 * no fdb entry to vsw? drop the packet. 757 */ 758 RW_EXIT(&vnetp->vsw_fp_rw); 759 freemsg(mp); 760 mp = next; 761 continue; 762 } 763 764 /* ref hold the fdb entry to vsw */ 765 VNET_FDBE_REFHOLD(vresp); 766 767 RW_EXIT(&vnetp->vsw_fp_rw); 768 769 /* 770 * In the case of a hybrid resource we need to insert 771 * the tag for the pvid case here; unlike packets that 772 * are destined to a vnet/vsw in which case the vgen 773 * layer does the tagging before sending it over ldc. 774 */ 775 if (hres == B_TRUE) { 776 /* 777 * Determine if the frame being transmitted 778 * over the hybrid resource is untagged. If so, 779 * insert the tag before transmitting. 780 */ 781 if (is_pvid == B_TRUE && 782 ehp->ether_type != htons(ETHERTYPE_VLAN)) { 783 784 mp = vnet_vlan_insert_tag(mp, 785 vnetp->pvid); 786 if (mp == NULL) { 787 VNET_FDBE_REFRELE(vresp); 788 mp = next; 789 continue; 790 } 791 792 } 793 } 794 795 macp = &vresp->macreg; 796 resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp); 797 798 /* tx done; now release ref on fdb entry */ 799 VNET_FDBE_REFRELE(vresp); 800 801 if (resid_mp != NULL) { 802 /* m_tx failed */ 803 mp->b_next = next; 804 break; 805 } 806 } 807 808 mp = next; 809 } 810 811 DBG1(vnetp, "exit\n"); 812 return (mp); 813 } 814 815 /* get statistics from the device */ 816 int 817 vnet_m_stat(void *arg, uint_t stat, uint64_t *val) 818 { 819 vnet_t *vnetp = arg; 820 vnet_res_t *vresp; 821 mac_register_t *macp; 822 mac_callbacks_t *cbp; 823 uint64_t val_total = 0; 824 825 DBG1(vnetp, "enter\n"); 826 827 /* 828 * get the specified statistic from each transport and return the 829 * aggregate val. This obviously only works for counters. 830 */ 831 if ((IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat)) || 832 (IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat))) { 833 return (ENOTSUP); 834 } 835 836 READ_ENTER(&vnetp->vrwlock); 837 for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) { 838 macp = &vresp->macreg; 839 cbp = macp->m_callbacks; 840 if (cbp->mc_getstat(macp->m_driver, stat, val) == 0) 841 val_total += *val; 842 } 843 RW_EXIT(&vnetp->vrwlock); 844 845 *val = val_total; 846 847 DBG1(vnetp, "exit\n"); 848 return (0); 849 } 850 851 /* wrapper function for mac_register() */ 852 static int 853 vnet_mac_register(vnet_t *vnetp) 854 { 855 mac_register_t *macp; 856 int err; 857 858 if ((macp = mac_alloc(MAC_VERSION)) == NULL) 859 return (DDI_FAILURE); 860 macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 861 macp->m_driver = vnetp; 862 macp->m_dip = vnetp->dip; 863 macp->m_src_addr = vnetp->curr_macaddr; 864 macp->m_callbacks = &vnet_m_callbacks; 865 macp->m_min_sdu = 0; 866 macp->m_max_sdu = vnetp->mtu; 867 macp->m_margin = VLAN_TAGSZ; 868 869 /* 870 * Finally, we're ready to register ourselves with the MAC layer 871 * interface; if this succeeds, we're all ready to start() 872 */ 873 err = mac_register(macp, &vnetp->mh); 874 mac_free(macp); 875 return (err == 0 ? DDI_SUCCESS : DDI_FAILURE); 876 } 877 878 /* read the mac address of the device */ 879 static int 880 vnet_read_mac_address(vnet_t *vnetp) 881 { 882 uchar_t *macaddr; 883 uint32_t size; 884 int rv; 885 886 rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip, 887 DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size); 888 if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) { 889 DWARN(vnetp, "prop_lookup failed(%s) err(%d)\n", 890 macaddr_propname, rv); 891 return (DDI_FAILURE); 892 } 893 bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL); 894 bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL); 895 ddi_prop_free(macaddr); 896 897 return (DDI_SUCCESS); 898 } 899 900 static void 901 vnet_fdb_create(vnet_t *vnetp) 902 { 903 char hashname[MAXNAMELEN]; 904 905 (void) snprintf(hashname, MAXNAMELEN, "vnet%d-fdbhash", 906 vnetp->instance); 907 vnetp->fdb_nchains = vnet_fdb_nchains; 908 vnetp->fdb_hashp = mod_hash_create_ptrhash(hashname, vnetp->fdb_nchains, 909 mod_hash_null_valdtor, sizeof (void *)); 910 } 911 912 static void 913 vnet_fdb_destroy(vnet_t *vnetp) 914 { 915 /* destroy fdb-hash-table */ 916 if (vnetp->fdb_hashp != NULL) { 917 mod_hash_destroy_hash(vnetp->fdb_hashp); 918 vnetp->fdb_hashp = NULL; 919 vnetp->fdb_nchains = 0; 920 } 921 } 922 923 /* 924 * Add an entry into the fdb. 925 */ 926 void 927 vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp) 928 { 929 uint64_t addr = 0; 930 int rv; 931 932 KEY_HASH(addr, vresp->rem_macaddr); 933 934 /* 935 * If the entry being added corresponds to LDC_SERVICE resource, 936 * that is, vswitch connection, it is added to the hash and also 937 * the entry is cached, an additional reference count reflects 938 * this. The HYBRID resource is not added to the hash, but only 939 * cached, as it is only used for sending out packets for unknown 940 * unicast destinations. 941 */ 942 (vresp->type == VIO_NET_RES_LDC_SERVICE) ? 943 (vresp->refcnt = 1) : (vresp->refcnt = 0); 944 945 /* 946 * Note: duplicate keys will be rejected by mod_hash. 947 */ 948 if (vresp->type != VIO_NET_RES_HYBRID) { 949 rv = mod_hash_insert(vnetp->fdb_hashp, (mod_hash_key_t)addr, 950 (mod_hash_val_t)vresp); 951 if (rv != 0) { 952 DWARN(vnetp, "Duplicate macaddr key(%lx)\n", addr); 953 return; 954 } 955 } 956 957 if (vresp->type == VIO_NET_RES_LDC_SERVICE) { 958 /* Cache the fdb entry to vsw-port */ 959 WRITE_ENTER(&vnetp->vsw_fp_rw); 960 if (vnetp->vsw_fp == NULL) 961 vnetp->vsw_fp = vresp; 962 RW_EXIT(&vnetp->vsw_fp_rw); 963 } else if (vresp->type == VIO_NET_RES_HYBRID) { 964 /* Cache the fdb entry to hybrid resource */ 965 WRITE_ENTER(&vnetp->vsw_fp_rw); 966 if (vnetp->hio_fp == NULL) 967 vnetp->hio_fp = vresp; 968 RW_EXIT(&vnetp->vsw_fp_rw); 969 } 970 } 971 972 /* 973 * Remove an entry from fdb. 974 */ 975 static void 976 vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp) 977 { 978 uint64_t addr = 0; 979 int rv; 980 uint32_t refcnt; 981 vnet_res_t *tmp; 982 983 KEY_HASH(addr, vresp->rem_macaddr); 984 985 /* 986 * Remove the entry from fdb hash table. 987 * This prevents further references to this fdb entry. 988 */ 989 if (vresp->type != VIO_NET_RES_HYBRID) { 990 rv = mod_hash_remove(vnetp->fdb_hashp, (mod_hash_key_t)addr, 991 (mod_hash_val_t *)&tmp); 992 if (rv != 0) { 993 /* 994 * As the resources are added to the hash only 995 * after they are started, this can occur if 996 * a resource unregisters before it is ever started. 997 */ 998 return; 999 } 1000 } 1001 1002 if (vresp->type == VIO_NET_RES_LDC_SERVICE) { 1003 WRITE_ENTER(&vnetp->vsw_fp_rw); 1004 1005 ASSERT(tmp == vnetp->vsw_fp); 1006 vnetp->vsw_fp = NULL; 1007 1008 RW_EXIT(&vnetp->vsw_fp_rw); 1009 } else if (vresp->type == VIO_NET_RES_HYBRID) { 1010 WRITE_ENTER(&vnetp->vsw_fp_rw); 1011 1012 vnetp->hio_fp = NULL; 1013 1014 RW_EXIT(&vnetp->vsw_fp_rw); 1015 } 1016 1017 /* 1018 * If there are threads already ref holding before the entry was 1019 * removed from hash table, then wait for ref count to drop to zero. 1020 */ 1021 (vresp->type == VIO_NET_RES_LDC_SERVICE) ? 1022 (refcnt = 1) : (refcnt = 0); 1023 while (vresp->refcnt > refcnt) { 1024 delay(drv_usectohz(vnet_fdbe_refcnt_delay)); 1025 } 1026 } 1027 1028 /* 1029 * Search fdb for a given mac address. If an entry is found, hold 1030 * a reference to it and return the entry; else returns NULL. 1031 */ 1032 static vnet_res_t * 1033 vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp) 1034 { 1035 uint64_t key = 0; 1036 vnet_res_t *vresp; 1037 int rv; 1038 1039 KEY_HASH(key, addrp->ether_addr_octet); 1040 1041 rv = mod_hash_find_cb(vnetp->fdb_hashp, (mod_hash_key_t)key, 1042 (mod_hash_val_t *)&vresp, vnet_fdbe_find_cb); 1043 1044 if (rv != 0) 1045 return (NULL); 1046 1047 return (vresp); 1048 } 1049 1050 /* 1051 * Callback function provided to mod_hash_find_cb(). After finding the fdb 1052 * entry corresponding to the key (macaddr), this callback will be invoked by 1053 * mod_hash_find_cb() to atomically increment the reference count on the fdb 1054 * entry before returning the found entry. 1055 */ 1056 static void 1057 vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val) 1058 { 1059 _NOTE(ARGUNUSED(key)) 1060 VNET_FDBE_REFHOLD((vnet_res_t *)val); 1061 } 1062 1063 /* 1064 * Frames received that are tagged with the pvid of the vnet device must be 1065 * untagged before sending up the stack. This function walks the chain of rx 1066 * frames, untags any such frames and returns the updated chain. 1067 * 1068 * Arguments: 1069 * pvid: pvid of the vnet device for which packets are being received 1070 * mp: head of pkt chain to be validated and untagged 1071 * 1072 * Returns: 1073 * mp: head of updated chain of packets 1074 */ 1075 static void 1076 vnet_rx_frames_untag(uint16_t pvid, mblk_t **mp) 1077 { 1078 struct ether_vlan_header *evhp; 1079 mblk_t *bp; 1080 mblk_t *bpt; 1081 mblk_t *bph; 1082 mblk_t *bpn; 1083 1084 bpn = bph = bpt = NULL; 1085 1086 for (bp = *mp; bp != NULL; bp = bpn) { 1087 1088 bpn = bp->b_next; 1089 bp->b_next = bp->b_prev = NULL; 1090 1091 evhp = (struct ether_vlan_header *)bp->b_rptr; 1092 1093 if (ntohs(evhp->ether_tpid) == ETHERTYPE_VLAN && 1094 VLAN_ID(ntohs(evhp->ether_tci)) == pvid) { 1095 1096 bp = vnet_vlan_remove_tag(bp); 1097 if (bp == NULL) { 1098 continue; 1099 } 1100 1101 } 1102 1103 /* build a chain of processed packets */ 1104 if (bph == NULL) { 1105 bph = bpt = bp; 1106 } else { 1107 bpt->b_next = bp; 1108 bpt = bp; 1109 } 1110 1111 } 1112 1113 *mp = bph; 1114 } 1115 1116 static void 1117 vnet_rx(vio_net_handle_t vrh, mblk_t *mp) 1118 { 1119 vnet_res_t *vresp = (vnet_res_t *)vrh; 1120 vnet_t *vnetp = vresp->vnetp; 1121 1122 if ((vnetp == NULL) || (vnetp->mh == 0)) { 1123 freemsgchain(mp); 1124 return; 1125 } 1126 1127 /* 1128 * Packets received over a hybrid resource need additional processing 1129 * to remove the tag, for the pvid case. The underlying resource is 1130 * not aware of the vnet's pvid and thus packets are received with the 1131 * vlan tag in the header; unlike packets that are received over a ldc 1132 * channel in which case the peer vnet/vsw would have already removed 1133 * the tag. 1134 */ 1135 if (vresp->type == VIO_NET_RES_HYBRID && 1136 vnetp->pvid != vnetp->default_vlan_id) { 1137 1138 vnet_rx_frames_untag(vnetp->pvid, &mp); 1139 if (mp == NULL) { 1140 return; 1141 } 1142 } 1143 1144 mac_rx(vnetp->mh, NULL, mp); 1145 } 1146 1147 void 1148 vnet_tx_update(vio_net_handle_t vrh) 1149 { 1150 vnet_res_t *vresp = (vnet_res_t *)vrh; 1151 vnet_t *vnetp = vresp->vnetp; 1152 1153 if ((vnetp != NULL) && (vnetp->mh != NULL)) { 1154 mac_tx_update(vnetp->mh); 1155 } 1156 } 1157 1158 /* 1159 * Update the new mtu of vnet into the mac layer. First check if the device has 1160 * been plumbed and if so fail the mtu update. Returns 0 on success. 1161 */ 1162 int 1163 vnet_mtu_update(vnet_t *vnetp, uint32_t mtu) 1164 { 1165 int rv; 1166 1167 if (vnetp == NULL || vnetp->mh == NULL) { 1168 return (EINVAL); 1169 } 1170 1171 WRITE_ENTER(&vnetp->vrwlock); 1172 1173 if (vnetp->flags & VNET_STARTED) { 1174 RW_EXIT(&vnetp->vrwlock); 1175 cmn_err(CE_NOTE, "!vnet%d: Unable to process mtu " 1176 "update as the device is plumbed\n", 1177 vnetp->instance); 1178 return (EBUSY); 1179 } 1180 1181 /* update mtu in the mac layer */ 1182 rv = mac_maxsdu_update(vnetp->mh, mtu); 1183 if (rv != 0) { 1184 RW_EXIT(&vnetp->vrwlock); 1185 cmn_err(CE_NOTE, 1186 "!vnet%d: Unable to update mtu with mac layer\n", 1187 vnetp->instance); 1188 return (EIO); 1189 } 1190 1191 vnetp->mtu = mtu; 1192 1193 RW_EXIT(&vnetp->vrwlock); 1194 1195 return (0); 1196 } 1197 1198 /* 1199 * Update the link state of vnet to the mac layer. 1200 */ 1201 void 1202 vnet_link_update(vnet_t *vnetp, link_state_t link_state) 1203 { 1204 if (vnetp == NULL || vnetp->mh == NULL) { 1205 return; 1206 } 1207 1208 WRITE_ENTER(&vnetp->vrwlock); 1209 if (vnetp->link_state == link_state) { 1210 RW_EXIT(&vnetp->vrwlock); 1211 return; 1212 } 1213 vnetp->link_state = link_state; 1214 RW_EXIT(&vnetp->vrwlock); 1215 1216 mac_link_update(vnetp->mh, link_state); 1217 } 1218 1219 /* 1220 * vio_net_resource_reg -- An interface called to register a resource 1221 * with vnet. 1222 * macp -- a GLDv3 mac_register that has all the details of 1223 * a resource and its callbacks etc. 1224 * type -- resource type. 1225 * local_macaddr -- resource's MAC address. This is used to 1226 * associate a resource with a corresponding vnet. 1227 * remote_macaddr -- remote side MAC address. This is ignored for 1228 * the Hybrid resources. 1229 * vhp -- A handle returned to the caller. 1230 * vcb -- A set of callbacks provided to the callers. 1231 */ 1232 int vio_net_resource_reg(mac_register_t *macp, vio_net_res_type_t type, 1233 ether_addr_t local_macaddr, ether_addr_t rem_macaddr, vio_net_handle_t *vhp, 1234 vio_net_callbacks_t *vcb) 1235 { 1236 vnet_t *vnetp; 1237 vnet_res_t *vresp; 1238 1239 vresp = kmem_zalloc(sizeof (vnet_res_t), KM_SLEEP); 1240 ether_copy(local_macaddr, vresp->local_macaddr); 1241 ether_copy(rem_macaddr, vresp->rem_macaddr); 1242 vresp->type = type; 1243 bcopy(macp, &vresp->macreg, sizeof (mac_register_t)); 1244 1245 DBG1(NULL, "Resource Registerig type=0%X\n", type); 1246 1247 READ_ENTER(&vnet_rw); 1248 vnetp = vnet_headp; 1249 while (vnetp != NULL) { 1250 if (VNET_MATCH_RES(vresp, vnetp)) { 1251 vresp->vnetp = vnetp; 1252 1253 /* Setup kstats for hio resource */ 1254 if (vresp->type == VIO_NET_RES_HYBRID) { 1255 vresp->ksp = vnet_hio_setup_kstats(DRV_NAME, 1256 "hio", vresp); 1257 if (vresp->ksp == NULL) { 1258 cmn_err(CE_NOTE, "!vnet%d: Cannot " 1259 "create kstats for hio resource", 1260 vnetp->instance); 1261 } 1262 } 1263 1264 WRITE_ENTER(&vnetp->vrwlock); 1265 vresp->nextp = vnetp->vres_list; 1266 vnetp->vres_list = vresp; 1267 RW_EXIT(&vnetp->vrwlock); 1268 break; 1269 } 1270 vnetp = vnetp->nextp; 1271 } 1272 RW_EXIT(&vnet_rw); 1273 if (vresp->vnetp == NULL) { 1274 DWARN(NULL, "No vnet instance"); 1275 kmem_free(vresp, sizeof (vnet_res_t)); 1276 return (ENXIO); 1277 } 1278 1279 *vhp = vresp; 1280 vcb->vio_net_rx_cb = vnet_rx; 1281 vcb->vio_net_tx_update = vnet_tx_update; 1282 vcb->vio_net_report_err = vnet_handle_res_err; 1283 1284 /* Dispatch a task to start resources */ 1285 vnet_dispatch_res_task(vnetp); 1286 return (0); 1287 } 1288 1289 /* 1290 * vio_net_resource_unreg -- An interface to unregister a resource. 1291 */ 1292 void 1293 vio_net_resource_unreg(vio_net_handle_t vhp) 1294 { 1295 vnet_res_t *vresp = (vnet_res_t *)vhp; 1296 vnet_t *vnetp = vresp->vnetp; 1297 vnet_res_t *vrp; 1298 kstat_t *ksp = NULL; 1299 1300 DBG1(NULL, "Resource Registerig hdl=0x%p", vhp); 1301 1302 ASSERT(vnetp != NULL); 1303 /* 1304 * Remove the resource from fdb; this ensures 1305 * there are no references to the resource. 1306 */ 1307 vnet_fdbe_del(vnetp, vresp); 1308 1309 /* Now remove the resource from the list */ 1310 WRITE_ENTER(&vnetp->vrwlock); 1311 if (vresp == vnetp->vres_list) { 1312 vnetp->vres_list = vresp->nextp; 1313 } else { 1314 vrp = vnetp->vres_list; 1315 while (vrp->nextp != NULL) { 1316 if (vrp->nextp == vresp) { 1317 vrp->nextp = vresp->nextp; 1318 break; 1319 } 1320 vrp = vrp->nextp; 1321 } 1322 } 1323 1324 ksp = vresp->ksp; 1325 vresp->ksp = NULL; 1326 1327 vresp->vnetp = NULL; 1328 vresp->nextp = NULL; 1329 RW_EXIT(&vnetp->vrwlock); 1330 vnet_hio_destroy_kstats(ksp); 1331 KMEM_FREE(vresp); 1332 } 1333 1334 /* 1335 * vnet_dds_rx -- an interface called by vgen to DDS messages. 1336 */ 1337 void 1338 vnet_dds_rx(void *arg, void *dmsg) 1339 { 1340 vnet_t *vnetp = arg; 1341 vdds_process_dds_msg(vnetp, dmsg); 1342 } 1343 1344 /* 1345 * vnet_send_dds_msg -- An interface provided to DDS to send 1346 * DDS messages. This simply sends meessages via vgen. 1347 */ 1348 int 1349 vnet_send_dds_msg(vnet_t *vnetp, void *dmsg) 1350 { 1351 int rv; 1352 1353 if (vnetp->vgenhdl != NULL) { 1354 rv = vgen_dds_tx(vnetp->vgenhdl, dmsg); 1355 } 1356 return (rv); 1357 } 1358 1359 /* 1360 * vnet_cleanup_hio -- an interface called by vgen to cleanup hio resources. 1361 */ 1362 void 1363 vnet_dds_cleanup_hio(vnet_t *vnetp) 1364 { 1365 vdds_cleanup_hio(vnetp); 1366 } 1367 1368 /* 1369 * vnet_handle_res_err -- A callback function called by a resource 1370 * to report an error. For example, vgen can call to report 1371 * an LDC down/reset event. This will trigger cleanup of associated 1372 * Hybrid resource. 1373 */ 1374 /* ARGSUSED */ 1375 static void 1376 vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err) 1377 { 1378 vnet_res_t *vresp = (vnet_res_t *)vrh; 1379 vnet_t *vnetp = vresp->vnetp; 1380 1381 if (vnetp == NULL) { 1382 return; 1383 } 1384 if ((vresp->type != VIO_NET_RES_LDC_SERVICE) && 1385 (vresp->type != VIO_NET_RES_HYBRID)) { 1386 return; 1387 } 1388 1389 vdds_cleanup_hio(vnetp); 1390 } 1391 1392 /* 1393 * vnet_dispatch_res_task -- A function to dispatch tasks start resources. 1394 */ 1395 static void 1396 vnet_dispatch_res_task(vnet_t *vnetp) 1397 { 1398 int rv; 1399 1400 /* 1401 * Dispatch the task. It could be the case that vnetp->flags does 1402 * not have VNET_STARTED set. This is ok as vnet_rest_start_task() 1403 * can abort the task when the task is started. See related comments 1404 * in vnet_m_stop() and vnet_stop_resources(). 1405 */ 1406 rv = ddi_taskq_dispatch(vnetp->taskqp, vnet_res_start_task, 1407 vnetp, DDI_NOSLEEP); 1408 if (rv != DDI_SUCCESS) { 1409 cmn_err(CE_WARN, 1410 "vnet%d:Can't dispatch start resource task", 1411 vnetp->instance); 1412 } 1413 } 1414 1415 /* 1416 * vnet_res_start_task -- A taskq callback function that starts a resource. 1417 */ 1418 static void 1419 vnet_res_start_task(void *arg) 1420 { 1421 vnet_t *vnetp = arg; 1422 1423 WRITE_ENTER(&vnetp->vrwlock); 1424 if (vnetp->flags & VNET_STARTED) { 1425 vnet_start_resources(vnetp); 1426 } 1427 RW_EXIT(&vnetp->vrwlock); 1428 } 1429 1430 /* 1431 * vnet_start_resources -- starts all resources associated with 1432 * a vnet. 1433 */ 1434 static void 1435 vnet_start_resources(vnet_t *vnetp) 1436 { 1437 mac_register_t *macp; 1438 mac_callbacks_t *cbp; 1439 vnet_res_t *vresp; 1440 int rv; 1441 1442 DBG1(vnetp, "enter\n"); 1443 1444 ASSERT(RW_WRITE_HELD(&vnetp->vrwlock)); 1445 1446 for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) { 1447 /* skip if it is already started */ 1448 if (vresp->flags & VNET_STARTED) { 1449 continue; 1450 } 1451 macp = &vresp->macreg; 1452 cbp = macp->m_callbacks; 1453 rv = cbp->mc_start(macp->m_driver); 1454 if (rv == 0) { 1455 /* 1456 * Successfully started the resource, so now 1457 * add it to the fdb. 1458 */ 1459 vresp->flags |= VNET_STARTED; 1460 vnet_fdbe_add(vnetp, vresp); 1461 } 1462 } 1463 1464 DBG1(vnetp, "exit\n"); 1465 1466 } 1467 1468 /* 1469 * vnet_stop_resources -- stop all resources associated with a vnet. 1470 */ 1471 static void 1472 vnet_stop_resources(vnet_t *vnetp) 1473 { 1474 vnet_res_t *vresp; 1475 mac_register_t *macp; 1476 mac_callbacks_t *cbp; 1477 1478 DBG1(vnetp, "enter\n"); 1479 1480 ASSERT(RW_WRITE_HELD(&vnetp->vrwlock)); 1481 1482 for (vresp = vnetp->vres_list; vresp != NULL; ) { 1483 if (vresp->flags & VNET_STARTED) { 1484 /* 1485 * Release the lock while invoking mc_stop() of the 1486 * underlying resource. We hold a reference to this 1487 * resource to prevent being removed from the list in 1488 * vio_net_resource_unreg(). Note that new resources 1489 * can be added to the head of the list while the lock 1490 * is released, but they won't be started, as 1491 * VNET_STARTED flag has been cleared for the vnet 1492 * device in vnet_m_stop(). Also, while the lock is 1493 * released a resource could be removed from the list 1494 * in vio_net_resource_unreg(); but that is ok, as we 1495 * re-acquire the lock and only then access the forward 1496 * link (vresp->nextp) to continue with the next 1497 * resource. 1498 */ 1499 vresp->flags &= ~VNET_STARTED; 1500 vresp->flags |= VNET_STOPPING; 1501 macp = &vresp->macreg; 1502 cbp = macp->m_callbacks; 1503 VNET_FDBE_REFHOLD(vresp); 1504 RW_EXIT(&vnetp->vrwlock); 1505 1506 cbp->mc_stop(macp->m_driver); 1507 1508 WRITE_ENTER(&vnetp->vrwlock); 1509 vresp->flags &= ~VNET_STOPPING; 1510 VNET_FDBE_REFRELE(vresp); 1511 } 1512 vresp = vresp->nextp; 1513 } 1514 DBG1(vnetp, "exit\n"); 1515 } 1516 1517 /* 1518 * Setup kstats for the HIO statistics. 1519 * NOTE: the synchronization for the statistics is the 1520 * responsibility of the caller. 1521 */ 1522 kstat_t * 1523 vnet_hio_setup_kstats(char *ks_mod, char *ks_name, vnet_res_t *vresp) 1524 { 1525 kstat_t *ksp; 1526 vnet_t *vnetp = vresp->vnetp; 1527 vnet_hio_kstats_t *hiokp; 1528 size_t size; 1529 1530 ASSERT(vnetp != NULL); 1531 size = sizeof (vnet_hio_kstats_t) / sizeof (kstat_named_t); 1532 ksp = kstat_create(ks_mod, vnetp->instance, ks_name, "net", 1533 KSTAT_TYPE_NAMED, size, 0); 1534 if (ksp == NULL) { 1535 return (NULL); 1536 } 1537 1538 hiokp = (vnet_hio_kstats_t *)ksp->ks_data; 1539 kstat_named_init(&hiokp->ipackets, "ipackets", 1540 KSTAT_DATA_ULONG); 1541 kstat_named_init(&hiokp->ierrors, "ierrors", 1542 KSTAT_DATA_ULONG); 1543 kstat_named_init(&hiokp->opackets, "opackets", 1544 KSTAT_DATA_ULONG); 1545 kstat_named_init(&hiokp->oerrors, "oerrors", 1546 KSTAT_DATA_ULONG); 1547 1548 1549 /* MIB II kstat variables */ 1550 kstat_named_init(&hiokp->rbytes, "rbytes", 1551 KSTAT_DATA_ULONG); 1552 kstat_named_init(&hiokp->obytes, "obytes", 1553 KSTAT_DATA_ULONG); 1554 kstat_named_init(&hiokp->multircv, "multircv", 1555 KSTAT_DATA_ULONG); 1556 kstat_named_init(&hiokp->multixmt, "multixmt", 1557 KSTAT_DATA_ULONG); 1558 kstat_named_init(&hiokp->brdcstrcv, "brdcstrcv", 1559 KSTAT_DATA_ULONG); 1560 kstat_named_init(&hiokp->brdcstxmt, "brdcstxmt", 1561 KSTAT_DATA_ULONG); 1562 kstat_named_init(&hiokp->norcvbuf, "norcvbuf", 1563 KSTAT_DATA_ULONG); 1564 kstat_named_init(&hiokp->noxmtbuf, "noxmtbuf", 1565 KSTAT_DATA_ULONG); 1566 1567 ksp->ks_update = vnet_hio_update_kstats; 1568 ksp->ks_private = (void *)vresp; 1569 kstat_install(ksp); 1570 return (ksp); 1571 } 1572 1573 /* 1574 * Destroy kstats. 1575 */ 1576 static void 1577 vnet_hio_destroy_kstats(kstat_t *ksp) 1578 { 1579 if (ksp != NULL) 1580 kstat_delete(ksp); 1581 } 1582 1583 /* 1584 * Update the kstats. 1585 */ 1586 static int 1587 vnet_hio_update_kstats(kstat_t *ksp, int rw) 1588 { 1589 vnet_t *vnetp; 1590 vnet_res_t *vresp; 1591 vnet_hio_stats_t statsp; 1592 vnet_hio_kstats_t *hiokp; 1593 1594 vresp = (vnet_res_t *)ksp->ks_private; 1595 vnetp = vresp->vnetp; 1596 1597 bzero(&statsp, sizeof (vnet_hio_stats_t)); 1598 1599 READ_ENTER(&vnetp->vsw_fp_rw); 1600 if (vnetp->hio_fp == NULL) { 1601 /* not using hio resources, just return */ 1602 RW_EXIT(&vnetp->vsw_fp_rw); 1603 return (0); 1604 } 1605 VNET_FDBE_REFHOLD(vnetp->hio_fp); 1606 RW_EXIT(&vnetp->vsw_fp_rw); 1607 vnet_hio_get_stats(vnetp->hio_fp, &statsp); 1608 VNET_FDBE_REFRELE(vnetp->hio_fp); 1609 1610 hiokp = (vnet_hio_kstats_t *)ksp->ks_data; 1611 1612 if (rw == KSTAT_READ) { 1613 /* Link Input/Output stats */ 1614 hiokp->ipackets.value.ul = (uint32_t)statsp.ipackets; 1615 hiokp->ipackets64.value.ull = statsp.ipackets; 1616 hiokp->ierrors.value.ul = statsp.ierrors; 1617 hiokp->opackets.value.ul = (uint32_t)statsp.opackets; 1618 hiokp->opackets64.value.ull = statsp.opackets; 1619 hiokp->oerrors.value.ul = statsp.oerrors; 1620 1621 /* MIB II kstat variables */ 1622 hiokp->rbytes.value.ul = (uint32_t)statsp.rbytes; 1623 hiokp->rbytes64.value.ull = statsp.rbytes; 1624 hiokp->obytes.value.ul = (uint32_t)statsp.obytes; 1625 hiokp->obytes64.value.ull = statsp.obytes; 1626 hiokp->multircv.value.ul = statsp.multircv; 1627 hiokp->multixmt.value.ul = statsp.multixmt; 1628 hiokp->brdcstrcv.value.ul = statsp.brdcstrcv; 1629 hiokp->brdcstxmt.value.ul = statsp.brdcstxmt; 1630 hiokp->norcvbuf.value.ul = statsp.norcvbuf; 1631 hiokp->noxmtbuf.value.ul = statsp.noxmtbuf; 1632 } else { 1633 return (EACCES); 1634 } 1635 1636 return (0); 1637 } 1638 1639 static void 1640 vnet_hio_get_stats(vnet_res_t *vresp, vnet_hio_stats_t *statsp) 1641 { 1642 mac_register_t *macp; 1643 mac_callbacks_t *cbp; 1644 uint64_t val; 1645 int stat; 1646 1647 /* 1648 * get the specified statistics from the underlying nxge. 1649 */ 1650 macp = &vresp->macreg; 1651 cbp = macp->m_callbacks; 1652 for (stat = MAC_STAT_MIN; stat < MAC_STAT_OVERFLOWS; stat++) { 1653 if (cbp->mc_getstat(macp->m_driver, stat, &val) == 0) { 1654 switch (stat) { 1655 case MAC_STAT_IPACKETS: 1656 statsp->ipackets = val; 1657 break; 1658 1659 case MAC_STAT_IERRORS: 1660 statsp->ierrors = val; 1661 break; 1662 1663 case MAC_STAT_OPACKETS: 1664 statsp->opackets = val; 1665 break; 1666 1667 case MAC_STAT_OERRORS: 1668 statsp->oerrors = val; 1669 break; 1670 1671 case MAC_STAT_RBYTES: 1672 statsp->rbytes = val; 1673 break; 1674 1675 case MAC_STAT_OBYTES: 1676 statsp->obytes = val; 1677 break; 1678 1679 case MAC_STAT_MULTIRCV: 1680 statsp->multircv = val; 1681 break; 1682 1683 case MAC_STAT_MULTIXMT: 1684 statsp->multixmt = val; 1685 break; 1686 1687 case MAC_STAT_BRDCSTRCV: 1688 statsp->brdcstrcv = val; 1689 break; 1690 1691 case MAC_STAT_BRDCSTXMT: 1692 statsp->brdcstxmt = val; 1693 break; 1694 1695 case MAC_STAT_NOXMTBUF: 1696 statsp->noxmtbuf = val; 1697 break; 1698 1699 case MAC_STAT_NORCVBUF: 1700 statsp->norcvbuf = val; 1701 break; 1702 1703 default: 1704 /* 1705 * parameters not interested. 1706 */ 1707 break; 1708 } 1709 } 1710 } 1711 } 1712 1713 #ifdef VNET_IOC_DEBUG 1714 1715 /* 1716 * The ioctl entry point is used only for debugging for now. The ioctl commands 1717 * can be used to force the link state of the channel connected to vsw. 1718 */ 1719 static void 1720 vnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 1721 { 1722 struct iocblk *iocp; 1723 vnet_t *vnetp; 1724 1725 iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 1726 iocp->ioc_error = 0; 1727 vnetp = (vnet_t *)arg; 1728 1729 if (vnetp == NULL) { 1730 miocnak(q, mp, 0, EINVAL); 1731 return; 1732 } 1733 1734 switch (iocp->ioc_cmd) { 1735 1736 case VNET_FORCE_LINK_DOWN: 1737 case VNET_FORCE_LINK_UP: 1738 vnet_force_link_state(vnetp, q, mp); 1739 break; 1740 1741 default: 1742 iocp->ioc_error = EINVAL; 1743 miocnak(q, mp, 0, iocp->ioc_error); 1744 break; 1745 1746 } 1747 } 1748 1749 static void 1750 vnet_force_link_state(vnet_t *vnetp, queue_t *q, mblk_t *mp) 1751 { 1752 mac_register_t *macp; 1753 mac_callbacks_t *cbp; 1754 vnet_res_t *vresp; 1755 1756 READ_ENTER(&vnetp->vsw_fp_rw); 1757 1758 vresp = vnetp->vsw_fp; 1759 if (vresp == NULL) { 1760 RW_EXIT(&vnetp->vsw_fp_rw); 1761 return; 1762 } 1763 1764 macp = &vresp->macreg; 1765 cbp = macp->m_callbacks; 1766 cbp->mc_ioctl(macp->m_driver, q, mp); 1767 1768 RW_EXIT(&vnetp->vsw_fp_rw); 1769 } 1770 1771 #else 1772 1773 static void 1774 vnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 1775 { 1776 vnet_t *vnetp; 1777 1778 vnetp = (vnet_t *)arg; 1779 1780 if (vnetp == NULL) { 1781 miocnak(q, mp, 0, EINVAL); 1782 return; 1783 } 1784 1785 /* ioctl support only for debugging */ 1786 miocnak(q, mp, 0, ENOTSUP); 1787 } 1788 1789 #endif 1790