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