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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * The following notice accompanied the original version of this file: 28 * 29 * BSD LICENSE 30 * 31 * Copyright(c) 2007 Intel Corporation. All rights reserved. 32 * All rights reserved. 33 * 34 * Redistribution and use in source and binary forms, with or without 35 * modification, are permitted provided that the following conditions 36 * are met: 37 * 38 * * Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * * Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in 42 * the documentation and/or other materials provided with the 43 * distribution. 44 * * Neither the name of Intel Corporation nor the names of its 45 * contributors may be used to endorse or promote products derived 46 * from this software without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 51 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 52 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 53 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 54 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 55 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 56 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 57 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 58 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 */ 60 61 /* 62 * Common FCoE interface interacts with MAC and FCoE clients, managing 63 * FCoE ports, doing MAC address discovery/managment, and FC frame 64 * encapsulation/decapsulation 65 */ 66 67 #include <sys/stat.h> 68 #include <sys/conf.h> 69 #include <sys/file.h> 70 #include <sys/cred.h> 71 72 #include <sys/ddi.h> 73 #include <sys/sunddi.h> 74 #include <sys/sunndi.h> 75 #include <sys/byteorder.h> 76 #include <sys/atomic.h> 77 #include <sys/sysmacros.h> 78 #include <sys/cmn_err.h> 79 #include <sys/crc32.h> 80 #include <sys/strsubr.h> 81 82 #include <sys/mac_client.h> 83 84 /* 85 * FCoE header files 86 */ 87 #include <sys/fcoe/fcoeio.h> 88 #include <sys/fcoe/fcoe_common.h> 89 90 /* 91 * Driver's own header files 92 */ 93 #include <fcoe.h> 94 #include <fcoe_fc.h> 95 #include <fcoe_eth.h> 96 97 /* 98 * Function forward declaration 99 */ 100 static int fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 101 static int fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 102 static int fcoe_bus_ctl(dev_info_t *fca_dip, dev_info_t *rip, 103 ddi_ctl_enum_t op, void *arg, void *result); 104 static int fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp); 105 static int fcoe_close(dev_t dev, int flag, int otype, cred_t *credp); 106 static int fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 107 cred_t *credp, int *rval); 108 static int fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio, 109 void **ibuf, void **abuf, void **obuf); 110 static int fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, 111 void *obuf); 112 static int fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode); 113 static int fcoe_attach_init(fcoe_soft_state_t *this_ss); 114 static int fcoe_detach_uninit(fcoe_soft_state_t *this_ss); 115 static int fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip); 116 static int fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip); 117 static void fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, 118 int is_pwwn, uint8_t idx); 119 static fcoe_mac_t *fcoe_create_mac_by_id(datalink_id_t linkid); 120 static int fcoe_cmp_wwn(fcoe_mac_t *checkedmac); 121 static void fcoe_watchdog(void *arg); 122 static void fcoe_worker_init(); 123 static int fcoe_worker_fini(); 124 static void fcoe_worker_frame(); 125 static int fcoe_get_port_list(fcoe_port_instance_t *ports, int count); 126 127 /* 128 * Driver identificaton stuff 129 */ 130 static struct cb_ops fcoe_cb_ops = { 131 fcoe_open, 132 fcoe_close, 133 nodev, 134 nodev, 135 nodev, 136 nodev, 137 nodev, 138 fcoe_ioctl, 139 nodev, 140 nodev, 141 nodev, 142 nochpoll, 143 ddi_prop_op, 144 0, 145 D_MP | D_NEW | D_HOTPLUG, 146 CB_REV, 147 nodev, 148 nodev 149 }; 150 151 static struct bus_ops fcoe_busops = { 152 BUSO_REV, 153 nullbusmap, /* bus_map */ 154 NULL, /* bus_get_intrspec */ 155 NULL, /* bus_add_intrspec */ 156 NULL, /* bus_remove_intrspec */ 157 i_ddi_map_fault, /* bus_map_fault */ 158 ddi_dma_map, /* bus_dma_map */ 159 ddi_dma_allochdl, /* bus_dma_allochdl */ 160 ddi_dma_freehdl, /* bus_dma_freehdl */ 161 ddi_dma_bindhdl, /* bus_dma_bindhdl */ 162 ddi_dma_unbindhdl, /* bus_unbindhdl */ 163 ddi_dma_flush, /* bus_dma_flush */ 164 ddi_dma_win, /* bus_dma_win */ 165 ddi_dma_mctl, /* bus_dma_ctl */ 166 fcoe_bus_ctl, /* bus_ctl */ 167 ddi_bus_prop_op, /* bus_prop_op */ 168 NULL, /* bus_get_eventcookie */ 169 NULL, /* bus_add_eventcall */ 170 NULL, /* bus_remove_event */ 171 NULL, /* bus_post_event */ 172 NULL, /* bus_intr_ctl */ 173 NULL, /* bus_config */ 174 NULL, /* bus_unconfig */ 175 NULL, /* bus_fm_init */ 176 NULL, /* bus_fm_fini */ 177 NULL, /* bus_fm_access_enter */ 178 NULL, /* bus_fm_access_exit */ 179 NULL, /* bus_power */ 180 NULL 181 }; 182 183 static struct dev_ops fcoe_ops = { 184 DEVO_REV, 185 0, 186 nodev, 187 nulldev, 188 nulldev, 189 fcoe_attach, 190 fcoe_detach, 191 nodev, 192 &fcoe_cb_ops, 193 &fcoe_busops, 194 ddi_power, 195 ddi_quiesce_not_needed 196 }; 197 198 #define FCOE_VERSION "20090729-1.01" 199 #define FCOE_NAME "FCoE Transport v" FCOE_VERSION 200 #define TASKQ_NAME_LEN 32 201 202 static struct modldrv modldrv = { 203 &mod_driverops, 204 FCOE_NAME, 205 &fcoe_ops, 206 }; 207 208 static struct modlinkage modlinkage = { 209 MODREV_1, &modldrv, NULL 210 }; 211 212 /* 213 * TRACE for all FCoE related modules 214 */ 215 static kmutex_t fcoe_trace_buf_lock; 216 static int fcoe_trace_buf_curndx = 0; 217 static int fcoe_trace_on = 1; 218 static caddr_t fcoe_trace_buf = NULL; 219 static clock_t fcoe_trace_start = 0; 220 static caddr_t ftb = NULL; 221 static int fcoe_trace_buf_size = (1 * 1024 * 1024); 222 223 /* 224 * Driver's global variables 225 */ 226 const fcoe_ver_e fcoe_ver_now = FCOE_VER_NOW; 227 static void *fcoe_state = NULL; 228 fcoe_soft_state_t *fcoe_global_ss = NULL; 229 int fcoe_use_ext_log = 1; 230 231 static ddi_taskq_t *fcoe_worker_taskq; 232 static fcoe_worker_t *fcoe_workers; 233 static uint32_t fcoe_nworkers_running; 234 235 const char *fcoe_workers_num = "workers-number"; 236 volatile int fcoe_nworkers; 237 238 /* 239 * Common loadable module entry points _init, _fini, _info 240 */ 241 242 int 243 _init(void) 244 { 245 int ret; 246 247 ret = ddi_soft_state_init(&fcoe_state, sizeof (fcoe_soft_state_t), 0); 248 if (ret == 0) { 249 ret = mod_install(&modlinkage); 250 if (ret != 0) { 251 ddi_soft_state_fini(&fcoe_state); 252 } else { 253 fcoe_trace_start = ddi_get_lbolt(); 254 ftb = kmem_zalloc(fcoe_trace_buf_size, 255 KM_SLEEP); 256 fcoe_trace_buf = ftb; 257 mutex_init(&fcoe_trace_buf_lock, NULL, MUTEX_DRIVER, 0); 258 } 259 } 260 261 FCOE_LOG("fcoe", "exit _init with %x", ret); 262 263 return (ret); 264 } 265 266 int 267 _fini(void) 268 { 269 int ret; 270 271 ret = mod_remove(&modlinkage); 272 if (ret == 0) { 273 ddi_soft_state_fini(&fcoe_state); 274 } 275 276 FCOE_LOG("fcoe", "exit _fini with %x", ret); 277 if (ret == 0) { 278 kmem_free(fcoe_trace_buf, fcoe_trace_buf_size); 279 mutex_destroy(&fcoe_trace_buf_lock); 280 } 281 282 return (ret); 283 } 284 285 int 286 _info(struct modinfo *modinfop) 287 { 288 return (mod_info(&modlinkage, modinfop)); 289 } 290 291 /* 292 * Autoconfiguration entry points: attach, detach, getinfo 293 */ 294 295 static int 296 fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 297 { 298 int ret = DDI_FAILURE; 299 int fcoe_ret; 300 int instance; 301 fcoe_soft_state_t *ss; 302 303 instance = ddi_get_instance(dip); 304 switch (cmd) { 305 case DDI_ATTACH: 306 ret = ddi_soft_state_zalloc(fcoe_state, instance); 307 if (ret == DDI_FAILURE) { 308 FCOE_LOG(0, "soft_state_zalloc-%x/%x", ret, instance); 309 return (ret); 310 } 311 312 ss = ddi_get_soft_state(fcoe_state, instance); 313 ss->ss_dip = dip; 314 315 ASSERT(fcoe_global_ss == NULL); 316 fcoe_global_ss = ss; 317 fcoe_ret = fcoe_attach_init(ss); 318 if (fcoe_ret == FCOE_SUCCESS) { 319 ret = DDI_SUCCESS; 320 } 321 322 FCOE_LOG("fcoe", "fcoe_attach_init end with-%x", fcoe_ret); 323 break; 324 325 case DDI_RESUME: 326 ret = DDI_SUCCESS; 327 break; 328 329 default: 330 FCOE_LOG("fcoe", "unsupported attach cmd-%x", cmd); 331 break; 332 } 333 334 return (ret); 335 } 336 337 static int 338 fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 339 { 340 int ret = DDI_FAILURE; 341 int fcoe_ret; 342 int instance; 343 fcoe_soft_state_t *ss; 344 345 instance = ddi_get_instance(dip); 346 ss = ddi_get_soft_state(fcoe_state, instance); 347 if (ss == NULL) { 348 return (ret); 349 } 350 351 ASSERT(fcoe_global_ss != NULL); 352 ASSERT(dip == fcoe_global_ss->ss_dip); 353 switch (cmd) { 354 case DDI_DETACH: 355 fcoe_ret = fcoe_detach_uninit(ss); 356 if (fcoe_ret == FCOE_SUCCESS) { 357 ret = DDI_SUCCESS; 358 fcoe_global_ss = NULL; 359 } 360 361 break; 362 363 case DDI_SUSPEND: 364 ret = DDI_SUCCESS; 365 break; 366 367 default: 368 FCOE_LOG(0, "unsupported detach cmd-%x", cmd); 369 break; 370 } 371 372 return (ret); 373 } 374 375 /* 376 * FCA driver's intercepted bus control operations. 377 */ 378 static int 379 fcoe_bus_ctl(dev_info_t *fcoe_dip, dev_info_t *rip, 380 ddi_ctl_enum_t op, void *clientarg, void *result) 381 { 382 int ret; 383 switch (op) { 384 case DDI_CTLOPS_REPORTDEV: 385 case DDI_CTLOPS_IOMIN: 386 ret = DDI_SUCCESS; 387 break; 388 389 case DDI_CTLOPS_INITCHILD: 390 ret = fcoe_initchild(fcoe_dip, (dev_info_t *)clientarg); 391 break; 392 393 case DDI_CTLOPS_UNINITCHILD: 394 ret = fcoe_uninitchild(fcoe_dip, (dev_info_t *)clientarg); 395 break; 396 397 default: 398 ret = ddi_ctlops(fcoe_dip, rip, op, clientarg, result); 399 break; 400 } 401 402 return (ret); 403 } 404 405 /* 406 * We need specify the dev address for client driver's instance, or we 407 * can't online client driver's instance. 408 */ 409 /* ARGSUSED */ 410 static int 411 fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip) 412 { 413 char client_addr[FCOE_STR_LEN]; 414 int rval; 415 416 rval = ddi_prop_get_int(DDI_DEV_T_ANY, client_dip, 417 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "mac_id", -1); 418 if (rval == -1) { 419 FCOE_LOG(__FUNCTION__, "no mac_id property: %p", client_dip); 420 return (DDI_FAILURE); 421 } 422 423 bzero(client_addr, FCOE_STR_LEN); 424 (void) sprintf((char *)client_addr, "%x,0", rval); 425 ddi_set_name_addr(client_dip, client_addr); 426 return (DDI_SUCCESS); 427 } 428 429 /* ARGSUSED */ 430 static int 431 fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip) 432 { 433 ddi_set_name_addr(client_dip, NULL); 434 return (DDI_SUCCESS); 435 } 436 437 /* 438 * Device access entry points 439 */ 440 static int 441 fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp) 442 { 443 int instance; 444 fcoe_soft_state_t *ss; 445 446 if (otype != OTYP_CHR) { 447 return (EINVAL); 448 } 449 450 /* 451 * Since this is for debugging only, only allow root to issue ioctl now 452 */ 453 if (drv_priv(credp) != 0) { 454 return (EPERM); 455 } 456 457 instance = (int)getminor(*devp); 458 ss = ddi_get_soft_state(fcoe_state, instance); 459 if (ss == NULL) { 460 return (ENXIO); 461 } 462 463 mutex_enter(&ss->ss_ioctl_mutex); 464 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) { 465 /* 466 * It is already open for exclusive access. 467 * So shut the door on this caller. 468 */ 469 mutex_exit(&ss->ss_ioctl_mutex); 470 return (EBUSY); 471 } 472 473 if (flag & FEXCL) { 474 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) { 475 /* 476 * Exclusive operation not possible 477 * as it is already opened 478 */ 479 mutex_exit(&ss->ss_ioctl_mutex); 480 return (EBUSY); 481 } 482 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL; 483 } 484 485 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_OPEN; 486 mutex_exit(&ss->ss_ioctl_mutex); 487 488 return (0); 489 } 490 491 /* ARGSUSED */ 492 static int 493 fcoe_close(dev_t dev, int flag, int otype, cred_t *credp) 494 { 495 int instance; 496 fcoe_soft_state_t *ss; 497 498 if (otype != OTYP_CHR) { 499 return (EINVAL); 500 } 501 502 instance = (int)getminor(dev); 503 ss = ddi_get_soft_state(fcoe_state, instance); 504 if (ss == NULL) { 505 return (ENXIO); 506 } 507 508 mutex_enter(&ss->ss_ioctl_mutex); 509 if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) { 510 mutex_exit(&ss->ss_ioctl_mutex); 511 return (ENODEV); 512 } 513 514 ss->ss_ioctl_flags &= ~FCOE_IOCTL_FLAG_MASK; 515 mutex_exit(&ss->ss_ioctl_mutex); 516 517 return (0); 518 } 519 520 /* ARGSUSED */ 521 static int 522 fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 523 cred_t *credp, int *rval) 524 { 525 fcoe_soft_state_t *ss; 526 int ret = 0; 527 528 if (drv_priv(credp) != 0) { 529 return (EPERM); 530 } 531 532 ss = ddi_get_soft_state(fcoe_state, (int32_t)getminor(dev)); 533 if (ss == NULL) { 534 return (ENXIO); 535 } 536 537 mutex_enter(&ss->ss_ioctl_mutex); 538 if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) { 539 mutex_exit(&ss->ss_ioctl_mutex); 540 return (ENXIO); 541 } 542 mutex_exit(&ss->ss_ioctl_mutex); 543 544 switch (cmd) { 545 case FCOEIO_CMD: 546 ret = fcoe_iocmd(ss, data, mode); 547 break; 548 default: 549 FCOE_LOG(0, "fcoe_ioctl: ioctl-0x%02X", cmd); 550 ret = ENOTTY; 551 break; 552 } 553 554 return (ret); 555 } 556 557 static int 558 fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio, 559 void **ibuf, void **abuf, void **obuf) 560 { 561 int ret = 0; 562 563 *ibuf = NULL; 564 *abuf = NULL; 565 *obuf = NULL; 566 *fcoeio = kmem_zalloc(sizeof (fcoeio_t), KM_SLEEP); 567 if (ddi_copyin((void *)data, *fcoeio, sizeof (fcoeio_t), mode) != 0) { 568 ret = EFAULT; 569 goto copyin_iocdata_fail; 570 } 571 572 if ((*fcoeio)->fcoeio_ilen > FCOEIO_MAX_BUF_LEN || 573 (*fcoeio)->fcoeio_alen > FCOEIO_MAX_BUF_LEN || 574 (*fcoeio)->fcoeio_olen > FCOEIO_MAX_BUF_LEN) { 575 ret = EFAULT; 576 goto copyin_iocdata_fail; 577 } 578 579 if ((*fcoeio)->fcoeio_ilen) { 580 *ibuf = kmem_zalloc((*fcoeio)->fcoeio_ilen, KM_SLEEP); 581 if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_ibuf, 582 *ibuf, (*fcoeio)->fcoeio_ilen, mode) != 0) { 583 ret = EFAULT; 584 goto copyin_iocdata_fail; 585 } 586 } 587 588 if ((*fcoeio)->fcoeio_alen) { 589 *abuf = kmem_zalloc((*fcoeio)->fcoeio_alen, KM_SLEEP); 590 if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_abuf, 591 *abuf, (*fcoeio)->fcoeio_alen, mode) != 0) { 592 ret = EFAULT; 593 goto copyin_iocdata_fail; 594 } 595 } 596 597 if ((*fcoeio)->fcoeio_olen) { 598 *obuf = kmem_zalloc((*fcoeio)->fcoeio_olen, KM_SLEEP); 599 } 600 return (ret); 601 602 copyin_iocdata_fail: 603 if (*abuf) { 604 kmem_free(*abuf, (*fcoeio)->fcoeio_alen); 605 *abuf = NULL; 606 } 607 608 if (*ibuf) { 609 kmem_free(*ibuf, (*fcoeio)->fcoeio_ilen); 610 *ibuf = NULL; 611 } 612 613 kmem_free(*fcoeio, sizeof (fcoeio_t)); 614 return (ret); 615 } 616 617 static int 618 fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, void *obuf) 619 { 620 if (fcoeio->fcoeio_olen) { 621 if (ddi_copyout(obuf, 622 (void *)(unsigned long)fcoeio->fcoeio_obuf, 623 fcoeio->fcoeio_olen, mode) != 0) { 624 return (EFAULT); 625 } 626 } 627 628 if (ddi_copyout(fcoeio, (void *)data, sizeof (fcoeio_t), mode) != 0) { 629 return (EFAULT); 630 } 631 return (0); 632 } 633 634 static int 635 fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode) 636 { 637 int ret; 638 fcoe_mac_t *fcoe_mac; 639 void *ibuf = NULL; 640 void *obuf = NULL; 641 void *abuf = NULL; 642 fcoeio_t *fcoeio; 643 644 ret = fcoe_copyin_iocdata(data, mode, &fcoeio, &ibuf, &abuf, &obuf); 645 if (ret != 0) { 646 goto fcoeiocmd_release_buf; 647 } 648 649 /* 650 * If an exclusive open was demanded during open, ensure that 651 * only one thread can execute an ioctl at a time 652 */ 653 mutex_enter(&ss->ss_ioctl_mutex); 654 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) { 655 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL_BUSY) { 656 mutex_exit(&ss->ss_ioctl_mutex); 657 fcoeio->fcoeio_status = FCOEIOE_BUSY; 658 ret = EBUSY; 659 goto fcoeiocmd_release_buf; 660 } 661 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL_BUSY; 662 } 663 mutex_exit(&ss->ss_ioctl_mutex); 664 665 fcoeio->fcoeio_status = 0; 666 667 switch (fcoeio->fcoeio_cmd) { 668 case FCOEIO_CREATE_FCOE_PORT: { 669 fcoeio_create_port_param_t *param = 670 (fcoeio_create_port_param_t *)ibuf; 671 int cmpwwn = 0; 672 fcoe_port_t *eport; 673 674 if (fcoeio->fcoeio_ilen != 675 sizeof (fcoeio_create_port_param_t) || 676 fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) { 677 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 678 ret = EINVAL; 679 break; 680 } 681 682 mutex_enter(&ss->ss_ioctl_mutex); 683 fcoe_mac = fcoe_create_mac_by_id(param->fcp_mac_linkid); 684 if (fcoe_mac == NULL) { 685 mutex_exit(&ss->ss_ioctl_mutex); 686 fcoeio->fcoeio_status = FCOEIOE_CREATE_MAC; 687 ret = EIO; 688 break; 689 } 690 691 if (fcoe_mac->fm_flags & FCOE_MAC_FLAG_ENABLED) { 692 mutex_exit(&ss->ss_ioctl_mutex); 693 fcoeio->fcoeio_status = FCOEIOE_ALREADY; 694 ret = EALREADY; 695 break; 696 } else { 697 ret = fcoe_open_mac(fcoe_mac, param->fcp_force_promisc, 698 &fcoeio->fcoeio_status); 699 if (ret != 0) { 700 fcoe_destroy_mac(fcoe_mac); 701 mutex_exit(&ss->ss_ioctl_mutex); 702 if (fcoeio->fcoeio_status == 0) { 703 fcoeio->fcoeio_status = 704 FCOEIOE_OPEN_MAC; 705 } 706 ret = EIO; 707 break; 708 } else { 709 fcoe_mac->fm_flags |= FCOE_MAC_FLAG_ENABLED; 710 } 711 } 712 713 /* 714 * Provide PWWN and NWWN based on mac address 715 */ 716 eport = &fcoe_mac->fm_eport; 717 if (!param->fcp_pwwn_provided) { 718 fcoe_init_wwn_from_mac(eport->eport_portwwn, 719 fcoe_mac->fm_current_addr, 1, 0); 720 } else { 721 (void) memcpy(eport->eport_portwwn, param->fcp_pwwn, 8); 722 } 723 724 if (!param->fcp_nwwn_provided) { 725 fcoe_init_wwn_from_mac(eport->eport_nodewwn, 726 fcoe_mac->fm_current_addr, 0, 0); 727 } else { 728 (void) memcpy(eport->eport_nodewwn, param->fcp_nwwn, 8); 729 } 730 731 cmpwwn = fcoe_cmp_wwn(fcoe_mac); 732 733 if (cmpwwn != 0) { 734 if (cmpwwn == 1) { 735 fcoeio->fcoeio_status = FCOEIOE_PWWN_CONFLICTED; 736 } else if (cmpwwn == -1) { 737 fcoeio->fcoeio_status = FCOEIOE_NWWN_CONFLICTED; 738 } 739 (void) fcoe_close_mac(fcoe_mac); 740 fcoe_destroy_mac(fcoe_mac); 741 mutex_exit(&ss->ss_ioctl_mutex); 742 ret = ENOTUNIQ; 743 break; 744 } 745 746 if (ret == 0) { 747 ret = fcoe_create_port(ss->ss_dip, 748 fcoe_mac, 749 (param->fcp_port_type == FCOE_CLIENT_TARGET)); 750 if (ret != 0) { 751 (void) fcoe_close_mac(fcoe_mac); 752 fcoe_destroy_mac(fcoe_mac); 753 fcoeio->fcoeio_status = FCOEIOE_CREATE_PORT; 754 ret = EIO; 755 } 756 } 757 mutex_exit(&ss->ss_ioctl_mutex); 758 759 break; 760 } 761 762 case FCOEIO_DELETE_FCOE_PORT: { 763 fcoeio_delete_port_param_t *del_port_param = 764 (fcoeio_delete_port_param_t *)ibuf; 765 uint64_t *is_target = (uint64_t *)obuf; 766 767 if (fcoeio->fcoeio_ilen < sizeof (fcoeio_delete_port_param_t) || 768 fcoeio->fcoeio_olen != sizeof (uint64_t) || 769 fcoeio->fcoeio_xfer != FCOEIO_XFER_RW) { 770 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 771 ret = EINVAL; 772 break; 773 } 774 775 mutex_enter(&ss->ss_ioctl_mutex); 776 ret = fcoe_delete_port(ss->ss_dip, fcoeio, 777 del_port_param->fdp_mac_linkid, is_target); 778 mutex_exit(&ss->ss_ioctl_mutex); 779 FCOE_LOG("fcoe", "fcoe_delete_port %x return: %d", 780 del_port_param->fdp_mac_linkid, ret); 781 break; 782 } 783 784 case FCOEIO_GET_FCOE_PORT_LIST: { 785 fcoe_port_list_t *list = (fcoe_port_list_t *)obuf; 786 int count; 787 788 if (fcoeio->fcoeio_xfer != FCOEIO_XFER_READ || 789 fcoeio->fcoeio_olen < sizeof (fcoe_port_list_t)) { 790 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 791 ret = EINVAL; 792 break; 793 } 794 mutex_enter(&ss->ss_ioctl_mutex); 795 796 list->numPorts = 1 + (fcoeio->fcoeio_olen - 797 sizeof (fcoe_port_list_t))/sizeof (fcoe_port_instance_t); 798 799 count = fcoe_get_port_list(list->ports, list->numPorts); 800 801 if (count > list->numPorts) { 802 fcoeio->fcoeio_status = FCOEIOE_MORE_DATA; 803 ret = ENOSPC; 804 } 805 list->numPorts = count; 806 mutex_exit(&ss->ss_ioctl_mutex); 807 808 break; 809 810 } 811 812 default: 813 return (ENOTTY); 814 } 815 816 FCOE_LOG("fcoe", "fcoe_ioctl %x returned %d, fcoeio_status = %d", 817 fcoeio->fcoeio_cmd, ret, fcoeio->fcoeio_status); 818 819 fcoeiocmd_release_buf: 820 if (ret == 0) { 821 ret = fcoe_copyout_iocdata(data, mode, fcoeio, obuf); 822 } else if (fcoeio->fcoeio_status) { 823 (void) fcoe_copyout_iocdata(data, mode, fcoeio, obuf); 824 } 825 826 if (obuf != NULL) { 827 kmem_free(obuf, fcoeio->fcoeio_olen); 828 obuf = NULL; 829 } 830 if (abuf != NULL) { 831 kmem_free(abuf, fcoeio->fcoeio_alen); 832 abuf = NULL; 833 } 834 835 if (ibuf != NULL) { 836 kmem_free(ibuf, fcoeio->fcoeio_ilen); 837 ibuf = NULL; 838 } 839 kmem_free(fcoeio, sizeof (fcoeio_t)); 840 841 return (ret); 842 } 843 844 /* 845 * Finish final initialization 846 */ 847 static int 848 fcoe_attach_init(fcoe_soft_state_t *ss) 849 { 850 char taskq_name[TASKQ_NAME_LEN]; 851 852 if (ddi_create_minor_node(ss->ss_dip, "admin", S_IFCHR, 853 ddi_get_instance(ss->ss_dip), DDI_PSEUDO, 0) != DDI_SUCCESS) { 854 FCOE_LOG("FCOE", "ddi_create_minor_node failed"); 855 return (FCOE_FAILURE); 856 } 857 858 /* 859 * watchdog responsible for release frame and dispatch events 860 */ 861 (void) snprintf(taskq_name, sizeof (taskq_name), "fcoe_mac"); 862 taskq_name[TASKQ_NAME_LEN - 1] = 0; 863 if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL, 864 taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 865 return (FCOE_FAILURE); 866 } 867 868 ss->ss_ioctl_flags = 0; 869 mutex_init(&ss->ss_ioctl_mutex, NULL, MUTEX_DRIVER, NULL); 870 list_create(&ss->ss_mac_list, sizeof (fcoe_mac_t), 871 offsetof(fcoe_mac_t, fm_ss_node)); 872 list_create(&ss->ss_pfrm_list, sizeof (fcoe_i_frame_t), 873 offsetof(fcoe_i_frame_t, fmi_pending_node)); 874 875 mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0); 876 cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL); 877 ss->ss_flags &= ~SS_FLAG_TERMINATE_WATCHDOG; 878 (void) ddi_taskq_dispatch(ss->ss_watchdog_taskq, 879 fcoe_watchdog, ss, DDI_SLEEP); 880 while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) { 881 delay(10); 882 } 883 fcoe_nworkers = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip, 884 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (char *)fcoe_workers_num, 4); 885 if (fcoe_nworkers < 1) { 886 fcoe_nworkers = 4; 887 } 888 fcoe_worker_init(); 889 890 ddi_report_dev(ss->ss_dip); 891 return (FCOE_SUCCESS); 892 } 893 894 /* 895 * Finish final uninitialization 896 */ 897 static int 898 fcoe_detach_uninit(fcoe_soft_state_t *ss) 899 { 900 int ret; 901 if (!list_is_empty(&ss->ss_mac_list)) { 902 FCOE_LOG("fcoe", "ss_mac_list is not empty when detach"); 903 return (FCOE_FAILURE); 904 } 905 906 if ((ret = fcoe_worker_fini()) != FCOE_SUCCESS) { 907 return (ret); 908 } 909 910 /* 911 * Stop watchdog 912 */ 913 if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 914 mutex_enter(&ss->ss_watch_mutex); 915 ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG; 916 cv_broadcast(&ss->ss_watch_cv); 917 mutex_exit(&ss->ss_watch_mutex); 918 while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 919 delay(10); 920 } 921 } 922 923 ddi_taskq_destroy(ss->ss_watchdog_taskq); 924 mutex_destroy(&ss->ss_watch_mutex); 925 cv_destroy(&ss->ss_watch_cv); 926 927 ddi_remove_minor_node(ss->ss_dip, NULL); 928 mutex_destroy(&ss->ss_ioctl_mutex); 929 list_destroy(&ss->ss_mac_list); 930 931 return (FCOE_SUCCESS); 932 } 933 934 /* 935 * Return mac instance if it exist, or else return NULL. 936 */ 937 fcoe_mac_t * 938 fcoe_lookup_mac_by_id(datalink_id_t linkid) 939 { 940 fcoe_mac_t *mac = NULL; 941 942 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 943 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 944 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 945 if (linkid != mac->fm_linkid) { 946 continue; 947 } 948 return (mac); 949 } 950 return (NULL); 951 } 952 953 /* 954 * port wwn will start with 20:..., node wwn will start with 10:... 955 */ 956 static void 957 fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, int is_pwwn, uint8_t idx) 958 { 959 ASSERT(wwn != NULL); 960 ASSERT(mac != NULL); 961 wwn[0] = (is_pwwn + 1) << 4; 962 wwn[1] = idx; 963 bcopy(mac, wwn + 2, ETHERADDRL); 964 } 965 966 /* 967 * Return fcoe_mac if it exists, otherwise create a new one 968 */ 969 static fcoe_mac_t * 970 fcoe_create_mac_by_id(datalink_id_t linkid) 971 { 972 fcoe_mac_t *mac = NULL; 973 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 974 975 mac = fcoe_lookup_mac_by_id(linkid); 976 if (mac != NULL) { 977 FCOE_LOG("fcoe", "fcoe_create_mac_by_id found one mac %d", 978 linkid); 979 return (mac); 980 } 981 982 mac = kmem_zalloc(sizeof (fcoe_mac_t), KM_SLEEP); 983 mac->fm_linkid = linkid; 984 mac->fm_flags = 0; 985 mac->fm_ss = fcoe_global_ss; 986 list_insert_tail(&mac->fm_ss->ss_mac_list, mac); 987 FCOE_LOG("fcoe", "fcoe_create_mac_by_id created one mac %d", linkid); 988 return (mac); 989 } 990 991 void 992 fcoe_destroy_mac(fcoe_mac_t *mac) 993 { 994 ASSERT(mac != NULL); 995 list_remove(&mac->fm_ss->ss_mac_list, mac); 996 kmem_free(mac, sizeof (fcoe_mac_t)); 997 } 998 999 /* 1000 * raw frame layout: 1001 * ethernet header + vlan header (optional) + FCoE header + 1002 * FC frame + FCoE tailer 1003 */ 1004 /* ARGSUSED */ 1005 mblk_t * 1006 fcoe_get_mblk(fcoe_mac_t *mac, uint32_t raw_frame_size) 1007 { 1008 mblk_t *mp; 1009 int err; 1010 1011 /* 1012 * FCFH_SIZE + PADDING_SIZE 1013 */ 1014 ASSERT(raw_frame_size >= 60); 1015 while ((mp = allocb((size_t)raw_frame_size, 0)) == NULL) { 1016 if ((err = strwaitbuf((size_t)raw_frame_size, BPRI_LO)) != 0) { 1017 FCOE_LOG("fcoe_get_mblk", "strwaitbuf return %d", err); 1018 return (NULL); 1019 } 1020 } 1021 mp->b_wptr = mp->b_rptr + raw_frame_size; 1022 1023 /* 1024 * We should always zero FC frame header 1025 */ 1026 bzero(mp->b_rptr + PADDING_HEADER_SIZE, 1027 sizeof (fcoe_fc_frame_header_t)); 1028 return (mp); 1029 } 1030 1031 static void 1032 fcoe_watchdog(void *arg) 1033 { 1034 fcoe_soft_state_t *ss = (fcoe_soft_state_t *)arg; 1035 fcoe_i_frame_t *fmi; 1036 fcoe_mac_t *mac = NULL; 1037 1038 FCOE_LOG("fcoe", "fcoe_soft_state is %p", ss); 1039 1040 mutex_enter(&ss->ss_watch_mutex); 1041 ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING; 1042 while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) { 1043 while (fmi = (fcoe_i_frame_t *)list_head(&ss->ss_pfrm_list)) { 1044 list_remove(&ss->ss_pfrm_list, fmi); 1045 mutex_exit(&ss->ss_watch_mutex); 1046 1047 mac = EPORT2MAC(fmi->fmi_frame->frm_eport); 1048 mac->fm_client.ect_release_sol_frame(fmi->fmi_frame); 1049 1050 mutex_enter(&ss->ss_watch_mutex); 1051 mac->fm_frm_cnt--; 1052 } 1053 1054 ss->ss_flags |= SS_FLAG_DOG_WAITING; 1055 (void) cv_wait(&ss->ss_watch_cv, &ss->ss_watch_mutex); 1056 ss->ss_flags &= ~SS_FLAG_DOG_WAITING; 1057 } 1058 1059 ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING; 1060 mutex_exit(&ss->ss_watch_mutex); 1061 } 1062 1063 static void 1064 fcoe_worker_init() 1065 { 1066 uint32_t i; 1067 1068 fcoe_nworkers_running = 0; 1069 fcoe_worker_taskq = ddi_taskq_create(0, "FCOE_WORKER_TASKQ", 1070 fcoe_nworkers, TASKQ_DEFAULTPRI, 0); 1071 fcoe_workers = (fcoe_worker_t *)kmem_zalloc(sizeof (fcoe_worker_t) * 1072 fcoe_nworkers, KM_SLEEP); 1073 for (i = 0; i < fcoe_nworkers; i++) { 1074 fcoe_worker_t *w = &fcoe_workers[i]; 1075 mutex_init(&w->worker_lock, NULL, MUTEX_DRIVER, NULL); 1076 cv_init(&w->worker_cv, NULL, CV_DRIVER, NULL); 1077 w->worker_flags &= ~FCOE_WORKER_TERMINATE; 1078 list_create(&w->worker_frm_list, sizeof (fcoe_i_frame_t), 1079 offsetof(fcoe_i_frame_t, fmi_pending_node)); 1080 (void) ddi_taskq_dispatch(fcoe_worker_taskq, fcoe_worker_frame, 1081 w, DDI_SLEEP); 1082 } 1083 while (fcoe_nworkers_running != fcoe_nworkers) { 1084 delay(10); 1085 } 1086 } 1087 1088 static int 1089 fcoe_worker_fini() 1090 { 1091 uint32_t i; 1092 1093 for (i = 0; i < fcoe_nworkers; i++) { 1094 fcoe_worker_t *w = &fcoe_workers[i]; 1095 mutex_enter(&w->worker_lock); 1096 if (w->worker_flags & FCOE_WORKER_STARTED) { 1097 w->worker_flags |= FCOE_WORKER_TERMINATE; 1098 cv_signal(&w->worker_cv); 1099 } 1100 mutex_exit(&w->worker_lock); 1101 } 1102 1103 while (fcoe_nworkers_running != 0) { 1104 delay(drv_usectohz(10000)); 1105 } 1106 1107 ddi_taskq_destroy(fcoe_worker_taskq); 1108 kmem_free(fcoe_workers, sizeof (fcoe_worker_t) * fcoe_nworkers); 1109 fcoe_workers = NULL; 1110 return (FCOE_SUCCESS); 1111 } 1112 1113 static int 1114 fcoe_crc_verify(fcoe_frame_t *frm) 1115 { 1116 uint32_t crc; 1117 uint8_t *crc_array = FRM2FMI(frm)->fmi_fft->fft_crc; 1118 uint32_t crc_from_frame = ~(crc_array[0] | (crc_array[1] << 8) | 1119 (crc_array[2] << 16) | (crc_array[3] << 24)); 1120 CRC32(crc, frm->frm_fc_frame, frm->frm_fc_frame_size, -1U, crc32_table); 1121 return (crc == crc_from_frame ? FCOE_SUCCESS : FCOE_FAILURE); 1122 } 1123 1124 static void 1125 fcoe_worker_frame(void *arg) 1126 { 1127 fcoe_worker_t *w = (fcoe_worker_t *)arg; 1128 fcoe_i_frame_t *fmi; 1129 int ret; 1130 1131 atomic_add_32(&fcoe_nworkers_running, 1); 1132 mutex_enter(&w->worker_lock); 1133 w->worker_flags |= FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE; 1134 while ((w->worker_flags & FCOE_WORKER_TERMINATE) == 0) { 1135 /* 1136 * loop through the frames 1137 */ 1138 while (fmi = list_head(&w->worker_frm_list)) { 1139 list_remove(&w->worker_frm_list, fmi); 1140 mutex_exit(&w->worker_lock); 1141 /* 1142 * do the checksum 1143 */ 1144 ret = fcoe_crc_verify(fmi->fmi_frame); 1145 if (ret == FCOE_SUCCESS) { 1146 fmi->fmi_mac->fm_client.ect_rx_frame( 1147 fmi->fmi_frame); 1148 } else { 1149 fcoe_release_frame(fmi->fmi_frame); 1150 } 1151 mutex_enter(&w->worker_lock); 1152 w->worker_ntasks--; 1153 } 1154 w->worker_flags &= ~FCOE_WORKER_ACTIVE; 1155 cv_wait(&w->worker_cv, &w->worker_lock); 1156 w->worker_flags |= FCOE_WORKER_ACTIVE; 1157 } 1158 w->worker_flags &= ~(FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE); 1159 mutex_exit(&w->worker_lock); 1160 atomic_add_32(&fcoe_nworkers_running, -1); 1161 list_destroy(&w->worker_frm_list); 1162 } 1163 1164 void 1165 fcoe_post_frame(fcoe_frame_t *frm) 1166 { 1167 fcoe_worker_t *w; 1168 uint16_t oxid = FRM_OXID(frm); 1169 1170 w = &fcoe_workers[oxid % fcoe_nworkers_running]; 1171 mutex_enter(&w->worker_lock); 1172 list_insert_tail(&w->worker_frm_list, frm->frm_fcoe_private); 1173 w->worker_ntasks++; 1174 if ((w->worker_flags & FCOE_WORKER_ACTIVE) == 0) { 1175 cv_signal(&w->worker_cv); 1176 } 1177 mutex_exit(&w->worker_lock); 1178 } 1179 1180 /* 1181 * The max length of every LOG is 158 1182 */ 1183 void 1184 fcoe_trace(caddr_t ident, const char *fmt, ...) 1185 { 1186 va_list args; 1187 char tbuf[160]; 1188 int len; 1189 clock_t curclock; 1190 clock_t usec; 1191 1192 if (fcoe_trace_on == 0) { 1193 return; 1194 } 1195 1196 curclock = ddi_get_lbolt(); 1197 usec = (curclock - fcoe_trace_start) * usec_per_tick; 1198 len = snprintf(tbuf, 158, "%lu.%03lus 0t%lu %s ", (usec / 1199 (1000 * 1000)), ((usec % (1000 * 1000)) / 1000), 1200 curclock, (ident ? ident : "unknown")); 1201 va_start(args, fmt); 1202 len += vsnprintf(tbuf + len, 158 - len, fmt, args); 1203 va_end(args); 1204 1205 if (len > 158) { 1206 len = 158; 1207 } 1208 tbuf[len++] = '\n'; 1209 tbuf[len] = 0; 1210 1211 mutex_enter(&fcoe_trace_buf_lock); 1212 bcopy(tbuf, &fcoe_trace_buf[fcoe_trace_buf_curndx], len+1); 1213 fcoe_trace_buf_curndx += len; 1214 if (fcoe_trace_buf_curndx > (fcoe_trace_buf_size - 320)) { 1215 fcoe_trace_buf_curndx = 0; 1216 } 1217 mutex_exit(&fcoe_trace_buf_lock); 1218 } 1219 1220 /* 1221 * Check whether the pwwn or nwwn already exist or not 1222 * Return value: 1223 * 1: PWWN conflicted 1224 * -1: NWWN conflicted 1225 * 0: No conflict 1226 */ 1227 static int 1228 fcoe_cmp_wwn(fcoe_mac_t *checkedmac) 1229 { 1230 fcoe_mac_t *mac; 1231 uint8_t *nwwn, *pwwn, *cnwwn, *cpwwn; 1232 1233 cnwwn = checkedmac->fm_eport.eport_nodewwn; 1234 cpwwn = checkedmac->fm_eport.eport_portwwn; 1235 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 1236 1237 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 1238 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 1239 if (mac == checkedmac) { 1240 continue; 1241 } 1242 nwwn = mac->fm_eport.eport_nodewwn; 1243 pwwn = mac->fm_eport.eport_portwwn; 1244 1245 if (memcmp(nwwn, cnwwn, 8) == 0) { 1246 return (-1); 1247 } 1248 1249 if (memcmp(pwwn, cpwwn, 8) == 0) { 1250 return (1); 1251 } 1252 } 1253 return (0); 1254 } 1255 1256 static int 1257 fcoe_get_port_list(fcoe_port_instance_t *ports, int count) 1258 { 1259 fcoe_mac_t *mac = NULL; 1260 int i = 0; 1261 1262 ASSERT(ports != NULL); 1263 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 1264 1265 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 1266 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 1267 if (i < count) { 1268 bcopy(mac->fm_eport.eport_portwwn, 1269 ports[i].fpi_pwwn, 8); 1270 ports[i].fpi_mac_linkid = mac->fm_linkid; 1271 bcopy(mac->fm_current_addr, 1272 ports[i].fpi_mac_current_addr, ETHERADDRL); 1273 bcopy(mac->fm_primary_addr, 1274 ports[i].fpi_mac_factory_addr, ETHERADDRL); 1275 ports[i].fpi_port_type = 1276 EPORT_CLT_TYPE(&mac->fm_eport); 1277 ports[i].fpi_mtu_size = 1278 mac->fm_eport.eport_mtu; 1279 ports[i].fpi_mac_promisc = 1280 mac->fm_promisc_handle != NULL ? 1 : 0; 1281 } 1282 i++; 1283 } 1284 return (i); 1285 } 1286