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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * The following notice accompanied the original version of this file: 27 * 28 * BSD LICENSE 29 * 30 * Copyright(c) 2007 Intel Corporation. All rights reserved. 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 37 * * Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * * Redistributions in binary form must reproduce the above copyright 40 * notice, this list of conditions and the following disclaimer in 41 * the documentation and/or other materials provided with the 42 * distribution. 43 * * Neither the name of Intel Corporation nor the names of its 44 * contributors may be used to endorse or promote products derived 45 * from this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 48 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 50 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 51 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 53 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 57 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 60 /* 61 * Driver kernel header files 62 */ 63 #include <sys/conf.h> 64 #include <sys/ddi.h> 65 #include <sys/stat.h> 66 #include <sys/pci.h> 67 #include <sys/sunddi.h> 68 #include <sys/modctl.h> 69 #include <sys/file.h> 70 #include <sys/cred.h> 71 #include <sys/byteorder.h> 72 #include <sys/atomic.h> 73 #include <sys/modhash.h> 74 #include <sys/scsi/scsi.h> 75 #include <sys/ethernet.h> 76 77 /* 78 * COMSTAR header files 79 */ 80 #include <sys/stmf_defines.h> 81 #include <sys/fct_defines.h> 82 #include <sys/stmf.h> 83 #include <sys/portif.h> 84 #include <sys/fct.h> 85 86 /* 87 * FCoE header files 88 */ 89 #include <sys/fcoe/fcoe_common.h> 90 91 /* 92 * Driver's own header files 93 */ 94 #include "fcoet.h" 95 #include "fcoet_eth.h" 96 #include "fcoet_fc.h" 97 98 /* 99 * static function forward declaration 100 */ 101 static int fcoet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 102 static int fcoet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 103 static int fcoet_open(dev_t *devp, int flag, int otype, cred_t *credp); 104 static int fcoet_close(dev_t dev, int flag, int otype, cred_t *credp); 105 static int fcoet_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 106 cred_t *credp, int *rval); 107 static fct_status_t fcoet_attach_init(fcoet_soft_state_t *ss); 108 static fct_status_t fcoet_detach_uninit(fcoet_soft_state_t *ss); 109 static void fcoet_watchdog(void *arg); 110 static void fcoet_handle_sol_flogi(fcoet_soft_state_t *ss); 111 static stmf_data_buf_t *fcoet_dbuf_alloc(fct_local_port_t *port, 112 uint32_t size, uint32_t *pminsize, uint32_t flags); 113 static void fcoet_dbuf_free(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf); 114 static int fcoet_dbuf_init(fcoet_soft_state_t *ss); 115 static void fcoet_dbuf_destroy(fcoet_soft_state_t *ss); 116 static uint_t 117 fcoet_sol_oxid_hash_empty(mod_hash_key_t key, mod_hash_val_t *val, void *arg); 118 static uint_t 119 fcoet_unsol_rxid_hash_empty(mod_hash_key_t key, mod_hash_val_t *val, void *arg); 120 121 /* 122 * Driver identificaton stuff 123 */ 124 static struct cb_ops fcoet_cb_ops = { 125 fcoet_open, 126 fcoet_close, 127 nodev, 128 nodev, 129 nodev, 130 nodev, 131 nodev, 132 fcoet_ioctl, 133 nodev, 134 nodev, 135 nodev, 136 nochpoll, 137 ddi_prop_op, 138 0, 139 D_MP | D_NEW 140 }; 141 142 static struct dev_ops fcoet_ops = { 143 DEVO_REV, 144 0, 145 nodev, 146 nulldev, 147 nulldev, 148 fcoet_attach, 149 fcoet_detach, 150 nodev, 151 &fcoet_cb_ops, 152 NULL, 153 ddi_power, 154 ddi_quiesce_not_needed 155 }; 156 157 static struct modldrv modldrv = { 158 &mod_driverops, 159 FCOET_MOD_NAME, 160 &fcoet_ops, 161 }; 162 163 static struct modlinkage modlinkage = { 164 MODREV_1, &modldrv, NULL 165 }; 166 167 /* 168 * Driver's global variables 169 */ 170 static kmutex_t fcoet_mutex; 171 static void *fcoet_state = NULL; 172 173 int fcoet_use_ext_log = 1; 174 static char fcoet_provider_name[] = "fcoet"; 175 static struct stmf_port_provider *fcoet_pp = NULL; 176 177 /* 178 * Common loadable module entry points _init, _fini, _info 179 */ 180 181 int 182 _init(void) 183 { 184 int ret; 185 186 ret = ddi_soft_state_init(&fcoet_state, sizeof (fcoet_soft_state_t), 0); 187 if (ret == 0) { 188 fcoet_pp = (stmf_port_provider_t *) 189 stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0); 190 fcoet_pp->pp_portif_rev = PORTIF_REV_1; 191 fcoet_pp->pp_name = fcoet_provider_name; 192 if (stmf_register_port_provider(fcoet_pp) != STMF_SUCCESS) { 193 stmf_free(fcoet_pp); 194 ddi_soft_state_fini(&fcoet_state); 195 return (EIO); 196 } 197 198 mutex_init(&fcoet_mutex, 0, MUTEX_DRIVER, 0); 199 ret = mod_install(&modlinkage); 200 if (ret) { 201 (void) stmf_deregister_port_provider(fcoet_pp); 202 stmf_free(fcoet_pp); 203 mutex_destroy(&fcoet_mutex); 204 ddi_soft_state_fini(&fcoet_state); 205 } 206 } 207 208 FCOET_LOG("_init", "exit _init with %x", ret); 209 return (ret); 210 } 211 212 int 213 _fini(void) 214 { 215 int ret; 216 217 ret = mod_remove(&modlinkage); 218 if (ret == 0) { 219 (void) stmf_deregister_port_provider(fcoet_pp); 220 stmf_free(fcoet_pp); 221 mutex_destroy(&fcoet_mutex); 222 ddi_soft_state_fini(&fcoet_state); 223 } 224 225 FCOET_LOG("_fini", "exit _fini with %x", ret); 226 return (ret); 227 } 228 229 int 230 _info(struct modinfo *modinfop) 231 { 232 return (mod_info(&modlinkage, modinfop)); 233 } 234 235 /* 236 * Autoconfiguration entry points: attach, detach, getinfo 237 */ 238 239 static int 240 fcoet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 241 { 242 int ret = DDI_FAILURE; 243 int instance; 244 fcoet_soft_state_t *ss; 245 246 instance = ddi_get_instance(dip); 247 FCOET_LOG("fcoet_attach", "get instance %d", instance); 248 249 switch (cmd) { 250 case DDI_ATTACH: 251 ret = ddi_soft_state_zalloc(fcoet_state, instance); 252 if (ret != DDI_SUCCESS) { 253 return (ret); 254 } 255 256 ss = ddi_get_soft_state(fcoet_state, instance); 257 ss->ss_instance = instance; 258 ss->ss_dip = dip; 259 260 ret = fcoet_attach_init(ss); 261 if (ret != FCOE_SUCCESS) { 262 ddi_soft_state_free(fcoet_state, instance); 263 ret = DDI_FAILURE; 264 } 265 266 FCOET_LOG("fcoet_attach", "end with-%x", ret); 267 break; 268 269 case DDI_RESUME: 270 ret = DDI_SUCCESS; 271 break; 272 273 default: 274 FCOET_LOG("fcoet_attach", "unspported attach cmd-%x", cmd); 275 break; 276 } 277 278 return (ret); 279 } 280 281 static int 282 fcoet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 283 { 284 int ret = DDI_FAILURE; 285 int fcoe_ret; 286 int instance; 287 fcoet_soft_state_t *ss; 288 289 instance = ddi_get_instance(dip); 290 ss = ddi_get_soft_state(fcoet_state, instance); 291 if (ss == NULL) { 292 return (ret); 293 } 294 295 switch (cmd) { 296 case DDI_DETACH: 297 fcoe_ret = fcoet_detach_uninit(ss); 298 if (fcoe_ret == FCOE_SUCCESS) { 299 ret = DDI_SUCCESS; 300 } 301 302 FCOET_LOG("fcoet_detach", "fcoet_detach_uninit end with-%x", 303 fcoe_ret); 304 break; 305 306 case DDI_SUSPEND: 307 ret = DDI_SUCCESS; 308 break; 309 310 default: 311 FCOET_LOG("fcoet_detach", "unsupported detach cmd-%x", cmd); 312 break; 313 } 314 315 return (ret); 316 } 317 318 /* 319 * Device access entry points 320 */ 321 static int 322 fcoet_open(dev_t *devp, int flag, int otype, cred_t *credp) 323 { 324 int instance; 325 fcoet_soft_state_t *ss; 326 327 if (otype != OTYP_CHR) { 328 return (EINVAL); 329 } 330 331 /* 332 * Since this is for debugging only, only allow root to issue ioctl now 333 */ 334 if (drv_priv(credp)) { 335 return (EPERM); 336 } 337 338 instance = (int)getminor(*devp); 339 ss = ddi_get_soft_state(fcoet_state, instance); 340 if (ss == NULL) { 341 return (ENXIO); 342 } 343 344 mutex_enter(&ss->ss_ioctl_mutex); 345 if (ss->ss_ioctl_flags & FCOET_IOCTL_FLAG_EXCL) { 346 /* 347 * It is already open for exclusive access. 348 * So shut the door on this caller. 349 */ 350 mutex_exit(&ss->ss_ioctl_mutex); 351 return (EBUSY); 352 } 353 354 if (flag & FEXCL) { 355 if (ss->ss_ioctl_flags & FCOET_IOCTL_FLAG_OPEN) { 356 /* 357 * Exclusive operation not possible 358 * as it is already opened 359 */ 360 mutex_exit(&ss->ss_ioctl_mutex); 361 return (EBUSY); 362 } 363 ss->ss_ioctl_flags |= FCOET_IOCTL_FLAG_EXCL; 364 } 365 ss->ss_ioctl_flags |= FCOET_IOCTL_FLAG_OPEN; 366 mutex_exit(&ss->ss_ioctl_mutex); 367 368 return (0); 369 } 370 371 /* ARGSUSED */ 372 static int 373 fcoet_close(dev_t dev, int flag, int otype, cred_t *credp) 374 { 375 int instance; 376 fcoet_soft_state_t *ss; 377 378 if (otype != OTYP_CHR) { 379 return (EINVAL); 380 } 381 382 instance = (int)getminor(dev); 383 ss = ddi_get_soft_state(fcoet_state, instance); 384 if (ss == NULL) { 385 return (ENXIO); 386 } 387 388 mutex_enter(&ss->ss_ioctl_mutex); 389 if ((ss->ss_ioctl_flags & FCOET_IOCTL_FLAG_OPEN) == 0) { 390 mutex_exit(&ss->ss_ioctl_mutex); 391 return (ENODEV); 392 } 393 394 /* 395 * It looks there's one hole here, maybe there could several concurrent 396 * shareed open session, but we never check this case. 397 * But it will not hurt too much, disregard it now. 398 */ 399 ss->ss_ioctl_flags &= ~FCOET_IOCTL_FLAG_MASK; 400 mutex_exit(&ss->ss_ioctl_mutex); 401 402 return (0); 403 } 404 405 /* ARGSUSED */ 406 static int 407 fcoet_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 408 cred_t *credp, int *rval) 409 { 410 fcoet_soft_state_t *ss; 411 int ret = 0; 412 413 if (drv_priv(credp) != 0) { 414 return (EPERM); 415 } 416 417 ss = ddi_get_soft_state(fcoet_state, (int32_t)getminor(dev)); 418 if (ss == NULL) { 419 return (ENXIO); 420 } 421 422 switch (cmd) { 423 default: 424 FCOET_LOG("fcoet_ioctl", "ioctl-0x%02X", cmd); 425 ret = ENOTTY; 426 break; 427 } 428 429 *rval = ret; 430 return (ret); 431 } 432 433 static fct_status_t 434 fcoet_attach_init(fcoet_soft_state_t *ss) 435 { 436 fcoe_client_t client_fcoet; 437 fcoe_port_t *eport; 438 fct_local_port_t *port; 439 fct_dbuf_store_t *fds; 440 char taskq_name[FCOET_TASKQ_NAME_LEN]; 441 int ret; 442 443 /* 444 * FCoE (fcoe is fcoet's dependent driver) 445 * First we need register fcoet to FCoE as one client 446 */ 447 client_fcoet.ect_eport_flags = EPORT_FLAG_TGT_MODE | 448 EPORT_FLAG_IS_DIRECT_P2P; 449 client_fcoet.ect_max_fc_frame_size = 2136; 450 client_fcoet.ect_private_frame_struct_size = sizeof (fcoet_frame_t); 451 client_fcoet.ect_rx_frame = fcoet_rx_frame; 452 client_fcoet.ect_port_event = fcoet_port_event; 453 client_fcoet.ect_release_sol_frame = fcoet_release_sol_frame; 454 client_fcoet.ect_client_port_struct = ss; 455 client_fcoet.ect_fcoe_ver = FCOE_VER_NOW; 456 FCOET_LOG(__FUNCTION__, "version: %x %x", FCOE_VER_NOW, fcoe_ver_now); 457 ret = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip, 458 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "mac_id", -1); 459 if (ret == -1) { 460 FCOET_LOG("fcoet_attach_init", "get mac_id failed"); 461 return (DDI_FAILURE); 462 } else { 463 client_fcoet.ect_channelid = ret; 464 } 465 FCOET_LOG("fcoet_attach_init", "channel_id is %d", 466 client_fcoet.ect_channelid); 467 468 /* 469 * It's FCoE's responsiblity to initialize eport's all elements 470 */ 471 eport = fcoe_register_client(&client_fcoet); 472 if (eport == NULL) { 473 goto fail_register_client; 474 } 475 476 /* 477 * Now it's time to register local port to FCT 478 */ 479 if (fcoet_dbuf_init(ss) != FCOE_SUCCESS) { 480 goto fail_init_dbuf; 481 } 482 483 fds = (fct_dbuf_store_t *)fct_alloc(FCT_STRUCT_DBUF_STORE, 0, 0); 484 if (fds == NULL) { 485 goto fail_alloc_dbuf; 486 } else { 487 fds->fds_alloc_data_buf = fcoet_dbuf_alloc; 488 fds->fds_free_data_buf = fcoet_dbuf_free; 489 fds->fds_fca_private = (void *)ss; 490 } 491 492 port = (fct_local_port_t *)fct_alloc(FCT_STRUCT_LOCAL_PORT, 0, 0); 493 if (port == NULL) { 494 goto fail_alloc_port; 495 } else { 496 /* 497 * Do ss's initialization now 498 */ 499 (void) snprintf(ss->ss_alias, sizeof (ss->ss_alias), "fcoet%d", 500 ss->ss_instance); 501 ret = ddi_create_minor_node(ss->ss_dip, "admin", 502 S_IFCHR, ss->ss_instance, DDI_NT_STMF_PP, 0); 503 if (ret != DDI_SUCCESS) { 504 goto fail_minor_node; 505 } 506 507 ss->ss_state = FCT_STATE_OFFLINE; 508 ss->ss_state_not_acked = 1; 509 ss->ss_flags = 0; 510 ss->ss_port = port; 511 ss->ss_eport = eport; 512 FCOE_SET_DEFAULT_FPORT_ADDR(eport->eport_efh_dst); 513 514 ss->ss_rportid_in_dereg = 0; 515 ss->ss_rport_dereg_state = 0; 516 517 ss->ss_next_sol_oxid = 0xFFFF; 518 ss->ss_next_unsol_rxid = 0xFFFF; 519 ss->ss_sol_oxid_hash = mod_hash_create_idhash( 520 "ss_sol_oxid_hash", FCOET_SOL_HASH_SIZE, 521 mod_hash_null_valdtor); 522 ss->ss_unsol_rxid_hash = mod_hash_create_idhash( 523 "ss_unsol_rxid_hash", FCOET_SOL_HASH_SIZE, 524 mod_hash_null_valdtor); 525 526 ss->ss_watch_count = 0; 527 mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0); 528 cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL); 529 530 list_create(&ss->ss_abort_xchg_list, sizeof (fcoet_exchange_t), 531 offsetof(fcoet_exchange_t, xch_abort_node)); 532 533 ss->ss_sol_flogi = NULL; 534 ss->ss_sol_flogi_state = SFS_WAIT_LINKUP; 535 536 bzero(&ss->ss_link_info, sizeof (fct_link_info_t)); 537 538 ss->ss_ioctl_flags = 0; 539 mutex_init(&ss->ss_ioctl_mutex, 0, MUTEX_DRIVER, 0); 540 541 ss->ss_change_state_flags = 0; 542 } 543 544 /* 545 * Do port's initialization 546 * 547 * port_fct_private and port_lport have been initialized by fct_alloc 548 */ 549 port->port_fca_private = ss; 550 port->port_fca_version = FCT_FCA_MODREV_1; 551 bcopy(ss->ss_eport->eport_nodewwn, port->port_nwwn, 8); 552 bcopy(ss->ss_eport->eport_portwwn, port->port_pwwn, 8); 553 port->port_default_alias = ss->ss_alias; 554 port->port_sym_node_name = NULL; 555 port->port_sym_port_name = NULL; 556 557 port->port_pp = fcoet_pp; 558 559 port->port_hard_address = 0; 560 port->port_max_logins = FCOET_MAX_LOGINS; 561 port->port_max_xchges = FCOET_MAX_XCHGES; 562 port->port_fca_fcp_cmd_size = sizeof (fcoet_exchange_t); 563 port->port_fca_rp_private_size = 0; 564 port->port_fca_sol_els_private_size = sizeof (fcoet_exchange_t); 565 port->port_fca_sol_ct_private_size = sizeof (fcoet_exchange_t); 566 567 port->port_fca_abort_timeout = 5 * 1000; /* 5 seconds */ 568 port->port_fds = fds; 569 570 port->port_get_link_info = fcoet_get_link_info; 571 port->port_register_remote_port = fcoet_register_remote_port; 572 port->port_deregister_remote_port = fcoet_deregister_remote_port; 573 port->port_send_cmd = fcoet_send_cmd; 574 port->port_xfer_scsi_data = fcoet_xfer_scsi_data; 575 port->port_send_cmd_response = fcoet_send_cmd_response; 576 port->port_abort_cmd = fcoet_abort_cmd; 577 port->port_ctl = fcoet_ctl; 578 port->port_flogi_xchg = fcoet_do_flogi; 579 port->port_populate_hba_details = fcoet_populate_hba_fru_details; 580 if (fct_register_local_port(port) != FCT_SUCCESS) { 581 goto fail_register_port; 582 } 583 584 /* 585 * Start watchdog thread 586 */ 587 (void) snprintf(taskq_name, sizeof (taskq_name), 588 "stmf_fct_fcoet_%d_taskq", ss->ss_instance); 589 if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL, 590 taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 591 goto fail_create_taskq; 592 } 593 594 atomic_and_32(&ss->ss_flags, ~SS_FLAG_TERMINATE_WATCHDOG); 595 (void) ddi_taskq_dispatch(ss->ss_watchdog_taskq, 596 fcoet_watchdog, ss, DDI_SLEEP); 597 while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) { 598 delay(10); 599 } 600 601 ddi_report_dev(ss->ss_dip); 602 return (DDI_SUCCESS); 603 604 fail_create_taskq: 605 if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 606 atomic_or_32(&ss->ss_flags, SS_FLAG_TERMINATE_WATCHDOG); 607 cv_broadcast(&ss->ss_watch_cv); 608 while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 609 delay(10); 610 } 611 } 612 613 ddi_taskq_destroy(ss->ss_watchdog_taskq); 614 FCOET_LOG("fcoet_attach_init", "fail_register_port"); 615 616 fail_register_port: 617 mutex_destroy(&ss->ss_ioctl_mutex); 618 mutex_destroy(&ss->ss_watch_mutex); 619 cv_destroy(&ss->ss_watch_cv); 620 mod_hash_destroy_hash(ss->ss_sol_oxid_hash); 621 mod_hash_destroy_hash(ss->ss_unsol_rxid_hash); 622 list_destroy(&ss->ss_abort_xchg_list); 623 FCOET_LOG("fcoet_attach_init", "fail_create_taskq"); 624 625 fail_minor_node: 626 fct_free(port); 627 FCOET_LOG("fcoet_attach_init", "fail_minor_node"); 628 629 fail_alloc_port: 630 fct_free(fds); 631 FCOET_LOG("fcoet_attach_init", "fail_alloc_port"); 632 633 fail_alloc_dbuf: 634 fcoet_dbuf_destroy(ss); 635 FCOET_LOG("fcoet_attach_init", "fail_alloc_dbuf"); 636 637 fail_init_dbuf: 638 ss->ss_eport->eport_deregister_client(ss->ss_eport); 639 FCOET_LOG("fcoet_attach_init", "fail_init_dbuf"); 640 641 fail_register_client: 642 FCOET_LOG("fcoet_attach_init", "fail_register_client"); 643 return (DDI_FAILURE); 644 } 645 646 static fct_status_t 647 fcoet_detach_uninit(fcoet_soft_state_t *ss) 648 { 649 if ((ss->ss_state != FCT_STATE_OFFLINE) || 650 ss->ss_state_not_acked) { 651 return (FCOE_FAILURE); 652 } 653 654 /* 655 * Avoid modunload before running fcinfo remove-target-port 656 */ 657 if (ss->ss_eport != NULL && 658 ss->ss_eport->eport_flags & EPORT_FLAG_MAC_IN_USE) { 659 return (FCOE_FAILURE); 660 } 661 662 if (ss->ss_port == NULL) { 663 return (FCOE_SUCCESS); 664 } 665 666 ss->ss_sol_oxid_hash_empty = 1; 667 ss->ss_unsol_rxid_hash_empty = 1; 668 mod_hash_walk(ss->ss_sol_oxid_hash, fcoet_sol_oxid_hash_empty, ss); 669 mod_hash_walk(ss->ss_unsol_rxid_hash, fcoet_unsol_rxid_hash_empty, ss); 670 if ((!ss->ss_sol_oxid_hash_empty) || (!ss->ss_unsol_rxid_hash_empty)) { 671 return (FCOE_FAILURE); 672 } 673 674 /* 675 * We need offline the port manually, before we want to detach it 676 * or it will not succeed. 677 */ 678 if (fct_deregister_local_port(ss->ss_port) != FCT_SUCCESS) { 679 FCOET_LOG("fcoet_detach_uninit", 680 "fct_deregister_local_port failed"); 681 return (FCOE_FAILURE); 682 } 683 684 /* 685 * Stop watchdog 686 */ 687 if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 688 atomic_or_32(&ss->ss_flags, SS_FLAG_TERMINATE_WATCHDOG); 689 cv_broadcast(&ss->ss_watch_cv); 690 while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 691 delay(10); 692 } 693 } 694 695 ddi_taskq_destroy(ss->ss_watchdog_taskq); 696 697 /* 698 * Release all resources 699 */ 700 mutex_destroy(&ss->ss_ioctl_mutex); 701 mutex_destroy(&ss->ss_watch_mutex); 702 cv_destroy(&ss->ss_watch_cv); 703 mod_hash_destroy_hash(ss->ss_sol_oxid_hash); 704 mod_hash_destroy_hash(ss->ss_unsol_rxid_hash); 705 list_destroy(&ss->ss_abort_xchg_list); 706 707 fct_free(ss->ss_port->port_fds); 708 fct_free(ss->ss_port); 709 ss->ss_port = NULL; 710 711 fcoet_dbuf_destroy(ss); 712 713 if (ss->ss_eport != NULL && 714 ss->ss_eport->eport_deregister_client != NULL) { 715 ss->ss_eport->eport_deregister_client(ss->ss_eport); 716 } 717 ddi_soft_state_free(fcoet_state, ss->ss_instance); 718 return (FCOE_SUCCESS); 719 } 720 721 static void 722 fcoet_watchdog(void *arg) 723 { 724 fcoet_soft_state_t *ss = (fcoet_soft_state_t *)arg; 725 clock_t tmp_delay = 0; 726 fcoet_exchange_t *xchg, *xchg_next; 727 728 FCOET_LOG("fcoet_watchdog", "fcoet_soft_state is %p", ss); 729 730 mutex_enter(&ss->ss_watch_mutex); 731 atomic_or_32(&ss->ss_flags, SS_FLAG_WATCHDOG_RUNNING); 732 tmp_delay = STMF_SEC2TICK(1)/2; 733 734 while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) { 735 ss->ss_watch_count++; 736 737 if (ss->ss_sol_flogi_state != SFS_FLOGI_DONE) { 738 fcoet_handle_sol_flogi(ss); 739 } 740 for (xchg = list_head(&ss->ss_abort_xchg_list); xchg; ) { 741 xchg_next = list_next(&ss->ss_abort_xchg_list, xchg); 742 if (xchg->xch_ref == 0) { 743 list_remove(&ss->ss_abort_xchg_list, xchg); 744 mutex_exit(&ss->ss_watch_mutex); 745 /* xchg abort done */ 746 if (xchg->xch_dbuf_num) { 747 kmem_free((void*)xchg->xch_dbufs, 748 xchg->xch_dbuf_num * 749 sizeof (void *)); 750 xchg->xch_dbufs = NULL; 751 xchg->xch_dbuf_num = 0; 752 } 753 fct_cmd_fca_aborted(xchg->xch_cmd, 754 FCT_ABORT_SUCCESS, FCT_IOF_FCA_DONE); 755 mutex_enter(&ss->ss_watch_mutex); 756 } 757 xchg = xchg_next; 758 } 759 760 atomic_or_32(&ss->ss_flags, SS_FLAG_DOG_WAITING); 761 (void) cv_reltimedwait(&ss->ss_watch_cv, &ss->ss_watch_mutex, 762 (clock_t)tmp_delay, TR_CLOCK_TICK); 763 atomic_and_32(&ss->ss_flags, ~SS_FLAG_DOG_WAITING); 764 } 765 766 /* 767 * Ensure no ongoing FLOGI, before terminate the watchdog 768 */ 769 if (ss->ss_sol_flogi) { 770 fcoet_clear_sol_exchange(ss->ss_sol_flogi); 771 fct_free(ss->ss_sol_flogi->xch_cmd); 772 ss->ss_sol_flogi = NULL; 773 } 774 775 atomic_and_32(&ss->ss_flags, ~SS_FLAG_WATCHDOG_RUNNING); 776 mutex_exit(&ss->ss_watch_mutex); 777 } 778 779 static void 780 fcoet_handle_sol_flogi(fcoet_soft_state_t *ss) 781 { 782 clock_t twosec = STMF_SEC2TICK(2); 783 784 check_state_again: 785 if (ss->ss_flags & SS_FLAG_PORT_DISABLED) { 786 ss->ss_sol_flogi_state = SFS_WAIT_LINKUP; 787 } 788 789 switch (ss->ss_sol_flogi_state) { 790 case SFS_WAIT_LINKUP: 791 if (ss->ss_sol_flogi) { 792 if (ss->ss_sol_flogi->xch_ref == 0) { 793 fcoet_clear_sol_exchange(ss->ss_sol_flogi); 794 fct_free(ss->ss_sol_flogi->xch_cmd); 795 ss->ss_sol_flogi = NULL; 796 } 797 } 798 break; 799 800 case SFS_FLOGI_INIT: 801 if (ss->ss_sol_flogi) { 802 /* 803 * wait for the response to finish 804 */ 805 ss->ss_sol_flogi_state = SFS_CLEAR_FLOGI; 806 break; 807 } 808 fcoet_send_sol_flogi(ss); 809 ss->ss_sol_flogi_state++; 810 break; 811 812 case SFS_FLOGI_CHECK_TIMEOUT: 813 if ((ss->ss_sol_flogi->xch_start_time + twosec) < 814 ddi_get_lbolt()) { 815 ss->ss_sol_flogi_state++; 816 } 817 break; 818 819 case SFS_ABTS_INIT: 820 fcoet_send_sol_abts(ss->ss_sol_flogi); 821 ss->ss_sol_flogi_state++; 822 break; 823 824 case SFS_CLEAR_FLOGI: 825 if (ss->ss_sol_flogi) { 826 if (ss->ss_sol_flogi->xch_ref) { 827 break; 828 } 829 fcoet_clear_sol_exchange(ss->ss_sol_flogi); 830 fct_free(ss->ss_sol_flogi->xch_cmd); 831 ss->ss_sol_flogi = NULL; 832 } 833 ss->ss_sol_flogi_state = SFS_FLOGI_INIT; 834 goto check_state_again; 835 836 case SFS_FLOGI_ACC: 837 ss->ss_sol_flogi_state++; 838 goto check_state_again; 839 840 case SFS_FLOGI_DONE: 841 if (!(ss->ss_flags & SS_FLAG_PORT_DISABLED) && 842 ss->ss_sol_flogi) { 843 fcoet_clear_sol_exchange(ss->ss_sol_flogi); 844 fct_free(ss->ss_sol_flogi->xch_cmd); 845 ss->ss_sol_flogi = NULL; 846 } 847 848 /* 849 * We'd better to offline it first, and delay 0.1 seconds, 850 * before we say it's on again. 851 */ 852 fct_handle_event(ss->ss_port, 853 FCT_EVENT_LINK_DOWN, 0, NULL); 854 delay(STMF_SEC2TICK(1)/10); 855 fct_handle_event(ss->ss_port, 856 FCT_EVENT_LINK_UP, 0, NULL); 857 break; 858 859 default: 860 ASSERT(0); 861 break; 862 } 863 } 864 865 /* ARGSUSED */ 866 static int 867 fcoet_dbuf_init(fcoet_soft_state_t *ss) 868 { 869 return (FCOE_SUCCESS); 870 } 871 872 /* ARGSUSED */ 873 static void 874 fcoet_dbuf_destroy(fcoet_soft_state_t *ss) 875 { 876 877 } 878 879 /* ARGSUSED */ 880 static stmf_data_buf_t * 881 fcoet_dbuf_alloc(fct_local_port_t *port, uint32_t size, uint32_t *pminsize, 882 uint32_t flags) 883 { 884 stmf_data_buf_t *dbuf; 885 int add_size; 886 int sge_num; 887 int sge_size; 888 int idx; 889 int ii; 890 void *netb; 891 uint8_t *fc_buf; 892 fcoet_soft_state_t *ss = 893 (fcoet_soft_state_t *)port->port_fca_private; 894 895 if (size > FCOET_MAX_DBUF_LEN) { 896 if (*pminsize > FCOET_MAX_DBUF_LEN) { 897 return (NULL); 898 } 899 900 size = FCOET_MAX_DBUF_LEN; 901 } 902 903 sge_num = (size - 1) / ss->ss_fcp_data_payload_size + 1; 904 add_size = (sge_num - 1) * sizeof (struct stmf_sglist_ent) + 905 sge_num * sizeof (mblk_t *); 906 dbuf = stmf_alloc(STMF_STRUCT_DATA_BUF, add_size, 0); 907 if (dbuf == NULL) { 908 return (NULL); 909 } 910 dbuf->db_buf_size = size; 911 dbuf->db_data_size = size; 912 dbuf->db_sglist_length = 0; 913 dbuf->db_flags |= DB_DONT_REUSE; 914 FCOET_SET_SEG_NUM(dbuf, sge_num); 915 916 /* 917 * Initialize non-last sg entries 918 */ 919 for (idx = 0; idx < sge_num - 1; idx++) { 920 sge_size = ss->ss_fcp_data_payload_size; 921 netb = ss->ss_eport->eport_alloc_netb( 922 ss->ss_eport, sizeof (fcoe_fc_frame_header_t) + 923 sge_size, &fc_buf); 924 if (netb == NULL) { 925 for (ii = 0; ii < idx; ii++) { 926 ss->ss_eport->eport_free_netb( 927 FCOET_GET_NETB(dbuf, ii)); 928 } 929 stmf_free(dbuf); 930 FCOET_LOG("fcoe_dbuf_alloc", "no netb"); 931 return (NULL); 932 } 933 FCOET_SET_NETB(dbuf, idx, netb); 934 dbuf->db_sglist[idx].seg_addr = fc_buf + 935 sizeof (fcoe_fc_frame_header_t); 936 dbuf->db_sglist[idx].seg_length = sge_size; 937 } 938 939 /* 940 * Initialize the last sg entry 941 */ 942 if (size % ss->ss_fcp_data_payload_size) { 943 sge_size = P2ROUNDUP(size % ss->ss_fcp_data_payload_size, 4); 944 } else { 945 sge_size = ss->ss_fcp_data_payload_size; 946 } 947 948 netb = ss->ss_eport->eport_alloc_netb( 949 ss->ss_eport, 950 sizeof (fcoe_fc_frame_header_t) + 951 sge_size, &fc_buf); 952 if (netb == NULL) { 953 for (ii = 0; ii < idx; ii++) { 954 ss->ss_eport->eport_free_netb( 955 FCOET_GET_NETB(dbuf, ii)); 956 } 957 stmf_free(dbuf); 958 FCOET_LOG("fcoe_dbuf_alloc", "no netb"); 959 return (NULL); 960 } 961 962 FCOET_SET_NETB(dbuf, idx, netb); 963 dbuf->db_sglist[idx].seg_addr = fc_buf + 964 sizeof (fcoe_fc_frame_header_t); 965 dbuf->db_sglist[idx].seg_length = sge_size; 966 967 /* 968 * Let COMSTAR know how many sg entries we will use 969 */ 970 dbuf->db_sglist_length = idx + 1; 971 972 return (dbuf); 973 } 974 975 static void 976 fcoet_dbuf_free(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf) 977 { 978 int idx; 979 fcoet_soft_state_t *ss = 980 (fcoet_soft_state_t *)fds->fds_fca_private; 981 982 for (idx = 0; idx < FCOET_GET_SEG_NUM(dbuf); idx++) { 983 if (FCOET_GET_NETB(dbuf, idx)) { 984 ss->ss_eport->eport_free_netb( 985 FCOET_GET_NETB(dbuf, idx)); 986 } 987 } 988 989 stmf_free(dbuf); 990 } 991 992 /* 993 * We should have initialized fcoe_frame_t before 994 */ 995 void 996 fcoet_init_tfm(fcoe_frame_t *frm, fcoet_exchange_t *xch) 997 { 998 FRM2TFM(frm)->tfm_fcoe_frame = frm; 999 FRM2TFM(frm)->tfm_xch = xch; 1000 FRM2TFM(frm)->tfm_seq = NULL; 1001 } 1002 1003 /* ARGSUSED */ 1004 static uint_t 1005 fcoet_sol_oxid_hash_empty(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 1006 { 1007 fcoet_soft_state_t *ss = (fcoet_soft_state_t *)arg; 1008 1009 ss->ss_sol_oxid_hash_empty = 0; 1010 FCOET_LOG("fcoet_sol_oxid_hash_empty", "one ongoing xch: %p", val); 1011 return (MH_WALK_CONTINUE); 1012 } 1013 1014 /* ARGSUSED */ 1015 static uint_t 1016 fcoet_unsol_rxid_hash_empty(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 1017 { 1018 fcoet_soft_state_t *ss = (fcoet_soft_state_t *)arg; 1019 1020 ss->ss_sol_oxid_hash_empty = 0; 1021 FCOET_LOG("fcoet_unsol_rxid_hash_empty", "one ongoing xch: %p", val); 1022 return (MH_WALK_CONTINUE); 1023 } 1024 1025 /* ARGSUSED */ 1026 void 1027 fcoet_modhash_find_cb(mod_hash_key_t key, mod_hash_val_t val) 1028 { 1029 ASSERT(val != NULL); 1030 fcoet_exchange_t *xch = (fcoet_exchange_t *)val; 1031 FCOET_BUSY_XCHG(xch); 1032 }