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 }; 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_id(param->fcp_mac_linkid); 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 fcoeio_delete_port_param_t *del_port_param = 766 (fcoeio_delete_port_param_t *)ibuf; 767 768 if (fcoeio->fcoeio_ilen < sizeof (fcoeio_delete_port_param_t) || 769 fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) { 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); 778 if (ret != 0) { 779 FCOE_LOG("fcoe", 780 "fcoe_delete_port failed: %d", ret); 781 } 782 mutex_exit(&ss->ss_ioctl_mutex); 783 break; 784 } 785 786 case FCOEIO_GET_FCOE_PORT_LIST: { 787 fcoe_port_list_t *list = (fcoe_port_list_t *)obuf; 788 int count; 789 790 if (fcoeio->fcoeio_xfer != FCOEIO_XFER_READ || 791 fcoeio->fcoeio_olen < sizeof (fcoe_port_list_t)) { 792 fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 793 ret = EINVAL; 794 break; 795 } 796 mutex_enter(&ss->ss_ioctl_mutex); 797 798 list->numPorts = 1 + (fcoeio->fcoeio_olen - 799 sizeof (fcoe_port_list_t))/sizeof (fcoe_port_instance_t); 800 801 count = fcoe_get_port_list(list->ports, list->numPorts); 802 803 if (count > list->numPorts) { 804 fcoeio->fcoeio_status = FCOEIOE_MORE_DATA; 805 ret = ENOSPC; 806 } 807 list->numPorts = count; 808 mutex_exit(&ss->ss_ioctl_mutex); 809 810 break; 811 812 } 813 814 default: 815 return (ENOTTY); 816 } 817 818 FCOE_LOG("fcoe", "fcoe_ioctl returned %d, fcoeio_status = %d", 819 ret, fcoeio->fcoeio_status); 820 821 fcoeiocmd_release_buf: 822 if (ret == 0) { 823 ret = fcoe_copyout_iocdata(data, mode, fcoeio, obuf); 824 } else if (fcoeio->fcoeio_status) { 825 (void) fcoe_copyout_iocdata(data, mode, fcoeio, obuf); 826 } 827 828 if (obuf != NULL) { 829 kmem_free(obuf, fcoeio->fcoeio_olen); 830 obuf = NULL; 831 } 832 if (abuf != NULL) { 833 kmem_free(abuf, fcoeio->fcoeio_alen); 834 abuf = NULL; 835 } 836 837 if (ibuf != NULL) { 838 kmem_free(ibuf, fcoeio->fcoeio_ilen); 839 ibuf = NULL; 840 } 841 kmem_free(fcoeio, sizeof (fcoeio_t)); 842 843 return (ret); 844 } 845 846 /* 847 * Finish final initialization 848 */ 849 static int 850 fcoe_attach_init(fcoe_soft_state_t *ss) 851 { 852 char taskq_name[TASKQ_NAME_LEN]; 853 854 if (ddi_create_minor_node(ss->ss_dip, "admin", S_IFCHR, 855 ddi_get_instance(ss->ss_dip), DDI_PSEUDO, 0) != DDI_SUCCESS) { 856 FCOE_LOG("FCOE", "ddi_create_minor_node failed"); 857 return (FCOE_FAILURE); 858 } 859 860 /* 861 * watchdog responsible for release frame and dispatch events 862 */ 863 (void) snprintf(taskq_name, sizeof (taskq_name), "fcoe_mac"); 864 taskq_name[TASKQ_NAME_LEN - 1] = 0; 865 if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL, 866 taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 867 return (FCOE_FAILURE); 868 } 869 870 ss->ss_ioctl_flags = 0; 871 mutex_init(&ss->ss_ioctl_mutex, NULL, MUTEX_DRIVER, NULL); 872 list_create(&ss->ss_mac_list, sizeof (fcoe_mac_t), 873 offsetof(fcoe_mac_t, fm_ss_node)); 874 list_create(&ss->ss_pfrm_list, sizeof (fcoe_i_frame_t), 875 offsetof(fcoe_i_frame_t, fmi_pending_node)); 876 877 mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0); 878 cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL); 879 ss->ss_flags &= ~SS_FLAG_TERMINATE_WATCHDOG; 880 (void) ddi_taskq_dispatch(ss->ss_watchdog_taskq, 881 fcoe_watchdog, ss, DDI_SLEEP); 882 while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) { 883 delay(10); 884 } 885 fcoe_nworkers = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip, 886 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (char *)fcoe_workers_num, 4); 887 if (fcoe_nworkers < 1) { 888 fcoe_nworkers = 4; 889 } 890 fcoe_worker_init(); 891 892 ddi_report_dev(ss->ss_dip); 893 return (FCOE_SUCCESS); 894 } 895 896 /* 897 * Finish final uninitialization 898 */ 899 static int 900 fcoe_detach_uninit(fcoe_soft_state_t *ss) 901 { 902 int ret; 903 if (!list_is_empty(&ss->ss_mac_list)) { 904 FCOE_LOG("fcoe", "ss_mac_list is not empty when detach"); 905 return (FCOE_FAILURE); 906 } 907 908 if ((ret = fcoe_worker_fini()) != FCOE_SUCCESS) { 909 return (ret); 910 } 911 912 /* 913 * Stop watchdog 914 */ 915 if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 916 mutex_enter(&ss->ss_watch_mutex); 917 ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG; 918 cv_broadcast(&ss->ss_watch_cv); 919 mutex_exit(&ss->ss_watch_mutex); 920 while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 921 delay(10); 922 } 923 } 924 925 ddi_taskq_destroy(ss->ss_watchdog_taskq); 926 mutex_destroy(&ss->ss_watch_mutex); 927 cv_destroy(&ss->ss_watch_cv); 928 929 ddi_remove_minor_node(ss->ss_dip, NULL); 930 mutex_destroy(&ss->ss_ioctl_mutex); 931 list_destroy(&ss->ss_mac_list); 932 933 return (FCOE_SUCCESS); 934 } 935 936 /* 937 * Return mac instance if it exist, or else return NULL. 938 */ 939 fcoe_mac_t * 940 fcoe_lookup_mac_by_id(datalink_id_t linkid) 941 { 942 fcoe_mac_t *mac = NULL; 943 944 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 945 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 946 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 947 if (linkid != mac->fm_linkid) { 948 continue; 949 } 950 return (mac); 951 } 952 return (NULL); 953 } 954 955 /* 956 * port wwn will start with 20:..., node wwn will start with 10:... 957 */ 958 static void 959 fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, int is_pwwn, uint8_t idx) 960 { 961 ASSERT(wwn != NULL); 962 ASSERT(mac != NULL); 963 wwn[0] = (is_pwwn + 1) << 4; 964 wwn[1] = idx; 965 bcopy(mac, wwn + 2, ETHERADDRL); 966 } 967 968 /* 969 * Return fcoe_mac if it exists, otherwise create a new one 970 */ 971 static fcoe_mac_t * 972 fcoe_create_mac_by_id(datalink_id_t linkid) 973 { 974 fcoe_mac_t *mac = NULL; 975 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 976 977 mac = fcoe_lookup_mac_by_id(linkid); 978 if (mac != NULL) { 979 FCOE_LOG("fcoe", "fcoe_create_mac_by_id found one mac %d", 980 linkid); 981 return (mac); 982 } 983 984 mac = kmem_zalloc(sizeof (fcoe_mac_t), KM_SLEEP); 985 mac->fm_linkid = linkid; 986 mac->fm_flags = 0; 987 mac->fm_ss = fcoe_global_ss; 988 list_insert_tail(&mac->fm_ss->ss_mac_list, mac); 989 FCOE_LOG("fcoe", "fcoe_create_mac_by_id created one mac %d", linkid); 990 return (mac); 991 } 992 993 void 994 fcoe_destroy_mac(fcoe_mac_t *mac) 995 { 996 ASSERT(mac != NULL); 997 list_remove(&mac->fm_ss->ss_mac_list, mac); 998 kmem_free(mac, sizeof (fcoe_mac_t)); 999 } 1000 1001 /* 1002 * raw frame layout: 1003 * ethernet header + vlan header (optional) + FCoE header + 1004 * FC frame + FCoE tailer 1005 */ 1006 /* ARGSUSED */ 1007 mblk_t * 1008 fcoe_get_mblk(fcoe_mac_t *mac, uint32_t raw_frame_size) 1009 { 1010 mblk_t *mp; 1011 int err; 1012 1013 /* 1014 * FCFH_SIZE + PADDING_SIZE 1015 */ 1016 ASSERT(raw_frame_size >= 60); 1017 while ((mp = allocb((size_t)raw_frame_size, 0)) == NULL) { 1018 if ((err = strwaitbuf((size_t)raw_frame_size, BPRI_LO)) != 0) { 1019 FCOE_LOG("fcoe_get_mblk", "strwaitbuf return %d", err); 1020 return (NULL); 1021 } 1022 } 1023 mp->b_wptr = mp->b_rptr + raw_frame_size; 1024 1025 /* 1026 * We should always zero FC frame header 1027 */ 1028 bzero(mp->b_rptr + PADDING_HEADER_SIZE, 1029 sizeof (fcoe_fc_frame_header_t)); 1030 return (mp); 1031 } 1032 1033 static void 1034 fcoe_watchdog(void *arg) 1035 { 1036 fcoe_soft_state_t *ss = (fcoe_soft_state_t *)arg; 1037 fcoe_i_frame_t *fmi; 1038 fcoe_mac_t *mac = NULL; 1039 1040 FCOE_LOG("fcoe", "fcoe_soft_state is %p", ss); 1041 1042 mutex_enter(&ss->ss_watch_mutex); 1043 ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING; 1044 while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) { 1045 while (fmi = (fcoe_i_frame_t *)list_head(&ss->ss_pfrm_list)) { 1046 list_remove(&ss->ss_pfrm_list, fmi); 1047 mutex_exit(&ss->ss_watch_mutex); 1048 1049 mac = EPORT2MAC(fmi->fmi_frame->frm_eport); 1050 mac->fm_client.ect_release_sol_frame(fmi->fmi_frame); 1051 1052 mutex_enter(&ss->ss_watch_mutex); 1053 mac->fm_frm_cnt--; 1054 } 1055 1056 ss->ss_flags |= SS_FLAG_DOG_WAITING; 1057 (void) cv_wait(&ss->ss_watch_cv, &ss->ss_watch_mutex); 1058 ss->ss_flags &= ~SS_FLAG_DOG_WAITING; 1059 } 1060 1061 ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING; 1062 mutex_exit(&ss->ss_watch_mutex); 1063 } 1064 1065 static void 1066 fcoe_worker_init() 1067 { 1068 uint32_t i; 1069 1070 fcoe_nworkers_running = 0; 1071 fcoe_worker_taskq = ddi_taskq_create(0, "FCOE_WORKER_TASKQ", 1072 fcoe_nworkers, TASKQ_DEFAULTPRI, 0); 1073 fcoe_workers = (fcoe_worker_t *)kmem_zalloc(sizeof (fcoe_worker_t) * 1074 fcoe_nworkers, KM_SLEEP); 1075 for (i = 0; i < fcoe_nworkers; i++) { 1076 fcoe_worker_t *w = &fcoe_workers[i]; 1077 mutex_init(&w->worker_lock, NULL, MUTEX_DRIVER, NULL); 1078 cv_init(&w->worker_cv, NULL, CV_DRIVER, NULL); 1079 w->worker_flags &= ~FCOE_WORKER_TERMINATE; 1080 list_create(&w->worker_frm_list, sizeof (fcoe_i_frame_t), 1081 offsetof(fcoe_i_frame_t, fmi_pending_node)); 1082 (void) ddi_taskq_dispatch(fcoe_worker_taskq, fcoe_worker_frame, 1083 w, DDI_SLEEP); 1084 } 1085 while (fcoe_nworkers_running != fcoe_nworkers) { 1086 delay(10); 1087 } 1088 } 1089 1090 static int 1091 fcoe_worker_fini() 1092 { 1093 uint32_t i; 1094 1095 for (i = 0; i < fcoe_nworkers; i++) { 1096 fcoe_worker_t *w = &fcoe_workers[i]; 1097 mutex_enter(&w->worker_lock); 1098 if (w->worker_flags & FCOE_WORKER_STARTED) { 1099 w->worker_flags |= FCOE_WORKER_TERMINATE; 1100 cv_signal(&w->worker_cv); 1101 } 1102 mutex_exit(&w->worker_lock); 1103 } 1104 1105 while (fcoe_nworkers_running != 0) { 1106 delay(drv_usectohz(10000)); 1107 } 1108 1109 ddi_taskq_destroy(fcoe_worker_taskq); 1110 kmem_free(fcoe_workers, sizeof (fcoe_worker_t) * fcoe_nworkers); 1111 fcoe_workers = NULL; 1112 return (FCOE_SUCCESS); 1113 } 1114 1115 static int 1116 fcoe_crc_verify(fcoe_frame_t *frm) 1117 { 1118 uint32_t crc; 1119 uint8_t *crc_array = FRM2FMI(frm)->fmi_fft->fft_crc; 1120 uint32_t crc_from_frame = ~(crc_array[0] | (crc_array[1] << 8) | 1121 (crc_array[2] << 16) | (crc_array[3] << 24)); 1122 CRC32(crc, frm->frm_fc_frame, frm->frm_fc_frame_size, -1U, crc32_table); 1123 return (crc == crc_from_frame ? FCOE_SUCCESS : FCOE_FAILURE); 1124 } 1125 1126 static void 1127 fcoe_worker_frame(void *arg) 1128 { 1129 fcoe_worker_t *w = (fcoe_worker_t *)arg; 1130 fcoe_i_frame_t *fmi; 1131 int ret; 1132 1133 atomic_add_32(&fcoe_nworkers_running, 1); 1134 mutex_enter(&w->worker_lock); 1135 w->worker_flags |= FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE; 1136 while ((w->worker_flags & FCOE_WORKER_TERMINATE) == 0) { 1137 /* 1138 * loop through the frames 1139 */ 1140 while (fmi = list_head(&w->worker_frm_list)) { 1141 list_remove(&w->worker_frm_list, fmi); 1142 mutex_exit(&w->worker_lock); 1143 /* 1144 * do the checksum 1145 */ 1146 ret = fcoe_crc_verify(fmi->fmi_frame); 1147 if (ret == FCOE_SUCCESS) { 1148 fmi->fmi_mac->fm_client.ect_rx_frame( 1149 fmi->fmi_frame); 1150 } else { 1151 fcoe_release_frame(fmi->fmi_frame); 1152 } 1153 mutex_enter(&w->worker_lock); 1154 w->worker_ntasks--; 1155 } 1156 w->worker_flags &= ~FCOE_WORKER_ACTIVE; 1157 cv_wait(&w->worker_cv, &w->worker_lock); 1158 w->worker_flags |= FCOE_WORKER_ACTIVE; 1159 } 1160 w->worker_flags &= ~(FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE); 1161 mutex_exit(&w->worker_lock); 1162 atomic_add_32(&fcoe_nworkers_running, -1); 1163 list_destroy(&w->worker_frm_list); 1164 } 1165 1166 void 1167 fcoe_post_frame(fcoe_frame_t *frm) 1168 { 1169 fcoe_worker_t *w; 1170 uint16_t oxid = FRM_OXID(frm); 1171 1172 w = &fcoe_workers[oxid % fcoe_nworkers_running]; 1173 mutex_enter(&w->worker_lock); 1174 list_insert_tail(&w->worker_frm_list, frm->frm_fcoe_private); 1175 w->worker_ntasks++; 1176 if ((w->worker_flags & FCOE_WORKER_ACTIVE) == 0) { 1177 cv_signal(&w->worker_cv); 1178 } 1179 mutex_exit(&w->worker_lock); 1180 } 1181 1182 /* 1183 * The max length of every LOG is 158 1184 */ 1185 void 1186 fcoe_trace(caddr_t ident, const char *fmt, ...) 1187 { 1188 va_list args; 1189 char tbuf[160]; 1190 int len; 1191 clock_t curclock; 1192 clock_t usec; 1193 1194 if (fcoe_trace_on == 0) { 1195 return; 1196 } 1197 1198 curclock = ddi_get_lbolt(); 1199 usec = (curclock - fcoe_trace_start) * usec_per_tick; 1200 len = snprintf(tbuf, 158, "%lu.%03lus 0t%lu %s ", (usec / 1201 (1000 * 1000)), ((usec % (1000 * 1000)) / 1000), 1202 curclock, (ident ? ident : "unknown")); 1203 va_start(args, fmt); 1204 len += vsnprintf(tbuf + len, 158 - len, fmt, args); 1205 va_end(args); 1206 1207 if (len > 158) { 1208 len = 158; 1209 } 1210 tbuf[len++] = '\n'; 1211 tbuf[len] = 0; 1212 1213 mutex_enter(&fcoe_trace_buf_lock); 1214 bcopy(tbuf, &fcoe_trace_buf[fcoe_trace_buf_curndx], len+1); 1215 fcoe_trace_buf_curndx += len; 1216 if (fcoe_trace_buf_curndx > (fcoe_trace_buf_size - 320)) { 1217 fcoe_trace_buf_curndx = 0; 1218 } 1219 mutex_exit(&fcoe_trace_buf_lock); 1220 } 1221 1222 /* 1223 * Check whether the pwwn or nwwn already exist or not 1224 * Return value: 1225 * 1: PWWN conflicted 1226 * -1: NWWN conflicted 1227 * 0: No conflict 1228 */ 1229 static int 1230 fcoe_cmp_wwn(fcoe_mac_t *checkedmac) 1231 { 1232 fcoe_mac_t *mac; 1233 uint8_t *nwwn, *pwwn, *cnwwn, *cpwwn; 1234 1235 cnwwn = checkedmac->fm_eport.eport_nodewwn; 1236 cpwwn = checkedmac->fm_eport.eport_portwwn; 1237 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 1238 1239 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 1240 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 1241 if (mac == checkedmac) { 1242 continue; 1243 } 1244 nwwn = mac->fm_eport.eport_nodewwn; 1245 pwwn = mac->fm_eport.eport_portwwn; 1246 1247 if (memcmp(nwwn, cnwwn, 8) == 0) { 1248 return (-1); 1249 } 1250 1251 if (memcmp(pwwn, cpwwn, 8) == 0) { 1252 return (1); 1253 } 1254 } 1255 return (0); 1256 } 1257 1258 static int 1259 fcoe_get_port_list(fcoe_port_instance_t *ports, int count) 1260 { 1261 fcoe_mac_t *mac = NULL; 1262 int i = 0; 1263 1264 ASSERT(ports != NULL); 1265 ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 1266 1267 for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 1268 mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 1269 if (i < count) { 1270 bcopy(mac->fm_eport.eport_portwwn, 1271 ports[i].fpi_pwwn, 8); 1272 ports[i].fpi_mac_linkid = mac->fm_linkid; 1273 bcopy(mac->fm_current_addr, 1274 ports[i].fpi_mac_current_addr, ETHERADDRL); 1275 bcopy(mac->fm_primary_addr, 1276 ports[i].fpi_mac_factory_addr, ETHERADDRL); 1277 ports[i].fpi_port_type = 1278 EPORT_CLT_TYPE(&mac->fm_eport); 1279 ports[i].fpi_mtu_size = 1280 mac->fm_eport.eport_mtu; 1281 ports[i].fpi_mac_promisc = 1282 mac->fm_promisc_handle != NULL ? 1 : 0; 1283 } 1284 i++; 1285 } 1286 return (i); 1287 } 1288