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_name(uint8_t *name); 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 }; 196 197 #define FCOE_VERSION "20090311-1.00" 198 #define FCOE_NAME "FCoE Transport v" FCOE_VERSION 199 #define TASKQ_NAME_LEN 32 200 201 static struct modldrv modldrv = { 202 &mod_driverops, 203 FCOE_NAME, 204 &fcoe_ops, 205 }; 206 207 static struct modlinkage modlinkage = { 208 MODREV_1, &modldrv, NULL 209 }; 210 211 /* 212 * TRACE for all FCoE related modules 213 */ 214 static kmutex_t fcoe_trace_buf_lock; 215 static int fcoe_trace_buf_curndx = 0; 216 static int fcoe_trace_on = 1; 217 static caddr_t fcoe_trace_buf = NULL; 218 static clock_t fcoe_trace_start = 0; 219 static caddr_t ftb = NULL; 220 static int fcoe_trace_buf_size = (1 * 1024 * 1024); 221 222 /* 223 * Driver's global variables 224 */ 225 static void *fcoe_state = NULL; 226 fcoe_soft_state_t *fcoe_global_ss = NULL; 227 int fcoe_use_ext_log = 1; 228 229 static ddi_taskq_t *fcoe_worker_taskq; 230 static fcoe_worker_t *fcoe_workers; 231 static uint32_t fcoe_nworkers_running; 232 233 const char *fcoe_workers_num = "workers-number"; 234 volatile int fcoe_nworkers; 235 236 /* 237 * Common loadable module entry points _init, _fini, _info 238 */ 239 240 int 241 _init(void) 242 { 243 int ret; 244 245 ret = ddi_soft_state_init(&fcoe_state, sizeof (fcoe_soft_state_t), 0); 246 if (ret == 0) { 247 ret = mod_install(&modlinkage); 248 if (ret != 0) { 249 ddi_soft_state_fini(&fcoe_state); 250 } else { 251 fcoe_trace_start = ddi_get_lbolt(); 252 ftb = kmem_zalloc(fcoe_trace_buf_size, 253 KM_SLEEP); 254 fcoe_trace_buf = ftb; 255 mutex_init(&fcoe_trace_buf_lock, NULL, MUTEX_DRIVER, 0); 256 } 257 } 258 259 FCOE_LOG("fcoe", "exit _init with %x", ret); 260 261 return (ret); 262 } 263 264 int 265 _fini(void) 266 { 267 int ret; 268 269 ret = mod_remove(&modlinkage); 270 if (ret == 0) { 271 ddi_soft_state_fini(&fcoe_state); 272 } 273 274 FCOE_LOG("fcoe", "exit _fini with %x", ret); 275 if (ret == 0) { 276 kmem_free(fcoe_trace_buf, fcoe_trace_buf_size); 277 mutex_destroy(&fcoe_trace_buf_lock); 278 } 279 280 return (ret); 281 } 282 283 int 284 _info(struct modinfo *modinfop) 285 { 286 return (mod_info(&modlinkage, modinfop)); 287 } 288 289 /* 290 * Autoconfiguration entry points: attach, detach, getinfo 291 */ 292 293 static int 294 fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 295 { 296 int ret = DDI_FAILURE; 297 int fcoe_ret; 298 int instance; 299 fcoe_soft_state_t *ss; 300 301 instance = ddi_get_instance(dip); 302 switch (cmd) { 303 case DDI_ATTACH: 304 ret = ddi_soft_state_zalloc(fcoe_state, instance); 305 if (ret == DDI_FAILURE) { 306 FCOE_LOG(0, "soft_state_zalloc-%x/%x", ret, instance); 307 return (ret); 308 } 309 310 ss = ddi_get_soft_state(fcoe_state, instance); 311 ss->ss_dip = dip; 312 313 ASSERT(fcoe_global_ss == NULL); 314 fcoe_global_ss = ss; 315 fcoe_ret = fcoe_attach_init(ss); 316 if (fcoe_ret == FCOE_SUCCESS) { 317 ret = DDI_SUCCESS; 318 } 319 320 FCOE_LOG("fcoe", "fcoe_attach_init end with-%x", fcoe_ret); 321 break; 322 323 case DDI_RESUME: 324 ret = DDI_SUCCESS; 325 break; 326 327 default: 328 FCOE_LOG("fcoe", "unsupported attach cmd-%x", cmd); 329 break; 330 } 331 332 return (ret); 333 } 334 335 static int 336 fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 337 { 338 int ret = DDI_FAILURE; 339 int fcoe_ret; 340 int instance; 341 fcoe_soft_state_t *ss; 342 343 instance = ddi_get_instance(dip); 344 ss = ddi_get_soft_state(fcoe_state, instance); 345 if (ss == NULL) { 346 return (ret); 347 } 348 349 ASSERT(fcoe_global_ss != NULL); 350 ASSERT(dip == fcoe_global_ss->ss_dip); 351 switch (cmd) { 352 case DDI_DETACH: 353 fcoe_ret = fcoe_detach_uninit(ss); 354 if (fcoe_ret == FCOE_SUCCESS) { 355 ret = DDI_SUCCESS; 356 fcoe_global_ss = NULL; 357 } 358 359 break; 360 361 case DDI_SUSPEND: 362 ret = DDI_SUCCESS; 363 break; 364 365 default: 366 FCOE_LOG(0, "unsupported detach cmd-%x", cmd); 367 break; 368 } 369 370 return (ret); 371 } 372 373 /* 374 * FCA driver's intercepted bus control operations. 375 */ 376 static int 377 fcoe_bus_ctl(dev_info_t *fcoe_dip, dev_info_t *rip, 378 ddi_ctl_enum_t op, void *clientarg, void *result) 379 { 380 int ret; 381 switch (op) { 382 case DDI_CTLOPS_REPORTDEV: 383 case DDI_CTLOPS_IOMIN: 384 ret = DDI_SUCCESS; 385 break; 386 387 case DDI_CTLOPS_INITCHILD: 388 ret = fcoe_initchild(fcoe_dip, (dev_info_t *)clientarg); 389 break; 390 391 case DDI_CTLOPS_UNINITCHILD: 392 ret = fcoe_uninitchild(fcoe_dip, (dev_info_t *)clientarg); 393 break; 394 395 default: 396 ret = ddi_ctlops(fcoe_dip, rip, op, clientarg, result); 397 break; 398 } 399 400 return (ret); 401 } 402 403 /* 404 * We need specify the dev address for client driver's instance, or we 405 * can't online client driver's instance. 406 */ 407 /* ARGSUSED */ 408 static int 409 fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip) 410 { 411 char name[32]; 412 static int inicounter = 0; 413 static int tgtcounter = 0; 414 int *counter; 415 416 if (strcmp(ddi_driver_name(client_dip), FCOET_DRIVER_NAME) == 0) { 417 counter = &tgtcounter; 418 tgtcounter++; 419 } else { 420 counter = &inicounter; 421 inicounter++; 422 } 423 424 bzero(name, 32); 425 (void) sprintf((char *)name, "%x,0", *counter); 426 ddi_set_name_addr(client_dip, name); 427 428 return (DDI_SUCCESS); 429 } 430 431 /* ARGSUSED */ 432 static int 433 fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip) 434 { 435 ddi_set_name_addr(client_dip, NULL); 436 return (DDI_SUCCESS); 437 } 438 439 /* 440 * Device access entry points 441 */ 442 static int 443 fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp) 444 { 445 int instance; 446 fcoe_soft_state_t *ss; 447 448 if (otype != OTYP_CHR) { 449 return (EINVAL); 450 } 451 452 /* 453 * Since this is for debugging only, only allow root to issue ioctl now 454 */ 455 if (drv_priv(credp) != 0) { 456 return (EPERM); 457 } 458 459 instance = (int)getminor(*devp); 460 ss = ddi_get_soft_state(fcoe_state, instance); 461 if (ss == NULL) { 462 return (ENXIO); 463 } 464 465 mutex_enter(&ss->ss_ioctl_mutex); 466 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) { 467 /* 468 * It is already open for exclusive access. 469 * So shut the door on this caller. 470 */ 471 mutex_exit(&ss->ss_ioctl_mutex); 472 return (EBUSY); 473 } 474 475 if (flag & FEXCL) { 476 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) { 477 /* 478 * Exclusive operation not possible 479 * as it is already opened 480 */ 481 mutex_exit(&ss->ss_ioctl_mutex); 482 return (EBUSY); 483 } 484 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL; 485 } 486 487 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_OPEN; 488 mutex_exit(&ss->ss_ioctl_mutex); 489 490 return (0); 491 } 492 493 /* ARGSUSED */ 494 static int 495 fcoe_close(dev_t dev, int flag, int otype, cred_t *credp) 496 { 497 int instance; 498 fcoe_soft_state_t *ss; 499 500 if (otype != OTYP_CHR) { 501 return (EINVAL); 502 } 503 504 instance = (int)getminor(dev); 505 ss = ddi_get_soft_state(fcoe_state, instance); 506 if (ss == NULL) { 507 return (ENXIO); 508 } 509 510 mutex_enter(&ss->ss_ioctl_mutex); 511 if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) { 512 mutex_exit(&ss->ss_ioctl_mutex); 513 return (ENODEV); 514 } 515 516 ss->ss_ioctl_flags &= ~FCOE_IOCTL_FLAG_MASK; 517 mutex_exit(&ss->ss_ioctl_mutex); 518 519 return (0); 520 } 521 522 /* ARGSUSED */ 523 static int 524 fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 525 cred_t *credp, int *rval) 526 { 527 fcoe_soft_state_t *ss; 528 int ret = 0; 529 530 if (drv_priv(credp) != 0) { 531 return (EPERM); 532 } 533 534 ss = ddi_get_soft_state(fcoe_state, (int32_t)getminor(dev)); 535 if (ss == NULL) { 536 return (ENXIO); 537 } 538 539 mutex_enter(&ss->ss_ioctl_mutex); 540 if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) { 541 mutex_exit(&ss->ss_ioctl_mutex); 542 return (ENXIO); 543 } 544 mutex_exit(&ss->ss_ioctl_mutex); 545 546 switch (cmd) { 547 case FCOEIO_CMD: 548 ret = fcoe_iocmd(ss, data, mode); 549 break; 550 default: 551 FCOE_LOG(0, "fcoe_ioctl: ioctl-0x%02X", cmd); 552 ret = ENOTTY; 553 break; 554 } 555 556 return (ret); 557 } 558 559 static int 560 fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio, 561 void **ibuf, void **abuf, void **obuf) 562 { 563 int ret = 0; 564 565 *ibuf = NULL; 566 *abuf = NULL; 567 *obuf = NULL; 568 *fcoeio = kmem_zalloc(sizeof (fcoeio_t), KM_SLEEP); 569 if (ddi_copyin((void *)data, *fcoeio, sizeof (fcoeio_t), mode) != 0) { 570 ret = EFAULT; 571 goto copyin_iocdata_fail; 572 } 573 574 if ((*fcoeio)->fcoeio_ilen > FCOEIO_MAX_BUF_LEN || 575 (*fcoeio)->fcoeio_alen > FCOEIO_MAX_BUF_LEN || 576 (*fcoeio)->fcoeio_olen > FCOEIO_MAX_BUF_LEN) { 577 ret = EFAULT; 578 goto copyin_iocdata_fail; 579 } 580 581 if ((*fcoeio)->fcoeio_ilen) { 582 *ibuf = kmem_zalloc((*fcoeio)->fcoeio_ilen, KM_SLEEP); 583 if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_ibuf, 584 *ibuf, (*fcoeio)->fcoeio_ilen, mode) != 0) { 585 ret = EFAULT; 586 goto copyin_iocdata_fail; 587 } 588 } 589 590 if ((*fcoeio)->fcoeio_alen) { 591 *abuf = kmem_zalloc((*fcoeio)->fcoeio_alen, KM_SLEEP); 592 if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_abuf, 593 *abuf, (*fcoeio)->fcoeio_alen, mode) != 0) { 594 ret = EFAULT; 595 goto copyin_iocdata_fail; 596 } 597 } 598 599 if ((*fcoeio)->fcoeio_olen) { 600 *obuf = kmem_zalloc((*fcoeio)->fcoeio_olen, KM_SLEEP); 601 } 602 return (ret); 603 604 copyin_iocdata_fail: 605 if (*abuf) { 606 kmem_free(*abuf, (*fcoeio)->fcoeio_alen); 607 *abuf = NULL; 608 } 609 610 if (*ibuf) { 611 kmem_free(*ibuf, (*fcoeio)->fcoeio_ilen); 612 *ibuf = NULL; 613 } 614 615 kmem_free(*fcoeio, sizeof (fcoeio_t)); 616 return (ret); 617 } 618 619 static int 620 fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, void *obuf) 621 { 622 if (fcoeio->fcoeio_olen) { 623 if (ddi_copyout(obuf, 624 (void *)(unsigned long)fcoeio->fcoeio_obuf, 625 fcoeio->fcoeio_olen, mode) != 0) { 626 return (EFAULT); 627 } 628 } 629 630 if (ddi_copyout(fcoeio, (void *)data, sizeof (fcoeio_t), mode) != 0) { 631 return (EFAULT); 632 } 633 return (0); 634 } 635 636 static int 637 fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode) 638 { 639 int ret; 640 fcoe_mac_t *fcoe_mac; 641 void *ibuf = NULL; 642 void *obuf = NULL; 643 void *abuf = NULL; 644 fcoeio_t *fcoeio; 645 646 ret = fcoe_copyin_iocdata(data, mode, &fcoeio, &ibuf, &abuf, &obuf); 647 if (ret != 0) { 648 goto fcoeiocmd_release_buf; 649 } 650 651 /* 652 * If an exclusive open was demanded during open, ensure that 653 * only one thread can execute an ioctl at a time 654 */ 655 mutex_enter(&ss->ss_ioctl_mutex); 656 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) { 657 if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL_BUSY) { 658 mutex_exit(&ss->ss_ioctl_mutex); 659 fcoeio->fcoeio_status = FCOEIOE_BUSY; 660 ret = EBUSY; 661 goto fcoeiocmd_release_buf; 662 } 663 ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL_BUSY; 664 } 665 mutex_exit(&ss->ss_ioctl_mutex); 666 667 fcoeio->fcoeio_status = 0; 668 669 switch (fcoeio->fcoeio_cmd) { 670 case FCOEIO_CREATE_FCOE_PORT: { 671 fcoeio_create_port_param_t *param = 672 (fcoeio_create_port_param_t *)ibuf; 673 int cmpwwn = 0; 674 fcoe_port_t *eport; 675 676 if (fcoeio->fcoeio_ilen != 677 sizeof (fcoeio_create_port_param_t) || 678 fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) { 679 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 680 ret = EINVAL; 681 break; 682 } 683 684 mutex_enter(&ss->ss_ioctl_mutex); 685 fcoe_mac = fcoe_create_mac_by_name(param->fcp_mac_name); 686 if (fcoe_mac == NULL) { 687 mutex_exit(&ss->ss_ioctl_mutex); 688 fcoeio->fcoeio_status = FCOEIOE_CREATE_MAC; 689 ret = EIO; 690 break; 691 } 692 693 if (fcoe_mac->fm_flags & FCOE_MAC_FLAG_ENABLED) { 694 mutex_exit(&ss->ss_ioctl_mutex); 695 fcoeio->fcoeio_status = FCOEIOE_ALREADY; 696 ret = EALREADY; 697 break; 698 } else { 699 ret = fcoe_open_mac(fcoe_mac, param->fcp_force_promisc, 700 &fcoeio->fcoeio_status); 701 if (ret != 0) { 702 fcoe_destroy_mac(fcoe_mac); 703 mutex_exit(&ss->ss_ioctl_mutex); 704 if (fcoeio->fcoeio_status == 0) { 705 fcoeio->fcoeio_status = 706 FCOEIOE_OPEN_MAC; 707 } 708 ret = EIO; 709 break; 710 } else { 711 fcoe_mac->fm_flags |= FCOE_MAC_FLAG_ENABLED; 712 } 713 } 714 715 /* 716 * Provide PWWN and NWWN based on mac address 717 */ 718 eport = &fcoe_mac->fm_eport; 719 if (!param->fcp_pwwn_provided) { 720 fcoe_init_wwn_from_mac(eport->eport_portwwn, 721 fcoe_mac->fm_current_addr, 1, 0); 722 } else { 723 (void) memcpy(eport->eport_portwwn, param->fcp_pwwn, 8); 724 } 725 726 if (!param->fcp_nwwn_provided) { 727 fcoe_init_wwn_from_mac(eport->eport_nodewwn, 728 fcoe_mac->fm_current_addr, 0, 0); 729 } else { 730 (void) memcpy(eport->eport_nodewwn, param->fcp_nwwn, 8); 731 } 732 733 cmpwwn = fcoe_cmp_wwn(fcoe_mac); 734 735 if (cmpwwn != 0) { 736 if (cmpwwn == 1) { 737 fcoeio->fcoeio_status = FCOEIOE_PWWN_CONFLICTED; 738 } else if (cmpwwn == -1) { 739 fcoeio->fcoeio_status = FCOEIOE_NWWN_CONFLICTED; 740 } 741 (void) fcoe_close_mac(fcoe_mac); 742 fcoe_destroy_mac(fcoe_mac); 743 mutex_exit(&ss->ss_ioctl_mutex); 744 ret = ENOTUNIQ; 745 break; 746 } 747 748 if (ret == 0) { 749 ret = fcoe_create_port(ss->ss_dip, 750 fcoe_mac, 751 (param->fcp_port_type == FCOE_CLIENT_TARGET)); 752 if (ret != 0) { 753 (void) fcoe_close_mac(fcoe_mac); 754 fcoe_destroy_mac(fcoe_mac); 755 fcoeio->fcoeio_status = FCOEIOE_CREATE_PORT; 756 ret = EIO; 757 } 758 } 759 mutex_exit(&ss->ss_ioctl_mutex); 760 761 break; 762 } 763 764 case FCOEIO_DELETE_FCOE_PORT: { 765 uint8_t *mac_name = (uint8_t *)ibuf; 766 767 if (fcoeio->fcoeio_ilen > FCOE_MAX_MAC_NAME_LEN || 768 fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) { 769 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 770 ret = EINVAL; 771 break; 772 } 773 774 mutex_enter(&ss->ss_ioctl_mutex); 775 ret = fcoe_delete_port(ss->ss_dip, fcoeio, mac_name); 776 if (ret != 0) { 777 FCOE_LOG("fcoe", 778 "fcoe_delete_port failed: %d", ret); 779 } 780 mutex_exit(&ss->ss_ioctl_mutex); 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 returned %d, fcoeio_status = %d", 817 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_name(uint8_t *name) 939 { 940 fcoe_mac_t *mac = NULL; 941 942 ASSERT(mutex_owned(&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 (strcmp((char *)name, mac->fm_link_name)) { 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_name(uint8_t *name) 971 { 972 fcoe_mac_t *mac = NULL; 973 ASSERT(mutex_owned(&fcoe_global_ss->ss_ioctl_mutex)); 974 975 mac = fcoe_lookup_mac_by_name(name); 976 if (mac != NULL) { 977 FCOE_LOG("fcoe", "fcoe_create_mac_by_name found one mac %s", 978 name); 979 return (mac); 980 } 981 982 mac = kmem_zalloc(sizeof (fcoe_mac_t), KM_SLEEP); 983 bcopy(name, mac->fm_link_name, strlen((char *)name) + 1); 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_name created one mac %s", name); 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_owned(&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_owned(&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 bcopy(mac->fm_link_name, 1271 ports[i].fpi_mac_link_name, MAXLINKNAMELEN); 1272 bcopy(mac->fm_current_addr, 1273 ports[i].fpi_mac_current_addr, ETHERADDRL); 1274 bcopy(mac->fm_primary_addr, 1275 ports[i].fpi_mac_factory_addr, ETHERADDRL); 1276 ports[i].fpi_port_type = 1277 EPORT_CLT_TYPE(&mac->fm_eport); 1278 ports[i].fpi_mtu_size = 1279 mac->fm_eport.eport_mtu; 1280 ports[i].fpi_mac_promisc = 1281 mac->fm_promisc_handle != NULL ? 1 : 0; 1282 } 1283 i++; 1284 } 1285 return (i); 1286 } 1287