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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/errno.h> 31 #include <sys/param.h> 32 #include <sys/stream.h> 33 #include <sys/kmem.h> 34 #include <sys/conf.h> 35 #include <sys/devops.h> 36 #include <sys/ksynch.h> 37 #include <sys/stat.h> 38 #include <sys/modctl.h> 39 #include <sys/debug.h> 40 #include <sys/ethernet.h> 41 #include <sys/dlpi.h> 42 #include <net/if.h> 43 #include <sys/mac.h> 44 #include <sys/mac_ether.h> 45 #include <sys/ddi.h> 46 #include <sys/sunddi.h> 47 #include <sys/strsun.h> 48 #include <sys/note.h> 49 #include <sys/vnet.h> 50 51 /* 52 * Function prototypes. 53 */ 54 55 /* DDI entrypoints */ 56 static int vnetdevinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 57 static int vnetattach(dev_info_t *, ddi_attach_cmd_t); 58 static int vnetdetach(dev_info_t *, ddi_detach_cmd_t); 59 60 /* MAC entrypoints */ 61 static int vnet_m_stat(void *, uint_t, uint64_t *); 62 static int vnet_m_start(void *); 63 static void vnet_m_stop(void *); 64 static int vnet_m_promisc(void *, boolean_t); 65 static int vnet_m_multicst(void *, boolean_t, const uint8_t *); 66 static int vnet_m_unicst(void *, const uint8_t *); 67 mblk_t *vnet_m_tx(void *, mblk_t *); 68 69 /* vnet internal functions */ 70 static int vnet_mac_register(vnet_t *); 71 static int vnet_read_mac_address(vnet_t *vnetp); 72 static void vnet_add_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp); 73 static void vnet_del_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp); 74 static vp_tl_t *vnet_get_vptl(vnet_t *vnetp, const char *devname); 75 static fdb_t *vnet_lookup_fdb(fdb_fanout_t *fdbhp, uint8_t *macaddr); 76 77 /* exported functions */ 78 void vnet_add_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg); 79 void vnet_del_fdb(void *arg, uint8_t *macaddr); 80 void vnet_modify_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, 81 void *txarg, boolean_t upgrade); 82 void vnet_add_def_rte(void *arg, mac_tx_t m_tx, void *txarg); 83 void vnet_del_def_rte(void *arg); 84 void vnet_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp); 85 void vnet_tx_update(void *arg); 86 87 /* externs */ 88 extern int vgen_init(void *vnetp, dev_info_t *vnetdip, const uint8_t *macaddr, 89 mac_register_t **vgenmacp); 90 extern int vgen_uninit(void *arg); 91 92 static mac_callbacks_t vnet_m_callbacks = { 93 0, 94 vnet_m_stat, 95 vnet_m_start, 96 vnet_m_stop, 97 vnet_m_promisc, 98 vnet_m_multicst, 99 vnet_m_unicst, 100 vnet_m_tx, 101 NULL, 102 NULL, 103 NULL 104 }; 105 106 /* 107 * Linked list of "vnet_t" structures - one per instance. 108 */ 109 static vnet_t *vnet_headp = NULL; 110 static krwlock_t vnet_rw; 111 112 /* Tunables */ 113 uint32_t vnet_ntxds = VNET_NTXDS; /* power of 2 transmit descriptors */ 114 uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */ 115 uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT; /* tx timeout in msec */ 116 uint32_t vnet_ldc_mtu = VNET_LDC_MTU; /* ldc mtu */ 117 uint32_t vnet_nfdb_hash = VNET_NFDB_HASH; /* size of fdb hash table */ 118 119 /* 120 * Property names 121 */ 122 static char macaddr_propname[] = "local-mac-address"; 123 124 /* 125 * This is the string displayed by modinfo(1m). 126 */ 127 static char vnet_ident[] = "vnet driver v%I%"; 128 extern struct mod_ops mod_driverops; 129 static struct cb_ops cb_vnetops = { 130 nulldev, /* cb_open */ 131 nulldev, /* cb_close */ 132 nodev, /* cb_strategy */ 133 nodev, /* cb_print */ 134 nodev, /* cb_dump */ 135 nodev, /* cb_read */ 136 nodev, /* cb_write */ 137 nodev, /* cb_ioctl */ 138 nodev, /* cb_devmap */ 139 nodev, /* cb_mmap */ 140 nodev, /* cb_segmap */ 141 nochpoll, /* cb_chpoll */ 142 ddi_prop_op, /* cb_prop_op */ 143 NULL, /* cb_stream */ 144 (int)(D_MP) /* cb_flag */ 145 }; 146 147 static struct dev_ops vnetops = { 148 DEVO_REV, /* devo_rev */ 149 0, /* devo_refcnt */ 150 NULL, /* devo_getinfo */ 151 nulldev, /* devo_identify */ 152 nulldev, /* devo_probe */ 153 vnetattach, /* devo_attach */ 154 vnetdetach, /* devo_detach */ 155 nodev, /* devo_reset */ 156 &cb_vnetops, /* devo_cb_ops */ 157 (struct bus_ops *)NULL /* devo_bus_ops */ 158 }; 159 160 static struct modldrv modldrv = { 161 &mod_driverops, /* Type of module. This one is a driver */ 162 vnet_ident, /* ID string */ 163 &vnetops /* driver specific ops */ 164 }; 165 166 static struct modlinkage modlinkage = { 167 MODREV_1, (void *)&modldrv, NULL 168 }; 169 170 #ifdef DEBUG 171 172 /* 173 * Print debug messages - set to 0xf to enable all msgs 174 */ 175 int vnet_dbglevel = 0x8; 176 177 static void 178 debug_printf(const char *fname, void *arg, const char *fmt, ...) 179 { 180 char buf[512]; 181 va_list ap; 182 vnet_t *vnetp = (vnet_t *)arg; 183 char *bufp = buf; 184 185 if (vnetp == NULL) { 186 (void) sprintf(bufp, "%s: ", fname); 187 bufp += strlen(bufp); 188 } else { 189 (void) sprintf(bufp, "vnet%d:%s: ", vnetp->instance, fname); 190 bufp += strlen(bufp); 191 } 192 va_start(ap, fmt); 193 (void) vsprintf(bufp, fmt, ap); 194 va_end(ap); 195 cmn_err(CE_CONT, "%s\n", buf); 196 } 197 198 #endif 199 200 /* _init(9E): initialize the loadable module */ 201 int 202 _init(void) 203 { 204 int status; 205 206 DBG1(NULL, "enter\n"); 207 208 mac_init_ops(&vnetops, "vnet"); 209 status = mod_install(&modlinkage); 210 if (status != 0) { 211 mac_fini_ops(&vnetops); 212 } 213 214 DBG1(NULL, "exit(%d)\n", status); 215 return (status); 216 } 217 218 /* _fini(9E): prepare the module for unloading. */ 219 int 220 _fini(void) 221 { 222 int status; 223 224 DBG1(NULL, "enter\n"); 225 226 status = mod_remove(&modlinkage); 227 if (status != 0) 228 return (status); 229 mac_fini_ops(&vnetops); 230 231 DBG1(NULL, "exit(%d)\n", status); 232 return (status); 233 } 234 235 /* _info(9E): return information about the loadable module */ 236 int 237 _info(struct modinfo *modinfop) 238 { 239 return (mod_info(&modlinkage, modinfop)); 240 } 241 242 /* 243 * attach(9E): attach a device to the system. 244 * called once for each instance of the device on the system. 245 */ 246 static int 247 vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd) 248 { 249 vnet_t *vnetp; 250 vp_tl_t *vp_tlp; 251 int instance; 252 int status; 253 mac_register_t *vgenmacp = NULL; 254 uint32_t nfdbh = 0; 255 enum { AST_init = 0x0, AST_vnet_alloc = 0x1, 256 AST_mac_alloc = 0x2, AST_read_macaddr = 0x4, 257 AST_vgen_init = 0x8, AST_vptl_alloc = 0x10, 258 AST_fdbh_alloc = 0x20 } attach_state; 259 260 attach_state = AST_init; 261 262 switch (cmd) { 263 case DDI_ATTACH: 264 break; 265 case DDI_RESUME: 266 case DDI_PM_RESUME: 267 default: 268 goto vnet_attach_fail; 269 } 270 271 instance = ddi_get_instance(dip); 272 DBG1(NULL, "instance(%d) enter\n", instance); 273 274 /* allocate vnet_t and mac_t structures */ 275 vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP); 276 attach_state |= AST_vnet_alloc; 277 278 /* setup links to vnet_t from both devinfo and mac_t */ 279 ddi_set_driver_private(dip, (caddr_t)vnetp); 280 vnetp->dip = dip; 281 vnetp->instance = instance; 282 283 /* read the mac address */ 284 status = vnet_read_mac_address(vnetp); 285 if (status != DDI_SUCCESS) { 286 goto vnet_attach_fail; 287 } 288 attach_state |= AST_read_macaddr; 289 290 /* 291 * Initialize the generic vnet proxy transport. This is the first 292 * and default transport used by vnet. The generic transport 293 * is provided by using sun4v LDC (logical domain channel). On success, 294 * vgen_init() provides a pointer to mac_t of generic transport. 295 * Currently, this generic layer provides network connectivity to other 296 * vnets within ldoms and also to remote hosts oustide ldoms through 297 * the virtual switch (vsw) device on domain0. In the future, when 298 * physical adapters that are able to share their resources (such as 299 * dma channels) with guest domains become available, the vnet device 300 * will use hardware specific driver to communicate directly over the 301 * physical device to reach remote hosts without going through vswitch. 302 */ 303 status = vgen_init(vnetp, vnetp->dip, (uint8_t *)vnetp->curr_macaddr, 304 &vgenmacp); 305 if (status != DDI_SUCCESS) { 306 DERR(vnetp, "vgen_init() failed\n"); 307 goto vnet_attach_fail; 308 } 309 attach_state |= AST_vgen_init; 310 311 vp_tlp = kmem_zalloc(sizeof (vp_tl_t), KM_SLEEP); 312 vp_tlp->macp = vgenmacp; 313 (void) snprintf(vp_tlp->name, MAXNAMELEN, "%s%u", "vgen", instance); 314 (void) strcpy(vnetp->vgen_name, vp_tlp->name); 315 316 /* add generic transport to the list of vnet proxy transports */ 317 vnet_add_vptl(vnetp, vp_tlp); 318 attach_state |= AST_vptl_alloc; 319 320 nfdbh = vnet_nfdb_hash; 321 if ((nfdbh < VNET_NFDB_HASH) || (nfdbh > VNET_NFDB_HASH_MAX)) { 322 vnetp->nfdb_hash = VNET_NFDB_HASH; 323 } 324 else 325 vnetp->nfdb_hash = nfdbh; 326 327 /* allocate fdb hash table, with an extra slot for default route */ 328 vnetp->fdbhp = kmem_zalloc(sizeof (fdb_fanout_t) * 329 (vnetp->nfdb_hash + 1), KM_SLEEP); 330 attach_state |= AST_fdbh_alloc; 331 332 /* register with MAC layer */ 333 status = vnet_mac_register(vnetp); 334 if (status != DDI_SUCCESS) { 335 goto vnet_attach_fail; 336 } 337 338 /* add to the list of vnet devices */ 339 WRITE_ENTER(&vnet_rw); 340 vnetp->nextp = vnet_headp; 341 vnet_headp = vnetp; 342 RW_EXIT(&vnet_rw); 343 344 DBG1(NULL, "instance(%d) exit\n", instance); 345 return (DDI_SUCCESS); 346 347 vnet_attach_fail: 348 if (attach_state & AST_fdbh_alloc) { 349 kmem_free(vnetp->fdbhp, 350 sizeof (fdb_fanout_t) * (vnetp->nfdb_hash + 1)); 351 } 352 if (attach_state & AST_vptl_alloc) { 353 WRITE_ENTER(&vnetp->trwlock); 354 vnet_del_vptl(vnetp, vp_tlp); 355 RW_EXIT(&vnetp->trwlock); 356 } 357 if (attach_state & AST_vgen_init) { 358 (void) vgen_uninit(vgenmacp->m_driver); 359 } 360 if (attach_state & AST_vnet_alloc) { 361 KMEM_FREE(vnetp); 362 } 363 return (DDI_FAILURE); 364 } 365 366 /* 367 * detach(9E): detach a device from the system. 368 */ 369 static int 370 vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) 371 { 372 vnet_t *vnetp; 373 vnet_t **vnetpp; 374 vp_tl_t *vp_tlp; 375 int instance; 376 int rv; 377 378 instance = ddi_get_instance(dip); 379 DBG1(NULL, "instance(%d) enter\n", instance); 380 381 vnetp = ddi_get_driver_private(dip); 382 if (vnetp == NULL) { 383 goto vnet_detach_fail; 384 } 385 386 switch (cmd) { 387 case DDI_DETACH: 388 break; 389 case DDI_SUSPEND: 390 case DDI_PM_SUSPEND: 391 default: 392 goto vnet_detach_fail; 393 } 394 395 /* uninit and free vnet proxy transports */ 396 WRITE_ENTER(&vnetp->trwlock); 397 while ((vp_tlp = vnetp->tlp) != NULL) { 398 if (strcmp(vnetp->vgen_name, vp_tlp->name) == 0) { 399 /* uninitialize generic transport */ 400 rv = vgen_uninit(vp_tlp->macp->m_driver); 401 if (rv != DDI_SUCCESS) { 402 RW_EXIT(&vnetp->trwlock); 403 goto vnet_detach_fail; 404 } 405 } 406 vnet_del_vptl(vnetp, vp_tlp); 407 } 408 RW_EXIT(&vnetp->trwlock); 409 410 /* 411 * Unregister from the MAC subsystem. This can fail, in 412 * particular if there are DLPI style-2 streams still open - 413 * in which case we just return failure. 414 */ 415 if (mac_unregister(vnetp->mh) != 0) 416 goto vnet_detach_fail; 417 418 /* unlink from instance(vnet_t) list */ 419 WRITE_ENTER(&vnet_rw); 420 for (vnetpp = &vnet_headp; *vnetpp; vnetpp = &(*vnetpp)->nextp) { 421 if (*vnetpp == vnetp) { 422 *vnetpp = vnetp->nextp; 423 break; 424 } 425 } 426 RW_EXIT(&vnet_rw); 427 428 kmem_free(vnetp->fdbhp, 429 sizeof (fdb_fanout_t) * (vnetp->nfdb_hash + 1)); 430 431 KMEM_FREE(vnetp); 432 433 return (DDI_SUCCESS); 434 435 vnet_detach_fail: 436 return (DDI_FAILURE); 437 } 438 439 /* enable the device for transmit/receive */ 440 static int 441 vnet_m_start(void *arg) 442 { 443 vnet_t *vnetp = arg; 444 vp_tl_t *vp_tlp; 445 mac_register_t *vp_macp; 446 mac_callbacks_t *cbp; 447 448 DBG1(vnetp, "enter\n"); 449 450 /* 451 * NOTE: 452 * Currently, we only have generic transport. m_start() invokes 453 * vgen_start() which enables ports/channels in vgen and 454 * initiates handshake with peer vnets and vsw. In the future when we 455 * have support for hardware specific transports, this information 456 * needs to be propagted back to vnet from vgen and we need to revisit 457 * this code (see comments in vnet_attach()). 458 * 459 */ 460 WRITE_ENTER(&vnetp->trwlock); 461 for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) { 462 vp_macp = vp_tlp->macp; 463 cbp = vp_macp->m_callbacks; 464 cbp->mc_start(vp_macp->m_driver); 465 } 466 RW_EXIT(&vnetp->trwlock); 467 468 DBG1(vnetp, "exit\n"); 469 return (VNET_SUCCESS); 470 471 } 472 473 /* stop transmit/receive for the device */ 474 static void 475 vnet_m_stop(void *arg) 476 { 477 vnet_t *vnetp = arg; 478 vp_tl_t *vp_tlp; 479 mac_register_t *vp_macp; 480 mac_callbacks_t *cbp; 481 482 DBG1(vnetp, "enter\n"); 483 484 WRITE_ENTER(&vnetp->trwlock); 485 for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) { 486 vp_macp = vp_tlp->macp; 487 cbp = vp_macp->m_callbacks; 488 cbp->mc_stop(vp_macp->m_driver); 489 } 490 RW_EXIT(&vnetp->trwlock); 491 492 DBG1(vnetp, "exit\n"); 493 } 494 495 /* set the unicast mac address of the device */ 496 static int 497 vnet_m_unicst(void *arg, const uint8_t *macaddr) 498 { 499 _NOTE(ARGUNUSED(macaddr)) 500 501 vnet_t *vnetp = arg; 502 503 DBG1(vnetp, "enter\n"); 504 /* 505 * NOTE: setting mac address dynamically is not supported. 506 */ 507 DBG1(vnetp, "exit\n"); 508 509 return (VNET_FAILURE); 510 } 511 512 /* enable/disable a multicast address */ 513 static int 514 vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 515 { 516 _NOTE(ARGUNUSED(add, mca)) 517 518 vnet_t *vnetp = arg; 519 vp_tl_t *vp_tlp; 520 mac_register_t *vp_macp; 521 mac_callbacks_t *cbp; 522 int rv = VNET_SUCCESS; 523 524 DBG1(vnetp, "enter\n"); 525 READ_ENTER(&vnetp->trwlock); 526 for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) { 527 if (strcmp(vnetp->vgen_name, vp_tlp->name) == 0) { 528 vp_macp = vp_tlp->macp; 529 cbp = vp_macp->m_callbacks; 530 rv = cbp->mc_multicst(vp_macp->m_driver, add, mca); 531 break; 532 } 533 } 534 RW_EXIT(&vnetp->trwlock); 535 DBG1(vnetp, "exit(%d)\n", rv); 536 return (rv); 537 } 538 539 /* set or clear promiscuous mode on the device */ 540 static int 541 vnet_m_promisc(void *arg, boolean_t on) 542 { 543 _NOTE(ARGUNUSED(on)) 544 545 vnet_t *vnetp = arg; 546 DBG1(vnetp, "enter\n"); 547 /* 548 * NOTE: setting promiscuous mode is not supported, just return success. 549 */ 550 DBG1(vnetp, "exit\n"); 551 return (VNET_SUCCESS); 552 } 553 554 /* 555 * Transmit a chain of packets. This function provides switching functionality 556 * based on the destination mac address to reach other guests (within ldoms) or 557 * external hosts. 558 */ 559 mblk_t * 560 vnet_m_tx(void *arg, mblk_t *mp) 561 { 562 vnet_t *vnetp; 563 mblk_t *next; 564 uint32_t fdbhash; 565 fdb_t *fdbp; 566 fdb_fanout_t *fdbhp; 567 struct ether_header *ehp; 568 uint8_t *macaddr; 569 mblk_t *resid_mp; 570 571 vnetp = (vnet_t *)arg; 572 DBG1(vnetp, "enter\n"); 573 ASSERT(mp != NULL); 574 575 while (mp != NULL) { 576 next = mp->b_next; 577 mp->b_next = NULL; 578 579 /* get the destination mac address in the eth header */ 580 ehp = (struct ether_header *)mp->b_rptr; 581 macaddr = (uint8_t *)&ehp->ether_dhost; 582 583 /* Calculate hash value and fdb fanout */ 584 fdbhash = MACHASH(macaddr, vnetp->nfdb_hash); 585 fdbhp = &(vnetp->fdbhp[fdbhash]); 586 587 READ_ENTER(&fdbhp->rwlock); 588 fdbp = vnet_lookup_fdb(fdbhp, macaddr); 589 if (fdbp) { 590 /* 591 * If the destination is in FDB, the destination is 592 * a vnet device within ldoms and directly reachable, 593 * invoke the tx function in the fdb entry. 594 */ 595 resid_mp = fdbp->m_tx(fdbp->txarg, mp); 596 if (resid_mp != NULL) { 597 /* m_tx failed */ 598 mp->b_next = next; 599 RW_EXIT(&fdbhp->rwlock); 600 break; 601 } 602 RW_EXIT(&fdbhp->rwlock); 603 } else { 604 /* destination is not in FDB */ 605 RW_EXIT(&fdbhp->rwlock); 606 /* 607 * If the destination is broadcast/multicast 608 * or an unknown unicast address, forward the 609 * packet to vsw, using the last slot in fdb which is 610 * reserved for default route. 611 */ 612 fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]); 613 READ_ENTER(&fdbhp->rwlock); 614 fdbp = fdbhp->headp; 615 if (fdbp) { 616 resid_mp = fdbp->m_tx(fdbp->txarg, mp); 617 if (resid_mp != NULL) { 618 /* m_tx failed */ 619 mp->b_next = next; 620 RW_EXIT(&fdbhp->rwlock); 621 break; 622 } 623 } else { 624 /* drop the packet */ 625 freemsg(mp); 626 } 627 RW_EXIT(&fdbhp->rwlock); 628 } 629 630 mp = next; 631 } 632 633 DBG1(vnetp, "exit\n"); 634 return (mp); 635 } 636 637 /* get statistics from the device */ 638 int 639 vnet_m_stat(void *arg, uint_t stat, uint64_t *val) 640 { 641 vnet_t *vnetp = arg; 642 vp_tl_t *vp_tlp; 643 mac_register_t *vp_macp; 644 mac_callbacks_t *cbp; 645 uint64_t val_total = 0; 646 647 DBG1(vnetp, "enter\n"); 648 649 /* 650 * get the specified statistic from each transport and return the 651 * aggregate val. This obviously only works for counters. 652 */ 653 if ((IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat)) || 654 (IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat))) { 655 return (ENOTSUP); 656 } 657 READ_ENTER(&vnetp->trwlock); 658 for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) { 659 vp_macp = vp_tlp->macp; 660 cbp = vp_macp->m_callbacks; 661 if (cbp->mc_getstat(vp_macp->m_driver, stat, val) == 0) 662 val_total += *val; 663 } 664 RW_EXIT(&vnetp->trwlock); 665 666 *val = val_total; 667 668 DBG1(vnetp, "exit\n"); 669 return (0); 670 } 671 672 /* wrapper function for mac_register() */ 673 static int 674 vnet_mac_register(vnet_t *vnetp) 675 { 676 mac_register_t *macp; 677 int err; 678 679 if ((macp = mac_alloc(MAC_VERSION)) == NULL) 680 return (DDI_FAILURE); 681 macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 682 macp->m_driver = vnetp; 683 macp->m_dip = vnetp->dip; 684 macp->m_src_addr = vnetp->curr_macaddr; 685 macp->m_callbacks = &vnet_m_callbacks; 686 macp->m_min_sdu = 0; 687 macp->m_max_sdu = ETHERMTU; 688 689 /* 690 * Finally, we're ready to register ourselves with the MAC layer 691 * interface; if this succeeds, we're all ready to start() 692 */ 693 err = mac_register(macp, &vnetp->mh); 694 mac_free(macp); 695 return (err == 0 ? DDI_SUCCESS : DDI_FAILURE); 696 } 697 698 /* add vp_tl to the list */ 699 static void 700 vnet_add_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp) 701 { 702 vp_tl_t *ttlp; 703 704 WRITE_ENTER(&vnetp->trwlock); 705 if (vnetp->tlp == NULL) { 706 vnetp->tlp = vp_tlp; 707 } else { 708 ttlp = vnetp->tlp; 709 while (ttlp->nextp) 710 ttlp = ttlp->nextp; 711 ttlp->nextp = vp_tlp; 712 } 713 RW_EXIT(&vnetp->trwlock); 714 } 715 716 /* remove vp_tl from the list */ 717 static void 718 vnet_del_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp) 719 { 720 vp_tl_t *ttlp, **pretlp; 721 boolean_t found = B_FALSE; 722 723 pretlp = &vnetp->tlp; 724 ttlp = *pretlp; 725 while (ttlp) { 726 if (ttlp == vp_tlp) { 727 found = B_TRUE; 728 (*pretlp) = ttlp->nextp; 729 ttlp->nextp = NULL; 730 break; 731 } 732 pretlp = &(ttlp->nextp); 733 ttlp = *pretlp; 734 } 735 736 if (found) { 737 KMEM_FREE(vp_tlp); 738 } 739 } 740 741 /* get vp_tl corresponding to the given name */ 742 static vp_tl_t * 743 vnet_get_vptl(vnet_t *vnetp, const char *name) 744 { 745 vp_tl_t *tlp; 746 747 tlp = vnetp->tlp; 748 while (tlp) { 749 if (strcmp(tlp->name, name) == 0) { 750 return (tlp); 751 } 752 tlp = tlp->nextp; 753 } 754 DWARN(vnetp, "can't find vp_tl with name (%s)\n", name); 755 return (NULL); 756 } 757 758 /* read the mac address of the device */ 759 static int 760 vnet_read_mac_address(vnet_t *vnetp) 761 { 762 uchar_t *macaddr; 763 uint32_t size; 764 int rv; 765 766 rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip, 767 DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size); 768 if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) { 769 DWARN(vnetp, "prop_lookup failed(%s) err(%d)\n", 770 macaddr_propname, rv); 771 return (DDI_FAILURE); 772 } 773 bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL); 774 bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL); 775 ddi_prop_free(macaddr); 776 777 return (DDI_SUCCESS); 778 } 779 780 781 /* 782 * Functions below are called only by generic transport to add/remove/modify 783 * entries in forwarding database. See comments in vgen_port_init(vnet_gen.c). 784 */ 785 786 /* add an entry into the forwarding database */ 787 void 788 vnet_add_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg) 789 { 790 vnet_t *vnetp = (vnet_t *)arg; 791 uint32_t fdbhash; 792 fdb_t *fdbp; 793 fdb_fanout_t *fdbhp; 794 795 /* Calculate hash value and fdb fanout */ 796 fdbhash = MACHASH(macaddr, vnetp->nfdb_hash); 797 fdbhp = &(vnetp->fdbhp[fdbhash]); 798 799 WRITE_ENTER(&fdbhp->rwlock); 800 801 fdbp = kmem_zalloc(sizeof (fdb_t), KM_NOSLEEP); 802 if (fdbp == NULL) { 803 RW_EXIT(&fdbhp->rwlock); 804 return; 805 } 806 bcopy(macaddr, (caddr_t)fdbp->macaddr, ETHERADDRL); 807 fdbp->m_tx = m_tx; 808 fdbp->txarg = txarg; 809 fdbp->nextp = fdbhp->headp; 810 fdbhp->headp = fdbp; 811 812 RW_EXIT(&fdbhp->rwlock); 813 } 814 815 /* delete an entry from the forwarding database */ 816 void 817 vnet_del_fdb(void *arg, uint8_t *macaddr) 818 { 819 vnet_t *vnetp = (vnet_t *)arg; 820 uint32_t fdbhash; 821 fdb_t *fdbp; 822 fdb_t **pfdbp; 823 fdb_fanout_t *fdbhp; 824 825 /* Calculate hash value and fdb fanout */ 826 fdbhash = MACHASH(macaddr, vnetp->nfdb_hash); 827 fdbhp = &(vnetp->fdbhp[fdbhash]); 828 829 WRITE_ENTER(&fdbhp->rwlock); 830 831 for (pfdbp = &fdbhp->headp; (fdbp = *pfdbp) != NULL; 832 pfdbp = &fdbp->nextp) { 833 if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) { 834 /* Unlink it from the list */ 835 *pfdbp = fdbp->nextp; 836 KMEM_FREE(fdbp); 837 break; 838 } 839 } 840 841 RW_EXIT(&fdbhp->rwlock); 842 } 843 844 /* modify an existing entry in the forwarding database */ 845 void 846 vnet_modify_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg, 847 boolean_t upgrade) 848 { 849 vnet_t *vnetp = (vnet_t *)arg; 850 uint32_t fdbhash; 851 fdb_t *fdbp; 852 fdb_fanout_t *fdbhp; 853 854 /* Calculate hash value and fdb fanout */ 855 fdbhash = MACHASH(macaddr, vnetp->nfdb_hash); 856 fdbhp = &(vnetp->fdbhp[fdbhash]); 857 858 if (upgrade == B_TRUE) { 859 /* 860 * Caller already holds the lock as a reader. This can 861 * occur if this function is invoked in the context 862 * of transmit routine - vnet_m_tx(), where the lock 863 * is held as a reader before calling the transmit 864 * function of an fdb entry (fdbp->m_tx). 865 * See comments in vgen_ldcsend() in vnet_gen.c 866 */ 867 if (!rw_tryupgrade(&fdbhp->rwlock)) { 868 RW_EXIT(&fdbhp->rwlock); 869 WRITE_ENTER(&fdbhp->rwlock); 870 } 871 } else { 872 /* Caller does not hold the lock */ 873 WRITE_ENTER(&fdbhp->rwlock); 874 } 875 876 for (fdbp = fdbhp->headp; fdbp != NULL; fdbp = fdbp->nextp) { 877 if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) { 878 /* change the entry to have new tx params */ 879 fdbp->m_tx = m_tx; 880 fdbp->txarg = txarg; 881 break; 882 } 883 } 884 885 if (upgrade == B_TRUE) { 886 /* restore the caller as a reader */ 887 rw_downgrade(&fdbhp->rwlock); 888 } else { 889 RW_EXIT(&fdbhp->rwlock); 890 } 891 } 892 893 /* look up an fdb entry based on the mac address, caller holds lock */ 894 static fdb_t * 895 vnet_lookup_fdb(fdb_fanout_t *fdbhp, uint8_t *macaddr) 896 { 897 fdb_t *fdbp = NULL; 898 899 for (fdbp = fdbhp->headp; fdbp != NULL; fdbp = fdbp->nextp) { 900 if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) { 901 break; 902 } 903 } 904 905 return (fdbp); 906 } 907 908 /* add default route entry into the forwarding database */ 909 void 910 vnet_add_def_rte(void *arg, mac_tx_t m_tx, void *txarg) 911 { 912 vnet_t *vnetp = (vnet_t *)arg; 913 fdb_t *fdbp; 914 fdb_fanout_t *fdbhp; 915 916 /* 917 * The last hash list is reserved for default route entry, 918 * and for now, we have only one entry in this list. 919 */ 920 fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]); 921 922 WRITE_ENTER(&fdbhp->rwlock); 923 924 if (fdbhp->headp) { 925 DWARN(vnetp, "default rte already exists\n"); 926 RW_EXIT(&fdbhp->rwlock); 927 return; 928 } 929 fdbp = kmem_zalloc(sizeof (fdb_t), KM_NOSLEEP); 930 if (fdbp == NULL) { 931 RW_EXIT(&fdbhp->rwlock); 932 return; 933 } 934 bzero(fdbp->macaddr, ETHERADDRL); 935 fdbp->m_tx = m_tx; 936 fdbp->txarg = txarg; 937 fdbp->nextp = NULL; 938 fdbhp->headp = fdbp; 939 940 RW_EXIT(&fdbhp->rwlock); 941 } 942 943 /* delete default route entry from the forwarding database */ 944 void 945 vnet_del_def_rte(void *arg) 946 { 947 vnet_t *vnetp = (vnet_t *)arg; 948 fdb_t *fdbp; 949 fdb_fanout_t *fdbhp; 950 951 /* 952 * The last hash list is reserved for default route entry, 953 * and for now, we have only one entry in this list. 954 */ 955 fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]); 956 957 WRITE_ENTER(&fdbhp->rwlock); 958 959 if (fdbhp->headp == NULL) { 960 RW_EXIT(&fdbhp->rwlock); 961 return; 962 } 963 fdbp = fdbhp->headp; 964 KMEM_FREE(fdbp); 965 fdbhp->headp = NULL; 966 967 RW_EXIT(&fdbhp->rwlock); 968 } 969 970 void 971 vnet_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp) 972 { 973 vnet_t *vnetp = arg; 974 mac_rx(vnetp->mh, mrh, mp); 975 } 976 977 void 978 vnet_tx_update(void *arg) 979 { 980 vnet_t *vnetp = arg; 981 mac_tx_update(vnetp->mh); 982 } 983