ctl_frontend_iscsi.c revision 267961
11558Srgrimes/*-
21558Srgrimes * Copyright (c) 2012 The FreeBSD Foundation
31558Srgrimes * All rights reserved.
41558Srgrimes *
51558Srgrimes * This software was developed by Edward Tomasz Napierala under sponsorship
61558Srgrimes * from the FreeBSD Foundation.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes *
291558Srgrimes * $FreeBSD: head/sys/cam/ctl/ctl_frontend_iscsi.c 267961 2014-06-27 16:33:43Z hselasky $
301558Srgrimes */
311558Srgrimes
321558Srgrimes/*
331558Srgrimes * CTL frontend for the iSCSI protocol.
341558Srgrimes */
351558Srgrimes
361558Srgrimes#include <sys/cdefs.h>
371558Srgrimes__FBSDID("$FreeBSD: head/sys/cam/ctl/ctl_frontend_iscsi.c 267961 2014-06-27 16:33:43Z hselasky $");
3837427Scharnier
391558Srgrimes#include <sys/param.h>
401558Srgrimes#include <sys/capsicum.h>
411558Srgrimes#include <sys/condvar.h>
421558Srgrimes#include <sys/file.h>
431558Srgrimes#include <sys/kernel.h>
4437427Scharnier#include <sys/kthread.h>
4523680Speter#include <sys/lock.h>
4637427Scharnier#include <sys/malloc.h>
4715770Swollman#include <sys/module.h>
4850476Speter#include <sys/mutex.h>
491558Srgrimes#include <sys/queue.h>
501558Srgrimes#include <sys/sbuf.h>
511558Srgrimes#include <sys/sysctl.h>
521558Srgrimes#include <sys/systm.h>
531558Srgrimes#include <sys/uio.h>
541558Srgrimes#include <sys/unistd.h>
551558Srgrimes#include <vm/uma.h>
561558Srgrimes
571558Srgrimes#include <cam/scsi/scsi_all.h>
581558Srgrimes#include <cam/scsi/scsi_da.h>
591558Srgrimes#include <cam/ctl/ctl_io.h>
601558Srgrimes#include <cam/ctl/ctl.h>
611558Srgrimes#include <cam/ctl/ctl_backend.h>
621558Srgrimes#include <cam/ctl/ctl_error.h>
631558Srgrimes#include <cam/ctl/ctl_frontend.h>
649336Sdfr#include <cam/ctl/ctl_frontend_internal.h>
6523680Speter#include <cam/ctl/ctl_debug.h>
661558Srgrimes#include <cam/ctl/ctl_ha.h>
671558Srgrimes#include <cam/ctl/ctl_ioctl.h>
681558Srgrimes#include <cam/ctl/ctl_private.h>
691558Srgrimes
709336Sdfr#include "../../dev/iscsi/icl.h"
711558Srgrimes#include "../../dev/iscsi/iscsi_proto.h"
721558Srgrimes#include "ctl_frontend_iscsi.h"
731558Srgrimes
741558Srgrimes#ifdef ICL_KERNEL_PROXY
751558Srgrimes#include <sys/socketvar.h>
761558Srgrimes#endif
771558Srgrimes
781558Srgrimes#ifdef ICL_KERNEL_PROXY
791558SrgrimesFEATURE(cfiscsi_kernel_proxy, "iSCSI target built with ICL_KERNEL_PROXY");
801558Srgrimes#endif
811558Srgrimes
821558Srgrimesstatic MALLOC_DEFINE(M_CFISCSI, "cfiscsi", "Memory used for CTL iSCSI frontend");
8315770Swollmanstatic uma_zone_t cfiscsi_data_wait_zone;
841558Srgrimes
851558SrgrimesSYSCTL_NODE(_kern_cam_ctl, OID_AUTO, iscsi, CTLFLAG_RD, 0,
861558Srgrimes    "CAM Target Layer iSCSI Frontend");
871558Srgrimesstatic int debug = 3;
884065SwollmanSYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, debug, CTLFLAG_RWTUN,
894065Swollman    &debug, 1, "Enable debug messages");
904065Swollmanstatic int ping_timeout = 5;
914065SwollmanSYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RWTUN,
924065Swollman    &ping_timeout, 5, "Interval between ping (NOP-Out) requests, in seconds");
939336Sdfrstatic int login_timeout = 60;
949336SdfrSYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, login_timeout, CTLFLAG_RWTUN,
959336Sdfr    &login_timeout, 60, "Time to wait for ctld(8) to finish Login Phase, in seconds");
964065Swollmanstatic int maxcmdsn_delta = 256;
974065SwollmanSYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, maxcmdsn_delta, CTLFLAG_RWTUN,
984065Swollman    &maxcmdsn_delta, 256, "Number of commands the initiator can send "
994065Swollman    "without confirmation");
1004065Swollman
1019230Skarl#define	CFISCSI_DEBUG(X, ...)						\
10225004Sdfr	do {								\
10336178Speter		if (debug > 1) {					\
10436178Speter			printf("%s: " X "\n",				\
10536178Speter			    __func__, ## __VA_ARGS__);			\
10636178Speter		}							\
1074065Swollman	} while (0)
1081558Srgrimes
1091558Srgrimes#define	CFISCSI_WARN(X, ...)						\
1101558Srgrimes	do {								\
1111558Srgrimes		if (debug > 0) {					\
11226417Sdfr			printf("WARNING: %s: " X "\n",			\
1134065Swollman			    __func__, ## __VA_ARGS__);			\
1144065Swollman		}							\
1154065Swollman	} while (0)
1164065Swollman
1179336Sdfr#define	CFISCSI_SESSION_DEBUG(S, X, ...)				\
1184065Swollman	do {								\
1194065Swollman		if (debug > 1) {					\
1209336Sdfr			printf("%s: %s (%s): " X "\n",			\
1219336Sdfr			    __func__, S->cs_initiator_addr,		\
1229336Sdfr			    S->cs_initiator_name, ## __VA_ARGS__);	\
1234065Swollman		}							\
1244065Swollman	} while (0)
1254065Swollman
1264065Swollman#define	CFISCSI_SESSION_WARN(S, X, ...)					\
1274065Swollman	do  {								\
1284065Swollman		if (debug > 0) {					\
1294065Swollman			printf("WARNING: %s (%s): " X "\n",		\
1309230Skarl			    S->cs_initiator_addr,			\
13125004Sdfr			    S->cs_initiator_name, ## __VA_ARGS__);	\
13236178Speter		}							\
13336178Speter	} while (0)
13436178Speter
13536178Speter#define CFISCSI_SESSION_LOCK(X)		mtx_lock(&X->cs_lock)
1361558Srgrimes#define CFISCSI_SESSION_UNLOCK(X)	mtx_unlock(&X->cs_lock)
1371558Srgrimes#define CFISCSI_SESSION_LOCK_ASSERT(X)	mtx_assert(&X->cs_lock, MA_OWNED)
1381558Srgrimes
1391558Srgrimes#define	CONN_SESSION(X)			((struct cfiscsi_session *)(X)->ic_prv0)
14023680Speter#define	PDU_SESSION(X)			CONN_SESSION((X)->ip_conn)
1411558Srgrimes#define	PDU_EXPDATASN(X)		(X)->ip_prv0
1421558Srgrimes#define	PDU_TOTAL_TRANSFER_LEN(X)	(X)->ip_prv1
1431558Srgrimes#define	PDU_R2TSN(X)			(X)->ip_prv2
1441558Srgrimes
1459336Sdfrint		cfiscsi_init(void);
1461558Srgrimesstatic void	cfiscsi_online(void *arg);
14724495Sguidostatic void	cfiscsi_offline(void *arg);
1481558Srgrimesstatic int	cfiscsi_targ_enable(void *arg, struct ctl_id targ_id);
1491558Srgrimesstatic int	cfiscsi_targ_disable(void *arg, struct ctl_id targ_id);
1509336Sdfrstatic int	cfiscsi_lun_enable(void *arg,
1519336Sdfr		    struct ctl_id target_id, int lun_id);
1521558Srgrimesstatic int	cfiscsi_lun_disable(void *arg,
1531558Srgrimes		    struct ctl_id target_id, int lun_id);
1541558Srgrimesstatic int	cfiscsi_ioctl(struct cdev *dev,
1551558Srgrimes		    u_long cmd, caddr_t addr, int flag, struct thread *td);
1561558Srgrimesstatic int	cfiscsi_devid(struct ctl_scsiio *ctsio, int alloc_len);
1571558Srgrimesstatic void	cfiscsi_datamove(union ctl_io *io);
15836178Speterstatic void	cfiscsi_done(union ctl_io *io);
15936178Speterstatic uint32_t	cfiscsi_map_lun(void *arg, uint32_t lun);
16036178Speterstatic bool	cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request);
16136178Speterstatic void	cfiscsi_pdu_handle_nop_out(struct icl_pdu *request);
16236178Speterstatic void	cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request);
1631558Srgrimesstatic void	cfiscsi_pdu_handle_task_request(struct icl_pdu *request);
1641558Srgrimesstatic void	cfiscsi_pdu_handle_data_out(struct icl_pdu *request);
1651558Srgrimesstatic void	cfiscsi_pdu_handle_logout_request(struct icl_pdu *request);
1669336Sdfrstatic void	cfiscsi_session_terminate(struct cfiscsi_session *cs);
1679336Sdfrstatic struct cfiscsi_target	*cfiscsi_target_find(struct cfiscsi_softc
1689336Sdfr		    *softc, const char *name);
1699336Sdfrstatic void	cfiscsi_target_release(struct cfiscsi_target *ct);
1709336Sdfrstatic void	cfiscsi_session_delete(struct cfiscsi_session *cs);
1711558Srgrimes
1721558Srgrimesstatic struct cfiscsi_softc cfiscsi_softc;
1731558Srgrimesextern struct ctl_softc *control_softc;
1741558Srgrimes
1751558Srgrimesstatic int cfiscsi_module_event_handler(module_t, int /*modeventtype_t*/, void *);
1761558Srgrimes
1779336Sdfrstatic moduledata_t cfiscsi_moduledata = {
1789336Sdfr	"ctlcfiscsi",
1799230Skarl	cfiscsi_module_event_handler,
18025004Sdfr	NULL
18125004Sdfr};
18225004Sdfr
18325004SdfrDECLARE_MODULE(ctlcfiscsi, cfiscsi_moduledata, SI_SUB_CONFIGURE, SI_ORDER_FOURTH);
18425004SdfrMODULE_VERSION(ctlcfiscsi, 1);
1851558SrgrimesMODULE_DEPEND(ctlcfiscsi, ctl, 1, 1, 1);
1869336SdfrMODULE_DEPEND(ctlcfiscsi, icl, 1, 1, 1);
1871558Srgrimes
1881558Srgrimesstatic struct icl_pdu *
1899336Sdfrcfiscsi_pdu_new_response(struct icl_pdu *request, int flags)
1909336Sdfr{
1919336Sdfr
1929336Sdfr	return (icl_pdu_new_bhs(request->ip_conn, flags));
1939336Sdfr}
1949336Sdfr
1959336Sdfrstatic bool
1969336Sdfrcfiscsi_pdu_update_cmdsn(const struct icl_pdu *request)
1979336Sdfr{
1989336Sdfr	const struct iscsi_bhs_scsi_command *bhssc;
1991558Srgrimes	struct cfiscsi_session *cs;
2001558Srgrimes	uint32_t cmdsn, expstatsn;
2011558Srgrimes
2021558Srgrimes	cs = PDU_SESSION(request);
2031558Srgrimes
2041558Srgrimes	/*
2051558Srgrimes	 * Every incoming PDU - not just NOP-Out - resets the ping timer.
20618286Sbde	 * The purpose of the timeout is to reset the connection when it stalls;
2071558Srgrimes	 * we don't want this to happen when NOP-In or NOP-Out ends up delayed
2081558Srgrimes	 * in some queue.
2091558Srgrimes	 *
21024546Sdfr	 * XXX: Locking?
21124546Sdfr	 */
21237427Scharnier	cs->cs_timeout = 0;
21324546Sdfr
21424546Sdfr	/*
21524546Sdfr	 * Data-Out PDUs don't contain CmdSN.
21624546Sdfr	 */
21724546Sdfr	if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
21824546Sdfr	    ISCSI_BHS_OPCODE_SCSI_DATA_OUT)
21924546Sdfr		return (false);
22024546Sdfr
22124546Sdfr	/*
22224546Sdfr	 * We're only using fields common for all the request
22324546Sdfr	 * (initiator -> target) PDUs.
22424546Sdfr	 */
22524546Sdfr	bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs;
22624546Sdfr	cmdsn = ntohl(bhssc->bhssc_cmdsn);
22724546Sdfr	expstatsn = ntohl(bhssc->bhssc_expstatsn);
22824546Sdfr
22924546Sdfr	CFISCSI_SESSION_LOCK(cs);
23024546Sdfr#if 0
23124546Sdfr	if (expstatsn != cs->cs_statsn) {
23224546Sdfr		CFISCSI_SESSION_DEBUG(cs, "received PDU with ExpStatSN %d, "
23324546Sdfr		    "while current StatSN is %d", expstatsn,
23424546Sdfr		    cs->cs_statsn);
23524546Sdfr	}
23624546Sdfr#endif
23724546Sdfr
23824546Sdfr	/*
23924546Sdfr	 * The target MUST silently ignore any non-immediate command outside
24024546Sdfr	 * of this range.
24124546Sdfr	 */
24224546Sdfr	if (cmdsn < cs->cs_cmdsn || cmdsn > cs->cs_cmdsn + maxcmdsn_delta) {
24324546Sdfr		CFISCSI_SESSION_UNLOCK(cs);
24424546Sdfr		CFISCSI_SESSION_WARN(cs, "received PDU with CmdSN %d, "
24524546Sdfr		    "while expected CmdSN was %d", cmdsn, cs->cs_cmdsn);
24624546Sdfr		return (true);
24724546Sdfr	}
2481558Srgrimes
2491558Srgrimes	if ((request->ip_bhs->bhs_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0)
2501558Srgrimes		cs->cs_cmdsn++;
2511558Srgrimes
2521558Srgrimes	CFISCSI_SESSION_UNLOCK(cs);
2531558Srgrimes
2541558Srgrimes	return (false);
2551558Srgrimes}
2561558Srgrimes
2574065Swollmanstatic void
2581558Srgrimescfiscsi_pdu_handle(struct icl_pdu *request)
25952055Sphk{
26023680Speter	struct cfiscsi_session *cs;
26123680Speter	bool ignore;
2629336Sdfr
2631558Srgrimes	cs = PDU_SESSION(request);
2641558Srgrimes
2651558Srgrimes	ignore = cfiscsi_pdu_update_cmdsn(request);
2661558Srgrimes	if (ignore) {
2679336Sdfr		icl_pdu_free(request);
2689336Sdfr		return;
2699336Sdfr	}
2709336Sdfr
2719336Sdfr	/*
2729336Sdfr	 * Handle the PDU; this includes e.g. receiving the remaining
2731558Srgrimes	 * part of PDU and submitting the SCSI command to CTL
2741558Srgrimes	 * or queueing a reply.  The handling routine is responsible
2751558Srgrimes	 * for freeing the PDU when it's no longer needed.
2764065Swollman	 */
2771558Srgrimes	switch (request->ip_bhs->bhs_opcode &
2781558Srgrimes	    ~ISCSI_BHS_OPCODE_IMMEDIATE) {
2791558Srgrimes	case ISCSI_BHS_OPCODE_NOP_OUT:
28030580Sjoerg		cfiscsi_pdu_handle_nop_out(request);
2811558Srgrimes		break;
28225004Sdfr	case ISCSI_BHS_OPCODE_SCSI_COMMAND:
28325004Sdfr		cfiscsi_pdu_handle_scsi_command(request);
28425004Sdfr		break;
2859336Sdfr	case ISCSI_BHS_OPCODE_TASK_REQUEST:
28625004Sdfr		cfiscsi_pdu_handle_task_request(request);
2879336Sdfr		break;
2881558Srgrimes	case ISCSI_BHS_OPCODE_SCSI_DATA_OUT:
2891558Srgrimes		cfiscsi_pdu_handle_data_out(request);
2901558Srgrimes		break;
2911558Srgrimes	case ISCSI_BHS_OPCODE_LOGOUT_REQUEST:
2921558Srgrimes		cfiscsi_pdu_handle_logout_request(request);
2931558Srgrimes		break;
2941558Srgrimes	default:
2951558Srgrimes		CFISCSI_SESSION_WARN(cs, "received PDU with unsupported "
2961558Srgrimes		    "opcode 0x%x; dropping connection",
2971558Srgrimes		    request->ip_bhs->bhs_opcode);
2981558Srgrimes		icl_pdu_free(request);
2991558Srgrimes		cfiscsi_session_terminate(cs);
3001558Srgrimes	}
3011558Srgrimes
3021558Srgrimes}
3031558Srgrimes
3041558Srgrimesstatic void
3051558Srgrimescfiscsi_receive_callback(struct icl_pdu *request)
3061558Srgrimes{
3071558Srgrimes	struct cfiscsi_session *cs;
3081558Srgrimes
3091558Srgrimes	cs = PDU_SESSION(request);
3101558Srgrimes
3111558Srgrimes#ifdef ICL_KERNEL_PROXY
3121558Srgrimes	if (cs->cs_waiting_for_ctld || cs->cs_login_phase) {
3131558Srgrimes		if (cs->cs_login_pdu == NULL)
3141558Srgrimes			cs->cs_login_pdu = request;
3159336Sdfr		else
3161558Srgrimes			icl_pdu_free(request);
3179336Sdfr		cv_signal(&cs->cs_login_cv);
3181558Srgrimes		return;
3191558Srgrimes	}
3201558Srgrimes#endif
3219336Sdfr
3229336Sdfr	cfiscsi_pdu_handle(request);
3239336Sdfr}
3249336Sdfr
3259336Sdfrstatic void
3269336Sdfrcfiscsi_error_callback(struct icl_conn *ic)
3279336Sdfr{
3281558Srgrimes	struct cfiscsi_session *cs;
3291558Srgrimes
3301558Srgrimes	cs = CONN_SESSION(ic);
3319336Sdfr
3321558Srgrimes	CFISCSI_SESSION_WARN(cs, "connection error; dropping connection");
3331558Srgrimes	cfiscsi_session_terminate(cs);
3341558Srgrimes}
3351558Srgrimes
3361558Srgrimesstatic int
3371558Srgrimescfiscsi_pdu_prepare(struct icl_pdu *response)
3381558Srgrimes{
3391558Srgrimes	struct cfiscsi_session *cs;
3401558Srgrimes	struct iscsi_bhs_scsi_response *bhssr;
3411558Srgrimes	bool advance_statsn = true;
3421558Srgrimes
3431558Srgrimes	cs = PDU_SESSION(response);
3449336Sdfr
3451558Srgrimes	CFISCSI_SESSION_LOCK_ASSERT(cs);
3469336Sdfr
3471558Srgrimes	/*
3481558Srgrimes	 * We're only using fields common for all the response
3491558Srgrimes	 * (target -> initiator) PDUs.
3501558Srgrimes	 */
3511558Srgrimes	bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs;
35230580Sjoerg
35330580Sjoerg	/*
35430580Sjoerg	 * 10.8.3: "The StatSN for this connection is not advanced
3551558Srgrimes	 * after this PDU is sent."
35624546Sdfr	 */
35724546Sdfr	if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_R2T)
35825004Sdfr		advance_statsn = false;
35925004Sdfr
36025004Sdfr	/*
36125004Sdfr	 * 10.19.2: "However, when the Initiator Task Tag is set to 0xffffffff,
3624065Swollman	 * StatSN for the connection is not advanced after this PDU is sent."
36324546Sdfr	 */
36424546Sdfr	if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_NOP_IN &&
36524546Sdfr	    bhssr->bhssr_initiator_task_tag == 0xffffffff)
36624546Sdfr		advance_statsn = false;
36724546Sdfr
3684065Swollman	/*
3694065Swollman	 * See the comment below - StatSN is not meaningful and must
3709336Sdfr	 * not be advanced.
3719336Sdfr	 */
3724065Swollman	if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_SCSI_DATA_IN)
3734065Swollman		advance_statsn = false;
3744065Swollman
3754065Swollman	/*
3769336Sdfr	 * 10.7.3: "The fields StatSN, Status, and Residual Count
3774065Swollman	 * only have meaningful content if the S bit is set to 1."
3789336Sdfr	 */
3799336Sdfr	if (bhssr->bhssr_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_IN)
3809230Skarl		bhssr->bhssr_statsn = htonl(cs->cs_statsn);
3819230Skarl	bhssr->bhssr_expcmdsn = htonl(cs->cs_cmdsn);
38225004Sdfr	bhssr->bhssr_maxcmdsn = htonl(cs->cs_cmdsn + maxcmdsn_delta);
38325004Sdfr
38425004Sdfr	if (advance_statsn)
38525004Sdfr		cs->cs_statsn++;
38625004Sdfr
38736178Speter	return (0);
38836178Speter}
38936178Speter
39036178Speterstatic void
39136178Spetercfiscsi_pdu_queue(struct icl_pdu *response)
39236178Speter{
39336178Speter	struct cfiscsi_session *cs;
39436178Speter
39536178Speter	cs = PDU_SESSION(response);
39636178Speter
39736178Speter	CFISCSI_SESSION_LOCK(cs);
39836178Speter	cfiscsi_pdu_prepare(response);
3991558Srgrimes	icl_pdu_queue(response);
4001558Srgrimes	CFISCSI_SESSION_UNLOCK(cs);
40130580Sjoerg}
4021558Srgrimes
4031558Srgrimesstatic uint32_t
4041558Srgrimescfiscsi_decode_lun(uint64_t encoded)
4051558Srgrimes{
4061558Srgrimes	uint8_t lun[8];
4071558Srgrimes	uint32_t result;
4081558Srgrimes
40925004Sdfr	/*
41025004Sdfr	 * The LUN field in iSCSI PDUs may look like an ordinary 64 bit number,
4111558Srgrimes	 * but is in fact an evil, multidimensional structure defined
4121558Srgrimes	 * in SCSI Architecture Model 5 (SAM-5), section 4.6.
4131558Srgrimes	 */
4141558Srgrimes	memcpy(lun, &encoded, sizeof(lun));
4151558Srgrimes	switch (lun[0] & 0xC0) {
4161558Srgrimes	case 0x00:
4171558Srgrimes		if ((lun[0] & 0x3f) != 0 || lun[2] != 0 || lun[3] != 0 ||
4181558Srgrimes		    lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0) {
4191558Srgrimes			CFISCSI_WARN("malformed LUN "
4201558Srgrimes			    "(peripheral device addressing method): 0x%jx",
4211558Srgrimes			    (uintmax_t)encoded);
4221558Srgrimes			result = 0xffffffff;
4231558Srgrimes			break;
4241558Srgrimes		}
4251558Srgrimes		result = lun[1];
4261558Srgrimes		break;
4271558Srgrimes	case 0x40:
4281558Srgrimes		if (lun[2] != 0 || lun[3] != 0 || lun[4] != 0 || lun[5] != 0 ||
4291558Srgrimes		    lun[6] != 0 || lun[7] != 0) {
4309336Sdfr			CFISCSI_WARN("malformed LUN "
4311558Srgrimes			    "(flat address space addressing method): 0x%jx",
4321558Srgrimes			    (uintmax_t)encoded);
4331558Srgrimes			result = 0xffffffff;
4341558Srgrimes			break;
4351558Srgrimes		}
4361558Srgrimes		result = ((lun[0] & 0x3f) << 8) + lun[1];
4371558Srgrimes		break;
4381558Srgrimes	case 0xC0:
4391558Srgrimes		if (lun[0] != 0xD2 || lun[4] != 0 || lun[5] != 0 ||
4401558Srgrimes		    lun[6] != 0 || lun[7] != 0) {
4411558Srgrimes			CFISCSI_WARN("malformed LUN (extended flat "
4421558Srgrimes			    "address space addressing method): 0x%jx",
4431558Srgrimes			    (uintmax_t)encoded);
4441558Srgrimes			result = 0xffffffff;
4451558Srgrimes			break;
4461558Srgrimes		}
4471558Srgrimes		result = (lun[1] << 16) + (lun[2] << 8) + lun[3];
4481558Srgrimes	default:
4491558Srgrimes		CFISCSI_WARN("unsupported LUN format 0x%jx",
4501558Srgrimes		    (uintmax_t)encoded);
4511558Srgrimes		result = 0xffffffff;
4521558Srgrimes		break;
4539336Sdfr	}
4549336Sdfr
4559336Sdfr	return (result);
4561558Srgrimes}
4571558Srgrimes
4581558Srgrimesstatic void
4591558Srgrimescfiscsi_pdu_handle_nop_out(struct icl_pdu *request)
4601558Srgrimes{
4611558Srgrimes	struct cfiscsi_session *cs;
4621558Srgrimes	struct iscsi_bhs_nop_out *bhsno;
46323680Speter	struct iscsi_bhs_nop_in *bhsni;
4645966Sdg	struct icl_pdu *response;
46523680Speter	void *data = NULL;
46623680Speter	size_t datasize;
4671558Srgrimes	int error;
4681558Srgrimes
4691558Srgrimes	cs = PDU_SESSION(request);
4701558Srgrimes	bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs;
4711558Srgrimes
4721558Srgrimes	if (bhsno->bhsno_initiator_task_tag == 0xffffffff) {
4732999Swollman		/*
47452055Sphk		 * Nothing to do, iscsi_pdu_update_statsn() already
47552055Sphk		 * zeroed the timeout.
47652055Sphk		 */
4779336Sdfr		icl_pdu_free(request);
47823680Speter		return;
47923680Speter	}
4802999Swollman
48115770Swollman	datasize = icl_pdu_data_segment_length(request);
48223680Speter	if (datasize > 0) {
48323680Speter		data = malloc(datasize, M_CFISCSI, M_NOWAIT | M_ZERO);
4842999Swollman		if (data == NULL) {
48523680Speter			CFISCSI_SESSION_WARN(cs, "failed to allocate memory; "
48623680Speter			    "dropping connection");
4872999Swollman			icl_pdu_free(request);
48852055Sphk			cfiscsi_session_terminate(cs);
48952055Sphk			return;
4909336Sdfr		}
49152055Sphk		icl_pdu_get_data(request, 0, data, datasize);
49252055Sphk	}
4939336Sdfr
4941558Srgrimes	response = cfiscsi_pdu_new_response(request, M_NOWAIT);
4951558Srgrimes	if (response == NULL) {
49637427Scharnier		CFISCSI_SESSION_WARN(cs, "failed to allocate memory; "
4971558Srgrimes		    "droppping connection");
4981558Srgrimes		free(data, M_CFISCSI);
4991558Srgrimes		icl_pdu_free(request);
5001558Srgrimes		cfiscsi_session_terminate(cs);
5011558Srgrimes		return;
5021558Srgrimes	}
5031558Srgrimes	bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs;
5041558Srgrimes	bhsni->bhsni_opcode = ISCSI_BHS_OPCODE_NOP_IN;
5051558Srgrimes	bhsni->bhsni_flags = 0x80;
5061558Srgrimes	bhsni->bhsni_initiator_task_tag = bhsno->bhsno_initiator_task_tag;
5071558Srgrimes	bhsni->bhsni_target_transfer_tag = 0xffffffff;
5081558Srgrimes	if (datasize > 0) {
50952055Sphk		error = icl_pdu_append_data(response, data, datasize, M_NOWAIT);
5101558Srgrimes		if (error != 0) {
5111558Srgrimes			CFISCSI_SESSION_WARN(cs, "failed to allocate memory; "
5121558Srgrimes			    "dropping connection");
5131558Srgrimes			free(data, M_CFISCSI);
5141558Srgrimes			icl_pdu_free(request);
5151558Srgrimes			icl_pdu_free(response);
5161558Srgrimes			cfiscsi_session_terminate(cs);
5179336Sdfr			return;
5181558Srgrimes		}
5191558Srgrimes		free(data, M_CFISCSI);
5201558Srgrimes	}
5211558Srgrimes
5221558Srgrimes	icl_pdu_free(request);
5231558Srgrimes	cfiscsi_pdu_queue(response);
5249336Sdfr}
52537427Scharnier
5269336Sdfrstatic void
5279336Sdfrcfiscsi_pdu_handle_scsi_command(struct icl_pdu *request)
5289336Sdfr{
5291558Srgrimes	struct iscsi_bhs_scsi_command *bhssc;
5301558Srgrimes	struct cfiscsi_session *cs;
5316043Sdfr	union ctl_io *io;
5326043Sdfr	int error;
5336043Sdfr
5346043Sdfr	cs = PDU_SESSION(request);
5351558Srgrimes	bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs;
5361558Srgrimes	//CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x",
5379336Sdfr	//    bhssc->bhssc_initiator_task_tag);
5389336Sdfr
5399336Sdfr	if (request->ip_data_len > 0 && cs->cs_immediate_data == false) {
5409336Sdfr		CFISCSI_SESSION_WARN(cs, "unsolicited data with "
5419336Sdfr		    "ImmediateData=No; dropping connection");
5429336Sdfr		icl_pdu_free(request);
5439336Sdfr		cfiscsi_session_terminate(cs);
5449336Sdfr		return;
5451558Srgrimes	}
5469336Sdfr	io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref);
5479336Sdfr	if (io == NULL) {
5489336Sdfr		CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io; "
5499336Sdfr		    "dropping connection");
5509336Sdfr		icl_pdu_free(request);
55137427Scharnier		cfiscsi_session_terminate(cs);
5529336Sdfr		return;
5539336Sdfr	}
5549336Sdfr	ctl_zero_io(io);
5559336Sdfr	io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = request;
5569336Sdfr	io->io_hdr.io_type = CTL_IO_SCSI;
5579336Sdfr	io->io_hdr.nexus.initid.id = cs->cs_ctl_initid;
5589336Sdfr	io->io_hdr.nexus.targ_port = cs->cs_target->ct_softc->fe.targ_port;
5599336Sdfr	io->io_hdr.nexus.targ_target.id = 0;
5609336Sdfr	io->io_hdr.nexus.targ_lun = cfiscsi_decode_lun(bhssc->bhssc_lun);
5619336Sdfr	io->io_hdr.nexus.lun_map_fn = cfiscsi_map_lun;
5629336Sdfr	io->io_hdr.nexus.lun_map_arg = cs;
5639336Sdfr	io->scsiio.tag_num = bhssc->bhssc_initiator_task_tag;
56423680Speter	switch ((bhssc->bhssc_flags & BHSSC_FLAGS_ATTR)) {
5659336Sdfr	case BHSSC_FLAGS_ATTR_UNTAGGED:
5669336Sdfr		io->scsiio.tag_type = CTL_TAG_UNTAGGED;
5679336Sdfr		break;
5689336Sdfr	case BHSSC_FLAGS_ATTR_SIMPLE:
5699336Sdfr		io->scsiio.tag_type = CTL_TAG_SIMPLE;
5709336Sdfr		break;
5719336Sdfr	case BHSSC_FLAGS_ATTR_ORDERED:
5729336Sdfr        	io->scsiio.tag_type = CTL_TAG_ORDERED;
5739336Sdfr		break;
5749336Sdfr	case BHSSC_FLAGS_ATTR_HOQ:
5759336Sdfr        	io->scsiio.tag_type = CTL_TAG_HEAD_OF_QUEUE;
5769336Sdfr		break;
5779336Sdfr	case BHSSC_FLAGS_ATTR_ACA:
5789336Sdfr		io->scsiio.tag_type = CTL_TAG_ACA;
5799336Sdfr		break;
5809336Sdfr	default:
5819336Sdfr		io->scsiio.tag_type = CTL_TAG_UNTAGGED;
5829336Sdfr		CFISCSI_SESSION_WARN(cs, "unhandled tag type %d",
5839336Sdfr		    bhssc->bhssc_flags & BHSSC_FLAGS_ATTR);
5849336Sdfr		break;
5859336Sdfr	}
5869336Sdfr	io->scsiio.cdb_len = sizeof(bhssc->bhssc_cdb); /* Which is 16. */
5879336Sdfr	memcpy(io->scsiio.cdb, bhssc->bhssc_cdb, sizeof(bhssc->bhssc_cdb));
5889336Sdfr	refcount_acquire(&cs->cs_outstanding_ctl_pdus);
5899336Sdfr	error = ctl_queue(io);
5909336Sdfr	if (error != CTL_RETVAL_COMPLETE) {
5919336Sdfr		CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d; "
5929336Sdfr		    "dropping connection", error);
5931558Srgrimes		ctl_free_io(io);
5941558Srgrimes		refcount_release(&cs->cs_outstanding_ctl_pdus);
5951558Srgrimes		icl_pdu_free(request);
5961558Srgrimes		cfiscsi_session_terminate(cs);
5971558Srgrimes	}
59825348Sdfr}
59925348Sdfr
60025348Sdfrstatic void
60125348Sdfrcfiscsi_pdu_handle_task_request(struct icl_pdu *request)
60225348Sdfr{
60325348Sdfr	struct iscsi_bhs_task_management_request *bhstmr;
60425348Sdfr	struct iscsi_bhs_task_management_response *bhstmr2;
60525348Sdfr	struct icl_pdu *response;
60625348Sdfr	struct cfiscsi_session *cs;
60725348Sdfr	union ctl_io *io;
60825348Sdfr	int error;
60925348Sdfr
61025348Sdfr	cs = PDU_SESSION(request);
61125348Sdfr	bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs;
61225348Sdfr	io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref);
61325348Sdfr	if (io == NULL) {
61425348Sdfr		CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io;"
61525348Sdfr		    "dropping connection");
61625348Sdfr		icl_pdu_free(request);
61725348Sdfr		cfiscsi_session_terminate(cs);
61825348Sdfr		return;
61925348Sdfr	}
62025348Sdfr	ctl_zero_io(io);
62125348Sdfr	io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = request;
62225348Sdfr	io->io_hdr.io_type = CTL_IO_TASK;
62325348Sdfr	io->io_hdr.nexus.initid.id = cs->cs_ctl_initid;
62425348Sdfr	io->io_hdr.nexus.targ_port = cs->cs_target->ct_softc->fe.targ_port;
62525348Sdfr	io->io_hdr.nexus.targ_target.id = 0;
62625348Sdfr	io->io_hdr.nexus.targ_lun = cfiscsi_decode_lun(bhstmr->bhstmr_lun);
62725348Sdfr	io->io_hdr.nexus.lun_map_fn = cfiscsi_map_lun;
62825348Sdfr	io->io_hdr.nexus.lun_map_arg = cs;
62925348Sdfr	io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */
63025348Sdfr
63125348Sdfr	switch (bhstmr->bhstmr_function & ~0x80) {
63225348Sdfr	case BHSTMR_FUNCTION_ABORT_TASK:
63325348Sdfr#if 0
63425348Sdfr		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_ABORT_TASK");
63525348Sdfr#endif
63625348Sdfr		io->taskio.task_action = CTL_TASK_ABORT_TASK;
63725348Sdfr		io->taskio.tag_num = bhstmr->bhstmr_referenced_task_tag;
63825348Sdfr		break;
63925348Sdfr	case BHSTMR_FUNCTION_LOGICAL_UNIT_RESET:
64025348Sdfr#if 0
64125348Sdfr		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_LOGICAL_UNIT_RESET");
64225348Sdfr#endif
64325348Sdfr		io->taskio.task_action = CTL_TASK_LUN_RESET;
6441558Srgrimes		break;
6451558Srgrimes	case BHSTMR_FUNCTION_TARGET_WARM_RESET:
6461558Srgrimes#if 0
6471558Srgrimes		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_TARGET_WARM_RESET");
6481558Srgrimes#endif
6491558Srgrimes		io->taskio.task_action = CTL_TASK_TARGET_RESET;
6501558Srgrimes		break;
6511558Srgrimes	default:
6521558Srgrimes		CFISCSI_SESSION_DEBUG(cs, "unsupported function 0x%x",
6531558Srgrimes		    bhstmr->bhstmr_function & ~0x80);
6541558Srgrimes		ctl_free_io(io);
6551558Srgrimes
6561558Srgrimes		response = cfiscsi_pdu_new_response(request, M_NOWAIT);
6571558Srgrimes		if (response == NULL) {
6581558Srgrimes			CFISCSI_SESSION_WARN(cs, "failed to allocate memory; "
65952055Sphk			    "dropping connection");
6601558Srgrimes			icl_pdu_free(request);
6619336Sdfr			cfiscsi_session_terminate(cs);
6621558Srgrimes			return;
6631558Srgrimes		}
66452055Sphk		bhstmr2 = (struct iscsi_bhs_task_management_response *)
6651558Srgrimes		    response->ip_bhs;
6661558Srgrimes		bhstmr2->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_RESPONSE;
6671558Srgrimes		bhstmr2->bhstmr_flags = 0x80;
66852055Sphk		bhstmr2->bhstmr_response =
66952055Sphk		    BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED;
67052055Sphk		bhstmr2->bhstmr_initiator_task_tag =
6711558Srgrimes		    bhstmr->bhstmr_initiator_task_tag;
6721558Srgrimes		icl_pdu_free(request);
67352055Sphk		cfiscsi_pdu_queue(response);
67452055Sphk		return;
67552055Sphk	}
6761558Srgrimes
67752055Sphk	refcount_acquire(&cs->cs_outstanding_ctl_pdus);
6781558Srgrimes	error = ctl_queue(io);
6791558Srgrimes	if (error != CTL_RETVAL_COMPLETE) {
68052055Sphk		CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d; "
6811558Srgrimes		    "dropping connection", error);
68252055Sphk		ctl_free_io(io);
6831558Srgrimes		refcount_release(&cs->cs_outstanding_ctl_pdus);
68452055Sphk		icl_pdu_free(request);
68552055Sphk		cfiscsi_session_terminate(cs);
68652055Sphk	}
68752055Sphk}
68852055Sphk
68952055Sphkstatic bool
69052055Sphkcfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *cdw)
69152055Sphk{
69252055Sphk	struct iscsi_bhs_data_out *bhsdo;
69352055Sphk	struct cfiscsi_session *cs;
69452055Sphk	struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
69552055Sphk	size_t copy_len, len, off, buffer_offset;
69652055Sphk	int ctl_sg_count;
69752055Sphk	union ctl_io *io;
69852055Sphk
69952055Sphk	cs = PDU_SESSION(request);
70052055Sphk
70152055Sphk	KASSERT((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
70252055Sphk	    ISCSI_BHS_OPCODE_SCSI_DATA_OUT ||
70352055Sphk	    (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
7041558Srgrimes	    ISCSI_BHS_OPCODE_SCSI_COMMAND,
7051558Srgrimes	    ("bad opcode 0x%x", request->ip_bhs->bhs_opcode));
7061558Srgrimes
7071558Srgrimes	/*
7081558Srgrimes	 * We're only using fields common for Data-Out and SCSI Command PDUs.
7091558Srgrimes	 */
7101558Srgrimes	bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs;
7111558Srgrimes
7121558Srgrimes	io = cdw->cdw_ctl_io;
7131558Srgrimes	KASSERT((io->io_hdr.flags & CTL_FLAG_DATA_MASK) != CTL_FLAG_DATA_IN,
7141558Srgrimes	    ("CTL_FLAG_DATA_IN"));
7151558Srgrimes
7161558Srgrimes#if 0
7171558Srgrimes	CFISCSI_SESSION_DEBUG(cs, "received %zd bytes out of %d",
7181558Srgrimes	    request->ip_data_len, io->scsiio.kern_total_len);
7191558Srgrimes#endif
7201558Srgrimes
7211558Srgrimes	if (io->scsiio.kern_sg_entries > 0) {
72223680Speter		ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
72323680Speter		ctl_sg_count = io->scsiio.kern_sg_entries;
7241558Srgrimes	} else {
7251558Srgrimes		ctl_sglist = &ctl_sg_entry;
7261558Srgrimes		ctl_sglist->addr = io->scsiio.kern_data_ptr;
7271558Srgrimes		ctl_sglist->len = io->scsiio.kern_data_len;
72823680Speter		ctl_sg_count = 1;
7291558Srgrimes	}
7301558Srgrimes
7311558Srgrimes	if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
7321558Srgrimes	    ISCSI_BHS_OPCODE_SCSI_DATA_OUT)
7331558Srgrimes		buffer_offset = ntohl(bhsdo->bhsdo_buffer_offset);
7341558Srgrimes	else
7351558Srgrimes		buffer_offset = 0;
7361558Srgrimes	len = icl_pdu_data_segment_length(request);
7371558Srgrimes
7381558Srgrimes	/*
7391558Srgrimes	 * Make sure the offset, as sent by the initiator, matches the offset
7401558Srgrimes	 * we're supposed to be at in the scatter-gather list.
7411558Srgrimes	 */
74223680Speter	if (buffer_offset >
74332008Simp	    io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled ||
74432008Simp	    buffer_offset + len <=
74523680Speter	    io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled) {
7462776Sphk		CFISCSI_SESSION_WARN(cs, "received bad buffer offset %zd, "
7472776Sphk		    "expected %zd; dropping connection", buffer_offset,
7482776Sphk		    (size_t)io->scsiio.kern_rel_offset +
7499336Sdfr		    (size_t)io->scsiio.ext_data_filled);
7502776Sphk		ctl_set_data_phase_error(&io->scsiio);
7512776Sphk		cfiscsi_session_terminate(cs);
7521558Srgrimes		return (true);
7531558Srgrimes	}
7541558Srgrimes
7551558Srgrimes	/*
75632008Simp	 * This is the offset within the PDU data segment, as opposed
75732008Simp	 * to buffer_offset, which is the offset within the task (SCSI
7581558Srgrimes	 * command).
7591558Srgrimes	 */
7601558Srgrimes	off = io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled -
7611558Srgrimes	    buffer_offset;
7621558Srgrimes
7639336Sdfr	/*
7641558Srgrimes	 * Iterate over the scatter/gather segments, filling them with data
76525004Sdfr	 * from the PDU data segment.  Note that this can get called multiple
76625004Sdfr	 * times for one SCSI command; the cdw structure holds state for the
76725004Sdfr	 * scatter/gather list.
7689336Sdfr	 */
7699336Sdfr	for (;;) {
77025004Sdfr		KASSERT(cdw->cdw_sg_index < ctl_sg_count,
7719336Sdfr		    ("cdw->cdw_sg_index >= ctl_sg_count"));
7729336Sdfr		if (cdw->cdw_sg_len == 0) {
7739336Sdfr			cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr;
77425004Sdfr			cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len;
7759336Sdfr		}
7761558Srgrimes		KASSERT(off <= len, ("len > off"));
7771558Srgrimes		copy_len = len - off;
7781558Srgrimes		if (copy_len > cdw->cdw_sg_len)
7791558Srgrimes			copy_len = cdw->cdw_sg_len;
7809230Skarl
7819230Skarl		icl_pdu_get_data(request, off, cdw->cdw_sg_addr, copy_len);
7829336Sdfr		cdw->cdw_sg_addr += copy_len;
7831558Srgrimes		cdw->cdw_sg_len -= copy_len;
7841558Srgrimes		off += copy_len;
7851558Srgrimes		io->scsiio.ext_data_filled += copy_len;
78625348Sdfr
78725348Sdfr		if (cdw->cdw_sg_len == 0) {
78825348Sdfr			/*
78925348Sdfr			 * End of current segment.
79025348Sdfr			 */
79125348Sdfr			if (cdw->cdw_sg_index == ctl_sg_count - 1) {
79225348Sdfr				/*
79325348Sdfr				 * Last segment in scatter/gather list.
79425348Sdfr				 */
79525348Sdfr				break;
79625348Sdfr			}
79737427Scharnier			cdw->cdw_sg_index++;
79825348Sdfr		}
79925348Sdfr
8001558Srgrimes		if (off == len) {
8011558Srgrimes			/*
8021558Srgrimes			 * End of PDU payload.
8039336Sdfr			 */
8049336Sdfr			break;
8059336Sdfr		}
8069336Sdfr	}
8079336Sdfr
8089336Sdfr	if (len > off) {
8099336Sdfr		/*
8101558Srgrimes		 * In case of unsolicited data, it's possible that the buffer
8114822Sats		 * provided by CTL is smaller than negotiated FirstBurstLength.
8121558Srgrimes		 * Just ignore the superfluous data; will ask for them with R2T
8131558Srgrimes		 * on next call to cfiscsi_datamove().
8141558Srgrimes		 *
8151558Srgrimes		 * This obviously can only happen with SCSI Command PDU.
8169336Sdfr		 */
8179336Sdfr		if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
8189336Sdfr		    ISCSI_BHS_OPCODE_SCSI_COMMAND)
8199336Sdfr			return (true);
8209336Sdfr
8211558Srgrimes		CFISCSI_SESSION_WARN(cs, "received too much data: got %zd bytes, "
8221558Srgrimes		    "expected %zd; dropping connection",
8231558Srgrimes		    icl_pdu_data_segment_length(request), off);
82425004Sdfr		ctl_set_data_phase_error(&io->scsiio);
82525004Sdfr		cfiscsi_session_terminate(cs);
82625004Sdfr		return (true);
82725004Sdfr	}
82825004Sdfr
82925004Sdfr	if (io->scsiio.ext_data_filled == io->scsiio.kern_data_len &&
83025004Sdfr	    (bhsdo->bhsdo_flags & BHSDO_FLAGS_F) == 0) {
83125004Sdfr		CFISCSI_SESSION_WARN(cs, "got the final packet without "
83225004Sdfr		    "the F flag; flags = 0x%x; dropping connection",
8331558Srgrimes		    bhsdo->bhsdo_flags);
8341558Srgrimes		ctl_set_data_phase_error(&io->scsiio);
8351558Srgrimes		cfiscsi_session_terminate(cs);
8361558Srgrimes		return (true);
8371558Srgrimes	}
8381558Srgrimes
8391558Srgrimes	if (io->scsiio.ext_data_filled != io->scsiio.kern_data_len &&
8401558Srgrimes	    (bhsdo->bhsdo_flags & BHSDO_FLAGS_F) != 0) {
8411558Srgrimes		if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
8421558Srgrimes		    ISCSI_BHS_OPCODE_SCSI_DATA_OUT) {
8431558Srgrimes			CFISCSI_SESSION_WARN(cs, "got the final packet, but the "
8441558Srgrimes			    "transmitted size was %zd bytes instead of %d; "
84551873Sdillon			    "dropping connection",
8461558Srgrimes			    (size_t)io->scsiio.ext_data_filled,
84737427Scharnier			    io->scsiio.kern_data_len);
8481558Srgrimes			ctl_set_data_phase_error(&io->scsiio);
8491558Srgrimes			cfiscsi_session_terminate(cs);
8501558Srgrimes			return (true);
8511558Srgrimes		} else {
8521558Srgrimes			/*
8531558Srgrimes			 * For SCSI Command PDU, this just means we need to
8541558Srgrimes			 * solicit more data by sending R2T.
8551558Srgrimes			 */
8561558Srgrimes			return (false);
8571558Srgrimes		}
8581558Srgrimes	}
8591558Srgrimes
8601558Srgrimes	if (io->scsiio.ext_data_filled == io->scsiio.kern_data_len) {
8611558Srgrimes#if 0
8621558Srgrimes		CFISCSI_SESSION_DEBUG(cs, "no longer expecting Data-Out with target "
8631558Srgrimes		    "transfer tag 0x%x", cdw->cdw_target_transfer_tag);
8641558Srgrimes#endif
86523680Speter
8661558Srgrimes		return (true);
8671558Srgrimes	}
8681558Srgrimes
8691558Srgrimes	return (false);
8701558Srgrimes}
8711558Srgrimes
8721558Srgrimesstatic void
8731558Srgrimescfiscsi_pdu_handle_data_out(struct icl_pdu *request)
8741558Srgrimes{
8751558Srgrimes	struct iscsi_bhs_data_out *bhsdo;
8761558Srgrimes	struct cfiscsi_session *cs;
8771558Srgrimes	struct cfiscsi_data_wait *cdw = NULL;
8781558Srgrimes	union ctl_io *io;
8799336Sdfr	bool done;
8809336Sdfr
8811558Srgrimes	cs = PDU_SESSION(request);
8821558Srgrimes	bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs;
8831558Srgrimes
8841558Srgrimes	CFISCSI_SESSION_LOCK(cs);
8851558Srgrimes	TAILQ_FOREACH(cdw, &cs->cs_waiting_for_data_out, cdw_next) {
8861558Srgrimes#if 0
8871558Srgrimes		CFISCSI_SESSION_DEBUG(cs, "have ttt 0x%x, itt 0x%x; looking for "
8881558Srgrimes		    "ttt 0x%x, itt 0x%x",
8891558Srgrimes		    bhsdo->bhsdo_target_transfer_tag,
8901558Srgrimes		    bhsdo->bhsdo_initiator_task_tag,
8911558Srgrimes		    cdw->cdw_target_transfer_tag, cdw->cdw_initiator_task_tag));
8921558Srgrimes#endif
8931558Srgrimes		if (bhsdo->bhsdo_target_transfer_tag ==
8941558Srgrimes		    cdw->cdw_target_transfer_tag)
8951558Srgrimes			break;
8961558Srgrimes	}
8971558Srgrimes	CFISCSI_SESSION_UNLOCK(cs);
8981558Srgrimes	if (cdw == NULL) {
8999336Sdfr		CFISCSI_SESSION_WARN(cs, "data transfer tag 0x%x, initiator task tag "
9001558Srgrimes		    "0x%x, not found; dropping connection",
9019336Sdfr		    bhsdo->bhsdo_target_transfer_tag, bhsdo->bhsdo_initiator_task_tag);
9029336Sdfr		icl_pdu_free(request);
9039336Sdfr		cfiscsi_session_terminate(cs);
9049336Sdfr		return;
9051558Srgrimes	}
9061558Srgrimes
9071558Srgrimes	io = cdw->cdw_ctl_io;
9089336Sdfr	KASSERT((io->io_hdr.flags & CTL_FLAG_DATA_MASK) != CTL_FLAG_DATA_IN,
9099336Sdfr	    ("CTL_FLAG_DATA_IN"));
9109336Sdfr
9119336Sdfr	done = cfiscsi_handle_data_segment(request, cdw);
9129336Sdfr	if (done) {
9139336Sdfr		CFISCSI_SESSION_LOCK(cs);
9149336Sdfr		TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next);
9159336Sdfr		CFISCSI_SESSION_UNLOCK(cs);
9169336Sdfr		uma_zfree(cfiscsi_data_wait_zone, cdw);
9179336Sdfr		io->scsiio.be_move_done(io);
9189336Sdfr	}
9199336Sdfr
9209336Sdfr	icl_pdu_free(request);
9219336Sdfr}
9229336Sdfr
9239336Sdfrstatic void
9249336Sdfrcfiscsi_pdu_handle_logout_request(struct icl_pdu *request)
9259336Sdfr{
9269336Sdfr	struct iscsi_bhs_logout_request *bhslr;
9279336Sdfr	struct iscsi_bhs_logout_response *bhslr2;
9289336Sdfr	struct icl_pdu *response;
9299336Sdfr	struct cfiscsi_session *cs;
9309336Sdfr
9319336Sdfr	cs = PDU_SESSION(request);
9329336Sdfr	bhslr = (struct iscsi_bhs_logout_request *)request->ip_bhs;
9339336Sdfr	switch (bhslr->bhslr_reason & 0x7f) {
9349336Sdfr	case BHSLR_REASON_CLOSE_SESSION:
9359336Sdfr	case BHSLR_REASON_CLOSE_CONNECTION:
9361558Srgrimes		response = cfiscsi_pdu_new_response(request, M_NOWAIT);
9371558Srgrimes		if (response == NULL) {
93818286Sbde			CFISCSI_SESSION_DEBUG(cs, "failed to allocate memory");
9391558Srgrimes			icl_pdu_free(request);
9401558Srgrimes			cfiscsi_session_terminate(cs);
94137427Scharnier			return;
94237427Scharnier		}
94337427Scharnier		bhslr2 = (struct iscsi_bhs_logout_response *)response->ip_bhs;
94437427Scharnier		bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE;
94537427Scharnier		bhslr2->bhslr_flags = 0x80;
9461558Srgrimes		bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY;
9471558Srgrimes		bhslr2->bhslr_initiator_task_tag =
948		    bhslr->bhslr_initiator_task_tag;
949		icl_pdu_free(request);
950		cfiscsi_pdu_queue(response);
951		cfiscsi_session_terminate(cs);
952		break;
953	case BHSLR_REASON_REMOVE_FOR_RECOVERY:
954		response = cfiscsi_pdu_new_response(request, M_NOWAIT);
955		if (response == NULL) {
956			CFISCSI_SESSION_WARN(cs,
957			    "failed to allocate memory; dropping connection");
958			icl_pdu_free(request);
959			cfiscsi_session_terminate(cs);
960			return;
961		}
962		bhslr2 = (struct iscsi_bhs_logout_response *)response->ip_bhs;
963		bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE;
964		bhslr2->bhslr_flags = 0x80;
965		bhslr2->bhslr_response = BHSLR_RESPONSE_RECOVERY_NOT_SUPPORTED;
966		bhslr2->bhslr_initiator_task_tag =
967		    bhslr->bhslr_initiator_task_tag;
968		icl_pdu_free(request);
969		cfiscsi_pdu_queue(response);
970		break;
971	default:
972		CFISCSI_SESSION_WARN(cs, "invalid reason 0%x; dropping connection",
973		    bhslr->bhslr_reason);
974		icl_pdu_free(request);
975		cfiscsi_session_terminate(cs);
976		break;
977	}
978}
979
980static void
981cfiscsi_callout(void *context)
982{
983	struct icl_pdu *cp;
984	struct iscsi_bhs_nop_in *bhsni;
985	struct cfiscsi_session *cs;
986
987	cs = context;
988
989	if (cs->cs_terminating)
990		return;
991
992	callout_schedule(&cs->cs_callout, 1 * hz);
993
994	atomic_add_int(&cs->cs_timeout, 1);
995
996#ifdef ICL_KERNEL_PROXY
997	if (cs->cs_waiting_for_ctld || cs->cs_login_phase) {
998		if (cs->cs_timeout > login_timeout) {
999			CFISCSI_SESSION_WARN(cs, "login timed out after "
1000			    "%d seconds; dropping connection", cs->cs_timeout);
1001			cfiscsi_session_terminate(cs);
1002		}
1003		return;
1004	}
1005#endif
1006
1007	if (cs->cs_timeout >= ping_timeout) {
1008		CFISCSI_SESSION_WARN(cs, "no ping reply (NOP-Out) after %d seconds; "
1009		    "dropping connection",  ping_timeout);
1010		cfiscsi_session_terminate(cs);
1011		return;
1012	}
1013
1014	/*
1015	 * If the ping was reset less than one second ago - which means
1016	 * that we've received some PDU during the last second - assume
1017	 * the traffic flows correctly and don't bother sending a NOP-Out.
1018	 *
1019	 * (It's 2 - one for one second, and one for incrementing is_timeout
1020	 * earlier in this routine.)
1021	 */
1022	if (cs->cs_timeout < 2)
1023		return;
1024
1025	cp = icl_pdu_new_bhs(cs->cs_conn, M_NOWAIT);
1026	if (cp == NULL) {
1027		CFISCSI_SESSION_WARN(cs, "failed to allocate memory");
1028		return;
1029	}
1030	bhsni = (struct iscsi_bhs_nop_in *)cp->ip_bhs;
1031	bhsni->bhsni_opcode = ISCSI_BHS_OPCODE_NOP_IN;
1032	bhsni->bhsni_flags = 0x80;
1033	bhsni->bhsni_initiator_task_tag = 0xffffffff;
1034
1035	cfiscsi_pdu_queue(cp);
1036}
1037
1038static void
1039cfiscsi_session_terminate_tasks(struct cfiscsi_session *cs)
1040{
1041	struct cfiscsi_data_wait *cdw, *tmpcdw;
1042	union ctl_io *io;
1043	int error, last;
1044
1045#ifdef notyet
1046	io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref);
1047	if (io == NULL) {
1048		CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io");
1049		return;
1050	}
1051	ctl_zero_io(io);
1052	io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = NULL;
1053	io->io_hdr.io_type = CTL_IO_TASK;
1054	io->io_hdr.nexus.initid.id = cs->cs_ctl_initid;
1055	io->io_hdr.nexus.targ_port = cs->cs_target->ct_softc->fe.targ_port;
1056	io->io_hdr.nexus.targ_target.id = 0;
1057	io->io_hdr.nexus.targ_lun = lun;
1058	io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */
1059	io->taskio.task_action = CTL_TASK_ABORT_TASK_SET;
1060	error = ctl_queue(io);
1061	if (error != CTL_RETVAL_COMPLETE) {
1062		CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error);
1063		ctl_free_io(io);
1064	}
1065#else
1066	/*
1067	 * CTL doesn't currently support CTL_TASK_ABORT_TASK_SET, so instead
1068	 * just iterate over tasks that are waiting for something - data - and
1069	 * terminate those.
1070	 */
1071	CFISCSI_SESSION_LOCK(cs);
1072	TAILQ_FOREACH_SAFE(cdw,
1073	    &cs->cs_waiting_for_data_out, cdw_next, tmpcdw) {
1074		io = ctl_alloc_io(cs->cs_target->ct_softc->fe.ctl_pool_ref);
1075		if (io == NULL) {
1076			CFISCSI_SESSION_WARN(cs, "can't allocate ctl_io");
1077			return;
1078		}
1079		ctl_zero_io(io);
1080		io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = NULL;
1081		io->io_hdr.io_type = CTL_IO_TASK;
1082		io->io_hdr.nexus.initid.id = cs->cs_ctl_initid;
1083		io->io_hdr.nexus.targ_port =
1084		    cs->cs_target->ct_softc->fe.targ_port;
1085		io->io_hdr.nexus.targ_target.id = 0;
1086		//io->io_hdr.nexus.targ_lun = lun; /* Not needed? */
1087		io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */
1088		io->taskio.task_action = CTL_TASK_ABORT_TASK;
1089		io->taskio.tag_num = cdw->cdw_initiator_task_tag;
1090		error = ctl_queue(io);
1091		if (error != CTL_RETVAL_COMPLETE) {
1092			CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error);
1093			ctl_free_io(io);
1094			return;
1095		}
1096#if 0
1097		CFISCSI_SESSION_DEBUG(cs, "removing csw for initiator task tag "
1098		    "0x%x", cdw->cdw_initiator_task_tag);
1099#endif
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		TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next);
1108		uma_zfree(cfiscsi_data_wait_zone, cdw);
1109	}
1110	CFISCSI_SESSION_UNLOCK(cs);
1111#endif
1112
1113	/*
1114	 * Wait for CTL to terminate all the tasks.
1115	 */
1116	for (;;) {
1117		refcount_acquire(&cs->cs_outstanding_ctl_pdus);
1118		last = refcount_release(&cs->cs_outstanding_ctl_pdus);
1119		if (last != 0)
1120			break;
1121		CFISCSI_SESSION_WARN(cs, "waiting for CTL to terminate tasks, "
1122		    "%d remaining", cs->cs_outstanding_ctl_pdus);
1123		pause("cfiscsi_terminate", 1);
1124	}
1125}
1126
1127static void
1128cfiscsi_maintenance_thread(void *arg)
1129{
1130	struct cfiscsi_session *cs;
1131
1132	cs = arg;
1133
1134	for (;;) {
1135		CFISCSI_SESSION_LOCK(cs);
1136		if (cs->cs_terminating == false)
1137			cv_wait(&cs->cs_maintenance_cv, &cs->cs_lock);
1138		CFISCSI_SESSION_UNLOCK(cs);
1139
1140		if (cs->cs_terminating) {
1141
1142			/*
1143			 * We used to wait up to 30 seconds to deliver queued
1144			 * PDUs to the initiator.  We also tried hard to deliver
1145			 * SCSI Responses for the aborted PDUs.  We don't do
1146			 * that anymore.  We might need to revisit that.
1147			 */
1148			callout_drain(&cs->cs_callout);
1149			icl_conn_shutdown(cs->cs_conn);
1150			icl_conn_close(cs->cs_conn);
1151
1152			/*
1153			 * At this point ICL receive thread is no longer
1154			 * running; no new tasks can be queued.
1155			 */
1156			cfiscsi_session_terminate_tasks(cs);
1157			cfiscsi_session_delete(cs);
1158			kthread_exit();
1159			return;
1160		}
1161		CFISCSI_SESSION_DEBUG(cs, "nothing to do");
1162	}
1163}
1164
1165static void
1166cfiscsi_session_terminate(struct cfiscsi_session *cs)
1167{
1168
1169	if (cs->cs_terminating)
1170		return;
1171	cs->cs_terminating = true;
1172	cv_signal(&cs->cs_maintenance_cv);
1173#ifdef ICL_KERNEL_PROXY
1174	cv_signal(&cs->cs_login_cv);
1175#endif
1176}
1177
1178static int
1179cfiscsi_session_register_initiator(struct cfiscsi_session *cs)
1180{
1181	int error, i;
1182	struct cfiscsi_softc *softc;
1183
1184	KASSERT(cs->cs_ctl_initid == -1, ("already registered"));
1185
1186	softc = &cfiscsi_softc;
1187
1188	mtx_lock(&softc->lock);
1189	for (i = 0; i < softc->max_initiators; i++) {
1190		if (softc->ctl_initids[i] == 0)
1191			break;
1192	}
1193	if (i == softc->max_initiators) {
1194		CFISCSI_SESSION_WARN(cs, "too many concurrent sessions (%d)",
1195		    softc->max_initiators);
1196		mtx_unlock(&softc->lock);
1197		return (1);
1198	}
1199	softc->ctl_initids[i] = 1;
1200	mtx_unlock(&softc->lock);
1201
1202#if 0
1203	CFISCSI_SESSION_DEBUG(cs, "adding initiator id %d, max %d",
1204	    i, softc->max_initiators);
1205#endif
1206	cs->cs_ctl_initid = i;
1207	error = ctl_add_initiator(0x0, softc->fe.targ_port, cs->cs_ctl_initid);
1208	if (error != 0) {
1209		CFISCSI_SESSION_WARN(cs, "ctl_add_initiator failed with error %d", error);
1210		mtx_lock(&softc->lock);
1211		softc->ctl_initids[cs->cs_ctl_initid] = 0;
1212		mtx_unlock(&softc->lock);
1213		cs->cs_ctl_initid = -1;
1214		return (1);
1215	}
1216
1217	return (0);
1218}
1219
1220static void
1221cfiscsi_session_unregister_initiator(struct cfiscsi_session *cs)
1222{
1223	int error;
1224	struct cfiscsi_softc *softc;
1225
1226	if (cs->cs_ctl_initid == -1)
1227		return;
1228
1229	softc = &cfiscsi_softc;
1230
1231	error = ctl_remove_initiator(softc->fe.targ_port, cs->cs_ctl_initid);
1232	if (error != 0) {
1233		CFISCSI_SESSION_WARN(cs, "ctl_remove_initiator failed with error %d",
1234		    error);
1235	}
1236	mtx_lock(&softc->lock);
1237	softc->ctl_initids[cs->cs_ctl_initid] = 0;
1238	mtx_unlock(&softc->lock);
1239	cs->cs_ctl_initid = -1;
1240}
1241
1242static struct cfiscsi_session *
1243cfiscsi_session_new(struct cfiscsi_softc *softc)
1244{
1245	struct cfiscsi_session *cs;
1246	int error;
1247
1248	cs = malloc(sizeof(*cs), M_CFISCSI, M_NOWAIT | M_ZERO);
1249	if (cs == NULL) {
1250		CFISCSI_WARN("malloc failed");
1251		return (NULL);
1252	}
1253	cs->cs_ctl_initid = -1;
1254
1255	refcount_init(&cs->cs_outstanding_ctl_pdus, 0);
1256	TAILQ_INIT(&cs->cs_waiting_for_data_out);
1257	mtx_init(&cs->cs_lock, "cfiscsi_lock", NULL, MTX_DEF);
1258	cv_init(&cs->cs_maintenance_cv, "cfiscsi_mt");
1259#ifdef ICL_KERNEL_PROXY
1260	cv_init(&cs->cs_login_cv, "cfiscsi_login");
1261#endif
1262
1263	cs->cs_conn = icl_conn_new("cfiscsi", &cs->cs_lock);
1264	cs->cs_conn->ic_receive = cfiscsi_receive_callback;
1265	cs->cs_conn->ic_error = cfiscsi_error_callback;
1266	cs->cs_conn->ic_prv0 = cs;
1267
1268	error = kthread_add(cfiscsi_maintenance_thread, cs, NULL, NULL, 0, 0, "cfiscsimt");
1269	if (error != 0) {
1270		CFISCSI_SESSION_WARN(cs, "kthread_add(9) failed with error %d", error);
1271		free(cs, M_CFISCSI);
1272		return (NULL);
1273	}
1274
1275	mtx_lock(&softc->lock);
1276	cs->cs_id = softc->last_session_id + 1;
1277	softc->last_session_id++;
1278	mtx_unlock(&softc->lock);
1279
1280	mtx_lock(&softc->lock);
1281	TAILQ_INSERT_TAIL(&softc->sessions, cs, cs_next);
1282	mtx_unlock(&softc->lock);
1283
1284	/*
1285	 * Start pinging the initiator.
1286	 */
1287	callout_init(&cs->cs_callout, 1);
1288	callout_reset(&cs->cs_callout, 1 * hz, cfiscsi_callout, cs);
1289
1290	return (cs);
1291}
1292
1293static void
1294cfiscsi_session_delete(struct cfiscsi_session *cs)
1295{
1296	struct cfiscsi_softc *softc;
1297
1298	softc = &cfiscsi_softc;
1299
1300	KASSERT(cs->cs_outstanding_ctl_pdus == 0,
1301	    ("destroying session with outstanding CTL pdus"));
1302	KASSERT(TAILQ_EMPTY(&cs->cs_waiting_for_data_out),
1303	    ("destroying session with non-empty queue"));
1304
1305	cfiscsi_session_unregister_initiator(cs);
1306	if (cs->cs_target != NULL)
1307		cfiscsi_target_release(cs->cs_target);
1308	icl_conn_close(cs->cs_conn);
1309	icl_conn_free(cs->cs_conn);
1310
1311	mtx_lock(&softc->lock);
1312	TAILQ_REMOVE(&softc->sessions, cs, cs_next);
1313	mtx_unlock(&softc->lock);
1314
1315	free(cs, M_CFISCSI);
1316}
1317
1318int
1319cfiscsi_init(void)
1320{
1321	struct cfiscsi_softc *softc;
1322	struct ctl_frontend *fe;
1323	int retval;
1324
1325	softc = &cfiscsi_softc;
1326	retval = 0;
1327	bzero(softc, sizeof(*softc));
1328	mtx_init(&softc->lock, "cfiscsi", NULL, MTX_DEF);
1329
1330#ifdef ICL_KERNEL_PROXY
1331	cv_init(&softc->accept_cv, "cfiscsi_accept");
1332#endif
1333	TAILQ_INIT(&softc->sessions);
1334	TAILQ_INIT(&softc->targets);
1335
1336	fe = &softc->fe;
1337	fe->port_type = CTL_PORT_ISCSI;
1338	/* XXX KDM what should the real number be here? */
1339	fe->num_requested_ctl_io = 4096;
1340	snprintf(softc->port_name, sizeof(softc->port_name), "iscsi");
1341	fe->port_name = softc->port_name;
1342	fe->port_online = cfiscsi_online;
1343	fe->port_offline = cfiscsi_offline;
1344	fe->onoff_arg = softc;
1345	fe->targ_enable = cfiscsi_targ_enable;
1346	fe->targ_disable = cfiscsi_targ_disable;
1347	fe->lun_enable = cfiscsi_lun_enable;
1348	fe->lun_disable = cfiscsi_lun_disable;
1349	fe->targ_lun_arg = softc;
1350	fe->ioctl = cfiscsi_ioctl;
1351	fe->devid = cfiscsi_devid;
1352	fe->fe_datamove = cfiscsi_datamove;
1353	fe->fe_done = cfiscsi_done;
1354
1355	/* XXX KDM what should we report here? */
1356	/* XXX These should probably be fetched from CTL. */
1357	fe->max_targets = 1;
1358	fe->max_target_id = 15;
1359
1360	retval = ctl_frontend_register(fe, /*master_SC*/ 1);
1361	if (retval != 0) {
1362		CFISCSI_WARN("ctl_frontend_register() failed with error %d",
1363		    retval);
1364		retval = 1;
1365		goto bailout;
1366	}
1367
1368	softc->max_initiators = fe->max_initiators;
1369
1370	cfiscsi_data_wait_zone = uma_zcreate("cfiscsi_data_wait",
1371	    sizeof(struct cfiscsi_data_wait), NULL, NULL, NULL, NULL,
1372	    UMA_ALIGN_PTR, 0);
1373
1374	return (0);
1375
1376bailout:
1377	return (retval);
1378}
1379
1380static int
1381cfiscsi_module_event_handler(module_t mod, int what, void *arg)
1382{
1383
1384	switch (what) {
1385	case MOD_LOAD:
1386		return (cfiscsi_init());
1387	case MOD_UNLOAD:
1388		return (EBUSY);
1389	default:
1390		return (EOPNOTSUPP);
1391	}
1392}
1393
1394#ifdef ICL_KERNEL_PROXY
1395static void
1396cfiscsi_accept(struct socket *so, struct sockaddr *sa, int portal_id)
1397{
1398	struct cfiscsi_session *cs;
1399
1400	cs = cfiscsi_session_new(&cfiscsi_softc);
1401	if (cs == NULL) {
1402		CFISCSI_WARN("failed to create session");
1403		return;
1404	}
1405
1406	icl_conn_handoff_sock(cs->cs_conn, so);
1407	cs->cs_initiator_sa = sa;
1408	cs->cs_portal_id = portal_id;
1409	cs->cs_waiting_for_ctld = true;
1410	cv_signal(&cfiscsi_softc.accept_cv);
1411}
1412#endif
1413
1414static void
1415cfiscsi_online(void *arg)
1416{
1417	struct cfiscsi_softc *softc;
1418
1419	softc = (struct cfiscsi_softc *)arg;
1420
1421	softc->online = 1;
1422#ifdef ICL_KERNEL_PROXY
1423	if (softc->listener != NULL)
1424		icl_listen_free(softc->listener);
1425	softc->listener = icl_listen_new(cfiscsi_accept);
1426#endif
1427}
1428
1429static void
1430cfiscsi_offline(void *arg)
1431{
1432	struct cfiscsi_softc *softc;
1433	struct cfiscsi_session *cs;
1434
1435	softc = (struct cfiscsi_softc *)arg;
1436
1437	softc->online = 0;
1438
1439	mtx_lock(&softc->lock);
1440	TAILQ_FOREACH(cs, &softc->sessions, cs_next)
1441		cfiscsi_session_terminate(cs);
1442	mtx_unlock(&softc->lock);
1443
1444#ifdef ICL_KERNEL_PROXY
1445	icl_listen_free(softc->listener);
1446	softc->listener = NULL;
1447#endif
1448}
1449
1450static int
1451cfiscsi_targ_enable(void *arg, struct ctl_id targ_id)
1452{
1453
1454	return (0);
1455}
1456
1457static int
1458cfiscsi_targ_disable(void *arg, struct ctl_id targ_id)
1459{
1460
1461	return (0);
1462}
1463
1464static void
1465cfiscsi_ioctl_handoff(struct ctl_iscsi *ci)
1466{
1467	struct cfiscsi_softc *softc;
1468	struct cfiscsi_session *cs;
1469	struct cfiscsi_target *ct;
1470	struct ctl_iscsi_handoff_params *cihp;
1471	int error;
1472
1473	cihp = (struct ctl_iscsi_handoff_params *)&(ci->data);
1474	softc = &cfiscsi_softc;
1475
1476	CFISCSI_DEBUG("new connection from %s (%s) to %s",
1477	    cihp->initiator_name, cihp->initiator_addr,
1478	    cihp->target_name);
1479
1480	if (softc->online == 0) {
1481		ci->status = CTL_ISCSI_ERROR;
1482		snprintf(ci->error_str, sizeof(ci->error_str),
1483		    "%s: port offline", __func__);
1484		return;
1485	}
1486
1487	ct = cfiscsi_target_find(softc, cihp->target_name);
1488	if (ct == NULL) {
1489		ci->status = CTL_ISCSI_ERROR;
1490		snprintf(ci->error_str, sizeof(ci->error_str),
1491		    "%s: target not found", __func__);
1492		return;
1493	}
1494
1495#ifdef ICL_KERNEL_PROXY
1496	if (cihp->socket > 0 && cihp->connection_id > 0) {
1497		snprintf(ci->error_str, sizeof(ci->error_str),
1498		    "both socket and connection_id set");
1499		ci->status = CTL_ISCSI_ERROR;
1500		cfiscsi_target_release(ct);
1501		return;
1502	}
1503	if (cihp->socket == 0) {
1504		mtx_lock(&cfiscsi_softc.lock);
1505		TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) {
1506			if (cs->cs_id == cihp->socket)
1507				break;
1508		}
1509		if (cs == NULL) {
1510			mtx_unlock(&cfiscsi_softc.lock);
1511			snprintf(ci->error_str, sizeof(ci->error_str),
1512			    "connection not found");
1513			ci->status = CTL_ISCSI_ERROR;
1514			cfiscsi_target_release(ct);
1515			return;
1516		}
1517		mtx_unlock(&cfiscsi_softc.lock);
1518	} else {
1519#endif
1520		cs = cfiscsi_session_new(softc);
1521		if (cs == NULL) {
1522			ci->status = CTL_ISCSI_ERROR;
1523			snprintf(ci->error_str, sizeof(ci->error_str),
1524			    "%s: cfiscsi_session_new failed", __func__);
1525			cfiscsi_target_release(ct);
1526			return;
1527		}
1528#ifdef ICL_KERNEL_PROXY
1529	}
1530#endif
1531	cs->cs_target = ct;
1532
1533	/*
1534	 * First PDU of Full Feature phase has the same CmdSN as the last
1535	 * PDU from the Login Phase received from the initiator.  Thus,
1536	 * the -1 below.
1537	 */
1538	cs->cs_portal_group_tag = cihp->portal_group_tag;
1539	cs->cs_cmdsn = cihp->cmdsn;
1540	cs->cs_statsn = cihp->statsn;
1541	cs->cs_max_data_segment_length = cihp->max_recv_data_segment_length;
1542	cs->cs_max_burst_length = cihp->max_burst_length;
1543	cs->cs_immediate_data = !!cihp->immediate_data;
1544	if (cihp->header_digest == CTL_ISCSI_DIGEST_CRC32C)
1545		cs->cs_conn->ic_header_crc32c = true;
1546	if (cihp->data_digest == CTL_ISCSI_DIGEST_CRC32C)
1547		cs->cs_conn->ic_data_crc32c = true;
1548
1549	strlcpy(cs->cs_initiator_name,
1550	    cihp->initiator_name, sizeof(cs->cs_initiator_name));
1551	strlcpy(cs->cs_initiator_addr,
1552	    cihp->initiator_addr, sizeof(cs->cs_initiator_addr));
1553	strlcpy(cs->cs_initiator_alias,
1554	    cihp->initiator_alias, sizeof(cs->cs_initiator_alias));
1555
1556#ifdef ICL_KERNEL_PROXY
1557	if (cihp->socket > 0) {
1558#endif
1559		error = icl_conn_handoff(cs->cs_conn, cihp->socket);
1560		if (error != 0) {
1561			cfiscsi_session_delete(cs);
1562			ci->status = CTL_ISCSI_ERROR;
1563			snprintf(ci->error_str, sizeof(ci->error_str),
1564			    "%s: icl_conn_handoff failed with error %d",
1565			    __func__, error);
1566			return;
1567		}
1568#ifdef ICL_KERNEL_PROXY
1569	}
1570#endif
1571
1572	/*
1573	 * Register initiator with CTL.
1574	 */
1575	cfiscsi_session_register_initiator(cs);
1576
1577#ifdef ICL_KERNEL_PROXY
1578	cs->cs_login_phase = false;
1579
1580	/*
1581	 * First PDU of the Full Feature phase has likely already arrived.
1582	 * We have to pick it up and execute properly.
1583	 */
1584	if (cs->cs_login_pdu != NULL) {
1585		CFISCSI_SESSION_DEBUG(cs, "picking up first PDU");
1586		cfiscsi_pdu_handle(cs->cs_login_pdu);
1587		cs->cs_login_pdu = NULL;
1588	}
1589#endif
1590
1591	ci->status = CTL_ISCSI_OK;
1592}
1593
1594static void
1595cfiscsi_ioctl_list(struct ctl_iscsi *ci)
1596{
1597	struct ctl_iscsi_list_params *cilp;
1598	struct cfiscsi_session *cs;
1599	struct cfiscsi_softc *softc;
1600	struct sbuf *sb;
1601	int error;
1602
1603	cilp = (struct ctl_iscsi_list_params *)&(ci->data);
1604	softc = &cfiscsi_softc;
1605
1606	sb = sbuf_new(NULL, NULL, cilp->alloc_len, SBUF_FIXEDLEN);
1607	if (sb == NULL) {
1608		ci->status = CTL_ISCSI_ERROR;
1609		snprintf(ci->error_str, sizeof(ci->error_str),
1610		    "Unable to allocate %d bytes for iSCSI session list",
1611		    cilp->alloc_len);
1612		return;
1613	}
1614
1615	sbuf_printf(sb, "<ctlislist>\n");
1616	mtx_lock(&softc->lock);
1617	TAILQ_FOREACH(cs, &softc->sessions, cs_next) {
1618#ifdef ICL_KERNEL_PROXY
1619		if (cs->cs_target == NULL)
1620			continue;
1621#endif
1622		error = sbuf_printf(sb, "<connection id=\"%d\">"
1623		    "<initiator>%s</initiator>"
1624		    "<initiator_addr>%s</initiator_addr>"
1625		    "<initiator_alias>%s</initiator_alias>"
1626		    "<target>%s</target>"
1627		    "<target_alias>%s</target_alias>"
1628		    "<header_digest>%s</header_digest>"
1629		    "<data_digest>%s</data_digest>"
1630		    "<max_data_segment_length>%zd</max_data_segment_length>"
1631		    "<immediate_data>%d</immediate_data>"
1632		    "<iser>%d</iser>"
1633		    "</connection>\n",
1634		    cs->cs_id,
1635		    cs->cs_initiator_name, cs->cs_initiator_addr, cs->cs_initiator_alias,
1636		    cs->cs_target->ct_name, cs->cs_target->ct_alias,
1637		    cs->cs_conn->ic_header_crc32c ? "CRC32C" : "None",
1638		    cs->cs_conn->ic_data_crc32c ? "CRC32C" : "None",
1639		    cs->cs_max_data_segment_length,
1640		    cs->cs_immediate_data,
1641		    cs->cs_conn->ic_iser);
1642		if (error != 0)
1643			break;
1644	}
1645	mtx_unlock(&softc->lock);
1646	error = sbuf_printf(sb, "</ctlislist>\n");
1647	if (error != 0) {
1648		sbuf_delete(sb);
1649		ci->status = CTL_ISCSI_LIST_NEED_MORE_SPACE;
1650		snprintf(ci->error_str, sizeof(ci->error_str),
1651		    "Out of space, %d bytes is too small", cilp->alloc_len);
1652		return;
1653	}
1654	sbuf_finish(sb);
1655
1656	error = copyout(sbuf_data(sb), cilp->conn_xml, sbuf_len(sb) + 1);
1657	cilp->fill_len = sbuf_len(sb) + 1;
1658	ci->status = CTL_ISCSI_OK;
1659	sbuf_delete(sb);
1660}
1661
1662static void
1663cfiscsi_ioctl_terminate(struct ctl_iscsi *ci)
1664{
1665	struct icl_pdu *response;
1666	struct iscsi_bhs_asynchronous_message *bhsam;
1667	struct ctl_iscsi_terminate_params *citp;
1668	struct cfiscsi_session *cs;
1669	struct cfiscsi_softc *softc;
1670	int found = 0;
1671
1672	citp = (struct ctl_iscsi_terminate_params *)&(ci->data);
1673	softc = &cfiscsi_softc;
1674
1675	mtx_lock(&softc->lock);
1676	TAILQ_FOREACH(cs, &softc->sessions, cs_next) {
1677		if (citp->all == 0 && cs->cs_id != citp->connection_id &&
1678		    strcmp(cs->cs_initiator_name, citp->initiator_name) != 0 &&
1679		    strcmp(cs->cs_initiator_addr, citp->initiator_addr) != 0)
1680			continue;
1681
1682		response = icl_pdu_new_bhs(cs->cs_conn, M_NOWAIT);
1683		if (response == NULL) {
1684			/*
1685			 * Oh well.  Just terminate the connection.
1686			 */
1687		} else {
1688			bhsam = (struct iscsi_bhs_asynchronous_message *)
1689			    response->ip_bhs;
1690			bhsam->bhsam_opcode = ISCSI_BHS_OPCODE_ASYNC_MESSAGE;
1691			bhsam->bhsam_flags = 0x80;
1692			bhsam->bhsam_0xffffffff = 0xffffffff;
1693			bhsam->bhsam_async_event =
1694			    BHSAM_EVENT_TARGET_TERMINATES_SESSION;
1695			cfiscsi_pdu_queue(response);
1696		}
1697		cfiscsi_session_terminate(cs);
1698		found++;
1699	}
1700	mtx_unlock(&softc->lock);
1701
1702	if (found == 0) {
1703		ci->status = CTL_ISCSI_SESSION_NOT_FOUND;
1704		snprintf(ci->error_str, sizeof(ci->error_str),
1705		    "No matching connections found");
1706		return;
1707	}
1708
1709	ci->status = CTL_ISCSI_OK;
1710}
1711
1712static void
1713cfiscsi_ioctl_logout(struct ctl_iscsi *ci)
1714{
1715	struct icl_pdu *response;
1716	struct iscsi_bhs_asynchronous_message *bhsam;
1717	struct ctl_iscsi_logout_params *cilp;
1718	struct cfiscsi_session *cs;
1719	struct cfiscsi_softc *softc;
1720	int found = 0;
1721
1722	cilp = (struct ctl_iscsi_logout_params *)&(ci->data);
1723	softc = &cfiscsi_softc;
1724
1725	mtx_lock(&softc->lock);
1726	TAILQ_FOREACH(cs, &softc->sessions, cs_next) {
1727		if (cilp->all == 0 && cs->cs_id != cilp->connection_id &&
1728		    strcmp(cs->cs_initiator_name, cilp->initiator_name) != 0 &&
1729		    strcmp(cs->cs_initiator_addr, cilp->initiator_addr) != 0)
1730			continue;
1731
1732		response = icl_pdu_new_bhs(cs->cs_conn, M_NOWAIT);
1733		if (response == NULL) {
1734			ci->status = CTL_ISCSI_ERROR;
1735			snprintf(ci->error_str, sizeof(ci->error_str),
1736			    "Unable to allocate memory");
1737			mtx_unlock(&softc->lock);
1738			return;
1739		}
1740		bhsam =
1741		    (struct iscsi_bhs_asynchronous_message *)response->ip_bhs;
1742		bhsam->bhsam_opcode = ISCSI_BHS_OPCODE_ASYNC_MESSAGE;
1743		bhsam->bhsam_flags = 0x80;
1744		bhsam->bhsam_async_event = BHSAM_EVENT_TARGET_REQUESTS_LOGOUT;
1745		bhsam->bhsam_parameter3 = htons(10);
1746		cfiscsi_pdu_queue(response);
1747		found++;
1748	}
1749	mtx_unlock(&softc->lock);
1750
1751	if (found == 0) {
1752		ci->status = CTL_ISCSI_SESSION_NOT_FOUND;
1753		snprintf(ci->error_str, sizeof(ci->error_str),
1754		    "No matching connections found");
1755		return;
1756	}
1757
1758	ci->status = CTL_ISCSI_OK;
1759}
1760
1761#ifdef ICL_KERNEL_PROXY
1762static void
1763cfiscsi_ioctl_listen(struct ctl_iscsi *ci)
1764{
1765	struct ctl_iscsi_listen_params *cilp;
1766	struct sockaddr *sa;
1767	int error;
1768
1769	cilp = (struct ctl_iscsi_listen_params *)&(ci->data);
1770
1771	if (cfiscsi_softc.listener == NULL) {
1772		CFISCSI_DEBUG("no listener");
1773		snprintf(ci->error_str, sizeof(ci->error_str), "no listener");
1774		ci->status = CTL_ISCSI_ERROR;
1775		return;
1776	}
1777
1778	error = getsockaddr(&sa, (void *)cilp->addr, cilp->addrlen);
1779	if (error != 0) {
1780		CFISCSI_DEBUG("getsockaddr, error %d", error);
1781		snprintf(ci->error_str, sizeof(ci->error_str), "getsockaddr failed");
1782		ci->status = CTL_ISCSI_ERROR;
1783		return;
1784	}
1785
1786	error = icl_listen_add(cfiscsi_softc.listener, cilp->iser, cilp->domain,
1787	    cilp->socktype, cilp->protocol, sa, cilp->portal_id);
1788	if (error != 0) {
1789		free(sa, M_SONAME);
1790		CFISCSI_DEBUG("icl_listen_add, error %d", error);
1791		snprintf(ci->error_str, sizeof(ci->error_str),
1792		    "icl_listen_add failed, error %d", error);
1793		ci->status = CTL_ISCSI_ERROR;
1794		return;
1795	}
1796
1797	ci->status = CTL_ISCSI_OK;
1798}
1799
1800static void
1801cfiscsi_ioctl_accept(struct ctl_iscsi *ci)
1802{
1803	struct ctl_iscsi_accept_params *ciap;
1804	struct cfiscsi_session *cs;
1805	int error;
1806
1807	ciap = (struct ctl_iscsi_accept_params *)&(ci->data);
1808
1809	mtx_lock(&cfiscsi_softc.lock);
1810	for (;;) {
1811		TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) {
1812			if (cs->cs_waiting_for_ctld)
1813				break;
1814		}
1815		if (cs != NULL)
1816			break;
1817		error = cv_wait_sig(&cfiscsi_softc.accept_cv, &cfiscsi_softc.lock);
1818		if (error != 0) {
1819			mtx_unlock(&cfiscsi_softc.lock);
1820			snprintf(ci->error_str, sizeof(ci->error_str), "interrupted");
1821			ci->status = CTL_ISCSI_ERROR;
1822			return;
1823		}
1824	}
1825	mtx_unlock(&cfiscsi_softc.lock);
1826
1827	cs->cs_waiting_for_ctld = false;
1828	cs->cs_login_phase = true;
1829
1830	ciap->connection_id = cs->cs_id;
1831	ciap->portal_id = cs->cs_portal_id;
1832	ciap->initiator_addrlen = cs->cs_initiator_sa->sa_len;
1833	error = copyout(cs->cs_initiator_sa, ciap->initiator_addr,
1834	    cs->cs_initiator_sa->sa_len);
1835	if (error != 0) {
1836		snprintf(ci->error_str, sizeof(ci->error_str),
1837		    "copyout failed with error %d", error);
1838		ci->status = CTL_ISCSI_ERROR;
1839		return;
1840	}
1841
1842	ci->status = CTL_ISCSI_OK;
1843}
1844
1845static void
1846cfiscsi_ioctl_send(struct ctl_iscsi *ci)
1847{
1848	struct ctl_iscsi_send_params *cisp;
1849	struct cfiscsi_session *cs;
1850	struct icl_pdu *ip;
1851	size_t datalen;
1852	void *data;
1853	int error;
1854
1855	cisp = (struct ctl_iscsi_send_params *)&(ci->data);
1856
1857	mtx_lock(&cfiscsi_softc.lock);
1858	TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) {
1859		if (cs->cs_id == cisp->connection_id)
1860			break;
1861	}
1862	if (cs == NULL) {
1863		mtx_unlock(&cfiscsi_softc.lock);
1864		snprintf(ci->error_str, sizeof(ci->error_str), "connection not found");
1865		ci->status = CTL_ISCSI_ERROR;
1866		return;
1867	}
1868	mtx_unlock(&cfiscsi_softc.lock);
1869
1870#if 0
1871	if (cs->cs_login_phase == false)
1872		return (EBUSY);
1873#endif
1874
1875	if (cs->cs_terminating) {
1876		snprintf(ci->error_str, sizeof(ci->error_str), "connection is terminating");
1877		ci->status = CTL_ISCSI_ERROR;
1878		return;
1879	}
1880
1881	datalen = cisp->data_segment_len;
1882	/*
1883	 * XXX
1884	 */
1885	//if (datalen > CFISCSI_MAX_DATA_SEGMENT_LENGTH) {
1886	if (datalen > 65535) {
1887		snprintf(ci->error_str, sizeof(ci->error_str), "data segment too big");
1888		ci->status = CTL_ISCSI_ERROR;
1889		return;
1890	}
1891	if (datalen > 0) {
1892		data = malloc(datalen, M_CFISCSI, M_WAITOK);
1893		error = copyin(cisp->data_segment, data, datalen);
1894		if (error != 0) {
1895			free(data, M_CFISCSI);
1896			snprintf(ci->error_str, sizeof(ci->error_str), "copyin error %d", error);
1897			ci->status = CTL_ISCSI_ERROR;
1898			return;
1899		}
1900	}
1901
1902	ip = icl_pdu_new_bhs(cs->cs_conn, M_WAITOK);
1903	memcpy(ip->ip_bhs, cisp->bhs, sizeof(*ip->ip_bhs));
1904	if (datalen > 0) {
1905		icl_pdu_append_data(ip, data, datalen, M_WAITOK);
1906		free(data, M_CFISCSI);
1907	}
1908	CFISCSI_SESSION_LOCK(cs);
1909	icl_pdu_queue(ip);
1910	CFISCSI_SESSION_UNLOCK(cs);
1911	ci->status = CTL_ISCSI_OK;
1912}
1913
1914static void
1915cfiscsi_ioctl_receive(struct ctl_iscsi *ci)
1916{
1917	struct ctl_iscsi_receive_params *cirp;
1918	struct cfiscsi_session *cs;
1919	struct icl_pdu *ip;
1920	void *data;
1921	int error;
1922
1923	cirp = (struct ctl_iscsi_receive_params *)&(ci->data);
1924
1925	mtx_lock(&cfiscsi_softc.lock);
1926	TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) {
1927		if (cs->cs_id == cirp->connection_id)
1928			break;
1929	}
1930	if (cs == NULL) {
1931		mtx_unlock(&cfiscsi_softc.lock);
1932		snprintf(ci->error_str, sizeof(ci->error_str),
1933		    "connection not found");
1934		ci->status = CTL_ISCSI_ERROR;
1935		return;
1936	}
1937	mtx_unlock(&cfiscsi_softc.lock);
1938
1939#if 0
1940	if (is->is_login_phase == false)
1941		return (EBUSY);
1942#endif
1943
1944	CFISCSI_SESSION_LOCK(cs);
1945	while (cs->cs_login_pdu == NULL && cs->cs_terminating == false) {
1946		error = cv_wait_sig(&cs->cs_login_cv, &cs->cs_lock);
1947		if (error != 0) {
1948			CFISCSI_SESSION_UNLOCK(cs);
1949			snprintf(ci->error_str, sizeof(ci->error_str),
1950			    "interrupted by signal");
1951			ci->status = CTL_ISCSI_ERROR;
1952			return;
1953		}
1954	}
1955
1956	if (cs->cs_terminating) {
1957		CFISCSI_SESSION_UNLOCK(cs);
1958		snprintf(ci->error_str, sizeof(ci->error_str),
1959		    "connection terminating");
1960		ci->status = CTL_ISCSI_ERROR;
1961		return;
1962	}
1963	ip = cs->cs_login_pdu;
1964	cs->cs_login_pdu = NULL;
1965	CFISCSI_SESSION_UNLOCK(cs);
1966
1967	if (ip->ip_data_len > cirp->data_segment_len) {
1968		icl_pdu_free(ip);
1969		snprintf(ci->error_str, sizeof(ci->error_str),
1970		    "data segment too big");
1971		ci->status = CTL_ISCSI_ERROR;
1972		return;
1973	}
1974
1975	copyout(ip->ip_bhs, cirp->bhs, sizeof(*ip->ip_bhs));
1976	if (ip->ip_data_len > 0) {
1977		data = malloc(ip->ip_data_len, M_CFISCSI, M_WAITOK);
1978		icl_pdu_get_data(ip, 0, data, ip->ip_data_len);
1979		copyout(data, cirp->data_segment, ip->ip_data_len);
1980		free(data, M_CFISCSI);
1981	}
1982
1983	icl_pdu_free(ip);
1984	ci->status = CTL_ISCSI_OK;
1985}
1986
1987#endif /* !ICL_KERNEL_PROXY */
1988
1989static int
1990cfiscsi_ioctl(struct cdev *dev,
1991    u_long cmd, caddr_t addr, int flag, struct thread *td)
1992{
1993	struct ctl_iscsi *ci;
1994
1995	if (cmd != CTL_ISCSI)
1996		return (ENOTTY);
1997
1998	ci = (struct ctl_iscsi *)addr;
1999	switch (ci->type) {
2000	case CTL_ISCSI_HANDOFF:
2001		cfiscsi_ioctl_handoff(ci);
2002		break;
2003	case CTL_ISCSI_LIST:
2004		cfiscsi_ioctl_list(ci);
2005		break;
2006	case CTL_ISCSI_TERMINATE:
2007		cfiscsi_ioctl_terminate(ci);
2008		break;
2009	case CTL_ISCSI_LOGOUT:
2010		cfiscsi_ioctl_logout(ci);
2011		break;
2012#ifdef ICL_KERNEL_PROXY
2013	case CTL_ISCSI_LISTEN:
2014		cfiscsi_ioctl_listen(ci);
2015		break;
2016	case CTL_ISCSI_ACCEPT:
2017		cfiscsi_ioctl_accept(ci);
2018		break;
2019	case CTL_ISCSI_SEND:
2020		cfiscsi_ioctl_send(ci);
2021		break;
2022	case CTL_ISCSI_RECEIVE:
2023		cfiscsi_ioctl_receive(ci);
2024		break;
2025#else
2026	case CTL_ISCSI_LISTEN:
2027	case CTL_ISCSI_ACCEPT:
2028	case CTL_ISCSI_SEND:
2029	case CTL_ISCSI_RECEIVE:
2030		ci->status = CTL_ISCSI_ERROR;
2031		snprintf(ci->error_str, sizeof(ci->error_str),
2032		    "%s: CTL compiled without ICL_KERNEL_PROXY",
2033		    __func__);
2034		break;
2035#endif /* !ICL_KERNEL_PROXY */
2036	default:
2037		ci->status = CTL_ISCSI_ERROR;
2038		snprintf(ci->error_str, sizeof(ci->error_str),
2039		    "%s: invalid iSCSI request type %d", __func__, ci->type);
2040		break;
2041	}
2042
2043	return (0);
2044}
2045
2046static int
2047cfiscsi_devid(struct ctl_scsiio *ctsio, int alloc_len)
2048{
2049	struct cfiscsi_session *cs;
2050	struct scsi_vpd_device_id *devid_ptr;
2051	struct scsi_vpd_id_descriptor *desc, *desc1, *desc2, *desc3, *desc4;
2052	struct scsi_vpd_id_descriptor *desc5;
2053	struct scsi_vpd_id_t10 *t10id;
2054	struct ctl_lun *lun;
2055	const struct icl_pdu *request;
2056	int i, ret;
2057	char *val;
2058	size_t data_len, devid_len, wwnn_len, wwpn_len, lun_name_len;
2059
2060	lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
2061	request = ctsio->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2062	cs = PDU_SESSION(request);
2063
2064	wwpn_len = strlen(cs->cs_target->ct_name);
2065	wwpn_len += strlen(",t,0x0001");
2066	wwpn_len += 1; /* '\0' */
2067	if ((wwpn_len % 4) != 0)
2068		wwpn_len += (4 - (wwpn_len % 4));
2069
2070	wwnn_len = strlen(cs->cs_target->ct_name);
2071	wwnn_len += 1; /* '\0' */
2072	if ((wwnn_len % 4) != 0)
2073		wwnn_len += (4 - (wwnn_len % 4));
2074
2075	if (lun == NULL) {
2076		devid_len = CTL_DEVID_MIN_LEN;
2077		lun_name_len = 0;
2078	} else {
2079		devid_len = max(CTL_DEVID_MIN_LEN,
2080		    strnlen(lun->be_lun->device_id, CTL_DEVID_LEN));
2081		lun_name_len = strlen(cs->cs_target->ct_name);
2082		lun_name_len += strlen(",lun,XXXXXXXX");
2083		lun_name_len += 1; /* '\0' */
2084		if ((lun_name_len % 4) != 0)
2085			lun_name_len += (4 - (lun_name_len % 4));
2086	}
2087
2088	data_len = sizeof(struct scsi_vpd_device_id) +
2089		sizeof(struct scsi_vpd_id_descriptor) +
2090		sizeof(struct scsi_vpd_id_t10) + devid_len +
2091		sizeof(struct scsi_vpd_id_descriptor) + lun_name_len +
2092		sizeof(struct scsi_vpd_id_descriptor) + wwnn_len +
2093		sizeof(struct scsi_vpd_id_descriptor) + wwpn_len +
2094		sizeof(struct scsi_vpd_id_descriptor) +
2095		sizeof(struct scsi_vpd_id_rel_trgt_port_id) +
2096		sizeof(struct scsi_vpd_id_descriptor) +
2097		sizeof(struct scsi_vpd_id_trgt_port_grp_id);
2098
2099	ctsio->kern_data_ptr = malloc(data_len, M_CTL, M_WAITOK | M_ZERO);
2100	devid_ptr = (struct scsi_vpd_device_id *)ctsio->kern_data_ptr;
2101	ctsio->kern_sg_entries = 0;
2102
2103	if (data_len < alloc_len) {
2104		ctsio->residual = alloc_len - data_len;
2105		ctsio->kern_data_len = data_len;
2106		ctsio->kern_total_len = data_len;
2107	} else {
2108		ctsio->residual = 0;
2109		ctsio->kern_data_len = alloc_len;
2110		ctsio->kern_total_len = alloc_len;
2111	}
2112	ctsio->kern_data_resid = 0;
2113	ctsio->kern_rel_offset = 0;
2114	ctsio->kern_sg_entries = 0;
2115
2116	desc = (struct scsi_vpd_id_descriptor *)devid_ptr->desc_list;
2117	t10id = (struct scsi_vpd_id_t10 *)&desc->identifier[0];
2118	desc1 = (struct scsi_vpd_id_descriptor *)(&desc->identifier[0] +
2119	    sizeof(struct scsi_vpd_id_t10) + devid_len);
2120	desc2 = (struct scsi_vpd_id_descriptor *)(&desc1->identifier[0] +
2121	    lun_name_len);
2122	desc3 = (struct scsi_vpd_id_descriptor *)(&desc2->identifier[0] +
2123	    wwnn_len);
2124	desc4 = (struct scsi_vpd_id_descriptor *)(&desc3->identifier[0] +
2125	    wwpn_len);
2126	desc5 = (struct scsi_vpd_id_descriptor *)(&desc4->identifier[0] +
2127	    sizeof(struct scsi_vpd_id_rel_trgt_port_id));
2128
2129	if (lun != NULL)
2130		devid_ptr->device = (SID_QUAL_LU_CONNECTED << 5) |
2131		    lun->be_lun->lun_type;
2132	else
2133		devid_ptr->device = (SID_QUAL_LU_OFFLINE << 5) | T_DIRECT;
2134
2135	devid_ptr->page_code = SVPD_DEVICE_ID;
2136
2137	scsi_ulto2b(data_len - 4, devid_ptr->length);
2138
2139	/*
2140	 * We're using a LUN association here.  i.e., this device ID is a
2141	 * per-LUN identifier.
2142	 */
2143	desc->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_ASCII;
2144	desc->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_LUN | SVPD_ID_TYPE_T10;
2145	desc->length = sizeof(*t10id) + devid_len;
2146	if (lun == NULL || (val = ctl_get_opt(lun->be_lun, "vendor")) == NULL) {
2147		strncpy((char *)t10id->vendor, CTL_VENDOR, sizeof(t10id->vendor));
2148	} else {
2149		memset(t10id->vendor, ' ', sizeof(t10id->vendor));
2150		strncpy(t10id->vendor, val,
2151		    min(sizeof(t10id->vendor), strlen(val)));
2152	}
2153
2154	/*
2155	 * If we've actually got a backend, copy the device id from the
2156	 * per-LUN data.  Otherwise, set it to all spaces.
2157	 */
2158	if (lun != NULL) {
2159		/*
2160		 * Copy the backend's LUN ID.
2161		 */
2162		strncpy((char *)t10id->vendor_spec_id,
2163		    (char *)lun->be_lun->device_id, devid_len);
2164	} else {
2165		/*
2166		 * No backend, set this to spaces.
2167		 */
2168		memset(t10id->vendor_spec_id, 0x20, devid_len);
2169	}
2170
2171	/*
2172	 * desc1 is for the unique LUN name.
2173	 *
2174	 * XXX: According to SPC-3, LUN must report the same ID through
2175	 *      all the ports.  The code below, however, reports the
2176	 *      ID only via iSCSI.
2177	 */
2178	desc1->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_UTF8;
2179	desc1->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_LUN |
2180		SVPD_ID_TYPE_SCSI_NAME;
2181	desc1->length = lun_name_len;
2182	if (lun != NULL) {
2183		/*
2184		 * Find the per-target LUN number.
2185		 */
2186		for (i = 0; i < CTL_MAX_LUNS; i++) {
2187			if (cs->cs_target->ct_luns[i] == lun->lun)
2188				break;
2189		}
2190		KASSERT(i < CTL_MAX_LUNS,
2191		    ("lun %jd not found", (uintmax_t)lun->lun));
2192		ret = snprintf(desc1->identifier, lun_name_len, "%s,lun,%d",
2193		    cs->cs_target->ct_name, i);
2194		KASSERT(ret > 0 && ret <= lun_name_len, ("bad snprintf"));
2195	} else {
2196		KASSERT(lun_name_len == 0, ("no lun, but lun_name_len != 0"));
2197	}
2198
2199	/*
2200	 * desc2 is for the Target Name.
2201	 */
2202	desc2->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_UTF8;
2203	desc2->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_TARGET |
2204	    SVPD_ID_TYPE_SCSI_NAME;
2205	desc2->length = wwnn_len;
2206	snprintf(desc2->identifier, wwnn_len, "%s", cs->cs_target->ct_name);
2207
2208	/*
2209	 * desc3 is for the WWPN which is a port asscociation.
2210	 */
2211	desc3->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_UTF8;
2212	desc3->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT |
2213	    SVPD_ID_TYPE_SCSI_NAME;
2214	desc3->length = wwpn_len;
2215	snprintf(desc3->identifier, wwpn_len, "%s,t,0x%4.4x",
2216	    cs->cs_target->ct_name, cs->cs_portal_group_tag);
2217
2218	/*
2219	 * desc3 is for the Relative Target Port(type 4h) identifier
2220	 */
2221	desc4->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_BINARY;
2222	desc4->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT |
2223	    SVPD_ID_TYPE_RELTARG;
2224	desc4->length = 4;
2225	desc4->identifier[3] = 1;
2226
2227	/*
2228	 * desc4 is for the Target Port Group(type 5h) identifier
2229	 */
2230	desc5->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_BINARY;
2231	desc5->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT |
2232	    SVPD_ID_TYPE_TPORTGRP;
2233	desc5->length = 4;
2234	desc5->identifier[3] = 1;
2235
2236	ctsio->scsi_status = SCSI_STATUS_OK;
2237
2238	ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
2239	ctsio->be_move_done = ctl_config_move_done;
2240	ctl_datamove((union ctl_io *)ctsio);
2241
2242	return (CTL_RETVAL_COMPLETE);
2243}
2244
2245static void
2246cfiscsi_target_hold(struct cfiscsi_target *ct)
2247{
2248
2249	refcount_acquire(&ct->ct_refcount);
2250}
2251
2252static void
2253cfiscsi_target_release(struct cfiscsi_target *ct)
2254{
2255	struct cfiscsi_softc *softc;
2256
2257	softc = ct->ct_softc;
2258	mtx_lock(&softc->lock);
2259	if (refcount_release(&ct->ct_refcount)) {
2260		TAILQ_REMOVE(&softc->targets, ct, ct_next);
2261		mtx_unlock(&softc->lock);
2262		free(ct, M_CFISCSI);
2263
2264		return;
2265	}
2266	mtx_unlock(&softc->lock);
2267}
2268
2269static struct cfiscsi_target *
2270cfiscsi_target_find(struct cfiscsi_softc *softc, const char *name)
2271{
2272	struct cfiscsi_target *ct;
2273
2274	mtx_lock(&softc->lock);
2275	TAILQ_FOREACH(ct, &softc->targets, ct_next) {
2276		if (strcmp(name, ct->ct_name) != 0)
2277			continue;
2278		cfiscsi_target_hold(ct);
2279		mtx_unlock(&softc->lock);
2280		return (ct);
2281	}
2282	mtx_unlock(&softc->lock);
2283
2284	return (NULL);
2285}
2286
2287static struct cfiscsi_target *
2288cfiscsi_target_find_or_create(struct cfiscsi_softc *softc, const char *name,
2289    const char *alias)
2290{
2291	struct cfiscsi_target *ct, *newct;
2292	int i;
2293
2294	if (name[0] == '\0' || strlen(name) >= CTL_ISCSI_NAME_LEN)
2295		return (NULL);
2296
2297	newct = malloc(sizeof(*newct), M_CFISCSI, M_WAITOK | M_ZERO);
2298
2299	mtx_lock(&softc->lock);
2300	TAILQ_FOREACH(ct, &softc->targets, ct_next) {
2301		if (strcmp(name, ct->ct_name) != 0)
2302			continue;
2303		cfiscsi_target_hold(ct);
2304		mtx_unlock(&softc->lock);
2305		free(newct, M_CFISCSI);
2306		return (ct);
2307	}
2308
2309	for (i = 0; i < CTL_MAX_LUNS; i++)
2310		newct->ct_luns[i] = -1;
2311
2312	strlcpy(newct->ct_name, name, sizeof(newct->ct_name));
2313	if (alias != NULL)
2314		strlcpy(newct->ct_alias, alias, sizeof(newct->ct_alias));
2315	refcount_init(&newct->ct_refcount, 1);
2316	newct->ct_softc = softc;
2317	TAILQ_INSERT_TAIL(&softc->targets, newct, ct_next);
2318	mtx_unlock(&softc->lock);
2319
2320	return (newct);
2321}
2322
2323/*
2324 * Takes LUN from the target space and returns LUN from the CTL space.
2325 */
2326static uint32_t
2327cfiscsi_map_lun(void *arg, uint32_t lun)
2328{
2329	struct cfiscsi_session *cs;
2330
2331	cs = arg;
2332
2333	if (lun >= CTL_MAX_LUNS) {
2334		CFISCSI_DEBUG("requested lun number %d is higher "
2335		    "than maximum %d", lun, CTL_MAX_LUNS - 1);
2336		return (0xffffffff);
2337	}
2338
2339	if (cs->cs_target->ct_luns[lun] < 0)
2340		return (0xffffffff);
2341
2342	return (cs->cs_target->ct_luns[lun]);
2343}
2344
2345static int
2346cfiscsi_target_set_lun(struct cfiscsi_target *ct,
2347    unsigned long lun_id, unsigned long ctl_lun_id)
2348{
2349
2350	if (lun_id >= CTL_MAX_LUNS) {
2351		CFISCSI_WARN("requested lun number %ld is higher "
2352		    "than maximum %d", lun_id, CTL_MAX_LUNS - 1);
2353		return (-1);
2354	}
2355
2356	if (ct->ct_luns[lun_id] >= 0) {
2357		/*
2358		 * CTL calls cfiscsi_lun_enable() twice for each LUN - once
2359		 * when the LUN is created, and a second time just before
2360		 * the port is brought online; don't emit warnings
2361		 * for that case.
2362		 */
2363		if (ct->ct_luns[lun_id] == ctl_lun_id)
2364			return (0);
2365		CFISCSI_WARN("lun %ld already allocated", lun_id);
2366		return (-1);
2367	}
2368
2369#if 0
2370	CFISCSI_DEBUG("adding mapping for lun %ld, target %s "
2371	    "to ctl lun %ld", lun_id, ct->ct_name, ctl_lun_id);
2372#endif
2373
2374	ct->ct_luns[lun_id] = ctl_lun_id;
2375	cfiscsi_target_hold(ct);
2376
2377	return (0);
2378}
2379
2380static int
2381cfiscsi_target_unset_lun(struct cfiscsi_target *ct, unsigned long lun_id)
2382{
2383
2384	if (ct->ct_luns[lun_id] < 0) {
2385		CFISCSI_WARN("lun %ld not allocated", lun_id);
2386		return (-1);
2387	}
2388
2389	ct->ct_luns[lun_id] = -1;
2390	cfiscsi_target_release(ct);
2391
2392	return (0);
2393}
2394
2395static int
2396cfiscsi_lun_enable(void *arg, struct ctl_id target_id, int lun_id)
2397{
2398	struct cfiscsi_softc *softc;
2399	struct cfiscsi_target *ct;
2400	const char *target = NULL, *target_alias = NULL;
2401	const char *lun = NULL;
2402	unsigned long tmp;
2403
2404	softc = (struct cfiscsi_softc *)arg;
2405
2406	target = ctl_get_opt(control_softc->ctl_luns[lun_id]->be_lun,
2407	    "cfiscsi_target");
2408	target_alias = ctl_get_opt(control_softc->ctl_luns[lun_id]->be_lun,
2409	    "cfiscsi_target_alias");
2410	lun = ctl_get_opt(control_softc->ctl_luns[lun_id]->be_lun,
2411	    "cfiscsi_lun");
2412
2413	if (target == NULL && lun == NULL)
2414		return (0);
2415
2416	if (target == NULL || lun == NULL) {
2417		CFISCSI_WARN("lun added with cfiscsi_target, but without "
2418		    "cfiscsi_lun, or the other way around; ignoring");
2419		return (0);
2420	}
2421
2422	ct = cfiscsi_target_find_or_create(softc, target, target_alias);
2423	if (ct == NULL) {
2424		CFISCSI_WARN("failed to create target \"%s\"", target);
2425		return (0);
2426	}
2427
2428	tmp = strtoul(lun, NULL, 10);
2429	cfiscsi_target_set_lun(ct, tmp, lun_id);
2430	cfiscsi_target_release(ct);
2431	return (0);
2432}
2433
2434static int
2435cfiscsi_lun_disable(void *arg, struct ctl_id target_id, int lun_id)
2436{
2437	struct cfiscsi_softc *softc;
2438	struct cfiscsi_target *ct;
2439	int i;
2440
2441	softc = (struct cfiscsi_softc *)arg;
2442
2443	mtx_lock(&softc->lock);
2444	TAILQ_FOREACH(ct, &softc->targets, ct_next) {
2445		for (i = 0; i < CTL_MAX_LUNS; i++) {
2446			if (ct->ct_luns[i] < 0)
2447				continue;
2448			if (ct->ct_luns[i] != lun_id)
2449				continue;
2450			mtx_unlock(&softc->lock);
2451			cfiscsi_target_unset_lun(ct, i);
2452			return (0);
2453		}
2454	}
2455	mtx_unlock(&softc->lock);
2456	return (0);
2457}
2458
2459static void
2460cfiscsi_datamove_in(union ctl_io *io)
2461{
2462	struct cfiscsi_session *cs;
2463	struct icl_pdu *request, *response;
2464	const struct iscsi_bhs_scsi_command *bhssc;
2465	struct iscsi_bhs_data_in *bhsdi;
2466	struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
2467	size_t len, expected_len, sg_len, buffer_offset;
2468	const char *sg_addr;
2469	int ctl_sg_count, error, i;
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	if (io->scsiio.kern_sg_entries > 0) {
2480		ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
2481		ctl_sg_count = io->scsiio.kern_sg_entries;
2482	} else {
2483		ctl_sglist = &ctl_sg_entry;
2484		ctl_sglist->addr = io->scsiio.kern_data_ptr;
2485		ctl_sglist->len = io->scsiio.kern_data_len;
2486		ctl_sg_count = 1;
2487	}
2488
2489	/*
2490	 * This is the total amount of data to be transferred within the current
2491	 * SCSI command.  We need to record it so that we can properly report
2492	 * underflow/underflow.
2493	 */
2494	PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len;
2495
2496	/*
2497	 * This is the offset within the current SCSI command; for the first
2498	 * call to cfiscsi_datamove() it will be 0, and for subsequent ones
2499	 * it will be the sum of lengths of previous ones.
2500	 */
2501	buffer_offset = io->scsiio.kern_rel_offset;
2502
2503	/*
2504	 * This is the transfer length expected by the initiator.  In theory,
2505	 * it could be different from the correct amount of data from the SCSI
2506	 * point of view, even if that doesn't make any sense.
2507	 */
2508	expected_len = ntohl(bhssc->bhssc_expected_data_transfer_length);
2509#if 0
2510	if (expected_len != io->scsiio.kern_total_len) {
2511		CFISCSI_SESSION_DEBUG(cs, "expected transfer length %zd, "
2512		    "actual length %zd", expected_len,
2513		    (size_t)io->scsiio.kern_total_len);
2514	}
2515#endif
2516
2517	if (buffer_offset >= expected_len) {
2518#if 0
2519		CFISCSI_SESSION_DEBUG(cs, "buffer_offset = %zd, "
2520		    "already sent the expected len", buffer_offset);
2521#endif
2522		io->scsiio.be_move_done(io);
2523		return;
2524	}
2525
2526	i = 0;
2527	sg_addr = NULL;
2528	sg_len = 0;
2529	response = NULL;
2530	bhsdi = NULL;
2531	for (;;) {
2532		if (response == NULL) {
2533			response = cfiscsi_pdu_new_response(request, M_NOWAIT);
2534			if (response == NULL) {
2535				CFISCSI_SESSION_WARN(cs, "failed to "
2536				    "allocate memory; dropping connection");
2537				ctl_set_busy(&io->scsiio);
2538				io->scsiio.be_move_done(io);
2539				cfiscsi_session_terminate(cs);
2540				return;
2541			}
2542			bhsdi = (struct iscsi_bhs_data_in *)response->ip_bhs;
2543			bhsdi->bhsdi_opcode = ISCSI_BHS_OPCODE_SCSI_DATA_IN;
2544			bhsdi->bhsdi_initiator_task_tag =
2545			    bhssc->bhssc_initiator_task_tag;
2546			bhsdi->bhsdi_datasn = htonl(PDU_EXPDATASN(request));
2547			PDU_EXPDATASN(request)++;
2548			bhsdi->bhsdi_buffer_offset = htonl(buffer_offset);
2549		}
2550
2551		KASSERT(i < ctl_sg_count, ("i >= ctl_sg_count"));
2552		if (sg_len == 0) {
2553			sg_addr = ctl_sglist[i].addr;
2554			sg_len = ctl_sglist[i].len;
2555			KASSERT(sg_len > 0, ("sg_len <= 0"));
2556		}
2557
2558		len = sg_len;
2559
2560		/*
2561		 * Truncate to maximum data segment length.
2562		 */
2563		KASSERT(response->ip_data_len < cs->cs_max_data_segment_length,
2564		    ("ip_data_len %zd >= max_data_segment_length %zd",
2565		    response->ip_data_len, cs->cs_max_data_segment_length));
2566		if (response->ip_data_len + len >
2567		    cs->cs_max_data_segment_length) {
2568			len = cs->cs_max_data_segment_length -
2569			    response->ip_data_len;
2570			KASSERT(len <= sg_len, ("len %zd > sg_len %zd",
2571			    len, sg_len));
2572		}
2573
2574		/*
2575		 * Truncate to expected data transfer length.
2576		 */
2577		KASSERT(buffer_offset + response->ip_data_len < expected_len,
2578		    ("buffer_offset %zd + ip_data_len %zd >= expected_len %zd",
2579		    buffer_offset, response->ip_data_len, expected_len));
2580		if (buffer_offset + response->ip_data_len + len > expected_len) {
2581			CFISCSI_SESSION_DEBUG(cs, "truncating from %zd "
2582			    "to expected data transfer length %zd",
2583			    buffer_offset + response->ip_data_len + len, expected_len);
2584			len = expected_len - (buffer_offset + response->ip_data_len);
2585			KASSERT(len <= sg_len, ("len %zd > sg_len %zd",
2586			    len, sg_len));
2587		}
2588
2589		error = icl_pdu_append_data(response, sg_addr, len, M_NOWAIT);
2590		if (error != 0) {
2591			CFISCSI_SESSION_WARN(cs, "failed to "
2592			    "allocate memory; dropping connection");
2593			icl_pdu_free(response);
2594			ctl_set_busy(&io->scsiio);
2595			io->scsiio.be_move_done(io);
2596			cfiscsi_session_terminate(cs);
2597			return;
2598		}
2599		sg_addr += len;
2600		sg_len -= len;
2601
2602		KASSERT(buffer_offset + request->ip_data_len <= expected_len,
2603		    ("buffer_offset %zd + ip_data_len %zd > expected_len %zd",
2604		    buffer_offset, request->ip_data_len, expected_len));
2605		if (buffer_offset + request->ip_data_len == expected_len) {
2606			/*
2607			 * Already have the amount of data the initiator wanted.
2608			 */
2609			break;
2610		}
2611
2612		if (sg_len == 0) {
2613			/*
2614			 * End of scatter-gather segment;
2615			 * proceed to the next one...
2616			 */
2617			if (i == ctl_sg_count - 1) {
2618				/*
2619				 * ... unless this was the last one.
2620				 */
2621				break;
2622			}
2623			i++;
2624		}
2625
2626		if (response->ip_data_len == cs->cs_max_data_segment_length) {
2627			/*
2628			 * Can't stuff more data into the current PDU;
2629			 * queue it.  Note that's not enough to check
2630			 * for kern_data_resid == 0 instead; there
2631			 * may be several Data-In PDUs for the final
2632			 * call to cfiscsi_datamove(), and we want
2633			 * to set the F flag only on the last of them.
2634			 */
2635			buffer_offset += response->ip_data_len;
2636			if (buffer_offset == io->scsiio.kern_total_len ||
2637			    buffer_offset == expected_len)
2638				bhsdi->bhsdi_flags |= BHSDI_FLAGS_F;
2639			cfiscsi_pdu_queue(response);
2640			response = NULL;
2641			bhsdi = NULL;
2642		}
2643	}
2644	if (response != NULL) {
2645		buffer_offset += response->ip_data_len;
2646		if (buffer_offset == io->scsiio.kern_total_len ||
2647		    buffer_offset == expected_len)
2648			bhsdi->bhsdi_flags |= BHSDI_FLAGS_F;
2649		KASSERT(response->ip_data_len > 0, ("sending empty Data-In"));
2650		cfiscsi_pdu_queue(response);
2651	}
2652
2653	io->scsiio.be_move_done(io);
2654}
2655
2656static void
2657cfiscsi_datamove_out(union ctl_io *io)
2658{
2659	struct cfiscsi_session *cs;
2660	struct icl_pdu *request, *response;
2661	const struct iscsi_bhs_scsi_command *bhssc;
2662	struct iscsi_bhs_r2t *bhsr2t;
2663	struct cfiscsi_data_wait *cdw;
2664	uint32_t target_transfer_tag;
2665	bool done;
2666
2667	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2668	cs = PDU_SESSION(request);
2669
2670	bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs;
2671	KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
2672	    ISCSI_BHS_OPCODE_SCSI_COMMAND,
2673	    ("bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_COMMAND"));
2674
2675	/*
2676	 * We need to record it so that we can properly report
2677	 * underflow/underflow.
2678	 */
2679	PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len;
2680
2681	/*
2682	 * We hadn't received anything during this datamove yet.
2683	 */
2684	io->scsiio.ext_data_filled = 0;
2685
2686	target_transfer_tag =
2687	    atomic_fetchadd_32(&cs->cs_target_transfer_tag, 1);
2688
2689#if 0
2690	CFISCSI_SESSION_DEBUG(cs, "expecting Data-Out with initiator "
2691	    "task tag 0x%x, target transfer tag 0x%x",
2692	    bhssc->bhssc_initiator_task_tag, target_transfer_tag);
2693#endif
2694	cdw = uma_zalloc(cfiscsi_data_wait_zone, M_NOWAIT | M_ZERO);
2695	if (cdw == NULL) {
2696		CFISCSI_SESSION_WARN(cs, "failed to "
2697		    "allocate memory; dropping connection");
2698		ctl_set_busy(&io->scsiio);
2699		io->scsiio.be_move_done(io);
2700		cfiscsi_session_terminate(cs);
2701		return;
2702	}
2703	cdw->cdw_ctl_io = io;
2704	cdw->cdw_target_transfer_tag = target_transfer_tag;
2705	cdw->cdw_initiator_task_tag = bhssc->bhssc_initiator_task_tag;
2706
2707	if (cs->cs_immediate_data && io->scsiio.kern_rel_offset <
2708	    icl_pdu_data_segment_length(request)) {
2709		done = cfiscsi_handle_data_segment(request, cdw);
2710		if (done) {
2711			uma_zfree(cfiscsi_data_wait_zone, cdw);
2712			io->scsiio.be_move_done(io);
2713			return;
2714		}
2715	}
2716
2717	CFISCSI_SESSION_LOCK(cs);
2718	TAILQ_INSERT_TAIL(&cs->cs_waiting_for_data_out, cdw, cdw_next);
2719	CFISCSI_SESSION_UNLOCK(cs);
2720
2721	/*
2722	 * XXX: We should limit the number of outstanding R2T PDUs
2723	 * 	per task to MaxOutstandingR2T.
2724	 */
2725	response = cfiscsi_pdu_new_response(request, M_NOWAIT);
2726	if (response == NULL) {
2727		CFISCSI_SESSION_WARN(cs, "failed to "
2728		    "allocate memory; dropping connection");
2729		ctl_set_busy(&io->scsiio);
2730		io->scsiio.be_move_done(io);
2731		cfiscsi_session_terminate(cs);
2732		return;
2733	}
2734	bhsr2t = (struct iscsi_bhs_r2t *)response->ip_bhs;
2735	bhsr2t->bhsr2t_opcode = ISCSI_BHS_OPCODE_R2T;
2736	bhsr2t->bhsr2t_flags = 0x80;
2737	bhsr2t->bhsr2t_lun = bhssc->bhssc_lun;
2738	bhsr2t->bhsr2t_initiator_task_tag = bhssc->bhssc_initiator_task_tag;
2739	bhsr2t->bhsr2t_target_transfer_tag = target_transfer_tag;
2740	/*
2741	 * XXX: Here we assume that cfiscsi_datamove() won't ever
2742	 *	be running concurrently on several CPUs for a given
2743	 *	command.
2744	 */
2745	bhsr2t->bhsr2t_r2tsn = htonl(PDU_R2TSN(request));
2746	PDU_R2TSN(request)++;
2747	/*
2748	 * This is the offset within the current SCSI command;
2749	 * i.e. for the first call of datamove(), it will be 0,
2750	 * and for subsequent ones it will be the sum of lengths
2751	 * of previous ones.
2752	 *
2753	 * The ext_data_filled is to account for unsolicited
2754	 * (immediate) data that might have already arrived.
2755	 */
2756	bhsr2t->bhsr2t_buffer_offset =
2757	    htonl(io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled);
2758	/*
2759	 * This is the total length (sum of S/G lengths) this call
2760	 * to cfiscsi_datamove() is supposed to handle.
2761	 *
2762	 * XXX: Limit it to MaxBurstLength.
2763	 */
2764	bhsr2t->bhsr2t_desired_data_transfer_length =
2765	    htonl(io->scsiio.kern_data_len - io->scsiio.ext_data_filled);
2766	cfiscsi_pdu_queue(response);
2767}
2768
2769static void
2770cfiscsi_datamove(union ctl_io *io)
2771{
2772
2773	if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN)
2774		cfiscsi_datamove_in(io);
2775	else
2776		cfiscsi_datamove_out(io);
2777}
2778
2779static void
2780cfiscsi_scsi_command_done(union ctl_io *io)
2781{
2782	struct icl_pdu *request, *response;
2783	struct iscsi_bhs_scsi_command *bhssc;
2784	struct iscsi_bhs_scsi_response *bhssr;
2785#ifdef DIAGNOSTIC
2786	struct cfiscsi_data_wait *cdw;
2787#endif
2788	struct cfiscsi_session *cs;
2789	uint16_t sense_length;
2790
2791	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2792	cs = PDU_SESSION(request);
2793	bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs;
2794	KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
2795	    ISCSI_BHS_OPCODE_SCSI_COMMAND,
2796	    ("replying to wrong opcode 0x%x", bhssc->bhssc_opcode));
2797
2798	//CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x",
2799	//    bhssc->bhssc_initiator_task_tag);
2800
2801#ifdef DIAGNOSTIC
2802	CFISCSI_SESSION_LOCK(cs);
2803	TAILQ_FOREACH(cdw, &cs->cs_waiting_for_data_out, cdw_next)
2804		KASSERT(bhssc->bhssc_initiator_task_tag !=
2805		    cdw->cdw_initiator_task_tag, ("dangling cdw"));
2806	CFISCSI_SESSION_UNLOCK(cs);
2807#endif
2808
2809	response = cfiscsi_pdu_new_response(request, M_WAITOK);
2810	bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs;
2811	bhssr->bhssr_opcode = ISCSI_BHS_OPCODE_SCSI_RESPONSE;
2812	bhssr->bhssr_flags = 0x80;
2813	/*
2814	 * XXX: We don't deal with bidirectional under/overflows;
2815	 *	does anything actually support those?
2816	 */
2817	if (PDU_TOTAL_TRANSFER_LEN(request) <
2818	    ntohl(bhssc->bhssc_expected_data_transfer_length)) {
2819		bhssr->bhssr_flags |= BHSSR_FLAGS_RESIDUAL_UNDERFLOW;
2820		bhssr->bhssr_residual_count =
2821		    htonl(ntohl(bhssc->bhssc_expected_data_transfer_length) -
2822		    PDU_TOTAL_TRANSFER_LEN(request));
2823		//CFISCSI_SESSION_DEBUG(cs, "underflow; residual count %d",
2824		//    ntohl(bhssr->bhssr_residual_count));
2825	} else if (PDU_TOTAL_TRANSFER_LEN(request) >
2826	    ntohl(bhssc->bhssc_expected_data_transfer_length)) {
2827		bhssr->bhssr_flags |= BHSSR_FLAGS_RESIDUAL_OVERFLOW;
2828		bhssr->bhssr_residual_count =
2829		    htonl(PDU_TOTAL_TRANSFER_LEN(request) -
2830		    ntohl(bhssc->bhssc_expected_data_transfer_length));
2831		//CFISCSI_SESSION_DEBUG(cs, "overflow; residual count %d",
2832		//    ntohl(bhssr->bhssr_residual_count));
2833	}
2834	bhssr->bhssr_response = BHSSR_RESPONSE_COMMAND_COMPLETED;
2835	bhssr->bhssr_status = io->scsiio.scsi_status;
2836	bhssr->bhssr_initiator_task_tag = bhssc->bhssc_initiator_task_tag;
2837	bhssr->bhssr_expdatasn = htonl(PDU_EXPDATASN(request));
2838
2839	if (io->scsiio.sense_len > 0) {
2840#if 0
2841		CFISCSI_SESSION_DEBUG(cs, "returning %d bytes of sense data",
2842		    io->scsiio.sense_len);
2843#endif
2844		sense_length = htons(io->scsiio.sense_len);
2845		icl_pdu_append_data(response,
2846		    &sense_length, sizeof(sense_length), M_WAITOK);
2847		icl_pdu_append_data(response,
2848		    &io->scsiio.sense_data, io->scsiio.sense_len, M_WAITOK);
2849	}
2850
2851	ctl_free_io(io);
2852	icl_pdu_free(request);
2853	cfiscsi_pdu_queue(response);
2854}
2855
2856static void
2857cfiscsi_task_management_done(union ctl_io *io)
2858{
2859	struct icl_pdu *request, *response;
2860	struct iscsi_bhs_task_management_request *bhstmr;
2861	struct iscsi_bhs_task_management_response *bhstmr2;
2862	struct cfiscsi_data_wait *cdw, *tmpcdw;
2863	struct cfiscsi_session *cs;
2864
2865	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2866	cs = PDU_SESSION(request);
2867	bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs;
2868	KASSERT((bhstmr->bhstmr_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) ==
2869	    ISCSI_BHS_OPCODE_TASK_REQUEST,
2870	    ("replying to wrong opcode 0x%x", bhstmr->bhstmr_opcode));
2871
2872#if 0
2873	CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x; referenced task tag 0x%x",
2874	    bhstmr->bhstmr_initiator_task_tag,
2875	    bhstmr->bhstmr_referenced_task_tag);
2876#endif
2877
2878	if ((bhstmr->bhstmr_function & ~0x80) ==
2879	    BHSTMR_FUNCTION_ABORT_TASK) {
2880		/*
2881		 * Make sure we no longer wait for Data-Out for this command.
2882		 */
2883		CFISCSI_SESSION_LOCK(cs);
2884		TAILQ_FOREACH_SAFE(cdw,
2885		    &cs->cs_waiting_for_data_out, cdw_next, tmpcdw) {
2886			if (bhstmr->bhstmr_referenced_task_tag !=
2887			    cdw->cdw_initiator_task_tag)
2888				continue;
2889
2890#if 0
2891			CFISCSI_SESSION_DEBUG(cs, "removing csw for initiator task "
2892			    "tag 0x%x", bhstmr->bhstmr_initiator_task_tag);
2893#endif
2894			TAILQ_REMOVE(&cs->cs_waiting_for_data_out,
2895			    cdw, cdw_next);
2896			cdw->cdw_ctl_io->scsiio.be_move_done(cdw->cdw_ctl_io);
2897			uma_zfree(cfiscsi_data_wait_zone, cdw);
2898		}
2899		CFISCSI_SESSION_UNLOCK(cs);
2900	}
2901
2902	response = cfiscsi_pdu_new_response(request, M_WAITOK);
2903	bhstmr2 = (struct iscsi_bhs_task_management_response *)
2904	    response->ip_bhs;
2905	bhstmr2->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_RESPONSE;
2906	bhstmr2->bhstmr_flags = 0x80;
2907	if (io->io_hdr.status == CTL_SUCCESS) {
2908		bhstmr2->bhstmr_response = BHSTMR_RESPONSE_FUNCTION_COMPLETE;
2909	} else {
2910		/*
2911		 * XXX: How to figure out what exactly went wrong?  iSCSI spec
2912		 * 	expects us to provide detailed error, e.g. "Task does
2913		 * 	not exist" or "LUN does not exist".
2914		 */
2915		CFISCSI_SESSION_DEBUG(cs, "BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED");
2916		bhstmr2->bhstmr_response =
2917		    BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED;
2918	}
2919	bhstmr2->bhstmr_initiator_task_tag = bhstmr->bhstmr_initiator_task_tag;
2920
2921	ctl_free_io(io);
2922	icl_pdu_free(request);
2923	cfiscsi_pdu_queue(response);
2924}
2925
2926static void
2927cfiscsi_done(union ctl_io *io)
2928{
2929	struct icl_pdu *request;
2930	struct cfiscsi_session *cs;
2931
2932	KASSERT(((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE),
2933		("invalid CTL status %#x", io->io_hdr.status));
2934
2935	request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
2936	if (request == NULL) {
2937		/*
2938		 * Implicit task termination has just completed; nothing to do.
2939		 */
2940		return;
2941	}
2942
2943	cs = PDU_SESSION(request);
2944	refcount_release(&cs->cs_outstanding_ctl_pdus);
2945
2946	switch (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) {
2947	case ISCSI_BHS_OPCODE_SCSI_COMMAND:
2948		cfiscsi_scsi_command_done(io);
2949		break;
2950	case ISCSI_BHS_OPCODE_TASK_REQUEST:
2951		cfiscsi_task_management_done(io);
2952		break;
2953	default:
2954		panic("cfiscsi_done called with wrong opcode 0x%x",
2955		    request->ip_bhs->bhs_opcode);
2956	}
2957}
2958