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 "20090311-1.00" 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 static void *fcoe_state = NULL; 227 fcoe_soft_state_t *fcoe_global_ss = NULL; 228 int fcoe_use_ext_log = 1; 229 230 static ddi_taskq_t *fcoe_worker_taskq; 231 static fcoe_worker_t *fcoe_workers; 232 static uint32_t fcoe_nworkers_running; 233 234 const char *fcoe_workers_num = "workers-number"; 235 volatile int fcoe_nworkers; 236 237 /* 238 * Common loadable module entry points _init, _fini, _info 239 */ 240 241 int 242 _init(void) 243 { 244 int ret; 245 246 ret = ddi_soft_state_init(&fcoe_state, sizeof (fcoe_soft_state_t), 0); 247 if (ret == 0) { 248 ret = mod_install(&modlinkage); 249 if (ret != 0) { 250 ddi_soft_state_fini(&fcoe_state); 251 } else { 252 fcoe_trace_start = ddi_get_lbolt(); 253 ftb = kmem_zalloc(fcoe_trace_buf_size, 254 KM_SLEEP); 255 fcoe_trace_buf = ftb; 256 mutex_init(&fcoe_trace_buf_lock, NULL, MUTEX_DRIVER, 0); 257 } 258 } 259 260 FCOE_LOG("fcoe", "exit _init with %x", ret); 261 262 return (ret); 263 } 264 265 int 266 _fini(void) 267 { 268 int ret; 269 270 ret = mod_remove(&modlinkage); 271 if (ret == 0) { 272 ddi_soft_state_fini(&fcoe_state); 273 } 274 275 FCOE_LOG("fcoe", "exit _fini with %x", ret); 276 if (ret == 0) { 277 kmem_free(fcoe_trace_buf, fcoe_trace_buf_size); 278 mutex_destroy(&fcoe_trace_buf_lock); 279 } 280 281 return (ret); 282 } 283 284 int 285 _info(struct modinfo *modinfop) 286 { 287 return (mod_info(&modlinkage, modinfop)); 288 } 289 290 /* 291 * Autoconfiguration entry points: attach, detach, getinfo 292 */ 293 294 static int 295 fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 296 { 297 int ret = DDI_FAILURE; 298 int fcoe_ret; 299 int instance; 300 fcoe_soft_state_t *ss; 301 302 instance = ddi_get_instance(dip); 303 switch (cmd) { 304 case DDI_ATTACH: 305 ret = ddi_soft_state_zalloc(fcoe_state, instance); 306 if (ret == DDI_FAILURE) { 307 FCOE_LOG(0, "soft_state_zalloc-%x/%x", ret, instance); 308 return (ret); 309 } 310 311 ss = ddi_get_soft_state(fcoe_state, instance); 312 ss->ss_dip = dip; 313 314 ASSERT(fcoe_global_ss == NULL); 315 fcoe_global_ss = ss; 316 fcoe_ret = fcoe_attach_init(ss); 317 if (fcoe_ret == FCOE_SUCCESS) { 318 ret = DDI_SUCCESS; 319 } 320 321 FCOE_LOG("fcoe", "fcoe_attach_init end with-%x", fcoe_ret); 322 break; 323 324 case DDI_RESUME: 325 ret = DDI_SUCCESS; 326 break; 327 328 default: 329 FCOE_LOG("fcoe", "unsupported attach cmd-%x", cmd); 330 break; 331 } 332 333 return (ret); 334 } 335 336 static int 337 fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 338 { 339 int ret = DDI_FAILURE; 340 int fcoe_ret; 341 int instance; 342 fcoe_soft_state_t *ss; 343 344 instance = ddi_get_instance(dip); 345 ss = ddi_get_soft_state(fcoe_state, instance); 346 if (ss == NULL) { 347 return (ret); 348 } 349 350 ASSERT(fcoe_global_ss != NULL); 351 ASSERT(dip == fcoe_global_ss->ss_dip); 352 switch (cmd) { 353 case DDI_DETACH: 354 fcoe_ret = fcoe_detach_uninit(ss); 355 if (fcoe_ret == FCOE_SUCCESS) { 356 ret = DDI_SUCCESS; 357 fcoe_global_ss = NULL; 358 } 359 360 break; 361 362 case DDI_SUSPEND: 363 ret = DDI_SUCCESS; 364 break; 365 366 default: 367 FCOE_LOG(0, "unsupported detach cmd-%x", cmd); 368 break; 369 } 370 371 return (ret); 372 } 373 374 /* 375 * FCA driver's intercepted bus control operations. 376 */ 377 static int 378 fcoe_bus_ctl(dev_info_t *fcoe_dip, dev_info_t *rip, 379 ddi_ctl_enum_t op, void *clientarg, void *result) 380 { 381 int ret; 382 switch (op) { 383 case DDI_CTLOPS_REPORTDEV: 384 case DDI_CTLOPS_IOMIN: 385 ret = DDI_SUCCESS; 386 break; 387 388 case DDI_CTLOPS_INITCHILD: 389 ret = fcoe_initchild(fcoe_dip, (dev_info_t *)clientarg); 390 break; 391 392 case DDI_CTLOPS_UNINITCHILD: 393 ret = fcoe_uninitchild(fcoe_dip, (dev_info_t *)clientarg); 394 break; 395 396 default: 397 ret = ddi_ctlops(fcoe_dip, rip, op, clientarg, result); 398 break; 399 } 400 401 return (ret); 402 } 403 404 /* 405 * We need specify the dev address for client driver's instance, or we 406 * can't online client driver's instance. 407 */ 408 /* ARGSUSED */ 409 static int 410 fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip) 411 { 412 char name[32]; 413 static int inicounter = 0; 414 static int tgtcounter = 0; 415 int *counter; 416 417 if (strcmp(ddi_driver_name(client_dip), FCOET_DRIVER_NAME) == 0) { 418 counter = &tgtcounter; 419 tgtcounter++; 420 } else { 421 counter = &inicounter; 422 inicounter++; 423 } 424 425 bzero(name, 32); 426 (void) sprintf((char *)name, "%x,0", *counter); 427 ddi_set_name_addr(client_dip, name); 428 429 return (DDI_SUCCESS); 430 } 431 432 /* ARGSUSED */ 433 static int 434 fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip) 435 { 436 ddi_set_name_addr(client_dip, NULL); 437 return (DDI_SUCCESS); 438 } 439 440 /* 441 * Device access entry points 442 */ 443 static int 444 fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp) 445 { 446 int instance; 447 fcoe_soft_state_t *ss; 448 449 if (otype != OTYP_CHR) { 450 return (EINVAL); 451 } 452 453 /* 454 * Since this is for debugging only, only allow root to issue ioctl now 455 */ 456 if (drv_priv(credp) != 0) { 457 return (EPERM); 458 } 459 460 instance = (int)getminor(*devp); 461 ss = ddi_get_soft_state(fcoe_state, instance); 462 if (ss == NULL) { 463 return (ENXIO); 464 } 465 466 mutex_enter(&ss->ss_ioctl_mutex); 467 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) { 468 /* 469 * It is already open for exclusive access. 470 * So shut the door on this caller. 471 */ 472 mutex_exit(&ss->ss_ioctl_mutex); 473 return (EBUSY); 474 } 475 476 if (flag & FEXCL) { 477 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) { 478 /* 479 * Exclusive operation not possible 480 * as it is already opened 481 */ 482 mutex_exit(&ss->ss_ioctl_mutex); 483 return (EBUSY); 484 } 485 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL; 486 } 487 488 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_OPEN; 489 mutex_exit(&ss->ss_ioctl_mutex); 490 491 return (0); 492 } 493 494 /* ARGSUSED */ 495 static int 496 fcoe_close(dev_t dev, int flag, int otype, cred_t *credp) 497 { 498 int instance; 499 fcoe_soft_state_t *ss; 500 501 if (otype != OTYP_CHR) { 502 return (EINVAL); 503 } 504 505 instance = (int)getminor(dev); 506 ss = ddi_get_soft_state(fcoe_state, instance); 507 if (ss == NULL) { 508 return (ENXIO); 509 } 510 511 mutex_enter(&ss->ss_ioctl_mutex); 512 if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) { 513 mutex_exit(&ss->ss_ioctl_mutex); 514 return (ENODEV); 515 } 516 517 ss->ss_ioctl_flags &= ~FCOE_IOCTL_FLAG_MASK; 518 mutex_exit(&ss->ss_ioctl_mutex); 519 520 return (0); 521 } 522 523 /* ARGSUSED */ 524 static int 525 fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 526 cred_t *credp, int *rval) 527 { 528 fcoe_soft_state_t *ss; 529 int ret = 0; 530 531 if (drv_priv(credp) != 0) { 532 return (EPERM); 533 } 534 535 ss = ddi_get_soft_state(fcoe_state, (int32_t)getminor(dev)); 536 if (ss == NULL) { 537 return (ENXIO); 538 } 539 540 mutex_enter(&ss->ss_ioctl_mutex); 541 if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) { 542 mutex_exit(&ss->ss_ioctl_mutex); 543 return (ENXIO); 544 } 545 mutex_exit(&ss->ss_ioctl_mutex); 546 547 switch (cmd) { 548 case FCOEIO_CMD: 549 ret = fcoe_iocmd(ss, data, mode); 550 break; 551 default: 552 FCOE_LOG(0, "fcoe_ioctl: ioctl-0x%02X", cmd); 553 ret = ENOTTY; 554 break; 555 } 556 557 return (ret); 558 } 559 560 static int 561 fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio, 562 void **ibuf, void **abuf, void **obuf) 563 { 564 int ret = 0; 565 566 *ibuf = NULL; 567 *abuf = NULL; 568 *obuf = NULL; 569 *fcoeio = kmem_zalloc(sizeof (fcoeio_t), KM_SLEEP); 570 if (ddi_copyin((void *)data, *fcoeio, sizeof (fcoeio_t), mode) != 0) { 571 ret = EFAULT; 572 goto copyin_iocdata_fail; 573 } 574 575 if ((*fcoeio)->fcoeio_ilen > FCOEIO_MAX_BUF_LEN || 576 (*fcoeio)->fcoeio_alen > FCOEIO_MAX_BUF_LEN || 577 (*fcoeio)->fcoeio_olen > FCOEIO_MAX_BUF_LEN) { 578 ret = EFAULT; 579 goto copyin_iocdata_fail; 580 } 581 582 if ((*fcoeio)->fcoeio_ilen) { 583 *ibuf = kmem_zalloc((*fcoeio)->fcoeio_ilen, KM_SLEEP); 584 if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_ibuf, 585 *ibuf, (*fcoeio)->fcoeio_ilen, mode) != 0) { 586 ret = EFAULT; 587 goto copyin_iocdata_fail; 588 } 589 } 590 591 if ((*fcoeio)->fcoeio_alen) { 592 *abuf = kmem_zalloc((*fcoeio)->fcoeio_alen, KM_SLEEP); 593 if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_abuf, 594 *abuf, (*fcoeio)->fcoeio_alen, mode) != 0) { 595 ret = EFAULT; 596 goto copyin_iocdata_fail; 597 } 598 } 599 600 if ((*fcoeio)->fcoeio_olen) { 601 *obuf = kmem_zalloc((*fcoeio)->fcoeio_olen, KM_SLEEP); 602 } 603 return (ret); 604 605 copyin_iocdata_fail: 606 if (*abuf) { 607 kmem_free(*abuf, (*fcoeio)->fcoeio_alen); 608 *abuf = NULL; 609 } 610 611 if (*ibuf) { 612 kmem_free(*ibuf, (*fcoeio)->fcoeio_ilen); 613 *ibuf = NULL; 614 } 615 616 kmem_free(*fcoeio, sizeof (fcoeio_t)); 617 return (ret); 618 } 619 620 static int 621 fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, void *obuf) 622 { 623 if (fcoeio->fcoeio_olen) { 624 if (ddi_copyout(obuf, 625 (void *)(unsigned long)fcoeio->fcoeio_obuf, 626 fcoeio->fcoeio_olen, mode) != 0) { 627 return (EFAULT); 628 } 629 } 630 631 if (ddi_copyout(fcoeio, (void *)data, sizeof (fcoeio_t), mode) != 0) { 632 return (EFAULT); 633 } 634 return (0); 635 } 636 637 static int 638 fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode) 639 { 640 int ret; 641 fcoe_mac_t *fcoe_mac; 642 void *ibuf = NULL; 643 void *obuf = NULL; 644 void *abuf = NULL; 645 fcoeio_t *fcoeio; 646 647 ret = fcoe_copyin_iocdata(data, mode, &fcoeio, &ibuf, &abuf, &obuf); 648 if (ret != 0) { 649 goto fcoeiocmd_release_buf; 650 } 651 652 /* 653 * If an exclusive open was demanded during open, ensure that 654 * only one thread can execute an ioctl at a time 655 */ 656 mutex_enter(&ss->ss_ioctl_mutex); 657 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) { 658 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL_BUSY) { 659 mutex_exit(&ss->ss_ioctl_mutex); 660 fcoeio->fcoeio_status = FCOEIOE_BUSY; 661 ret = EBUSY; 662 goto fcoeiocmd_release_buf; 663 } 664 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL_BUSY; 665 } 666 mutex_exit(&ss->ss_ioctl_mutex); 667 668 fcoeio->fcoeio_status = 0; 669 670 switch (fcoeio->fcoeio_cmd) { 671 case FCOEIO_CREATE_FCOE_PORT: { 672 fcoeio_create_port_param_t *param = 673 (fcoeio_create_port_param_t *)ibuf; 674 int cmpwwn = 0; 675 fcoe_port_t *eport; 676 677 if (fcoeio->fcoeio_ilen != 678 sizeof (fcoeio_create_port_param_t) || 679 fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) { 680 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 681 ret = EINVAL; 682 break; 683 } 684 685 mutex_enter(&ss->ss_ioctl_mutex); 686 fcoe_mac = fcoe_create_mac_by_id(param->fcp_mac_linkid); 687 if (fcoe_mac == NULL) { 688 mutex_exit(&ss->ss_ioctl_mutex); 689 fcoeio->fcoeio_status = FCOEIOE_CREATE_MAC; 690 ret = EIO; 691 break; 692 } 693 694 if (fcoe_mac->fm_flags & FCOE_MAC_FLAG_ENABLED) { 695 mutex_exit(&ss->ss_ioctl_mutex); 696 fcoeio->fcoeio_status = FCOEIOE_ALREADY; 697 ret = EALREADY; 698 break; 699 } else { 700 ret = fcoe_open_mac(fcoe_mac, param->fcp_force_promisc, 701 &fcoeio->fcoeio_status); 702 if (ret != 0) { 703 fcoe_destroy_mac(fcoe_mac); 704 mutex_exit(&ss->ss_ioctl_mutex); 705 if (fcoeio->fcoeio_status == 0) { 706 fcoeio->fcoeio_status = 707 FCOEIOE_OPEN_MAC; 708 } 709 ret = EIO; 710 break; 711 } else { 712 fcoe_mac->fm_flags |= FCOE_MAC_FLAG_ENABLED; 713 } 714 } 715 716 /* 717 * Provide PWWN and NWWN based on mac address 718 */ 719 eport = &fcoe_mac->fm_eport; 720 if (!param->fcp_pwwn_provided) { 721 fcoe_init_wwn_from_mac(eport->eport_portwwn, 722 fcoe_mac->fm_current_addr, 1, 0); 723 } else { 724 (void) memcpy(eport->eport_portwwn, param->fcp_pwwn, 8); 725 } 726 727 if (!param->fcp_nwwn_provided) { 728 fcoe_init_wwn_from_mac(eport->eport_nodewwn, 729 fcoe_mac->fm_current_addr, 0, 0); 730 } else { 731 (void) memcpy(eport->eport_nodewwn, param->fcp_nwwn, 8); 732 } 733 734 cmpwwn = fcoe_cmp_wwn(fcoe_mac); 735 736 if (cmpwwn != 0) { 737 if (cmpwwn == 1) { 738 fcoeio->fcoeio_status = FCOEIOE_PWWN_CONFLICTED; 739 } else if (cmpwwn == -1) { 740 fcoeio->fcoeio_status = FCOEIOE_NWWN_CONFLICTED; 741 } 742 (void) fcoe_close_mac(fcoe_mac); 743 fcoe_destroy_mac(fcoe_mac); 744 mutex_exit(&ss->ss_ioctl_mutex); 745 ret = ENOTUNIQ; 746 break; 747 } 748 749 if (ret == 0) { 750 ret = fcoe_create_port(ss->ss_dip, 751 fcoe_mac, 752 (param->fcp_port_type == FCOE_CLIENT_TARGET)); 753 if (ret != 0) { 754 (void) fcoe_close_mac(fcoe_mac); 755 fcoe_destroy_mac(fcoe_mac); 756 fcoeio->fcoeio_status = FCOEIOE_CREATE_PORT; 757 ret = EIO; 758 } 759 } 760 mutex_exit(&ss->ss_ioctl_mutex); 761 762 break; 763 } 764 765 case FCOEIO_DELETE_FCOE_PORT: { 766 fcoeio_delete_port_param_t *del_port_param = 767 (fcoeio_delete_port_param_t *)ibuf; 768 uint64_t *is_target = (uint64_t *)obuf; 769 770 if (fcoeio->fcoeio_ilen < sizeof (fcoeio_delete_port_param_t) || 771 fcoeio->fcoeio_olen != sizeof (uint64_t) || 772 fcoeio->fcoeio_xfer != FCOEIO_XFER_RW) { 773 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 774 ret = EINVAL; 775 break; 776 } 777 778 mutex_enter(&ss->ss_ioctl_mutex); 779 ret = fcoe_delete_port(ss->ss_dip, fcoeio, 780 del_port_param->fdp_mac_linkid, is_target); 781 if (ret != 0) { 782 FCOE_LOG("fcoe", 783 "fcoe_delete_port failed: %d", ret); 784 } 785 mutex_exit(&ss->ss_ioctl_mutex); 786 break; 787 } 788 789 case FCOEIO_GET_FCOE_PORT_LIST: { 790 fcoe_port_list_t *list = (fcoe_port_list_t *)obuf; 791 int count; 792 793 if (fcoeio->fcoeio_xfer != FCOEIO_XFER_READ || 794 fcoeio->fcoeio_olen < sizeof (fcoe_port_list_t)) { 795 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 796 ret = EINVAL; 797 break; 798 } 799 mutex_enter(&ss->ss_ioctl_mutex); 800 801 list->numPorts = 1 + (fcoeio->fcoeio_olen - 802 sizeof (fcoe_port_list_t))/sizeof (fcoe_port_instance_t); 803 804 count = fcoe_get_port_list(list->ports, list->numPorts); 805 806 if (count > list->numPorts) { 807 fcoeio->fcoeio_status = FCOEIOE_MORE_DATA; 808 ret = ENOSPC; 809 } 810 list->numPorts = count; 811 mutex_exit(&ss->ss_ioctl_mutex); 812 813 break; 814 815 } 816 817 default: 818 return (ENOTTY); 819 } 820 821 FCOE_LOG("fcoe", "fcoe_ioctl returned %d, fcoeio_status = %d", 822 ret, fcoeio->fcoeio_status); 823 824 fcoeiocmd_release_buf: 825 if (ret == 0) { 826 ret = fcoe_copyout_iocdata(data, mode, fcoeio, obuf); 827 } else if (fcoeio->fcoeio_status) { 828 (void) fcoe_copyout_iocdata(data, mode, fcoeio, obuf); 829 } 830 831 if (obuf != NULL) { 832 kmem_free(obuf, fcoeio->fcoeio_olen); 833 obuf = NULL; 834 } 835 if (abuf != NULL) { 836 kmem_free(abuf, fcoeio->fcoeio_alen); 837 abuf = NULL; 838 } 839 840 if (ibuf != NULL) { 841 kmem_free(ibuf, fcoeio->fcoeio_ilen); 842 ibuf = NULL; 843 } 844 kmem_free(fcoeio, sizeof (fcoeio_t)); 845 846 return (ret); 847 } 848 849 /* 850 * Finish final initialization 851 */ 852 static int 853 fcoe_attach_init(fcoe_soft_state_t *ss) 854 { 855 char taskq_name[TASKQ_NAME_LEN]; 856 857 if (ddi_create_minor_node(ss->ss_dip, "admin", S_IFCHR, 858 ddi_get_instance(ss->ss_dip), DDI_PSEUDO, 0) != DDI_SUCCESS) { 859 FCOE_LOG("FCOE", "ddi_create_minor_node failed"); 860 return (FCOE_FAILURE); 861 } 862 863 /* 864 * watchdog responsible for release frame and dispatch events 865 */ 866 (void) snprintf(taskq_name, sizeof (taskq_name), "fcoe_mac"); 867 taskq_name[TASKQ_NAME_LEN - 1] = 0; 868 if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL, 869 taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 870 return (FCOE_FAILURE); 871 } 872 873 ss->ss_ioctl_flags = 0; 874 mutex_init(&ss->ss_ioctl_mutex, NULL, MUTEX_DRIVER, NULL); 875 list_create(&ss->ss_mac_list, sizeof (fcoe_mac_t), 876 offsetof(fcoe_mac_t, fm_ss_node)); 877 list_create(&ss->ss_pfrm_list, sizeof (fcoe_i_frame_t), 878 offsetof(fcoe_i_frame_t, fmi_pending_node)); 879 880 mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0); 881 cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL); 882 ss->ss_flags &= ~SS_FLAG_TERMINATE_WATCHDOG; 883 (void) ddi_taskq_dispatch(ss->ss_watchdog_taskq, 884 fcoe_watchdog, ss, DDI_SLEEP); 885 while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) { 886 delay(10); 887 } 888 fcoe_nworkers = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip, 889 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (char *)fcoe_workers_num, 4); 890 if (fcoe_nworkers < 1) { 891 fcoe_nworkers = 4; 892 } 893 fcoe_worker_init(); 894 895 ddi_report_dev(ss->ss_dip); 896 return (FCOE_SUCCESS); 897 } 898 899 /* 900 * Finish final uninitialization 901 */ 902 static int 903 fcoe_detach_uninit(fcoe_soft_state_t *ss) 904 { 905 int ret; 906 if (!list_is_empty(&ss->ss_mac_list)) { 907 FCOE_LOG("fcoe", "ss_mac_list is not empty when detach"); 908 return (FCOE_FAILURE); 909 } 910 911 if ((ret = fcoe_worker_fini()) != FCOE_SUCCESS) { 912 return (ret); 913 } 914 915 /* 916 * Stop watchdog 917 */ 918 if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 919 mutex_enter(&ss->ss_watch_mutex); 920 ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG; 921 cv_broadcast(&ss->ss_watch_cv); 922 mutex_exit(&ss->ss_watch_mutex); 923 while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 924 delay(10); 925 } 926 } 927 928 ddi_taskq_destroy(ss->ss_watchdog_taskq); 929 mutex_destroy(&ss->ss_watch_mutex); 930 cv_destroy(&ss->ss_watch_cv); 931 932 ddi_remove_minor_node(ss->ss_dip, NULL); 933 mutex_destroy(&ss->ss_ioctl_mutex); 934 list_destroy(&ss->ss_mac_list); 935 936 return (FCOE_SUCCESS); 937 } 938 939 /* 940 * Return mac instance if it exist, or else return NULL. 941 */ 942 fcoe_mac_t * 943 fcoe_lookup_mac_by_id(datalink_id_t linkid) 944 { 945 fcoe_mac_t *mac = NULL; 946 947 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 948 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 949 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 950 if (linkid != mac->fm_linkid) { 951 continue; 952 } 953 return (mac); 954 } 955 return (NULL); 956 } 957 958 /* 959 * port wwn will start with 20:..., node wwn will start with 10:... 960 */ 961 static void 962 fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, int is_pwwn, uint8_t idx) 963 { 964 ASSERT(wwn != NULL); 965 ASSERT(mac != NULL); 966 wwn[0] = (is_pwwn + 1) << 4; 967 wwn[1] = idx; 968 bcopy(mac, wwn + 2, ETHERADDRL); 969 } 970 971 /* 972 * Return fcoe_mac if it exists, otherwise create a new one 973 */ 974 static fcoe_mac_t * 975 fcoe_create_mac_by_id(datalink_id_t linkid) 976 { 977 fcoe_mac_t *mac = NULL; 978 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 979 980 mac = fcoe_lookup_mac_by_id(linkid); 981 if (mac != NULL) { 982 FCOE_LOG("fcoe", "fcoe_create_mac_by_id found one mac %d", 983 linkid); 984 return (mac); 985 } 986 987 mac = kmem_zalloc(sizeof (fcoe_mac_t), KM_SLEEP); 988 mac->fm_linkid = linkid; 989 mac->fm_flags = 0; 990 mac->fm_ss = fcoe_global_ss; 991 list_insert_tail(&mac->fm_ss->ss_mac_list, mac); 992 FCOE_LOG("fcoe", "fcoe_create_mac_by_id created one mac %d", linkid); 993 return (mac); 994 } 995 996 void 997 fcoe_destroy_mac(fcoe_mac_t *mac) 998 { 999 ASSERT(mac != NULL); 1000 list_remove(&mac->fm_ss->ss_mac_list, mac); 1001 kmem_free(mac, sizeof (fcoe_mac_t)); 1002 } 1003 1004 /* 1005 * raw frame layout: 1006 * ethernet header + vlan header (optional) + FCoE header + 1007 * FC frame + FCoE tailer 1008 */ 1009 /* ARGSUSED */ 1010 mblk_t * 1011 fcoe_get_mblk(fcoe_mac_t *mac, uint32_t raw_frame_size) 1012 { 1013 mblk_t *mp; 1014 int err; 1015 1016 /* 1017 * FCFH_SIZE + PADDING_SIZE 1018 */ 1019 ASSERT(raw_frame_size >= 60); 1020 while ((mp = allocb((size_t)raw_frame_size, 0)) == NULL) { 1021 if ((err = strwaitbuf((size_t)raw_frame_size, BPRI_LO)) != 0) { 1022 FCOE_LOG("fcoe_get_mblk", "strwaitbuf return %d", err); 1023 return (NULL); 1024 } 1025 } 1026 mp->b_wptr = mp->b_rptr + raw_frame_size; 1027 1028 /* 1029 * We should always zero FC frame header 1030 */ 1031 bzero(mp->b_rptr + PADDING_HEADER_SIZE, 1032 sizeof (fcoe_fc_frame_header_t)); 1033 return (mp); 1034 } 1035 1036 static void 1037 fcoe_watchdog(void *arg) 1038 { 1039 fcoe_soft_state_t *ss = (fcoe_soft_state_t *)arg; 1040 fcoe_i_frame_t *fmi; 1041 fcoe_mac_t *mac = NULL; 1042 1043 FCOE_LOG("fcoe", "fcoe_soft_state is %p", ss); 1044 1045 mutex_enter(&ss->ss_watch_mutex); 1046 ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING; 1047 while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) { 1048 while (fmi = (fcoe_i_frame_t *)list_head(&ss->ss_pfrm_list)) { 1049 list_remove(&ss->ss_pfrm_list, fmi); 1050 mutex_exit(&ss->ss_watch_mutex); 1051 1052 mac = EPORT2MAC(fmi->fmi_frame->frm_eport); 1053 mac->fm_client.ect_release_sol_frame(fmi->fmi_frame); 1054 1055 mutex_enter(&ss->ss_watch_mutex); 1056 mac->fm_frm_cnt--; 1057 } 1058 1059 ss->ss_flags |= SS_FLAG_DOG_WAITING; 1060 (void) cv_wait(&ss->ss_watch_cv, &ss->ss_watch_mutex); 1061 ss->ss_flags &= ~SS_FLAG_DOG_WAITING; 1062 } 1063 1064 ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING; 1065 mutex_exit(&ss->ss_watch_mutex); 1066 } 1067 1068 static void 1069 fcoe_worker_init() 1070 { 1071 uint32_t i; 1072 1073 fcoe_nworkers_running = 0; 1074 fcoe_worker_taskq = ddi_taskq_create(0, "FCOE_WORKER_TASKQ", 1075 fcoe_nworkers, TASKQ_DEFAULTPRI, 0); 1076 fcoe_workers = (fcoe_worker_t *)kmem_zalloc(sizeof (fcoe_worker_t) * 1077 fcoe_nworkers, KM_SLEEP); 1078 for (i = 0; i < fcoe_nworkers; i++) { 1079 fcoe_worker_t *w = &fcoe_workers[i]; 1080 mutex_init(&w->worker_lock, NULL, MUTEX_DRIVER, NULL); 1081 cv_init(&w->worker_cv, NULL, CV_DRIVER, NULL); 1082 w->worker_flags &= ~FCOE_WORKER_TERMINATE; 1083 list_create(&w->worker_frm_list, sizeof (fcoe_i_frame_t), 1084 offsetof(fcoe_i_frame_t, fmi_pending_node)); 1085 (void) ddi_taskq_dispatch(fcoe_worker_taskq, fcoe_worker_frame, 1086 w, DDI_SLEEP); 1087 } 1088 while (fcoe_nworkers_running != fcoe_nworkers) { 1089 delay(10); 1090 } 1091 } 1092 1093 static int 1094 fcoe_worker_fini() 1095 { 1096 uint32_t i; 1097 1098 for (i = 0; i < fcoe_nworkers; i++) { 1099 fcoe_worker_t *w = &fcoe_workers[i]; 1100 mutex_enter(&w->worker_lock); 1101 if (w->worker_flags & FCOE_WORKER_STARTED) { 1102 w->worker_flags |= FCOE_WORKER_TERMINATE; 1103 cv_signal(&w->worker_cv); 1104 } 1105 mutex_exit(&w->worker_lock); 1106 } 1107 1108 while (fcoe_nworkers_running != 0) { 1109 delay(drv_usectohz(10000)); 1110 } 1111 1112 ddi_taskq_destroy(fcoe_worker_taskq); 1113 kmem_free(fcoe_workers, sizeof (fcoe_worker_t) * fcoe_nworkers); 1114 fcoe_workers = NULL; 1115 return (FCOE_SUCCESS); 1116 } 1117 1118 static int 1119 fcoe_crc_verify(fcoe_frame_t *frm) 1120 { 1121 uint32_t crc; 1122 uint8_t *crc_array = FRM2FMI(frm)->fmi_fft->fft_crc; 1123 uint32_t crc_from_frame = ~(crc_array[0] | (crc_array[1] << 8) | 1124 (crc_array[2] << 16) | (crc_array[3] << 24)); 1125 CRC32(crc, frm->frm_fc_frame, frm->frm_fc_frame_size, -1U, crc32_table); 1126 return (crc == crc_from_frame ? FCOE_SUCCESS : FCOE_FAILURE); 1127 } 1128 1129 static void 1130 fcoe_worker_frame(void *arg) 1131 { 1132 fcoe_worker_t *w = (fcoe_worker_t *)arg; 1133 fcoe_i_frame_t *fmi; 1134 int ret; 1135 1136 atomic_add_32(&fcoe_nworkers_running, 1); 1137 mutex_enter(&w->worker_lock); 1138 w->worker_flags |= FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE; 1139 while ((w->worker_flags & FCOE_WORKER_TERMINATE) == 0) { 1140 /* 1141 * loop through the frames 1142 */ 1143 while (fmi = list_head(&w->worker_frm_list)) { 1144 list_remove(&w->worker_frm_list, fmi); 1145 mutex_exit(&w->worker_lock); 1146 /* 1147 * do the checksum 1148 */ 1149 ret = fcoe_crc_verify(fmi->fmi_frame); 1150 if (ret == FCOE_SUCCESS) { 1151 fmi->fmi_mac->fm_client.ect_rx_frame( 1152 fmi->fmi_frame); 1153 } else { 1154 fcoe_release_frame(fmi->fmi_frame); 1155 } 1156 mutex_enter(&w->worker_lock); 1157 w->worker_ntasks--; 1158 } 1159 w->worker_flags &= ~FCOE_WORKER_ACTIVE; 1160 cv_wait(&w->worker_cv, &w->worker_lock); 1161 w->worker_flags |= FCOE_WORKER_ACTIVE; 1162 } 1163 w->worker_flags &= ~(FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE); 1164 mutex_exit(&w->worker_lock); 1165 atomic_add_32(&fcoe_nworkers_running, -1); 1166 list_destroy(&w->worker_frm_list); 1167 } 1168 1169 void 1170 fcoe_post_frame(fcoe_frame_t *frm) 1171 { 1172 fcoe_worker_t *w; 1173 uint16_t oxid = FRM_OXID(frm); 1174 1175 w = &fcoe_workers[oxid % fcoe_nworkers_running]; 1176 mutex_enter(&w->worker_lock); 1177 list_insert_tail(&w->worker_frm_list, frm->frm_fcoe_private); 1178 w->worker_ntasks++; 1179 if ((w->worker_flags & FCOE_WORKER_ACTIVE) == 0) { 1180 cv_signal(&w->worker_cv); 1181 } 1182 mutex_exit(&w->worker_lock); 1183 } 1184 1185 /* 1186 * The max length of every LOG is 158 1187 */ 1188 void 1189 fcoe_trace(caddr_t ident, const char *fmt, ...) 1190 { 1191 va_list args; 1192 char tbuf[160]; 1193 int len; 1194 clock_t curclock; 1195 clock_t usec; 1196 1197 if (fcoe_trace_on == 0) { 1198 return; 1199 } 1200 1201 curclock = ddi_get_lbolt(); 1202 usec = (curclock - fcoe_trace_start) * usec_per_tick; 1203 len = snprintf(tbuf, 158, "%lu.%03lus 0t%lu %s ", (usec / 1204 (1000 * 1000)), ((usec % (1000 * 1000)) / 1000), 1205 curclock, (ident ? ident : "unknown")); 1206 va_start(args, fmt); 1207 len += vsnprintf(tbuf + len, 158 - len, fmt, args); 1208 va_end(args); 1209 1210 if (len > 158) { 1211 len = 158; 1212 } 1213 tbuf[len++] = '\n'; 1214 tbuf[len] = 0; 1215 1216 mutex_enter(&fcoe_trace_buf_lock); 1217 bcopy(tbuf, &fcoe_trace_buf[fcoe_trace_buf_curndx], len+1); 1218 fcoe_trace_buf_curndx += len; 1219 if (fcoe_trace_buf_curndx > (fcoe_trace_buf_size - 320)) { 1220 fcoe_trace_buf_curndx = 0; 1221 } 1222 mutex_exit(&fcoe_trace_buf_lock); 1223 } 1224 1225 /* 1226 * Check whether the pwwn or nwwn already exist or not 1227 * Return value: 1228 * 1: PWWN conflicted 1229 * -1: NWWN conflicted 1230 * 0: No conflict 1231 */ 1232 static int 1233 fcoe_cmp_wwn(fcoe_mac_t *checkedmac) 1234 { 1235 fcoe_mac_t *mac; 1236 uint8_t *nwwn, *pwwn, *cnwwn, *cpwwn; 1237 1238 cnwwn = checkedmac->fm_eport.eport_nodewwn; 1239 cpwwn = checkedmac->fm_eport.eport_portwwn; 1240 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 1241 1242 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 1243 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 1244 if (mac == checkedmac) { 1245 continue; 1246 } 1247 nwwn = mac->fm_eport.eport_nodewwn; 1248 pwwn = mac->fm_eport.eport_portwwn; 1249 1250 if (memcmp(nwwn, cnwwn, 8) == 0) { 1251 return (-1); 1252 } 1253 1254 if (memcmp(pwwn, cpwwn, 8) == 0) { 1255 return (1); 1256 } 1257 } 1258 return (0); 1259 } 1260 1261 static int 1262 fcoe_get_port_list(fcoe_port_instance_t *ports, int count) 1263 { 1264 fcoe_mac_t *mac = NULL; 1265 int i = 0; 1266 1267 ASSERT(ports != NULL); 1268 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 1269 1270 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 1271 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 1272 if (i < count) { 1273 bcopy(mac->fm_eport.eport_portwwn, 1274 ports[i].fpi_pwwn, 8); 1275 ports[i].fpi_mac_linkid = mac->fm_linkid; 1276 bcopy(mac->fm_current_addr, 1277 ports[i].fpi_mac_current_addr, ETHERADDRL); 1278 bcopy(mac->fm_primary_addr, 1279 ports[i].fpi_mac_factory_addr, ETHERADDRL); 1280 ports[i].fpi_port_type = 1281 EPORT_CLT_TYPE(&mac->fm_eport); 1282 ports[i].fpi_mtu_size = 1283 mac->fm_eport.eport_mtu; 1284 ports[i].fpi_mac_promisc = 1285 mac->fm_promisc_handle != NULL ? 1 : 0; 1286 } 1287 i++; 1288 } 1289 return (i); 1290 } 1291