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