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 #include <sys/cpuvar.h> 27 #include <sys/types.h> 28 #include <sys/conf.h> 29 #include <sys/stat.h> 30 #include <sys/file.h> 31 #include <sys/ddi.h> 32 #include <sys/sunddi.h> 33 #include <sys/modctl.h> 34 #include <sys/sysmacros.h> 35 #include <sys/nvpair.h> 36 #include <sys/door.h> 37 38 #include <sys/stmf.h> 39 #include <sys/stmf_ioctl.h> 40 #include <sys/pppt_ioctl.h> 41 #include <sys/portif.h> 42 #include <pppt.h> 43 44 #define PPPT_VERSION BUILD_DATE "-1.18dev" 45 #define PPPT_NAME_VERSION "COMSTAR PPPT v" PPPT_VERSION 46 47 /* 48 * DDI entry points. 49 */ 50 static int pppt_drv_attach(dev_info_t *, ddi_attach_cmd_t); 51 static int pppt_drv_detach(dev_info_t *, ddi_detach_cmd_t); 52 static int pppt_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 53 static int pppt_drv_open(dev_t *, int, int, cred_t *); 54 static int pppt_drv_close(dev_t, int, int, cred_t *); 55 static boolean_t pppt_drv_busy(void); 56 static int pppt_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 57 58 extern pppt_status_t pppt_ic_so_enable(boolean_t); 59 extern void pppt_ic_so_disable(); 60 extern void stmf_ic_rx_msg(char *, size_t); 61 62 extern struct mod_ops mod_miscops; 63 64 static struct cb_ops pppt_cb_ops = { 65 pppt_drv_open, /* cb_open */ 66 pppt_drv_close, /* cb_close */ 67 nodev, /* cb_strategy */ 68 nodev, /* cb_print */ 69 nodev, /* cb_dump */ 70 nodev, /* cb_read */ 71 nodev, /* cb_write */ 72 pppt_drv_ioctl, /* cb_ioctl */ 73 nodev, /* cb_devmap */ 74 nodev, /* cb_mmap */ 75 nodev, /* cb_segmap */ 76 nochpoll, /* cb_chpoll */ 77 ddi_prop_op, /* cb_prop_op */ 78 NULL, /* cb_streamtab */ 79 D_MP, /* cb_flag */ 80 CB_REV, /* cb_rev */ 81 nodev, /* cb_aread */ 82 nodev, /* cb_awrite */ 83 }; 84 85 static struct dev_ops pppt_dev_ops = { 86 DEVO_REV, /* devo_rev */ 87 0, /* devo_refcnt */ 88 pppt_drv_getinfo, /* devo_getinfo */ 89 nulldev, /* devo_identify */ 90 nulldev, /* devo_probe */ 91 pppt_drv_attach, /* devo_attach */ 92 pppt_drv_detach, /* devo_detach */ 93 nodev, /* devo_reset */ 94 &pppt_cb_ops, /* devo_cb_ops */ 95 NULL, /* devo_bus_ops */ 96 NULL, /* devo_power */ 97 ddi_quiesce_not_needed, /* quiesce */ 98 }; 99 100 static struct modldrv modldrv = { 101 &mod_driverops, 102 "Proxy Port Provider", 103 &pppt_dev_ops, 104 }; 105 106 static struct modlinkage modlinkage = { 107 MODREV_1, 108 &modldrv, 109 NULL, 110 }; 111 112 pppt_global_t pppt_global; 113 114 int pppt_logging = 0; 115 116 static int pppt_enable_svc(void); 117 118 static void pppt_disable_svc(void); 119 120 static int pppt_task_avl_compare(const void *tgt1, const void *tgt2); 121 122 static stmf_data_buf_t *pppt_dbuf_alloc(scsi_task_t *task, 123 uint32_t size, uint32_t *pminsize, uint32_t flags); 124 125 static void pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf); 126 127 static void pppt_sess_destroy_task(void *ps_void); 128 129 static void pppt_task_sent_status(pppt_task_t *ptask); 130 131 static pppt_status_t pppt_task_try_abort(pppt_task_t *ptask); 132 133 static pppt_status_t pppt_task_hold(pppt_task_t *ptask); 134 135 static void pppt_task_rele(pppt_task_t *ptask); 136 137 static void pppt_task_update_state(pppt_task_t *ptask, 138 pppt_task_state_t new_state); 139 140 /* 141 * Lock order: global --> target --> session --> task 142 */ 143 144 int 145 _init(void) 146 { 147 int rc; 148 149 mutex_init(&pppt_global.global_lock, NULL, MUTEX_DEFAULT, NULL); 150 mutex_init(&pppt_global.global_door_lock, NULL, MUTEX_DEFAULT, NULL); 151 pppt_global.global_svc_state = PSS_DETACHED; 152 153 if ((rc = mod_install(&modlinkage)) != 0) { 154 mutex_destroy(&pppt_global.global_door_lock); 155 mutex_destroy(&pppt_global.global_lock); 156 return (rc); 157 } 158 159 return (rc); 160 } 161 162 int 163 _info(struct modinfo *modinfop) 164 { 165 return (mod_info(&modlinkage, modinfop)); 166 } 167 168 int 169 _fini(void) 170 { 171 int rc; 172 173 rc = mod_remove(&modlinkage); 174 175 if (rc == 0) { 176 mutex_destroy(&pppt_global.global_lock); 177 mutex_destroy(&pppt_global.global_door_lock); 178 } 179 180 return (rc); 181 } 182 183 /* 184 * DDI entry points. 185 */ 186 187 /* ARGSUSED */ 188 static int 189 pppt_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, 190 void **result) 191 { 192 ulong_t instance = getminor((dev_t)arg); 193 194 switch (cmd) { 195 case DDI_INFO_DEVT2DEVINFO: 196 *result = pppt_global.global_dip; 197 return (DDI_SUCCESS); 198 199 case DDI_INFO_DEVT2INSTANCE: 200 *result = (void *)instance; 201 return (DDI_SUCCESS); 202 203 default: 204 break; 205 } 206 207 return (DDI_FAILURE); 208 } 209 210 static int 211 pppt_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 212 { 213 if (cmd != DDI_ATTACH) { 214 return (DDI_FAILURE); 215 } 216 217 if (ddi_get_instance(dip) != 0) { 218 /* we only allow instance 0 to attach */ 219 return (DDI_FAILURE); 220 } 221 222 /* create the minor node */ 223 if (ddi_create_minor_node(dip, PPPT_MODNAME, S_IFCHR, 0, 224 DDI_PSEUDO, 0) != DDI_SUCCESS) { 225 cmn_err(CE_WARN, "pppt_drv_attach: " 226 "failed creating minor node"); 227 return (DDI_FAILURE); 228 } 229 230 pppt_global.global_svc_state = PSS_DISABLED; 231 pppt_global.global_dip = dip; 232 233 return (DDI_SUCCESS); 234 } 235 236 /*ARGSUSED*/ 237 static int 238 pppt_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 239 { 240 if (cmd != DDI_DETACH) 241 return (DDI_FAILURE); 242 243 PPPT_GLOBAL_LOCK(); 244 if (pppt_drv_busy()) { 245 PPPT_GLOBAL_UNLOCK(); 246 return (EBUSY); 247 } 248 249 ddi_remove_minor_node(dip, NULL); 250 ddi_prop_remove_all(dip); 251 252 pppt_global.global_svc_state = PSS_DETACHED; 253 254 PPPT_GLOBAL_UNLOCK(); 255 256 return (DDI_SUCCESS); 257 } 258 259 /*ARGSUSED*/ 260 static int 261 pppt_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) 262 { 263 int rc = 0; 264 265 PPPT_GLOBAL_LOCK(); 266 267 switch (pppt_global.global_svc_state) { 268 case PSS_DISABLED: 269 pppt_global.global_svc_state = PSS_ENABLING; 270 PPPT_GLOBAL_UNLOCK(); 271 rc = pppt_enable_svc(); 272 PPPT_GLOBAL_LOCK(); 273 if (rc == 0) { 274 pppt_global.global_svc_state = PSS_ENABLED; 275 } else { 276 pppt_global.global_svc_state = PSS_DISABLED; 277 } 278 break; 279 case PSS_DISABLING: 280 case PSS_ENABLING: 281 case PSS_ENABLED: 282 rc = EBUSY; 283 break; 284 default: 285 rc = EFAULT; 286 break; 287 } 288 289 PPPT_GLOBAL_UNLOCK(); 290 291 return (rc); 292 } 293 294 /* ARGSUSED */ 295 static int 296 pppt_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) 297 { 298 int rc = 0; 299 300 PPPT_GLOBAL_LOCK(); 301 302 switch (pppt_global.global_svc_state) { 303 case PSS_ENABLED: 304 pppt_global.global_svc_state = PSS_DISABLING; 305 PPPT_GLOBAL_UNLOCK(); 306 pppt_disable_svc(); 307 PPPT_GLOBAL_LOCK(); 308 pppt_global.global_svc_state = PSS_DISABLED; 309 /* 310 * release the door to the daemon 311 */ 312 mutex_enter(&pppt_global.global_door_lock); 313 if (pppt_global.global_door != NULL) { 314 door_ki_rele(pppt_global.global_door); 315 pppt_global.global_door = NULL; 316 } 317 mutex_exit(&pppt_global.global_door_lock); 318 break; 319 default: 320 rc = EFAULT; 321 break; 322 } 323 324 PPPT_GLOBAL_UNLOCK(); 325 326 return (rc); 327 } 328 329 static boolean_t 330 pppt_drv_busy(void) 331 { 332 switch (pppt_global.global_svc_state) { 333 case PSS_DISABLED: 334 case PSS_DETACHED: 335 return (B_FALSE); 336 default: 337 return (B_TRUE); 338 } 339 /* NOTREACHED */ 340 } 341 342 /* ARGSUSED */ 343 static int 344 pppt_drv_ioctl(dev_t drv, int cmd, intptr_t argp, int flag, cred_t *cred, 345 int *retval) 346 { 347 int rc; 348 void *buf; 349 size_t buf_size; 350 pppt_iocdata_t iocd; 351 door_handle_t new_handle; 352 353 if (drv_priv(cred) != 0) { 354 return (EPERM); 355 } 356 357 rc = ddi_copyin((void *)argp, &iocd, sizeof (iocd), flag); 358 if (rc) 359 return (EFAULT); 360 361 if (iocd.pppt_version != PPPT_VERSION_1) 362 return (EINVAL); 363 364 switch (cmd) { 365 case PPPT_MESSAGE: 366 367 /* XXX limit buf_size ? */ 368 buf_size = (size_t)iocd.pppt_buf_size; 369 buf = kmem_alloc(buf_size, KM_SLEEP); 370 if (buf == NULL) 371 return (ENOMEM); 372 373 rc = ddi_copyin((void *)(unsigned long)iocd.pppt_buf, 374 buf, buf_size, flag); 375 if (rc) { 376 kmem_free(buf, buf_size); 377 return (EFAULT); 378 } 379 380 stmf_ic_rx_msg(buf, buf_size); 381 382 kmem_free(buf, buf_size); 383 break; 384 case PPPT_INSTALL_DOOR: 385 386 new_handle = door_ki_lookup((int)iocd.pppt_door_fd); 387 if (new_handle == NULL) 388 return (EINVAL); 389 390 mutex_enter(&pppt_global.global_door_lock); 391 ASSERT(pppt_global.global_svc_state == PSS_ENABLED); 392 if (pppt_global.global_door != NULL) { 393 /* 394 * There can only be one door installed 395 */ 396 mutex_exit(&pppt_global.global_door_lock); 397 door_ki_rele(new_handle); 398 return (EBUSY); 399 } 400 pppt_global.global_door = new_handle; 401 mutex_exit(&pppt_global.global_door_lock); 402 break; 403 } 404 405 return (rc); 406 } 407 408 /* 409 * pppt_enable_svc 410 * 411 * registers all the configured targets and target portals with STMF 412 */ 413 static int 414 pppt_enable_svc(void) 415 { 416 stmf_port_provider_t *pp; 417 stmf_dbuf_store_t *dbuf_store; 418 int rc = 0; 419 420 ASSERT(pppt_global.global_svc_state == PSS_ENABLING); 421 422 /* 423 * Make sure that can tell if we have partially allocated 424 * in case we need to exit and tear down anything allocated. 425 */ 426 pppt_global.global_dbuf_store = NULL; 427 pp = NULL; 428 pppt_global.global_pp = NULL; 429 pppt_global.global_dispatch_taskq = NULL; 430 pppt_global.global_sess_taskq = NULL; 431 432 avl_create(&pppt_global.global_target_list, 433 pppt_tgt_avl_compare, sizeof (pppt_tgt_t), 434 offsetof(pppt_tgt_t, target_global_ln)); 435 436 avl_create(&pppt_global.global_sess_list, 437 pppt_sess_avl_compare_by_id, sizeof (pppt_sess_t), 438 offsetof(pppt_sess_t, ps_global_ln)); 439 440 /* 441 * Setup STMF dbuf store. Tf buffers are associated with a particular 442 * lport (FC, SRP) then the dbuf_store should stored in the lport 443 * context, otherwise (iSCSI) the dbuf_store should be global. 444 */ 445 dbuf_store = stmf_alloc(STMF_STRUCT_DBUF_STORE, 0, 0); 446 if (dbuf_store == NULL) { 447 rc = ENOMEM; 448 goto tear_down_and_return; 449 } 450 dbuf_store->ds_alloc_data_buf = pppt_dbuf_alloc; 451 dbuf_store->ds_free_data_buf = pppt_dbuf_free; 452 dbuf_store->ds_port_private = NULL; 453 pppt_global.global_dbuf_store = dbuf_store; 454 455 /* Register port provider */ 456 pp = stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0); 457 if (pp == NULL) { 458 rc = ENOMEM; 459 goto tear_down_and_return; 460 } 461 462 pp->pp_portif_rev = PORTIF_REV_1; 463 pp->pp_instance = 0; 464 pp->pp_name = PPPT_MODNAME; 465 pp->pp_cb = NULL; 466 467 pppt_global.global_pp = pp; 468 469 if (stmf_register_port_provider(pp) != STMF_SUCCESS) { 470 rc = EIO; 471 goto tear_down_and_return; 472 } 473 474 pppt_global.global_dispatch_taskq = taskq_create("pppt_dispatch", 475 1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE); 476 477 pppt_global.global_sess_taskq = taskq_create("pppt_session", 478 1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE); 479 480 return (0); 481 482 tear_down_and_return: 483 484 if (pppt_global.global_sess_taskq) { 485 taskq_destroy(pppt_global.global_sess_taskq); 486 pppt_global.global_sess_taskq = NULL; 487 } 488 489 if (pppt_global.global_dispatch_taskq) { 490 taskq_destroy(pppt_global.global_dispatch_taskq); 491 pppt_global.global_dispatch_taskq = NULL; 492 } 493 494 if (pppt_global.global_pp) 495 pppt_global.global_pp = NULL; 496 497 if (pp) 498 stmf_free(pp); 499 500 if (pppt_global.global_dbuf_store) { 501 stmf_free(pppt_global.global_dbuf_store); 502 pppt_global.global_dbuf_store = NULL; 503 } 504 505 avl_destroy(&pppt_global.global_sess_list); 506 avl_destroy(&pppt_global.global_target_list); 507 508 return (rc); 509 } 510 511 /* 512 * pppt_disable_svc 513 * 514 * clean up all existing sessions and deregister targets from STMF 515 */ 516 static void 517 pppt_disable_svc(void) 518 { 519 pppt_tgt_t *tgt, *next_tgt; 520 avl_tree_t delete_target_list; 521 522 ASSERT(pppt_global.global_svc_state == PSS_DISABLING); 523 524 avl_create(&delete_target_list, 525 pppt_tgt_avl_compare, sizeof (pppt_tgt_t), 526 offsetof(pppt_tgt_t, target_global_ln)); 527 528 PPPT_GLOBAL_LOCK(); 529 for (tgt = avl_first(&pppt_global.global_target_list); 530 tgt != NULL; 531 tgt = next_tgt) { 532 next_tgt = AVL_NEXT(&pppt_global.global_target_list, tgt); 533 avl_remove(&pppt_global.global_target_list, tgt); 534 avl_add(&delete_target_list, tgt); 535 pppt_tgt_async_delete(tgt); 536 } 537 PPPT_GLOBAL_UNLOCK(); 538 539 for (tgt = avl_first(&delete_target_list); 540 tgt != NULL; 541 tgt = next_tgt) { 542 next_tgt = AVL_NEXT(&delete_target_list, tgt); 543 mutex_enter(&tgt->target_mutex); 544 while ((tgt->target_refcount > 0) || 545 (tgt->target_state != TS_DELETING)) { 546 cv_wait(&tgt->target_cv, &tgt->target_mutex); 547 } 548 mutex_exit(&tgt->target_mutex); 549 550 avl_remove(&delete_target_list, tgt); 551 pppt_tgt_destroy(tgt); 552 } 553 554 taskq_destroy(pppt_global.global_sess_taskq); 555 556 taskq_destroy(pppt_global.global_dispatch_taskq); 557 558 avl_destroy(&pppt_global.global_sess_list); 559 avl_destroy(&pppt_global.global_target_list); 560 561 (void) stmf_deregister_port_provider(pppt_global.global_pp); 562 563 stmf_free(pppt_global.global_dbuf_store); 564 pppt_global.global_dbuf_store = NULL; 565 566 stmf_free(pppt_global.global_pp); 567 pppt_global.global_pp = NULL; 568 } 569 570 /* 571 * STMF callbacks 572 */ 573 574 /*ARGSUSED*/ 575 static stmf_data_buf_t * 576 pppt_dbuf_alloc(scsi_task_t *task, uint32_t size, uint32_t *pminsize, 577 uint32_t flags) 578 { 579 stmf_data_buf_t *result; 580 pppt_buf_t *pbuf; 581 uint8_t *buf; 582 583 /* Get buffer */ 584 buf = kmem_alloc(size, KM_SLEEP); 585 586 /* 587 * Allocate stmf buf with private port provider section 588 * (pppt_buf_t) 589 */ 590 result = stmf_alloc(STMF_STRUCT_DATA_BUF, sizeof (pppt_buf_t), 0); 591 if (result != NULL) { 592 /* Fill in pppt_buf_t */ 593 pbuf = result->db_port_private; 594 pbuf->pbuf_stmf_buf = result; 595 pbuf->pbuf_is_immed = B_FALSE; 596 597 /* 598 * Fill in stmf_data_buf_t. DB_DONT CACHE tells 599 * stmf not to cache buffers but STMF doesn't do 600 * that yet so it's a no-op. Port providers like 601 * FC and SRP that have buffers associated with the 602 * target port would want to let STMF cache 603 * the buffers. Port providers like iSCSI would 604 * not want STMF to cache because the buffers are 605 * really associated with a connection, not an 606 * STMF target port so there is no way for STMF 607 * to cache the buffers effectively. These port 608 * providers should cache buffers internally if 609 * there is significant buffer setup overhead. 610 * 611 * And of course, since STMF doesn't do any internal 612 * caching right now anyway, all port providers should 613 * do what they can to minimize buffer setup overhead. 614 */ 615 result->db_flags = DB_DONT_CACHE; 616 result->db_buf_size = size; 617 result->db_data_size = size; 618 result->db_sglist_length = 1; 619 result->db_sglist[0].seg_addr = buf; 620 result->db_sglist[0].seg_length = size; 621 return (result); 622 } else { 623 /* 624 * Couldn't get the stmf_data_buf_t so free the 625 * buffer 626 */ 627 kmem_free(buf, size); 628 } 629 630 return (NULL); 631 } 632 633 /*ARGSUSED*/ 634 static void 635 pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf) 636 { 637 pppt_buf_t *pbuf = dbuf->db_port_private; 638 639 if (pbuf->pbuf_is_immed) { 640 stmf_ic_msg_free(pbuf->pbuf_immed_msg); 641 } else { 642 kmem_free(dbuf->db_sglist[0].seg_addr, 643 dbuf->db_sglist[0].seg_length); 644 stmf_free(dbuf); 645 } 646 } 647 648 /*ARGSUSED*/ 649 stmf_status_t 650 pppt_lport_xfer_data(scsi_task_t *task, stmf_data_buf_t *dbuf, 651 uint32_t ioflags) 652 { 653 pppt_task_t *pppt_task = task->task_port_private; 654 pppt_buf_t *pbuf = dbuf->db_port_private; 655 stmf_ic_msg_t *msg; 656 stmf_ic_msg_status_t ic_msg_status; 657 658 /* 659 * If we are aborting then we can ignore this request, otherwise 660 * add a reference. 661 */ 662 if (pppt_task_hold(pppt_task) != PPPT_STATUS_SUCCESS) { 663 return (STMF_SUCCESS); 664 } 665 666 /* 667 * If it's not immediate data then start the transfer 668 */ 669 ASSERT(pbuf->pbuf_is_immed == B_FALSE); 670 if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) { 671 672 /* Send read data */ 673 msg = stmf_ic_scsi_data_msg_alloc( 674 pppt_task->pt_task_id, 675 pppt_task->pt_sess->ps_session_id, 676 pppt_task->pt_lun_id, 677 dbuf->db_sglist[0].seg_length, 678 dbuf->db_sglist[0].seg_addr, 0); 679 680 pppt_task->pt_read_buf = pbuf; 681 pppt_task->pt_read_xfer_msgid = msg->icm_msgid; 682 683 ic_msg_status = stmf_ic_tx_msg(msg); 684 pppt_task_rele(pppt_task); 685 if (ic_msg_status != STMF_IC_MSG_SUCCESS) { 686 return (STMF_FAILURE); 687 } else { 688 return (STMF_SUCCESS); 689 } 690 } else if (dbuf->db_flags & DB_DIRECTION_FROM_RPORT) { 691 pppt_task_rele(pppt_task); 692 return (STMF_FAILURE); 693 } 694 695 pppt_task_rele(pppt_task); 696 697 return (STMF_INVALID_ARG); 698 } 699 700 void 701 pppt_xfer_read_complete(pppt_task_t *pppt_task, stmf_status_t status) 702 { 703 pppt_buf_t *pppt_buf; 704 stmf_data_buf_t *dbuf; 705 706 /* 707 * Caller should have taken a task hold (likely via pppt_task_lookup) 708 * 709 * Get pppt_buf_t and stmf_data_buf_t pointers 710 */ 711 pppt_buf = pppt_task->pt_read_buf; 712 dbuf = pppt_buf->pbuf_stmf_buf; 713 dbuf->db_xfer_status = (status == STMF_SUCCESS) ? 714 STMF_SUCCESS : STMF_FAILURE; 715 716 /* 717 * COMSTAR currently requires port providers to support 718 * the DB_SEND_STATUS_GOOD flag even if phase collapse is 719 * not supported. So we will roll our own... pretend we are 720 * COMSTAR and ask for a status message. 721 */ 722 if ((dbuf->db_flags & DB_SEND_STATUS_GOOD) && 723 (status == STMF_SUCCESS)) { 724 /* 725 * It's possible the task has been aborted since the time we 726 * looked it up. We need to release the hold before calling 727 * pppt_lport_send_status and as soon as we release the hold 728 * the task may disappear. Calling pppt_task_done allows us 729 * to determine whether the task has been aborted (in which 730 * case we will stop processing and return) and mark the task 731 * "done" which will prevent the task from being aborted while 732 * we are trying to send the status. 733 */ 734 if (pppt_task_done(pppt_task) != PPPT_STATUS_SUCCESS) { 735 /* STMF will free task and buffer(s) */ 736 pppt_task_rele(pppt_task); 737 return; 738 } 739 pppt_task_rele(pppt_task); 740 741 if (pppt_lport_send_status(pppt_task->pt_stmf_task, 0) 742 != STMF_SUCCESS) { 743 /* Failed to send status */ 744 dbuf->db_xfer_status = STMF_FAILURE; 745 stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf, 746 STMF_IOF_LPORT_DONE); 747 } 748 } else { 749 pppt_task_rele(pppt_task); 750 stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf, 0); 751 } 752 } 753 754 /*ARGSUSED*/ 755 stmf_status_t 756 pppt_lport_send_status(scsi_task_t *task, uint32_t ioflags) 757 { 758 pppt_task_t *ptask = task->task_port_private; 759 stmf_ic_msg_t *msg; 760 stmf_ic_msg_status_t ic_msg_status; 761 762 /* 763 * Mark task completed. If the state indicates it was aborted 764 * then we don't need to respond. 765 */ 766 if (pppt_task_done(ptask) == PPPT_STATUS_ABORTED) { 767 return (STMF_SUCCESS); 768 } 769 770 /* 771 * Send status. 772 */ 773 msg = stmf_ic_scsi_status_msg_alloc( 774 ptask->pt_task_id, 775 ptask->pt_sess->ps_session_id, 776 ptask->pt_lun_id, 777 0, 778 task->task_scsi_status, 779 task->task_status_ctrl, task->task_resid, 780 task->task_sense_length, task->task_sense_data, 0); 781 782 ic_msg_status = stmf_ic_tx_msg(msg); 783 784 if (ic_msg_status != STMF_IC_MSG_SUCCESS) { 785 pppt_task_sent_status(ptask); 786 stmf_send_status_done(ptask->pt_stmf_task, 787 STMF_FAILURE, STMF_IOF_LPORT_DONE); 788 return (STMF_FAILURE); 789 } else { 790 pppt_task_sent_status(ptask); 791 stmf_send_status_done(ptask->pt_stmf_task, 792 STMF_SUCCESS, STMF_IOF_LPORT_DONE); 793 return (STMF_SUCCESS); 794 } 795 } 796 797 void 798 pppt_lport_task_free(scsi_task_t *task) 799 { 800 pppt_task_t *ptask = task->task_port_private; 801 pppt_sess_t *ps = ptask->pt_sess; 802 803 pppt_task_free(ptask); 804 pppt_sess_rele(ps); 805 } 806 807 /*ARGSUSED*/ 808 stmf_status_t 809 pppt_lport_abort(stmf_local_port_t *lport, int abort_cmd, void *arg, 810 uint32_t flags) 811 { 812 scsi_task_t *st = (scsi_task_t *)arg; 813 pppt_task_t *ptask; 814 815 ptask = st->task_port_private; 816 817 if (pppt_task_try_abort(ptask) == PPPT_STATUS_DONE) { 818 /* 819 * This task is beyond the point where abort makes sense 820 * and we will soon be sending status. Tell STMF to 821 * go away. 822 */ 823 return (STMF_BUSY); 824 } else { 825 return (STMF_ABORT_SUCCESS); 826 } 827 /*NOTREACHED*/ 828 } 829 830 /*ARGSUSED*/ 831 void 832 pppt_lport_ctl(stmf_local_port_t *lport, int cmd, void *arg) 833 { 834 switch (cmd) { 835 case STMF_CMD_LPORT_ONLINE: 836 case STMF_CMD_LPORT_OFFLINE: 837 case STMF_ACK_LPORT_ONLINE_COMPLETE: 838 case STMF_ACK_LPORT_OFFLINE_COMPLETE: 839 pppt_tgt_sm_ctl(lport, cmd, arg); 840 break; 841 842 default: 843 ASSERT(0); 844 break; 845 } 846 } 847 848 pppt_sess_t * 849 pppt_sess_lookup_locked(uint64_t session_id, 850 scsi_devid_desc_t *lport_devid, 851 scsi_devid_desc_t *rport_devid) 852 { 853 pppt_tgt_t *tgt; 854 pppt_sess_t *ps; 855 int lport_cmp; 856 int rport_cmp; 857 858 ASSERT(mutex_owned(&pppt_global.global_lock)); 859 860 /* 861 * Look for existing session for this ID 862 */ 863 ps = pppt_sess_lookup_by_id_locked(session_id); 864 if (ps == NULL) { 865 PPPT_INC_STAT(es_sess_lookup_no_session); 866 return (NULL); 867 } 868 869 tgt = ps->ps_target; 870 871 mutex_enter(&tgt->target_mutex); 872 873 /* Validate local/remote port names */ 874 if ((lport_devid->ident_length != 875 tgt->target_stmf_lport->lport_id->ident_length) || 876 (rport_devid->ident_length != 877 ps->ps_stmf_sess->ss_rport_id->ident_length)) { 878 mutex_exit(&tgt->target_mutex); 879 PPPT_INC_STAT(es_sess_lookup_ident_mismatch); 880 return (NULL); 881 } else { 882 lport_cmp = bcmp(lport_devid->ident, 883 tgt->target_stmf_lport->lport_id->ident, 884 lport_devid->ident_length); 885 rport_cmp = bcmp(rport_devid->ident, 886 ps->ps_stmf_sess->ss_rport_id->ident, 887 rport_devid->ident_length); 888 if (lport_cmp || rport_cmp) { 889 mutex_exit(&tgt->target_mutex); 890 PPPT_INC_STAT(es_sess_lookup_ident_mismatch); 891 return (NULL); 892 } 893 894 if (tgt->target_state != TS_STMF_ONLINE) { 895 mutex_exit(&tgt->target_mutex); 896 PPPT_INC_STAT(es_sess_lookup_bad_tgt_state); 897 return (NULL); 898 } 899 } 900 mutex_exit(&tgt->target_mutex); 901 902 return (ps); 903 } 904 905 pppt_sess_t * 906 pppt_sess_lookup_by_id_locked(uint64_t session_id) 907 { 908 pppt_sess_t tmp_ps; 909 pppt_sess_t *ps; 910 911 ASSERT(mutex_owned(&pppt_global.global_lock)); 912 tmp_ps.ps_session_id = session_id; 913 tmp_ps.ps_closed = 0; 914 ps = avl_find(&pppt_global.global_sess_list, &tmp_ps, NULL); 915 if (ps != NULL) { 916 mutex_enter(&ps->ps_mutex); 917 if (!ps->ps_closed) { 918 ps->ps_refcnt++; 919 mutex_exit(&ps->ps_mutex); 920 return (ps); 921 } 922 mutex_exit(&ps->ps_mutex); 923 } 924 925 return (NULL); 926 } 927 928 /* New session */ 929 pppt_sess_t * 930 pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid, 931 scsi_devid_desc_t *rport_devid, uint64_t session_id, 932 stmf_status_t *statusp) 933 { 934 pppt_tgt_t *tgt; 935 pppt_sess_t *ps; 936 stmf_scsi_session_t *ss; 937 pppt_sess_t tmp_ps; 938 stmf_scsi_session_t tmp_ss; 939 *statusp = STMF_SUCCESS; 940 941 PPPT_GLOBAL_LOCK(); 942 943 /* 944 * Look for existing session for this ID 945 */ 946 ps = pppt_sess_lookup_locked(session_id, lport_devid, rport_devid); 947 948 if (ps != NULL) { 949 PPPT_GLOBAL_UNLOCK(); 950 return (ps); 951 } 952 953 /* 954 * No session with that ID, look for another session corresponding 955 * to the same IT nexus. 956 */ 957 tgt = pppt_tgt_lookup_locked(lport_devid); 958 if (tgt == NULL) { 959 *statusp = STMF_NOT_FOUND; 960 PPPT_GLOBAL_UNLOCK(); 961 return (NULL); 962 } 963 964 mutex_enter(&tgt->target_mutex); 965 if (tgt->target_state != TS_STMF_ONLINE) { 966 *statusp = STMF_NOT_FOUND; 967 mutex_exit(&tgt->target_mutex); 968 PPPT_GLOBAL_UNLOCK(); 969 /* Can't create session to offline target */ 970 return (NULL); 971 } 972 973 bzero(&tmp_ps, sizeof (tmp_ps)); 974 bzero(&tmp_ss, sizeof (tmp_ss)); 975 tmp_ps.ps_stmf_sess = &tmp_ss; 976 tmp_ss.ss_rport_id = rport_devid; 977 978 /* 979 * Look for an existing session on this IT nexus 980 */ 981 ps = avl_find(&tgt->target_sess_list, &tmp_ps, NULL); 982 983 if (ps != NULL) { 984 /* 985 * Now check the session ID. It should not match because if 986 * it did we would have found it on the global session list. 987 * If the session ID in the command is higher than the existing 988 * session ID then we need to tear down the existing session. 989 */ 990 mutex_enter(&ps->ps_mutex); 991 ASSERT(ps->ps_session_id != session_id); 992 if (ps->ps_session_id > session_id) { 993 /* Invalid session ID */ 994 mutex_exit(&ps->ps_mutex); 995 mutex_exit(&tgt->target_mutex); 996 PPPT_GLOBAL_UNLOCK(); 997 *statusp = STMF_INVALID_ARG; 998 return (NULL); 999 } else { 1000 /* Existing session needs to be invalidated */ 1001 if (!ps->ps_closed) { 1002 pppt_sess_close_locked(ps); 1003 } 1004 } 1005 mutex_exit(&ps->ps_mutex); 1006 1007 /* Fallthrough and create new session */ 1008 } 1009 1010 /* 1011 * Allocate and fill in pppt_session_t with the appropriate data 1012 * for the protocol. 1013 */ 1014 ps = kmem_zalloc(sizeof (*ps), KM_SLEEP); 1015 1016 /* Fill in session fields */ 1017 ps->ps_target = tgt; 1018 ps->ps_session_id = session_id; 1019 1020 ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0, 1021 0); 1022 if (ss == NULL) { 1023 mutex_exit(&tgt->target_mutex); 1024 PPPT_GLOBAL_UNLOCK(); 1025 kmem_free(ps, sizeof (*ps)); 1026 *statusp = STMF_ALLOC_FAILURE; 1027 return (NULL); 1028 } 1029 1030 ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) + 1031 rport_devid->ident_length + 1, KM_SLEEP); 1032 bcopy(rport_devid, ss->ss_rport_id, 1033 sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1); 1034 1035 ss->ss_lport = tgt->target_stmf_lport; 1036 1037 if (stmf_register_scsi_session(tgt->target_stmf_lport, ss) != 1038 STMF_SUCCESS) { 1039 mutex_exit(&tgt->target_mutex); 1040 PPPT_GLOBAL_UNLOCK(); 1041 kmem_free(ss->ss_rport_id, 1042 sizeof (scsi_devid_desc_t) + 1043 rport_devid->ident_length + 1); 1044 stmf_free(ss); 1045 kmem_free(ps, sizeof (*ps)); 1046 *statusp = STMF_TARGET_FAILURE; 1047 return (NULL); 1048 } 1049 1050 ss->ss_port_private = ps; 1051 mutex_init(&ps->ps_mutex, NULL, MUTEX_DEFAULT, NULL); 1052 cv_init(&ps->ps_cv, NULL, CV_DEFAULT, NULL); 1053 avl_create(&ps->ps_task_list, pppt_task_avl_compare, 1054 sizeof (pppt_task_t), offsetof(pppt_task_t, pt_sess_ln)); 1055 ps->ps_refcnt = 1; 1056 ps->ps_stmf_sess = ss; 1057 avl_add(&tgt->target_sess_list, ps); 1058 avl_add(&pppt_global.global_sess_list, ps); 1059 mutex_exit(&tgt->target_mutex); 1060 PPPT_GLOBAL_UNLOCK(); 1061 PPPT_LOG(CE_NOTE, "New session %p", (void *)ps); 1062 1063 return (ps); 1064 } 1065 1066 void 1067 pppt_sess_rele(pppt_sess_t *ps) 1068 { 1069 mutex_enter(&ps->ps_mutex); 1070 pppt_sess_rele_locked(ps); 1071 mutex_exit(&ps->ps_mutex); 1072 } 1073 1074 void 1075 pppt_sess_rele_locked(pppt_sess_t *ps) 1076 { 1077 ASSERT(mutex_owned(&ps->ps_mutex)); 1078 ps->ps_refcnt--; 1079 if (ps->ps_refcnt == 0) { 1080 cv_signal(&ps->ps_cv); 1081 } 1082 } 1083 1084 static void pppt_sess_destroy_task(void *ps_void) 1085 { 1086 pppt_sess_t *ps = ps_void; 1087 stmf_scsi_session_t *ss; 1088 1089 PPPT_LOG(CE_NOTE, "Session destroy task %p", (void *)ps); 1090 1091 ss = ps->ps_stmf_sess; 1092 mutex_enter(&ps->ps_mutex); 1093 stmf_deregister_scsi_session(ss->ss_lport, ss); 1094 kmem_free(ss->ss_rport_id, 1095 sizeof (scsi_devid_desc_t) + 1096 ss->ss_rport_id->ident_length + 1); 1097 avl_destroy(&ps->ps_task_list); 1098 mutex_exit(&ps->ps_mutex); 1099 cv_destroy(&ps->ps_cv); 1100 mutex_destroy(&ps->ps_mutex); 1101 stmf_free(ps->ps_stmf_sess); 1102 kmem_free(ps, sizeof (*ps)); 1103 1104 PPPT_LOG(CE_NOTE, "Session destroy task complete %p", (void *)ps); 1105 } 1106 1107 int 1108 pppt_sess_avl_compare_by_id(const void *void_sess1, const void *void_sess2) 1109 { 1110 const pppt_sess_t *psess1 = void_sess1; 1111 const pppt_sess_t *psess2 = void_sess2; 1112 1113 if (psess1->ps_session_id < psess2->ps_session_id) 1114 return (-1); 1115 else if (psess1->ps_session_id > psess2->ps_session_id) 1116 return (1); 1117 1118 /* Allow multiple duplicate sessions if one is closed */ 1119 ASSERT(!(psess1->ps_closed && psess2->ps_closed)); 1120 if (psess1->ps_closed) 1121 return (-1); 1122 else if (psess2->ps_closed) 1123 return (1); 1124 1125 return (0); 1126 } 1127 1128 int 1129 pppt_sess_avl_compare_by_name(const void *void_sess1, const void *void_sess2) 1130 { 1131 const pppt_sess_t *psess1 = void_sess1; 1132 const pppt_sess_t *psess2 = void_sess2; 1133 int result; 1134 1135 /* Sort by code set then ident */ 1136 if (psess1->ps_stmf_sess->ss_rport_id->code_set < 1137 psess2->ps_stmf_sess->ss_rport_id->code_set) { 1138 return (-1); 1139 } else if (psess1->ps_stmf_sess->ss_rport_id->code_set > 1140 psess2->ps_stmf_sess->ss_rport_id->code_set) { 1141 return (1); 1142 } 1143 1144 /* Next by ident length */ 1145 if (psess1->ps_stmf_sess->ss_rport_id->ident_length < 1146 psess2->ps_stmf_sess->ss_rport_id->ident_length) { 1147 return (-1); 1148 } else if (psess1->ps_stmf_sess->ss_rport_id->ident_length > 1149 psess2->ps_stmf_sess->ss_rport_id->ident_length) { 1150 return (1); 1151 } 1152 1153 /* Code set and ident length both match, now compare idents */ 1154 result = memcmp(psess1->ps_stmf_sess->ss_rport_id->ident, 1155 psess2->ps_stmf_sess->ss_rport_id->ident, 1156 psess1->ps_stmf_sess->ss_rport_id->ident_length); 1157 1158 if (result < 0) { 1159 return (-1); 1160 } else if (result > 0) { 1161 return (1); 1162 } 1163 1164 return (0); 1165 } 1166 1167 void 1168 pppt_sess_close_locked(pppt_sess_t *ps) 1169 { 1170 pppt_tgt_t *tgt = ps->ps_target; 1171 pppt_task_t *ptask; 1172 1173 PPPT_LOG(CE_NOTE, "Session close %p", (void *)ps); 1174 1175 ASSERT(mutex_owned(&pppt_global.global_lock)); 1176 ASSERT(mutex_owned(&tgt->target_mutex)); 1177 ASSERT(mutex_owned(&ps->ps_mutex)); 1178 ASSERT(!ps->ps_closed); /* Caller should ensure session is not closed */ 1179 1180 ps->ps_closed = B_TRUE; 1181 for (ptask = avl_first(&ps->ps_task_list); ptask != NULL; 1182 ptask = AVL_NEXT(&ps->ps_task_list, ptask)) { 1183 mutex_enter(&ptask->pt_mutex); 1184 if (ptask->pt_state == PTS_ACTIVE) { 1185 stmf_abort(STMF_QUEUE_TASK_ABORT, ptask->pt_stmf_task, 1186 STMF_ABORTED, NULL); 1187 } 1188 mutex_exit(&ptask->pt_mutex); 1189 } 1190 1191 /* 1192 * Now that all the tasks are aborting the session refcnt should 1193 * go to 0. 1194 */ 1195 while (ps->ps_refcnt != 0) { 1196 cv_wait(&ps->ps_cv, &ps->ps_mutex); 1197 } 1198 1199 avl_remove(&tgt->target_sess_list, ps); 1200 avl_remove(&pppt_global.global_sess_list, ps); 1201 (void) taskq_dispatch(pppt_global.global_sess_taskq, 1202 &pppt_sess_destroy_task, ps, KM_SLEEP); 1203 1204 PPPT_LOG(CE_NOTE, "Session close complete %p", (void *)ps); 1205 } 1206 1207 pppt_task_t * 1208 pppt_task_alloc(void) 1209 { 1210 pppt_task_t *ptask; 1211 pppt_buf_t *immed_pbuf; 1212 1213 ptask = kmem_alloc(sizeof (pppt_task_t) + sizeof (pppt_buf_t) + 1214 sizeof (stmf_data_buf_t), KM_NOSLEEP); 1215 if (ptask != NULL) { 1216 ptask->pt_state = PTS_INIT; 1217 ptask->pt_read_buf = NULL; 1218 ptask->pt_read_xfer_msgid = 0; 1219 cv_init(&ptask->pt_cv, NULL, CV_DRIVER, NULL); 1220 mutex_init(&ptask->pt_mutex, NULL, MUTEX_DRIVER, NULL); 1221 immed_pbuf = (pppt_buf_t *)(ptask + 1); 1222 bzero(immed_pbuf, sizeof (*immed_pbuf)); 1223 immed_pbuf->pbuf_is_immed = B_TRUE; 1224 immed_pbuf->pbuf_stmf_buf = (stmf_data_buf_t *)(immed_pbuf + 1); 1225 1226 bzero(immed_pbuf->pbuf_stmf_buf, sizeof (stmf_data_buf_t)); 1227 immed_pbuf->pbuf_stmf_buf->db_port_private = immed_pbuf; 1228 immed_pbuf->pbuf_stmf_buf->db_sglist_length = 1; 1229 immed_pbuf->pbuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT | 1230 DB_DONT_CACHE; 1231 ptask->pt_immed_data = immed_pbuf; 1232 } 1233 1234 return (ptask); 1235 1236 } 1237 1238 void 1239 pppt_task_free(pppt_task_t *ptask) 1240 { 1241 mutex_enter(&ptask->pt_mutex); 1242 mutex_destroy(&ptask->pt_mutex); 1243 cv_destroy(&ptask->pt_cv); 1244 kmem_free(ptask, sizeof (pppt_task_t) + sizeof (pppt_buf_t) + 1245 sizeof (stmf_data_buf_t)); 1246 } 1247 1248 pppt_status_t 1249 pppt_task_start(pppt_task_t *ptask) 1250 { 1251 avl_index_t where; 1252 1253 ASSERT(ptask->pt_state == PTS_INIT); 1254 1255 mutex_enter(&ptask->pt_sess->ps_mutex); 1256 mutex_enter(&ptask->pt_mutex); 1257 if (avl_find(&ptask->pt_sess->ps_task_list, ptask, &where) == NULL) { 1258 pppt_task_update_state(ptask, PTS_ACTIVE); 1259 avl_insert(&ptask->pt_sess->ps_task_list, ptask, where); 1260 mutex_exit(&ptask->pt_mutex); 1261 mutex_exit(&ptask->pt_sess->ps_mutex); 1262 return (PPPT_STATUS_SUCCESS); 1263 } 1264 mutex_exit(&ptask->pt_mutex); 1265 mutex_exit(&ptask->pt_sess->ps_mutex); 1266 1267 return (PPPT_STATUS_FAIL); 1268 } 1269 1270 pppt_status_t 1271 pppt_task_done(pppt_task_t *ptask) 1272 { 1273 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1274 boolean_t remove = B_FALSE; 1275 1276 mutex_enter(&ptask->pt_mutex); 1277 1278 switch (ptask->pt_state) { 1279 case PTS_ACTIVE: 1280 remove = B_TRUE; 1281 pppt_task_update_state(ptask, PTS_DONE); 1282 break; 1283 case PTS_ABORTED: 1284 pppt_status = PPPT_STATUS_ABORTED; 1285 break; 1286 case PTS_DONE: 1287 /* Repeat calls are OK. Do nothing, return success */ 1288 break; 1289 default: 1290 ASSERT(0); 1291 } 1292 1293 mutex_exit(&ptask->pt_mutex); 1294 1295 if (remove) { 1296 mutex_enter(&ptask->pt_sess->ps_mutex); 1297 avl_remove(&ptask->pt_sess->ps_task_list, ptask); 1298 mutex_exit(&ptask->pt_sess->ps_mutex); 1299 } 1300 1301 return (pppt_status); 1302 } 1303 1304 void 1305 pppt_task_sent_status(pppt_task_t *ptask) 1306 { 1307 /* 1308 * If STMF tries to abort a task after the task state changed to 1309 * PTS_DONE (meaning all task processing is complete from 1310 * the port provider perspective) then we return STMF_BUSY 1311 * from pppt_lport_abort. STMF will return after a short interval 1312 * but our calls to stmf_send_status_done will be ignored since 1313 * STMF is aborting the task. That's where this state comes in. 1314 * This state essentially says we are calling stmf_send_status_done 1315 * so we will not be touching the task again. The next time 1316 * STMF calls pppt_lport_abort we will return a success full 1317 * status and the abort will succeed. 1318 */ 1319 mutex_enter(&ptask->pt_mutex); 1320 pppt_task_update_state(ptask, PTS_SENT_STATUS); 1321 mutex_exit(&ptask->pt_mutex); 1322 } 1323 1324 pppt_task_t * 1325 pppt_task_lookup(stmf_ic_msgid_t msgid) 1326 { 1327 pppt_tgt_t *tgt; 1328 pppt_sess_t *sess; 1329 pppt_task_t lookup_task; 1330 pppt_task_t *result; 1331 1332 bzero(&lookup_task, sizeof (lookup_task)); 1333 lookup_task.pt_task_id = msgid; 1334 PPPT_GLOBAL_LOCK(); 1335 for (tgt = avl_first(&pppt_global.global_target_list); tgt != NULL; 1336 tgt = AVL_NEXT(&pppt_global.global_target_list, tgt)) { 1337 1338 mutex_enter(&tgt->target_mutex); 1339 for (sess = avl_first(&tgt->target_sess_list); sess != NULL; 1340 sess = AVL_NEXT(&tgt->target_sess_list, sess)) { 1341 mutex_enter(&sess->ps_mutex); 1342 if ((result = avl_find(&sess->ps_task_list, 1343 &lookup_task, NULL)) != NULL) { 1344 if (pppt_task_hold(result) != 1345 PPPT_STATUS_SUCCESS) { 1346 result = NULL; 1347 } 1348 mutex_exit(&sess->ps_mutex); 1349 mutex_exit(&tgt->target_mutex); 1350 PPPT_GLOBAL_UNLOCK(); 1351 return (result); 1352 } 1353 mutex_exit(&sess->ps_mutex); 1354 } 1355 mutex_exit(&tgt->target_mutex); 1356 } 1357 PPPT_GLOBAL_UNLOCK(); 1358 1359 return (NULL); 1360 } 1361 1362 static int 1363 pppt_task_avl_compare(const void *void_task1, const void *void_task2) 1364 { 1365 const pppt_task_t *ptask1 = void_task1; 1366 const pppt_task_t *ptask2 = void_task2; 1367 1368 if (ptask1->pt_task_id < ptask2->pt_task_id) 1369 return (-1); 1370 else if (ptask1->pt_task_id > ptask2->pt_task_id) 1371 return (1); 1372 1373 return (0); 1374 } 1375 1376 static pppt_status_t 1377 pppt_task_try_abort(pppt_task_t *ptask) 1378 { 1379 boolean_t remove = B_FALSE; 1380 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1381 1382 mutex_enter(&ptask->pt_mutex); 1383 1384 switch (ptask->pt_state) { 1385 case PTS_ACTIVE: 1386 remove = B_TRUE; 1387 pppt_task_update_state(ptask, PTS_ABORTED); 1388 break; 1389 case PTS_DONE: 1390 pppt_status = PPPT_STATUS_DONE; 1391 break; 1392 case PTS_SENT_STATUS: 1393 /* 1394 * Already removed so leave remove set to B_FALSE 1395 * and leave status set to PPPT_STATUS_SUCCESS. 1396 */ 1397 pppt_task_update_state(ptask, PTS_ABORTED); 1398 break; 1399 case PTS_ABORTED: 1400 break; 1401 default: 1402 ASSERT(0); 1403 } 1404 1405 mutex_exit(&ptask->pt_mutex); 1406 1407 if (remove) { 1408 mutex_enter(&ptask->pt_sess->ps_mutex); 1409 avl_remove(&ptask->pt_sess->ps_task_list, ptask); 1410 mutex_exit(&ptask->pt_sess->ps_mutex); 1411 } 1412 1413 return (pppt_status); 1414 } 1415 1416 static pppt_status_t 1417 pppt_task_hold(pppt_task_t *ptask) 1418 { 1419 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1420 1421 mutex_enter(&ptask->pt_mutex); 1422 if (ptask->pt_state == PTS_ACTIVE) { 1423 ptask->pt_refcnt++; 1424 } else { 1425 pppt_status = PPPT_STATUS_FAIL; 1426 } 1427 mutex_exit(&ptask->pt_mutex); 1428 1429 return (pppt_status); 1430 } 1431 1432 static void 1433 pppt_task_rele(pppt_task_t *ptask) 1434 { 1435 mutex_enter(&ptask->pt_mutex); 1436 ptask->pt_refcnt--; 1437 cv_signal(&ptask->pt_cv); 1438 mutex_exit(&ptask->pt_mutex); 1439 } 1440 1441 static void 1442 pppt_task_update_state(pppt_task_t *ptask, 1443 pppt_task_state_t new_state) 1444 { 1445 PPPT_LOG(CE_NOTE, "task %p %d -> %d", (void *)ptask, 1446 ptask->pt_state, new_state); 1447 1448 ASSERT(mutex_owned(&ptask->pt_mutex)); 1449 ptask->pt_state = new_state; 1450 cv_signal(&ptask->pt_cv); 1451 } 1452