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