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 371 rc = ddi_copyin((void *)(unsigned long)iocd.pppt_buf, 372 buf, buf_size, flag); 373 if (rc) { 374 kmem_free(buf, buf_size); 375 return (EFAULT); 376 } 377 378 stmf_ic_rx_msg(buf, buf_size); 379 380 kmem_free(buf, buf_size); 381 break; 382 case PPPT_INSTALL_DOOR: 383 384 new_handle = door_ki_lookup((int)iocd.pppt_door_fd); 385 if (new_handle == NULL) 386 return (EINVAL); 387 388 mutex_enter(&pppt_global.global_door_lock); 389 ASSERT(pppt_global.global_svc_state == PSS_ENABLED); 390 if (pppt_global.global_door != NULL) { 391 /* 392 * There can only be one door installed 393 */ 394 mutex_exit(&pppt_global.global_door_lock); 395 door_ki_rele(new_handle); 396 return (EBUSY); 397 } 398 pppt_global.global_door = new_handle; 399 mutex_exit(&pppt_global.global_door_lock); 400 break; 401 } 402 403 return (rc); 404 } 405 406 /* 407 * pppt_enable_svc 408 * 409 * registers all the configured targets and target portals with STMF 410 */ 411 static int 412 pppt_enable_svc(void) 413 { 414 stmf_port_provider_t *pp; 415 stmf_dbuf_store_t *dbuf_store; 416 int rc = 0; 417 418 ASSERT(pppt_global.global_svc_state == PSS_ENABLING); 419 420 /* 421 * Make sure that can tell if we have partially allocated 422 * in case we need to exit and tear down anything allocated. 423 */ 424 pppt_global.global_dbuf_store = NULL; 425 pp = NULL; 426 pppt_global.global_pp = NULL; 427 pppt_global.global_dispatch_taskq = NULL; 428 pppt_global.global_sess_taskq = NULL; 429 430 avl_create(&pppt_global.global_target_list, 431 pppt_tgt_avl_compare, sizeof (pppt_tgt_t), 432 offsetof(pppt_tgt_t, target_global_ln)); 433 434 avl_create(&pppt_global.global_sess_list, 435 pppt_sess_avl_compare_by_id, sizeof (pppt_sess_t), 436 offsetof(pppt_sess_t, ps_global_ln)); 437 438 /* 439 * Setup STMF dbuf store. Tf buffers are associated with a particular 440 * lport (FC, SRP) then the dbuf_store should stored in the lport 441 * context, otherwise (iSCSI) the dbuf_store should be global. 442 */ 443 dbuf_store = stmf_alloc(STMF_STRUCT_DBUF_STORE, 0, 0); 444 if (dbuf_store == NULL) { 445 rc = ENOMEM; 446 goto tear_down_and_return; 447 } 448 dbuf_store->ds_alloc_data_buf = pppt_dbuf_alloc; 449 dbuf_store->ds_free_data_buf = pppt_dbuf_free; 450 dbuf_store->ds_port_private = NULL; 451 pppt_global.global_dbuf_store = dbuf_store; 452 453 /* Register port provider */ 454 pp = stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0); 455 if (pp == NULL) { 456 rc = ENOMEM; 457 goto tear_down_and_return; 458 } 459 460 pp->pp_portif_rev = PORTIF_REV_1; 461 pp->pp_instance = 0; 462 pp->pp_name = PPPT_MODNAME; 463 pp->pp_cb = NULL; 464 465 pppt_global.global_pp = pp; 466 467 if (stmf_register_port_provider(pp) != STMF_SUCCESS) { 468 rc = EIO; 469 goto tear_down_and_return; 470 } 471 472 pppt_global.global_dispatch_taskq = taskq_create("pppt_dispatch", 473 1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE); 474 475 pppt_global.global_sess_taskq = taskq_create("pppt_session", 476 1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE); 477 478 return (0); 479 480 tear_down_and_return: 481 482 if (pppt_global.global_sess_taskq) { 483 taskq_destroy(pppt_global.global_sess_taskq); 484 pppt_global.global_sess_taskq = NULL; 485 } 486 487 if (pppt_global.global_dispatch_taskq) { 488 taskq_destroy(pppt_global.global_dispatch_taskq); 489 pppt_global.global_dispatch_taskq = NULL; 490 } 491 492 if (pppt_global.global_pp) 493 pppt_global.global_pp = NULL; 494 495 if (pp) 496 stmf_free(pp); 497 498 if (pppt_global.global_dbuf_store) { 499 stmf_free(pppt_global.global_dbuf_store); 500 pppt_global.global_dbuf_store = NULL; 501 } 502 503 avl_destroy(&pppt_global.global_sess_list); 504 avl_destroy(&pppt_global.global_target_list); 505 506 return (rc); 507 } 508 509 /* 510 * pppt_disable_svc 511 * 512 * clean up all existing sessions and deregister targets from STMF 513 */ 514 static void 515 pppt_disable_svc(void) 516 { 517 pppt_tgt_t *tgt, *next_tgt; 518 avl_tree_t delete_target_list; 519 520 ASSERT(pppt_global.global_svc_state == PSS_DISABLING); 521 522 avl_create(&delete_target_list, 523 pppt_tgt_avl_compare, sizeof (pppt_tgt_t), 524 offsetof(pppt_tgt_t, target_global_ln)); 525 526 PPPT_GLOBAL_LOCK(); 527 for (tgt = avl_first(&pppt_global.global_target_list); 528 tgt != NULL; 529 tgt = next_tgt) { 530 next_tgt = AVL_NEXT(&pppt_global.global_target_list, tgt); 531 avl_remove(&pppt_global.global_target_list, tgt); 532 avl_add(&delete_target_list, tgt); 533 pppt_tgt_async_delete(tgt); 534 } 535 PPPT_GLOBAL_UNLOCK(); 536 537 for (tgt = avl_first(&delete_target_list); 538 tgt != NULL; 539 tgt = next_tgt) { 540 next_tgt = AVL_NEXT(&delete_target_list, tgt); 541 mutex_enter(&tgt->target_mutex); 542 while ((tgt->target_refcount > 0) || 543 (tgt->target_state != TS_DELETING)) { 544 cv_wait(&tgt->target_cv, &tgt->target_mutex); 545 } 546 mutex_exit(&tgt->target_mutex); 547 548 avl_remove(&delete_target_list, tgt); 549 pppt_tgt_destroy(tgt); 550 } 551 552 taskq_destroy(pppt_global.global_sess_taskq); 553 554 taskq_destroy(pppt_global.global_dispatch_taskq); 555 556 avl_destroy(&pppt_global.global_sess_list); 557 avl_destroy(&pppt_global.global_target_list); 558 559 (void) stmf_deregister_port_provider(pppt_global.global_pp); 560 561 stmf_free(pppt_global.global_dbuf_store); 562 pppt_global.global_dbuf_store = NULL; 563 564 stmf_free(pppt_global.global_pp); 565 pppt_global.global_pp = NULL; 566 } 567 568 /* 569 * STMF callbacks 570 */ 571 572 /*ARGSUSED*/ 573 static stmf_data_buf_t * 574 pppt_dbuf_alloc(scsi_task_t *task, uint32_t size, uint32_t *pminsize, 575 uint32_t flags) 576 { 577 stmf_data_buf_t *result; 578 pppt_buf_t *pbuf; 579 uint8_t *buf; 580 581 /* Get buffer */ 582 buf = kmem_alloc(size, KM_SLEEP); 583 584 /* 585 * Allocate stmf buf with private port provider section 586 * (pppt_buf_t) 587 */ 588 result = stmf_alloc(STMF_STRUCT_DATA_BUF, sizeof (pppt_buf_t), 0); 589 if (result != NULL) { 590 /* Fill in pppt_buf_t */ 591 pbuf = result->db_port_private; 592 pbuf->pbuf_stmf_buf = result; 593 pbuf->pbuf_is_immed = B_FALSE; 594 595 /* 596 * Fill in stmf_data_buf_t. DB_DONT CACHE tells 597 * stmf not to cache buffers but STMF doesn't do 598 * that yet so it's a no-op. Port providers like 599 * FC and SRP that have buffers associated with the 600 * target port would want to let STMF cache 601 * the buffers. Port providers like iSCSI would 602 * not want STMF to cache because the buffers are 603 * really associated with a connection, not an 604 * STMF target port so there is no way for STMF 605 * to cache the buffers effectively. These port 606 * providers should cache buffers internally if 607 * there is significant buffer setup overhead. 608 * 609 * And of course, since STMF doesn't do any internal 610 * caching right now anyway, all port providers should 611 * do what they can to minimize buffer setup overhead. 612 */ 613 result->db_flags = DB_DONT_CACHE; 614 result->db_buf_size = size; 615 result->db_data_size = size; 616 result->db_sglist_length = 1; 617 result->db_sglist[0].seg_addr = buf; 618 result->db_sglist[0].seg_length = size; 619 return (result); 620 } else { 621 /* 622 * Couldn't get the stmf_data_buf_t so free the 623 * buffer 624 */ 625 kmem_free(buf, size); 626 } 627 628 return (NULL); 629 } 630 631 /*ARGSUSED*/ 632 static void 633 pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf) 634 { 635 pppt_buf_t *pbuf = dbuf->db_port_private; 636 637 if (pbuf->pbuf_is_immed) { 638 stmf_ic_msg_free(pbuf->pbuf_immed_msg); 639 } else { 640 kmem_free(dbuf->db_sglist[0].seg_addr, 641 dbuf->db_sglist[0].seg_length); 642 stmf_free(dbuf); 643 } 644 } 645 646 /*ARGSUSED*/ 647 stmf_status_t 648 pppt_lport_xfer_data(scsi_task_t *task, stmf_data_buf_t *dbuf, 649 uint32_t ioflags) 650 { 651 pppt_task_t *pppt_task = task->task_port_private; 652 pppt_buf_t *pbuf = dbuf->db_port_private; 653 stmf_ic_msg_t *msg; 654 stmf_ic_msg_status_t ic_msg_status; 655 656 /* 657 * If we are aborting then we can ignore this request, otherwise 658 * add a reference. 659 */ 660 if (pppt_task_hold(pppt_task) != PPPT_STATUS_SUCCESS) { 661 return (STMF_SUCCESS); 662 } 663 664 /* 665 * If it's not immediate data then start the transfer 666 */ 667 ASSERT(pbuf->pbuf_is_immed == B_FALSE); 668 if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) { 669 670 /* Send read data */ 671 msg = stmf_ic_scsi_data_msg_alloc( 672 pppt_task->pt_task_id, 673 pppt_task->pt_sess->ps_session_id, 674 pppt_task->pt_lun_id, 675 dbuf->db_sglist[0].seg_length, 676 dbuf->db_sglist[0].seg_addr, 0); 677 678 pppt_task->pt_read_buf = pbuf; 679 pppt_task->pt_read_xfer_msgid = msg->icm_msgid; 680 681 ic_msg_status = stmf_ic_tx_msg(msg); 682 pppt_task_rele(pppt_task); 683 if (ic_msg_status != STMF_IC_MSG_SUCCESS) { 684 return (STMF_FAILURE); 685 } else { 686 return (STMF_SUCCESS); 687 } 688 } else if (dbuf->db_flags & DB_DIRECTION_FROM_RPORT) { 689 pppt_task_rele(pppt_task); 690 return (STMF_FAILURE); 691 } 692 693 pppt_task_rele(pppt_task); 694 695 return (STMF_INVALID_ARG); 696 } 697 698 void 699 pppt_xfer_read_complete(pppt_task_t *pppt_task, stmf_status_t status) 700 { 701 pppt_buf_t *pppt_buf; 702 stmf_data_buf_t *dbuf; 703 704 /* 705 * Caller should have taken a task hold (likely via pppt_task_lookup) 706 * 707 * Get pppt_buf_t and stmf_data_buf_t pointers 708 */ 709 pppt_buf = pppt_task->pt_read_buf; 710 dbuf = pppt_buf->pbuf_stmf_buf; 711 dbuf->db_xfer_status = (status == STMF_SUCCESS) ? 712 STMF_SUCCESS : STMF_FAILURE; 713 714 /* 715 * COMSTAR currently requires port providers to support 716 * the DB_SEND_STATUS_GOOD flag even if phase collapse is 717 * not supported. So we will roll our own... pretend we are 718 * COMSTAR and ask for a status message. 719 */ 720 if ((dbuf->db_flags & DB_SEND_STATUS_GOOD) && 721 (status == STMF_SUCCESS)) { 722 /* 723 * It's possible the task has been aborted since the time we 724 * looked it up. We need to release the hold before calling 725 * pppt_lport_send_status and as soon as we release the hold 726 * the task may disappear. Calling pppt_task_done allows us 727 * to determine whether the task has been aborted (in which 728 * case we will stop processing and return) and mark the task 729 * "done" which will prevent the task from being aborted while 730 * we are trying to send the status. 731 */ 732 if (pppt_task_done(pppt_task) != PPPT_STATUS_SUCCESS) { 733 /* STMF will free task and buffer(s) */ 734 pppt_task_rele(pppt_task); 735 return; 736 } 737 pppt_task_rele(pppt_task); 738 739 if (pppt_lport_send_status(pppt_task->pt_stmf_task, 0) 740 != STMF_SUCCESS) { 741 /* Failed to send status */ 742 dbuf->db_xfer_status = STMF_FAILURE; 743 stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf, 744 STMF_IOF_LPORT_DONE); 745 } 746 } else { 747 pppt_task_rele(pppt_task); 748 stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf, 0); 749 } 750 } 751 752 /*ARGSUSED*/ 753 stmf_status_t 754 pppt_lport_send_status(scsi_task_t *task, uint32_t ioflags) 755 { 756 pppt_task_t *ptask = task->task_port_private; 757 stmf_ic_msg_t *msg; 758 stmf_ic_msg_status_t ic_msg_status; 759 760 /* 761 * Mark task completed. If the state indicates it was aborted 762 * then we don't need to respond. 763 */ 764 if (pppt_task_done(ptask) == PPPT_STATUS_ABORTED) { 765 return (STMF_SUCCESS); 766 } 767 768 /* 769 * Send status. 770 */ 771 msg = stmf_ic_scsi_status_msg_alloc( 772 ptask->pt_task_id, 773 ptask->pt_sess->ps_session_id, 774 ptask->pt_lun_id, 775 0, 776 task->task_scsi_status, 777 task->task_status_ctrl, task->task_resid, 778 task->task_sense_length, task->task_sense_data, 0); 779 780 ic_msg_status = stmf_ic_tx_msg(msg); 781 782 if (ic_msg_status != STMF_IC_MSG_SUCCESS) { 783 pppt_task_sent_status(ptask); 784 stmf_send_status_done(ptask->pt_stmf_task, 785 STMF_FAILURE, STMF_IOF_LPORT_DONE); 786 return (STMF_FAILURE); 787 } else { 788 pppt_task_sent_status(ptask); 789 stmf_send_status_done(ptask->pt_stmf_task, 790 STMF_SUCCESS, STMF_IOF_LPORT_DONE); 791 return (STMF_SUCCESS); 792 } 793 } 794 795 void 796 pppt_lport_task_free(scsi_task_t *task) 797 { 798 pppt_task_t *ptask = task->task_port_private; 799 pppt_sess_t *ps = ptask->pt_sess; 800 801 pppt_task_rele(ptask); 802 pppt_sess_rele(ps); 803 } 804 805 /*ARGSUSED*/ 806 stmf_status_t 807 pppt_lport_abort(stmf_local_port_t *lport, int abort_cmd, void *arg, 808 uint32_t flags) 809 { 810 scsi_task_t *st = (scsi_task_t *)arg; 811 pppt_task_t *ptask; 812 813 ptask = st->task_port_private; 814 815 if (pppt_task_try_abort(ptask) == PPPT_STATUS_DONE) { 816 /* 817 * This task is beyond the point where abort makes sense 818 * and we will soon be sending status. Tell STMF to 819 * go away. 820 */ 821 return (STMF_BUSY); 822 } else { 823 return (STMF_ABORT_SUCCESS); 824 } 825 /*NOTREACHED*/ 826 } 827 828 /*ARGSUSED*/ 829 void 830 pppt_lport_ctl(stmf_local_port_t *lport, int cmd, void *arg) 831 { 832 switch (cmd) { 833 case STMF_CMD_LPORT_ONLINE: 834 case STMF_CMD_LPORT_OFFLINE: 835 case STMF_ACK_LPORT_ONLINE_COMPLETE: 836 case STMF_ACK_LPORT_OFFLINE_COMPLETE: 837 pppt_tgt_sm_ctl(lport, cmd, arg); 838 break; 839 840 default: 841 ASSERT(0); 842 break; 843 } 844 } 845 846 pppt_sess_t * 847 pppt_sess_lookup_locked(uint64_t session_id, 848 scsi_devid_desc_t *lport_devid, stmf_remote_port_t *rport) 849 { 850 pppt_tgt_t *tgt; 851 pppt_sess_t *ps; 852 int lport_cmp; 853 854 ASSERT(mutex_owned(&pppt_global.global_lock)); 855 856 /* 857 * Look for existing session for this ID 858 */ 859 ps = pppt_sess_lookup_by_id_locked(session_id); 860 if (ps == NULL) { 861 PPPT_INC_STAT(es_sess_lookup_no_session); 862 return (NULL); 863 } 864 865 tgt = ps->ps_target; 866 867 mutex_enter(&tgt->target_mutex); 868 869 /* Validate local/remote port names */ 870 if ((lport_devid->ident_length != 871 tgt->target_stmf_lport->lport_id->ident_length) || 872 (rport->rport_tptid_sz != 873 ps->ps_stmf_sess->ss_rport->rport_tptid_sz)) { 874 mutex_exit(&tgt->target_mutex); 875 PPPT_INC_STAT(es_sess_lookup_ident_mismatch); 876 return (NULL); 877 } else { 878 lport_cmp = bcmp(lport_devid->ident, 879 tgt->target_stmf_lport->lport_id->ident, 880 lport_devid->ident_length); 881 if (lport_cmp != 0 || 882 (stmf_scsilib_tptid_compare(rport->rport_tptid, 883 ps->ps_stmf_sess->ss_rport->rport_tptid) != B_TRUE)) { 884 mutex_exit(&tgt->target_mutex); 885 PPPT_INC_STAT(es_sess_lookup_ident_mismatch); 886 return (NULL); 887 } 888 889 if (tgt->target_state != TS_STMF_ONLINE) { 890 mutex_exit(&tgt->target_mutex); 891 PPPT_INC_STAT(es_sess_lookup_bad_tgt_state); 892 return (NULL); 893 } 894 } 895 mutex_exit(&tgt->target_mutex); 896 897 return (ps); 898 } 899 900 pppt_sess_t * 901 pppt_sess_lookup_by_id_locked(uint64_t session_id) 902 { 903 pppt_sess_t tmp_ps; 904 pppt_sess_t *ps; 905 906 ASSERT(mutex_owned(&pppt_global.global_lock)); 907 tmp_ps.ps_session_id = session_id; 908 tmp_ps.ps_closed = 0; 909 ps = avl_find(&pppt_global.global_sess_list, &tmp_ps, NULL); 910 if (ps != NULL) { 911 mutex_enter(&ps->ps_mutex); 912 if (!ps->ps_closed) { 913 ps->ps_refcnt++; 914 mutex_exit(&ps->ps_mutex); 915 return (ps); 916 } 917 mutex_exit(&ps->ps_mutex); 918 } 919 920 return (NULL); 921 } 922 923 /* New session */ 924 pppt_sess_t * 925 pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid, 926 scsi_devid_desc_t *rport_devid, stmf_remote_port_t *rport, 927 uint64_t session_id, stmf_status_t *statusp) 928 { 929 pppt_tgt_t *tgt; 930 pppt_sess_t *ps; 931 stmf_scsi_session_t *ss; 932 pppt_sess_t tmp_ps; 933 stmf_scsi_session_t tmp_ss; 934 *statusp = STMF_SUCCESS; 935 936 PPPT_GLOBAL_LOCK(); 937 938 /* 939 * Look for existing session for this ID 940 */ 941 ps = pppt_sess_lookup_locked(session_id, lport_devid, rport); 942 943 if (ps != NULL) { 944 PPPT_GLOBAL_UNLOCK(); 945 return (ps); 946 } 947 948 /* 949 * No session with that ID, look for another session corresponding 950 * to the same IT nexus. 951 */ 952 tgt = pppt_tgt_lookup_locked(lport_devid); 953 if (tgt == NULL) { 954 *statusp = STMF_NOT_FOUND; 955 PPPT_GLOBAL_UNLOCK(); 956 return (NULL); 957 } 958 959 mutex_enter(&tgt->target_mutex); 960 if (tgt->target_state != TS_STMF_ONLINE) { 961 *statusp = STMF_NOT_FOUND; 962 mutex_exit(&tgt->target_mutex); 963 PPPT_GLOBAL_UNLOCK(); 964 /* Can't create session to offline target */ 965 return (NULL); 966 } 967 968 bzero(&tmp_ps, sizeof (tmp_ps)); 969 bzero(&tmp_ss, sizeof (tmp_ss)); 970 tmp_ps.ps_stmf_sess = &tmp_ss; 971 tmp_ss.ss_rport = rport; 972 973 /* 974 * Look for an existing session on this IT nexus 975 */ 976 ps = avl_find(&tgt->target_sess_list, &tmp_ps, NULL); 977 978 if (ps != NULL) { 979 /* 980 * Now check the session ID. It should not match because if 981 * it did we would have found it on the global session list. 982 * If the session ID in the command is higher than the existing 983 * session ID then we need to tear down the existing session. 984 */ 985 mutex_enter(&ps->ps_mutex); 986 ASSERT(ps->ps_session_id != session_id); 987 if (ps->ps_session_id > session_id) { 988 /* Invalid session ID */ 989 mutex_exit(&ps->ps_mutex); 990 mutex_exit(&tgt->target_mutex); 991 PPPT_GLOBAL_UNLOCK(); 992 *statusp = STMF_INVALID_ARG; 993 return (NULL); 994 } else { 995 /* Existing session needs to be invalidated */ 996 if (!ps->ps_closed) { 997 pppt_sess_close_locked(ps); 998 } 999 } 1000 mutex_exit(&ps->ps_mutex); 1001 1002 /* Fallthrough and create new session */ 1003 } 1004 1005 /* 1006 * Allocate and fill in pppt_session_t with the appropriate data 1007 * for the protocol. 1008 */ 1009 ps = kmem_zalloc(sizeof (*ps), KM_SLEEP); 1010 1011 /* Fill in session fields */ 1012 ps->ps_target = tgt; 1013 ps->ps_session_id = session_id; 1014 1015 ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0, 1016 0); 1017 if (ss == NULL) { 1018 mutex_exit(&tgt->target_mutex); 1019 PPPT_GLOBAL_UNLOCK(); 1020 kmem_free(ps, sizeof (*ps)); 1021 *statusp = STMF_ALLOC_FAILURE; 1022 return (NULL); 1023 } 1024 1025 ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) + 1026 rport_devid->ident_length + 1, KM_SLEEP); 1027 bcopy(rport_devid, ss->ss_rport_id, 1028 sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1); 1029 1030 ss->ss_lport = tgt->target_stmf_lport; 1031 1032 ss->ss_rport = stmf_remote_port_alloc(rport->rport_tptid_sz); 1033 bcopy(rport->rport_tptid, ss->ss_rport->rport_tptid, 1034 rport->rport_tptid_sz); 1035 1036 if (stmf_register_scsi_session(tgt->target_stmf_lport, ss) != 1037 STMF_SUCCESS) { 1038 mutex_exit(&tgt->target_mutex); 1039 PPPT_GLOBAL_UNLOCK(); 1040 kmem_free(ss->ss_rport_id, 1041 sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1); 1042 stmf_remote_port_free(ss->ss_rport); 1043 stmf_free(ss); 1044 kmem_free(ps, sizeof (*ps)); 1045 *statusp = STMF_TARGET_FAILURE; 1046 return (NULL); 1047 } 1048 1049 ss->ss_port_private = ps; 1050 mutex_init(&ps->ps_mutex, NULL, MUTEX_DEFAULT, NULL); 1051 cv_init(&ps->ps_cv, NULL, CV_DEFAULT, NULL); 1052 avl_create(&ps->ps_task_list, pppt_task_avl_compare, 1053 sizeof (pppt_task_t), offsetof(pppt_task_t, pt_sess_ln)); 1054 ps->ps_refcnt = 1; 1055 ps->ps_stmf_sess = ss; 1056 avl_add(&tgt->target_sess_list, ps); 1057 avl_add(&pppt_global.global_sess_list, ps); 1058 mutex_exit(&tgt->target_mutex); 1059 PPPT_GLOBAL_UNLOCK(); 1060 stmf_trace("pppt", "New session %p", (void *)ps); 1061 1062 return (ps); 1063 } 1064 1065 void 1066 pppt_sess_rele(pppt_sess_t *ps) 1067 { 1068 mutex_enter(&ps->ps_mutex); 1069 pppt_sess_rele_locked(ps); 1070 mutex_exit(&ps->ps_mutex); 1071 } 1072 1073 void 1074 pppt_sess_rele_locked(pppt_sess_t *ps) 1075 { 1076 ASSERT(mutex_owned(&ps->ps_mutex)); 1077 ps->ps_refcnt--; 1078 if (ps->ps_refcnt == 0) { 1079 cv_signal(&ps->ps_cv); 1080 } 1081 } 1082 1083 static void pppt_sess_destroy_task(void *ps_void) 1084 { 1085 pppt_sess_t *ps = ps_void; 1086 stmf_scsi_session_t *ss; 1087 1088 stmf_trace("pppt", "Session destroy task %p", (void *)ps); 1089 1090 ss = ps->ps_stmf_sess; 1091 mutex_enter(&ps->ps_mutex); 1092 stmf_deregister_scsi_session(ss->ss_lport, ss); 1093 kmem_free(ss->ss_rport_id, 1094 sizeof (scsi_devid_desc_t) + ss->ss_rport_id->ident_length + 1); 1095 stmf_remote_port_free(ss->ss_rport); 1096 avl_destroy(&ps->ps_task_list); 1097 mutex_exit(&ps->ps_mutex); 1098 cv_destroy(&ps->ps_cv); 1099 mutex_destroy(&ps->ps_mutex); 1100 stmf_free(ps->ps_stmf_sess); 1101 kmem_free(ps, sizeof (*ps)); 1102 1103 stmf_trace("pppt", "Session destroy task complete %p", (void *)ps); 1104 } 1105 1106 int 1107 pppt_sess_avl_compare_by_id(const void *void_sess1, const void *void_sess2) 1108 { 1109 const pppt_sess_t *psess1 = void_sess1; 1110 const pppt_sess_t *psess2 = void_sess2; 1111 1112 if (psess1->ps_session_id < psess2->ps_session_id) 1113 return (-1); 1114 else if (psess1->ps_session_id > psess2->ps_session_id) 1115 return (1); 1116 1117 /* Allow multiple duplicate sessions if one is closed */ 1118 ASSERT(!(psess1->ps_closed && psess2->ps_closed)); 1119 if (psess1->ps_closed) 1120 return (-1); 1121 else if (psess2->ps_closed) 1122 return (1); 1123 1124 return (0); 1125 } 1126 1127 int 1128 pppt_sess_avl_compare_by_name(const void *void_sess1, const void *void_sess2) 1129 { 1130 const pppt_sess_t *psess1 = void_sess1; 1131 const pppt_sess_t *psess2 = void_sess2; 1132 int result; 1133 1134 /* Compare by tptid size */ 1135 if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz < 1136 psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) { 1137 return (-1); 1138 } else if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz > 1139 psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) { 1140 return (1); 1141 } 1142 1143 /* Now compare tptid */ 1144 result = memcmp(psess1->ps_stmf_sess->ss_rport->rport_tptid, 1145 psess2->ps_stmf_sess->ss_rport->rport_tptid, 1146 psess1->ps_stmf_sess->ss_rport->rport_tptid_sz); 1147 1148 if (result < 0) { 1149 return (-1); 1150 } else if (result > 0) { 1151 return (1); 1152 } 1153 1154 return (0); 1155 } 1156 1157 void 1158 pppt_sess_close_locked(pppt_sess_t *ps) 1159 { 1160 pppt_tgt_t *tgt = ps->ps_target; 1161 pppt_task_t *ptask; 1162 1163 stmf_trace("pppt", "Session close %p", (void *)ps); 1164 1165 ASSERT(mutex_owned(&pppt_global.global_lock)); 1166 ASSERT(mutex_owned(&tgt->target_mutex)); 1167 ASSERT(mutex_owned(&ps->ps_mutex)); 1168 ASSERT(!ps->ps_closed); /* Caller should ensure session is not closed */ 1169 1170 ps->ps_closed = B_TRUE; 1171 for (ptask = avl_first(&ps->ps_task_list); ptask != NULL; 1172 ptask = AVL_NEXT(&ps->ps_task_list, ptask)) { 1173 mutex_enter(&ptask->pt_mutex); 1174 if (ptask->pt_state == PTS_ACTIVE) { 1175 stmf_abort(STMF_QUEUE_TASK_ABORT, ptask->pt_stmf_task, 1176 STMF_ABORTED, NULL); 1177 } 1178 mutex_exit(&ptask->pt_mutex); 1179 } 1180 1181 /* 1182 * Now that all the tasks are aborting the session refcnt should 1183 * go to 0. 1184 */ 1185 while (ps->ps_refcnt != 0) { 1186 cv_wait(&ps->ps_cv, &ps->ps_mutex); 1187 } 1188 1189 avl_remove(&tgt->target_sess_list, ps); 1190 avl_remove(&pppt_global.global_sess_list, ps); 1191 (void) taskq_dispatch(pppt_global.global_sess_taskq, 1192 &pppt_sess_destroy_task, ps, KM_SLEEP); 1193 1194 stmf_trace("pppt", "Session close complete %p", (void *)ps); 1195 } 1196 1197 pppt_task_t * 1198 pppt_task_alloc(void) 1199 { 1200 pppt_task_t *ptask; 1201 pppt_buf_t *immed_pbuf; 1202 1203 ptask = kmem_alloc(sizeof (pppt_task_t) + sizeof (pppt_buf_t) + 1204 sizeof (stmf_data_buf_t), KM_NOSLEEP); 1205 if (ptask != NULL) { 1206 ptask->pt_state = PTS_INIT; 1207 ptask->pt_read_buf = NULL; 1208 ptask->pt_read_xfer_msgid = 0; 1209 ptask->pt_refcnt = 0; 1210 mutex_init(&ptask->pt_mutex, NULL, MUTEX_DRIVER, NULL); 1211 immed_pbuf = (pppt_buf_t *)(ptask + 1); 1212 bzero(immed_pbuf, sizeof (*immed_pbuf)); 1213 immed_pbuf->pbuf_is_immed = B_TRUE; 1214 immed_pbuf->pbuf_stmf_buf = (stmf_data_buf_t *)(immed_pbuf + 1); 1215 1216 bzero(immed_pbuf->pbuf_stmf_buf, sizeof (stmf_data_buf_t)); 1217 immed_pbuf->pbuf_stmf_buf->db_port_private = immed_pbuf; 1218 immed_pbuf->pbuf_stmf_buf->db_sglist_length = 1; 1219 immed_pbuf->pbuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT | 1220 DB_DONT_CACHE; 1221 ptask->pt_immed_data = immed_pbuf; 1222 } 1223 1224 return (ptask); 1225 1226 } 1227 1228 void 1229 pppt_task_free(pppt_task_t *ptask) 1230 { 1231 mutex_enter(&ptask->pt_mutex); 1232 ASSERT(ptask->pt_refcnt == 0); 1233 mutex_destroy(&ptask->pt_mutex); 1234 kmem_free(ptask, sizeof (pppt_task_t) + sizeof (pppt_buf_t) + 1235 sizeof (stmf_data_buf_t)); 1236 } 1237 1238 pppt_status_t 1239 pppt_task_start(pppt_task_t *ptask) 1240 { 1241 avl_index_t where; 1242 1243 ASSERT(ptask->pt_state == PTS_INIT); 1244 1245 mutex_enter(&ptask->pt_sess->ps_mutex); 1246 mutex_enter(&ptask->pt_mutex); 1247 if (avl_find(&ptask->pt_sess->ps_task_list, ptask, &where) == NULL) { 1248 pppt_task_update_state(ptask, PTS_ACTIVE); 1249 /* Manually increment refcnt, sincd we hold the mutex... */ 1250 ptask->pt_refcnt++; 1251 avl_insert(&ptask->pt_sess->ps_task_list, ptask, where); 1252 mutex_exit(&ptask->pt_mutex); 1253 mutex_exit(&ptask->pt_sess->ps_mutex); 1254 return (PPPT_STATUS_SUCCESS); 1255 } 1256 mutex_exit(&ptask->pt_mutex); 1257 mutex_exit(&ptask->pt_sess->ps_mutex); 1258 1259 return (PPPT_STATUS_FAIL); 1260 } 1261 1262 pppt_status_t 1263 pppt_task_done(pppt_task_t *ptask) 1264 { 1265 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1266 boolean_t remove = B_FALSE; 1267 1268 mutex_enter(&ptask->pt_mutex); 1269 1270 switch (ptask->pt_state) { 1271 case PTS_ACTIVE: 1272 remove = B_TRUE; 1273 pppt_task_update_state(ptask, PTS_DONE); 1274 break; 1275 case PTS_ABORTED: 1276 pppt_status = PPPT_STATUS_ABORTED; 1277 break; 1278 case PTS_DONE: 1279 /* Repeat calls are OK. Do nothing, return success */ 1280 break; 1281 default: 1282 ASSERT(0); 1283 } 1284 1285 mutex_exit(&ptask->pt_mutex); 1286 1287 if (remove) { 1288 mutex_enter(&ptask->pt_sess->ps_mutex); 1289 avl_remove(&ptask->pt_sess->ps_task_list, ptask); 1290 mutex_exit(&ptask->pt_sess->ps_mutex); 1291 /* Out of the AVL tree, so drop a reference. */ 1292 pppt_task_rele(ptask); 1293 } 1294 1295 return (pppt_status); 1296 } 1297 1298 void 1299 pppt_task_sent_status(pppt_task_t *ptask) 1300 { 1301 /* 1302 * If STMF tries to abort a task after the task state changed to 1303 * PTS_DONE (meaning all task processing is complete from 1304 * the port provider perspective) then we return STMF_BUSY 1305 * from pppt_lport_abort. STMF will return after a short interval 1306 * but our calls to stmf_send_status_done will be ignored since 1307 * STMF is aborting the task. That's where this state comes in. 1308 * This state essentially says we are calling stmf_send_status_done 1309 * so we will not be touching the task again. The next time 1310 * STMF calls pppt_lport_abort we will return a success full 1311 * status and the abort will succeed. 1312 */ 1313 mutex_enter(&ptask->pt_mutex); 1314 pppt_task_update_state(ptask, PTS_SENT_STATUS); 1315 mutex_exit(&ptask->pt_mutex); 1316 } 1317 1318 pppt_task_t * 1319 pppt_task_lookup(stmf_ic_msgid_t msgid) 1320 { 1321 pppt_tgt_t *tgt; 1322 pppt_sess_t *sess; 1323 pppt_task_t lookup_task; 1324 pppt_task_t *result; 1325 1326 bzero(&lookup_task, sizeof (lookup_task)); 1327 lookup_task.pt_task_id = msgid; 1328 PPPT_GLOBAL_LOCK(); 1329 for (tgt = avl_first(&pppt_global.global_target_list); tgt != NULL; 1330 tgt = AVL_NEXT(&pppt_global.global_target_list, tgt)) { 1331 1332 mutex_enter(&tgt->target_mutex); 1333 for (sess = avl_first(&tgt->target_sess_list); sess != NULL; 1334 sess = AVL_NEXT(&tgt->target_sess_list, sess)) { 1335 mutex_enter(&sess->ps_mutex); 1336 if ((result = avl_find(&sess->ps_task_list, 1337 &lookup_task, NULL)) != NULL) { 1338 if (pppt_task_hold(result) != 1339 PPPT_STATUS_SUCCESS) { 1340 result = NULL; 1341 } 1342 mutex_exit(&sess->ps_mutex); 1343 mutex_exit(&tgt->target_mutex); 1344 PPPT_GLOBAL_UNLOCK(); 1345 return (result); 1346 } 1347 mutex_exit(&sess->ps_mutex); 1348 } 1349 mutex_exit(&tgt->target_mutex); 1350 } 1351 PPPT_GLOBAL_UNLOCK(); 1352 1353 return (NULL); 1354 } 1355 1356 static int 1357 pppt_task_avl_compare(const void *void_task1, const void *void_task2) 1358 { 1359 const pppt_task_t *ptask1 = void_task1; 1360 const pppt_task_t *ptask2 = void_task2; 1361 1362 if (ptask1->pt_task_id < ptask2->pt_task_id) 1363 return (-1); 1364 else if (ptask1->pt_task_id > ptask2->pt_task_id) 1365 return (1); 1366 1367 return (0); 1368 } 1369 1370 static pppt_status_t 1371 pppt_task_try_abort(pppt_task_t *ptask) 1372 { 1373 boolean_t remove = B_FALSE; 1374 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1375 1376 mutex_enter(&ptask->pt_mutex); 1377 1378 switch (ptask->pt_state) { 1379 case PTS_ACTIVE: 1380 remove = B_TRUE; 1381 pppt_task_update_state(ptask, PTS_ABORTED); 1382 break; 1383 case PTS_DONE: 1384 pppt_status = PPPT_STATUS_DONE; 1385 break; 1386 case PTS_SENT_STATUS: 1387 /* 1388 * Already removed so leave remove set to B_FALSE 1389 * and leave status set to PPPT_STATUS_SUCCESS. 1390 */ 1391 pppt_task_update_state(ptask, PTS_ABORTED); 1392 break; 1393 case PTS_ABORTED: 1394 break; 1395 default: 1396 ASSERT(0); 1397 } 1398 1399 mutex_exit(&ptask->pt_mutex); 1400 1401 if (remove) { 1402 mutex_enter(&ptask->pt_sess->ps_mutex); 1403 avl_remove(&ptask->pt_sess->ps_task_list, ptask); 1404 mutex_exit(&ptask->pt_sess->ps_mutex); 1405 /* Out of the AVL tree, so drop a reference. */ 1406 pppt_task_rele(ptask); 1407 } 1408 1409 return (pppt_status); 1410 } 1411 1412 pppt_status_t 1413 pppt_task_hold(pppt_task_t *ptask) 1414 { 1415 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS; 1416 1417 mutex_enter(&ptask->pt_mutex); 1418 if (ptask->pt_state == PTS_ACTIVE) { 1419 ptask->pt_refcnt++; 1420 } else { 1421 pppt_status = PPPT_STATUS_FAIL; 1422 } 1423 mutex_exit(&ptask->pt_mutex); 1424 1425 return (pppt_status); 1426 } 1427 1428 static void 1429 pppt_task_rele(pppt_task_t *ptask) 1430 { 1431 boolean_t freeit; 1432 1433 mutex_enter(&ptask->pt_mutex); 1434 ptask->pt_refcnt--; 1435 freeit = (ptask->pt_refcnt == 0); 1436 mutex_exit(&ptask->pt_mutex); 1437 if (freeit) 1438 pppt_task_free(ptask); 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 }