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 src/sun_nws/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 src/sun_nws/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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "@(#)idm.c      1.19    08/03/26 SMI"
  27 
  28 #include <sys/cpuvar.h>
  29 #include <sys/conf.h>
  30 #include <sys/file.h>
  31 #include <sys/ddi.h>
  32 #include <sys/sunddi.h>
  33 #include <sys/modctl.h>
  34 #include <sys/priv.h>
  35 
  36 #include <sys/socket.h>
  37 #include <sys/strsubr.h>
  38 #include <sys/sysmacros.h>
  39 
  40 #include <netinet/tcp.h>  /* TCP_NODELAY */
  41 #include <sys/socketvar.h>        /* _ALLOC_SLEEP */
  42 #include <netinet/in.h>
  43 #include <arpa/inet.h>
  44 
  45 #include <idm.h>
  46 
  47 #define IDM_NAME_VERSION        "iSCSI Data Mover"
  48 
  49 extern struct mod_ops mod_miscops;
  50 extern struct mod_ops mod_miscops;
  51 
  52 static struct modlmisc modlmisc = {
  53         &mod_miscops,       /* Type of module */
  54         IDM_NAME_VERSION
  55 };
  56 
  57 static struct modlinkage modlinkage = {
  58         MODREV_1, (void *)&modlmisc, NULL
  59 };
  60 
  61 /*
  62  * IDM Native Sockets transport operations
  63  */
  64 idm_transport_ops_t idm_so_transport_ops = {
  65         &idm_so_tx,                 /* it_tx_pdu */
  66         &idm_so_buf_tx_to_ini,              /* it_buf_tx_to_ini */
  67         &idm_so_buf_rx_from_ini,    /* it_buf_rx_from_ini */
  68         &idm_so_rx_datain,          /* it_rx_datain */
  69         &idm_so_rx_rtt,                     /* it_rx_rtt */
  70         &idm_so_rx_dataout,         /* it_rx_dataout */
  71         NULL,                           /* it_alloc_conn_rsrc */
  72         NULL,                           /* it_free_conn_rsrc */
  73         NULL,                           /* it_enable_datamover */
  74         NULL,                           /* it_conn_terminate */
  75         NULL,                           /* it_free_task_rsrc */
  76         &idm_so_notice_key_values,  /* it_notice_key_values */
  77         &idm_so_conn_is_capable,    /* it_conn_is_capable */
  78         &idm_so_buf_setup,          /* it_buf_setup */
  79         &idm_so_buf_teardown,               /* it_buf_teardown */
  80         &idm_so_tgt_svc_create,             /* it_tgt_svc_create */
  81         &idm_so_tgt_svc_destroy,    /* it_tgt_svc_destroy */
  82         &idm_so_tgt_svc_online,             /* it_tgt_svc_online */
  83         &idm_so_tgt_svc_offline,    /* it_tgt_svc_offline */
  84         &idm_so_tgt_conn_connect,   /* it_tgt_conn_connect */
  85         &idm_so_ini_conn_create,    /* it_ini_conn_create */
  86         &idm_so_ini_conn_destroy,   /* it_ini_conn_destroy */
  87         &idm_so_ini_conn_connect,   /* it_ini_conn_connect */
  88         &idm_so_ini_conn_disconnect /* it_ini_conn_disconnect */
  89 };
  90 
  91 /*
  92  * Global list of transport handles
  93  *   These are listed in preferential order, so we can simply take the
  94  *   first "it_conn_is_capable" hit. Note also that the order maps to
  95  *   the order of the idm_transport_type_t list.
  96  */
  97 idm_transport_t idm_transport_list[] = {
  98 
  99         /* iSER on InfiniBand transport handle */
 100         {IDM_TRANSPORT_TYPE_ISER,       /* type */
 101         "/devices/ib/iser@0:iser",      /* device path */
 102         NULL,                           /* LDI handle */
 103         NULL,                           /* transport ops */
 104         NULL},                          /* transport caps */
 105 
 106         /* IDM native sockets transport handle */
 107         {IDM_TRANSPORT_TYPE_SOCKETS,    /* type */
 108         NULL,                           /* device path */
 109         NULL,                           /* LDI handle */
 110         NULL,                           /* transport ops */
 111         NULL}                           /* transport caps */
 112 
 113 };
 114 
 115 extern int idm_task_compare(const void *t1, const void *t2);
 116 static int _idm_init(void);
 117 static void _idm_fini(void);
 118 
 119 /*
 120  * Hopefully once the socket transport API is in place we can consolidate
 121  * all these implementation details into the socket transport code.
 122  *  JBDB - these resources are allocated during init - do we want a
 123  *          sockets transport init?  if not, then these stay here, i think
 124  */
 125 extern kmem_cache_t     *idm_sotx_pdu_cache;
 126 extern kmem_cache_t     *idm_sorx_pdu_cache;
 127 kmem_cache_t    *idm_buf_cache;
 128 kmem_cache_t    *idm_task_cache;
 129 vmem_t          *idm_taskid_cache;
 130 idm_idpool_t    idm_conn_id_pool;
 131 
 132 idm_global_t    idm; /* Global state */
 133 
 134 char *idm_cs_name[CS_MAX_STATE]; /* Connection state names */
 135 char *idm_ce_name[CE_MAX_EVENT]; /* Connection event names */
 136 
 137 int
 138 _init(void)
 139 {
 140         int rc;
 141 
 142         if ((rc = _idm_init()) != 0) {
 143                 return (rc);
 144         }
 145 
 146         return (mod_install(&modlinkage));
 147 }
 148 
 149 int
 150 _fini(void)
 151 {
 152 
 153         _idm_fini();
 154         return (mod_remove(&modlinkage));
 155 }
 156 
 157 int
 158 _info(struct modinfo *modinfop)
 159 {
 160         return (mod_info(&modlinkage, modinfop));
 161 }
 162 
 163 /*
 164  * idm_ini_conn_create
 165  *
 166  * This function is invoked by the iSCSI layer to create a connection context.
 167  * This does not actually establish the socket connection.
 168  *
 169  * cr - Connection request parameters
 170  * new_con - Output parameter that contains the new request if successful
 171  *
 172  */
 173 idm_status_t
 174 idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con)
 175 {
 176         idm_transport_caps_t    *caps;
 177         idm_transport_type_t    type;
 178         idm_transport_t         *it;
 179         idm_conn_t              *ic;
 180         int                     rc;
 181 
 182         ic = kmem_zalloc(sizeof (idm_conn_t), KM_SLEEP);
 183 
 184         /* Initialize data */
 185         mutex_init(&ic->ic_mutex, NULL, MUTEX_DEFAULT, NULL);
 186         cv_init(&ic->ic_cv, NULL, CV_DEFAULT, NULL);
 187         ic->ic_conn_type = CONN_TYPE_INI;
 188         ic->ic_conn_ops = cr->icr_conn_ops;
 189         ic->ic_internal_cid = idm_cid_alloc();
 190         if (ic->ic_internal_cid == 0) {
 191                 kmem_free(ic, sizeof (idm_conn_t));
 192                 return (IDM_STATUS_FAIL);
 193         }
 194 
 195         /* Determine the transport for this connection */
 196         for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
 197                 it = &idm_transport_list[type];
 198 
 199                 /*
 200                  * JBDB LATER
 201                  * if ((it->ldi_hdl == NULL) && (it->type != SOCKETS))
 202                  *      ldi_open_by_devname(it->device_path)
 203                  * but, for now...
 204                  */
 205                 if (it->it_ops == NULL) {
 206                         /* transport is not registered */
 207                         continue;
 208                 }
 209 
 210                 rc = it->it_ops->it_conn_is_capable(cr, caps);
 211                 if (rc == IDM_STATUS_SUCCESS) {
 212                         ic->ic_transport_ops = it->it_ops;
 213                         ic->ic_transport_type = type;
 214                         ic->ic_transport_ops->it_buf_tx_to_ini = NULL;
 215                         ic->ic_transport_ops->it_buf_rx_from_ini = NULL;
 216                         break;
 217                 }
 218         }
 219 
 220         /* create an avl tree to maintain active tasks per connection */
 221         avl_create(&ic->ic_task_tree, idm_task_compare, sizeof (idm_task_t),
 222             offsetof(idm_task_t, idt_avl_link));
 223 
 224         bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr,
 225             sizeof (cr->cr_ini_dst_addr));
 226 
 227         /* create the transport-specific connection components */
 228         rc = it->it_ops->it_ini_conn_create(cr, ic);
 229         if (rc != IDM_STATUS_SUCCESS) {
 230                 avl_destroy(&ic->ic_task_tree);
 231                 kmem_free(ic, sizeof (idm_conn_t));
 232                 return (IDM_STATUS_FAIL); /* XXX Error? */
 233         }
 234 
 235         *new_con = ic;
 236 
 237         mutex_enter(&idm.idm_global_mutex);
 238         list_insert_tail(&idm.idm_ini_conn_list, ic);
 239         mutex_exit(&idm.idm_global_mutex);
 240 
 241         return (IDM_STATUS_SUCCESS);
 242 }
 243 
 244 /*
 245  * idm_ini_conn_destroy
 246  *
 247  * Releases any resources associated with the connection.  This is the
 248  * complement to idm_ini_conn_create.
 249  * ic - idm_conn_t structure representing the relevant connection
 250  *
 251  */
 252 void
 253 idm_ini_conn_destroy(idm_conn_t *ic)
 254 {
 255         idm_task_t      *tnode;
 256         void            *cookie = NULL;
 257 
 258         mutex_enter(&idm.idm_global_mutex);
 259         list_remove(&idm.idm_ini_conn_list, ic);
 260         mutex_exit(&idm.idm_global_mutex);
 261 
 262         /* destroy the nodes in the tree */
 263         while ((tnode =
 264             avl_destroy_nodes(&ic->ic_task_tree, &cookie)) != NULL) {
 265                 /* free data ? */
 266                 kmem_cache_free(idm_task_cache, tnode);
 267         }
 268         avl_destroy(&ic->ic_task_tree);
 269 
 270         /* teardown the transport connection resources */
 271         (void) ic->ic_transport_ops->it_ini_conn_destroy(ic);
 272 
 273         kmem_free(ic, sizeof (idm_conn_t));
 274 }
 275 
 276 /*
 277  * idm_ini_conn_connect
 278  *
 279  * Establish connection to the remote system identified in idm_conn_t.
 280  * The connection parameters including the remote IP address were established
 281  * in the call to idm_ini_conn_create.
 282  *
 283  * ic - idm_conn_t structure representing the relevant connection
 284  *
 285  * Returns success if the connection was established, otherwise some kind
 286  * of meaningful error code.
 287  *
 288  * Upon return the initiator can send a "login" request when it is ready.
 289  */
 290 idm_status_t
 291 idm_ini_conn_connect(idm_conn_t *ic)
 292 {
 293         idm_status_t    istat;
 294         if ((istat = idm_conn_sm_init(ic)) != 0) {
 295                 /* CRM: should I cleanup here or in iscsi? */
 296                 return (ic->ic_conn_sm_status);
 297         }
 298         /* Kick state machine */
 299         idm_conn_event(ic, CE_CONNECT_REQ, NULL);
 300 
 301         /* Wait for login flag */
 302         mutex_enter(&ic->ic_state_mutex);
 303         while (!(ic->ic_state_flags & CF_LOGIN_READY) &&
 304             !(ic->ic_state_flags & CF_ERROR)) {
 305                 cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
 306         }
 307         mutex_exit(&ic->ic_state_mutex);
 308 
 309         if (ic->ic_state_flags & CF_ERROR) {
 310                 /* ic->ic_conn_sm_status will contains failure status */
 311                 return (ic->ic_conn_sm_status);
 312         }
 313 
 314         /* Ready to login */
 315         ASSERT(ic->ic_state_flags & CF_LOGIN_READY);
 316 
 317         return (0);
 318 }
 319 
 320 /*
 321  * idm_ini_conn_disconnect
 322  *
 323  * Forces a connection (previously established using idm_ini_conn_connect)
 324  * to perform a controlled shutdown, cleaning up any outstanding requests.
 325  *
 326  * ic - idm_conn_t structure representing the relevant connection
 327  *
 328  * ** For now lets assume this is synchronous and it will return when the
 329  * connection has been properly shutdown.
 330  */
 331 
 332 void
 333 idm_ini_conn_disconnect(idm_conn_t *ic)
 334 {
 335 
 336 }
 337 
 338 /*
 339  * idm_tgt_svc_create
 340  *
 341  * The target calls this service to obtain a service context for each available
 342  * transport, starting a service of each type related to the IP address and port
 343  * passed. The idm_svc_req_t contains the service parameters.
 344  */
 345 idm_status_t
 346 idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc)
 347 {
 348         idm_transport_type_t    type;
 349         idm_transport_t         *it;
 350         idm_svc_t               *is;
 351         int                     rc;
 352 
 353         /*
 354          * XXX We really need a "server" state machine here.  The state
 355          * machine should monitor the state of the listening connection.
 356          * As connections are accepted the server state machine keeps
 357          * track of outstanding connections.  When the service is terminated
 358          * with idm_tgt_svc_disconnect the state machine performs a controlled
 359          * shutdown, terminating each connection.
 360          *
 361          * For now we can probably get by without it.
 362          */
 363         *new_svc = NULL;
 364         is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP);
 365 
 366         /* Initialize transport-agnostic components of the service handle */
 367         is->is_port = sr->sr_port;
 368         is->is_conn_ops = sr->sr_conn_ops;
 369         mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL);
 370         cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL);
 371         mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL);
 372         cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL);
 373         list_create(&is->is_conn_list, sizeof (idm_conn_t),
 374             offsetof(idm_conn_t, ic_list_node));
 375 
 376         /*
 377          * Loop through the transports, configuring the transport-specific
 378          * components of each one.
 379          */
 380         for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
 381                 it = &idm_transport_list[type];
 382                 if (it->it_ops == NULL) {
 383                         /* transport is not registered */
 384                         continue;
 385                 }
 386 
 387                 rc = it->it_ops->it_tgt_svc_create(sr, is);
 388                 if (rc != IDM_STATUS_SUCCESS) {
 389                         /*
 390                          * JBDB - how best to clean up a single failure
 391                          * when multiple transports are being config'd?
 392                          */
 393                         kmem_free(is, sizeof (idm_svc_t));
 394                         return (rc);
 395                 }
 396         }
 397 
 398         *new_svc = is;
 399 
 400         mutex_enter(&idm.idm_global_mutex);
 401         list_insert_tail(&idm.idm_tgt_svc_list, is);
 402         mutex_exit(&idm.idm_global_mutex);
 403 
 404         return (IDM_STATUS_SUCCESS);
 405 }
 406 
 407 /*
 408  * idm_tgt_svc_destroy
 409  *
 410  * is - idm_svc_t returned by the call to idm_tgt_svc_create
 411  *
 412  * Cleanup any resources associated with the idm_svc_t.
 413  */
 414 void
 415 idm_tgt_svc_destroy(idm_svc_t *is)
 416 {
 417         idm_transport_type_t    type;
 418         idm_transport_t         *it;
 419 
 420         /* remove this service from the global list */
 421         mutex_enter(&idm.idm_global_mutex);
 422         list_remove(&idm.idm_tgt_svc_list, is);
 423         mutex_exit(&idm.idm_global_mutex);
 424 
 425         /* tear down the svc resources */
 426         list_destroy(&is->is_conn_list);
 427         cv_destroy(&is->is_count_cv);
 428         mutex_destroy(&is->is_count_mutex);
 429         cv_destroy(&is->is_cv);
 430         mutex_destroy(&is->is_mutex);
 431 
 432         /* teardown each transport-specific service */
 433         for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
 434                 it = &idm_transport_list[type];
 435                 if (it->it_ops == NULL) {
 436                         continue;
 437                 }
 438 
 439                 it->it_ops->it_tgt_svc_destroy(is);
 440         }
 441 
 442         /* free the svc handle */
 443         kmem_free(is, sizeof (idm_svc_t));
 444 }
 445 
 446 /*
 447  * idm_tgt_svc_online
 448  *
 449  * is - idm_svc_t returned by the call to idm_tgt_svc_create
 450  *
 451  * Online each transport service, as we want this target to be accessible
 452  * via any configured transport.
 453  *
 454  * When the initiator establishes a new connection to the target, IDM will
 455  * call the "new connect" callback defined in the idm_svc_req_t structure
 456  * and it will pass an idm_conn_t structure representing that new connection.
 457  */
 458 idm_status_t
 459 idm_tgt_svc_online(idm_svc_t *is)
 460 {
 461 
 462         idm_transport_type_t    type;
 463         idm_transport_t         *it;
 464         int                     rc;
 465 
 466         /* Walk through each of the transports and online them */
 467         for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
 468                 it = &idm_transport_list[type];
 469                 if (it->it_ops == NULL) {
 470                         /* transport is not registered */
 471                         continue;
 472                 }
 473 
 474                 rc = it->it_ops->it_tgt_svc_online(is);
 475                 if (rc != IDM_STATUS_SUCCESS) {
 476                         /*
 477                          * JBDB - currently, iscsit invokes this routine,
 478                          * and cleans up after itself.  How best to handle
 479                          * this for multiple transports?
 480                          */
 481                         return (IDM_STATUS_FAIL);
 482                 }
 483         }
 484 
 485         return (IDM_STATUS_SUCCESS);
 486 }
 487 
 488 /*
 489  * idm_tgt_svc_offline
 490  *
 491  * is - idm_svc_t returned by the call to idm_tgt_svc_create
 492  *
 493  * Shutdown any online target services.
 494  */
 495 void
 496 idm_tgt_svc_offline(idm_svc_t *is)
 497 {
 498         idm_transport_type_t    type;
 499         idm_transport_t         *it;
 500 
 501         /* Walk through each of the transports and offline them */
 502         for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
 503                 it = &idm_transport_list[type];
 504                 if (it->it_ops == NULL) {
 505                         /* transport is not registered */
 506                         continue;
 507                 }
 508 
 509                 it->it_ops->it_tgt_svc_offline(is);
 510         }
 511 }
 512 
 513 /*
 514  * idm_tgt_svc_lookup
 515  *
 516  * Lookup a service instance listening on the specified port
 517  */
 518 idm_svc_t *
 519 idm_tgt_svc_lookup(uint16_t port)
 520 {
 521         idm_svc_t *result;
 522 
 523         for (result = list_head(&idm.idm_tgt_svc_list);
 524             result != NULL;
 525             result = list_next(&idm.idm_tgt_svc_list, result)) {
 526                 if (result->is_port == port) {
 527                         return (result);
 528                 }
 529         }
 530 
 531         return (NULL);
 532 }
 533 
 534 /*
 535  * idm_notice_key_values()
 536  * Passes the set of key value pairs to the transport for validatation.
 537  * This will be invoked once the connection is established.
 538  */
 539 idm_status_t
 540 idm_notice_key_values(idm_conn_t *ic, nvlist_t *request_nvl,
 541     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
 542 {
 543         int                     rc;
 544 
 545         ASSERT(ic->ic_transport_ops != NULL);
 546 
 547         rc = ic->ic_transport_ops->it_notice_key_values(ic, request_nvl,
 548             response_nvl, negotiated_nvl);
 549         if (rc != IDM_STATUS_SUCCESS) {
 550                 /* JBDB - this is unlikely, what's the best course of action? */
 551         }
 552 
 553         return (rc);
 554 }
 555 
 556 /*
 557  * idm_buf_tx_to_ini
 558  *
 559  * This is IDM's implementation of the 'Put_Data' operational primitive.
 560  *
 561  * This function is invoked by a target iSCSI layer to request its local
 562  * Datamover layer to transmit the Data-In PDU to the peer iSCSI layer
 563  * on the remote iSCSI node. The I/O buffer represented by 'idb' is
 564  * transferred to the initiator associated with task 'idt'. The connection
 565  * info, contents of the Data-In PDU header, the DataDescriptorIn, BHS,
 566  * and the callback (idb->idb_buf_cb) at transfer completion are
 567  * provided as input.
 568  *
 569  * This data transfer takes place transparently to the remote iSCSI layer,
 570  * i.e. without its participation.
 571  *
 572  * Using sockets, IDM implements the data transfer by segmenting the data
 573  * buffer into appropriately sized iSCSI PDUs and transmitting them to the
 574  * initiator. iSER performs the transfer using RDMA write.
 575  *
 576  */
 577 idm_status_t
 578 idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb,
 579     uint32_t offset, uint32_t xfer_len,
 580     idm_buf_cb_t idb_buf_cb, void *cb_arg)
 581 {
 582         idm_status_t rc;
 583 
 584         idb->idb_task_binding = idt;
 585         idb->idb_bufoffset = offset;
 586         idb->idb_xfer_len = xfer_len;
 587         idb->idb_buf_cb = idb_buf_cb;
 588         idb->idb_cb_arg = cb_arg;
 589         /*
 590          * "In" buf list is for "Data In" PDU's, "Out" buf list is for
 591          * "Data Out" PDU's
 592          */
 593         mutex_enter(&idt->idt_mutex);
 594         idt->idt_tx_to_ini_start++;
 595         idm_listbuf_insert(&idt->idt_inbufv, idb, offset);
 596         mutex_exit(&idt->idt_mutex);
 597 
 598         rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini)(idt, idb);
 599 
 600         return (rc);
 601 }
 602 
 603 /*
 604  * idm_buf_rx_from_ini
 605  *
 606  * This is IDM's implementation of the 'Get_Data' operational primitive.
 607  *
 608  * This function is invoked by a target iSCSI layer to request its local
 609  * Datamover layer to retrieve certain data identified by the R2T PDU from the
 610  * peer iSCSI layer on the remote node. The retrieved Data-Out PDU will be
 611  * mapped to the respective buffer by the task tags (ITT & TTT).
 612  * The connection information, contents of an R2T PDU, DataDescriptor, BHS, and
 613  * the callback (idb->idb_buf_cb) notification for data transfer completion are
 614  * are provided as input.
 615  *
 616  * When an iSCSI node sends an R2T PDU to its local Datamover layer, the local
 617  * Datamover layer, the local and remote Datamover layers transparently bring
 618  * about the data transfer requested by the R2T PDU, without the participation
 619  * of the iSCSI layers.
 620  *
 621  * Using sockets, IDM transmits an R2T PDU for each buffer and the rx_data_out()
 622  * assembles the Data-Out PDUs into the buffer. iSER uses RDMA read.
 623  *
 624  */
 625 idm_status_t
 626 idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb,
 627     uint32_t offset, uint32_t xfer_len,
 628     idm_buf_cb_t idb_buf_cb, void *cb_arg)
 629 {
 630         idm_status_t rc;
 631 
 632         idb->idb_task_binding = idt;
 633         idb->idb_bufoffset = offset;
 634         idb->idb_xfer_len = xfer_len;
 635         idb->idb_buf_cb = idb_buf_cb;
 636         idb->idb_cb_arg = cb_arg;
 637         /*
 638          * "In" buf list is for "Data In" PDU's, "Out" buf list is for
 639          * "Data Out" PDU's
 640          */
 641         mutex_enter(&idt->idt_mutex);
 642         idt->idt_rx_from_ini_start++;
 643         idm_listbuf_insert(&idt->idt_outbufv, idb, offset);
 644         mutex_exit(&idt->idt_mutex);
 645 
 646         rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini)(idt, idb);
 647 
 648         return (rc);
 649 }
 650 
 651 /*
 652  * idm_buf_alloc
 653  *
 654  * Allocates a buffer handle and allocates a buffer of size "buflen" if the
 655  * bufptr is NULL. If bufptr is not NULL, bufptr is assigned to the buffer.
 656  *
 657  * ic           - connection on which the buffer will be transferred
 658  * bufptr       - allocate memory for buffer if NULL, else assign to buffer
 659  * buflen       - length of buffer
 660  * flags        - data transfer direction  'inbound' or 'outbound'
 661  *
 662  * Returns idm_buf_t handle if successful, otherwise NULL
 663  */
 664 idm_buf_t *
 665 idm_buf_alloc(idm_conn_t *ic, void *bufptr, uint64_t buflen)
 666 {
 667         idm_buf_t *buf = NULL;
 668 
 669         ASSERT(ic != NULL);
 670         ASSERT(idm_buf_cache != NULL);
 671         ASSERT(buflen > 0);
 672 
 673         buf = kmem_cache_alloc(idm_buf_cache, KM_NOSLEEP);
 674         if (buf == NULL) {
 675                 return (NULL);
 676         }
 677 
 678         buf->idb_buf = (bufptr == NULL) ?
 679             kmem_alloc(buflen, KM_NOSLEEP) : bufptr;
 680 
 681         if (buf->idb_buf == NULL) {
 682                 kmem_cache_free(idm_buf_cache, buf);
 683                 return (NULL);
 684         }
 685 
 686         idm_conn_hold_impl(ic, &ic->ic_buf_refcount);
 687         buf->idb_ic          = ic;
 688         buf->idb_buflen              = buflen;
 689         buf->idb_exp_offset  = 0;
 690 
 691 #ifdef DEBUG
 692         memset(&buf->idb_buflink, 0, sizeof (list_node_t));
 693         buf->idb_bufoffset   = 0;
 694         buf->idb_mr_handle   = NULL;
 695         buf->idb_buf_cb              = NULL;
 696 #endif
 697 
 698         return (buf);
 699 
 700 }
 701 
 702 /*
 703  * idm_buf_free
 704  *
 705  * Release a buffer handle along with the associated buffer that was allocated
 706  * or assigned with idm_buf_alloc
 707  */
 708 void
 709 idm_buf_free(idm_buf_t *buf)
 710 {
 711         idm_conn_t *ic = buf->idb_ic;
 712 
 713 
 714         buf->idb_task_binding        = NULL;
 715 
 716         kmem_free(buf->idb_buf, buf->idb_buflen);
 717         kmem_cache_free(idm_buf_cache, buf);
 718         idm_conn_rele_impl(ic, &ic->ic_buf_refcount);
 719 }
 720 
 721 /*
 722  * idm_buf_bind_in
 723  *
 724  * This function associates a buffer with a task. This is only for use by the
 725  * iSCSI initiator that will have only one buffer per transfer direction
 726  *
 727  */
 728 void
 729 idm_buf_bind_in(idm_task_t *idt, idm_buf_t *buf)
 730 {
 731         buf->idb_task_binding = idt;
 732         buf->idb_ic = idt->idt_ic;
 733         idm_conn_hold_impl(buf->idb_ic, &buf->idb_ic->ic_buf_refcount);
 734         mutex_enter(&idt->idt_mutex);
 735         idm_listbuf_insert(&idt->idt_inbufv, buf, 0);
 736         mutex_exit(&idt->idt_mutex);
 737 }
 738 
 739 void
 740 idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf)
 741 {
 742         buf->idb_ic = idt->idt_ic;
 743         buf->idb_task_binding = idt;
 744         idm_conn_hold_impl(buf->idb_ic, &buf->idb_ic->ic_buf_refcount);
 745         mutex_enter(&idt->idt_mutex);
 746         idm_listbuf_insert(&idt->idt_outbufv, buf, 0);
 747         mutex_exit(&idt->idt_mutex);
 748 }
 749 
 750 void
 751 idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf)
 752 {
 753         mutex_enter(&idt->idt_mutex);
 754         list_remove(&idt->idt_inbufv, buf);
 755         mutex_exit(&idt->idt_mutex);
 756         buf->idb_task_binding        = NULL;
 757 }
 758 
 759 void
 760 idm_buf_unbind_out(idm_task_t *idt, idm_buf_t *buf)
 761 {
 762         mutex_enter(&idt->idt_mutex);
 763         list_remove(&idt->idt_outbufv, buf);
 764         mutex_exit(&idt->idt_mutex);
 765         buf->idb_task_binding        = NULL;
 766 }
 767 
 768 /*
 769  * idm_buf_find() will lookup the idm_buf_t based on the relative offset in the
 770  * iSCSI PDU
 771  */
 772 idm_buf_t *
 773 idm_buf_find(void *lbuf, size_t data_offset)
 774 {
 775         idm_buf_t       *idb;
 776         list_t          *lst = (list_t *)lbuf;
 777 
 778         /* iterate through the list to find the buffer */
 779         for (idb = list_head(lst); idb != NULL; idb = list_next(lst, idb)) {
 780 
 781                 /* CRM: it doesn't like it when this is taken out */
 782                 if (idb->idb_ic->ic_conn_type == CONN_TYPE_INI)
 783                         return (idb);
 784                 if ((data_offset >= idb->idb_bufoffset) &&
 785                     (data_offset < (idb->idb_bufoffset + idb->idb_buflen))) {
 786 
 787                         return (idb);
 788                 }
 789         }
 790 
 791         return (NULL);
 792 }
 793 
 794 /*
 795  * idm_task_alloc
 796  *
 797  * This function will allocate a idm_task_t structure. A task tag is also
 798  * generated and saved in idt_tt. The task is not active.
 799  */
 800 idm_task_t *
 801 idm_task_alloc(idm_conn_t *ic)
 802 {
 803         void            *addr;
 804         idm_task_t      *idt;
 805 
 806         ASSERT(ic != NULL);
 807 
 808         idt = kmem_cache_alloc(idm_task_cache, KM_SLEEP);
 809         if (idt == NULL) {
 810                 return (NULL);
 811         }
 812 
 813         ASSERT(list_is_empty(&idt->idt_inbufv));
 814         ASSERT(list_is_empty(&idt->idt_outbufv));
 815 
 816         idm_conn_hold_impl(ic, &ic->ic_task_refcount);
 817         idt->idt_ic          = ic;
 818         idt->idt_active              = B_FALSE;
 819         idt->idt_private     = NULL;
 820         idt->idt_exp_sn              = 0;
 821 
 822         return (idt);
 823 }
 824 
 825 /*
 826  * idm_task_start
 827  *
 828  * Add the task to an AVL tree to notify IDM about a new task. The caller
 829  * sets up the idm_task_t structure with a prior call to idm_task_alloc().
 830  * The task service does not function as a task/work engine, it is the
 831  * responsibility of the initiator to start the data transfer and free the
 832  * resources.
 833  */
 834 void
 835 idm_task_start(idm_task_t *idt)
 836 {
 837         idm_conn_t      *ic;
 838 
 839         ASSERT(idt != NULL);
 840 
 841         ic = idt->idt_ic;
 842 
 843         /* mark the task as ACTIVE */
 844         idt->idt_active = B_TRUE;
 845         idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done =
 846             idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done = 0;
 847 
 848         /*
 849          * add the task to AVL tree. The AVL tree keeps track of active tasks.
 850          * memory for avl_tree is allocated earlier in conn_create().
 851          * Tasks with duplicate tags are considered to be an error
 852          */
 853         mutex_enter(&ic->ic_mutex);
 854         avl_add(&ic->ic_task_tree, idt);
 855         mutex_exit(&ic->ic_mutex);
 856 
 857 }
 858 
 859 /*
 860  * idm_task_done
 861  *
 862  * This function will remove the task from the AVL tree indicating that the
 863  * task is no longer active.
 864  */
 865 void
 866 idm_task_done(idm_task_t *idt)
 867 {
 868         idm_conn_t      *ic;
 869 
 870         ASSERT(idt != NULL);
 871 
 872         ic = idt->idt_ic;
 873 
 874         mutex_enter(&ic->ic_mutex);
 875         avl_remove(&ic->ic_task_tree, idt);
 876         mutex_exit(&ic->ic_mutex);
 877 
 878         idt->idt_active = B_FALSE;
 879 
 880 }
 881 
 882 /*
 883  * idm_task_free
 884  *
 885  * This function will free the Task Tag and the memory allocated for the task
 886  * idm_task_done should be called prior to this call
 887  */
 888 void
 889 idm_task_free(idm_task_t *idt)
 890 {
 891         idm_conn_t *ic = idt->idt_ic;
 892         ASSERT(idt != NULL);
 893 
 894         /* vmem free and list free in destructor */
 895 
 896         kmem_cache_free(idm_task_cache, idt);
 897 
 898         idm_conn_rele_impl(ic, &ic->ic_task_refcount);
 899 }
 900 
 901 /*
 902  * idm_task_find
 903  *
 904  * This function looks up a task by task tag
 905  */
 906 idm_task_t *
 907 idm_task_find(idm_conn_t *ic, uint32_t itt)
 908 {
 909         idm_task_t      query, *idt;
 910         avl_index_t     where;
 911 
 912         query.idt_tt = itt;     /* lookup task by task tag */
 913 
 914         mutex_enter(&ic->ic_mutex);
 915         if ((idt = avl_find(&ic->ic_task_tree, &query, &where)) == NULL) {
 916                 mutex_exit(&ic->ic_mutex);
 917                 /* task not found in tree, return NULL */
 918                 return (NULL);
 919         }
 920         mutex_exit(&ic->ic_mutex);
 921 
 922         return (idt);
 923 }
 924 
 925 /*
 926  * idm_pdu_tx
 927  *
 928  * This is IDM's implementation of the 'Send_Control' operational primitive.
 929  * This function is invoked by an initiator iSCSI layer requesting the transfer
 930  * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a
 931  * iSCSI response PDU. The PDU will be transmitted as-is by the local Datamover
 932  * layer to the peer iSCSI layer in the remote iSCSI node. The connection info
 933  * and iSCSI PDU-specific qualifiers namely BHS, AHS, DataDescriptor and Size
 934  * are provided as input.
 935  *
 936  */
 937 void
 938 idm_pdu_tx(idm_pdu_t *pdu)
 939 {
 940         idm_conn_t              *ic = pdu->isp_ic;
 941         iscsi_login_rsp_hdr_t   *login_rsp;
 942         iscsi_logout_rsp_hdr_t  *logout_rsp;
 943         iscsi_async_evt_hdr_t   *async_evt;
 944 
 945         /*
 946          * If we are in full-featured mode then route SCSI-related
 947          * commands to the appropriate function vector without checking
 948          * the connection state.  We will only be in full-feature mode
 949          * when we are in an acceptable state for SCSI PDU's.
 950          *
 951          * We also need to ensure that there are no PDU events outstanding
 952          * on the state machine.  Any non-SCSI PDU's received in full-feature
 953          * mode will result in PDU events and until these have been handled
 954          * we need to route all PDU's through the state machine as PDU
 955          * events to maintain ordering.  XXX This scenario could cause
 956          * some unfortunate pathological behavior as we suddenly route
 957          * all our performance sensitive PDU's through a single-threaded
 958          * slow path.  We need to force this condition in testing under
 959          * heavy load and see how we recover.  It might be necessary to
 960          * do something else like hold off SCSI I/O until the PDU event
 961          * count has returned to 0.
 962          */
 963         mutex_enter(&ic->ic_state_mutex);
 964         if (ic->ic_ffm && (ic->ic_pdu_events == 0)) {
 965                 mutex_exit(&ic->ic_state_mutex);
 966                 switch (IDM_PDU_OPCODE(pdu)) {
 967                 case ISCSI_OP_SCSI_CMD:
 968                 case ISCSI_OP_SCSI_RSP:
 969                 case ISCSI_OP_SCSI_TASK_MGT_MSG:
 970                 case ISCSI_OP_SCSI_TASK_MGT_RSP:
 971                 case ISCSI_OP_SCSI_DATA:
 972                 case ISCSI_OP_SCSI_DATA_RSP:
 973                 case ISCSI_OP_RTT_RSP:
 974                         /*
 975                          * Send the PDU
 976                          */
 977                         idm_pdu_tx_forward(ic, pdu);
 978                         return;
 979                 default:
 980                         break;
 981                 }
 982                 mutex_enter(&ic->ic_state_mutex);
 983         }
 984         mutex_exit(&ic->ic_state_mutex);
 985 
 986         /*
 987          * Any PDU's processed outside of full-feature mode and non-SCSI
 988          * PDU's in full-feature mode are handled by generating an
 989          * event to the connection state machine.  The state machine
 990          * will validate the PDU against the current state and either
 991          * transmit the PDU if the opcode is allowed or handle an
 992          * error if the PDU is not allowed.
 993          *
 994          * This code-path will also generate any events that are implied
 995          * by the PDU opcode.  For example a "login response" with success
 996          * status generates a CE_LOGOUT_SUCCESS_SND event.
 997          */
 998         switch (IDM_PDU_OPCODE(pdu)) {
 999         case ISCSI_OP_LOGIN_CMD:
1000                 idm_conn_tx_pdu_event(ic, CE_LOGIN_SND, (uintptr_t)pdu);
1001                 break;
1002         case ISCSI_OP_LOGIN_RSP:
1003                 idm_parse_login_rsp(ic, pdu, /* Is RX */ B_FALSE);
1004                 break;
1005         case ISCSI_OP_LOGOUT_CMD:
1006                 idm_parse_logout_req(ic, pdu, /* Is RX */ B_FALSE);
1007                 break;
1008         case ISCSI_OP_LOGOUT_RSP:
1009                 idm_parse_logout_rsp(ic, pdu, /* Is RX */ B_FALSE);
1010                 break;
1011         case ISCSI_OP_ASYNC_EVENT:
1012                 async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr;
1013                 switch (async_evt->async_event) {
1014                 case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
1015                         idm_conn_tx_pdu_event(ic, CE_ASYNC_LOGOUT_SND,
1016                             (uintptr_t)pdu);
1017                         break;
1018                 case ISCSI_ASYNC_EVENT_SCSI_EVENT:
1019                 case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
1020                 case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
1021                 case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
1022                 default:
1023                         idm_conn_tx_pdu_event(ic, CE_MISC_TX,
1024                             (uintptr_t)pdu);
1025                         break;
1026                 }
1027                 break;
1028         case ISCSI_OP_SCSI_CMD:
1029         case ISCSI_OP_SCSI_DATA:
1030         case ISCSI_OP_SCSI_DATA_RSP:
1031         case ISCSI_OP_RTT_RSP:
1032         case ISCSI_OP_SNACK_CMD:
1033         case ISCSI_OP_NOOP_IN:
1034         case ISCSI_OP_NOOP_OUT:
1035         case ISCSI_OP_TEXT_CMD:
1036         case ISCSI_OP_TEXT_RSP:
1037         case ISCSI_OP_REJECT_MSG:
1038         default:
1039                 /*
1040                  * Connection state machine will validate these PDU's against
1041                  * the current state.  A PDU not allowed in the current
1042                  * state will cause a protocol error.
1043                  */
1044                 idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1045                 break;
1046         }
1047 }
1048 
1049 /*
1050  * Allocates a PDU along with memory for header and data.
1051  */
1052 
1053 idm_pdu_t *
1054 idm_pdu_alloc(uint_t hdrlen, uint_t datalen)
1055 {
1056         idm_pdu_t *result;
1057 
1058         /*
1059          * IDM clients should cache these structures for performance
1060          * critical paths.  We can't cache effectively in IDM because we
1061          * don't know the correct header and data size.
1062          *
1063          * Valid header length is assumed to be hdrlen and valid data
1064          * length is assumed to be datalen.  isp_hdrlen and isp_datalen
1065          * can be adjusted after the PDU is returned if necessary.
1066          */
1067         result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, KM_SLEEP);
1068         result->isp_flags |= IDM_PDU_ALLOC; /* For idm_pdu_free sanity check */
1069         result->isp_hdr = (iscsi_hdr_t *)(result + 1); /* Ptr. Arithmetic */
1070         result->isp_hdrlen = hdrlen;
1071         result->isp_hdrbuflen = hdrlen;
1072         result->isp_data = (uint8_t *)result->isp_hdr + hdrlen;
1073         result->isp_datalen = datalen;
1074         result->isp_databuflen = datalen;
1075 
1076         return (result);
1077 }
1078 
1079 /*
1080  * Free a PDU previously allocated with idm_pdu_alloc() including any
1081  * header and data space allocated as part of the original request.
1082  * Additional memory regions referenced by subsequent modification of
1083  * the isp_hdr and/or isp_data fields will not be freed.
1084  */
1085 void
1086 idm_pdu_free(idm_pdu_t *pdu)
1087 {
1088         /* Make sure the structure was allocated using idm_pdu_alloc() */
1089         ASSERT(pdu->isp_flags & IDM_PDU_ALLOC);
1090         kmem_free(pdu,
1091             sizeof (idm_pdu_t) + pdu->isp_hdrbuflen + pdu->isp_databuflen);
1092 }
1093 
1094 /*
1095  * Initialize the connection, private and callback fields in a PDU.
1096  */
1097 void
1098 idm_pdu_init(idm_pdu_t *pdu, idm_conn_t *ic, void *private, idm_pdu_cb_t *cb)
1099 {
1100         /*
1101          * idm_pdu_complete() will call idm_pdu_free if the callback is
1102          * NULL.  This will only work if the PDU was originally allocated
1103          * with idm_pdu_alloc().
1104          */
1105         ASSERT((pdu->isp_flags & IDM_PDU_ALLOC) ||
1106             (pdu->isp_callback != NULL));
1107         pdu->isp_ic = ic;
1108         pdu->isp_private = private;
1109         pdu->isp_callback = cb;
1110 }
1111 
1112 /*
1113  * Initialize the header and header length field.  This function should
1114  * not be used to adjust the header length in a buffer allocated via
1115  * pdu_pdu_alloc since it overwrites the existing header pointer.
1116  */
1117 void
1118 idm_pdu_init_hdr(idm_pdu_t *pdu, uint8_t *hdr, uint_t hdrlen)
1119 {
1120         pdu->isp_hdr = (iscsi_hdr_t *)hdr;
1121         pdu->isp_hdrlen = hdrlen;
1122 }
1123 
1124 /*
1125  * Initialize the data and data length fields.  This function should
1126  * not be used to adjust the data length of a buffer allocated via
1127  * idm_pdu_alloc since it overwrites the existing data pointer.
1128  */
1129 void
1130 idm_pdu_init_data(idm_pdu_t *pdu, uint8_t *data, uint_t datalen)
1131 {
1132         pdu->isp_data = data;
1133         pdu->isp_datalen = datalen;
1134 }
1135 
1136 void
1137 idm_pdu_complete(idm_pdu_t *pdu, idm_status_t status)
1138 {
1139         if (pdu->isp_callback) {
1140                 /* XXX Might want to do a taskq in some cases */
1141                 pdu->isp_status = status;
1142                 (*pdu->isp_callback)(pdu, status);
1143         } else {
1144                 idm_pdu_free(pdu);
1145         }
1146 }
1147 
1148 void
1149 idm_conn_hold(idm_conn_t *ic)
1150 {
1151         idm_conn_hold_impl(ic, &ic->ic_client_refcount);
1152 }
1153 
1154 void
1155 idm_conn_rele(idm_conn_t *ic)
1156 {
1157         idm_conn_rele_impl(ic, &ic->ic_client_refcount);
1158 }
1159 
1160 
1161 static int
1162 _idm_init(void)
1163 {
1164         int i;
1165 
1166         /*
1167          * Setup the state/event names.  Doing this programmatically
1168          * allows us to ensure that names are mapped to the appropriate
1169          * numerical enum value even if a state or event is added and
1170          * the name table doesn't get updated
1171          */
1172         for (i = 0; i < CS_MAX_STATE; i++) {
1173                 idm_cs_name[i] = "UNDEFINED";
1174         }
1175         idm_cs_name[CS_S0_UNDEFINED] = "CS_S0_UNDEFINED";
1176         idm_cs_name[CS_S1_FREE] = "CS_S1_FREE";
1177         idm_cs_name[CS_S2_XPT_WAIT] = "CS_S2_XPT_WAIT";
1178         idm_cs_name[CS_S3_XPT_UP] = "CS_S3_XPT_UP";
1179         idm_cs_name[CS_S4_IN_LOGIN] = "CS_S4_IN_LOGIN";
1180         idm_cs_name[CS_S5_LOGGED_IN] = "CS_S5_LOGGED_IN";
1181         idm_cs_name[CS_S6_IN_LOGOUT] = "CS_S6_IN_LOGOUT";
1182         idm_cs_name[CS_S7_LOGOUT_REQ] = "CS_S7_LOGOUT_REQ";
1183         idm_cs_name[CS_S8_CLEANUP] = "CS_S8_CLEANUP";
1184         idm_cs_name[CS_S9_INIT_ERROR] = "CS_S9_INIT_ERROR";
1185         idm_cs_name[CS_S10_IN_CLEANUP] = "CS_S10_IN_CLEANUP";
1186         idm_cs_name[CS_S11_COMPLETE] = "CS_S11_COMPLETE";
1187         idm_cs_name[CS_MAX_STATE] = "CS_MAX_STATE";
1188 
1189         for (i = 0; i < CE_MAX_EVENT; i++) {
1190                 idm_ce_name[i] = "UNDEFINED";
1191         }
1192         idm_ce_name[CE_CONNECT_REQ] = "CE_CONNECT_REQ";
1193         idm_ce_name[CE_CONNECT_FAIL] = "CE_CONNECT_FAIL";
1194         idm_ce_name[CE_CONNECT_SUCCESS] = "CE_CONNECT_SUCCESS";
1195         idm_ce_name[CE_LOGIN_SND] = "CE_LOGIN_SND";
1196         idm_ce_name[CE_LOGIN_SUCCESS_RCV] = "CE_LOGIN_SUCCESS_RCV";
1197         idm_ce_name[CE_LOGIN_FAIL_RCV] = "CE_LOGIN_FAIL_RCV";
1198         idm_ce_name[CE_LOGOUT_THIS_CONN_SND] = "CE_LOGOUT_THIS_CONN_SND";
1199         idm_ce_name[CE_LOGOUT_OTHER_CONN_SND] = "CE_LOGOUT_OTHER_CONN_SND";
1200         idm_ce_name[CE_LOGOUT_SESSION_SND] = "CE_LOGOUT_SESSION_SND";
1201         idm_ce_name[CE_LOGOUT_SUCCESS_RCV] = "CE_LOGOUT_SUCCESS_RCV";
1202         idm_ce_name[CE_LOGOUT_FAIL_RCV] = "CE_LOGOUT_FAIL_RCV";
1203         idm_ce_name[CE_ASYNC_LOGOUT_RCV] = "CE_ASYNC_LOGOUT_RCV";
1204         idm_ce_name[CE_ASYNC_DROP_CONN_RCV] = "CE_ASYNC_DROP_CONN_RCV";
1205         idm_ce_name[CE_ASYNC_DROP_ALL_CONN_RCV] = "CE_ASYNC_DROP_ALL_CONN_RCV";
1206         idm_ce_name[CE_CONNECT_ACCEPT] = "CE_CONNECT_ACCEPT";
1207         idm_ce_name[CE_CONNECT_REJECT] = "CE_CONNECT_REJECT";
1208         idm_ce_name[CE_LOGIN_RCV] = "CE_LOGIN_RCV";
1209         idm_ce_name[CE_LOGIN_TIMEOUT] = "CE_LOGIN_TIMEOUT";
1210         idm_ce_name[CE_LOGIN_SUCCESS_SND] = "CE_LOGIN_SUCCESS_SND";
1211         idm_ce_name[CE_LOGIN_FAIL_SND] = "CE_LOGIN_FAIL_SND";
1212         idm_ce_name[CE_LOGOUT_THIS_CONN_RCV] = "CE_LOGOUT_THIS_CONN_RCV";
1213         idm_ce_name[CE_LOGOUT_OTHER_CONN_RCV] = "CE_LOGOUT_OTHER_CONN_RCV";
1214         idm_ce_name[CE_LOGOUT_SESSION_RCV] = "CE_LOGOUT_SESSION_RCV";
1215         idm_ce_name[CE_LOGOUT_SUCCESS_SND] = "CE_LOGOUT_SUCCESS_SND";
1216         idm_ce_name[CE_LOGOUT_FAIL_SND] = "CE_LOGOUT_FAIL_SND";
1217         idm_ce_name[CE_CLEANUP_TIMEOUT] = "CE_CLEANUP_TIMEOUT";
1218         idm_ce_name[CE_ASYNC_LOGOUT_SND] = "CE_ASYNC_LOGOUT_SND";
1219         idm_ce_name[CE_ASYNC_DROP_CONN_SND] = "CE_ASYNC_DROP_CONN_SND";
1220         idm_ce_name[CE_ASYNC_DROP_ALL_CONN_SND] = "CE_ASYNC_DROP_ALL_CONN_SND";
1221         idm_ce_name[CE_TRANSPORT_FAIL] = "CE_TRANSPORT_FAIL";
1222         idm_ce_name[CE_MISC_TX] = "CE_MISC_TX";
1223         idm_ce_name[CE_TX_PROTOCOL_ERROR] = "CE_TX_PROTOCOL_ERROR";
1224         idm_ce_name[CE_MISC_RX] = "CE_MISC_RX";
1225         idm_ce_name[CE_RX_PROTOCOL_ERROR] = "CE_RX_PROTOCOL_ERROR";
1226         idm_ce_name[CE_LOGOUT_SESSION_SUCCESS] = "CE_LOGOUT_SESSION_SUCCESS";
1227         idm_ce_name[CE_CONN_REINSTATE] = "CE_CONN_REINSTATE";
1228         idm_ce_name[CE_CONN_REINSTATE_SUCCESS] = "CE_CONN_REINSTATE_SUCCESS";
1229         idm_ce_name[CE_CONN_REINSTATE_FAIL] = "CE_CONN_REINSTATE_FAIL";
1230         idm_ce_name[CE_MAX_EVENT] = "CE_MAX_EVENT";
1231 
1232         /*
1233          * XXX Move these into idm_global_t to consolidate global state
1234          * for easier MDB access
1235          */
1236         mutex_init(&idm.idm_global_mutex, NULL, MUTEX_DEFAULT, NULL);
1237 
1238         idm.idm_global_taskq = taskq_create("IDM global taskq", 1, minclsyspri,
1239             1, 1, 0);
1240         if (idm.idm_global_taskq == NULL) {
1241                 return (ENOMEM);
1242         }
1243 
1244         /* Cache for IDM Data and R2T Transmit PDU's */
1245         idm_sotx_pdu_cache = kmem_cache_create("IDM TX PDU Cache",
1246             sizeof (idm_pdu_t) + sizeof (iscsi_hdr_t), 8,
1247             &idm_sotx_pdu_constructor, NULL,
1248             NULL, NULL, NULL, KM_SLEEP);
1249 
1250         /* Cache for IDM Receive PDU's */
1251         idm_sorx_pdu_cache = kmem_cache_create("IDM RX PDU Cache",
1252             sizeof (idm_pdu_t) + IDM_SORX_CACHE_HDRLEN, 8,
1253             &idm_sorx_pdu_constructor, NULL,
1254             NULL, NULL, NULL, KM_SLEEP);
1255 
1256         idm_buf_cache = kmem_cache_create("idm_buf_cache", sizeof (idm_buf_t),
1257             8, NULL, NULL, NULL, NULL, NULL,
1258             KM_SLEEP);
1259 
1260         idm_task_cache = kmem_cache_create("idm_task_cache",
1261             sizeof (idm_task_t), 8, &idm_task_constructor, &idm_task_destructor,
1262             NULL, NULL, NULL, KM_SLEEP);
1263 
1264         list_create(&idm.idm_tgt_svc_list, sizeof (idm_svc_t),
1265             offsetof(idm_svc_t, is_list_node));
1266         list_create(&idm.idm_ini_conn_list, sizeof (idm_conn_t),
1267             offsetof(idm_conn_t, ic_list_node));
1268 
1269         /*
1270          * Use vmem arenas to create task ids in the range [1, IDM_TASKIDS_MAX]
1271          * task id 0 is never allocated, it is used to indicate a error
1272          */
1273         if ((idm_taskid_cache = vmem_create("idm_tasktag_cache", (void *)1,
1274             IDM_TASKIDS_MAX, 1, NULL, NULL, NULL, 0,
1275             VM_NOSLEEP | VMC_IDENTIFIER)) == NULL) {
1276                 cmn_err(CE_NOTE, "unable to create vmem arena");
1277         }
1278 
1279         /*
1280          * JBDB later - walk idm_transport_list and open LDI handles
1281          * on each available transport. For now, just set the sockets
1282          * transport ops up.
1283          */
1284         idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS].it_ops =
1285             &idm_so_transport_ops;
1286 
1287         (void) idm_idpool_create(&idm_conn_id_pool);
1288 
1289         return (0);
1290 }
1291 
1292 static void
1293 _idm_fini(void)
1294 {
1295 
1296         /* JBDB later - clean up LDI handles on transport drivers */
1297 
1298         /* idm_idpool_destroy(&idm_conn_id_pool); */
1299         vmem_destroy(idm_taskid_cache);
1300         list_destroy(&idm.idm_ini_conn_list);
1301         list_destroy(&idm.idm_tgt_svc_list);
1302         kmem_cache_destroy(idm_task_cache);
1303         kmem_cache_destroy(idm_buf_cache);
1304         kmem_cache_destroy(idm_sorx_pdu_cache);
1305         kmem_cache_destroy(idm_sotx_pdu_cache);
1306         mutex_destroy(&idm.idm_global_mutex);
1307 }