ctl_frontend_iscsi.c revision 288724
1/*-
2 * Copyright (c) 2012 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Edward Tomasz Napierala under sponsorship
6 * from the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: stable/10/sys/cam/ctl/ctl_frontend_iscsi.c 288724 2015-10-05 08:47:45Z mav $
30 */
31
32/*
33 * CTL frontend for the iSCSI protocol.
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: stable/10/sys/cam/ctl/ctl_frontend_iscsi.c 288724 2015-10-05 08:47:45Z mav $");
38
39#include <sys/param.h>
40#include <sys/capsicum.h>
41#include <sys/condvar.h>
42#include <sys/file.h>
43#include <sys/kernel.h>
44#include <sys/kthread.h>
45#include <sys/lock.h>
46#include <sys/malloc.h>
47#include <sys/module.h>
48#include <sys/mutex.h>
49#include <sys/queue.h>
50#include <sys/sbuf.h>
51#include <sys/sysctl.h>
52#include <sys/systm.h>
53#include <sys/uio.h>
54#include <sys/unistd.h>
55#include <vm/uma.h>
56
57#include <cam/scsi/scsi_all.h>
58#include <cam/scsi/scsi_da.h>
59#include <cam/ctl/ctl_io.h>
60#include <cam/ctl/ctl.h>
61#include <cam/ctl/ctl_backend.h>
62#include <cam/ctl/ctl_error.h>
63#include <cam/ctl/ctl_frontend.h>
64#include <cam/ctl/ctl_debug.h>
65#include <cam/ctl/ctl_ha.h>
66#include <cam/ctl/ctl_ioctl.h>
67#include <cam/ctl/ctl_private.h>
68
69#include <dev/iscsi/icl.h>
70#include <dev/iscsi/iscsi_proto.h>
71#include <cam/ctl/ctl_frontend_iscsi.h>
72
73#ifdef ICL_KERNEL_PROXY
74#include <sys/socketvar.h>
75#endif
76
77#ifdef ICL_KERNEL_PROXY
78FEATURE(cfiscsi_kernel_proxy, "iSCSI target built with ICL_KERNEL_PROXY");
79#endif
80
81static MALLOC_DEFINE(M_CFISCSI, "cfiscsi", "Memory used for CTL iSCSI frontend");
82static uma_zone_t cfiscsi_data_wait_zone;
83
84SYSCTL_NODE(_kern_cam_ctl, OID_AUTO, iscsi, CTLFLAG_RD, 0,
85    "CAM Target Layer iSCSI Frontend");
86static int debug = 1;
87TUNABLE_INT("kern.cam.ctl.iscsi.debug", &debug);
88SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, debug, CTLFLAG_RWTUN,
89    &debug, 1, "Enable debug messages");
90static int ping_timeout = 5;
91TUNABLE_INT("kern.cam.ctl.iscsi.ping_timeout", &ping_timeout);
92SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RWTUN,
93    &ping_timeout, 5, "Interval between ping (NOP-Out) requests, in seconds");
94static int login_timeout = 60;
95TUNABLE_INT("kern.cam.ctl.iscsi.login_timeout", &login_timeout);
96SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, login_timeout, CTLFLAG_RWTUN,
97    &login_timeout, 60, "Time to wait for ctld(8) to finish Login Phase, in seconds");
98static int maxcmdsn_delta = 256;
99TUNABLE_INT("kern.cam.ctl.iscsi.maxcmdsn_delta", &maxcmdsn_delta);
100SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, maxcmdsn_delta, CTLFLAG_RWTUN,
101    &maxcmdsn_delta, 256, "Number of commands the initiator can send "
102    "without confirmation");
103
104#define	CFISCSI_DEBUG(X, ...)						\
105	do {								\
106		if (debug > 1) {					\
107			printf("%s: " X "\n",				\
108			    __func__, ## __VA_ARGS__);			\
109		}							\
110	} while (0)
111
112#define	CFISCSI_WARN(X, ...)						\
113	do {								\
114		if (debug > 0) {					\
115			printf("WARNING: %s: " X "\n",			\
116			    __func__, ## __VA_ARGS__);			\
117		}							\
118	} while (0)
119
120#define	CFISCSI_SESSION_DEBUG(S, X, ...)				\
121	do {								\
122		if (debug > 1) {					\
123			printf("%s: %s (%s): " X "\n",			\
124			    __func__, S->cs_initiator_addr,		\
125			    S->cs_initiator_name, ## __VA_ARGS__);	\
126		}							\
127	} while (0)
128
129#define	CFISCSI_SESSION_WARN(S, X, ...)					\
130	do  {								\
131		if (debug > 0) {					\
132			printf("WARNING: %s (%s): " X "\n",		\
133			    S->cs_initiator_addr,			\
134			    S->cs_initiator_name, ## __VA_ARGS__);	\
135		}							\
136	} while (0)
137
138#define CFISCSI_SESSION_LOCK(X)		mtx_lock(&X->cs_lock)
139#define CFISCSI_SESSION_UNLOCK(X)	mtx_unlock(&X->cs_lock)
140#define CFISCSI_SESSION_LOCK_ASSERT(X)	mtx_assert(&X->cs_lock, MA_OWNED)
141
142#define	CONN_SESSION(X)			((struct cfiscsi_session *)(X)->ic_prv0)
143#define	PDU_SESSION(X)			CONN_SESSION((X)->ip_conn)
144#define	PDU_EXPDATASN(X)		(X)->ip_prv0
145#define	PDU_TOTAL_TRANSFER_LEN(X)	(X)->ip_prv1
146#define	PDU_R2TSN(X)			(X)->ip_prv2
147
148int		cfiscsi_init(void);
149static void	cfiscsi_online(void *arg);
150static void	cfiscsi_offline(void *arg);
151static int	cfiscsi_info(void *arg, struct sbuf *sb);
152static int	cfiscsi_ioctl(struct cdev *dev,
153		    u_long cmd, caddr_t addr, int flag, struct thread *td);
154static void	cfiscsi_datamove(union ctl_io *io);
155static void	cfiscsi_datamove_in(union ctl_io *io);
156static void	cfiscsi_datamove_out(union ctl_io *io);
157static void	cfiscsi_done(union ctl_io *io);
158static bool	cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request);
159static void	cfiscsi_pdu_handle_nop_out(struct icl_pdu *request);
160static void	cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request);
161static void	cfiscsi_pdu_handle_task_request(struct icl_pdu *request);
162static void	cfiscsi_pdu_handle_data_out(struct icl_pdu *request);
163static void	cfiscsi_pdu_handle_logout_request(struct icl_pdu *request);
164static void	cfiscsi_session_terminate(struct cfiscsi_session *cs);
165static struct cfiscsi_target	*cfiscsi_target_find(struct cfiscsi_softc
166		    *softc, const char *name, uint16_t tag);
167static struct cfiscsi_target	*cfiscsi_target_find_or_create(
168    struct cfiscsi_softc *softc, const char *name, const char *alias,
169    uint16_t tag);
170static void	cfiscsi_target_release(struct cfiscsi_target *ct);
171static void	cfiscsi_session_delete(struct cfiscsi_session *cs);
172
173static struct cfiscsi_softc cfiscsi_softc;
174extern struct ctl_softc *control_softc;
175
176static struct ctl_frontend cfiscsi_frontend =
177{
178	.name = "iscsi",
179	.init = cfiscsi_init,
180	.ioctl = cfiscsi_ioctl,
181};
182CTL_FRONTEND_DECLARE(ctlcfiscsi, cfiscsi_frontend);
183MODULE_DEPEND(ctlcfiscsi, icl, 1, 1, 1);
184
185static struct icl_pdu *
186cfiscsi_pdu_new_response(struct icl_pdu *request, int flags)
187{
188
189	return (icl_pdu_new(request->ip_conn, flags));
190}
191
192static bool
193cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request)
194{
195	const struct iscsi_bhs_scsi_command *bhssc;
196	struct cfiscsi_session *cs;
197	uint32_t cmdsn, expstatsn;
198
199	cs = PDU_SESSION(request);
200
201	/*
202	 * Every incoming PDU - not just NOP-Out - resets the ping timer.
203	 * The purpose of the timeout is to reset the connection when it stalls;
204	 * we don't want this to happen when NOP-In or NOP-Out ends up delayed
205	 * in some queue.
206	 *
207	 * XXX: Locking?
208	 */
209	cs->cs_timeout = 0;
210
211	/*
212	 * Data-Out PDUs don't contain CmdSN.
213	 */
214	if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
215	    ISCSI_BHS_OPCODE_SCSI_DATA_OUT)
216		return (false);
217
218	/*
219	 * We're only using fields common for all the request
220	 * (initiator -> target) PDUs.
221	 */
222	bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs;
223	cmdsn = ntohl(bhssc->bhssc_cmdsn);
224	expstatsn = ntohl(bhssc->bhssc_expstatsn);
225
226	CFISCSI_SESSION_LOCK(cs);
227#if 0
228	if (expstatsn != cs->cs_statsn) {
229		CFISCSI_SESSION_DEBUG(cs, "received PDU with ExpStatSN %d, "
230		    "while current StatSN is %d", expstatsn,
231		    cs->cs_statsn);
232	}
233#endif
234
235	if ((request->ip_bhs->bhs_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) {
236		/*
237		 * The target MUST silently ignore any non-immediate command
238		 * outside of this range.
239		 */
240		if (ISCSI_SNLT(cmdsn, cs->cs_cmdsn) ||
241		    ISCSI_SNGT(cmdsn, cs->cs_cmdsn + maxcmdsn_delta)) {
242			CFISCSI_SESSION_UNLOCK(cs);
243			CFISCSI_SESSION_WARN(cs, "received PDU with CmdSN %u, "
244			    "while expected %u", cmdsn, cs->cs_cmdsn);
245			return (true);
246		}
247
248		/*
249		 * We don't support multiple connections now, so any
250		 * discontinuity in CmdSN means lost PDUs.  Since we don't
251		 * support PDU retransmission -- terminate the connection.
252		 */
253		if (cmdsn != cs->cs_cmdsn) {
254			CFISCSI_SESSION_UNLOCK(cs);
255			CFISCSI_SESSION_WARN(cs, "received PDU with CmdSN %u, "
256			    "while expected %u; dropping connection",
257			    cmdsn, cs->cs_cmdsn);
258			cfiscsi_session_terminate(cs);
259			return (true);
260		}
261		cs->cs_cmdsn++;
262	}
263
264	CFISCSI_SESSION_UNLOCK(cs);
265
266	return (false);
267}
268
269static void
270cfiscsi_pdu_handle(struct icl_pdu *request)
271{
272	struct cfiscsi_session *cs;
273	bool ignore;
274
275	cs = PDU_SESSION(request);
276
277	ignore = cfiscsi_pdu_update_cmdsn(request);
278	if (ignore) {
279		icl_pdu_free(request);
280		return;
281	}
282
283	/*
284	 * Handle the PDU; this includes e.g. receiving the remaining
285	 * part of PDU and submitting the SCSI command to CTL
286	 * or queueing a reply.  The handling routine is responsible
287	 * for freeing the PDU when it's no longer needed.
288	 */
289	switch (request->ip_bhs->bhs_opcode &
290	    ~ISCSI_BHS_OPCODE_IMMEDIATE) {
291	case ISCSI_BHS_OPCODE_NOP_OUT:
292		cfiscsi_pdu_handle_nop_out(request);
293		break;
294	case ISCSI_BHS_OPCODE_SCSI_COMMAND:
295		cfiscsi_pdu_handle_scsi_command(request);
296		break;
297	case ISCSI_BHS_OPCODE_TASK_REQUEST:
298		cfiscsi_pdu_handle_task_request(request);
299		break;
300	case ISCSI_BHS_OPCODE_SCSI_DATA_OUT:
301		cfiscsi_pdu_handle_data_out(request);
302		break;
303	case ISCSI_BHS_OPCODE_LOGOUT_REQUEST:
304		cfiscsi_pdu_handle_logout_request(request);
305		break;
306	default:
307		CFISCSI_SESSION_WARN(cs, "received PDU with unsupported "
308		    "opcode 0x%x; dropping connection",
309		    request->ip_bhs->bhs_opcode);
310		icl_pdu_free(request);
311		cfiscsi_session_terminate(cs);
312	}
313
314}
315
316static void
317cfiscsi_receive_callback(struct icl_pdu *request)
318{
319	struct cfiscsi_session *cs;
320
321	cs = PDU_SESSION(request);
322
323#ifdef ICL_KERNEL_PROXY
324	if (cs->cs_waiting_for_ctld || cs->cs_login_phase) {
325		if (cs->cs_login_pdu == NULL)
326			cs->cs_login_pdu = request;
327		else
328			icl_pdu_free(request);
329		cv_signal(&cs->cs_login_cv);
330		return;
331	}
332#endif
333
334	cfiscsi_pdu_handle(request);
335}
336
337static void
338cfiscsi_error_callback(struct icl_conn *ic)
339{
340	struct cfiscsi_session *cs;
341
342	cs = CONN_SESSION(ic);
343
344	CFISCSI_SESSION_WARN(cs, "connection error; dropping connection");
345	cfiscsi_session_terminate(cs);
346}
347
348static int
349cfiscsi_pdu_prepare(struct icl_pdu *response)
350{
351	struct cfiscsi_session *cs;
352	struct iscsi_bhs_scsi_response *bhssr;
353	bool advance_statsn = true;
354
355	cs = PDU_SESSION(response);
356
357	CFISCSI_SESSION_LOCK_ASSERT(cs);
358
359	/*
360	 * We're only using fields common for all the response
361	 * (target -> initiator) PDUs.
362	 */
363	bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs;
364
365	/*
366	 * 10.8.3: "The StatSN for this connection is not advanced
367	 * after this PDU is sent."
368	 */
369	if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_R2T)
370		advance_statsn = false;
371
372	/*
373	 * 10.19.2: "However, when the Initiator Task Tag is set to 0xffffffff,
374	 * StatSN for the connection is not advanced after this PDU is sent."
375	 */
376	if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_NOP_IN &&
377	    bhssr->bhssr_initiator_task_tag == 0xffffffff)
378		advance_statsn = false;
379
380	/*
381	 * See the comment below - StatSN is not meaningful and must
382	 * not be advanced.
383	 */
384	if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_SCSI_DATA_IN &&
385	    (bhssr->bhssr_flags & BHSDI_FLAGS_S) == 0)
386		advance_statsn = false;
387
388	/*
389	 * 10.7.3: "The fields StatSN, Status, and Residual Count
390	 * only have meaningful content if the S bit is set to 1."
391	 */
392	if (bhssr->bhssr_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_IN ||
393	    (bhssr->bhssr_flags & BHSDI_FLAGS_S))
394		bhssr->bhssr_statsn = htonl(cs->cs_statsn);
395	bhssr->bhssr_expcmdsn = htonl(cs->cs_cmdsn);
396	bhssr->bhssr_maxcmdsn = htonl(cs->cs_cmdsn + maxcmdsn_delta);
397
398	if (advance_statsn)
399		cs->cs_statsn++;
400
401	return (0);
402}
403
404static void
405cfiscsi_pdu_queue(struct icl_pdu *response)
406{
407	struct cfiscsi_session *cs;
408
409	cs = PDU_SESSION(response);
410
411	CFISCSI_SESSION_LOCK(cs);
412	cfiscsi_pdu_prepare(response);
413	icl_pdu_queue(response);
414	CFISCSI_SESSION_UNLOCK(cs);
415}
416
417static uint32_t
418cfiscsi_decode_lun(uint64_t encoded)
419{
420	uint8_t lun[8];
421	uint32_t result;
422
423	/*
424	 * The LUN field in iSCSI PDUs may look like an ordinary 64 bit number,
425	 * but is in fact an evil, multidimensional structure defined
426	 * in SCSI Architecture Model 5 (SAM-5), section 4.6.
427	 */
428	memcpy(lun, &encoded, sizeof(lun));
429	switch (lun[0] & 0xC0) {
430	case 0x00:
431		if ((lun[0] & 0x3f) != 0 || lun[2] != 0 || lun[3] != 0 ||
432		    lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0) {
433			CFISCSI_WARN("malformed LUN "
434			    "(peripheral device addressing method): 0x%jx",
435			    (uintmax_t)encoded);
436			result = 0xffffffff;
437			break;
438		}
439		result = lun[1];
440		break;
441	case 0x40:
442		if (lun[2] != 0 || lun[3] != 0 || lun[4] != 0 || lun[5] != 0 ||
443		    lun[6] != 0 || lun[7] != 0) {
444			CFISCSI_WARN("malformed LUN "
445			    "(flat address space addressing method): 0x%jx",
446			    (uintmax_t)encoded);
447			result = 0xffffffff;
448			break;
449		}
450		result = ((lun[0] & 0x3f) << 8) + lun[1];
451		break;
452	case 0xC0:
453		if (lun[0] != 0xD2 || lun[4] != 0 || lun[5] != 0 ||
454		    lun[6] != 0 || lun[7] != 0) {
455			CFISCSI_WARN("malformed LUN (extended flat "
456			    "address space addressing method): 0x%jx",
457			    (uintmax_t)encoded);
458			result = 0xffffffff;
459			break;
460		}
461		result = (lun[1] << 16) + (lun[2] << 8) + lun[3];
462	default:
463		CFISCSI_WARN("unsupported LUN format 0x%jx",
464		    (uintmax_t)encoded);
465		result = 0xffffffff;
466		break;
467	}
468
469	return (result);
470}
471
472static void
473cfiscsi_pdu_handle_nop_out(struct icl_pdu *request)
474{
475	struct cfiscsi_session *cs;
476	struct iscsi_bhs_nop_out *bhsno;
477	struct iscsi_bhs_nop_in *bhsni;
478	struct icl_pdu *response;
479	void *data = NULL;
480	size_t datasize;
481	int error;
482
483	cs = PDU_SESSION(request);
484	bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs;
485
486	if (bhsno->bhsno_initiator_task_tag == 0xffffffff) {
487		/*
488		 * Nothing to do, iscsi_pdu_update_statsn() already
489		 * zeroed the timeout.
490		 */
491		icl_pdu_free(request);
492		return;
493	}
494
495	datasize = icl_pdu_data_segment_length(request);
496	if (datasize > 0) {
497		data = malloc(datasize, M_CFISCSI, M_NOWAIT | M_ZERO);
498		if (data == NULL) {
499			CFISCSI_SESSION_WARN(cs, "failed to allocate memory; "
500			    "dropping connection");
501			icl_pdu_free(request);
502			cfiscsi_session_terminate(cs);
503			return;
504		}
505		icl_pdu_get_data(request, 0, data, datasize);
506	}
507
508	response = cfiscsi_pdu_new_response(request, M_NOWAIT);
509	if (response == NULL) {
510		CFISCSI_SESSION_WARN(cs, "failed to allocate memory; "
511		    "droppping connection");
512		free(data, M_CFISCSI);
513		icl_pdu_free(request);
514		cfiscsi_session_terminate(cs);
515		return;
516	}
517	bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs;
518	bhsni->bhsni_opcode = ISCSI_BHS_OPCODE_NOP_IN;
519	bhsni->bhsni_flags = 0x80;
520	bhsni->bhsni_initiator_task_tag = bhsno->bhsno_initiator_task_tag;
521	bhsni->bhsni_target_transfer_tag = 0xffffffff;
522	if (datasize > 0) {
523		error = icl_pdu_append_data(response, data, datasize, M_NOWAIT);
524		if (error != 0) {
525			CFISCSI_SESSION_WARN(cs, "failed to allocate memory; "
526			    "dropping connection");
527			free(data, M_CFISCSI);
528			icl_pdu_free(request);
529			icl_pdu_free(response);
530			cfiscsi_session_terminate(cs);
531			return;
532		}
533		free(data, M_CFISCSI);
534	}
535
536	icl_pdu_free(request);
537	cfiscsi_pdu_queue(response);
538}
539
540static void
541cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request)
542{
543	struct iscsi_bhs_scsi_command *bhssc;
544	struct cfiscsi_session *cs;
545	union ctl_io *io;
546	int error;
547
548	cs = PDU_SESSION(request);
549	bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs;
550	//CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x",
551	//    bhssc->bhssc_initiator_task_tag);
552
553	if (request->ip_data_len > 0 && cs->cs_immediate_data == false) {
554		CFISCSI_SESSION_WARN(cs, "unsolicited data with "
555		    "ImmediateData=No; dropping connection");
556		icl_pdu_free(request);
557		cfiscsi_session_terminate(cs);
558		return;
559	}
560	io = ctl_alloc_io(cs->cs_target->ct_port.ctl_pool_ref);
561	ctl_zero_io(io);
562	io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = request;
563	io->io_hdr.io_type = CTL_IO_SCSI;
564	io->io_hdr.nexus.initid.id = cs->cs_ctl_initid;
565	io->io_hdr.nexus.targ_port = cs->cs_target->ct_port.targ_port;
566	io->io_hdr.nexus.targ_target.id = 0;
567	io->io_hdr.nexus.targ_lun = cfiscsi_decode_lun(bhssc->bhssc_lun);
568	io->scsiio.tag_num = bhssc->bhssc_initiator_task_tag;
569	switch ((bhssc->bhssc_flags & BHSSC_FLAGS_ATTR)) {
570	case BHSSC_FLAGS_ATTR_UNTAGGED:
571		io->scsiio.tag_type = CTL_TAG_UNTAGGED;
572		break;
573	case BHSSC_FLAGS_ATTR_SIMPLE:
574		io->scsiio.tag_type = CTL_TAG_SIMPLE;
575		break;
576	case BHSSC_FLAGS_ATTR_ORDERED:
577        	io->scsiio.tag_type = CTL_TAG_ORDERED;
578		break;
579	case BHSSC_FLAGS_ATTR_HOQ:
580        	io->scsiio.tag_type = CTL_TAG_HEAD_OF_QUEUE;
581		break;
582	case BHSSC_FLAGS_ATTR_ACA:
583		io->scsiio.tag_type = CTL_TAG_ACA;
584		break;
585	default:
586		io->scsiio.tag_type = CTL_TAG_UNTAGGED;
587		CFISCSI_SESSION_WARN(cs, "unhandled tag type %d",
588		    bhssc->bhssc_flags & BHSSC_FLAGS_ATTR);
589		break;
590	}
591	io->scsiio.cdb_len = sizeof(bhssc->bhssc_cdb); /* Which is 16. */
592	memcpy(io->scsiio.cdb, bhssc->bhssc_cdb, sizeof(bhssc->bhssc_cdb));
593	refcount_acquire(&cs->cs_outstanding_ctl_pdus);
594	error = ctl_queue(io);
595	if (error != CTL_RETVAL_COMPLETE) {
596		CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d; "
597		    "dropping connection", error);
598		ctl_free_io(io);
599		refcount_release(&cs->cs_outstanding_ctl_pdus);
600		icl_pdu_free(request);
601		cfiscsi_session_terminate(cs);
602	}
603}
604
605static void
606cfiscsi_pdu_handle_task_request(struct icl_pdu *request)
607{
608	struct iscsi_bhs_task_management_request *bhstmr;
609	struct iscsi_bhs_task_management_response *bhstmr2;
610	struct icl_pdu *response;
611	struct cfiscsi_session *cs;
612	union ctl_io *io;
613	int error;
614
615	cs = PDU_SESSION(request);
616	bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs;
617	io = ctl_alloc_io(cs->cs_target->ct_port.ctl_pool_ref);
618	ctl_zero_io(io);
619	io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = request;
620	io->io_hdr.io_type = CTL_IO_TASK;
621	io->io_hdr.nexus.initid.id = cs->cs_ctl_initid;
622	io->io_hdr.nexus.targ_port = cs->cs_target->ct_port.targ_port;
623	io->io_hdr.nexus.targ_target.id = 0;
624	io->io_hdr.nexus.targ_lun = cfiscsi_decode_lun(bhstmr->bhstmr_lun);
625	io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */
626
627	switch (bhstmr->bhstmr_function & ~0x80) {
628	case BHSTMR_FUNCTION_ABORT_TASK:
629#if 0
630		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_ABORT_TASK");
631#endif
632		io->taskio.task_action = CTL_TASK_ABORT_TASK;
633		io->taskio.tag_num = bhstmr->bhstmr_referenced_task_tag;
634		break;
635	case BHSTMR_FUNCTION_ABORT_TASK_SET:
636#if 0
637		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_ABORT_TASK_SET");
638#endif
639		io->taskio.task_action = CTL_TASK_ABORT_TASK_SET;
640		break;
641	case BHSTMR_FUNCTION_LOGICAL_UNIT_RESET:
642#if 0
643		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_LOGICAL_UNIT_RESET");
644#endif
645		io->taskio.task_action = CTL_TASK_LUN_RESET;
646		break;
647	case BHSTMR_FUNCTION_TARGET_WARM_RESET:
648#if 0
649		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_TARGET_WARM_RESET");
650#endif
651		io->taskio.task_action = CTL_TASK_TARGET_RESET;
652		break;
653	default:
654		CFISCSI_SESSION_DEBUG(cs, "unsupported function 0x%x",
655		    bhstmr->bhstmr_function & ~0x80);
656		ctl_free_io(io);
657
658		response = cfiscsi_pdu_new_response(request, M_NOWAIT);
659		if (response == NULL) {
660			CFISCSI_SESSION_WARN(cs, "failed to allocate memory; "
661			    "dropping connection");
662			icl_pdu_free(request);
663			cfiscsi_session_terminate(cs);
664			return;
665		}
666		bhstmr2 = (struct iscsi_bhs_task_management_response *)
667		    response->ip_bhs;
668		bhstmr2->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_RESPONSE;
669		bhstmr2->bhstmr_flags = 0x80;
670		bhstmr2->bhstmr_response =
671		    BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED;
672		bhstmr2->bhstmr_initiator_task_tag =
673		    bhstmr->bhstmr_initiator_task_tag;
674		icl_pdu_free(request);
675		cfiscsi_pdu_queue(response);
676		return;
677	}
678
679	refcount_acquire(&cs->cs_outstanding_ctl_pdus);
680	error = ctl_queue(io);
681	if (error != CTL_RETVAL_COMPLETE) {
682		CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d; "
683		    "dropping connection", error);
684		ctl_free_io(io);
685		refcount_release(&cs->cs_outstanding_ctl_pdus);
686		icl_pdu_free(request);
687		cfiscsi_session_terminate(cs);
688	}
689}
690
691static bool
692cfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *cdw)
693{
694	struct iscsi_bhs_data_out *bhsdo;
695	struct cfiscsi_session *cs;
696	struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
697	size_t copy_len, len, off, buffer_offset;
698	int ctl_sg_count;
699	union ctl_io *io;
700
701	cs = PDU_SESSION(request);
702
703	KASSERT((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
704	    ISCSI_BHS_OPCODE_SCSI_DATA_OUT ||
705	    (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
706	    ISCSI_BHS_OPCODE_SCSI_COMMAND,
707	    ("bad opcode 0x%x", request->ip_bhs->bhs_opcode));
708
709	/*
710	 * We're only using fields common for Data-Out and SCSI Command PDUs.
711	 */
712	bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs;
713
714	io = cdw->cdw_ctl_io;
715	KASSERT((io->io_hdr.flags & CTL_FLAG_DATA_MASK) != CTL_FLAG_DATA_IN,
716	    ("CTL_FLAG_DATA_IN"));
717
718#if 0
719	CFISCSI_SESSION_DEBUG(cs, "received %zd bytes out of %d",
720	    request->ip_data_len, io->scsiio.kern_total_len);
721#endif
722
723	if (io->scsiio.kern_sg_entries > 0) {
724		ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
725		ctl_sg_count = io->scsiio.kern_sg_entries;
726	} else {
727		ctl_sglist = &ctl_sg_entry;
728		ctl_sglist->addr = io->scsiio.kern_data_ptr;
729		ctl_sglist->len = io->scsiio.kern_data_len;
730		ctl_sg_count = 1;
731	}
732
733	if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
734	    ISCSI_BHS_OPCODE_SCSI_DATA_OUT)
735		buffer_offset = ntohl(bhsdo->bhsdo_buffer_offset);
736	else
737		buffer_offset = 0;
738	len = icl_pdu_data_segment_length(request);
739
740	/*
741	 * Make sure the offset, as sent by the initiator, matches the offset
742	 * we're supposed to be at in the scatter-gather list.
743	 */
744	if (buffer_offset >
745	    io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled ||
746	    buffer_offset + len <=
747	    io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled) {
748		CFISCSI_SESSION_WARN(cs, "received bad buffer offset %zd, "
749		    "expected %zd; dropping connection", buffer_offset,
750		    (size_t)io->scsiio.kern_rel_offset +
751		    (size_t)io->scsiio.ext_data_filled);
752		ctl_set_data_phase_error(&io->scsiio);
753		cfiscsi_session_terminate(cs);
754		return (true);
755	}
756
757	/*
758	 * This is the offset within the PDU data segment, as opposed
759	 * to buffer_offset, which is the offset within the task (SCSI
760	 * command).
761	 */
762	off = io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled -
763	    buffer_offset;
764
765	/*
766	 * Iterate over the scatter/gather segments, filling them with data
767	 * from the PDU data segment.  Note that this can get called multiple
768	 * times for one SCSI command; the cdw structure holds state for the
769	 * scatter/gather list.
770	 */
771	for (;;) {
772		KASSERT(cdw->cdw_sg_index < ctl_sg_count,
773		    ("cdw->cdw_sg_index >= ctl_sg_count"));
774		if (cdw->cdw_sg_len == 0) {
775			cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr;
776			cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len;
777		}
778		KASSERT(off <= len, ("len > off"));
779		copy_len = len - off;
780		if (copy_len > cdw->cdw_sg_len)
781			copy_len = cdw->cdw_sg_len;
782
783		icl_pdu_get_data(request, off, cdw->cdw_sg_addr, copy_len);
784		cdw->cdw_sg_addr += copy_len;
785		cdw->cdw_sg_len -= copy_len;
786		off += copy_len;
787		io->scsiio.ext_data_filled += copy_len;
788
789		if (cdw->cdw_sg_len == 0) {
790			/*
791			 * End of current segment.
792			 */
793			if (cdw->cdw_sg_index == ctl_sg_count - 1) {
794				/*
795				 * Last segment in scatter/gather list.
796				 */
797				break;
798			}
799			cdw->cdw_sg_index++;
800		}
801
802		if (off == len) {
803			/*
804			 * End of PDU payload.
805			 */
806			break;
807		}
808	}
809
810	if (len > off) {
811		/*
812		 * In case of unsolicited data, it's possible that the buffer
813		 * provided by CTL is smaller than negotiated FirstBurstLength.
814		 * Just ignore the superfluous data; will ask for them with R2T
815		 * on next call to cfiscsi_datamove().
816		 *
817		 * This obviously can only happen with SCSI Command PDU.
818		 */
819		if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
820		    ISCSI_BHS_OPCODE_SCSI_COMMAND)
821			return (true);
822
823		CFISCSI_SESSION_WARN(cs, "received too much data: got %zd bytes, "
824		    "expected %zd; dropping connection",
825		    icl_pdu_data_segment_length(request), off);
826		ctl_set_data_phase_error(&io->scsiio);
827		cfiscsi_session_terminate(cs);
828		return (true);
829	}
830
831	if (io->scsiio.ext_data_filled == cdw->cdw_r2t_end &&
832	    (bhsdo->bhsdo_flags & BHSDO_FLAGS_F) == 0) {
833		CFISCSI_SESSION_WARN(cs, "got the final packet without "
834		    "the F flag; flags = 0x%x; dropping connection",
835		    bhsdo->bhsdo_flags);
836		ctl_set_data_phase_error(&io->scsiio);
837		cfiscsi_session_terminate(cs);
838		return (true);
839	}
840
841	if (io->scsiio.ext_data_filled != cdw->cdw_r2t_end &&
842	    (bhsdo->bhsdo_flags & BHSDO_FLAGS_F) != 0) {
843		if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
844		    ISCSI_BHS_OPCODE_SCSI_DATA_OUT) {
845			CFISCSI_SESSION_WARN(cs, "got the final packet, but the "
846			    "transmitted size was %zd bytes instead of %d; "
847			    "dropping connection",
848			    (size_t)io->scsiio.ext_data_filled,
849			    cdw->cdw_r2t_end);
850			ctl_set_data_phase_error(&io->scsiio);
851			cfiscsi_session_terminate(cs);
852			return (true);
853		} else {
854			/*
855			 * For SCSI Command PDU, this just means we need to
856			 * solicit more data by sending R2T.
857			 */
858			return (false);
859		}
860	}
861
862	if (io->scsiio.ext_data_filled == cdw->cdw_r2t_end) {
863#if 0
864		CFISCSI_SESSION_DEBUG(cs, "no longer expecting Data-Out with target "
865		    "transfer tag 0x%x", cdw->cdw_target_transfer_tag);
866#endif
867
868		return (true);
869	}
870
871	return (false);
872}
873
874static void
875cfiscsi_pdu_handle_data_out(struct icl_pdu *request)
876{
877	struct iscsi_bhs_data_out *bhsdo;
878	struct cfiscsi_session *cs;
879	struct cfiscsi_data_wait *cdw = NULL;
880	union ctl_io *io;
881	bool done;
882
883	cs = PDU_SESSION(request);
884	bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs;
885
886	CFISCSI_SESSION_LOCK(cs);
887	TAILQ_FOREACH(cdw, &cs->cs_waiting_for_data_out, cdw_next) {
888#if 0
889		CFISCSI_SESSION_DEBUG(cs, "have ttt 0x%x, itt 0x%x; looking for "
890		    "ttt 0x%x, itt 0x%x",
891		    bhsdo->bhsdo_target_transfer_tag,
892		    bhsdo->bhsdo_initiator_task_tag,
893		    cdw->cdw_target_transfer_tag, cdw->cdw_initiator_task_tag));
894#endif
895		if (bhsdo->bhsdo_target_transfer_tag ==
896		    cdw->cdw_target_transfer_tag)
897			break;
898	}
899	CFISCSI_SESSION_UNLOCK(cs);
900	if (cdw == NULL) {
901		CFISCSI_SESSION_WARN(cs, "data transfer tag 0x%x, initiator task tag "
902		    "0x%x, not found; dropping connection",
903		    bhsdo->bhsdo_target_transfer_tag, bhsdo->bhsdo_initiator_task_tag);
904		icl_pdu_free(request);
905		cfiscsi_session_terminate(cs);
906		return;
907	}
908
909	if (cdw->cdw_datasn != ntohl(bhsdo->bhsdo_datasn)) {
910		CFISCSI_SESSION_WARN(cs, "received Data-Out PDU with "
911		    "DataSN %u, while expected %u; dropping connection",
912		    ntohl(bhsdo->bhsdo_datasn), cdw->cdw_datasn);
913		icl_pdu_free(request);
914		cfiscsi_session_terminate(cs);
915		return;
916	}
917	cdw->cdw_datasn++;
918
919	io = cdw->cdw_ctl_io;
920	KASSERT((io->io_hdr.flags & CTL_FLAG_DATA_MASK) != CTL_FLAG_DATA_IN,
921	    ("CTL_FLAG_DATA_IN"));
922
923	done = cfiscsi_handle_data_segment(request, cdw);
924	if (done) {
925		CFISCSI_SESSION_LOCK(cs);
926		TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next);
927		CFISCSI_SESSION_UNLOCK(cs);
928		done = (io->scsiio.ext_data_filled != cdw->cdw_r2t_end ||
929		    io->scsiio.ext_data_filled == io->scsiio.kern_data_len);
930		uma_zfree(cfiscsi_data_wait_zone, cdw);
931		if (done)
932			io->scsiio.be_move_done(io);
933		else
934			cfiscsi_datamove_out(io);
935	}
936
937	icl_pdu_free(request);
938}
939
940static void
941cfiscsi_pdu_handle_logout_request(struct icl_pdu *request)
942{
943	struct iscsi_bhs_logout_request *bhslr;
944	struct iscsi_bhs_logout_response *bhslr2;
945	struct icl_pdu *response;
946	struct cfiscsi_session *cs;
947
948	cs = PDU_SESSION(request);
949	bhslr = (struct iscsi_bhs_logout_request *)request->ip_bhs;
950	switch (bhslr->bhslr_reason & 0x7f) {
951	case BHSLR_REASON_CLOSE_SESSION:
952	case BHSLR_REASON_CLOSE_CONNECTION:
953		response = cfiscsi_pdu_new_response(request, M_NOWAIT);
954		if (response == NULL) {
955			CFISCSI_SESSION_DEBUG(cs, "failed to allocate memory");
956			icl_pdu_free(request);
957			cfiscsi_session_terminate(cs);
958			return;
959		}
960		bhslr2 = (struct iscsi_bhs_logout_response *)response->ip_bhs;
961		bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE;
962		bhslr2->bhslr_flags = 0x80;
963		bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY;
964		bhslr2->bhslr_initiator_task_tag =
965		    bhslr->bhslr_initiator_task_tag;
966		icl_pdu_free(request);
967		cfiscsi_pdu_queue(response);
968		cfiscsi_session_terminate(cs);
969		break;
970	case BHSLR_REASON_REMOVE_FOR_RECOVERY:
971		response = cfiscsi_pdu_new_response(request, M_NOWAIT);
972		if (response == NULL) {
973			CFISCSI_SESSION_WARN(cs,
974			    "failed to allocate memory; dropping connection");
975			icl_pdu_free(request);
976			cfiscsi_session_terminate(cs);
977			return;
978		}
979		bhslr2 = (struct iscsi_bhs_logout_response *)response->ip_bhs;
980		bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE;
981		bhslr2->bhslr_flags = 0x80;
982		bhslr2->bhslr_response = BHSLR_RESPONSE_RECOVERY_NOT_SUPPORTED;
983		bhslr2->bhslr_initiator_task_tag =
984		    bhslr->bhslr_initiator_task_tag;
985		icl_pdu_free(request);
986		cfiscsi_pdu_queue(response);
987		break;
988	default:
989		CFISCSI_SESSION_WARN(cs, "invalid reason 0%x; dropping connection",
990		    bhslr->bhslr_reason);
991		icl_pdu_free(request);
992		cfiscsi_session_terminate(cs);
993		break;
994	}
995}
996
997static void
998cfiscsi_callout(void *context)
999{
1000	struct icl_pdu *cp;
1001	struct iscsi_bhs_nop_in *bhsni;
1002	struct cfiscsi_session *cs;
1003
1004	cs = context;
1005
1006	if (cs->cs_terminating)
1007		return;
1008
1009	callout_schedule(&cs->cs_callout, 1 * hz);
1010
1011	atomic_add_int(&cs->cs_timeout, 1);
1012
1013#ifdef ICL_KERNEL_PROXY
1014	if (cs->cs_waiting_for_ctld || cs->cs_login_phase) {
1015		if (login_timeout > 0 && cs->cs_timeout > login_timeout) {
1016			CFISCSI_SESSION_WARN(cs, "login timed out after "
1017			    "%d seconds; dropping connection", cs->cs_timeout);
1018			cfiscsi_session_terminate(cs);
1019		}
1020		return;
1021	}
1022#endif
1023
1024	if (ping_timeout <= 0) {
1025		/*
1026		 * Pings are disabled.  Don't send NOP-In in this case;
1027		 * user might have disabled pings to work around problems
1028		 * with certain initiators that can't properly handle
1029		 * NOP-In, such as iPXE.  Reset the timeout, to avoid
1030		 * triggering reconnection, should the user decide to
1031		 * reenable them.
1032		 */
1033		cs->cs_timeout = 0;
1034		return;
1035	}
1036
1037	if (cs->cs_timeout >= ping_timeout) {
1038		CFISCSI_SESSION_WARN(cs, "no ping reply (NOP-Out) after %d seconds; "
1039		    "dropping connection",  ping_timeout);
1040		cfiscsi_session_terminate(cs);
1041		return;
1042	}
1043
1044	/*
1045	 * If the ping was reset less than one second ago - which means
1046	 * that we've received some PDU during the last second - assume
1047	 * the traffic flows correctly and don't bother sending a NOP-Out.
1048	 *
1049	 * (It's 2 - one for one second, and one for incrementing is_timeout
1050	 * earlier in this routine.)
1051	 */
1052	if (cs->cs_timeout < 2)
1053		return;
1054
1055	cp = icl_pdu_new(cs->cs_conn, M_NOWAIT);
1056	if (cp == NULL) {
1057		CFISCSI_SESSION_WARN(cs, "failed to allocate memory");
1058		return;
1059	}
1060	bhsni = (struct iscsi_bhs_nop_in *)cp->ip_bhs;
1061	bhsni->bhsni_opcode = ISCSI_BHS_OPCODE_NOP_IN;
1062	bhsni->bhsni_flags = 0x80;
1063	bhsni->bhsni_initiator_task_tag = 0xffffffff;
1064
1065	cfiscsi_pdu_queue(cp);
1066}
1067
1068static void
1069cfiscsi_session_terminate_tasks(struct cfiscsi_session *cs)
1070{
1071	struct cfiscsi_data_wait *cdw;
1072	union ctl_io *io;
1073	int error, last, wait;
1074
1075	if (cs->cs_target == NULL)
1076		return;		/* No target yet, so nothing to do. */
1077	io = ctl_alloc_io(cs->cs_target->ct_port.ctl_pool_ref);
1078	ctl_zero_io(io);
1079	io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = cs;
1080	io->io_hdr.io_type = CTL_IO_TASK;
1081	io->io_hdr.nexus.initid.id = cs->cs_ctl_initid;
1082	io->io_hdr.nexus.targ_port = cs->cs_target->ct_port.targ_port;
1083	io->io_hdr.nexus.targ_target.id = 0;
1084	io->io_hdr.nexus.targ_lun = 0;
1085	io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */
1086	io->taskio.task_action = CTL_TASK_I_T_NEXUS_RESET;
1087	wait = cs->cs_outstanding_ctl_pdus;
1088	refcount_acquire(&cs->cs_outstanding_ctl_pdus);
1089	error = ctl_queue(io);
1090	if (error != CTL_RETVAL_COMPLETE) {
1091		CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error);
1092		refcount_release(&cs->cs_outstanding_ctl_pdus);
1093		ctl_free_io(io);
1094	}
1095
1096	CFISCSI_SESSION_LOCK(cs);
1097	while ((cdw = TAILQ_FIRST(&cs->cs_waiting_for_data_out)) != NULL) {
1098		TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next);
1099		CFISCSI_SESSION_UNLOCK(cs);
1100		/*
1101		 * Set nonzero port status; this prevents backends from
1102		 * assuming that the data transfer actually succeeded
1103		 * and writing uninitialized data to disk.
1104		 */
1105		cdw->cdw_ctl_io->scsiio.io_hdr.port_status = 42;
1106		cdw->cdw_ctl_io->scsiio.be_move_done(cdw->cdw_ctl_io);
1107		uma_zfree(cfiscsi_data_wait_zone, cdw);
1108		CFISCSI_SESSION_LOCK(cs);
1109	}
1110	CFISCSI_SESSION_UNLOCK(cs);
1111
1112	/*
1113	 * Wait for CTL to terminate all the tasks.
1114	 */
1115	if (wait > 0)
1116		CFISCSI_SESSION_WARN(cs,
1117		    "waiting for CTL to terminate %d tasks", wait);
1118	for (;;) {
1119		refcount_acquire(&cs->cs_outstanding_ctl_pdus);
1120		last = refcount_release(&cs->cs_outstanding_ctl_pdus);
1121		if (last != 0)
1122			break;
1123		tsleep(__DEVOLATILE(void *, &cs->cs_outstanding_ctl_pdus),
1124		    0, "cfiscsi_terminate", hz / 100);
1125	}
1126	if (wait > 0)
1127		CFISCSI_SESSION_WARN(cs, "tasks terminated");
1128}
1129
1130static void
1131cfiscsi_maintenance_thread(void *arg)
1132{
1133	struct cfiscsi_session *cs;
1134
1135	cs = arg;
1136
1137	for (;;) {
1138		CFISCSI_SESSION_LOCK(cs);
1139		if (cs->cs_terminating == false)
1140			cv_wait(&cs->cs_maintenance_cv, &cs->cs_lock);
1141		CFISCSI_SESSION_UNLOCK(cs);
1142
1143		if (cs->cs_terminating) {
1144
1145			/*
1146			 * We used to wait up to 30 seconds to deliver queued
1147			 * PDUs to the initiator.  We also tried hard to deliver
1148			 * SCSI Responses for the aborted PDUs.  We don't do
1149			 * that anymore.  We might need to revisit that.
1150			 */
1151			callout_drain(&cs->cs_callout);
1152			icl_conn_close(cs->cs_conn);
1153
1154			/*
1155			 * At this point ICL receive thread is no longer
1156			 * running; no new tasks can be queued.
1157			 */
1158			cfiscsi_session_terminate_tasks(cs);
1159			cfiscsi_session_delete(cs);
1160			kthread_exit();
1161			return;
1162		}
1163		CFISCSI_SESSION_DEBUG(cs, "nothing to do");
1164	}
1165}
1166
1167static void
1168cfiscsi_session_terminate(struct cfiscsi_session *cs)
1169{
1170
1171	if (cs->cs_terminating)
1172		return;
1173	cs->cs_terminating = true;
1174	cv_signal(&cs->cs_maintenance_cv);
1175#ifdef ICL_KERNEL_PROXY
1176	cv_signal(&cs->cs_login_cv);
1177#endif
1178}
1179
1180static int
1181cfiscsi_session_register_initiator(struct cfiscsi_session *cs)
1182{
1183	struct cfiscsi_target *ct;
1184	char *name;
1185	int i;
1186
1187	KASSERT(cs->cs_ctl_initid == -1, ("already registered"));
1188
1189	ct = cs->cs_target;
1190	name = strdup(cs->cs_initiator_id, M_CTL);
1191	i = ctl_add_initiator(&ct->ct_port, -1, 0, name);
1192	if (i < 0) {
1193		CFISCSI_SESSION_WARN(cs, "ctl_add_initiator failed with error %d",
1194		    i);
1195		cs->cs_ctl_initid = -1;
1196		return (1);
1197	}
1198	cs->cs_ctl_initid = i;
1199#if 0
1200	CFISCSI_SESSION_DEBUG(cs, "added initiator id %d", i);
1201#endif
1202
1203	return (0);
1204}
1205
1206static void
1207cfiscsi_session_unregister_initiator(struct cfiscsi_session *cs)
1208{
1209	int error;
1210
1211	if (cs->cs_ctl_initid == -1)
1212		return;
1213
1214	error = ctl_remove_initiator(&cs->cs_target->ct_port, cs->cs_ctl_initid);
1215	if (error != 0) {
1216		CFISCSI_SESSION_WARN(cs, "ctl_remove_initiator failed with error %d",
1217		    error);
1218	}
1219	cs->cs_ctl_initid = -1;
1220}
1221
1222static struct cfiscsi_session *
1223cfiscsi_session_new(struct cfiscsi_softc *softc)
1224{
1225	struct cfiscsi_session *cs;
1226	int error;
1227
1228	cs = malloc(sizeof(*cs), M_CFISCSI, M_NOWAIT | M_ZERO);
1229	if (cs == NULL) {
1230		CFISCSI_WARN("malloc failed");
1231		return (NULL);
1232	}
1233	cs->cs_ctl_initid = -1;
1234
1235	refcount_init(&cs->cs_outstanding_ctl_pdus, 0);
1236	TAILQ_INIT(&cs->cs_waiting_for_data_out);
1237	mtx_init(&cs->cs_lock, "cfiscsi_lock", NULL, MTX_DEF);
1238	cv_init(&cs->cs_maintenance_cv, "cfiscsi_mt");
1239#ifdef ICL_KERNEL_PROXY
1240	cv_init(&cs->cs_login_cv, "cfiscsi_login");
1241#endif
1242
1243	cs->cs_conn = icl_conn_new("cfiscsi", &cs->cs_lock);
1244	cs->cs_conn->ic_receive = cfiscsi_receive_callback;
1245	cs->cs_conn->ic_error = cfiscsi_error_callback;
1246	cs->cs_conn->ic_prv0 = cs;
1247
1248	error = kthread_add(cfiscsi_maintenance_thread, cs, NULL, NULL, 0, 0, "cfiscsimt");
1249	if (error != 0) {
1250		CFISCSI_SESSION_WARN(cs, "kthread_add(9) failed with error %d", error);
1251		free(cs, M_CFISCSI);
1252		return (NULL);
1253	}
1254
1255	mtx_lock(&softc->lock);
1256	cs->cs_id = ++softc->last_session_id;
1257	TAILQ_INSERT_TAIL(&softc->sessions, cs, cs_next);
1258	mtx_unlock(&softc->lock);
1259
1260	/*
1261	 * Start pinging the initiator.
1262	 */
1263	callout_init(&cs->cs_callout, 1);
1264	callout_reset(&cs->cs_callout, 1 * hz, cfiscsi_callout, cs);
1265
1266	return (cs);
1267}
1268
1269static void
1270cfiscsi_session_delete(struct cfiscsi_session *cs)
1271{
1272	struct cfiscsi_softc *softc;
1273
1274	softc = &cfiscsi_softc;
1275
1276	KASSERT(cs->cs_outstanding_ctl_pdus == 0,
1277	    ("destroying session with outstanding CTL pdus"));
1278	KASSERT(TAILQ_EMPTY(&cs->cs_waiting_for_data_out),
1279	    ("destroying session with non-empty queue"));
1280
1281	cfiscsi_session_unregister_initiator(cs);
1282	if (cs->cs_target != NULL)
1283		cfiscsi_target_release(cs->cs_target);
1284	icl_conn_close(cs->cs_conn);
1285	icl_conn_free(cs->cs_conn);
1286
1287	mtx_lock(&softc->lock);
1288	TAILQ_REMOVE(&softc->sessions, cs, cs_next);
1289	cv_signal(&softc->sessions_cv);
1290	mtx_unlock(&softc->lock);
1291
1292	free(cs, M_CFISCSI);
1293}
1294
1295int
1296cfiscsi_init(void)
1297{
1298	struct cfiscsi_softc *softc;
1299	int retval;
1300
1301	softc = &cfiscsi_softc;
1302	retval = 0;
1303	bzero(softc, sizeof(*softc));
1304	mtx_init(&softc->lock, "cfiscsi", NULL, MTX_DEF);
1305
1306	cv_init(&softc->sessions_cv, "cfiscsi_sessions");
1307#ifdef ICL_KERNEL_PROXY
1308	cv_init(&softc->accept_cv, "cfiscsi_accept");
1309#endif
1310	TAILQ_INIT(&softc->sessions);
1311	TAILQ_INIT(&softc->targets);
1312
1313	cfiscsi_data_wait_zone = uma_zcreate("cfiscsi_data_wait",
1314	    sizeof(struct cfiscsi_data_wait), NULL, NULL, NULL, NULL,
1315	    UMA_ALIGN_PTR, 0);
1316
1317	return (0);
1318}
1319
1320#ifdef ICL_KERNEL_PROXY
1321static void
1322cfiscsi_accept(struct socket *so, struct sockaddr *sa, int portal_id)
1323{
1324	struct cfiscsi_session *cs;
1325
1326	cs = cfiscsi_session_new(&cfiscsi_softc);
1327	if (cs == NULL) {
1328		CFISCSI_WARN("failed to create session");
1329		return;
1330	}
1331
1332	icl_conn_handoff_sock(cs->cs_conn, so);
1333	cs->cs_initiator_sa = sa;
1334	cs->cs_portal_id = portal_id;
1335	cs->cs_waiting_for_ctld = true;
1336	cv_signal(&cfiscsi_softc.accept_cv);
1337}
1338#endif
1339
1340static void
1341cfiscsi_online(void *arg)
1342{
1343	struct cfiscsi_softc *softc;
1344	struct cfiscsi_target *ct;
1345	int online;
1346
1347	ct = (struct cfiscsi_target *)arg;
1348	softc = ct->ct_softc;
1349
1350	mtx_lock(&softc->lock);
1351	if (ct->ct_online) {
1352		mtx_unlock(&softc->lock);
1353		return;
1354	}
1355	ct->ct_online = 1;
1356	online = softc->online++;
1357	mtx_unlock(&softc->lock);
1358	if (online > 0)
1359		return;
1360
1361#ifdef ICL_KERNEL_PROXY
1362	if (softc->listener != NULL)
1363		icl_listen_free(softc->listener);
1364	softc->listener = icl_listen_new(cfiscsi_accept);
1365#endif
1366}
1367
1368static void
1369cfiscsi_offline(void *arg)
1370{
1371	struct cfiscsi_softc *softc;
1372	struct cfiscsi_target *ct;
1373	struct cfiscsi_session *cs;
1374	int online;
1375
1376	ct = (struct cfiscsi_target *)arg;
1377	softc = ct->ct_softc;
1378
1379	mtx_lock(&softc->lock);
1380	if (!ct->ct_online) {
1381		mtx_unlock(&softc->lock);
1382		return;
1383	}
1384	ct->ct_online = 0;
1385	online = --softc->online;
1386
1387	TAILQ_FOREACH(cs, &softc->sessions, cs_next) {
1388		if (cs->cs_target == ct)
1389			cfiscsi_session_terminate(cs);
1390	}
1391	do {
1392		TAILQ_FOREACH(cs, &softc->sessions, cs_next) {
1393			if (cs->cs_target == ct)
1394				break;
1395		}
1396		if (cs != NULL)
1397			cv_wait(&softc->sessions_cv, &softc->lock);
1398	} while (cs != NULL && ct->ct_online == 0);
1399	mtx_unlock(&softc->lock);
1400	if (online > 0)
1401		return;
1402
1403#ifdef ICL_KERNEL_PROXY
1404	icl_listen_free(softc->listener);
1405	softc->listener = NULL;
1406#endif
1407}
1408
1409static int
1410cfiscsi_info(void *arg, struct sbuf *sb)
1411{
1412	struct cfiscsi_target *ct = (struct cfiscsi_target *)arg;
1413	int retval;
1414
1415	retval = sbuf_printf(sb, "\t<cfiscsi_state>%d</cfiscsi_state>\n",
1416	    ct->ct_state);
1417	return (retval);
1418}
1419
1420static void
1421cfiscsi_ioctl_handoff(struct ctl_iscsi *ci)
1422{
1423	struct cfiscsi_softc *softc;
1424	struct cfiscsi_session *cs, *cs2;
1425	struct cfiscsi_target *ct;
1426	struct ctl_iscsi_handoff_params *cihp;
1427	int error;
1428
1429	cihp = (struct ctl_iscsi_handoff_params *)&(ci->data);
1430	softc = &cfiscsi_softc;
1431
1432	CFISCSI_DEBUG("new connection from %s (%s) to %s",
1433	    cihp->initiator_name, cihp->initiator_addr,
1434	    cihp->target_name);
1435
1436	ct = cfiscsi_target_find(softc, cihp->target_name,
1437	    cihp->portal_group_tag);
1438	if (ct == NULL) {
1439		ci->status = CTL_ISCSI_ERROR;
1440		snprintf(ci->error_str, sizeof(ci->error_str),
1441		    "%s: target not found", __func__);
1442		return;
1443	}
1444
1445#ifdef ICL_KERNEL_PROXY
1446	if (cihp->socket > 0 && cihp->connection_id > 0) {
1447		snprintf(ci->error_str, sizeof(ci->error_str),
1448		    "both socket and connection_id set");
1449		ci->status = CTL_ISCSI_ERROR;
1450		cfiscsi_target_release(ct);
1451		return;
1452	}
1453	if (cihp->socket == 0) {
1454		mtx_lock(&cfiscsi_softc.lock);
1455		TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) {
1456			if (cs->cs_id == cihp->connection_id)
1457				break;
1458		}
1459		if (cs == NULL) {
1460			mtx_unlock(&cfiscsi_softc.lock);
1461			snprintf(ci->error_str, sizeof(ci->error_str),
1462			    "connection not found");
1463			ci->status = CTL_ISCSI_ERROR;
1464			cfiscsi_target_release(ct);
1465			return;
1466		}
1467		mtx_unlock(&cfiscsi_softc.lock);
1468	} else {
1469#endif
1470		cs = cfiscsi_session_new(softc);
1471		if (cs == NULL) {
1472			ci->status = CTL_ISCSI_ERROR;
1473			snprintf(ci->error_str, sizeof(ci->error_str),
1474			    "%s: cfiscsi_session_new failed", __func__);
1475			cfiscsi_target_release(ct);
1476			return;
1477		}
1478#ifdef ICL_KERNEL_PROXY
1479	}
1480#endif
1481
1482	/*
1483	 * First PDU of Full Feature phase has the same CmdSN as the last
1484	 * PDU from the Login Phase received from the initiator.  Thus,
1485	 * the -1 below.
1486	 */
1487	cs->cs_cmdsn = cihp->cmdsn;
1488	cs->cs_statsn = cihp->statsn;
1489	cs->cs_max_data_segment_length = cihp->max_recv_data_segment_length;
1490	cs->cs_max_burst_length = cihp->max_burst_length;
1491	cs->cs_immediate_data = !!cihp->immediate_data;
1492	if (cihp->header_digest == CTL_ISCSI_DIGEST_CRC32C)
1493		cs->cs_conn->ic_header_crc32c = true;
1494	if (cihp->data_digest == CTL_ISCSI_DIGEST_CRC32C)
1495		cs->cs_conn->ic_data_crc32c = true;
1496
1497	strlcpy(cs->cs_initiator_name,
1498	    cihp->initiator_name, sizeof(cs->cs_initiator_name));
1499	strlcpy(cs->cs_initiator_addr,
1500	    cihp->initiator_addr, sizeof(cs->cs_initiator_addr));
1501	strlcpy(cs->cs_initiator_alias,
1502	    cihp->initiator_alias, sizeof(cs->cs_initiator_alias));
1503	memcpy(cs->cs_initiator_isid,
1504	    cihp->initiator_isid, sizeof(cs->cs_initiator_isid));
1505	snprintf(cs->cs_initiator_id, sizeof(cs->cs_initiator_id),
1506	    "%s,i,0x%02x%02x%02x%02x%02x%02x", cs->cs_initiator_name,
1507	    cihp->initiator_isid[0], cihp->initiator_isid[1],
1508	    cihp->initiator_isid[2], cihp->initiator_isid[3],
1509	    cihp->initiator_isid[4], cihp->initiator_isid[5]);
1510
1511	mtx_lock(&softc->lock);
1512	if (ct->ct_online == 0) {
1513		mtx_unlock(&softc->lock);
1514		cfiscsi_session_terminate(cs);
1515		cfiscsi_target_release(ct);
1516		ci->status = CTL_ISCSI_ERROR;
1517		snprintf(ci->error_str, sizeof(ci->error_str),
1518		    "%s: port offline", __func__);
1519		return;
1520	}
1521	cs->cs_target = ct;
1522	mtx_unlock(&softc->lock);
1523
1524	refcount_acquire(&cs->cs_outstanding_ctl_pdus);
1525restart:
1526	if (!cs->cs_terminating) {
1527		mtx_lock(&softc->lock);
1528		TAILQ_FOREACH(cs2, &softc->sessions, cs_next) {
1529			if (cs2 != cs && cs2->cs_tasks_aborted == false &&
1530			    cs->cs_target == cs2->cs_target &&
1531			    strcmp(cs->cs_initiator_id, cs2->cs_initiator_id) == 0) {
1532				cfiscsi_session_terminate(cs2);
1533				mtx_unlock(&softc->lock);
1534				pause("cfiscsi_reinstate", 1);
1535				goto restart;
1536			}
1537		}
1538		mtx_unlock(&softc->lock);
1539	}
1540
1541	/*
1542	 * Register initiator with CTL.
1543	 */
1544	cfiscsi_session_register_initiator(cs);
1545
1546#ifdef ICL_KERNEL_PROXY
1547	if (cihp->socket > 0) {
1548#endif
1549		error = icl_conn_handoff(cs->cs_conn, cihp->socket);
1550		if (error != 0) {
1551			cfiscsi_session_terminate(cs);
1552			refcount_release(&cs->cs_outstanding_ctl_pdus);
1553			ci->status = CTL_ISCSI_ERROR;
1554			snprintf(ci->error_str, sizeof(ci->error_str),
1555			    "%s: icl_conn_handoff failed with error %d",
1556			    __func__, error);
1557			return;
1558		}
1559#ifdef ICL_KERNEL_PROXY
1560	}
1561#endif
1562
1563#ifdef ICL_KERNEL_PROXY
1564	cs->cs_login_phase = false;
1565
1566	/*
1567	 * First PDU of the Full Feature phase has likely already arrived.
1568	 * We have to pick it up and execute properly.
1569	 */
1570	if (cs->cs_login_pdu != NULL) {
1571		CFISCSI_SESSION_DEBUG(cs, "picking up first PDU");
1572		cfiscsi_pdu_handle(cs->cs_login_pdu);
1573		cs->cs_login_pdu = NULL;
1574	}
1575#endif
1576
1577	refcount_release(&cs->cs_outstanding_ctl_pdus);
1578	ci->status = CTL_ISCSI_OK;
1579}
1580
1581static void
1582cfiscsi_ioctl_list(struct ctl_iscsi *ci)
1583{
1584	struct ctl_iscsi_list_params *cilp;
1585	struct cfiscsi_session *cs;
1586	struct cfiscsi_softc *softc;
1587	struct sbuf *sb;
1588	int error;
1589
1590	cilp = (struct ctl_iscsi_list_params *)&(ci->data);
1591	softc = &cfiscsi_softc;
1592
1593	sb = sbuf_new(NULL, NULL, cilp->alloc_len, SBUF_FIXEDLEN);
1594	if (sb == NULL) {
1595		ci->status = CTL_ISCSI_ERROR;
1596		snprintf(ci->error_str, sizeof(ci->error_str),
1597		    "Unable to allocate %d bytes for iSCSI session list",
1598		    cilp->alloc_len);
1599		return;
1600	}
1601
1602	sbuf_printf(sb, "<ctlislist>\n");
1603	mtx_lock(&softc->lock);
1604	TAILQ_FOREACH(cs, &softc->sessions, cs_next) {
1605#ifdef ICL_KERNEL_PROXY
1606		if (cs->cs_target == NULL)
1607			continue;
1608#endif
1609		error = sbuf_printf(sb, "<connection id=\"%d\">"
1610		    "<initiator>%s</initiator>"
1611		    "<initiator_addr>%s</initiator_addr>"
1612		    "<initiator_alias>%s</initiator_alias>"
1613		    "<target>%s</target>"
1614		    "<target_alias>%s</target_alias>"
1615		    "<target_portal_group_tag>%u</target_portal_group_tag>"
1616		    "<header_digest>%s</header_digest>"
1617		    "<data_digest>%s</data_digest>"
1618		    "<max_data_segment_length>%zd</max_data_segment_length>"
1619		    "<immediate_data>%d</immediate_data>"
1620		    "<iser>%d</iser>"
1621		    "</connection>\n",
1622		    cs->cs_id,
1623		    cs->cs_initiator_name, cs->cs_initiator_addr, cs->cs_initiator_alias,
1624		    cs->cs_target->ct_name, cs->cs_target->ct_alias,
1625		    cs->cs_target->ct_tag,
1626		    cs->cs_conn->ic_header_crc32c ? "CRC32C" : "None",
1627		    cs->cs_conn->ic_data_crc32c ? "CRC32C" : "None",
1628		    cs->cs_max_data_segment_length,
1629		    cs->cs_immediate_data,
1630		    cs->cs_conn->ic_iser);
1631		if (error != 0)
1632			break;
1633	}
1634	mtx_unlock(&softc->lock);
1635	error = sbuf_printf(sb, "</ctlislist>\n");
1636	if (error != 0) {
1637		sbuf_delete(sb);
1638		ci->status = CTL_ISCSI_LIST_NEED_MORE_SPACE;
1639		snprintf(ci->error_str, sizeof(ci->error_str),
1640		    "Out of space, %d bytes is too small", cilp->alloc_len);
1641		return;
1642	}
1643	sbuf_finish(sb);
1644
1645	error = copyout(sbuf_data(sb), cilp->conn_xml, sbuf_len(sb) + 1);
1646	cilp->fill_len = sbuf_len(sb) + 1;
1647	ci->status = CTL_ISCSI_OK;
1648	sbuf_delete(sb);
1649}
1650
1651static void
1652cfiscsi_ioctl_logout(struct ctl_iscsi *ci)
1653{
1654	struct icl_pdu *response;
1655	struct iscsi_bhs_asynchronous_message *bhsam;
1656	struct ctl_iscsi_logout_params *cilp;
1657	struct cfiscsi_session *cs;
1658	struct cfiscsi_softc *softc;
1659	int found = 0;
1660
1661	cilp = (struct ctl_iscsi_logout_params *)&(ci->data);
1662	softc = &cfiscsi_softc;
1663
1664	mtx_lock(&softc->lock);
1665	TAILQ_FOREACH(cs, &softc->sessions, cs_next) {
1666		if (cilp->all == 0 && cs->cs_id != cilp->connection_id &&
1667		    strcmp(cs->cs_initiator_name, cilp->initiator_name) != 0 &&
1668		    strcmp(cs->cs_initiator_addr, cilp->initiator_addr) != 0)
1669			continue;
1670
1671		response = icl_pdu_new(cs->cs_conn, M_NOWAIT);
1672		if (response == NULL) {
1673			ci->status = CTL_ISCSI_ERROR;
1674			snprintf(ci->error_str, sizeof(ci->error_str),
1675			    "Unable to allocate memory");
1676			mtx_unlock(&softc->lock);
1677			return;
1678		}
1679		bhsam =
1680		    (struct iscsi_bhs_asynchronous_message *)response->ip_bhs;
1681		bhsam->bhsam_opcode = ISCSI_BHS_OPCODE_ASYNC_MESSAGE;
1682		bhsam->bhsam_flags = 0x80;
1683		bhsam->bhsam_async_event = BHSAM_EVENT_TARGET_REQUESTS_LOGOUT;
1684		bhsam->bhsam_parameter3 = htons(10);
1685		cfiscsi_pdu_queue(response);
1686		found++;
1687	}
1688	mtx_unlock(&softc->lock);
1689
1690	if (found == 0) {
1691		ci->status = CTL_ISCSI_SESSION_NOT_FOUND;
1692		snprintf(ci->error_str, sizeof(ci->error_str),
1693		    "No matching connections found");
1694		return;
1695	}
1696
1697	ci->status = CTL_ISCSI_OK;
1698}
1699
1700static void
1701cfiscsi_ioctl_terminate(struct ctl_iscsi *ci)
1702{
1703	struct icl_pdu *response;
1704	struct iscsi_bhs_asynchronous_message *bhsam;
1705	struct ctl_iscsi_terminate_params *citp;
1706	struct cfiscsi_session *cs;
1707	struct cfiscsi_softc *softc;
1708	int found = 0;
1709
1710	citp = (struct ctl_iscsi_terminate_params *)&(ci->data);
1711	softc = &cfiscsi_softc;
1712
1713	mtx_lock(&softc->lock);
1714	TAILQ_FOREACH(cs, &softc->sessions, cs_next) {
1715		if (citp->all == 0 && cs->cs_id != citp->connection_id &&
1716		    strcmp(cs->cs_initiator_name, citp->initiator_name) != 0 &&
1717		    strcmp(cs->cs_initiator_addr, citp->initiator_addr) != 0)
1718			continue;
1719
1720		response = icl_pdu_new(cs->cs_conn, M_NOWAIT);
1721		if (response == NULL) {
1722			/*
1723			 * Oh well.  Just terminate the connection.
1724			 */
1725		} else {
1726			bhsam = (struct iscsi_bhs_asynchronous_message *)
1727			    response->ip_bhs;
1728			bhsam->bhsam_opcode = ISCSI_BHS_OPCODE_ASYNC_MESSAGE;
1729			bhsam->bhsam_flags = 0x80;
1730			bhsam->bhsam_0xffffffff = 0xffffffff;
1731			bhsam->bhsam_async_event =
1732			    BHSAM_EVENT_TARGET_TERMINATES_SESSION;
1733			cfiscsi_pdu_queue(response);
1734		}
1735		cfiscsi_session_terminate(cs);
1736		found++;
1737	}
1738	mtx_unlock(&softc->lock);
1739
1740	if (found == 0) {
1741		ci->status = CTL_ISCSI_SESSION_NOT_FOUND;
1742		snprintf(ci->error_str, sizeof(ci->error_str),
1743		    "No matching connections found");
1744		return;
1745	}
1746
1747	ci->status = CTL_ISCSI_OK;
1748}
1749
1750#ifdef ICL_KERNEL_PROXY
1751static void
1752cfiscsi_ioctl_listen(struct ctl_iscsi *ci)
1753{
1754	struct ctl_iscsi_listen_params *cilp;
1755	struct sockaddr *sa;
1756	int error;
1757
1758	cilp = (struct ctl_iscsi_listen_params *)&(ci->data);
1759
1760	if (cfiscsi_softc.listener == NULL) {
1761		CFISCSI_DEBUG("no listener");
1762		snprintf(ci->error_str, sizeof(ci->error_str), "no listener");
1763		ci->status = CTL_ISCSI_ERROR;
1764		return;
1765	}
1766
1767	error = getsockaddr(&sa, (void *)cilp->addr, cilp->addrlen);
1768	if (error != 0) {
1769		CFISCSI_DEBUG("getsockaddr, error %d", error);
1770		snprintf(ci->error_str, sizeof(ci->error_str), "getsockaddr failed");
1771		ci->status = CTL_ISCSI_ERROR;
1772		return;
1773	}
1774
1775	error = icl_listen_add(cfiscsi_softc.listener, cilp->iser, cilp->domain,
1776	    cilp->socktype, cilp->protocol, sa, cilp->portal_id);
1777	if (error != 0) {
1778		free(sa, M_SONAME);
1779		CFISCSI_DEBUG("icl_listen_add, error %d", error);
1780		snprintf(ci->error_str, sizeof(ci->error_str),
1781		    "icl_listen_add failed, error %d", error);
1782		ci->status = CTL_ISCSI_ERROR;
1783		return;
1784	}
1785
1786	ci->status = CTL_ISCSI_OK;
1787}
1788
1789static void
1790cfiscsi_ioctl_accept(struct ctl_iscsi *ci)
1791{
1792	struct ctl_iscsi_accept_params *ciap;
1793	struct cfiscsi_session *cs;
1794	int error;
1795
1796	ciap = (struct ctl_iscsi_accept_params *)&(ci->data);
1797
1798	mtx_lock(&cfiscsi_softc.lock);
1799	for (;;) {
1800		TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) {
1801			if (cs->cs_waiting_for_ctld)
1802				break;
1803		}
1804		if (cs != NULL)
1805			break;
1806		error = cv_wait_sig(&cfiscsi_softc.accept_cv, &cfiscsi_softc.lock);
1807		if (error != 0) {
1808			mtx_unlock(&cfiscsi_softc.lock);
1809			snprintf(ci->error_str, sizeof(ci->error_str), "interrupted");
1810			ci->status = CTL_ISCSI_ERROR;
1811			return;
1812		}
1813	}
1814	mtx_unlock(&cfiscsi_softc.lock);
1815
1816	cs->cs_waiting_for_ctld = false;
1817	cs->cs_login_phase = true;
1818
1819	ciap->connection_id = cs->cs_id;
1820	ciap->portal_id = cs->cs_portal_id;
1821	ciap->initiator_addrlen = cs->cs_initiator_sa->sa_len;
1822	error = copyout(cs->cs_initiator_sa, ciap->initiator_addr,
1823	    cs->cs_initiator_sa->sa_len);
1824	if (error != 0) {
1825		snprintf(ci->error_str, sizeof(ci->error_str),
1826		    "copyout failed with error %d", error);
1827		ci->status = CTL_ISCSI_ERROR;
1828		return;
1829	}
1830
1831	ci->status = CTL_ISCSI_OK;
1832}
1833
1834static void
1835cfiscsi_ioctl_send(struct ctl_iscsi *ci)
1836{
1837	struct ctl_iscsi_send_params *cisp;
1838	struct cfiscsi_session *cs;
1839	struct icl_pdu *ip;
1840	size_t datalen;
1841	void *data;
1842	int error;
1843
1844	cisp = (struct ctl_iscsi_send_params *)&(ci->data);
1845
1846	mtx_lock(&cfiscsi_softc.lock);
1847	TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) {
1848		if (cs->cs_id == cisp->connection_id)
1849			break;
1850	}
1851	if (cs == NULL) {
1852		mtx_unlock(&cfiscsi_softc.lock);
1853		snprintf(ci->error_str, sizeof(ci->error_str), "connection not found");
1854		ci->status = CTL_ISCSI_ERROR;
1855		return;
1856	}
1857	mtx_unlock(&cfiscsi_softc.lock);
1858
1859#if 0
1860	if (cs->cs_login_phase == false)
1861		return (EBUSY);
1862#endif
1863
1864	if (cs->cs_terminating) {
1865		snprintf(ci->error_str, sizeof(ci->error_str), "connection is terminating");
1866		ci->status = CTL_ISCSI_ERROR;
1867		return;
1868	}
1869
1870	datalen = cisp->data_segment_len;
1871	/*
1872	 * XXX
1873	 */
1874	//if (datalen > CFISCSI_MAX_DATA_SEGMENT_LENGTH) {
1875	if (datalen > 65535) {
1876		snprintf(ci->error_str, sizeof(ci->error_str), "data segment too big");
1877		ci->status = CTL_ISCSI_ERROR;
1878		return;
1879	}
1880	if (datalen > 0) {
1881		data = malloc(datalen, M_CFISCSI, M_WAITOK);
1882		error = copyin(cisp->data_segment, data, datalen);
1883		if (error != 0) {
1884			free(data, M_CFISCSI);
1885			snprintf(ci->error_str, sizeof(ci->error_str), "copyin error %d", error);
1886			ci->status = CTL_ISCSI_ERROR;
1887			return;
1888		}
1889	}
1890
1891	ip = icl_pdu_new(cs->cs_conn, M_WAITOK);
1892	memcpy(ip->ip_bhs, cisp->bhs, sizeof(*ip->ip_bhs));
1893	if (datalen > 0) {
1894		icl_pdu_append_data(ip, data, datalen, M_WAITOK);
1895		free(data, M_CFISCSI);
1896	}
1897	CFISCSI_SESSION_LOCK(cs);
1898	icl_pdu_queue(ip);
1899	CFISCSI_SESSION_UNLOCK(cs);
1900	ci->status = CTL_ISCSI_OK;
1901}
1902
1903static void
1904cfiscsi_ioctl_receive(struct ctl_iscsi *ci)
1905{
1906	struct ctl_iscsi_receive_params *cirp;
1907	struct cfiscsi_session *cs;
1908	struct icl_pdu *ip;
1909	void *data;
1910	int error;
1911
1912	cirp = (struct ctl_iscsi_receive_params *)&(ci->data);
1913
1914	mtx_lock(&cfiscsi_softc.lock);
1915	TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) {
1916		if (cs->cs_id == cirp->connection_id)
1917			break;
1918	}
1919	if (cs == NULL) {
1920		mtx_unlock(&cfiscsi_softc.lock);
1921		snprintf(ci->error_str, sizeof(ci->error_str),
1922		    "connection not found");
1923		ci->status = CTL_ISCSI_ERROR;
1924		return;
1925	}
1926	mtx_unlock(&cfiscsi_softc.lock);
1927
1928#if 0
1929	if (is->is_login_phase == false)
1930		return (EBUSY);
1931#endif
1932
1933	CFISCSI_SESSION_LOCK(cs);
1934	while (cs->cs_login_pdu == NULL && cs->cs_terminating == false) {
1935		error = cv_wait_sig(&cs->cs_login_cv, &cs->cs_lock);
1936		if (error != 0) {
1937			CFISCSI_SESSION_UNLOCK(cs);
1938			snprintf(ci->error_str, sizeof(ci->error_str),
1939			    "interrupted by signal");
1940			ci->status = CTL_ISCSI_ERROR;
1941			return;
1942		}
1943	}
1944
1945	if (cs->cs_terminating) {
1946		CFISCSI_SESSION_UNLOCK(cs);
1947		snprintf(ci->error_str, sizeof(ci->error_str),
1948		    "connection terminating");
1949		ci->status = CTL_ISCSI_ERROR;
1950		return;
1951	}
1952	ip = cs->cs_login_pdu;
1953	cs->cs_login_pdu = NULL;
1954	CFISCSI_SESSION_UNLOCK(cs);
1955
1956	if (ip->ip_data_len > cirp->data_segment_len) {
1957		icl_pdu_free(ip);
1958		snprintf(ci->error_str, sizeof(ci->error_str),
1959		    "data segment too big");
1960		ci->status = CTL_ISCSI_ERROR;
1961		return;
1962	}
1963
1964	copyout(ip->ip_bhs, cirp->bhs, sizeof(*ip->ip_bhs));
1965	if (ip->ip_data_len > 0) {
1966		data = malloc(ip->ip_data_len, M_CFISCSI, M_WAITOK);
1967		icl_pdu_get_data(ip, 0, data, ip->ip_data_len);
1968		copyout(data, cirp->data_segment, ip->ip_data_len);
1969		free(data, M_CFISCSI);
1970	}
1971
1972	icl_pdu_free(ip);
1973	ci->status = CTL_ISCSI_OK;
1974}
1975
1976#endif /* !ICL_KERNEL_PROXY */
1977
1978static void
1979cfiscsi_ioctl_port_create(struct ctl_req *req)
1980{
1981	struct cfiscsi_target *ct;
1982	struct ctl_port *port;
1983	const char *target, *alias, *tags;
1984	struct scsi_vpd_id_descriptor *desc;
1985	ctl_options_t opts;
1986	int retval, len, idlen;
1987	uint16_t tag;
1988
1989	ctl_init_opts(&opts, req->num_args, req->kern_args);
1990	target = ctl_get_opt(&opts, "cfiscsi_target");
1991	alias = ctl_get_opt(&opts, "cfiscsi_target_alias");
1992	tags = ctl_get_opt(&opts, "cfiscsi_portal_group_tag");
1993	if (target == NULL || tags == NULL) {
1994		req->status = CTL_LUN_ERROR;
1995		snprintf(req->error_str, sizeof(req->error_str),
1996		    "Missing required argument");
1997		ctl_free_opts(&opts);
1998		return;
1999	}
2000	tag = strtol(tags, (char **)NULL, 10);
2001	ct = cfiscsi_target_find_or_create(&cfiscsi_softc, target, alias, tag);
2002	if (ct == NULL) {
2003		req->status = CTL_LUN_ERROR;
2004		snprintf(req->error_str, sizeof(req->error_str),
2005		    "failed to create target \"%s\"", target);
2006		ctl_free_opts(&opts);
2007		return;
2008	}
2009	if (ct->ct_state == CFISCSI_TARGET_STATE_ACTIVE) {
2010		req->status = CTL_LUN_ERROR;
2011		snprintf(req->error_str, sizeof(req->error_str),
2012		    "target \"%s\" already exists", target);
2013		cfiscsi_target_release(ct);
2014		ctl_free_opts(&opts);
2015		return;
2016	}
2017	port = &ct->ct_port;
2018	if (ct->ct_state == CFISCSI_TARGET_STATE_DYING)
2019		goto done;
2020
2021	port->frontend = &cfiscsi_frontend;
2022	port->port_type = CTL_PORT_ISCSI;
2023	/* XXX KDM what should the real number be here? */
2024	port->num_requested_ctl_io = 4096;
2025	port->port_name = "iscsi";
2026	port->physical_port = tag;
2027	port->virtual_port = ct->ct_target_id;
2028	port->port_online = cfiscsi_online;
2029	port->port_offline = cfiscsi_offline;
2030	port->port_info = cfiscsi_info;
2031	port->onoff_arg = ct;
2032	port->fe_datamove = cfiscsi_datamove;
2033	port->fe_done = cfiscsi_done;
2034
2035	/* XXX KDM what should we report here? */
2036	/* XXX These should probably be fetched from CTL. */
2037	port->max_targets = 1;
2038	port->max_target_id = 15;
2039
2040	port->options = opts;
2041	STAILQ_INIT(&opts);
2042
2043	/* Generate Port ID. */
2044	idlen = strlen(target) + strlen(",t,0x0001") + 1;
2045	idlen = roundup2(idlen, 4);
2046	len = sizeof(struct scsi_vpd_device_id) + idlen;
2047	port->port_devid = malloc(sizeof(struct ctl_devid) + len,
2048	    M_CTL, M_WAITOK | M_ZERO);
2049	port->port_devid->len = len;
2050	desc = (struct scsi_vpd_id_descriptor *)port->port_devid->data;
2051	desc->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_UTF8;
2052	desc->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT |
2053	    SVPD_ID_TYPE_SCSI_NAME;
2054	desc->length = idlen;
2055	snprintf(desc->identifier, idlen, "%s,t,0x%4.4x", target, tag);
2056
2057	/* Generate Target ID. */
2058	idlen = strlen(target) + 1;
2059	idlen = roundup2(idlen, 4);
2060	len = sizeof(struct scsi_vpd_device_id) + idlen;
2061	port->target_devid = malloc(sizeof(struct ctl_devid) + len,
2062	    M_CTL, M_WAITOK | M_ZERO);
2063	port->target_devid->len = len;
2064	desc = (struct scsi_vpd_id_descriptor *)port->target_devid->data;
2065	desc->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_UTF8;
2066	desc->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_TARGET |
2067	    SVPD_ID_TYPE_SCSI_NAME;
2068	desc->length = idlen;
2069	strlcpy(desc->identifier, target, idlen);
2070
2071	retval = ctl_port_register(port);
2072	if (retval != 0) {
2073		ctl_free_opts(&port->options);
2074		cfiscsi_target_release(ct);
2075		free(port->port_devid, M_CFISCSI);
2076		free(port->target_devid, M_CFISCSI);
2077		req->status = CTL_LUN_ERROR;
2078		snprintf(req->error_str, sizeof(req->error_str),
2079		    "ctl_port_register() failed with error %d", retval);
2080		return;
2081	}
2082done:
2083	ct->ct_state = CFISCSI_TARGET_STATE_ACTIVE;
2084	req->status = CTL_LUN_OK;
2085	memcpy(req->kern_args[0].kvalue, &port->targ_port,
2086	    sizeof(port->targ_port)); //XXX
2087}
2088
2089static void
2090cfiscsi_ioctl_port_remove(struct ctl_req *req)
2091{
2092	struct cfiscsi_target *ct;
2093	const char *target, *tags;
2094	ctl_options_t opts;
2095	uint16_t tag;
2096
2097	ctl_init_opts(&opts, req->num_args, req->kern_args);
2098	target = ctl_get_opt(&opts, "cfiscsi_target");
2099	tags = ctl_get_opt(&opts, "cfiscsi_portal_group_tag");
2100	if (target == NULL || tags == NULL) {
2101		ctl_free_opts(&opts);
2102		req->status = CTL_LUN_ERROR;
2103		snprintf(req->error_str, sizeof(req->error_str),
2104		    "Missing required argument");
2105		return;
2106	}
2107	tag = strtol(tags, (char **)NULL, 10);
2108	ct = cfiscsi_target_find(&cfiscsi_softc, target, tag);
2109	if (ct == NULL) {
2110		ctl_free_opts(&opts);
2111		req->status = CTL_LUN_ERROR;
2112		snprintf(req->error_str, sizeof(req->error_str),
2113		    "can't find target \"%s\"", target);
2114		return;
2115	}
2116	if (ct->ct_state != CFISCSI_TARGET_STATE_ACTIVE) {
2117		ctl_free_opts(&opts);
2118		req->status = CTL_LUN_ERROR;
2119		snprintf(req->error_str, sizeof(req->error_str),
2120		    "target \"%s\" is already dying", target);
2121		return;
2122	}
2123	ctl_free_opts(&opts);
2124
2125	ct->ct_state = CFISCSI_TARGET_STATE_DYING;
2126	ctl_port_offline(&ct->ct_port);
2127	cfiscsi_target_release(ct);
2128	cfiscsi_target_release(ct);
2129	req->status = CTL_LUN_OK;
2130}
2131
2132static int
2133cfiscsi_ioctl(struct cdev *dev,
2134    u_long cmd, caddr_t addr, int flag, struct thread *td)
2135{
2136	struct ctl_iscsi *ci;
2137	struct ctl_req *req;
2138
2139	if (cmd == CTL_PORT_REQ) {
2140		req = (struct ctl_req *)addr;
2141		switch (req->reqtype) {
2142		case CTL_REQ_CREATE:
2143			cfiscsi_ioctl_port_create(req);
2144			break;
2145		case CTL_REQ_REMOVE:
2146			cfiscsi_ioctl_port_remove(req);
2147			break;
2148		default:
2149			req->status = CTL_LUN_ERROR;
2150			snprintf(req->error_str, sizeof(req->error_str),
2151			    "Unsupported request type %d", req->reqtype);
2152		}
2153		return (0);
2154	}
2155
2156	if (cmd != CTL_ISCSI)
2157		return (ENOTTY);
2158
2159	ci = (struct ctl_iscsi *)addr;
2160	switch (ci->type) {
2161	case CTL_ISCSI_HANDOFF:
2162		cfiscsi_ioctl_handoff(ci);
2163		break;
2164	case CTL_ISCSI_LIST:
2165		cfiscsi_ioctl_list(ci);
2166		break;
2167	case CTL_ISCSI_LOGOUT:
2168		cfiscsi_ioctl_logout(ci);
2169		break;
2170	case CTL_ISCSI_TERMINATE:
2171		cfiscsi_ioctl_terminate(ci);
2172		break;
2173#ifdef ICL_KERNEL_PROXY
2174	case CTL_ISCSI_LISTEN:
2175		cfiscsi_ioctl_listen(ci);
2176		break;
2177	case CTL_ISCSI_ACCEPT:
2178		cfiscsi_ioctl_accept(ci);
2179		break;
2180	case CTL_ISCSI_SEND:
2181		cfiscsi_ioctl_send(ci);
2182		break;
2183	case CTL_ISCSI_RECEIVE:
2184		cfiscsi_ioctl_receive(ci);
2185		break;
2186#else
2187	case CTL_ISCSI_LISTEN:
2188	case CTL_ISCSI_ACCEPT:
2189	case CTL_ISCSI_SEND:
2190	case CTL_ISCSI_RECEIVE:
2191		ci->status = CTL_ISCSI_ERROR;
2192		snprintf(ci->error_str, sizeof(ci->error_str),
2193		    "%s: CTL compiled without ICL_KERNEL_PROXY",
2194		    __func__);
2195		break;
2196#endif /* !ICL_KERNEL_PROXY */
2197	default:
2198		ci->status = CTL_ISCSI_ERROR;
2199		snprintf(ci->error_str, sizeof(ci->error_str),
2200		    "%s: invalid iSCSI request type %d", __func__, ci->type);
2201		break;
2202	}
2203
2204	return (0);
2205}
2206
2207static void
2208cfiscsi_target_hold(struct cfiscsi_target *ct)
2209{
2210
2211	refcount_acquire(&ct->ct_refcount);
2212}
2213
2214static void
2215cfiscsi_target_release(struct cfiscsi_target *ct)
2216{
2217	struct cfiscsi_softc *softc;
2218
2219	softc = ct->ct_softc;
2220	mtx_lock(&softc->lock);
2221	if (refcount_release(&ct->ct_refcount)) {
2222		TAILQ_REMOVE(&softc->targets, ct, ct_next);
2223		mtx_unlock(&softc->lock);
2224		if (ct->ct_state != CFISCSI_TARGET_STATE_INVALID) {
2225			ct->ct_state = CFISCSI_TARGET_STATE_INVALID;
2226			if (ctl_port_deregister(&ct->ct_port) != 0)
2227				printf("%s: ctl_port_deregister() failed\n",
2228				    __func__);
2229		}
2230		free(ct, M_CFISCSI);
2231
2232		return;
2233	}
2234	mtx_unlock(&softc->lock);
2235}
2236
2237static struct cfiscsi_target *
2238cfiscsi_target_find(struct cfiscsi_softc *softc, const char *name, uint16_t tag)
2239{
2240	struct cfiscsi_target *ct;
2241
2242	mtx_lock(&softc->lock);
2243	TAILQ_FOREACH(ct, &softc->targets, ct_next) {
2244		if (ct->ct_tag != tag ||
2245		    strcmp(name, ct->ct_name) != 0 ||
2246		    ct->ct_state != CFISCSI_TARGET_STATE_ACTIVE)
2247			continue;
2248		cfiscsi_target_hold(ct);
2249		mtx_unlock(&softc->lock);
2250		return (ct);
2251	}
2252	mtx_unlock(&softc->lock);
2253
2254	return (NULL);
2255}
2256
2257static struct cfiscsi_target *
2258cfiscsi_target_find_or_create(struct cfiscsi_softc *softc, const char *name,
2259    const char *alias, uint16_t tag)
2260{
2261	struct cfiscsi_target *ct, *newct;
2262
2263	if (name[0] == '\0' || strlen(name) >= CTL_ISCSI_NAME_LEN)
2264		return (NULL);
2265
2266	newct = malloc(sizeof(*newct), M_CFISCSI, M_WAITOK | M_ZERO);
2267
2268	mtx_lock(&softc->lock);
2269	TAILQ_FOREACH(ct, &softc->targets, ct_next) {
2270		if (ct->ct_tag != tag ||
2271		    strcmp(name, ct->ct_name) != 0 ||
2272		    ct->ct_state == CFISCSI_TARGET_STATE_INVALID)
2273			continue;
2274		cfiscsi_target_hold(ct);
2275		mtx_unlock(&softc->lock);
2276		free(newct, M_CFISCSI);
2277		return (ct);
2278	}
2279
2280	strlcpy(newct->ct_name, name, sizeof(newct->ct_name));
2281	if (alias != NULL)
2282		strlcpy(newct->ct_alias, alias, sizeof(newct->ct_alias));
2283	newct->ct_tag = tag;
2284	refcount_init(&newct->ct_refcount, 1);
2285	newct->ct_softc = softc;
2286	if (TAILQ_EMPTY(&softc->targets))
2287		softc->last_target_id = 0;
2288	newct->ct_target_id = ++softc->last_target_id;
2289	TAILQ_INSERT_TAIL(&softc->targets, newct, ct_next);
2290	mtx_unlock(&softc->lock);
2291
2292	return (newct);
2293}
2294
2295static void
2296cfiscsi_datamove_in(union ctl_io *io)
2297{
2298	struct cfiscsi_session *cs;
2299	struct icl_pdu *request, *response;
2300	const struct iscsi_bhs_scsi_command *bhssc;
2301	struct iscsi_bhs_data_in *bhsdi;
2302	struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
2303	size_t len, expected_len, sg_len, buffer_offset;
2304	const char *sg_addr;
2305	int ctl_sg_count, error, i;
2306
2307	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2308	cs = PDU_SESSION(request);
2309
2310	bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs;
2311	KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
2312	    ISCSI_BHS_OPCODE_SCSI_COMMAND,
2313	    ("bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_COMMAND"));
2314
2315	if (io->scsiio.kern_sg_entries > 0) {
2316		ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
2317		ctl_sg_count = io->scsiio.kern_sg_entries;
2318	} else {
2319		ctl_sglist = &ctl_sg_entry;
2320		ctl_sglist->addr = io->scsiio.kern_data_ptr;
2321		ctl_sglist->len = io->scsiio.kern_data_len;
2322		ctl_sg_count = 1;
2323	}
2324
2325	/*
2326	 * This is the total amount of data to be transferred within the current
2327	 * SCSI command.  We need to record it so that we can properly report
2328	 * underflow/underflow.
2329	 */
2330	PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len;
2331
2332	/*
2333	 * This is the offset within the current SCSI command; for the first
2334	 * call to cfiscsi_datamove() it will be 0, and for subsequent ones
2335	 * it will be the sum of lengths of previous ones.
2336	 */
2337	buffer_offset = io->scsiio.kern_rel_offset;
2338
2339	/*
2340	 * This is the transfer length expected by the initiator.  In theory,
2341	 * it could be different from the correct amount of data from the SCSI
2342	 * point of view, even if that doesn't make any sense.
2343	 */
2344	expected_len = ntohl(bhssc->bhssc_expected_data_transfer_length);
2345#if 0
2346	if (expected_len != io->scsiio.kern_total_len) {
2347		CFISCSI_SESSION_DEBUG(cs, "expected transfer length %zd, "
2348		    "actual length %zd", expected_len,
2349		    (size_t)io->scsiio.kern_total_len);
2350	}
2351#endif
2352
2353	if (buffer_offset >= expected_len) {
2354#if 0
2355		CFISCSI_SESSION_DEBUG(cs, "buffer_offset = %zd, "
2356		    "already sent the expected len", buffer_offset);
2357#endif
2358		io->scsiio.be_move_done(io);
2359		return;
2360	}
2361
2362	i = 0;
2363	sg_addr = NULL;
2364	sg_len = 0;
2365	response = NULL;
2366	bhsdi = NULL;
2367	for (;;) {
2368		if (response == NULL) {
2369			response = cfiscsi_pdu_new_response(request, M_NOWAIT);
2370			if (response == NULL) {
2371				CFISCSI_SESSION_WARN(cs, "failed to "
2372				    "allocate memory; dropping connection");
2373				ctl_set_busy(&io->scsiio);
2374				io->scsiio.be_move_done(io);
2375				cfiscsi_session_terminate(cs);
2376				return;
2377			}
2378			bhsdi = (struct iscsi_bhs_data_in *)response->ip_bhs;
2379			bhsdi->bhsdi_opcode = ISCSI_BHS_OPCODE_SCSI_DATA_IN;
2380			bhsdi->bhsdi_initiator_task_tag =
2381			    bhssc->bhssc_initiator_task_tag;
2382			bhsdi->bhsdi_datasn = htonl(PDU_EXPDATASN(request));
2383			PDU_EXPDATASN(request)++;
2384			bhsdi->bhsdi_buffer_offset = htonl(buffer_offset);
2385		}
2386
2387		KASSERT(i < ctl_sg_count, ("i >= ctl_sg_count"));
2388		if (sg_len == 0) {
2389			sg_addr = ctl_sglist[i].addr;
2390			sg_len = ctl_sglist[i].len;
2391			KASSERT(sg_len > 0, ("sg_len <= 0"));
2392		}
2393
2394		len = sg_len;
2395
2396		/*
2397		 * Truncate to maximum data segment length.
2398		 */
2399		KASSERT(response->ip_data_len < cs->cs_max_data_segment_length,
2400		    ("ip_data_len %zd >= max_data_segment_length %zd",
2401		    response->ip_data_len, cs->cs_max_data_segment_length));
2402		if (response->ip_data_len + len >
2403		    cs->cs_max_data_segment_length) {
2404			len = cs->cs_max_data_segment_length -
2405			    response->ip_data_len;
2406			KASSERT(len <= sg_len, ("len %zd > sg_len %zd",
2407			    len, sg_len));
2408		}
2409
2410		/*
2411		 * Truncate to expected data transfer length.
2412		 */
2413		KASSERT(buffer_offset + response->ip_data_len < expected_len,
2414		    ("buffer_offset %zd + ip_data_len %zd >= expected_len %zd",
2415		    buffer_offset, response->ip_data_len, expected_len));
2416		if (buffer_offset + response->ip_data_len + len > expected_len) {
2417			CFISCSI_SESSION_DEBUG(cs, "truncating from %zd "
2418			    "to expected data transfer length %zd",
2419			    buffer_offset + response->ip_data_len + len, expected_len);
2420			len = expected_len - (buffer_offset + response->ip_data_len);
2421			KASSERT(len <= sg_len, ("len %zd > sg_len %zd",
2422			    len, sg_len));
2423		}
2424
2425		error = icl_pdu_append_data(response, sg_addr, len, M_NOWAIT);
2426		if (error != 0) {
2427			CFISCSI_SESSION_WARN(cs, "failed to "
2428			    "allocate memory; dropping connection");
2429			icl_pdu_free(response);
2430			ctl_set_busy(&io->scsiio);
2431			io->scsiio.be_move_done(io);
2432			cfiscsi_session_terminate(cs);
2433			return;
2434		}
2435		sg_addr += len;
2436		sg_len -= len;
2437
2438		KASSERT(buffer_offset + response->ip_data_len <= expected_len,
2439		    ("buffer_offset %zd + ip_data_len %zd > expected_len %zd",
2440		    buffer_offset, response->ip_data_len, expected_len));
2441		if (buffer_offset + response->ip_data_len == expected_len) {
2442			/*
2443			 * Already have the amount of data the initiator wanted.
2444			 */
2445			break;
2446		}
2447
2448		if (sg_len == 0) {
2449			/*
2450			 * End of scatter-gather segment;
2451			 * proceed to the next one...
2452			 */
2453			if (i == ctl_sg_count - 1) {
2454				/*
2455				 * ... unless this was the last one.
2456				 */
2457				break;
2458			}
2459			i++;
2460		}
2461
2462		if (response->ip_data_len == cs->cs_max_data_segment_length) {
2463			/*
2464			 * Can't stuff more data into the current PDU;
2465			 * queue it.  Note that's not enough to check
2466			 * for kern_data_resid == 0 instead; there
2467			 * may be several Data-In PDUs for the final
2468			 * call to cfiscsi_datamove(), and we want
2469			 * to set the F flag only on the last of them.
2470			 */
2471			buffer_offset += response->ip_data_len;
2472			if (buffer_offset == io->scsiio.kern_total_len ||
2473			    buffer_offset == expected_len) {
2474				buffer_offset -= response->ip_data_len;
2475				break;
2476			}
2477			cfiscsi_pdu_queue(response);
2478			response = NULL;
2479			bhsdi = NULL;
2480		}
2481	}
2482	if (response != NULL) {
2483		buffer_offset += response->ip_data_len;
2484		if (buffer_offset == io->scsiio.kern_total_len ||
2485		    buffer_offset == expected_len) {
2486			bhsdi->bhsdi_flags |= BHSDI_FLAGS_F;
2487			if (io->io_hdr.status == CTL_SUCCESS) {
2488				bhsdi->bhsdi_flags |= BHSDI_FLAGS_S;
2489				if (PDU_TOTAL_TRANSFER_LEN(request) <
2490				    ntohl(bhssc->bhssc_expected_data_transfer_length)) {
2491					bhsdi->bhsdi_flags |= BHSSR_FLAGS_RESIDUAL_UNDERFLOW;
2492					bhsdi->bhsdi_residual_count =
2493					    htonl(ntohl(bhssc->bhssc_expected_data_transfer_length) -
2494					    PDU_TOTAL_TRANSFER_LEN(request));
2495				} else if (PDU_TOTAL_TRANSFER_LEN(request) >
2496				    ntohl(bhssc->bhssc_expected_data_transfer_length)) {
2497					bhsdi->bhsdi_flags |= BHSSR_FLAGS_RESIDUAL_OVERFLOW;
2498					bhsdi->bhsdi_residual_count =
2499					    htonl(PDU_TOTAL_TRANSFER_LEN(request) -
2500					    ntohl(bhssc->bhssc_expected_data_transfer_length));
2501				}
2502				bhsdi->bhsdi_status = io->scsiio.scsi_status;
2503				io->io_hdr.flags |= CTL_FLAG_STATUS_SENT;
2504			}
2505		}
2506		KASSERT(response->ip_data_len > 0, ("sending empty Data-In"));
2507		cfiscsi_pdu_queue(response);
2508	}
2509
2510	io->scsiio.be_move_done(io);
2511}
2512
2513static void
2514cfiscsi_datamove_out(union ctl_io *io)
2515{
2516	struct cfiscsi_session *cs;
2517	struct icl_pdu *request, *response;
2518	const struct iscsi_bhs_scsi_command *bhssc;
2519	struct iscsi_bhs_r2t *bhsr2t;
2520	struct cfiscsi_data_wait *cdw;
2521	struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
2522	uint32_t expected_len, r2t_off, r2t_len;
2523	uint32_t target_transfer_tag;
2524	bool done;
2525
2526	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2527	cs = PDU_SESSION(request);
2528
2529	bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs;
2530	KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
2531	    ISCSI_BHS_OPCODE_SCSI_COMMAND,
2532	    ("bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_COMMAND"));
2533
2534	/*
2535	 * We need to record it so that we can properly report
2536	 * underflow/underflow.
2537	 */
2538	PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len;
2539
2540	/*
2541	 * Report write underflow as error since CTL and backends don't
2542	 * really support it, and SCSI does not tell how to do it right.
2543	 */
2544	expected_len = ntohl(bhssc->bhssc_expected_data_transfer_length);
2545	if (io->scsiio.kern_rel_offset + io->scsiio.kern_data_len >
2546	    expected_len) {
2547		io->scsiio.io_hdr.port_status = 43;
2548		io->scsiio.be_move_done(io);
2549		return;
2550	}
2551
2552	target_transfer_tag =
2553	    atomic_fetchadd_32(&cs->cs_target_transfer_tag, 1);
2554
2555#if 0
2556	CFISCSI_SESSION_DEBUG(cs, "expecting Data-Out with initiator "
2557	    "task tag 0x%x, target transfer tag 0x%x",
2558	    bhssc->bhssc_initiator_task_tag, target_transfer_tag);
2559#endif
2560	cdw = uma_zalloc(cfiscsi_data_wait_zone, M_NOWAIT | M_ZERO);
2561	if (cdw == NULL) {
2562		CFISCSI_SESSION_WARN(cs, "failed to "
2563		    "allocate memory; dropping connection");
2564		ctl_set_busy(&io->scsiio);
2565		io->scsiio.be_move_done(io);
2566		cfiscsi_session_terminate(cs);
2567		return;
2568	}
2569	cdw->cdw_ctl_io = io;
2570	cdw->cdw_target_transfer_tag = target_transfer_tag;
2571	cdw->cdw_initiator_task_tag = bhssc->bhssc_initiator_task_tag;
2572	cdw->cdw_r2t_end = io->scsiio.kern_data_len;
2573	cdw->cdw_datasn = 0;
2574
2575	/* Set initial data pointer for the CDW respecting ext_data_filled. */
2576	if (io->scsiio.kern_sg_entries > 0) {
2577		ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
2578	} else {
2579		ctl_sglist = &ctl_sg_entry;
2580		ctl_sglist->addr = io->scsiio.kern_data_ptr;
2581		ctl_sglist->len = io->scsiio.kern_data_len;
2582	}
2583	cdw->cdw_sg_index = 0;
2584	cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr;
2585	cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len;
2586	r2t_off = io->scsiio.ext_data_filled;
2587	while (r2t_off > 0) {
2588		if (r2t_off >= cdw->cdw_sg_len) {
2589			r2t_off -= cdw->cdw_sg_len;
2590			cdw->cdw_sg_index++;
2591			cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr;
2592			cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len;
2593			continue;
2594		}
2595		cdw->cdw_sg_addr += r2t_off;
2596		cdw->cdw_sg_len -= r2t_off;
2597		r2t_off = 0;
2598	}
2599
2600	if (cs->cs_immediate_data &&
2601	    io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled <
2602	    icl_pdu_data_segment_length(request)) {
2603		done = cfiscsi_handle_data_segment(request, cdw);
2604		if (done) {
2605			uma_zfree(cfiscsi_data_wait_zone, cdw);
2606			io->scsiio.be_move_done(io);
2607			return;
2608		}
2609	}
2610
2611	r2t_off = io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled;
2612	r2t_len = MIN(io->scsiio.kern_data_len - io->scsiio.ext_data_filled,
2613	    cs->cs_max_burst_length);
2614	cdw->cdw_r2t_end = io->scsiio.ext_data_filled + r2t_len;
2615
2616	CFISCSI_SESSION_LOCK(cs);
2617	TAILQ_INSERT_TAIL(&cs->cs_waiting_for_data_out, cdw, cdw_next);
2618	CFISCSI_SESSION_UNLOCK(cs);
2619
2620	/*
2621	 * XXX: We should limit the number of outstanding R2T PDUs
2622	 * 	per task to MaxOutstandingR2T.
2623	 */
2624	response = cfiscsi_pdu_new_response(request, M_NOWAIT);
2625	if (response == NULL) {
2626		CFISCSI_SESSION_WARN(cs, "failed to "
2627		    "allocate memory; dropping connection");
2628		ctl_set_busy(&io->scsiio);
2629		io->scsiio.be_move_done(io);
2630		cfiscsi_session_terminate(cs);
2631		return;
2632	}
2633	bhsr2t = (struct iscsi_bhs_r2t *)response->ip_bhs;
2634	bhsr2t->bhsr2t_opcode = ISCSI_BHS_OPCODE_R2T;
2635	bhsr2t->bhsr2t_flags = 0x80;
2636	bhsr2t->bhsr2t_lun = bhssc->bhssc_lun;
2637	bhsr2t->bhsr2t_initiator_task_tag = bhssc->bhssc_initiator_task_tag;
2638	bhsr2t->bhsr2t_target_transfer_tag = target_transfer_tag;
2639	/*
2640	 * XXX: Here we assume that cfiscsi_datamove() won't ever
2641	 *	be running concurrently on several CPUs for a given
2642	 *	command.
2643	 */
2644	bhsr2t->bhsr2t_r2tsn = htonl(PDU_R2TSN(request));
2645	PDU_R2TSN(request)++;
2646	/*
2647	 * This is the offset within the current SCSI command;
2648	 * i.e. for the first call of datamove(), it will be 0,
2649	 * and for subsequent ones it will be the sum of lengths
2650	 * of previous ones.
2651	 *
2652	 * The ext_data_filled is to account for unsolicited
2653	 * (immediate) data that might have already arrived.
2654	 */
2655	bhsr2t->bhsr2t_buffer_offset = htonl(r2t_off);
2656	/*
2657	 * This is the total length (sum of S/G lengths) this call
2658	 * to cfiscsi_datamove() is supposed to handle, limited by
2659	 * MaxBurstLength.
2660	 */
2661	bhsr2t->bhsr2t_desired_data_transfer_length = htonl(r2t_len);
2662	cfiscsi_pdu_queue(response);
2663}
2664
2665static void
2666cfiscsi_datamove(union ctl_io *io)
2667{
2668
2669	if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN)
2670		cfiscsi_datamove_in(io);
2671	else {
2672		/* We hadn't received anything during this datamove yet. */
2673		io->scsiio.ext_data_filled = 0;
2674		cfiscsi_datamove_out(io);
2675	}
2676}
2677
2678static void
2679cfiscsi_scsi_command_done(union ctl_io *io)
2680{
2681	struct icl_pdu *request, *response;
2682	struct iscsi_bhs_scsi_command *bhssc;
2683	struct iscsi_bhs_scsi_response *bhssr;
2684#ifdef DIAGNOSTIC
2685	struct cfiscsi_data_wait *cdw;
2686#endif
2687	struct cfiscsi_session *cs;
2688	uint16_t sense_length;
2689
2690	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2691	cs = PDU_SESSION(request);
2692	bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs;
2693	KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
2694	    ISCSI_BHS_OPCODE_SCSI_COMMAND,
2695	    ("replying to wrong opcode 0x%x", bhssc->bhssc_opcode));
2696
2697	//CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x",
2698	//    bhssc->bhssc_initiator_task_tag);
2699
2700#ifdef DIAGNOSTIC
2701	CFISCSI_SESSION_LOCK(cs);
2702	TAILQ_FOREACH(cdw, &cs->cs_waiting_for_data_out, cdw_next)
2703		KASSERT(bhssc->bhssc_initiator_task_tag !=
2704		    cdw->cdw_initiator_task_tag, ("dangling cdw"));
2705	CFISCSI_SESSION_UNLOCK(cs);
2706#endif
2707
2708	/*
2709	 * Do not return status for aborted commands.
2710	 * There are exceptions, but none supported by CTL yet.
2711	 */
2712	if (((io->io_hdr.flags & CTL_FLAG_ABORT) &&
2713	     (io->io_hdr.flags & CTL_FLAG_ABORT_STATUS) == 0) ||
2714	    (io->io_hdr.flags & CTL_FLAG_STATUS_SENT)) {
2715		ctl_free_io(io);
2716		icl_pdu_free(request);
2717		return;
2718	}
2719
2720	response = cfiscsi_pdu_new_response(request, M_WAITOK);
2721	bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs;
2722	bhssr->bhssr_opcode = ISCSI_BHS_OPCODE_SCSI_RESPONSE;
2723	bhssr->bhssr_flags = 0x80;
2724	/*
2725	 * XXX: We don't deal with bidirectional under/overflows;
2726	 *	does anything actually support those?
2727	 */
2728	if (PDU_TOTAL_TRANSFER_LEN(request) <
2729	    ntohl(bhssc->bhssc_expected_data_transfer_length)) {
2730		bhssr->bhssr_flags |= BHSSR_FLAGS_RESIDUAL_UNDERFLOW;
2731		bhssr->bhssr_residual_count =
2732		    htonl(ntohl(bhssc->bhssc_expected_data_transfer_length) -
2733		    PDU_TOTAL_TRANSFER_LEN(request));
2734		//CFISCSI_SESSION_DEBUG(cs, "underflow; residual count %d",
2735		//    ntohl(bhssr->bhssr_residual_count));
2736	} else if (PDU_TOTAL_TRANSFER_LEN(request) >
2737	    ntohl(bhssc->bhssc_expected_data_transfer_length)) {
2738		bhssr->bhssr_flags |= BHSSR_FLAGS_RESIDUAL_OVERFLOW;
2739		bhssr->bhssr_residual_count =
2740		    htonl(PDU_TOTAL_TRANSFER_LEN(request) -
2741		    ntohl(bhssc->bhssc_expected_data_transfer_length));
2742		//CFISCSI_SESSION_DEBUG(cs, "overflow; residual count %d",
2743		//    ntohl(bhssr->bhssr_residual_count));
2744	}
2745	bhssr->bhssr_response = BHSSR_RESPONSE_COMMAND_COMPLETED;
2746	bhssr->bhssr_status = io->scsiio.scsi_status;
2747	bhssr->bhssr_initiator_task_tag = bhssc->bhssc_initiator_task_tag;
2748	bhssr->bhssr_expdatasn = htonl(PDU_EXPDATASN(request));
2749
2750	if (io->scsiio.sense_len > 0) {
2751#if 0
2752		CFISCSI_SESSION_DEBUG(cs, "returning %d bytes of sense data",
2753		    io->scsiio.sense_len);
2754#endif
2755		sense_length = htons(io->scsiio.sense_len);
2756		icl_pdu_append_data(response,
2757		    &sense_length, sizeof(sense_length), M_WAITOK);
2758		icl_pdu_append_data(response,
2759		    &io->scsiio.sense_data, io->scsiio.sense_len, M_WAITOK);
2760	}
2761
2762	ctl_free_io(io);
2763	icl_pdu_free(request);
2764	cfiscsi_pdu_queue(response);
2765}
2766
2767static void
2768cfiscsi_task_management_done(union ctl_io *io)
2769{
2770	struct icl_pdu *request, *response;
2771	struct iscsi_bhs_task_management_request *bhstmr;
2772	struct iscsi_bhs_task_management_response *bhstmr2;
2773	struct cfiscsi_data_wait *cdw, *tmpcdw;
2774	struct cfiscsi_session *cs;
2775
2776	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2777	cs = PDU_SESSION(request);
2778	bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs;
2779	KASSERT((bhstmr->bhstmr_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
2780	    ISCSI_BHS_OPCODE_TASK_REQUEST,
2781	    ("replying to wrong opcode 0x%x", bhstmr->bhstmr_opcode));
2782
2783#if 0
2784	CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x; referenced task tag 0x%x",
2785	    bhstmr->bhstmr_initiator_task_tag,
2786	    bhstmr->bhstmr_referenced_task_tag);
2787#endif
2788
2789	if ((bhstmr->bhstmr_function & ~0x80) ==
2790	    BHSTMR_FUNCTION_ABORT_TASK) {
2791		/*
2792		 * Make sure we no longer wait for Data-Out for this command.
2793		 */
2794		CFISCSI_SESSION_LOCK(cs);
2795		TAILQ_FOREACH_SAFE(cdw,
2796		    &cs->cs_waiting_for_data_out, cdw_next, tmpcdw) {
2797			if (bhstmr->bhstmr_referenced_task_tag !=
2798			    cdw->cdw_initiator_task_tag)
2799				continue;
2800
2801#if 0
2802			CFISCSI_SESSION_DEBUG(cs, "removing csw for initiator task "
2803			    "tag 0x%x", bhstmr->bhstmr_initiator_task_tag);
2804#endif
2805			TAILQ_REMOVE(&cs->cs_waiting_for_data_out,
2806			    cdw, cdw_next);
2807			cdw->cdw_ctl_io->scsiio.be_move_done(cdw->cdw_ctl_io);
2808			uma_zfree(cfiscsi_data_wait_zone, cdw);
2809		}
2810		CFISCSI_SESSION_UNLOCK(cs);
2811	}
2812
2813	response = cfiscsi_pdu_new_response(request, M_WAITOK);
2814	bhstmr2 = (struct iscsi_bhs_task_management_response *)
2815	    response->ip_bhs;
2816	bhstmr2->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_RESPONSE;
2817	bhstmr2->bhstmr_flags = 0x80;
2818	if (io->io_hdr.status == CTL_SUCCESS) {
2819		bhstmr2->bhstmr_response = BHSTMR_RESPONSE_FUNCTION_COMPLETE;
2820	} else {
2821		/*
2822		 * XXX: How to figure out what exactly went wrong?  iSCSI spec
2823		 * 	expects us to provide detailed error, e.g. "Task does
2824		 * 	not exist" or "LUN does not exist".
2825		 */
2826		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED");
2827		bhstmr2->bhstmr_response =
2828		    BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED;
2829	}
2830	bhstmr2->bhstmr_initiator_task_tag = bhstmr->bhstmr_initiator_task_tag;
2831
2832	ctl_free_io(io);
2833	icl_pdu_free(request);
2834	cfiscsi_pdu_queue(response);
2835}
2836
2837static void
2838cfiscsi_done(union ctl_io *io)
2839{
2840	struct icl_pdu *request;
2841	struct cfiscsi_session *cs;
2842
2843	KASSERT(((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE),
2844		("invalid CTL status %#x", io->io_hdr.status));
2845
2846	if (io->io_hdr.io_type == CTL_IO_TASK &&
2847	    io->taskio.task_action == CTL_TASK_I_T_NEXUS_RESET) {
2848		/*
2849		 * Implicit task termination has just completed; nothing to do.
2850		 */
2851		cs = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2852		cs->cs_tasks_aborted = true;
2853		refcount_release(&cs->cs_outstanding_ctl_pdus);
2854		wakeup(__DEVOLATILE(void *, &cs->cs_outstanding_ctl_pdus));
2855		ctl_free_io(io);
2856		return;
2857	}
2858
2859	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2860	cs = PDU_SESSION(request);
2861	refcount_release(&cs->cs_outstanding_ctl_pdus);
2862
2863	switch (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) {
2864	case ISCSI_BHS_OPCODE_SCSI_COMMAND:
2865		cfiscsi_scsi_command_done(io);
2866		break;
2867	case ISCSI_BHS_OPCODE_TASK_REQUEST:
2868		cfiscsi_task_management_done(io);
2869		break;
2870	default:
2871		panic("cfiscsi_done called with wrong opcode 0x%x",
2872		    request->ip_bhs->bhs_opcode);
2873	}
2874}
2875