2021-06-06 13:40:14 +03:00
|
|
|
/* $NetBSD: iscsi_main.c,v 1.36 2021/06/06 10:40:14 mlelstv Exp $ */
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Wasabi Systems, Inc.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
#include "iscsi_globals.h"
|
|
|
|
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/buf.h>
|
2015-05-30 23:09:47 +03:00
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/filedesc.h>
|
2011-10-24 01:15:02 +04:00
|
|
|
#include <sys/kmem.h>
|
|
|
|
#include <sys/socketvar.h>
|
2016-05-29 16:51:16 +03:00
|
|
|
#include <sys/sysctl.h>
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2015-08-20 17:40:16 +03:00
|
|
|
#include "ioconf.h"
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
/*------------------------- Global Variables ------------------------*/
|
|
|
|
|
|
|
|
extern struct cfdriver iscsi_cd;
|
|
|
|
|
|
|
|
#if defined(ISCSI_DEBUG)
|
2012-06-02 20:52:11 +04:00
|
|
|
int iscsi_debug_level = ISCSI_DEBUG;
|
2011-10-24 01:15:02 +04:00
|
|
|
#endif
|
2019-04-21 14:26:46 +03:00
|
|
|
bool iscsi_hex_bignums = false;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2016-05-29 16:51:16 +03:00
|
|
|
bool iscsi_detaching;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
/* the list of sessions */
|
2012-08-12 17:26:18 +04:00
|
|
|
session_list_t iscsi_sessions = TAILQ_HEAD_INITIALIZER(iscsi_sessions);
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
/* the number of active send threads (for cleanup thread) */
|
2012-08-12 17:26:18 +04:00
|
|
|
uint32_t iscsi_num_send_threads = 0;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
/* Our node name, alias, and ISID */
|
2012-08-12 17:26:18 +04:00
|
|
|
uint8_t iscsi_InitiatorName[ISCSI_STRING_LENGTH] = "";
|
|
|
|
uint8_t iscsi_InitiatorAlias[ISCSI_STRING_LENGTH] = "";
|
|
|
|
login_isid_t iscsi_InitiatorISID;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
System interface: autoconf and device structures
|
|
|
|
*/
|
|
|
|
|
2012-08-12 17:26:18 +04:00
|
|
|
static void iscsi_attach(device_t parent, device_t self, void *aux);
|
|
|
|
static int iscsi_match(device_t, cfdata_t, void *);
|
|
|
|
static int iscsi_detach(device_t, int);
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2016-05-29 16:51:16 +03:00
|
|
|
struct iscsi_softc {
|
|
|
|
device_t dev;
|
|
|
|
kmutex_t lock;
|
|
|
|
TAILQ_HEAD(, iscsifd) fds;
|
|
|
|
};
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
CFATTACH_DECL_NEW(iscsi, sizeof(struct iscsi_softc), iscsi_match, iscsi_attach,
|
|
|
|
iscsi_detach, NULL);
|
|
|
|
|
|
|
|
|
2012-08-12 17:26:18 +04:00
|
|
|
static dev_type_open(iscsiopen);
|
2015-05-30 23:09:47 +03:00
|
|
|
static int iscsiclose(struct file *);
|
|
|
|
|
|
|
|
static const struct fileops iscsi_fileops = {
|
2017-11-30 23:25:54 +03:00
|
|
|
.fo_name = "iscsi",
|
2021-06-06 13:40:14 +03:00
|
|
|
.fo_read = fbadop_read,
|
|
|
|
.fo_write = fbadop_write,
|
2015-05-30 23:09:47 +03:00
|
|
|
.fo_ioctl = iscsiioctl,
|
2021-06-06 13:40:14 +03:00
|
|
|
.fo_fcntl = fnullop_fcntl,
|
|
|
|
.fo_poll = fnullop_poll,
|
|
|
|
.fo_stat = fbadop_stat,
|
2015-05-30 23:09:47 +03:00
|
|
|
.fo_close = iscsiclose,
|
2021-06-06 13:40:14 +03:00
|
|
|
.fo_kqfilter = fnullop_kqfilter,
|
|
|
|
.fo_restart = fnullop_restart
|
2015-05-30 23:09:47 +03:00
|
|
|
};
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
struct cdevsw iscsi_cdevsw = {
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_open = iscsiopen,
|
2015-05-30 23:09:47 +03:00
|
|
|
.d_close = noclose,
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_read = noread,
|
|
|
|
.d_write = nowrite,
|
2015-05-30 23:09:47 +03:00
|
|
|
.d_ioctl = noioctl,
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_stop = nostop,
|
|
|
|
.d_tty = notty,
|
|
|
|
.d_poll = nopoll,
|
|
|
|
.d_mmap = nommap,
|
|
|
|
.d_kqfilter = nokqfilter,
|
2014-07-25 12:10:31 +04:00
|
|
|
.d_discard = nodiscard,
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_flag = D_OTHER
|
2011-10-24 01:15:02 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
STATIC void iscsi_scsipi_request(struct scsipi_channel *,
|
2016-06-05 07:50:23 +03:00
|
|
|
scsipi_adapter_req_t, void *);
|
2011-10-24 01:15:02 +04:00
|
|
|
STATIC void iscsi_minphys(struct buf *);
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
* Open and Close device interfaces. We don't really need them, because we don't
|
|
|
|
* have to keep track of device opens and closes from userland. But apps can't
|
|
|
|
* call ioctl without a handle to the device, and the kernel doesn't hand out
|
|
|
|
* handles without an open routine in the driver. So here they are in all their
|
|
|
|
* glory...
|
|
|
|
*******************************************************************************/
|
|
|
|
|
|
|
|
int
|
2015-05-15 19:24:30 +03:00
|
|
|
iscsiopen(dev_t dev, int flag, int mode, struct lwp *l)
|
2011-10-24 01:15:02 +04:00
|
|
|
{
|
2015-05-30 23:09:47 +03:00
|
|
|
struct iscsifd *d;
|
2016-05-29 16:51:16 +03:00
|
|
|
struct iscsi_softc *sc;
|
2015-05-30 23:09:47 +03:00
|
|
|
struct file *fp;
|
2016-05-29 16:51:16 +03:00
|
|
|
int error, fd, unit;
|
|
|
|
|
|
|
|
unit = minor(dev);
|
|
|
|
|
|
|
|
DEB(99, ("ISCSI Open unit=%d\n",unit));
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2016-05-29 16:51:16 +03:00
|
|
|
sc = device_lookup_private(&iscsi_cd, unit);
|
|
|
|
if (sc == NULL)
|
|
|
|
return ENXIO;
|
2015-05-30 23:09:47 +03:00
|
|
|
|
|
|
|
if ((error = fd_allocfile(&fp, &fd)) != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
d = kmem_alloc(sizeof(*d), KM_SLEEP);
|
2017-12-03 22:07:10 +03:00
|
|
|
d->fd_dev = sc->dev;
|
|
|
|
d->fd_unit = unit;
|
2016-05-29 16:51:16 +03:00
|
|
|
|
|
|
|
mutex_enter(&sc->lock);
|
|
|
|
if (iscsi_detaching) {
|
|
|
|
mutex_exit(&sc->lock);
|
|
|
|
kmem_free(d, sizeof(*d));
|
|
|
|
DEB(99, ("ISCSI Open aborting\n"));
|
|
|
|
fd_abort(curproc, fp, fd);
|
|
|
|
return ENXIO;
|
|
|
|
}
|
2017-12-03 22:07:10 +03:00
|
|
|
TAILQ_INSERT_TAIL(&sc->fds, d, fd_link);
|
2016-05-29 16:51:16 +03:00
|
|
|
mutex_exit(&sc->lock);
|
2015-05-30 23:09:47 +03:00
|
|
|
|
|
|
|
return fd_clone(fp, fd, flag, &iscsi_fileops, d);
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
|
|
|
|
2015-05-30 23:09:47 +03:00
|
|
|
static int
|
|
|
|
iscsiclose(struct file *fp)
|
2011-10-24 01:15:02 +04:00
|
|
|
{
|
2015-05-30 23:09:47 +03:00
|
|
|
struct iscsifd *d = fp->f_iscsi;
|
2016-05-29 16:51:16 +03:00
|
|
|
struct iscsi_softc *sc;
|
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
sc = device_lookup_private(&iscsi_cd, d->fd_unit);
|
2021-06-06 13:40:14 +03:00
|
|
|
if (sc != NULL) {
|
|
|
|
mutex_enter(&sc->lock);
|
|
|
|
TAILQ_REMOVE(&sc->fds, d, fd_link);
|
|
|
|
mutex_exit(&sc->lock);
|
2016-05-29 16:51:16 +03:00
|
|
|
}
|
|
|
|
|
2015-05-30 23:09:47 +03:00
|
|
|
kmem_free(d, sizeof(*d));
|
|
|
|
fp->f_iscsi = NULL;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
DEB(99, ("ISCSI Close\n"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The config Match routine.
|
|
|
|
* Not much to do here, either - this is a pseudo-device.
|
|
|
|
*/
|
|
|
|
|
2012-08-12 17:26:18 +04:00
|
|
|
static int
|
2011-10-24 01:15:02 +04:00
|
|
|
iscsi_match(device_t self, cfdata_t cfdata, void *arg)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iscsiattach:
|
|
|
|
* Only called when statically configured into a kernel
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
iscsiattach(int n)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
cfdata_t cf;
|
|
|
|
|
|
|
|
err = config_cfattach_attach(iscsi_cd.cd_name, &iscsi_ca);
|
|
|
|
if (err) {
|
|
|
|
aprint_error("%s: couldn't register cfattach: %d\n",
|
|
|
|
iscsi_cd.cd_name, err);
|
|
|
|
config_cfdriver_detach(&iscsi_cd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n > 1)
|
|
|
|
aprint_error("%s: only one device supported\n",
|
|
|
|
iscsi_cd.cd_name);
|
|
|
|
|
2019-10-01 21:00:07 +03:00
|
|
|
cf = kmem_alloc(sizeof(struct cfdata), KM_SLEEP);
|
2011-10-24 01:15:02 +04:00
|
|
|
cf->cf_name = iscsi_cd.cd_name;
|
|
|
|
cf->cf_atname = iscsi_cd.cd_name;
|
|
|
|
cf->cf_unit = 0;
|
|
|
|
cf->cf_fstate = FSTATE_NOTFOUND;
|
|
|
|
|
|
|
|
(void)config_attach_pseudo(cf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iscsi_attach:
|
|
|
|
* One-time inits go here. Not much for now, probably even less later.
|
|
|
|
*/
|
2012-08-12 17:26:18 +04:00
|
|
|
static void
|
2011-10-24 01:15:02 +04:00
|
|
|
iscsi_attach(device_t parent, device_t self, void *aux)
|
|
|
|
{
|
2016-05-29 16:51:16 +03:00
|
|
|
struct iscsi_softc *sc;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2016-05-29 16:51:16 +03:00
|
|
|
DEB(1, ("ISCSI: iscsi_attach, parent=%p, self=%p, aux=%p\n", parent,
|
2011-10-24 01:15:02 +04:00
|
|
|
self, aux));
|
2016-05-29 16:51:16 +03:00
|
|
|
sc = (struct iscsi_softc *) device_private(self);
|
|
|
|
sc->dev = self;
|
|
|
|
|
|
|
|
TAILQ_INIT(&sc->fds);
|
|
|
|
mutex_init(&sc->lock, MUTEX_DEFAULT, IPL_NONE);
|
|
|
|
|
|
|
|
iscsi_detaching = false;
|
|
|
|
iscsi_init_cleanup();
|
|
|
|
|
2019-07-13 20:06:00 +03:00
|
|
|
if (!pmf_device_register(self, NULL, NULL))
|
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
|
|
|
|
2020-05-14 00:59:45 +03:00
|
|
|
aprint_verbose("%s: attached. major = %d\n", iscsi_cd.cd_name,
|
2011-10-24 01:15:02 +04:00
|
|
|
cdevsw_lookup_major(&iscsi_cdevsw));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iscsi_detach:
|
|
|
|
* Cleanup.
|
|
|
|
*/
|
2012-08-12 17:26:18 +04:00
|
|
|
static int
|
2011-10-24 01:15:02 +04:00
|
|
|
iscsi_detach(device_t self, int flags)
|
|
|
|
{
|
2016-05-29 16:51:16 +03:00
|
|
|
struct iscsi_softc *sc;
|
2016-06-15 07:30:30 +03:00
|
|
|
int error;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2016-05-29 16:51:16 +03:00
|
|
|
DEB(1, ("ISCSI: detach\n"));
|
|
|
|
sc = (struct iscsi_softc *) device_private(self);
|
|
|
|
|
|
|
|
mutex_enter(&sc->lock);
|
|
|
|
if (!TAILQ_EMPTY(&sc->fds)) {
|
|
|
|
mutex_exit(&sc->lock);
|
|
|
|
return EBUSY;
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
2016-05-29 16:51:16 +03:00
|
|
|
iscsi_detaching = true;
|
|
|
|
mutex_exit(&sc->lock);
|
|
|
|
|
2016-06-15 07:30:30 +03:00
|
|
|
error = kill_all_sessions();
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
error = iscsi_destroy_cleanup();
|
|
|
|
if (error)
|
|
|
|
return error;
|
2016-05-29 16:51:16 +03:00
|
|
|
|
2019-07-13 20:06:00 +03:00
|
|
|
pmf_device_deregister(sc->dev);
|
|
|
|
|
2016-05-29 16:51:16 +03:00
|
|
|
mutex_destroy(&sc->lock);
|
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
typedef struct quirktab_t {
|
|
|
|
const char *tgt;
|
|
|
|
const char *iqn;
|
|
|
|
uint32_t quirks;
|
|
|
|
} quirktab_t;
|
|
|
|
|
|
|
|
static const quirktab_t quirktab[] = {
|
2015-05-15 21:28:36 +03:00
|
|
|
{ "StarWind", "iqn.2008-08.com.starwindsoftware", PQUIRK_ONLYBIG },
|
|
|
|
{ "UNH", "iqn.2002-10.edu.unh.",
|
|
|
|
PQUIRK_NOBIGMODESENSE |
|
|
|
|
PQUIRK_NOMODESENSE |
|
|
|
|
PQUIRK_NOSYNCCACHE },
|
|
|
|
{ "NetBSD", "iqn.1994-04.org.netbsd.", 0 },
|
|
|
|
{ "Unknown", "unknown", 0 },
|
|
|
|
{ NULL, NULL, 0 }
|
2011-10-24 01:15:02 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
/* loop through the quirktab looking for a match on target name */
|
|
|
|
static const quirktab_t *
|
|
|
|
getquirks(const char *iqn)
|
|
|
|
{
|
|
|
|
const quirktab_t *qp;
|
2015-05-15 21:28:36 +03:00
|
|
|
size_t iqnlen, quirklen;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2015-05-15 21:28:36 +03:00
|
|
|
if (iqn == NULL)
|
2011-10-24 01:15:02 +04:00
|
|
|
iqn = "unknown";
|
2015-05-15 21:28:36 +03:00
|
|
|
iqnlen = strlen(iqn);
|
2011-10-24 01:15:02 +04:00
|
|
|
for (qp = quirktab ; qp->iqn ; qp++) {
|
2015-05-15 21:28:36 +03:00
|
|
|
quirklen = strlen(qp->iqn);
|
|
|
|
if (quirklen > iqnlen)
|
|
|
|
continue;
|
|
|
|
if (memcmp(qp->iqn, iqn, quirklen) == 0)
|
2011-10-24 01:15:02 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return qp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* map_session
|
|
|
|
* This (indirectly) maps the existing LUNs for a target to SCSI devices
|
|
|
|
* by going through config_found to tell any child drivers that there's
|
|
|
|
* a new adapter.
|
|
|
|
* Note that each session is equivalent to a SCSI adapter.
|
|
|
|
*
|
|
|
|
* Parameter: the session pointer
|
|
|
|
*
|
|
|
|
* Returns: 1 on success, 0 on failure
|
|
|
|
*
|
|
|
|
* ToDo: Figuring out how to handle more than one LUN. It appears that
|
|
|
|
* the NetBSD SCSI LUN discovery doesn't use "report LUNs", and instead
|
|
|
|
* goes through the LUNs sequentially, stopping somewhere on the way if it
|
|
|
|
* gets an error. We may have to do some LUN mapping in here if this is
|
|
|
|
* really how things work.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2017-12-03 22:07:10 +03:00
|
|
|
map_session(session_t *sess, device_t dev)
|
2011-10-24 01:15:02 +04:00
|
|
|
{
|
2017-12-03 22:07:10 +03:00
|
|
|
struct scsipi_adapter *adapt = &sess->s_sc_adapter;
|
|
|
|
struct scsipi_channel *chan = &sess->s_sc_channel;
|
2011-10-24 01:15:02 +04:00
|
|
|
const quirktab_t *tgt;
|
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
mutex_enter(&sess->s_lock);
|
|
|
|
sess->s_send_window = max(2, window_size(sess, CCBS_FOR_SCSIPI));
|
|
|
|
mutex_exit(&sess->s_lock);
|
2016-06-15 07:30:30 +03:00
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
/*
|
|
|
|
* Fill in the scsipi_adapter.
|
|
|
|
*/
|
2016-05-29 16:51:16 +03:00
|
|
|
adapt->adapt_dev = dev;
|
2011-10-24 01:15:02 +04:00
|
|
|
adapt->adapt_nchannels = 1;
|
|
|
|
adapt->adapt_request = iscsi_scsipi_request;
|
|
|
|
adapt->adapt_minphys = iscsi_minphys;
|
2017-12-03 22:07:10 +03:00
|
|
|
adapt->adapt_openings = sess->s_send_window;
|
2016-06-15 07:30:30 +03:00
|
|
|
adapt->adapt_max_periph = CCBS_FOR_SCSIPI;
|
2016-12-25 09:55:28 +03:00
|
|
|
adapt->adapt_flags = SCSIPI_ADAPT_MPSAFE;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill in the scsipi_channel.
|
|
|
|
*/
|
|
|
|
if ((tgt = getquirks(chan->chan_name)) == NULL) {
|
|
|
|
tgt = getquirks("unknown");
|
|
|
|
}
|
|
|
|
chan->chan_name = tgt->tgt;
|
|
|
|
chan->chan_defquirks = tgt->quirks;
|
|
|
|
chan->chan_adapter = adapt;
|
|
|
|
chan->chan_bustype = &scsi_bustype;
|
|
|
|
chan->chan_channel = 0;
|
2016-06-15 07:30:30 +03:00
|
|
|
chan->chan_flags = SCSIPI_CHAN_NOSETTLE | SCSIPI_CHAN_CANGROW;
|
2011-10-24 01:15:02 +04:00
|
|
|
chan->chan_ntargets = 1;
|
2019-04-21 14:26:46 +03:00
|
|
|
chan->chan_nluns = 16;
|
2017-12-03 22:07:10 +03:00
|
|
|
chan->chan_id = sess->s_id;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
Merge thorpej-cfargs branch:
Simplify and make extensible the config_search() / config_found() /
config_attach() interfaces: rather than having different variants for
which arguments you want pass along, just have a single call that
takes a variadic list of tag-value arguments.
Adjust all call sites:
- Simplify wherever possible; don't pass along arguments that aren't
actually needed.
- Don't be explicit about what interface attribute is attaching if
the device only has one. (More simplification.)
- Add a config_probe() function to be used in indirect configuiration
situations, making is visibly easier to see when indirect config is
in play, and allowing for future change in semantics. (As of now,
this is just a wrapper around config_match(), but that is an
implementation detail.)
Remove unnecessary or redundant interface attributes where they're not
needed.
There are currently 5 "cfargs" defined:
- CFARG_SUBMATCH (submatch function for direct config)
- CFARG_SEARCH (search function for indirect config)
- CFARG_IATTR (interface attribte)
- CFARG_LOCATORS (locators array)
- CFARG_DEVHANDLE (devhandle_t - wraps OFW, ACPI, etc. handles)
...and a sentinel value CFARG_EOL.
Add some extra sanity checking to ensure that interface attributes
aren't ambiguous.
Use CFARG_DEVHANDLE in MI FDT, OFW, and ACPI code, and macppc and shark
ports to associate those device handles with device_t instance. This
will trickle trough to more places over time (need back-end for pre-OFW
Sun OBP; any others?).
2021-04-25 02:36:23 +03:00
|
|
|
sess->s_child_dev = config_found(dev, chan, scsiprint, CFARG_EOL);
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
return sess->s_child_dev != NULL;
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* unmap_session
|
|
|
|
* This (indirectly) unmaps the existing all LUNs for a target by
|
|
|
|
* telling the config system that the adapter has detached.
|
|
|
|
*
|
|
|
|
* Parameter: the session pointer
|
2012-12-29 15:05:29 +04:00
|
|
|
*
|
|
|
|
* Returns: 1 on success, 0 on failure
|
2011-10-24 01:15:02 +04:00
|
|
|
*/
|
|
|
|
|
2012-12-29 15:05:29 +04:00
|
|
|
int
|
2017-12-03 22:07:10 +03:00
|
|
|
unmap_session(session_t *sess)
|
2011-10-24 01:15:02 +04:00
|
|
|
{
|
|
|
|
device_t dev;
|
2012-12-29 15:05:29 +04:00
|
|
|
int rv = 1;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
if ((dev = sess->s_child_dev) != NULL) {
|
|
|
|
sess->s_child_dev = NULL;
|
2012-12-29 15:05:29 +04:00
|
|
|
if (config_detach(dev, 0))
|
|
|
|
rv = 0;
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
2012-12-29 15:05:29 +04:00
|
|
|
|
|
|
|
return rv;
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
|
|
|
|
2016-06-15 07:30:30 +03:00
|
|
|
/*
|
|
|
|
* grow_resources
|
|
|
|
* Try to grow openings up to current window size
|
|
|
|
*/
|
2021-03-07 15:30:03 +03:00
|
|
|
static int
|
2017-12-03 22:07:10 +03:00
|
|
|
grow_resources(session_t *sess)
|
2016-06-15 07:30:30 +03:00
|
|
|
{
|
2017-12-03 22:07:10 +03:00
|
|
|
struct scsipi_adapter *adapt = &sess->s_sc_adapter;
|
2016-06-15 07:30:30 +03:00
|
|
|
int win;
|
2021-03-07 15:30:03 +03:00
|
|
|
int rc = -1;
|
2016-06-15 07:30:30 +03:00
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
mutex_enter(&sess->s_lock);
|
|
|
|
if (sess->s_refcount < CCBS_FOR_SCSIPI &&
|
|
|
|
sess->s_send_window < CCBS_FOR_SCSIPI) {
|
|
|
|
win = window_size(sess, CCBS_FOR_SCSIPI - sess->s_refcount);
|
|
|
|
if (win > sess->s_send_window) {
|
|
|
|
sess->s_send_window++;
|
2016-06-15 07:30:30 +03:00
|
|
|
adapt->adapt_openings++;
|
2021-03-07 15:30:03 +03:00
|
|
|
rc = 0;
|
2017-12-03 22:07:10 +03:00
|
|
|
DEB(5, ("Grow send window to %d\n", sess->s_send_window));
|
2016-06-15 07:30:30 +03:00
|
|
|
}
|
|
|
|
}
|
2017-12-03 22:07:10 +03:00
|
|
|
mutex_exit(&sess->s_lock);
|
2021-03-07 15:30:03 +03:00
|
|
|
|
|
|
|
return rc;
|
2016-06-15 07:30:30 +03:00
|
|
|
}
|
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* SCSI interface routines
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iscsi_scsipi_request:
|
|
|
|
* Perform a request for the SCSIPI layer.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
iscsi_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
struct scsipi_adapter *adapt = chan->chan_adapter;
|
|
|
|
struct scsipi_xfer *xs;
|
2017-12-03 22:07:10 +03:00
|
|
|
session_t *sess;
|
2011-10-24 01:15:02 +04:00
|
|
|
int flags;
|
2012-12-29 15:05:29 +04:00
|
|
|
struct scsipi_xfer_mode *xm;
|
2016-06-15 07:30:30 +03:00
|
|
|
int error;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
sess = (session_t *) adapt; /* adapter is first field in session */
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
error = ref_session(sess);
|
2016-06-15 07:30:30 +03:00
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
switch (req) {
|
|
|
|
case ADAPTER_REQ_RUN_XFER:
|
|
|
|
DEB(9, ("ISCSI: scsipi_request RUN_XFER\n"));
|
|
|
|
xs = arg;
|
|
|
|
flags = xs->xs_control;
|
|
|
|
|
2016-06-15 07:30:30 +03:00
|
|
|
if (error) {
|
|
|
|
DEB(9, ("ISCSI: refcount too high: %d, winsize %d\n",
|
2017-12-03 22:07:10 +03:00
|
|
|
sess->s_refcount, sess->s_send_window));
|
2016-06-15 07:30:30 +03:00
|
|
|
xs->error = XS_BUSY;
|
|
|
|
xs->status = XS_BUSY;
|
|
|
|
scsipi_done(xs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
if ((flags & XS_CTL_POLL) != 0) {
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
DEBOUT(("Run Xfer request with polling\n"));
|
|
|
|
scsipi_done(xs);
|
2016-06-15 07:30:30 +03:00
|
|
|
break;
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* NOTE: It appears that XS_CTL_DATA_UIO is not actually used anywhere.
|
2016-05-29 16:51:16 +03:00
|
|
|
* Since it really would complicate matters to handle offsets
|
|
|
|
* into scatter-gather lists, and a number of other drivers don't
|
|
|
|
* handle uio-based data as well, XS_CTL_DATA_UIO isn't
|
|
|
|
* implemented in this driver (at least for now).
|
2011-10-24 01:15:02 +04:00
|
|
|
*/
|
|
|
|
if (flags & XS_CTL_DATA_UIO) {
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
DEBOUT(("Run Xfer with data in UIO\n"));
|
|
|
|
scsipi_done(xs);
|
2016-06-15 07:30:30 +03:00
|
|
|
break;
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
send_run_xfer(sess, xs);
|
|
|
|
DEB(15, ("scsipi_req returns, refcount = %d\n", sess->s_refcount));
|
2011-10-24 01:15:02 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
case ADAPTER_REQ_GROW_RESOURCES:
|
2016-05-29 16:51:16 +03:00
|
|
|
DEB(5, ("ISCSI: scsipi_request GROW_RESOURCES\n"));
|
2021-03-07 15:30:03 +03:00
|
|
|
if (grow_resources(sess)) {
|
|
|
|
/* reached maximum */
|
|
|
|
chan->chan_flags &= ~SCSIPI_CHAN_CANGROW;
|
|
|
|
}
|
2016-06-15 07:30:30 +03:00
|
|
|
break;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
case ADAPTER_REQ_SET_XFER_MODE:
|
|
|
|
DEB(5, ("ISCSI: scsipi_request SET_XFER_MODE\n"));
|
2012-12-29 15:05:29 +04:00
|
|
|
xm = (struct scsipi_xfer_mode *)arg;
|
|
|
|
xm->xm_mode = PERIPH_CAP_TQING;
|
|
|
|
scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
|
2016-06-15 07:30:30 +03:00
|
|
|
break;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
default:
|
2016-06-15 07:30:30 +03:00
|
|
|
DEBOUT(("ISCSI: scsipi_request with invalid REQ code %d\n", req));
|
2011-10-24 01:15:02 +04:00
|
|
|
break;
|
|
|
|
}
|
2016-06-15 07:30:30 +03:00
|
|
|
|
|
|
|
if (!error)
|
2017-12-03 22:07:10 +03:00
|
|
|
unref_session(sess);
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* cap the transfer at 64K */
|
|
|
|
#define ISCSI_MAX_XFER 65536
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iscsi_minphys:
|
|
|
|
* Limit a transfer to our maximum transfer size.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
iscsi_minphys(struct buf *bp)
|
|
|
|
{
|
|
|
|
if (bp->b_bcount > ISCSI_MAX_XFER) {
|
|
|
|
bp->b_bcount = ISCSI_MAX_XFER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* SCSI job execution helper routines
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iscsi_done:
|
|
|
|
*
|
|
|
|
* A CCB has completed execution. Pass the status back to the
|
|
|
|
* upper layer.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
iscsi_done(ccb_t *ccb)
|
|
|
|
{
|
2017-12-03 22:07:10 +03:00
|
|
|
struct scsipi_xfer *xs = ccb->ccb_xs;
|
2016-05-29 16:51:16 +03:00
|
|
|
DEB(9, ("iscsi_done\n"));
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
if (xs != NULL) {
|
2017-12-03 22:07:10 +03:00
|
|
|
xs->resid = ccb->ccb_residual;
|
|
|
|
ccb->ccb_xs = NULL;
|
|
|
|
xs->resid = ccb->ccb_residual;
|
2011-10-24 01:15:02 +04:00
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
switch (ccb->ccb_status) {
|
2011-10-24 01:15:02 +04:00
|
|
|
case ISCSI_STATUS_SUCCESS:
|
2016-05-29 16:51:16 +03:00
|
|
|
xs->error = XS_NOERROR;
|
|
|
|
xs->status = SCSI_OK;
|
2011-10-24 01:15:02 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ISCSI_STATUS_CHECK_CONDITION:
|
|
|
|
xs->error = XS_SENSE;
|
2016-05-29 16:51:16 +03:00
|
|
|
xs->status = SCSI_CHECK;
|
2011-10-24 01:15:02 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ISCSI_STATUS_TARGET_BUSY:
|
2016-06-15 07:30:30 +03:00
|
|
|
case ISCSI_STATUS_NO_RESOURCES:
|
2017-12-03 22:07:10 +03:00
|
|
|
DEBC(ccb->ccb_connection, 5, ("target busy, ccb %p\n", ccb));
|
2011-10-24 01:15:02 +04:00
|
|
|
xs->error = XS_BUSY;
|
2016-05-29 16:51:16 +03:00
|
|
|
xs->status = SCSI_BUSY;
|
2011-10-24 01:15:02 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ISCSI_STATUS_SOCKET_ERROR:
|
|
|
|
case ISCSI_STATUS_TIMEOUT:
|
|
|
|
xs->error = XS_SELTIMEOUT;
|
2016-05-29 16:51:16 +03:00
|
|
|
xs->status = SCSI_BUSY;
|
|
|
|
break;
|
|
|
|
|
2016-06-15 07:30:30 +03:00
|
|
|
case ISCSI_STATUS_QUEUE_FULL:
|
2017-12-03 22:07:10 +03:00
|
|
|
DEBC(ccb->ccb_connection, 5, ("queue full, ccb %p\n", ccb));
|
2016-05-29 16:51:16 +03:00
|
|
|
xs->error = XS_BUSY;
|
|
|
|
xs->status = SCSI_QUEUE_FULL;
|
2011-10-24 01:15:02 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-12-03 22:07:10 +03:00
|
|
|
unref_session(ccb->ccb_session);
|
2017-12-03 10:24:47 +03:00
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
DEB(99, ("Calling scsipi_done (%p), err = %d\n", xs, xs->error));
|
|
|
|
scsipi_done(xs);
|
|
|
|
DEB(99, ("scsipi_done returned\n"));
|
2016-05-29 16:51:16 +03:00
|
|
|
} else {
|
|
|
|
DEBOUT(("ISCSI: iscsi_done CCB %p without XS\n", ccb));
|
2011-10-24 01:15:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-29 16:51:16 +03:00
|
|
|
SYSCTL_SETUP(sysctl_iscsi_setup, "ISCSI subtree setup")
|
|
|
|
{
|
|
|
|
const struct sysctlnode *node = NULL;
|
|
|
|
|
|
|
|
sysctl_createv(clog, 0, NULL, &node,
|
|
|
|
CTLFLAG_PERMANENT,
|
|
|
|
CTLTYPE_NODE, "iscsi",
|
|
|
|
SYSCTL_DESCR("iscsi controls"),
|
|
|
|
NULL, 0, NULL, 0,
|
|
|
|
CTL_HW, CTL_CREATE, CTL_EOL);
|
2019-04-21 14:26:46 +03:00
|
|
|
sysctl_createv(clog, 0, &node, NULL,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
|
|
|
|
CTLTYPE_BOOL, "hexbignums",
|
|
|
|
SYSCTL_DESCR("encode parameters in hex"),
|
|
|
|
NULL, 0, &iscsi_hex_bignums, 0,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
2016-05-29 16:51:16 +03:00
|
|
|
|
|
|
|
#ifdef ISCSI_DEBUG
|
|
|
|
sysctl_createv(clog, 0, &node, NULL,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
|
|
|
|
CTLTYPE_INT, "debug",
|
|
|
|
SYSCTL_DESCR("debug level"),
|
|
|
|
NULL, 0, &iscsi_debug_level, sizeof(iscsi_debug_level),
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
/* Kernel Module support */
|
|
|
|
|
|
|
|
#include <sys/module.h>
|
|
|
|
|
2015-02-21 20:14:37 +03:00
|
|
|
MODULE(MODULE_CLASS_DRIVER, iscsi, NULL); /* Possibly a builtin module */
|
2014-11-25 00:49:17 +03:00
|
|
|
|
2015-02-21 20:14:37 +03:00
|
|
|
#ifdef _MODULE
|
2011-10-24 01:15:02 +04:00
|
|
|
static const struct cfiattrdata ibescsi_info = { "scsi", 1,
|
|
|
|
{{"channel", "-1", -1},}
|
|
|
|
};
|
2014-11-25 00:49:17 +03:00
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
static const struct cfiattrdata *const iscsi_attrs[] = { &ibescsi_info, NULL };
|
|
|
|
|
|
|
|
CFDRIVER_DECL(iscsi, DV_DULL, iscsi_attrs);
|
|
|
|
|
|
|
|
static struct cfdata iscsi_cfdata[] = {
|
|
|
|
{
|
|
|
|
.cf_name = "iscsi",
|
|
|
|
.cf_atname = "iscsi",
|
|
|
|
.cf_unit = 0, /* Only unit 0 is ever used */
|
|
|
|
.cf_fstate = FSTATE_NOTFOUND,
|
|
|
|
.cf_loc = NULL,
|
|
|
|
.cf_flags = 0,
|
|
|
|
.cf_pspec = NULL,
|
|
|
|
},
|
|
|
|
{ NULL, NULL, 0, 0, NULL, 0, NULL }
|
|
|
|
};
|
2015-02-21 20:14:37 +03:00
|
|
|
#endif
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
static int
|
|
|
|
iscsi_modcmd(modcmd_t cmd, void *arg)
|
|
|
|
{
|
2015-02-21 20:14:37 +03:00
|
|
|
#ifdef _MODULE
|
2011-10-24 01:15:02 +04:00
|
|
|
devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR;
|
|
|
|
int error;
|
2015-02-21 20:14:37 +03:00
|
|
|
#endif
|
2011-10-24 01:15:02 +04:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case MODULE_CMD_INIT:
|
2015-02-21 20:14:37 +03:00
|
|
|
#ifdef _MODULE
|
2011-10-24 01:15:02 +04:00
|
|
|
error = config_cfdriver_attach(&iscsi_cd);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = config_cfattach_attach(iscsi_cd.cd_name, &iscsi_ca);
|
|
|
|
if (error) {
|
|
|
|
config_cfdriver_detach(&iscsi_cd);
|
|
|
|
aprint_error("%s: unable to register cfattach\n",
|
|
|
|
iscsi_cd.cd_name);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = config_cfdata_attach(iscsi_cfdata, 1);
|
|
|
|
if (error) {
|
|
|
|
aprint_error("%s: unable to attach cfdata\n",
|
|
|
|
iscsi_cd.cd_name);
|
|
|
|
config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
|
|
|
|
config_cfdriver_detach(&iscsi_cd);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = devsw_attach(iscsi_cd.cd_name, NULL, &bmajor,
|
|
|
|
&iscsi_cdevsw, &cmajor);
|
|
|
|
if (error) {
|
|
|
|
aprint_error("%s: unable to register devsw\n",
|
|
|
|
iscsi_cd.cd_name);
|
|
|
|
config_cfdata_detach(iscsi_cfdata);
|
|
|
|
config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
|
|
|
|
config_cfdriver_detach(&iscsi_cd);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config_attach_pseudo(iscsi_cfdata) == NULL) {
|
|
|
|
aprint_error("%s: config_attach_pseudo failed\n",
|
|
|
|
iscsi_cd.cd_name);
|
|
|
|
config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
|
|
|
|
config_cfdriver_detach(&iscsi_cd);
|
|
|
|
return ENXIO;
|
|
|
|
}
|
2015-02-21 20:14:37 +03:00
|
|
|
#endif
|
2011-10-24 01:15:02 +04:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MODULE_CMD_FINI:
|
2015-02-21 20:14:37 +03:00
|
|
|
#ifdef _MODULE
|
2011-10-24 01:15:02 +04:00
|
|
|
error = config_cfdata_detach(iscsi_cfdata);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
|
|
|
|
config_cfdriver_detach(&iscsi_cd);
|
|
|
|
devsw_detach(NULL, &iscsi_cdevsw);
|
2015-02-21 20:14:37 +03:00
|
|
|
#endif
|
2011-10-24 01:15:02 +04:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
|
2012-05-28 07:43:18 +04:00
|
|
|
case MODULE_CMD_AUTOUNLOAD:
|
|
|
|
return EBUSY;
|
|
|
|
break;
|
|
|
|
|
2011-10-24 01:15:02 +04:00
|
|
|
default:
|
|
|
|
return ENOTTY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|