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