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