xref: /netbsd-src/sys/dev/iscsi/iscsi_main.c (revision 7f0211635ef52628e62501d4d5b351ca21f525ef)
1*7f021163Smlelstv /*	$NetBSD: iscsi_main.c,v 1.42 2023/12/28 15:58:24 mlelstv Exp $	*/
2e01460c0Sagc 
3e01460c0Sagc /*-
4e01460c0Sagc  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
5e01460c0Sagc  * All rights reserved.
6e01460c0Sagc  *
7e01460c0Sagc  * This code is derived from software contributed to The NetBSD Foundation
8e01460c0Sagc  * by Wasabi Systems, Inc.
9e01460c0Sagc  *
10e01460c0Sagc  * Redistribution and use in source and binary forms, with or without
11e01460c0Sagc  * modification, are permitted provided that the following conditions
12e01460c0Sagc  * are met:
13e01460c0Sagc  * 1. Redistributions of source code must retain the above copyright
14e01460c0Sagc  *    notice, this list of conditions and the following disclaimer.
15e01460c0Sagc  * 2. Redistributions in binary form must reproduce the above copyright
16e01460c0Sagc  *    notice, this list of conditions and the following disclaimer in the
17e01460c0Sagc  *    documentation and/or other materials provided with the distribution.
18e01460c0Sagc  *
19e01460c0Sagc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20e01460c0Sagc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21e01460c0Sagc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22e01460c0Sagc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23e01460c0Sagc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24e01460c0Sagc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25e01460c0Sagc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26e01460c0Sagc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27e01460c0Sagc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28e01460c0Sagc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29e01460c0Sagc  * POSSIBILITY OF SUCH DAMAGE.
30e01460c0Sagc  */
31e01460c0Sagc #include "iscsi_globals.h"
32e01460c0Sagc 
33e01460c0Sagc #include <sys/systm.h>
34e01460c0Sagc #include <sys/buf.h>
35e7547741Sjoerg #include <sys/file.h>
36e7547741Sjoerg #include <sys/filedesc.h>
37e01460c0Sagc #include <sys/kmem.h>
38e01460c0Sagc #include <sys/socketvar.h>
39996d5208Smlelstv #include <sys/sysctl.h>
40e01460c0Sagc 
41e7ae23fdSchristos #include "ioconf.h"
42e01460c0Sagc 
43e01460c0Sagc /*------------------------- Global Variables ------------------------*/
44e01460c0Sagc 
45e01460c0Sagc extern struct cfdriver iscsi_cd;
46e01460c0Sagc 
47e01460c0Sagc #if defined(ISCSI_DEBUG)
487c7e8556Smlelstv int iscsi_debug_level = ISCSI_DEBUG;
49e01460c0Sagc #endif
50f1a7fa8cSmlelstv bool iscsi_hex_bignums = false;
51e01460c0Sagc 
52996d5208Smlelstv bool iscsi_detaching;
53e01460c0Sagc 
54e01460c0Sagc /* the list of sessions */
55a22a9581Smlelstv session_list_t iscsi_sessions = TAILQ_HEAD_INITIALIZER(iscsi_sessions);
56e01460c0Sagc 
57e01460c0Sagc /* the number of active send threads (for cleanup thread) */
58a22a9581Smlelstv uint32_t iscsi_num_send_threads = 0;
59e01460c0Sagc 
60e01460c0Sagc /* Our node name, alias, and ISID */
61a22a9581Smlelstv uint8_t iscsi_InitiatorName[ISCSI_STRING_LENGTH] = "";
62a22a9581Smlelstv uint8_t iscsi_InitiatorAlias[ISCSI_STRING_LENGTH] = "";
63a22a9581Smlelstv login_isid_t iscsi_InitiatorISID;
64e01460c0Sagc 
65e01460c0Sagc /******************************************************************************/
66e01460c0Sagc 
67e01460c0Sagc /*
68e01460c0Sagc    System interface: autoconf and device structures
69e01460c0Sagc */
70e01460c0Sagc 
71a22a9581Smlelstv static void iscsi_attach(device_t parent, device_t self, void *aux);
72a22a9581Smlelstv static int iscsi_match(device_t, cfdata_t, void *);
73a22a9581Smlelstv static int iscsi_detach(device_t, int);
74e01460c0Sagc 
75996d5208Smlelstv struct iscsi_softc {
76996d5208Smlelstv 	device_t		dev;
77996d5208Smlelstv 	kmutex_t		lock;
78996d5208Smlelstv 	TAILQ_HEAD(, iscsifd)	fds;
79996d5208Smlelstv };
80e01460c0Sagc 
81e01460c0Sagc CFATTACH_DECL_NEW(iscsi, sizeof(struct iscsi_softc), iscsi_match, iscsi_attach,
82e01460c0Sagc 			  iscsi_detach, NULL);
83e01460c0Sagc 
84e01460c0Sagc 
85a22a9581Smlelstv static dev_type_open(iscsiopen);
86e7547741Sjoerg static int iscsiclose(struct file *);
87e7547741Sjoerg 
88e7547741Sjoerg static const struct fileops iscsi_fileops = {
89ea05286dSchristos 	.fo_name = "iscsi",
901f5bdd0aSmlelstv 	.fo_read = fbadop_read,
911f5bdd0aSmlelstv 	.fo_write = fbadop_write,
92e7547741Sjoerg 	.fo_ioctl = iscsiioctl,
931f5bdd0aSmlelstv 	.fo_fcntl = fnullop_fcntl,
941f5bdd0aSmlelstv 	.fo_poll = fnullop_poll,
951f5bdd0aSmlelstv 	.fo_stat = fbadop_stat,
96e7547741Sjoerg 	.fo_close = iscsiclose,
971f5bdd0aSmlelstv 	.fo_kqfilter = fnullop_kqfilter,
981f5bdd0aSmlelstv 	.fo_restart = fnullop_restart
99e7547741Sjoerg };
100e01460c0Sagc 
101e01460c0Sagc struct cdevsw iscsi_cdevsw = {
102a68f9396Sdholland 	.d_open = iscsiopen,
103e7547741Sjoerg 	.d_close = noclose,
104a68f9396Sdholland 	.d_read = noread,
105a68f9396Sdholland 	.d_write = nowrite,
106e7547741Sjoerg 	.d_ioctl = noioctl,
107a68f9396Sdholland 	.d_stop = nostop,
108a68f9396Sdholland 	.d_tty = notty,
109a68f9396Sdholland 	.d_poll = nopoll,
110a68f9396Sdholland 	.d_mmap = nommap,
111a68f9396Sdholland 	.d_kqfilter = nokqfilter,
112f9228f42Sdholland 	.d_discard = nodiscard,
113a68f9396Sdholland 	.d_flag = D_OTHER
114e01460c0Sagc };
115e01460c0Sagc 
116e01460c0Sagc /******************************************************************************/
117e01460c0Sagc 
118e01460c0Sagc STATIC void iscsi_scsipi_request(struct scsipi_channel *,
119e01460c0Sagc                                  scsipi_adapter_req_t, void *);
120e01460c0Sagc STATIC void iscsi_minphys(struct buf *);
121e01460c0Sagc 
122e01460c0Sagc /******************************************************************************/
123e01460c0Sagc 
124e01460c0Sagc /*******************************************************************************
125e01460c0Sagc * Open and Close device interfaces. We don't really need them, because we don't
126e01460c0Sagc * have to keep track of device opens and closes from userland. But apps can't
127e01460c0Sagc * call ioctl without a handle to the device, and the kernel doesn't hand out
128e01460c0Sagc * handles without an open routine in the driver. So here they are in all their
129e01460c0Sagc * glory...
130e01460c0Sagc *******************************************************************************/
131e01460c0Sagc 
132e01460c0Sagc int
iscsiopen(dev_t dev,int flag,int mode,struct lwp * l)1338f2555d8Sjoerg iscsiopen(dev_t dev, int flag, int mode, struct lwp *l)
134e01460c0Sagc {
135e7547741Sjoerg 	struct iscsifd *d;
136996d5208Smlelstv 	struct iscsi_softc *sc;
137e7547741Sjoerg 	struct file *fp;
138996d5208Smlelstv 	int error, fd, unit;
139e01460c0Sagc 
140996d5208Smlelstv 	unit = minor(dev);
141996d5208Smlelstv 
142996d5208Smlelstv 	DEB(99, ("ISCSI Open unit=%d\n",unit));
143996d5208Smlelstv 
144996d5208Smlelstv 	sc = device_lookup_private(&iscsi_cd, unit);
145996d5208Smlelstv 	if (sc == NULL)
146996d5208Smlelstv 		return ENXIO;
147e7547741Sjoerg 
148e7547741Sjoerg 	if ((error = fd_allocfile(&fp, &fd)) != 0)
149e7547741Sjoerg 		return error;
150e7547741Sjoerg 
151e7547741Sjoerg 	d = kmem_alloc(sizeof(*d), KM_SLEEP);
1528ab41cb9Schristos 	d->fd_dev = sc->dev;
1538ab41cb9Schristos 	d->fd_unit = unit;
154996d5208Smlelstv 
155996d5208Smlelstv 	mutex_enter(&sc->lock);
156996d5208Smlelstv 	if (iscsi_detaching) {
157996d5208Smlelstv 		mutex_exit(&sc->lock);
158996d5208Smlelstv 		kmem_free(d, sizeof(*d));
159996d5208Smlelstv 		DEB(99, ("ISCSI Open aborting\n"));
160996d5208Smlelstv 		fd_abort(curproc, fp, fd);
161996d5208Smlelstv 		return ENXIO;
162996d5208Smlelstv 	}
1638ab41cb9Schristos 	TAILQ_INSERT_TAIL(&sc->fds, d, fd_link);
164996d5208Smlelstv 	mutex_exit(&sc->lock);
165e7547741Sjoerg 
166e7547741Sjoerg 	return fd_clone(fp, fd, flag, &iscsi_fileops, d);
167e01460c0Sagc }
168e01460c0Sagc 
169e7547741Sjoerg static int
iscsiclose(struct file * fp)170e7547741Sjoerg iscsiclose(struct file *fp)
171e01460c0Sagc {
172e7547741Sjoerg 	struct iscsifd *d = fp->f_iscsi;
173996d5208Smlelstv 	struct iscsi_softc *sc;
174996d5208Smlelstv 
1758ab41cb9Schristos 	sc = device_lookup_private(&iscsi_cd, d->fd_unit);
1761f5bdd0aSmlelstv 	if (sc != NULL) {
177996d5208Smlelstv 		mutex_enter(&sc->lock);
1788ab41cb9Schristos 		TAILQ_REMOVE(&sc->fds, d, fd_link);
179996d5208Smlelstv 		mutex_exit(&sc->lock);
1801f5bdd0aSmlelstv 	}
181e7547741Sjoerg 
182e7547741Sjoerg 	kmem_free(d, sizeof(*d));
183e7547741Sjoerg 	fp->f_iscsi = NULL;
184e01460c0Sagc 
185e01460c0Sagc 	DEB(99, ("ISCSI Close\n"));
186e01460c0Sagc 	return 0;
187e01460c0Sagc }
188e01460c0Sagc 
189e01460c0Sagc /******************************************************************************/
190e01460c0Sagc 
191e01460c0Sagc /*
192e01460c0Sagc  * The config Match routine.
193e01460c0Sagc  *    Not much to do here, either - this is a pseudo-device.
194e01460c0Sagc  */
195e01460c0Sagc 
196a22a9581Smlelstv static int
iscsi_match(device_t self,cfdata_t cfdata,void * arg)197e01460c0Sagc iscsi_match(device_t self, cfdata_t cfdata, void *arg)
198e01460c0Sagc {
199e01460c0Sagc 	return 1;
200e01460c0Sagc }
201e01460c0Sagc 
202e01460c0Sagc /*
203e01460c0Sagc  * iscsiattach:
204e01460c0Sagc  *    Only called when statically configured into a kernel
205e01460c0Sagc  */
206e01460c0Sagc void
iscsiattach(int n)207e01460c0Sagc iscsiattach(int n)
208e01460c0Sagc {
209e01460c0Sagc 	int err;
210e01460c0Sagc 	cfdata_t cf;
211e01460c0Sagc 
212e01460c0Sagc 	err = config_cfattach_attach(iscsi_cd.cd_name, &iscsi_ca);
213e01460c0Sagc 	if (err) {
214e01460c0Sagc 		aprint_error("%s: couldn't register cfattach: %d\n",
215e01460c0Sagc 		    iscsi_cd.cd_name, err);
216e01460c0Sagc 		config_cfdriver_detach(&iscsi_cd);
217e01460c0Sagc 		return;
218e01460c0Sagc 	}
219e01460c0Sagc 
220e01460c0Sagc 	if (n > 1)
221e01460c0Sagc 		aprint_error("%s: only one device supported\n",
222e01460c0Sagc 		    iscsi_cd.cd_name);
223e01460c0Sagc 
22402991323Schs 	cf = kmem_alloc(sizeof(struct cfdata), KM_SLEEP);
225e01460c0Sagc 	cf->cf_name = iscsi_cd.cd_name;
226e01460c0Sagc 	cf->cf_atname = iscsi_cd.cd_name;
227e01460c0Sagc 	cf->cf_unit = 0;
228e01460c0Sagc 	cf->cf_fstate = FSTATE_NOTFOUND;
229e01460c0Sagc 
230e01460c0Sagc 	(void)config_attach_pseudo(cf);
231e01460c0Sagc 	return;
232e01460c0Sagc }
233e01460c0Sagc 
234e01460c0Sagc /*
235e01460c0Sagc  * iscsi_attach:
236e01460c0Sagc  *    One-time inits go here. Not much for now, probably even less later.
237e01460c0Sagc  */
238a22a9581Smlelstv static void
iscsi_attach(device_t parent,device_t self,void * aux)239e01460c0Sagc iscsi_attach(device_t parent, device_t self, void *aux)
240e01460c0Sagc {
241996d5208Smlelstv 	struct iscsi_softc *sc;
242e01460c0Sagc 
243996d5208Smlelstv 	DEB(1, ("ISCSI: iscsi_attach, parent=%p, self=%p, aux=%p\n", parent,
244e01460c0Sagc 			self, aux));
245996d5208Smlelstv 	sc = (struct iscsi_softc *) device_private(self);
246996d5208Smlelstv 	sc->dev = self;
247996d5208Smlelstv 
248996d5208Smlelstv 	TAILQ_INIT(&sc->fds);
249996d5208Smlelstv 	mutex_init(&sc->lock, MUTEX_DEFAULT, IPL_NONE);
250996d5208Smlelstv 
251996d5208Smlelstv 	iscsi_detaching = false;
252996d5208Smlelstv 	iscsi_init_cleanup();
253996d5208Smlelstv 
254eea8587cSmlelstv 	if (!pmf_device_register(self, NULL, NULL))
255eea8587cSmlelstv 		aprint_error_dev(self, "couldn't establish power handler\n");
256eea8587cSmlelstv 
25775014c50Sjdolecek 	aprint_verbose("%s: attached.  major = %d\n", iscsi_cd.cd_name,
258e01460c0Sagc 	    cdevsw_lookup_major(&iscsi_cdevsw));
259e01460c0Sagc }
260e01460c0Sagc 
261e01460c0Sagc /*
262e01460c0Sagc  * iscsi_detach:
263e01460c0Sagc  *    Cleanup.
264e01460c0Sagc  */
265a22a9581Smlelstv static int
iscsi_detach(device_t self,int flags)266e01460c0Sagc iscsi_detach(device_t self, int flags)
267e01460c0Sagc {
268996d5208Smlelstv 	struct iscsi_softc *sc;
269bbe94f43Smlelstv 	int error;
270e01460c0Sagc 
271996d5208Smlelstv 	DEB(1, ("ISCSI: detach\n"));
272996d5208Smlelstv 	sc = (struct iscsi_softc *) device_private(self);
273996d5208Smlelstv 
274996d5208Smlelstv 	mutex_enter(&sc->lock);
275996d5208Smlelstv 	if (!TAILQ_EMPTY(&sc->fds)) {
276996d5208Smlelstv 		mutex_exit(&sc->lock);
277996d5208Smlelstv 		return EBUSY;
278e01460c0Sagc 	}
279996d5208Smlelstv 	iscsi_detaching = true;
280996d5208Smlelstv 	mutex_exit(&sc->lock);
281996d5208Smlelstv 
282bbe94f43Smlelstv 	error = kill_all_sessions();
283bbe94f43Smlelstv 	if (error)
284bbe94f43Smlelstv 		return error;
285bbe94f43Smlelstv 
286bbe94f43Smlelstv 	error = iscsi_destroy_cleanup();
287bbe94f43Smlelstv 	if (error)
288bbe94f43Smlelstv 		return error;
289996d5208Smlelstv 
290eea8587cSmlelstv 	pmf_device_deregister(sc->dev);
291eea8587cSmlelstv 
292996d5208Smlelstv 	mutex_destroy(&sc->lock);
293996d5208Smlelstv 
294e01460c0Sagc 	return 0;
295e01460c0Sagc }
296e01460c0Sagc 
297e01460c0Sagc /******************************************************************************/
298e01460c0Sagc 
299e01460c0Sagc typedef struct quirktab_t {
300e01460c0Sagc 	const char	*tgt;
301e01460c0Sagc 	const char	*iqn;
302e01460c0Sagc 	uint32_t	 quirks;
303e01460c0Sagc } quirktab_t;
304e01460c0Sagc 
305e01460c0Sagc static const quirktab_t	quirktab[] = {
30673311a54Sjoerg 	{ "StarWind", "iqn.2008-08.com.starwindsoftware", PQUIRK_ONLYBIG },
30773311a54Sjoerg 	{ "UNH", "iqn.2002-10.edu.unh.",
308e01460c0Sagc 	    PQUIRK_NOBIGMODESENSE |
309e01460c0Sagc 	    PQUIRK_NOMODESENSE |
310e01460c0Sagc 	    PQUIRK_NOSYNCCACHE },
31173311a54Sjoerg 	{ "NetBSD", "iqn.1994-04.org.netbsd.", 0 },
31273311a54Sjoerg 	{ "Unknown", "unknown", 0 },
31373311a54Sjoerg 	{ NULL, NULL, 0 }
314e01460c0Sagc };
315e01460c0Sagc 
316e01460c0Sagc /* loop through the quirktab looking for a match on target name */
317e01460c0Sagc static const quirktab_t *
getquirks(const char * iqn)318e01460c0Sagc getquirks(const char *iqn)
319e01460c0Sagc {
320e01460c0Sagc 	const quirktab_t	*qp;
32173311a54Sjoerg 	size_t iqnlen, quirklen;
322e01460c0Sagc 
32373311a54Sjoerg 	if (iqn == NULL)
324e01460c0Sagc 		iqn = "unknown";
32573311a54Sjoerg 	iqnlen = strlen(iqn);
326e01460c0Sagc 	for (qp = quirktab ; qp->iqn ; qp++) {
32773311a54Sjoerg 		quirklen = strlen(qp->iqn);
32873311a54Sjoerg 		if (quirklen > iqnlen)
32973311a54Sjoerg 			continue;
33073311a54Sjoerg 		if (memcmp(qp->iqn, iqn, quirklen) == 0)
331e01460c0Sagc 			break;
332e01460c0Sagc 	}
333e01460c0Sagc 	return qp;
334e01460c0Sagc }
335e01460c0Sagc 
336e01460c0Sagc /******************************************************************************/
337e01460c0Sagc 
338e01460c0Sagc /*
339e01460c0Sagc  * map_session
340e01460c0Sagc  *    This (indirectly) maps the existing LUNs for a target to SCSI devices
341e01460c0Sagc  *    by going through config_found to tell any child drivers that there's
342e01460c0Sagc  *    a new adapter.
343e01460c0Sagc  *    Note that each session is equivalent to a SCSI adapter.
344e01460c0Sagc  *
345e01460c0Sagc  *    Parameter:  the session pointer
346e01460c0Sagc  *
347e01460c0Sagc  *    Returns:    1 on success, 0 on failure
348e01460c0Sagc  *
349e01460c0Sagc  * ToDo: Figuring out how to handle more than one LUN. It appears that
350e01460c0Sagc  *    the NetBSD SCSI LUN discovery doesn't use "report LUNs", and instead
351e01460c0Sagc  *    goes through the LUNs sequentially, stopping somewhere on the way if it
352e01460c0Sagc  *    gets an error. We may have to do some LUN mapping in here if this is
353e01460c0Sagc  *    really how things work.
354e01460c0Sagc  */
355e01460c0Sagc 
356e01460c0Sagc int
map_session(session_t * sess,device_t dev)3578ab41cb9Schristos map_session(session_t *sess, device_t dev)
358e01460c0Sagc {
3598ab41cb9Schristos 	struct scsipi_adapter *adapt = &sess->s_sc_adapter;
3608ab41cb9Schristos 	struct scsipi_channel *chan = &sess->s_sc_channel;
361e01460c0Sagc 	const quirktab_t	*tgt;
362adeed0a0Sriastradh 	int found;
363e01460c0Sagc 
3648ab41cb9Schristos 	mutex_enter(&sess->s_lock);
3658ab41cb9Schristos 	sess->s_send_window = max(2, window_size(sess, CCBS_FOR_SCSIPI));
3668ab41cb9Schristos 	mutex_exit(&sess->s_lock);
367bbe94f43Smlelstv 
368e01460c0Sagc 	/*
369e01460c0Sagc 	 * Fill in the scsipi_adapter.
370e01460c0Sagc 	 */
371996d5208Smlelstv 	adapt->adapt_dev = dev;
372e01460c0Sagc 	adapt->adapt_nchannels = 1;
373e01460c0Sagc 	adapt->adapt_request = iscsi_scsipi_request;
374e01460c0Sagc 	adapt->adapt_minphys = iscsi_minphys;
3758ab41cb9Schristos 	adapt->adapt_openings = sess->s_send_window;
376bbe94f43Smlelstv 	adapt->adapt_max_periph = CCBS_FOR_SCSIPI;
377015f574cSmlelstv 	adapt->adapt_flags = SCSIPI_ADAPT_MPSAFE;
378e01460c0Sagc 
379e01460c0Sagc 	/*
380e01460c0Sagc 	 * Fill in the scsipi_channel.
381e01460c0Sagc 	 */
382e01460c0Sagc 	if ((tgt = getquirks(chan->chan_name)) == NULL) {
383e01460c0Sagc 		tgt = getquirks("unknown");
384e01460c0Sagc 	}
385e01460c0Sagc 	chan->chan_name = tgt->tgt;
386e01460c0Sagc 	chan->chan_defquirks = tgt->quirks;
387e01460c0Sagc 	chan->chan_adapter = adapt;
388e01460c0Sagc 	chan->chan_bustype = &scsi_bustype;
389e01460c0Sagc 	chan->chan_channel = 0;
390bbe94f43Smlelstv 	chan->chan_flags = SCSIPI_CHAN_NOSETTLE | SCSIPI_CHAN_CANGROW;
391e01460c0Sagc 	chan->chan_ntargets = 1;
392f1a7fa8cSmlelstv 	chan->chan_nluns = 16;
3938ab41cb9Schristos 	chan->chan_id = sess->s_id;
394e01460c0Sagc 
395adeed0a0Sriastradh 	KERNEL_LOCK(1, NULL);
396c7fb772bSthorpej 	sess->s_child_dev = config_found(dev, chan, scsiprint, CFARGS_NONE);
397adeed0a0Sriastradh 	found = (sess->s_child_dev != NULL);
398adeed0a0Sriastradh 	KERNEL_UNLOCK_ONE(NULL);
399e01460c0Sagc 
400adeed0a0Sriastradh 	return found;
401e01460c0Sagc }
402e01460c0Sagc 
403e01460c0Sagc 
404e01460c0Sagc /*
405e01460c0Sagc  * unmap_session
406e01460c0Sagc  *    This (indirectly) unmaps the existing all LUNs for a target by
407e01460c0Sagc  *    telling the config system that the adapter has detached.
408e01460c0Sagc  *
409e01460c0Sagc  *    Parameter:  the session pointer
41021dddfceSmlelstv  *
41121dddfceSmlelstv  *    Returns:    1 on success, 0 on failure
412e01460c0Sagc  */
413e01460c0Sagc 
41421dddfceSmlelstv int
unmap_session(session_t * sess)4158ab41cb9Schristos unmap_session(session_t *sess)
416e01460c0Sagc {
417e01460c0Sagc 	device_t dev;
41821dddfceSmlelstv 	int rv = 1;
419e01460c0Sagc 
4208ab41cb9Schristos 	if ((dev = sess->s_child_dev) != NULL) {
42121dddfceSmlelstv 		if (config_detach(dev, 0))
42221dddfceSmlelstv 			rv = 0;
4231ec2b09aSmlelstv 		if (rv)
4241ec2b09aSmlelstv 			sess->s_child_dev = NULL;
425e01460c0Sagc 	}
42621dddfceSmlelstv 
42721dddfceSmlelstv 	return rv;
428e01460c0Sagc }
429e01460c0Sagc 
430bbe94f43Smlelstv /*
431bbe94f43Smlelstv  * grow_resources
432bbe94f43Smlelstv  *    Try to grow openings up to current window size
433bbe94f43Smlelstv  */
4340f81aa30Smlelstv static int
grow_resources(session_t * sess)4358ab41cb9Schristos grow_resources(session_t *sess)
436bbe94f43Smlelstv {
4378ab41cb9Schristos 	struct scsipi_adapter *adapt = &sess->s_sc_adapter;
438bbe94f43Smlelstv 	int win;
4390f81aa30Smlelstv 	int rc = -1;
440bbe94f43Smlelstv 
4418ab41cb9Schristos 	mutex_enter(&sess->s_lock);
4428ab41cb9Schristos 	if (sess->s_refcount < CCBS_FOR_SCSIPI &&
4438ab41cb9Schristos 	    sess->s_send_window < CCBS_FOR_SCSIPI) {
4448ab41cb9Schristos 		win = window_size(sess, CCBS_FOR_SCSIPI - sess->s_refcount);
4458ab41cb9Schristos 		if (win > sess->s_send_window) {
4468ab41cb9Schristos 			sess->s_send_window++;
447bbe94f43Smlelstv 			adapt->adapt_openings++;
4480f81aa30Smlelstv 			rc = 0;
4498ab41cb9Schristos 			DEB(5, ("Grow send window to %d\n", sess->s_send_window));
450bbe94f43Smlelstv 		}
451bbe94f43Smlelstv 	}
4528ab41cb9Schristos 	mutex_exit(&sess->s_lock);
4530f81aa30Smlelstv 
4540f81aa30Smlelstv 	return rc;
455bbe94f43Smlelstv }
456bbe94f43Smlelstv 
457e01460c0Sagc /******************************************************************************/
458e01460c0Sagc 
459e01460c0Sagc /*****************************************************************************
460e01460c0Sagc  * SCSI interface routines
461e01460c0Sagc  *****************************************************************************/
462e01460c0Sagc 
463e01460c0Sagc /*
464e01460c0Sagc  * iscsi_scsipi_request:
465e01460c0Sagc  *    Perform a request for the SCSIPI layer.
466e01460c0Sagc  */
467e01460c0Sagc 
468e01460c0Sagc void
iscsi_scsipi_request(struct scsipi_channel * chan,scsipi_adapter_req_t req,void * arg)469e01460c0Sagc iscsi_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
470e01460c0Sagc 					 void *arg)
471e01460c0Sagc {
472e01460c0Sagc 	struct scsipi_adapter *adapt = chan->chan_adapter;
473e01460c0Sagc 	struct scsipi_xfer *xs;
4748ab41cb9Schristos 	session_t *sess;
475e01460c0Sagc 	int flags;
47621dddfceSmlelstv 	struct scsipi_xfer_mode *xm;
477bbe94f43Smlelstv 	int error;
478e01460c0Sagc 
4798ab41cb9Schristos 	sess = (session_t *) adapt;	/* adapter is first field in session */
480e01460c0Sagc 
4818ab41cb9Schristos 	error = ref_session(sess);
482bbe94f43Smlelstv 
483e01460c0Sagc 	switch (req) {
484e01460c0Sagc 	case ADAPTER_REQ_RUN_XFER:
485e01460c0Sagc 		DEB(9, ("ISCSI: scsipi_request RUN_XFER\n"));
486e01460c0Sagc 		xs = arg;
487e01460c0Sagc 		flags = xs->xs_control;
488e01460c0Sagc 
489bbe94f43Smlelstv 		if (error) {
490bbe94f43Smlelstv 			DEB(9, ("ISCSI: refcount too high: %d, winsize %d\n",
4918ab41cb9Schristos 				sess->s_refcount, sess->s_send_window));
492bbe94f43Smlelstv 			xs->error = XS_BUSY;
493*7f021163Smlelstv 			xs->status = SCSI_BUSY;
494bbe94f43Smlelstv 			scsipi_done(xs);
495bbe94f43Smlelstv 			return;
496bbe94f43Smlelstv 		}
497bbe94f43Smlelstv 
498e01460c0Sagc 		if ((flags & XS_CTL_POLL) != 0) {
499e01460c0Sagc 			xs->error = XS_DRIVER_STUFFUP;
500e01460c0Sagc 			DEBOUT(("Run Xfer request with polling\n"));
501e01460c0Sagc 			scsipi_done(xs);
502bbe94f43Smlelstv 			break;
503e01460c0Sagc 		}
504e01460c0Sagc 		/*
505e01460c0Sagc 		 * NOTE: It appears that XS_CTL_DATA_UIO is not actually used anywhere.
506e01460c0Sagc 		 * Since it really would complicate matters to handle offsets
507e01460c0Sagc 		 * into scatter-gather lists, and a number of other drivers don't
508e01460c0Sagc 		 * handle uio-based data as well, XS_CTL_DATA_UIO isn't
509e01460c0Sagc 		 * implemented in this driver (at least for now).
510e01460c0Sagc 		 */
511e01460c0Sagc 		if (flags & XS_CTL_DATA_UIO) {
512e01460c0Sagc 			xs->error = XS_DRIVER_STUFFUP;
513e01460c0Sagc 			DEBOUT(("Run Xfer with data in UIO\n"));
514e01460c0Sagc 			scsipi_done(xs);
515bbe94f43Smlelstv 			break;
516e01460c0Sagc 		}
517e01460c0Sagc 
5188ab41cb9Schristos 		send_run_xfer(sess, xs);
5198ab41cb9Schristos 		DEB(15, ("scsipi_req returns, refcount = %d\n", sess->s_refcount));
520e01460c0Sagc 		return;
521e01460c0Sagc 
522e01460c0Sagc 	case ADAPTER_REQ_GROW_RESOURCES:
523996d5208Smlelstv 		DEB(5, ("ISCSI: scsipi_request GROW_RESOURCES\n"));
5240f81aa30Smlelstv 		if (grow_resources(sess)) {
5250f81aa30Smlelstv 			/* reached maximum */
5260f81aa30Smlelstv 			chan->chan_flags &= ~SCSIPI_CHAN_CANGROW;
5270f81aa30Smlelstv 		}
528bbe94f43Smlelstv 		break;
529e01460c0Sagc 
530e01460c0Sagc 	case ADAPTER_REQ_SET_XFER_MODE:
531e01460c0Sagc 		DEB(5, ("ISCSI: scsipi_request SET_XFER_MODE\n"));
53221dddfceSmlelstv 		xm = (struct scsipi_xfer_mode *)arg;
53321dddfceSmlelstv 		xm->xm_mode = PERIPH_CAP_TQING;
53421dddfceSmlelstv 		scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
535bbe94f43Smlelstv 		break;
536e01460c0Sagc 
537e01460c0Sagc 	default:
538bbe94f43Smlelstv 		DEBOUT(("ISCSI: scsipi_request with invalid REQ code %d\n", req));
539e01460c0Sagc 		break;
540e01460c0Sagc 	}
541bbe94f43Smlelstv 
542bbe94f43Smlelstv 	if (!error)
5438ab41cb9Schristos 		unref_session(sess);
544e01460c0Sagc }
545e01460c0Sagc 
546e01460c0Sagc /* cap the transfer at 64K */
547e01460c0Sagc #define ISCSI_MAX_XFER	65536
548e01460c0Sagc 
549e01460c0Sagc /*
550e01460c0Sagc  * iscsi_minphys:
551e01460c0Sagc  *    Limit a transfer to our maximum transfer size.
552e01460c0Sagc  */
553e01460c0Sagc 
554e01460c0Sagc void
iscsi_minphys(struct buf * bp)555e01460c0Sagc iscsi_minphys(struct buf *bp)
556e01460c0Sagc {
557e01460c0Sagc 	if (bp->b_bcount > ISCSI_MAX_XFER) {
558e01460c0Sagc 		bp->b_bcount = ISCSI_MAX_XFER;
559e01460c0Sagc 	}
560e01460c0Sagc }
561e01460c0Sagc 
562e01460c0Sagc /*****************************************************************************
563e01460c0Sagc  * SCSI job execution helper routines
564e01460c0Sagc  *****************************************************************************/
565e01460c0Sagc 
566e01460c0Sagc /*
567e01460c0Sagc  * iscsi_done:
568e01460c0Sagc  *
569e01460c0Sagc  * A CCB has completed execution.  Pass the status back to the
570e01460c0Sagc  * upper layer.
571e01460c0Sagc  */
572e01460c0Sagc void
iscsi_done(ccb_t * ccb)573e01460c0Sagc iscsi_done(ccb_t *ccb)
574e01460c0Sagc {
5758ab41cb9Schristos 	struct scsipi_xfer *xs = ccb->ccb_xs;
576996d5208Smlelstv 	DEB(9, ("iscsi_done\n"));
577e01460c0Sagc 
578e01460c0Sagc 	if (xs != NULL) {
5798ab41cb9Schristos 		xs->resid = ccb->ccb_residual;
5808ab41cb9Schristos 		ccb->ccb_xs = NULL;
5818ab41cb9Schristos 		xs->resid = ccb->ccb_residual;
582e01460c0Sagc 
5838ab41cb9Schristos 		switch (ccb->ccb_status) {
584e01460c0Sagc 		case ISCSI_STATUS_SUCCESS:
585996d5208Smlelstv 			xs->error = XS_NOERROR;
586996d5208Smlelstv 			xs->status = SCSI_OK;
587e01460c0Sagc 			break;
588e01460c0Sagc 
589e01460c0Sagc 		case ISCSI_STATUS_CHECK_CONDITION:
590e01460c0Sagc 			xs->error = XS_SENSE;
591996d5208Smlelstv 			xs->status = SCSI_CHECK;
592e01460c0Sagc 			break;
593e01460c0Sagc 
594e01460c0Sagc 		case ISCSI_STATUS_TARGET_BUSY:
595bbe94f43Smlelstv 		case ISCSI_STATUS_NO_RESOURCES:
5968ab41cb9Schristos 			DEBC(ccb->ccb_connection, 5, ("target busy, ccb %p\n", ccb));
597e01460c0Sagc 			xs->error = XS_BUSY;
598996d5208Smlelstv 			xs->status = SCSI_BUSY;
599e01460c0Sagc 			break;
600e01460c0Sagc 
601e01460c0Sagc 		case ISCSI_STATUS_SOCKET_ERROR:
602e01460c0Sagc 		case ISCSI_STATUS_TIMEOUT:
603e01460c0Sagc 			xs->error = XS_SELTIMEOUT;
604996d5208Smlelstv 			xs->status = SCSI_BUSY;
605996d5208Smlelstv 			break;
606996d5208Smlelstv 
607bbe94f43Smlelstv 		case ISCSI_STATUS_QUEUE_FULL:
6088ab41cb9Schristos 			DEBC(ccb->ccb_connection, 5, ("queue full, ccb %p\n", ccb));
609996d5208Smlelstv 			xs->error = XS_BUSY;
610996d5208Smlelstv 			xs->status = SCSI_QUEUE_FULL;
611e01460c0Sagc 			break;
612e01460c0Sagc 
613e01460c0Sagc 		default:
614e01460c0Sagc 			xs->error = XS_DRIVER_STUFFUP;
615e01460c0Sagc 			break;
616e01460c0Sagc 		}
617e01460c0Sagc 
6188ab41cb9Schristos 		unref_session(ccb->ccb_session);
61967cf3e04Smlelstv 
620e01460c0Sagc 		DEB(99, ("Calling scsipi_done (%p), err = %d\n", xs, xs->error));
621e01460c0Sagc 		scsipi_done(xs);
622e01460c0Sagc 		DEB(99, ("scsipi_done returned\n"));
623996d5208Smlelstv 	} else {
624996d5208Smlelstv 		DEBOUT(("ISCSI: iscsi_done CCB %p without XS\n", ccb));
625e01460c0Sagc 	}
626e01460c0Sagc }
627e01460c0Sagc 
628996d5208Smlelstv SYSCTL_SETUP(sysctl_iscsi_setup, "ISCSI subtree setup")
629996d5208Smlelstv {
630996d5208Smlelstv 	const struct sysctlnode *node = NULL;
631996d5208Smlelstv 
632996d5208Smlelstv 	sysctl_createv(clog, 0, NULL, &node,
633996d5208Smlelstv 		CTLFLAG_PERMANENT,
634996d5208Smlelstv 		CTLTYPE_NODE, "iscsi",
635996d5208Smlelstv 		SYSCTL_DESCR("iscsi controls"),
636996d5208Smlelstv 		NULL, 0, NULL, 0,
637996d5208Smlelstv 		CTL_HW, CTL_CREATE, CTL_EOL);
638f1a7fa8cSmlelstv 	sysctl_createv(clog, 0, &node, NULL,
639f1a7fa8cSmlelstv 		CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
640f1a7fa8cSmlelstv 		CTLTYPE_BOOL, "hexbignums",
641f1a7fa8cSmlelstv 		SYSCTL_DESCR("encode parameters in hex"),
642f1a7fa8cSmlelstv 		NULL, 0,  &iscsi_hex_bignums, 0,
643f1a7fa8cSmlelstv 		CTL_CREATE, CTL_EOL);
644996d5208Smlelstv 
645996d5208Smlelstv #ifdef ISCSI_DEBUG
646996d5208Smlelstv 	sysctl_createv(clog, 0, &node, NULL,
647996d5208Smlelstv 		CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
648996d5208Smlelstv 		CTLTYPE_INT, "debug",
649996d5208Smlelstv 		SYSCTL_DESCR("debug level"),
650996d5208Smlelstv 		NULL, 0,  &iscsi_debug_level, sizeof(iscsi_debug_level),
651996d5208Smlelstv 		CTL_CREATE, CTL_EOL);
652996d5208Smlelstv #endif
653996d5208Smlelstv }
654996d5208Smlelstv 
655996d5208Smlelstv 
656e01460c0Sagc /* Kernel Module support */
657e01460c0Sagc 
658e01460c0Sagc #include <sys/module.h>
659e01460c0Sagc 
6609dda5c7fSpgoyette MODULE(MODULE_CLASS_DRIVER, iscsi, "scsi_subr"); /* Possibly a builtin module */
6611cca922bSjoerg 
662d16ea69cSjoerg #ifdef _MODULE
663e01460c0Sagc static const struct cfiattrdata ibescsi_info = { "scsi", 1,
664e01460c0Sagc 	{{"channel", "-1", -1},}
665e01460c0Sagc };
6661cca922bSjoerg 
667e01460c0Sagc static const struct cfiattrdata *const iscsi_attrs[] = { &ibescsi_info, NULL };
668e01460c0Sagc 
669e01460c0Sagc CFDRIVER_DECL(iscsi, DV_DULL, iscsi_attrs);
670e01460c0Sagc 
671e01460c0Sagc static struct cfdata iscsi_cfdata[] = {
672e01460c0Sagc 	{
673e01460c0Sagc 		.cf_name = "iscsi",
674e01460c0Sagc 		.cf_atname = "iscsi",
675e01460c0Sagc 		.cf_unit = 0,		/* Only unit 0 is ever used  */
676e01460c0Sagc 		.cf_fstate = FSTATE_NOTFOUND,
677e01460c0Sagc 		.cf_loc = NULL,
678e01460c0Sagc 		.cf_flags = 0,
679e01460c0Sagc 		.cf_pspec = NULL,
680e01460c0Sagc 	},
681e01460c0Sagc 	{ NULL, NULL, 0, 0, NULL, 0, NULL }
682e01460c0Sagc };
683d16ea69cSjoerg #endif
684e01460c0Sagc 
685e01460c0Sagc static int
iscsi_modcmd(modcmd_t cmd,void * arg)686e01460c0Sagc iscsi_modcmd(modcmd_t cmd, void *arg)
687e01460c0Sagc {
688d16ea69cSjoerg #ifdef _MODULE
689e01460c0Sagc 	devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR;
690e01460c0Sagc 	int error;
691d16ea69cSjoerg #endif
692e01460c0Sagc 
693e01460c0Sagc 	switch (cmd) {
694e01460c0Sagc 	case MODULE_CMD_INIT:
695d16ea69cSjoerg #ifdef _MODULE
69697f8debdSpgoyette 		error = devsw_attach(iscsi_cd.cd_name, NULL, &bmajor,
69797f8debdSpgoyette 			&iscsi_cdevsw, &cmajor);
69897f8debdSpgoyette 		if (error) {
69997f8debdSpgoyette 			aprint_error("%s: unable to register devsw\n",
70097f8debdSpgoyette 				iscsi_cd.cd_name);
70197f8debdSpgoyette 			return error;
70297f8debdSpgoyette 		}
703e01460c0Sagc 		error = config_cfdriver_attach(&iscsi_cd);
704e01460c0Sagc 		if (error) {
70597f8debdSpgoyette 			devsw_detach(NULL, &iscsi_cdevsw);
706e01460c0Sagc 			return error;
707e01460c0Sagc 		}
708e01460c0Sagc 
709e01460c0Sagc 		error = config_cfattach_attach(iscsi_cd.cd_name, &iscsi_ca);
710e01460c0Sagc 		if (error) {
711e01460c0Sagc 			config_cfdriver_detach(&iscsi_cd);
71297f8debdSpgoyette 			devsw_detach(NULL, &iscsi_cdevsw);
713e01460c0Sagc 			aprint_error("%s: unable to register cfattach\n",
714e01460c0Sagc 				iscsi_cd.cd_name);
715e01460c0Sagc 			return error;
716e01460c0Sagc 		}
717e01460c0Sagc 
718e01460c0Sagc 		error = config_cfdata_attach(iscsi_cfdata, 1);
719e01460c0Sagc 		if (error) {
720e01460c0Sagc 			aprint_error("%s: unable to attach cfdata\n",
721e01460c0Sagc 				iscsi_cd.cd_name);
722e01460c0Sagc 			config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
723e01460c0Sagc 			config_cfdriver_detach(&iscsi_cd);
72497f8debdSpgoyette 			devsw_detach(NULL, &iscsi_cdevsw);
725e01460c0Sagc 			return error;
726e01460c0Sagc 		}
727e01460c0Sagc 
728e01460c0Sagc 		if (config_attach_pseudo(iscsi_cfdata) == NULL) {
729e01460c0Sagc 			aprint_error("%s: config_attach_pseudo failed\n",
730e01460c0Sagc 				iscsi_cd.cd_name);
73197f8debdSpgoyette 
73297f8debdSpgoyette 			config_cfdata_detach(iscsi_cfdata);
733e01460c0Sagc 			config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
734e01460c0Sagc 			config_cfdriver_detach(&iscsi_cd);
73597f8debdSpgoyette 			devsw_detach(NULL, &iscsi_cdevsw);
736e01460c0Sagc 			return ENXIO;
737e01460c0Sagc 		}
738d16ea69cSjoerg #endif
739e01460c0Sagc 		return 0;
740e01460c0Sagc 		break;
741e01460c0Sagc 
742e01460c0Sagc 	case MODULE_CMD_FINI:
743d16ea69cSjoerg #ifdef _MODULE
744e01460c0Sagc 		error = config_cfdata_detach(iscsi_cfdata);
745e01460c0Sagc 		if (error)
746e01460c0Sagc 			return error;
747e01460c0Sagc 
74897f8debdSpgoyette 		config_cfdata_detach(iscsi_cfdata);
749e01460c0Sagc 		config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
750e01460c0Sagc 		config_cfdriver_detach(&iscsi_cd);
751e01460c0Sagc 		devsw_detach(NULL, &iscsi_cdevsw);
752d16ea69cSjoerg #endif
753e01460c0Sagc 		return 0;
754e01460c0Sagc 		break;
755e01460c0Sagc 
7563cab8a33Sriz 	case MODULE_CMD_AUTOUNLOAD:
7573cab8a33Sriz 		return EBUSY;
7583cab8a33Sriz 		break;
7593cab8a33Sriz 
760e01460c0Sagc 	default:
761e01460c0Sagc 		return ENOTTY;
762e01460c0Sagc 		break;
763e01460c0Sagc 	}
764e01460c0Sagc }
765