xref: /illumos-gate/usr/src/uts/common/io/idm/idm_impl.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <sys/conf.h>
27 #include <sys/file.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/cpuvar.h>
31 #include <sys/sdt.h>
32 
33 #include <sys/socket.h>
34 #include <sys/strsubr.h>
35 #include <sys/socketvar.h>
36 #include <sys/sysmacros.h>
37 
38 #include <sys/idm/idm.h>
39 #include <sys/idm/idm_so.h>
40 #include <hd_crc.h>
41 
42 extern idm_transport_t  idm_transport_list[];
43 /*
44  * -1 - uninitialized
45  * 0  - applicable
46  * others - NA
47  */
48 static int iscsi_crc32_hd = -1;
49 
50 void
51 idm_pdu_rx(idm_conn_t *ic, idm_pdu_t *pdu)
52 {
53 	iscsi_async_evt_hdr_t *async_evt;
54 
55 	/*
56 	 * If we are in full-featured mode then route SCSI-related
57 	 * commands to the appropriate function vector
58 	 */
59 	ic->ic_timestamp = ddi_get_lbolt();
60 	mutex_enter(&ic->ic_state_mutex);
61 	if (ic->ic_ffp && ic->ic_pdu_events == 0) {
62 		mutex_exit(&ic->ic_state_mutex);
63 
64 		if (idm_pdu_rx_forward_ffp(ic, pdu) == B_TRUE) {
65 			/* Forwarded SCSI-related commands */
66 			return;
67 		}
68 		mutex_enter(&ic->ic_state_mutex);
69 	}
70 
71 	/*
72 	 * If we get here with a SCSI-related PDU then we are not in
73 	 * full-feature mode and the PDU is a protocol error (SCSI command
74 	 * PDU's may sometimes be an exception, see below).  All
75 	 * non-SCSI PDU's get treated them the same regardless of whether
76 	 * we are in full-feature mode.
77 	 *
78 	 * Look at the opcode and in some cases the PDU status and
79 	 * determine the appropriate event to send to the connection
80 	 * state machine.  Generate the event, passing the PDU as data.
81 	 * If the current connection state allows reception of the event
82 	 * the PDU will be submitted to the IDM client for processing,
83 	 * otherwise the PDU will be dropped.
84 	 */
85 	switch (IDM_PDU_OPCODE(pdu)) {
86 	case ISCSI_OP_LOGIN_CMD:
87 		DTRACE_ISCSI_2(login__command, idm_conn_t *, ic,
88 		    iscsi_login_hdr_t *, (iscsi_login_hdr_t *)pdu->isp_hdr);
89 		idm_conn_rx_pdu_event(ic, CE_LOGIN_RCV, (uintptr_t)pdu);
90 		break;
91 	case ISCSI_OP_LOGIN_RSP:
92 		idm_parse_login_rsp(ic, pdu, /* RX */ B_TRUE);
93 		break;
94 	case ISCSI_OP_LOGOUT_CMD:
95 		DTRACE_ISCSI_2(logout__command, idm_conn_t *, ic,
96 		    iscsi_logout_hdr_t *,
97 		    (iscsi_logout_hdr_t *)pdu->isp_hdr);
98 		idm_parse_logout_req(ic, pdu, /* RX */ B_TRUE);
99 		break;
100 	case ISCSI_OP_LOGOUT_RSP:
101 		idm_parse_logout_rsp(ic, pdu, /* RX */ B_TRUE);
102 		break;
103 	case ISCSI_OP_ASYNC_EVENT:
104 		async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr;
105 		switch (async_evt->async_event) {
106 		case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
107 			idm_conn_rx_pdu_event(ic, CE_ASYNC_LOGOUT_RCV,
108 			    (uintptr_t)pdu);
109 			break;
110 		case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
111 			idm_conn_rx_pdu_event(ic, CE_ASYNC_DROP_CONN_RCV,
112 			    (uintptr_t)pdu);
113 			break;
114 		case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
115 			idm_conn_rx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_RCV,
116 			    (uintptr_t)pdu);
117 			break;
118 		case ISCSI_ASYNC_EVENT_SCSI_EVENT:
119 		case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
120 		default:
121 			idm_conn_rx_pdu_event(ic, CE_MISC_RX,
122 			    (uintptr_t)pdu);
123 			break;
124 		}
125 		break;
126 	case ISCSI_OP_SCSI_CMD:
127 		/*
128 		 * Consider this scenario:  We are a target connection
129 		 * in "in login" state and a "login success sent" event has
130 		 * been generated but not yet handled.  Since we've sent
131 		 * the login response but we haven't actually transitioned
132 		 * to FFP mode we might conceivably receive a SCSI command
133 		 * from the initiator before we are ready.  We are actually
134 		 * in FFP we just don't know it yet -- to address this we
135 		 * can generate an event corresponding to the SCSI command.
136 		 * At the point when the event is handled by the state
137 		 * machine the login request will have been handled and we
138 		 * should be in FFP.  If we are not in FFP by that time
139 		 * we can reject the SCSI command with a protocol error.
140 		 *
141 		 * This scenario only applies to the target.
142 		 *
143 		 * Handle dtrace probe in iscsit so we can find all the
144 		 * pieces of the CDB
145 		 */
146 		idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu);
147 		break;
148 	case ISCSI_OP_SCSI_DATA:
149 		DTRACE_ISCSI_2(data__receive, idm_conn_t *, ic,
150 		    iscsi_data_hdr_t *,
151 		    (iscsi_data_hdr_t *)pdu->isp_hdr);
152 		idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu);
153 		break;
154 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
155 		DTRACE_ISCSI_2(task__command, idm_conn_t *, ic,
156 		    iscsi_scsi_task_mgt_hdr_t *,
157 		    (iscsi_scsi_task_mgt_hdr_t *)pdu->isp_hdr);
158 		idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu);
159 		break;
160 	case ISCSI_OP_NOOP_OUT:
161 		DTRACE_ISCSI_2(nop__receive, idm_conn_t *, ic,
162 		    iscsi_nop_out_hdr_t *,
163 		    (iscsi_nop_out_hdr_t *)pdu->isp_hdr);
164 		idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu);
165 		break;
166 	case ISCSI_OP_TEXT_CMD:
167 		DTRACE_ISCSI_2(text__command, idm_conn_t *, ic,
168 		    iscsi_text_hdr_t *,
169 		    (iscsi_text_hdr_t *)pdu->isp_hdr);
170 		idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu);
171 		break;
172 	/* Initiator PDU's */
173 	case ISCSI_OP_SCSI_DATA_RSP:
174 	case ISCSI_OP_RTT_RSP:
175 	case ISCSI_OP_SNACK_CMD:
176 	case ISCSI_OP_NOOP_IN:
177 	case ISCSI_OP_TEXT_RSP:
178 	case ISCSI_OP_REJECT_MSG:
179 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
180 		/* Validate received PDU against current state */
181 		idm_conn_rx_pdu_event(ic, CE_MISC_RX,
182 		    (uintptr_t)pdu);
183 		break;
184 	}
185 	mutex_exit(&ic->ic_state_mutex);
186 }
187 
188 void
189 idm_pdu_tx_forward(idm_conn_t *ic, idm_pdu_t *pdu)
190 {
191 	(*ic->ic_transport_ops->it_tx_pdu)(ic, pdu);
192 }
193 
194 boolean_t
195 idm_pdu_rx_forward_ffp(idm_conn_t *ic, idm_pdu_t *pdu)
196 {
197 	/*
198 	 * If this is an FFP request, call the appropriate handler
199 	 * and return B_TRUE, otherwise return B_FALSE.
200 	 */
201 	switch (IDM_PDU_OPCODE(pdu)) {
202 	case ISCSI_OP_SCSI_CMD:
203 		(*ic->ic_conn_ops.icb_rx_scsi_cmd)(ic, pdu);
204 		return (B_TRUE);
205 	case ISCSI_OP_SCSI_DATA:
206 		DTRACE_ISCSI_2(data__receive, idm_conn_t *, ic,
207 		    iscsi_data_hdr_t *,
208 		    (iscsi_data_hdr_t *)pdu->isp_hdr);
209 		(*ic->ic_transport_ops->it_rx_dataout)(ic, pdu);
210 		return (B_TRUE);
211 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
212 		DTRACE_ISCSI_2(task__command, idm_conn_t *, ic,
213 		    iscsi_scsi_task_mgt_hdr_t *,
214 		    (iscsi_scsi_task_mgt_hdr_t *)pdu->isp_hdr);
215 		(*ic->ic_conn_ops.icb_rx_misc)(ic, pdu);
216 		return (B_TRUE);
217 	case ISCSI_OP_NOOP_OUT:
218 		DTRACE_ISCSI_2(nop__receive, idm_conn_t *, ic,
219 		    iscsi_nop_out_hdr_t *,
220 		    (iscsi_nop_out_hdr_t *)pdu->isp_hdr);
221 		(*ic->ic_conn_ops.icb_rx_misc)(ic, pdu);
222 		return (B_TRUE);
223 	case ISCSI_OP_TEXT_CMD:
224 		DTRACE_ISCSI_2(text__command, idm_conn_t *, ic,
225 		    iscsi_text_hdr_t *,
226 		    (iscsi_text_hdr_t *)pdu->isp_hdr);
227 		(*ic->ic_conn_ops.icb_rx_misc)(ic, pdu);
228 		return (B_TRUE);
229 		/* Initiator only */
230 	case ISCSI_OP_SCSI_RSP:
231 		(*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu);
232 		return (B_TRUE);
233 	case ISCSI_OP_SCSI_DATA_RSP:
234 		(*ic->ic_transport_ops->it_rx_datain)(ic, pdu);
235 		return (B_TRUE);
236 	case ISCSI_OP_RTT_RSP:
237 		(*ic->ic_transport_ops->it_rx_rtt)(ic, pdu);
238 		return (B_TRUE);
239 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
240 	case ISCSI_OP_TEXT_RSP:
241 	case ISCSI_OP_NOOP_IN:
242 		(*ic->ic_conn_ops.icb_rx_misc)(ic, pdu);
243 		return (B_TRUE);
244 	default:
245 		return (B_FALSE);
246 	}
247 	/*NOTREACHED*/
248 }
249 
250 void
251 idm_pdu_rx_forward(idm_conn_t *ic, idm_pdu_t *pdu)
252 {
253 	/*
254 	 * Some PDU's specific to FFP get special handling.  This function
255 	 * will normally never be called in FFP with an FFP PDU since this
256 	 * is a slow path but in can happen on the target side during
257 	 * the transition to FFP.  We primarily call
258 	 * idm_pdu_rx_forward_ffp here to avoid code duplication.
259 	 */
260 	if (idm_pdu_rx_forward_ffp(ic, pdu) == B_FALSE) {
261 		/*
262 		 * Non-FFP PDU, use generic RC handler
263 		 */
264 		(*ic->ic_conn_ops.icb_rx_misc)(ic, pdu);
265 	}
266 }
267 
268 void
269 idm_parse_login_rsp(idm_conn_t *ic, idm_pdu_t *login_rsp_pdu, boolean_t rx)
270 {
271 	iscsi_login_rsp_hdr_t	*login_rsp =
272 	    (iscsi_login_rsp_hdr_t *)login_rsp_pdu->isp_hdr;
273 	idm_conn_event_t	new_event;
274 
275 	if (login_rsp->status_class == ISCSI_STATUS_CLASS_SUCCESS) {
276 		if (!(login_rsp->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
277 		    (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) &&
278 		    (ISCSI_LOGIN_NEXT_STAGE(login_rsp->flags) ==
279 		    ISCSI_FULL_FEATURE_PHASE)) {
280 			new_event = (rx ? CE_LOGIN_SUCCESS_RCV :
281 			    CE_LOGIN_SUCCESS_SND);
282 		} else {
283 			new_event = (rx ? CE_MISC_RX : CE_MISC_TX);
284 		}
285 	} else {
286 		new_event = (rx ? CE_LOGIN_FAIL_RCV : CE_LOGIN_FAIL_SND);
287 	}
288 
289 	if (rx) {
290 		idm_conn_rx_pdu_event(ic, new_event, (uintptr_t)login_rsp_pdu);
291 	} else {
292 		idm_conn_tx_pdu_event(ic, new_event, (uintptr_t)login_rsp_pdu);
293 	}
294 }
295 
296 
297 void
298 idm_parse_logout_req(idm_conn_t *ic, idm_pdu_t *logout_req_pdu, boolean_t rx)
299 {
300 	iscsi_logout_hdr_t 	*logout_req =
301 	    (iscsi_logout_hdr_t *)logout_req_pdu->isp_hdr;
302 	idm_conn_event_t	new_event;
303 	uint8_t			reason =
304 	    (logout_req->flags & ISCSI_FLAG_LOGOUT_REASON_MASK);
305 
306 	/*
307 	 *	For a normal logout (close connection or close session) IDM
308 	 *	will terminate processing of all tasks completing the tasks
309 	 *	back to the client with a status indicating the connection
310 	 *	was logged out.  These tasks do not get completed.
311 	 *
312 	 *	For a "close connection for recovery logout) IDM suspends
313 	 *	processing of all tasks and completes them back to the client
314 	 *	with a status indicating connection was logged out for
315 	 *	recovery.  Both initiator and target hang onto these tasks.
316 	 *	When we add ERL2 support IDM will need to provide mechanisms
317 	 *	to change the task and buffer associations to a new connection.
318 	 *
319 	 *	This code doesn't address the possibility of MC/S.  We'll
320 	 *	need to decide how the separate connections get handled
321 	 *	in that case.  One simple option is to make the client
322 	 *	generate the events for the other connections.
323 	 */
324 	if (reason == ISCSI_LOGOUT_REASON_CLOSE_SESSION) {
325 		new_event =
326 		    (rx ? CE_LOGOUT_SESSION_RCV : CE_LOGOUT_SESSION_SND);
327 	} else if ((reason == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION) ||
328 	    (reason == ISCSI_LOGOUT_REASON_RECOVERY)) {
329 		/* Check logout CID against this connection's CID */
330 		if (ntohs(logout_req->cid) == ic->ic_login_cid) {
331 			/* Logout is for this connection */
332 			new_event = (rx ? CE_LOGOUT_THIS_CONN_RCV :
333 			    CE_LOGOUT_THIS_CONN_SND);
334 		} else {
335 			/*
336 			 * Logout affects another connection.  This is not
337 			 * a relevant event for this connection so we'll
338 			 * just treat it as a normal PDU event.  Client
339 			 * will need to lookup the other connection and
340 			 * generate the event.
341 			 */
342 			new_event = (rx ? CE_MISC_RX : CE_MISC_TX);
343 		}
344 	} else {
345 		/* Invalid reason code */
346 		new_event = (rx ? CE_RX_PROTOCOL_ERROR : CE_TX_PROTOCOL_ERROR);
347 	}
348 
349 	if (rx) {
350 		idm_conn_rx_pdu_event(ic, new_event, (uintptr_t)logout_req_pdu);
351 	} else {
352 		idm_conn_tx_pdu_event(ic, new_event, (uintptr_t)logout_req_pdu);
353 	}
354 }
355 
356 
357 
358 void
359 idm_parse_logout_rsp(idm_conn_t *ic, idm_pdu_t *logout_rsp_pdu, boolean_t rx)
360 {
361 	idm_conn_event_t	new_event;
362 	iscsi_logout_rsp_hdr_t *logout_rsp =
363 	    (iscsi_logout_rsp_hdr_t *)logout_rsp_pdu->isp_hdr;
364 
365 	if (logout_rsp->response == ISCSI_STATUS_CLASS_SUCCESS) {
366 		new_event = rx ? CE_LOGOUT_SUCCESS_RCV : CE_LOGOUT_SUCCESS_SND;
367 	} else {
368 		new_event = rx ? CE_LOGOUT_FAIL_RCV : CE_LOGOUT_FAIL_SND;
369 	}
370 
371 	if (rx) {
372 		idm_conn_rx_pdu_event(ic, new_event, (uintptr_t)logout_rsp_pdu);
373 	} else {
374 		idm_conn_tx_pdu_event(ic, new_event, (uintptr_t)logout_rsp_pdu);
375 	}
376 }
377 
378 /*
379  * idm_svc_conn_create()
380  * Transport-agnostic service connection creation, invoked from the transport
381  * layer.
382  */
383 idm_status_t
384 idm_svc_conn_create(idm_svc_t *is, idm_transport_type_t tt,
385     idm_conn_t **ic_result)
386 {
387 	idm_conn_t	*ic;
388 	idm_status_t	rc;
389 
390 	/*
391 	 * Skip some work if we can already tell we are going offline.
392 	 * Otherwise we will destroy this connection later as part of
393 	 * shutting down the svc.
394 	 */
395 	mutex_enter(&is->is_mutex);
396 	if (!is->is_online) {
397 		mutex_exit(&is->is_mutex);
398 		return (IDM_STATUS_FAIL);
399 	}
400 	mutex_exit(&is->is_mutex);
401 
402 	ic = idm_conn_create_common(CONN_TYPE_TGT, tt,
403 	    &is->is_svc_req.sr_conn_ops);
404 	if (ic == NULL) {
405 		return (IDM_STATUS_FAIL);
406 	}
407 	ic->ic_svc_binding = is;
408 
409 	/*
410 	 * Prepare connection state machine
411 	 */
412 	if ((rc = idm_conn_sm_init(ic)) != 0) {
413 		idm_conn_destroy_common(ic);
414 		return (rc);
415 	}
416 
417 
418 	*ic_result = ic;
419 
420 	mutex_enter(&idm.idm_global_mutex);
421 	list_insert_tail(&idm.idm_tgt_conn_list, ic);
422 	idm.idm_tgt_conn_count++;
423 	mutex_exit(&idm.idm_global_mutex);
424 
425 	return (IDM_STATUS_SUCCESS);
426 }
427 
428 void
429 idm_svc_conn_destroy(idm_conn_t *ic)
430 {
431 	mutex_enter(&idm.idm_global_mutex);
432 	list_remove(&idm.idm_tgt_conn_list, ic);
433 	idm.idm_tgt_conn_count--;
434 	mutex_exit(&idm.idm_global_mutex);
435 
436 	if (ic->ic_transport_private != NULL) {
437 		ic->ic_transport_ops->it_tgt_conn_destroy(ic);
438 	}
439 	idm_conn_destroy_common(ic);
440 }
441 
442 /*
443  * idm_conn_create_common()
444  *
445  * Allocate and initialize IDM connection context
446  */
447 idm_conn_t *
448 idm_conn_create_common(idm_conn_type_t conn_type, idm_transport_type_t tt,
449     idm_conn_ops_t *conn_ops)
450 {
451 	idm_conn_t		*ic;
452 	idm_transport_t		*it;
453 	idm_transport_type_t	type;
454 
455 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
456 		it = &idm_transport_list[type];
457 
458 		if ((it->it_ops != NULL) && (it->it_type == tt))
459 			break;
460 	}
461 	ASSERT(it->it_type == tt);
462 	if (it->it_type != tt)
463 		return (NULL);
464 
465 	ic = kmem_zalloc(sizeof (idm_conn_t), KM_SLEEP);
466 
467 	/* Initialize data */
468 	ic->ic_target_name[0] = '\0';
469 	ic->ic_initiator_name[0] = '\0';
470 	ic->ic_isid[0] = '\0';
471 	ic->ic_tsih[0] = '\0';
472 	ic->ic_conn_type = conn_type;
473 	ic->ic_conn_ops = *conn_ops;
474 	ic->ic_transport_ops = it->it_ops;
475 	ic->ic_transport_type = tt;
476 	ic->ic_transport_private = NULL; /* Set by transport service */
477 	ic->ic_internal_cid = idm_cid_alloc();
478 	if (ic->ic_internal_cid == 0) {
479 		kmem_free(ic, sizeof (idm_conn_t));
480 		return (NULL);
481 	}
482 	mutex_init(&ic->ic_mutex, NULL, MUTEX_DEFAULT, NULL);
483 	cv_init(&ic->ic_cv, NULL, CV_DEFAULT, NULL);
484 	idm_refcnt_init(&ic->ic_refcnt, ic);
485 
486 	return (ic);
487 }
488 
489 void
490 idm_conn_destroy_common(idm_conn_t *ic)
491 {
492 	idm_conn_sm_fini(ic);
493 	idm_refcnt_destroy(&ic->ic_refcnt);
494 	cv_destroy(&ic->ic_cv);
495 	mutex_destroy(&ic->ic_mutex);
496 	idm_cid_free(ic->ic_internal_cid);
497 
498 	kmem_free(ic, sizeof (idm_conn_t));
499 }
500 
501 /*
502  * Invoked from the SM as a result of client's invocation of
503  * idm_ini_conn_connect()
504  */
505 idm_status_t
506 idm_ini_conn_finish(idm_conn_t *ic)
507 {
508 	/* invoke transport-specific connection */
509 	return (ic->ic_transport_ops->it_ini_conn_connect(ic));
510 }
511 
512 idm_status_t
513 idm_tgt_conn_finish(idm_conn_t *ic)
514 {
515 	idm_status_t rc;
516 
517 	rc = idm_notify_client(ic, CN_CONNECT_ACCEPT, NULL);
518 	if (rc != IDM_STATUS_SUCCESS) {
519 		return (IDM_STATUS_REJECT);
520 	}
521 
522 	/* Target client is ready to receive a login, start connection */
523 	return (ic->ic_transport_ops->it_tgt_conn_connect(ic));
524 }
525 
526 idm_transport_t *
527 idm_transport_lookup(idm_conn_req_t *cr)
528 {
529 	idm_transport_type_t	type;
530 	idm_transport_t		*it;
531 	idm_transport_caps_t	caps;
532 
533 	/*
534 	 * Make sure all available transports are setup.  We call this now
535 	 * instead of at initialization time in case IB has become available
536 	 * since we started (hotplug, etc).
537 	 */
538 	idm_transport_setup(cr->cr_li, cr->cr_boot_conn);
539 
540 	/* Determine the transport for this connection */
541 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
542 		it = &idm_transport_list[type];
543 
544 		if (it->it_ops == NULL) {
545 			/* transport is not registered */
546 			continue;
547 		}
548 
549 		if (it->it_ops->it_conn_is_capable(cr, &caps)) {
550 			return (it);
551 		}
552 	}
553 
554 	ASSERT(0);
555 	return (NULL); /* Make gcc happy */
556 }
557 
558 void
559 idm_transport_setup(ldi_ident_t li, boolean_t boot_conn)
560 {
561 	idm_transport_type_t	type;
562 	idm_transport_t		*it;
563 	int			rc;
564 
565 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
566 		it = &idm_transport_list[type];
567 		/*
568 		 * We may want to store the LDI handle in the idm_svc_t
569 		 * and then allow multiple calls to ldi_open_by_name.  This
570 		 * would enable the LDI code to track who has the device open
571 		 * which could be useful in the case where we have multiple
572 		 * services and perhaps also have initiator and target opening
573 		 * the transport simultaneously.  For now we stick with the
574 		 * plan.
575 		 */
576 		if (it->it_ops == NULL) {
577 			/* transport is not ready, try to initialize it */
578 			if (it->it_type == IDM_TRANSPORT_TYPE_SOCKETS) {
579 				idm_so_init(it);
580 			} else {
581 				if (boot_conn == B_TRUE) {
582 					/*
583 					 * iSCSI boot doesn't need iSER.
584 					 * Open iSER here may drive IO to
585 					 * a failed session and cause
586 					 * deadlock
587 					 */
588 					continue;
589 				}
590 				rc = ldi_open_by_name(it->it_device_path,
591 				    FREAD | FWRITE, kcred, &it->it_ldi_hdl, li);
592 				/*
593 				 * If the open is successful we will have
594 				 * filled in the LDI handle in the transport
595 				 * table and we expect that the transport
596 				 * registered itself.
597 				 */
598 				if (rc != 0) {
599 					it->it_ldi_hdl = NULL;
600 				}
601 			}
602 		}
603 	}
604 }
605 
606 void
607 idm_transport_teardown()
608 {
609 	idm_transport_type_t	type;
610 	idm_transport_t		*it;
611 
612 	ASSERT(mutex_owned(&idm.idm_global_mutex));
613 
614 	/* Caller holds the IDM global mutex */
615 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
616 		it = &idm_transport_list[type];
617 		/* If we have an open LDI handle on this driver, close it */
618 		if (it->it_ldi_hdl != NULL) {
619 			(void) ldi_close(it->it_ldi_hdl, FNDELAY, kcred);
620 			it->it_ldi_hdl = NULL;
621 		}
622 	}
623 }
624 
625 /*
626  * ID pool code.  We use this to generate unique structure identifiers without
627  * searching the existing structures.  This avoids the need to lock entire
628  * sets of structures at inopportune times.  Adapted from the CIFS server code.
629  *
630  *    A pool of IDs is a pool of 16 bit numbers. It is implemented as a bitmap.
631  *    A bit set to '1' indicates that that particular value has been allocated.
632  *    The allocation process is done shifting a bit through the whole bitmap.
633  *    The current position of that index bit is kept in the idm_idpool_t
634  *    structure and represented by a byte index (0 to buffer size minus 1) and
635  *    a bit index (0 to 7).
636  *
637  *    The pools start with a size of 8 bytes or 64 IDs. Each time the pool runs
638  *    out of IDs its current size is doubled until it reaches its maximum size
639  *    (8192 bytes or 65536 IDs). The IDs 0 and 65535 are never given out which
640  *    means that a pool can have a maximum number of 65534 IDs available.
641  */
642 
643 static int
644 idm_idpool_increment(
645     idm_idpool_t	*pool)
646 {
647 	uint8_t		*new_pool;
648 	uint32_t	new_size;
649 
650 	ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC);
651 
652 	new_size = pool->id_size * 2;
653 	if (new_size <= IDM_IDPOOL_MAX_SIZE) {
654 		new_pool = kmem_alloc(new_size / 8, KM_NOSLEEP);
655 		if (new_pool) {
656 			bzero(new_pool, new_size / 8);
657 			bcopy(pool->id_pool, new_pool, pool->id_size / 8);
658 			kmem_free(pool->id_pool, pool->id_size / 8);
659 			pool->id_pool = new_pool;
660 			pool->id_free_counter += new_size - pool->id_size;
661 			pool->id_max_free_counter += new_size - pool->id_size;
662 			pool->id_size = new_size;
663 			pool->id_idx_msk = (new_size / 8) - 1;
664 			if (new_size >= IDM_IDPOOL_MAX_SIZE) {
665 				/* id -1 made unavailable */
666 				pool->id_pool[pool->id_idx_msk] = 0x80;
667 				pool->id_free_counter--;
668 				pool->id_max_free_counter--;
669 			}
670 			return (0);
671 		}
672 	}
673 	return (-1);
674 }
675 
676 /*
677  * idm_idpool_constructor
678  *
679  * This function initializes the pool structure provided.
680  */
681 
682 int
683 idm_idpool_create(idm_idpool_t *pool)
684 {
685 
686 	ASSERT(pool->id_magic != IDM_IDPOOL_MAGIC);
687 
688 	pool->id_size = IDM_IDPOOL_MIN_SIZE;
689 	pool->id_idx_msk = (IDM_IDPOOL_MIN_SIZE / 8) - 1;
690 	pool->id_free_counter = IDM_IDPOOL_MIN_SIZE - 1;
691 	pool->id_max_free_counter = IDM_IDPOOL_MIN_SIZE - 1;
692 	pool->id_bit = 0x02;
693 	pool->id_bit_idx = 1;
694 	pool->id_idx = 0;
695 	pool->id_pool = (uint8_t *)kmem_alloc((IDM_IDPOOL_MIN_SIZE / 8),
696 	    KM_SLEEP);
697 	bzero(pool->id_pool, (IDM_IDPOOL_MIN_SIZE / 8));
698 	/* -1 id made unavailable */
699 	pool->id_pool[0] = 0x01;		/* id 0 made unavailable */
700 	mutex_init(&pool->id_mutex, NULL, MUTEX_DEFAULT, NULL);
701 	pool->id_magic = IDM_IDPOOL_MAGIC;
702 	return (0);
703 }
704 
705 /*
706  * idm_idpool_destructor
707  *
708  * This function tears down and frees the resources associated with the
709  * pool provided.
710  */
711 
712 void
713 idm_idpool_destroy(idm_idpool_t *pool)
714 {
715 	ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC);
716 	ASSERT(pool->id_free_counter == pool->id_max_free_counter);
717 	pool->id_magic = (uint32_t)~IDM_IDPOOL_MAGIC;
718 	mutex_destroy(&pool->id_mutex);
719 	kmem_free(pool->id_pool, (size_t)(pool->id_size / 8));
720 }
721 
722 /*
723  * idm_idpool_alloc
724  *
725  * This function allocates an ID from the pool provided.
726  */
727 int
728 idm_idpool_alloc(idm_idpool_t *pool, uint16_t *id)
729 {
730 	uint32_t	i;
731 	uint8_t		bit;
732 	uint8_t		bit_idx;
733 	uint8_t		byte;
734 
735 	ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC);
736 
737 	mutex_enter(&pool->id_mutex);
738 	if ((pool->id_free_counter == 0) && idm_idpool_increment(pool)) {
739 		mutex_exit(&pool->id_mutex);
740 		return (-1);
741 	}
742 
743 	i = pool->id_size;
744 	while (i) {
745 		bit = pool->id_bit;
746 		bit_idx = pool->id_bit_idx;
747 		byte = pool->id_pool[pool->id_idx];
748 		while (bit) {
749 			if (byte & bit) {
750 				bit = bit << 1;
751 				bit_idx++;
752 				continue;
753 			}
754 			pool->id_pool[pool->id_idx] |= bit;
755 			*id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx);
756 			pool->id_free_counter--;
757 			pool->id_bit = bit;
758 			pool->id_bit_idx = bit_idx;
759 			mutex_exit(&pool->id_mutex);
760 			return (0);
761 		}
762 		pool->id_bit = 1;
763 		pool->id_bit_idx = 0;
764 		pool->id_idx++;
765 		pool->id_idx &= pool->id_idx_msk;
766 		--i;
767 	}
768 	/*
769 	 * This section of code shouldn't be reached. If there are IDs
770 	 * available and none could be found there's a problem.
771 	 */
772 	ASSERT(0);
773 	mutex_exit(&pool->id_mutex);
774 	return (-1);
775 }
776 
777 /*
778  * idm_idpool_free
779  *
780  * This function frees the ID provided.
781  */
782 void
783 idm_idpool_free(idm_idpool_t *pool, uint16_t id)
784 {
785 	ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC);
786 	ASSERT(id != 0);
787 	ASSERT(id != 0xFFFF);
788 
789 	mutex_enter(&pool->id_mutex);
790 	if (pool->id_pool[id >> 3] & (1 << (id & 7))) {
791 		pool->id_pool[id >> 3] &= ~(1 << (id & 7));
792 		pool->id_free_counter++;
793 		ASSERT(pool->id_free_counter <= pool->id_max_free_counter);
794 		mutex_exit(&pool->id_mutex);
795 		return;
796 	}
797 	/* Freeing a free ID. */
798 	ASSERT(0);
799 	mutex_exit(&pool->id_mutex);
800 }
801 
802 uint32_t
803 idm_cid_alloc(void)
804 {
805 	/*
806 	 * ID pool works with 16-bit identifiers right now.  That should
807 	 * be plenty since we will probably never have more than 2^16
808 	 * connections simultaneously.
809 	 */
810 	uint16_t cid16;
811 
812 	if (idm_idpool_alloc(&idm.idm_conn_id_pool, &cid16) == -1) {
813 		return (0); /* Fail */
814 	}
815 
816 	return ((uint32_t)cid16);
817 }
818 
819 void
820 idm_cid_free(uint32_t cid)
821 {
822 	idm_idpool_free(&idm.idm_conn_id_pool, (uint16_t)cid);
823 }
824 
825 
826 /*
827  * Code for generating the header and data digests
828  *
829  * This is the CRC-32C table
830  * Generated with:
831  * width = 32 bits
832  * poly = 0x1EDC6F41
833  * reflect input bytes = true
834  * reflect output bytes = true
835  */
836 
837 uint32_t idm_crc32c_table[256] =
838 {
839 	0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
840 	0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
841 	0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
842 	0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
843 	0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
844 	0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
845 	0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
846 	0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
847 	0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
848 	0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
849 	0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
850 	0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
851 	0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
852 	0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
853 	0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
854 	0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
855 	0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
856 	0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
857 	0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
858 	0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
859 	0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
860 	0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
861 	0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
862 	0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
863 	0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
864 	0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
865 	0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
866 	0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
867 	0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
868 	0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
869 	0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
870 	0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
871 	0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
872 	0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
873 	0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
874 	0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
875 	0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
876 	0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
877 	0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
878 	0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
879 	0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
880 	0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
881 	0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
882 	0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
883 	0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
884 	0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
885 	0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
886 	0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
887 	0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
888 	0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
889 	0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
890 	0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
891 	0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
892 	0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
893 	0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
894 	0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
895 	0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
896 	0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
897 	0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
898 	0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
899 	0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
900 	0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
901 	0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
902 	0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
903 };
904 
905 /*
906  * iscsi_crc32c - Steps through buffer one byte at at time, calculates
907  * reflected crc using table.
908  */
909 uint32_t
910 idm_crc32c(void *address, unsigned long length)
911 {
912 	uint8_t *buffer = address;
913 	uint32_t crc = 0xffffffff, result;
914 #ifdef _BIG_ENDIAN
915 	uint8_t byte0, byte1, byte2, byte3;
916 #endif
917 
918 	ASSERT(address != NULL);
919 
920 	if (iscsi_crc32_hd == -1) {
921 		if (hd_crc32_avail((uint32_t *)idm_crc32c_table) == B_TRUE) {
922 			iscsi_crc32_hd = 0;
923 		} else {
924 			iscsi_crc32_hd = 1;
925 		}
926 	}
927 	if (iscsi_crc32_hd == 0)
928 		return (HW_CRC32(buffer, length, crc));
929 
930 	while (length--) {
931 		crc = idm_crc32c_table[(crc ^ *buffer++) & 0xFFL] ^
932 		    (crc >> 8);
933 	}
934 	result = crc ^ 0xffffffff;
935 
936 #ifdef	_BIG_ENDIAN
937 	byte0 = (uint8_t)(result & 0xFF);
938 	byte1 = (uint8_t)((result >> 8) & 0xFF);
939 	byte2 = (uint8_t)((result >> 16) & 0xFF);
940 	byte3 = (uint8_t)((result >> 24) & 0xFF);
941 	result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
942 #endif	/* _BIG_ENDIAN */
943 
944 	return (result);
945 }
946 
947 
948 /*
949  * idm_crc32c_continued - Continues stepping through buffer one
950  * byte at at time, calculates reflected crc using table.
951  */
952 uint32_t
953 idm_crc32c_continued(void *address, unsigned long length, uint32_t crc)
954 {
955 	uint8_t *buffer = address;
956 	uint32_t result;
957 #ifdef	_BIG_ENDIAN
958 	uint8_t byte0, byte1, byte2, byte3;
959 #endif
960 
961 	ASSERT(address != NULL);
962 
963 	if (iscsi_crc32_hd == -1) {
964 		if (hd_crc32_avail((uint32_t *)idm_crc32c_table) == B_TRUE) {
965 			iscsi_crc32_hd = 0;
966 		} else {
967 			iscsi_crc32_hd = 1;
968 		}
969 	}
970 	if (iscsi_crc32_hd == 0)
971 		return (HW_CRC32_CONT(buffer, length, crc));
972 
973 
974 #ifdef	_BIG_ENDIAN
975 	byte0 = (uint8_t)((crc >> 24) & 0xFF);
976 	byte1 = (uint8_t)((crc >> 16) & 0xFF);
977 	byte2 = (uint8_t)((crc >> 8) & 0xFF);
978 	byte3 = (uint8_t)(crc & 0xFF);
979 	crc = ((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0);
980 #endif
981 
982 	crc = crc ^ 0xffffffff;
983 	while (length--) {
984 		crc = idm_crc32c_table[(crc ^ *buffer++) & 0xFFL] ^
985 		    (crc >> 8);
986 	}
987 	result = crc ^ 0xffffffff;
988 
989 #ifdef	_BIG_ENDIAN
990 	byte0 = (uint8_t)(result & 0xFF);
991 	byte1 = (uint8_t)((result >> 8) & 0xFF);
992 	byte2 = (uint8_t)((result >> 16) & 0xFF);
993 	byte3 = (uint8_t)((result >> 24) & 0xFF);
994 	result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
995 #endif
996 	return (result);
997 }
998 
999 /* ARGSUSED */
1000 int
1001 idm_task_constructor(void *hdl, void *arg, int flags)
1002 {
1003 	idm_task_t *idt = (idm_task_t *)hdl;
1004 	uint32_t next_task;
1005 
1006 	mutex_init(&idt->idt_mutex, NULL, MUTEX_DEFAULT, NULL);
1007 
1008 	/* Find the next free task ID */
1009 	rw_enter(&idm.idm_taskid_table_lock, RW_WRITER);
1010 	next_task = idm.idm_taskid_next;
1011 	while (idm.idm_taskid_table[next_task]) {
1012 		next_task++;
1013 		if (next_task == idm.idm_taskid_max)
1014 			next_task = 0;
1015 		if (next_task == idm.idm_taskid_next) {
1016 			rw_exit(&idm.idm_taskid_table_lock);
1017 			return (-1);
1018 		}
1019 	}
1020 
1021 	idm.idm_taskid_table[next_task] = idt;
1022 	idm.idm_taskid_next = (next_task + 1) % idm.idm_taskid_max;
1023 	rw_exit(&idm.idm_taskid_table_lock);
1024 
1025 	idt->idt_tt = next_task;
1026 
1027 	list_create(&idt->idt_inbufv, sizeof (idm_buf_t),
1028 	    offsetof(idm_buf_t, idb_buflink));
1029 	list_create(&idt->idt_outbufv, sizeof (idm_buf_t),
1030 	    offsetof(idm_buf_t, idb_buflink));
1031 	idm_refcnt_init(&idt->idt_refcnt, idt);
1032 
1033 	/*
1034 	 * Set the transport header pointer explicitly.  This removes the
1035 	 * need for per-transport header allocation, which simplifies cache
1036 	 * init considerably.  If at a later date we have an additional IDM
1037 	 * transport that requires a different size, we'll revisit this.
1038 	 */
1039 	idt->idt_transport_hdr = (void *)(idt + 1); /* pointer arithmetic */
1040 	idt->idt_flags = 0;
1041 	return (0);
1042 }
1043 
1044 /* ARGSUSED */
1045 void
1046 idm_task_destructor(void *hdl, void *arg)
1047 {
1048 	idm_task_t *idt = (idm_task_t *)hdl;
1049 
1050 	/* Remove the task from the ID table */
1051 	rw_enter(&idm.idm_taskid_table_lock, RW_WRITER);
1052 	idm.idm_taskid_table[idt->idt_tt] = NULL;
1053 	rw_exit(&idm.idm_taskid_table_lock);
1054 
1055 	/* free the inbuf and outbuf */
1056 	idm_refcnt_destroy(&idt->idt_refcnt);
1057 	list_destroy(&idt->idt_inbufv);
1058 	list_destroy(&idt->idt_outbufv);
1059 
1060 	/*
1061 	 * The final call to idm_task_rele may happen with the task
1062 	 * mutex held which may invoke this destructor immediately.
1063 	 * Stall here until the task mutex owner lets go.
1064 	 */
1065 	mutex_enter(&idt->idt_mutex);
1066 	mutex_destroy(&idt->idt_mutex);
1067 }
1068 
1069 /*
1070  * idm_listbuf_insert searches from the back of the list looking for the
1071  * insertion point.
1072  */
1073 void
1074 idm_listbuf_insert(list_t *lst, idm_buf_t *buf)
1075 {
1076 	idm_buf_t	*idb;
1077 
1078 	/* iterate through the list to find the insertion point */
1079 	for (idb = list_tail(lst); idb != NULL; idb = list_prev(lst, idb)) {
1080 
1081 		if (idb->idb_bufoffset < buf->idb_bufoffset) {
1082 
1083 			list_insert_after(lst, idb, buf);
1084 			return;
1085 		}
1086 	}
1087 
1088 	/* add the buf to the head of the list */
1089 	list_insert_head(lst, buf);
1090 
1091 }
1092 
1093 /*ARGSUSED*/
1094 void
1095 idm_wd_thread(void *arg)
1096 {
1097 	idm_conn_t	*ic;
1098 	clock_t		wake_time = SEC_TO_TICK(IDM_WD_INTERVAL);
1099 	clock_t		idle_time;
1100 
1101 	/* Record the thread id for thread_join() */
1102 	idm.idm_wd_thread_did = curthread->t_did;
1103 	mutex_enter(&idm.idm_global_mutex);
1104 	idm.idm_wd_thread_running = B_TRUE;
1105 	cv_signal(&idm.idm_wd_cv);
1106 
1107 	while (idm.idm_wd_thread_running) {
1108 		for (ic = list_head(&idm.idm_tgt_conn_list);
1109 		    ic != NULL;
1110 		    ic = list_next(&idm.idm_tgt_conn_list, ic)) {
1111 			idle_time = ddi_get_lbolt() - ic->ic_timestamp;
1112 
1113 			/*
1114 			 * If this connection is in FFP then grab a hold
1115 			 * and check the various timeout thresholds.  Otherwise
1116 			 * the connection is closing and we should just
1117 			 * move on to the next one.
1118 			 */
1119 			mutex_enter(&ic->ic_state_mutex);
1120 			if (ic->ic_ffp) {
1121 				idm_conn_hold(ic);
1122 			} else {
1123 				mutex_exit(&ic->ic_state_mutex);
1124 				continue;
1125 			}
1126 
1127 			/*
1128 			 * If there hasn't been any activity on this
1129 			 * connection for the keepalive timeout period
1130 			 * and if the client has provided a keepalive
1131 			 * callback then call the keepalive callback.
1132 			 * This allows the client to take action to keep
1133 			 * the link alive (like send a nop PDU).
1134 			 */
1135 			if ((TICK_TO_SEC(idle_time) >=
1136 			    IDM_TRANSPORT_KEEPALIVE_IDLE_TIMEOUT) &&
1137 			    !ic->ic_keepalive) {
1138 				ic->ic_keepalive = B_TRUE;
1139 				if (ic->ic_conn_ops.icb_keepalive) {
1140 					mutex_exit(&ic->ic_state_mutex);
1141 					mutex_exit(&idm.idm_global_mutex);
1142 					(*ic->ic_conn_ops.icb_keepalive)(ic);
1143 					mutex_enter(&idm.idm_global_mutex);
1144 					mutex_enter(&ic->ic_state_mutex);
1145 				}
1146 			} else if ((TICK_TO_SEC(idle_time) <
1147 			    IDM_TRANSPORT_KEEPALIVE_IDLE_TIMEOUT)) {
1148 				/* Reset keepalive */
1149 				ic->ic_keepalive = B_FALSE;
1150 			}
1151 
1152 			/*
1153 			 * If there hasn't been any activity on this
1154 			 * connection for the failure timeout period then
1155 			 * drop the connection.  We expect the initiator
1156 			 * to keep the connection alive if it wants the
1157 			 * connection to stay open.
1158 			 *
1159 			 * If it turns out to be desireable to take a
1160 			 * more active role in maintaining the connect
1161 			 * we could add a client callback to send
1162 			 * a "keepalive" kind of message (no doubt a nop)
1163 			 * and fire that on a shorter timer.
1164 			 */
1165 			if (TICK_TO_SEC(idle_time) >
1166 			    IDM_TRANSPORT_FAIL_IDLE_TIMEOUT) {
1167 				mutex_exit(&ic->ic_state_mutex);
1168 				mutex_exit(&idm.idm_global_mutex);
1169 				IDM_SM_LOG(CE_WARN, "idm_wd_thread: "
1170 				    "conn %p idle for %d seconds, "
1171 				    "sending CE_TRANSPORT_FAIL",
1172 				    (void *)ic, (int)idle_time);
1173 				idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
1174 				mutex_enter(&idm.idm_global_mutex);
1175 				mutex_enter(&ic->ic_state_mutex);
1176 			}
1177 
1178 			idm_conn_rele(ic);
1179 
1180 			mutex_exit(&ic->ic_state_mutex);
1181 		}
1182 
1183 		(void) cv_reltimedwait(&idm.idm_wd_cv, &idm.idm_global_mutex,
1184 		    wake_time, TR_CLOCK_TICK);
1185 	}
1186 	mutex_exit(&idm.idm_global_mutex);
1187 
1188 	thread_exit();
1189 }
1190