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