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