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