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 * Copyright 2013, Nexenta Systems, Inc. All rights reserved. 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 #include <sys/sdt.h> 38 39 #include <sys/stmf.h> 40 #include <sys/stmf_ioctl.h> 41 #include <sys/pppt_ioctl.h> 42 #include <sys/portif.h> 43 44 #include "pppt.h" 45 46 #define PPPT_VERSION BUILD_DATE "-1.18dev" 47 #define PPPT_NAME_VERSION "COMSTAR PPPT v" PPPT_VERSION 48 49 /* 50 * DDI entry points. 51 */ 52 static int pppt_drv_attach(dev_info_t *, ddi_attach_cmd_t); 53 static int pppt_drv_detach(dev_info_t *, ddi_detach_cmd_t); 54 static int pppt_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 55 static int pppt_drv_open(dev_t *, int, int, cred_t *); 56 static int pppt_drv_close(dev_t, int, int, cred_t *); 57 static boolean_t pppt_drv_busy(void); 58 static int pppt_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 59 60 extern pppt_status_t pppt_ic_so_enable(boolean_t); 61 extern void pppt_ic_so_disable(); 62 extern void stmf_ic_rx_msg(char *, size_t); 63 64 extern struct mod_ops mod_miscops; 65 66 static struct cb_ops pppt_cb_ops = { 67 pppt_drv_open, /* cb_open */ 68 pppt_drv_close, /* cb_close */ 69 nodev, /* cb_strategy */ 70 nodev, /* cb_print */ 71 nodev, /* cb_dump */ 72 nodev, /* cb_read */ 73 nodev, /* cb_write */ 74 pppt_drv_ioctl, /* cb_ioctl */ 75 nodev, /* cb_devmap */ 76 nodev, /* cb_mmap */ 77 nodev, /* cb_segmap */ 78 nochpoll, /* cb_chpoll */ 79 ddi_prop_op, /* cb_prop_op */ 80 NULL, /* cb_streamtab */ 81 D_MP, /* cb_flag */ 82 CB_REV, /* cb_rev */ 83 nodev, /* cb_aread */ 84 nodev, /* cb_awrite */ 85 }; 86 87 static struct dev_ops pppt_dev_ops = { 88 DEVO_REV, /* devo_rev */ 89 0, /* devo_refcnt */ 90 pppt_drv_getinfo, /* devo_getinfo */ 91 nulldev, /* devo_identify */ 92 nulldev, /* devo_probe */ 93 pppt_drv_attach, /* devo_attach */ 94 pppt_drv_detach, /* devo_detach */ 95 nodev, /* devo_reset */ 96 &pppt_cb_ops, /* devo_cb_ops */ 97 NULL, /* devo_bus_ops */ 98 NULL, /* devo_power */ 99 ddi_quiesce_not_needed, /* quiesce */ 100 }; 101 102 static struct modldrv modldrv = { 103 &mod_driverops, 104 "Proxy Port Provider", 105 &pppt_dev_ops, 106 }; 107 108 static struct modlinkage modlinkage = { 109 MODREV_1, 110 &modldrv, 111 NULL, 112 }; 113 114 pppt_global_t pppt_global; 115 116 int pppt_logging = 0; 117 118 static int pppt_enable_svc(void); 119 120 static void pppt_disable_svc(void); 121 122 static int pppt_task_avl_compare(const void *tgt1, const void *tgt2); 123 124 static stmf_data_buf_t *pppt_dbuf_alloc(scsi_task_t *task, 125 uint32_t size, uint32_t *pminsize, uint32_t flags); 126 127 static void pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf); 128 129 static void pppt_sess_destroy_task(void *ps_void); 130 131 static void pppt_task_sent_status(pppt_task_t *ptask); 132 133 static pppt_status_t pppt_task_try_abort(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_rele(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, stmf_remote_port_t *rport) 851 { 852 pppt_tgt_t *tgt; 853 pppt_sess_t *ps; 854 int lport_cmp; 855 856 ASSERT(mutex_owned(&pppt_global.global_lock)); 857 858 /* 859 * Look for existing session for this ID 860 */ 861 ps = pppt_sess_lookup_by_id_locked(session_id); 862 if (ps == NULL) { 863 PPPT_INC_STAT(es_sess_lookup_no_session); 864 return (NULL); 865 } 866 867 tgt = ps->ps_target; 868 869 mutex_enter(&tgt->target_mutex); 870 871 /* Validate local/remote port names */ 872 if ((lport_devid->ident_length != 873 tgt->target_stmf_lport->lport_id->ident_length) || 874 (rport->rport_tptid_sz != 875 ps->ps_stmf_sess->ss_rport->rport_tptid_sz)) { 876 mutex_exit(&tgt->target_mutex); 877 PPPT_INC_STAT(es_sess_lookup_ident_mismatch); 878 return (NULL); 879 } else { 880 lport_cmp = bcmp(lport_devid->ident, 881 tgt->target_stmf_lport->lport_id->ident, 882 lport_devid->ident_length); 883 if (lport_cmp != 0 || 884 (stmf_scsilib_tptid_compare(rport->rport_tptid, 885 ps->ps_stmf_sess->ss_rport->rport_tptid) != B_TRUE)) { 886 mutex_exit(&tgt->target_mutex); 887 PPPT_INC_STAT(es_sess_lookup_ident_mismatch); 888 return (NULL); 889 } 890 891 if (tgt->target_state != TS_STMF_ONLINE) { 892 mutex_exit(&tgt->target_mutex); 893 PPPT_INC_STAT(es_sess_lookup_bad_tgt_state); 894 return (NULL); 895 } 896 } 897 mutex_exit(&tgt->target_mutex); 898 899 return (ps); 900 } 901 902 pppt_sess_t * 903 pppt_sess_lookup_by_id_locked(uint64_t session_id) 904 { 905 pppt_sess_t tmp_ps; 906 pppt_sess_t *ps; 907 908 ASSERT(mutex_owned(&pppt_global.global_lock)); 909 tmp_ps.ps_session_id = session_id; 910 tmp_ps.ps_closed = 0; 911 ps = avl_find(&pppt_global.global_sess_list, &tmp_ps, NULL); 912 if (ps != NULL) { 913 mutex_enter(&ps->ps_mutex); 914 if (!ps->ps_closed) { 915 ps->ps_refcnt++; 916 mutex_exit(&ps->ps_mutex); 917 return (ps); 918 } 919 mutex_exit(&ps->ps_mutex); 920 } 921 922 return (NULL); 923 } 924 925 /* New session */ 926 pppt_sess_t * 927 pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid, 928 scsi_devid_desc_t *rport_devid, stmf_remote_port_t *rport, 929 uint64_t session_id, stmf_status_t *statusp) 930 { 931 pppt_tgt_t *tgt; 932 pppt_sess_t *ps; 933 stmf_scsi_session_t *ss; 934 pppt_sess_t tmp_ps; 935 stmf_scsi_session_t tmp_ss; 936 *statusp = STMF_SUCCESS; 937 938 PPPT_GLOBAL_LOCK(); 939 940 /* 941 * Look for existing session for this ID 942 */ 943 ps = pppt_sess_lookup_locked(session_id, lport_devid, rport); 944 945 if (ps != NULL) { 946 PPPT_GLOBAL_UNLOCK(); 947 return (ps); 948 } 949 950 /* 951 * No session with that ID, look for another session corresponding 952 * to the same IT nexus. 953 */ 954 tgt = pppt_tgt_lookup_locked(lport_devid); 955 if (tgt == NULL) { 956 *statusp = STMF_NOT_FOUND; 957 PPPT_GLOBAL_UNLOCK(); 958 return (NULL); 959 } 960 961 mutex_enter(&tgt->target_mutex); 962 if (tgt->target_state != TS_STMF_ONLINE) { 963 *statusp = STMF_NOT_FOUND; 964 mutex_exit(&tgt->target_mutex); 965 PPPT_GLOBAL_UNLOCK(); 966 /* Can't create session to offline target */ 967 return (NULL); 968 } 969 970 bzero(&tmp_ps, sizeof (tmp_ps)); 971 bzero(&tmp_ss, sizeof (tmp_ss)); 972 tmp_ps.ps_stmf_sess = &tmp_ss; 973 tmp_ss.ss_rport = rport; 974 975 /* 976 * Look for an existing session on this IT nexus 977 */ 978 ps = avl_find(&tgt->target_sess_list, &tmp_ps, NULL); 979 980 if (ps != NULL) { 981 /* 982 * Now check the session ID. It should not match because if 983 * it did we would have found it on the global session list. 984 * If the session ID in the command is higher than the existing 985 * session ID then we need to tear down the existing session. 986 */ 987 mutex_enter(&ps->ps_mutex); 988 ASSERT(ps->ps_session_id != session_id); 989 if (ps->ps_session_id > session_id) { 990 /* Invalid session ID */ 991 mutex_exit(&ps->ps_mutex); 992 mutex_exit(&tgt->target_mutex); 993 PPPT_GLOBAL_UNLOCK(); 994 *statusp = STMF_INVALID_ARG; 995 return (NULL); 996 } else { 997 /* Existing session needs to be invalidated */ 998 if (!ps->ps_closed) { 999 pppt_sess_close_locked(ps); 1000 } 1001 } 1002 mutex_exit(&ps->ps_mutex); 1003 1004 /* Fallthrough and create new session */ 1005 } 1006 1007 /* 1008 * Allocate and fill in pppt_session_t with the appropriate data 1009 * for the protocol. 1010 */ 1011 ps = kmem_zalloc(sizeof (*ps), KM_SLEEP); 1012 1013 /* Fill in session fields */ 1014 ps->ps_target = tgt; 1015 ps->ps_session_id = session_id; 1016 1017 ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0, 1018 0); 1019 if (ss == NULL) { 1020 mutex_exit(&tgt->target_mutex); 1021 PPPT_GLOBAL_UNLOCK(); 1022 kmem_free(ps, sizeof (*ps)); 1023 *statusp = STMF_ALLOC_FAILURE; 1024 return (NULL); 1025 } 1026 1027 ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) + 1028 rport_devid->ident_length + 1, KM_SLEEP); 1029 bcopy(rport_devid, ss->ss_rport_id, 1030 sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1); 1031 1032 ss->ss_lport = tgt->target_stmf_lport; 1033 1034 ss->ss_rport = stmf_remote_port_alloc(rport->rport_tptid_sz); 1035 bcopy(rport->rport_tptid, ss->ss_rport->rport_tptid, 1036 rport->rport_tptid_sz); 1037 1038 if (stmf_register_scsi_session(tgt->target_stmf_lport, ss) != 1039 STMF_SUCCESS) { 1040 mutex_exit(&tgt->target_mutex); 1041 PPPT_GLOBAL_UNLOCK(); 1042 kmem_free(ss->ss_rport_id, 1043 sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1); 1044 stmf_remote_port_free(ss->ss_rport); 1045 stmf_free(ss); 1046 kmem_free(ps, sizeof (*ps)); 1047 *statusp = STMF_TARGET_FAILURE; 1048 return (NULL); 1049 } 1050 1051 ss->ss_port_private = ps; 1052 mutex_init(&ps->ps_mutex, NULL, MUTEX_DEFAULT, NULL); 1053 cv_init(&ps->ps_cv, NULL, CV_DEFAULT, NULL); 1054 avl_create(&ps->ps_task_list, pppt_task_avl_compare, 1055 sizeof (pppt_task_t), offsetof(pppt_task_t, pt_sess_ln)); 1056 ps->ps_refcnt = 1; 1057 ps->ps_stmf_sess = ss; 1058 avl_add(&tgt->target_sess_list, ps); 1059 avl_add(&pppt_global.global_sess_list, ps); 1060 mutex_exit(&tgt->target_mutex); 1061 PPPT_GLOBAL_UNLOCK(); 1062 stmf_trace("pppt", "New session %p", (void *)ps); 1063 1064 return (ps); 1065 } 1066 1067 void 1068 pppt_sess_rele(pppt_sess_t *ps) 1069 { 1070 mutex_enter(&ps->ps_mutex); 1071 pppt_sess_rele_locked(ps); 1072 mutex_exit(&ps->ps_mutex); 1073 } 1074 1075 void 1076 pppt_sess_rele_locked(pppt_sess_t *ps) 1077 { 1078 ASSERT(mutex_owned(&ps->ps_mutex)); 1079 ps->ps_refcnt--; 1080 if (ps->ps_refcnt == 0) { 1081 cv_signal(&ps->ps_cv); 1082 } 1083 } 1084 1085 static void pppt_sess_destroy_task(void *ps_void) 1086 { 1087 pppt_sess_t *ps = ps_void; 1088 stmf_scsi_session_t *ss; 1089 1090 stmf_trace("pppt", "Session destroy task %p", (void *)ps); 1091 1092 ss = ps->ps_stmf_sess; 1093 mutex_enter(&ps->ps_mutex); 1094 stmf_deregister_scsi_session(ss->ss_lport, ss); 1095 kmem_free(ss->ss_rport_id, 1096 sizeof (scsi_devid_desc_t) + ss->ss_rport_id->ident_length + 1); 1097 stmf_remote_port_free(ss->ss_rport); 1098 avl_destroy(&ps->ps_task_list); 1099 mutex_exit(&ps->ps_mutex); 1100 cv_destroy(&ps->ps_cv); 1101 mutex_destroy(&ps->ps_mutex); 1102 stmf_free(ps->ps_stmf_sess); 1103 kmem_free(ps, sizeof (*ps)); 1104 1105 stmf_trace("pppt", "Session destroy task complete %p", (void *)ps); 1106 } 1107 1108 int 1109 pppt_sess_avl_compare_by_id(const void *void_sess1, const void *void_sess2) 1110 { 1111 const pppt_sess_t *psess1 = void_sess1; 1112 const pppt_sess_t *psess2 = void_sess2; 1113 1114 if (psess1->ps_session_id < psess2->ps_session_id) 1115 return (-1); 1116 else if (psess1->ps_session_id > psess2->ps_session_id) 1117 return (1); 1118 1119 /* Allow multiple duplicate sessions if one is closed */ 1120 ASSERT(!(psess1->ps_closed && psess2->ps_closed)); 1121 if (psess1->ps_closed) 1122 return (-1); 1123 else if (psess2->ps_closed) 1124 return (1); 1125 1126 return (0); 1127 } 1128 1129 int 1130 pppt_sess_avl_compare_by_name(const void *void_sess1, const void *void_sess2) 1131 { 1132 const pppt_sess_t *psess1 = void_sess1; 1133 const pppt_sess_t *psess2 = void_sess2; 1134 int result; 1135 1136 /* Compare by tptid size */ 1137 if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz < 1138 psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) { 1139 return (-1); 1140 } else if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz > 1141 psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) { 1142 return (1); 1143 } 1144 1145 /* Now compare tptid */ 1146 result = memcmp(psess1->ps_stmf_sess->ss_rport->rport_tptid, 1147 psess2->ps_stmf_sess->ss_rport->rport_tptid, 1148 psess1->ps_stmf_sess->ss_rport->rport_tptid_sz); 1149 1150 if (result < 0) { 1151 return (-1); 1152 } else if (result > 0) { 1153 return (1); 1154 } 1155 1156 return (0); 1157 } 1158 1159 void 1160 pppt_sess_close_locked(pppt_sess_t *ps) 1161 { 1162 pppt_tgt_t *tgt = ps->ps_target; 1163 pppt_task_t *ptask; 1164 1165 stmf_trace("pppt", "Session close %p", (void *)ps); 1166 1167 ASSERT(mutex_owned(&pppt_global.global_lock)); 1168 ASSERT(mutex_owned(&tgt->target_mutex)); 1169 ASSERT(mutex_owned(&ps->ps_mutex)); 1170 ASSERT(!ps->ps_closed); /* Caller should ensure session is not closed */ 1171 1172 ps->ps_closed = B_TRUE; 1173 for (ptask = avl_first(&ps->ps_task_list); ptask != NULL; 1174 ptask = AVL_NEXT(&ps->ps_task_list, ptask)) { 1175 mutex_enter(&ptask->pt_mutex); 1176 if (ptask->pt_state == PTS_ACTIVE) { 1177 stmf_abort(STMF_QUEUE_TASK_ABORT, ptask->pt_stmf_task, 1178 STMF_ABORTED, NULL); 1179 } 1180 mutex_exit(&ptask->pt_mutex); 1181 } 1182 1183 /* 1184 * Now that all the tasks are aborting the session refcnt should 1185 * go to 0. 1186 */ 1187 while (ps->ps_refcnt != 0) { 1188 cv_wait(&ps->ps_cv, &ps->ps_mutex); 1189 } 1190 1191 avl_remove(&tgt->target_sess_list, ps); 1192 avl_remove(&pppt_global.global_sess_list, ps); 1193 (void) taskq_dispatch(pppt_global.global_sess_taskq, 1194 &pppt_sess_destroy_task, ps, KM_SLEEP); 1195 1196 stmf_trace("pppt", "Session close complete %p", (void *)ps); 1197 } 1198 1199 pppt_task_t * 1200 pppt_task_alloc(void) 1201 { 1202 pppt_task_t *ptask; 1203 pppt_buf_t *immed_pbuf; 1204 1205 ptask = kmem_alloc(sizeof (pppt_task_t) + sizeof (pppt_buf_t) + 1206 sizeof (stmf_data_buf_t), KM_NOSLEEP); 1207 if (ptask != NULL) { 1208 ptask->pt_state = PTS_INIT; 1209 ptask->pt_read_buf = NULL; 1210 ptask->pt_read_xfer_msgid = 0; 1211 ptask->pt_refcnt = 0; 1212 mutex_init(&ptask->pt_mutex, NULL, MUTEX_DRIVER, NULL); 1213 immed_pbuf = (pppt_buf_t *)(ptask + 1); 1214 bzero(immed_pbuf, sizeof (*immed_pbuf)); 1215 immed_pbuf->pbuf_is_immed = B_TRUE; 1216 immed_pbuf->pbuf_stmf_buf = (stmf_data_buf_t *)(immed_pbuf + 1); 1217 1218 bzero(immed_pbuf->pbuf_stmf_buf, sizeof (stmf_data_buf_t)); 1219 immed_pbuf->pbuf_stmf_buf->db_port_private = immed_pbuf; 1220 immed_pbuf->pbuf_stmf_buf->db_sglist_length = 1; 1221 immed_pbuf->pbuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT | 1222 DB_DONT_CACHE; 1223 ptask->pt_immed_data = immed_pbuf; 1224 } 1225 1226 return (ptask); 1227 1228 } 1229 1230 void 1231 pppt_task_free(pppt_task_t *ptask) 1232 { 1233 mutex_enter(&ptask->pt_mutex); 1234 ASSERT(ptask->pt_refcnt == 0); 1235 mutex_destroy(&ptask->pt_mutex); 1236 kmem_free(ptask, sizeof (pppt_task_t) + sizeof (pppt_buf_t) + 1237 sizeof (stmf_data_buf_t)); 1238 } 1239 1240 pppt_status_t 1241 pppt_task_start(pppt_task_t *ptask) 1242 { 1243 avl_index_t where; 1244 1245 ASSERT(ptask->pt_state == PTS_INIT); 1246 1247 mutex_enter(&ptask->pt_sess->ps_mutex); 1248 mutex_enter(&ptask->pt_mutex); 1249 if (avl_find(&ptask->pt_sess->ps_task_list, ptask, &where) == NULL) { 1250 pppt_task_update_state(ptask, PTS_ACTIVE); 1251 /* Manually increment refcnt, sincd we hold the mutex... */ 1252 ptask->pt_refcnt++; 1253 avl_insert(&ptask->pt_sess->ps_task_list, ptask, where); 1254 mutex_exit(&ptask->pt_mutex); 1255 mutex_exit(&ptask->pt_sess->ps_mutex); 1256 return (PPPT_STATUS_SUCCESS); 1257 } 1258 mutex_exit(&ptask->pt_mutex); 1259 mutex_exit(&ptask->pt_sess->ps_mutex); 1260 1261 return (PPPT_STATUS_FAIL); 1262 } 1263 1264 pppt_status_t 1265 pppt_task_done(pppt_task_t *ptask) 1266 { 1267 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1268 boolean_t remove = B_FALSE; 1269 1270 mutex_enter(&ptask->pt_mutex); 1271 1272 switch (ptask->pt_state) { 1273 case PTS_ACTIVE: 1274 remove = B_TRUE; 1275 pppt_task_update_state(ptask, PTS_DONE); 1276 break; 1277 case PTS_ABORTED: 1278 pppt_status = PPPT_STATUS_ABORTED; 1279 break; 1280 case PTS_DONE: 1281 /* Repeat calls are OK. Do nothing, return success */ 1282 break; 1283 default: 1284 ASSERT(0); 1285 } 1286 1287 mutex_exit(&ptask->pt_mutex); 1288 1289 if (remove) { 1290 mutex_enter(&ptask->pt_sess->ps_mutex); 1291 avl_remove(&ptask->pt_sess->ps_task_list, ptask); 1292 mutex_exit(&ptask->pt_sess->ps_mutex); 1293 /* Out of the AVL tree, so drop a reference. */ 1294 pppt_task_rele(ptask); 1295 } 1296 1297 return (pppt_status); 1298 } 1299 1300 void 1301 pppt_task_sent_status(pppt_task_t *ptask) 1302 { 1303 /* 1304 * If STMF tries to abort a task after the task state changed to 1305 * PTS_DONE (meaning all task processing is complete from 1306 * the port provider perspective) then we return STMF_BUSY 1307 * from pppt_lport_abort. STMF will return after a short interval 1308 * but our calls to stmf_send_status_done will be ignored since 1309 * STMF is aborting the task. That's where this state comes in. 1310 * This state essentially says we are calling stmf_send_status_done 1311 * so we will not be touching the task again. The next time 1312 * STMF calls pppt_lport_abort we will return a success full 1313 * status and the abort will succeed. 1314 */ 1315 mutex_enter(&ptask->pt_mutex); 1316 pppt_task_update_state(ptask, PTS_SENT_STATUS); 1317 mutex_exit(&ptask->pt_mutex); 1318 } 1319 1320 pppt_task_t * 1321 pppt_task_lookup(stmf_ic_msgid_t msgid) 1322 { 1323 pppt_tgt_t *tgt; 1324 pppt_sess_t *sess; 1325 pppt_task_t lookup_task; 1326 pppt_task_t *result; 1327 1328 bzero(&lookup_task, sizeof (lookup_task)); 1329 lookup_task.pt_task_id = msgid; 1330 PPPT_GLOBAL_LOCK(); 1331 for (tgt = avl_first(&pppt_global.global_target_list); tgt != NULL; 1332 tgt = AVL_NEXT(&pppt_global.global_target_list, tgt)) { 1333 1334 mutex_enter(&tgt->target_mutex); 1335 for (sess = avl_first(&tgt->target_sess_list); sess != NULL; 1336 sess = AVL_NEXT(&tgt->target_sess_list, sess)) { 1337 mutex_enter(&sess->ps_mutex); 1338 if ((result = avl_find(&sess->ps_task_list, 1339 &lookup_task, NULL)) != NULL) { 1340 if (pppt_task_hold(result) != 1341 PPPT_STATUS_SUCCESS) { 1342 result = NULL; 1343 } 1344 mutex_exit(&sess->ps_mutex); 1345 mutex_exit(&tgt->target_mutex); 1346 PPPT_GLOBAL_UNLOCK(); 1347 return (result); 1348 } 1349 mutex_exit(&sess->ps_mutex); 1350 } 1351 mutex_exit(&tgt->target_mutex); 1352 } 1353 PPPT_GLOBAL_UNLOCK(); 1354 1355 return (NULL); 1356 } 1357 1358 static int 1359 pppt_task_avl_compare(const void *void_task1, const void *void_task2) 1360 { 1361 const pppt_task_t *ptask1 = void_task1; 1362 const pppt_task_t *ptask2 = void_task2; 1363 1364 if (ptask1->pt_task_id < ptask2->pt_task_id) 1365 return (-1); 1366 else if (ptask1->pt_task_id > ptask2->pt_task_id) 1367 return (1); 1368 1369 return (0); 1370 } 1371 1372 static pppt_status_t 1373 pppt_task_try_abort(pppt_task_t *ptask) 1374 { 1375 boolean_t remove = B_FALSE; 1376 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1377 1378 mutex_enter(&ptask->pt_mutex); 1379 1380 switch (ptask->pt_state) { 1381 case PTS_ACTIVE: 1382 remove = B_TRUE; 1383 pppt_task_update_state(ptask, PTS_ABORTED); 1384 break; 1385 case PTS_DONE: 1386 pppt_status = PPPT_STATUS_DONE; 1387 break; 1388 case PTS_SENT_STATUS: 1389 /* 1390 * Already removed so leave remove set to B_FALSE 1391 * and leave status set to PPPT_STATUS_SUCCESS. 1392 */ 1393 pppt_task_update_state(ptask, PTS_ABORTED); 1394 break; 1395 case PTS_ABORTED: 1396 break; 1397 default: 1398 ASSERT(0); 1399 } 1400 1401 mutex_exit(&ptask->pt_mutex); 1402 1403 if (remove) { 1404 mutex_enter(&ptask->pt_sess->ps_mutex); 1405 avl_remove(&ptask->pt_sess->ps_task_list, ptask); 1406 mutex_exit(&ptask->pt_sess->ps_mutex); 1407 /* Out of the AVL tree, so drop a reference. */ 1408 pppt_task_rele(ptask); 1409 } 1410 1411 return (pppt_status); 1412 } 1413 1414 pppt_status_t 1415 pppt_task_hold(pppt_task_t *ptask) 1416 { 1417 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1418 1419 mutex_enter(&ptask->pt_mutex); 1420 if (ptask->pt_state == PTS_ACTIVE) { 1421 ptask->pt_refcnt++; 1422 } else { 1423 pppt_status = PPPT_STATUS_FAIL; 1424 } 1425 mutex_exit(&ptask->pt_mutex); 1426 1427 return (pppt_status); 1428 } 1429 1430 static void 1431 pppt_task_rele(pppt_task_t *ptask) 1432 { 1433 boolean_t freeit; 1434 1435 mutex_enter(&ptask->pt_mutex); 1436 ptask->pt_refcnt--; 1437 freeit = (ptask->pt_refcnt == 0); 1438 mutex_exit(&ptask->pt_mutex); 1439 if (freeit) 1440 pppt_task_free(ptask); 1441 } 1442 1443 static void 1444 pppt_task_update_state(pppt_task_t *ptask, 1445 pppt_task_state_t new_state) 1446 { 1447 PPPT_LOG(CE_NOTE, "task %p %d -> %d", (void *)ptask, 1448 ptask->pt_state, new_state); 1449 1450 ASSERT(mutex_owned(&ptask->pt_mutex)); 1451 ptask->pt_state = new_state; 1452 } 1453