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