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