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