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