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