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  */
  24 
  25 #include <sys/cpuvar.h>
  26 #include <sys/types.h>
  27 #include <sys/conf.h>
  28 #include <sys/stat.h>
  29 #include <sys/file.h>
  30 #include <sys/ddi.h>
  31 #include <sys/sunddi.h>
  32 #include <sys/modctl.h>
  33 #include <sys/sysmacros.h>
  34 #include <sys/nvpair.h>
  35 #include <sys/door.h>
  36 #include <sys/sdt.h>
  37 
  38 #include <sys/stmf.h>
  39 #include <sys/stmf_ioctl.h>
  40 #include <sys/pppt_ioctl.h>
  41 #include <sys/portif.h>
  42 
  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, stmf_remote_port_t *rport)
 852 {
 853         pppt_tgt_t                              *tgt;
 854         pppt_sess_t                             *ps;
 855         int                                     lport_cmp;
 856 
 857         ASSERT(mutex_owned(&pppt_global.global_lock));
 858 
 859         /*
 860          * Look for existing session for this ID
 861          */
 862         ps = pppt_sess_lookup_by_id_locked(session_id);
 863         if (ps == NULL) {
 864                 PPPT_INC_STAT(es_sess_lookup_no_session);
 865                 return (NULL);
 866         }
 867 
 868         tgt = ps->ps_target;
 869 
 870         mutex_enter(&tgt->target_mutex);
 871 
 872         /* Validate local/remote port names */
 873         if ((lport_devid->ident_length !=
 874             tgt->target_stmf_lport->lport_id->ident_length) ||
 875             (rport->rport_tptid_sz !=
 876             ps->ps_stmf_sess->ss_rport->rport_tptid_sz)) {
 877                 mutex_exit(&tgt->target_mutex);
 878                 PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
 879                 return (NULL);
 880         } else {
 881                 lport_cmp = bcmp(lport_devid->ident,
 882                     tgt->target_stmf_lport->lport_id->ident,
 883                     lport_devid->ident_length);
 884                 if (lport_cmp != 0 ||
 885                     (stmf_scsilib_tptid_compare(rport->rport_tptid,
 886                     ps->ps_stmf_sess->ss_rport->rport_tptid) != B_TRUE)) {
 887                         mutex_exit(&tgt->target_mutex);
 888                         PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
 889                         return (NULL);
 890                 }
 891 
 892                 if (tgt->target_state != TS_STMF_ONLINE) {
 893                         mutex_exit(&tgt->target_mutex);
 894                         PPPT_INC_STAT(es_sess_lookup_bad_tgt_state);
 895                         return (NULL);
 896                 }
 897         }
 898         mutex_exit(&tgt->target_mutex);
 899 
 900         return (ps);
 901 }
 902 
 903 pppt_sess_t *
 904 pppt_sess_lookup_by_id_locked(uint64_t session_id)
 905 {
 906         pppt_sess_t             tmp_ps;
 907         pppt_sess_t             *ps;
 908 
 909         ASSERT(mutex_owned(&pppt_global.global_lock));
 910         tmp_ps.ps_session_id = session_id;
 911         tmp_ps.ps_closed = 0;
 912         ps = avl_find(&pppt_global.global_sess_list, &tmp_ps, NULL);
 913         if (ps != NULL) {
 914                 mutex_enter(&ps->ps_mutex);
 915                 if (!ps->ps_closed) {
 916                         ps->ps_refcnt++;
 917                         mutex_exit(&ps->ps_mutex);
 918                         return (ps);
 919                 }
 920                 mutex_exit(&ps->ps_mutex);
 921         }
 922 
 923         return (NULL);
 924 }
 925 
 926 /* New session */
 927 pppt_sess_t *
 928 pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid,
 929     scsi_devid_desc_t *rport_devid, stmf_remote_port_t *rport,
 930     uint64_t session_id, stmf_status_t *statusp)
 931 {
 932         pppt_tgt_t              *tgt;
 933         pppt_sess_t             *ps;
 934         stmf_scsi_session_t     *ss;
 935         pppt_sess_t             tmp_ps;
 936         stmf_scsi_session_t     tmp_ss;
 937         *statusp = STMF_SUCCESS;
 938 
 939         PPPT_GLOBAL_LOCK();
 940 
 941         /*
 942          * Look for existing session for this ID
 943          */
 944         ps = pppt_sess_lookup_locked(session_id, lport_devid, rport);
 945 
 946         if (ps != NULL) {
 947                 PPPT_GLOBAL_UNLOCK();
 948                 return (ps);
 949         }
 950 
 951         /*
 952          * No session with that ID, look for another session corresponding
 953          * to the same IT nexus.
 954          */
 955         tgt = pppt_tgt_lookup_locked(lport_devid);
 956         if (tgt == NULL) {
 957                 *statusp = STMF_NOT_FOUND;
 958                 PPPT_GLOBAL_UNLOCK();
 959                 return (NULL);
 960         }
 961 
 962         mutex_enter(&tgt->target_mutex);
 963         if (tgt->target_state != TS_STMF_ONLINE) {
 964                 *statusp = STMF_NOT_FOUND;
 965                 mutex_exit(&tgt->target_mutex);
 966                 PPPT_GLOBAL_UNLOCK();
 967                 /* Can't create session to offline target */
 968                 return (NULL);
 969         }
 970 
 971         bzero(&tmp_ps, sizeof (tmp_ps));
 972         bzero(&tmp_ss, sizeof (tmp_ss));
 973         tmp_ps.ps_stmf_sess = &tmp_ss;
 974         tmp_ss.ss_rport = rport;
 975 
 976         /*
 977          * Look for an existing session on this IT nexus
 978          */
 979         ps = avl_find(&tgt->target_sess_list, &tmp_ps, NULL);
 980 
 981         if (ps != NULL) {
 982                 /*
 983                  * Now check the session ID.  It should not match because if
 984                  * it did we would have found it on the global session list.
 985                  * If the session ID in the command is higher than the existing
 986                  * session ID then we need to tear down the existing session.
 987                  */
 988                 mutex_enter(&ps->ps_mutex);
 989                 ASSERT(ps->ps_session_id != session_id);
 990                 if (ps->ps_session_id > session_id) {
 991                         /* Invalid session ID */
 992                         mutex_exit(&ps->ps_mutex);
 993                         mutex_exit(&tgt->target_mutex);
 994                         PPPT_GLOBAL_UNLOCK();
 995                         *statusp = STMF_INVALID_ARG;
 996                         return (NULL);
 997                 } else {
 998                         /* Existing session needs to be invalidated */
 999                         if (!ps->ps_closed) {
1000                                 pppt_sess_close_locked(ps);
1001                         }
1002                 }
1003                 mutex_exit(&ps->ps_mutex);
1004 
1005                 /* Fallthrough and create new session */
1006         }
1007 
1008         /*
1009          * Allocate and fill in pppt_session_t with the appropriate data
1010          * for the protocol.
1011          */
1012         ps = kmem_zalloc(sizeof (*ps), KM_SLEEP);
1013 
1014         /* Fill in session fields */
1015         ps->ps_target = tgt;
1016         ps->ps_session_id = session_id;
1017 
1018         ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0,
1019             0);
1020         if (ss == NULL) {
1021                 mutex_exit(&tgt->target_mutex);
1022                 PPPT_GLOBAL_UNLOCK();
1023                 kmem_free(ps, sizeof (*ps));
1024                 *statusp = STMF_ALLOC_FAILURE;
1025                 return (NULL);
1026         }
1027 
1028         ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) +
1029             rport_devid->ident_length + 1, KM_SLEEP);
1030         bcopy(rport_devid, ss->ss_rport_id,
1031             sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1);
1032 
1033         ss->ss_lport = tgt->target_stmf_lport;
1034 
1035         ss->ss_rport = stmf_remote_port_alloc(rport->rport_tptid_sz);
1036         bcopy(rport->rport_tptid, ss->ss_rport->rport_tptid,
1037             rport->rport_tptid_sz);
1038 
1039         if (stmf_register_scsi_session(tgt->target_stmf_lport, ss) !=
1040             STMF_SUCCESS) {
1041                 mutex_exit(&tgt->target_mutex);
1042                 PPPT_GLOBAL_UNLOCK();
1043                 kmem_free(ss->ss_rport_id,
1044                     sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1);
1045                 stmf_remote_port_free(ss->ss_rport);
1046                 stmf_free(ss);
1047                 kmem_free(ps, sizeof (*ps));
1048                 *statusp = STMF_TARGET_FAILURE;
1049                 return (NULL);
1050         }
1051 
1052         ss->ss_port_private = ps;
1053         mutex_init(&ps->ps_mutex, NULL, MUTEX_DEFAULT, NULL);
1054         cv_init(&ps->ps_cv, NULL, CV_DEFAULT, NULL);
1055         avl_create(&ps->ps_task_list, pppt_task_avl_compare,
1056             sizeof (pppt_task_t), offsetof(pppt_task_t, pt_sess_ln));
1057         ps->ps_refcnt = 1;
1058         ps->ps_stmf_sess = ss;
1059         avl_add(&tgt->target_sess_list, ps);
1060         avl_add(&pppt_global.global_sess_list, ps);
1061         mutex_exit(&tgt->target_mutex);
1062         PPPT_GLOBAL_UNLOCK();
1063         stmf_trace("pppt", "New session %p", (void *)ps);
1064 
1065         return (ps);
1066 }
1067 
1068 void
1069 pppt_sess_rele(pppt_sess_t *ps)
1070 {
1071         mutex_enter(&ps->ps_mutex);
1072         pppt_sess_rele_locked(ps);
1073         mutex_exit(&ps->ps_mutex);
1074 }
1075 
1076 void
1077 pppt_sess_rele_locked(pppt_sess_t *ps)
1078 {
1079         ASSERT(mutex_owned(&ps->ps_mutex));
1080         ps->ps_refcnt--;
1081         if (ps->ps_refcnt == 0) {
1082                 cv_signal(&ps->ps_cv);
1083         }
1084 }
1085 
1086 static void pppt_sess_destroy_task(void *ps_void)
1087 {
1088         pppt_sess_t *ps = ps_void;
1089         stmf_scsi_session_t     *ss;
1090 
1091         stmf_trace("pppt", "Session destroy task %p", (void *)ps);
1092 
1093         ss = ps->ps_stmf_sess;
1094         mutex_enter(&ps->ps_mutex);
1095         stmf_deregister_scsi_session(ss->ss_lport, ss);
1096         kmem_free(ss->ss_rport_id,
1097             sizeof (scsi_devid_desc_t) + ss->ss_rport_id->ident_length + 1);
1098         stmf_remote_port_free(ss->ss_rport);
1099         avl_destroy(&ps->ps_task_list);
1100         mutex_exit(&ps->ps_mutex);
1101         cv_destroy(&ps->ps_cv);
1102         mutex_destroy(&ps->ps_mutex);
1103         stmf_free(ps->ps_stmf_sess);
1104         kmem_free(ps, sizeof (*ps));
1105 
1106         stmf_trace("pppt", "Session destroy task complete %p", (void *)ps);
1107 }
1108 
1109 int
1110 pppt_sess_avl_compare_by_id(const void *void_sess1, const void *void_sess2)
1111 {
1112         const   pppt_sess_t     *psess1 = void_sess1;
1113         const   pppt_sess_t     *psess2 = void_sess2;
1114 
1115         if (psess1->ps_session_id < psess2->ps_session_id)
1116                 return (-1);
1117         else if (psess1->ps_session_id > psess2->ps_session_id)
1118                 return (1);
1119 
1120         /* Allow multiple duplicate sessions if one is closed */
1121         ASSERT(!(psess1->ps_closed && psess2->ps_closed));
1122         if (psess1->ps_closed)
1123                 return (-1);
1124         else if (psess2->ps_closed)
1125                 return (1);
1126 
1127         return (0);
1128 }
1129 
1130 int
1131 pppt_sess_avl_compare_by_name(const void *void_sess1, const void *void_sess2)
1132 {
1133         const   pppt_sess_t     *psess1 = void_sess1;
1134         const   pppt_sess_t     *psess2 = void_sess2;
1135         int                     result;
1136 
1137         /* Compare by tptid size */
1138         if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz <
1139             psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) {
1140                 return (-1);
1141         } else if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz >
1142             psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) {
1143                 return (1);
1144         }
1145 
1146         /* Now compare tptid */
1147         result = memcmp(psess1->ps_stmf_sess->ss_rport->rport_tptid,
1148             psess2->ps_stmf_sess->ss_rport->rport_tptid,
1149             psess1->ps_stmf_sess->ss_rport->rport_tptid_sz);
1150 
1151         if (result < 0) {
1152                 return (-1);
1153         } else if (result > 0) {
1154                 return (1);
1155         }
1156 
1157         return (0);
1158 }
1159 
1160 void
1161 pppt_sess_close_locked(pppt_sess_t *ps)
1162 {
1163         pppt_tgt_t      *tgt = ps->ps_target;
1164         pppt_task_t     *ptask;
1165 
1166         stmf_trace("pppt", "Session close %p", (void *)ps);
1167 
1168         ASSERT(mutex_owned(&pppt_global.global_lock));
1169         ASSERT(mutex_owned(&tgt->target_mutex));
1170         ASSERT(mutex_owned(&ps->ps_mutex));
1171         ASSERT(!ps->ps_closed); /* Caller should ensure session is not closed */
1172 
1173         ps->ps_closed = B_TRUE;
1174         for (ptask = avl_first(&ps->ps_task_list); ptask != NULL;
1175             ptask = AVL_NEXT(&ps->ps_task_list, ptask)) {
1176                 mutex_enter(&ptask->pt_mutex);
1177                 if (ptask->pt_state == PTS_ACTIVE) {
1178                         stmf_abort(STMF_QUEUE_TASK_ABORT, ptask->pt_stmf_task,
1179                             STMF_ABORTED, NULL);
1180                 }
1181                 mutex_exit(&ptask->pt_mutex);
1182         }
1183 
1184         /*
1185          * Now that all the tasks are aborting the session refcnt should
1186          * go to 0.
1187          */
1188         while (ps->ps_refcnt != 0) {
1189                 cv_wait(&ps->ps_cv, &ps->ps_mutex);
1190         }
1191 
1192         avl_remove(&tgt->target_sess_list, ps);
1193         avl_remove(&pppt_global.global_sess_list, ps);
1194         (void) taskq_dispatch(pppt_global.global_sess_taskq,
1195             &pppt_sess_destroy_task, ps, KM_SLEEP);
1196 
1197         stmf_trace("pppt", "Session close complete %p", (void *)ps);
1198 }
1199 
1200 pppt_task_t *
1201 pppt_task_alloc(void)
1202 {
1203         pppt_task_t     *ptask;
1204         pppt_buf_t      *immed_pbuf;
1205 
1206         ptask = kmem_alloc(sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
1207             sizeof (stmf_data_buf_t), KM_NOSLEEP);
1208         if (ptask != NULL) {
1209                 ptask->pt_state = PTS_INIT;
1210                 ptask->pt_read_buf = NULL;
1211                 ptask->pt_read_xfer_msgid = 0;
1212                 cv_init(&ptask->pt_cv, NULL, CV_DRIVER, NULL);
1213                 mutex_init(&ptask->pt_mutex, NULL, MUTEX_DRIVER, NULL);
1214                 immed_pbuf = (pppt_buf_t *)(ptask + 1);
1215                 bzero(immed_pbuf, sizeof (*immed_pbuf));
1216                 immed_pbuf->pbuf_is_immed = B_TRUE;
1217                 immed_pbuf->pbuf_stmf_buf = (stmf_data_buf_t *)(immed_pbuf + 1);
1218 
1219                 bzero(immed_pbuf->pbuf_stmf_buf, sizeof (stmf_data_buf_t));
1220                 immed_pbuf->pbuf_stmf_buf->db_port_private = immed_pbuf;
1221                 immed_pbuf->pbuf_stmf_buf->db_sglist_length = 1;
1222                 immed_pbuf->pbuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT |
1223                     DB_DONT_CACHE;
1224                 ptask->pt_immed_data = immed_pbuf;
1225         }
1226 
1227         return (ptask);
1228 
1229 }
1230 
1231 void
1232 pppt_task_free(pppt_task_t *ptask)
1233 {
1234         mutex_enter(&ptask->pt_mutex);
1235         mutex_destroy(&ptask->pt_mutex);
1236         cv_destroy(&ptask->pt_cv);
1237         kmem_free(ptask, sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
1238             sizeof (stmf_data_buf_t));
1239 }
1240 
1241 pppt_status_t
1242 pppt_task_start(pppt_task_t *ptask)
1243 {
1244         avl_index_t             where;
1245 
1246         ASSERT(ptask->pt_state == PTS_INIT);
1247 
1248         mutex_enter(&ptask->pt_sess->ps_mutex);
1249         mutex_enter(&ptask->pt_mutex);
1250         if (avl_find(&ptask->pt_sess->ps_task_list, ptask, &where) == NULL) {
1251                 pppt_task_update_state(ptask, PTS_ACTIVE);
1252                 avl_insert(&ptask->pt_sess->ps_task_list, ptask, where);
1253                 mutex_exit(&ptask->pt_mutex);
1254                 mutex_exit(&ptask->pt_sess->ps_mutex);
1255                 return (PPPT_STATUS_SUCCESS);
1256         }
1257         mutex_exit(&ptask->pt_mutex);
1258         mutex_exit(&ptask->pt_sess->ps_mutex);
1259 
1260         return (PPPT_STATUS_FAIL);
1261 }
1262 
1263 pppt_status_t
1264 pppt_task_done(pppt_task_t *ptask)
1265 {
1266         pppt_status_t   pppt_status = PPPT_STATUS_SUCCESS;
1267         boolean_t       remove = B_FALSE;
1268 
1269         mutex_enter(&ptask->pt_mutex);
1270 
1271         switch (ptask->pt_state) {
1272         case PTS_ACTIVE:
1273                 remove = B_TRUE;
1274                 pppt_task_update_state(ptask, PTS_DONE);
1275                 break;
1276         case PTS_ABORTED:
1277                 pppt_status = PPPT_STATUS_ABORTED;
1278                 break;
1279         case PTS_DONE:
1280                 /* Repeat calls are OK.  Do nothing, return success */
1281                 break;
1282         default:
1283                 ASSERT(0);
1284         }
1285 
1286         mutex_exit(&ptask->pt_mutex);
1287 
1288         if (remove) {
1289                 mutex_enter(&ptask->pt_sess->ps_mutex);
1290                 avl_remove(&ptask->pt_sess->ps_task_list, ptask);
1291                 mutex_exit(&ptask->pt_sess->ps_mutex);
1292         }
1293 
1294         return (pppt_status);
1295 }
1296 
1297 void
1298 pppt_task_sent_status(pppt_task_t *ptask)
1299 {
1300         /*
1301          * If STMF tries to abort a task after the task state changed to
1302          * PTS_DONE (meaning all task processing is complete from
1303          * the port provider perspective) then we return STMF_BUSY
1304          * from pppt_lport_abort.  STMF will return after a short interval
1305          * but our calls to stmf_send_status_done will be ignored since
1306          * STMF is aborting the task.  That's where this state comes in.
1307          * This state essentially says we are calling stmf_send_status_done
1308          * so we will not be touching the task again.  The next time
1309          * STMF calls pppt_lport_abort we will return a success full
1310          * status and the abort will succeed.
1311          */
1312         mutex_enter(&ptask->pt_mutex);
1313         pppt_task_update_state(ptask, PTS_SENT_STATUS);
1314         mutex_exit(&ptask->pt_mutex);
1315 }
1316 
1317 pppt_task_t *
1318 pppt_task_lookup(stmf_ic_msgid_t msgid)
1319 {
1320         pppt_tgt_t      *tgt;
1321         pppt_sess_t     *sess;
1322         pppt_task_t     lookup_task;
1323         pppt_task_t     *result;
1324 
1325         bzero(&lookup_task, sizeof (lookup_task));
1326         lookup_task.pt_task_id = msgid;
1327         PPPT_GLOBAL_LOCK();
1328         for (tgt = avl_first(&pppt_global.global_target_list); tgt != NULL;
1329             tgt = AVL_NEXT(&pppt_global.global_target_list, tgt)) {
1330 
1331                 mutex_enter(&tgt->target_mutex);
1332                 for (sess = avl_first(&tgt->target_sess_list); sess != NULL;
1333                     sess = AVL_NEXT(&tgt->target_sess_list, sess)) {
1334                         mutex_enter(&sess->ps_mutex);
1335                         if ((result = avl_find(&sess->ps_task_list,
1336                             &lookup_task, NULL)) != NULL) {
1337                                 if (pppt_task_hold(result) !=
1338                                     PPPT_STATUS_SUCCESS) {
1339                                         result = NULL;
1340                                 }
1341                                 mutex_exit(&sess->ps_mutex);
1342                                 mutex_exit(&tgt->target_mutex);
1343                                 PPPT_GLOBAL_UNLOCK();
1344                                 return (result);
1345                         }
1346                         mutex_exit(&sess->ps_mutex);
1347                 }
1348                 mutex_exit(&tgt->target_mutex);
1349         }
1350         PPPT_GLOBAL_UNLOCK();
1351 
1352         return (NULL);
1353 }
1354 
1355 static int
1356 pppt_task_avl_compare(const void *void_task1, const void *void_task2)
1357 {
1358         const pppt_task_t       *ptask1 = void_task1;
1359         const pppt_task_t       *ptask2 = void_task2;
1360 
1361         if (ptask1->pt_task_id < ptask2->pt_task_id)
1362                 return (-1);
1363         else if (ptask1->pt_task_id > ptask2->pt_task_id)
1364                 return (1);
1365 
1366         return (0);
1367 }
1368 
1369 static pppt_status_t
1370 pppt_task_try_abort(pppt_task_t *ptask)
1371 {
1372         boolean_t       remove = B_FALSE;
1373         pppt_status_t   pppt_status = PPPT_STATUS_SUCCESS;
1374 
1375         mutex_enter(&ptask->pt_mutex);
1376 
1377         switch (ptask->pt_state) {
1378         case PTS_ACTIVE:
1379                 remove = B_TRUE;
1380                 pppt_task_update_state(ptask, PTS_ABORTED);
1381                 break;
1382         case PTS_DONE:
1383                 pppt_status = PPPT_STATUS_DONE;
1384                 break;
1385         case PTS_SENT_STATUS:
1386                 /*
1387                  * Already removed so leave remove set to B_FALSE
1388                  * and leave status set to PPPT_STATUS_SUCCESS.
1389                  */
1390                 pppt_task_update_state(ptask, PTS_ABORTED);
1391                 break;
1392         case PTS_ABORTED:
1393                 break;
1394         default:
1395                 ASSERT(0);
1396         }
1397 
1398         mutex_exit(&ptask->pt_mutex);
1399 
1400         if (remove) {
1401                 mutex_enter(&ptask->pt_sess->ps_mutex);
1402                 avl_remove(&ptask->pt_sess->ps_task_list, ptask);
1403                 mutex_exit(&ptask->pt_sess->ps_mutex);
1404         }
1405 
1406         return (pppt_status);
1407 }
1408 
1409 static pppt_status_t
1410 pppt_task_hold(pppt_task_t *ptask)
1411 {
1412         pppt_status_t   pppt_status = PPPT_STATUS_SUCCESS;
1413 
1414         mutex_enter(&ptask->pt_mutex);
1415         if (ptask->pt_state == PTS_ACTIVE) {
1416                 ptask->pt_refcnt++;
1417         } else {
1418                 pppt_status = PPPT_STATUS_FAIL;
1419         }
1420         mutex_exit(&ptask->pt_mutex);
1421 
1422         return (pppt_status);
1423 }
1424 
1425 static void
1426 pppt_task_rele(pppt_task_t *ptask)
1427 {
1428         mutex_enter(&ptask->pt_mutex);
1429         ptask->pt_refcnt--;
1430         cv_signal(&ptask->pt_cv);
1431         mutex_exit(&ptask->pt_mutex);
1432 }
1433 
1434 static void
1435 pppt_task_update_state(pppt_task_t *ptask,
1436     pppt_task_state_t new_state)
1437 {
1438         PPPT_LOG(CE_NOTE, "task %p %d -> %d", (void *)ptask,
1439             ptask->pt_state, new_state);
1440 
1441         ASSERT(mutex_owned(&ptask->pt_mutex));
1442         ptask->pt_state = new_state;
1443         cv_signal(&ptask->pt_cv);
1444 }