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 }