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