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