673 lines
14 KiB
C
673 lines
14 KiB
C
/* $OpenBSD: vscsi.c,v 1.62 2023/10/25 09:36:47 mvs Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2008 David Gwynne <dlg@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/device.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/rwlock.h>
|
|
#include <sys/pool.h>
|
|
#include <sys/task.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/event.h>
|
|
|
|
#include <scsi/scsi_all.h>
|
|
#include <scsi/scsiconf.h>
|
|
|
|
#include <dev/vscsivar.h>
|
|
|
|
/*
|
|
* Locks used to protect struct members and global data
|
|
* s sc_state_mtx
|
|
*/
|
|
|
|
int vscsi_match(struct device *, void *, void *);
|
|
void vscsi_attach(struct device *, struct device *, void *);
|
|
void vscsi_shutdown(void *);
|
|
|
|
struct vscsi_ccb {
|
|
TAILQ_ENTRY(vscsi_ccb) ccb_entry;
|
|
int ccb_tag;
|
|
struct scsi_xfer *ccb_xs;
|
|
size_t ccb_datalen;
|
|
};
|
|
|
|
TAILQ_HEAD(vscsi_ccb_list, vscsi_ccb);
|
|
|
|
enum vscsi_state {
|
|
VSCSI_S_CLOSED,
|
|
VSCSI_S_CONFIG,
|
|
VSCSI_S_RUNNING
|
|
};
|
|
|
|
struct vscsi_softc {
|
|
struct device sc_dev;
|
|
struct scsibus_softc *sc_scsibus;
|
|
|
|
struct mutex sc_state_mtx;
|
|
enum vscsi_state sc_state;
|
|
u_int sc_ref_count;
|
|
struct pool sc_ccb_pool;
|
|
|
|
struct scsi_iopool sc_iopool;
|
|
|
|
struct vscsi_ccb_list sc_ccb_i2t; /* [s] */
|
|
struct vscsi_ccb_list sc_ccb_t2i;
|
|
int sc_ccb_tag;
|
|
struct mutex sc_poll_mtx;
|
|
struct rwlock sc_ioc_lock;
|
|
|
|
struct klist sc_klist; /* [s] */
|
|
};
|
|
|
|
#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
|
|
#define DEV2SC(_d) ((struct vscsi_softc *)device_lookup(&vscsi_cd, minor(_d)))
|
|
|
|
const struct cfattach vscsi_ca = {
|
|
sizeof(struct vscsi_softc),
|
|
vscsi_match,
|
|
vscsi_attach
|
|
};
|
|
|
|
struct cfdriver vscsi_cd = {
|
|
NULL,
|
|
"vscsi",
|
|
DV_DULL
|
|
};
|
|
|
|
void vscsi_cmd(struct scsi_xfer *);
|
|
int vscsi_probe(struct scsi_link *);
|
|
void vscsi_free(struct scsi_link *);
|
|
|
|
const struct scsi_adapter vscsi_switch = {
|
|
vscsi_cmd, NULL, vscsi_probe, vscsi_free, NULL
|
|
};
|
|
|
|
int vscsi_i2t(struct vscsi_softc *, struct vscsi_ioc_i2t *);
|
|
int vscsi_data(struct vscsi_softc *, struct vscsi_ioc_data *, int);
|
|
int vscsi_t2i(struct vscsi_softc *, struct vscsi_ioc_t2i *);
|
|
int vscsi_devevent(struct vscsi_softc *, u_long,
|
|
struct vscsi_ioc_devevent *);
|
|
void vscsi_devevent_task(void *);
|
|
void vscsi_done(struct vscsi_softc *, struct vscsi_ccb *);
|
|
|
|
void * vscsi_ccb_get(void *);
|
|
void vscsi_ccb_put(void *, void *);
|
|
|
|
void filt_vscsidetach(struct knote *);
|
|
int filt_vscsiread(struct knote *, long);
|
|
int filt_vscsimodify(struct kevent *, struct knote *);
|
|
int filt_vscsiprocess(struct knote *, struct kevent *);
|
|
|
|
const struct filterops vscsi_filtops = {
|
|
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
|
|
.f_attach = NULL,
|
|
.f_detach = filt_vscsidetach,
|
|
.f_event = filt_vscsiread,
|
|
.f_modify = filt_vscsimodify,
|
|
.f_process = filt_vscsiprocess,
|
|
};
|
|
|
|
|
|
int
|
|
vscsi_match(struct device *parent, void *match, void *aux)
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
vscsi_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct vscsi_softc *sc = (struct vscsi_softc *)self;
|
|
struct scsibus_attach_args saa;
|
|
|
|
printf("\n");
|
|
|
|
mtx_init(&sc->sc_state_mtx, IPL_MPFLOOR);
|
|
sc->sc_state = VSCSI_S_CLOSED;
|
|
|
|
TAILQ_INIT(&sc->sc_ccb_i2t);
|
|
TAILQ_INIT(&sc->sc_ccb_t2i);
|
|
mtx_init(&sc->sc_poll_mtx, IPL_BIO);
|
|
rw_init(&sc->sc_ioc_lock, "vscsiioc");
|
|
scsi_iopool_init(&sc->sc_iopool, sc, vscsi_ccb_get, vscsi_ccb_put);
|
|
klist_init_mutex(&sc->sc_klist, &sc->sc_state_mtx);
|
|
|
|
saa.saa_adapter = &vscsi_switch;
|
|
saa.saa_adapter_softc = sc;
|
|
saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
|
|
saa.saa_adapter_buswidth = 256;
|
|
saa.saa_luns = 8;
|
|
saa.saa_openings = 16;
|
|
saa.saa_pool = &sc->sc_iopool;
|
|
saa.saa_quirks = saa.saa_flags = 0;
|
|
saa.saa_wwpn = saa.saa_wwnn = 0;
|
|
|
|
sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
|
|
&saa, scsiprint);
|
|
}
|
|
|
|
void
|
|
vscsi_cmd(struct scsi_xfer *xs)
|
|
{
|
|
struct scsi_link *link = xs->sc_link;
|
|
struct vscsi_softc *sc = link->bus->sb_adapter_softc;
|
|
struct vscsi_ccb *ccb = xs->io;
|
|
int polled = ISSET(xs->flags, SCSI_POLL);
|
|
int running = 0;
|
|
|
|
if (ISSET(xs->flags, SCSI_POLL) && ISSET(xs->flags, SCSI_NOSLEEP)) {
|
|
printf("%s: POLL && NOSLEEP for 0x%02x\n", DEVNAME(sc),
|
|
xs->cmd.opcode);
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
scsi_done(xs);
|
|
return;
|
|
}
|
|
|
|
ccb->ccb_xs = xs;
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
if (sc->sc_state == VSCSI_S_RUNNING) {
|
|
running = 1;
|
|
TAILQ_INSERT_TAIL(&sc->sc_ccb_i2t, ccb, ccb_entry);
|
|
}
|
|
knote_locked(&sc->sc_klist, 0);
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
if (!running) {
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
scsi_done(xs);
|
|
return;
|
|
}
|
|
|
|
if (polled) {
|
|
mtx_enter(&sc->sc_poll_mtx);
|
|
while (ccb->ccb_xs != NULL)
|
|
msleep_nsec(ccb, &sc->sc_poll_mtx, PRIBIO, "vscsipoll",
|
|
INFSLP);
|
|
mtx_leave(&sc->sc_poll_mtx);
|
|
scsi_done(xs);
|
|
}
|
|
}
|
|
|
|
void
|
|
vscsi_done(struct vscsi_softc *sc, struct vscsi_ccb *ccb)
|
|
{
|
|
struct scsi_xfer *xs = ccb->ccb_xs;
|
|
|
|
if (ISSET(xs->flags, SCSI_POLL)) {
|
|
mtx_enter(&sc->sc_poll_mtx);
|
|
ccb->ccb_xs = NULL;
|
|
wakeup(ccb);
|
|
mtx_leave(&sc->sc_poll_mtx);
|
|
} else
|
|
scsi_done(xs);
|
|
}
|
|
|
|
int
|
|
vscsi_probe(struct scsi_link *link)
|
|
{
|
|
struct vscsi_softc *sc = link->bus->sb_adapter_softc;
|
|
int rv = 0;
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
if (sc->sc_state == VSCSI_S_RUNNING)
|
|
sc->sc_ref_count++;
|
|
else
|
|
rv = ENXIO;
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
void
|
|
vscsi_free(struct scsi_link *link)
|
|
{
|
|
struct vscsi_softc *sc = link->bus->sb_adapter_softc;
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
sc->sc_ref_count--;
|
|
if (sc->sc_state != VSCSI_S_RUNNING && sc->sc_ref_count == 0)
|
|
wakeup(&sc->sc_ref_count);
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
}
|
|
|
|
int
|
|
vscsiopen(dev_t dev, int flags, int mode, struct proc *p)
|
|
{
|
|
struct vscsi_softc *sc = DEV2SC(dev);
|
|
enum vscsi_state state = VSCSI_S_RUNNING;
|
|
int rv = 0;
|
|
|
|
if (sc == NULL)
|
|
return (ENXIO);
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
if (sc->sc_state != VSCSI_S_CLOSED)
|
|
rv = EBUSY;
|
|
else
|
|
sc->sc_state = VSCSI_S_CONFIG;
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
if (rv != 0) {
|
|
device_unref(&sc->sc_dev);
|
|
return (rv);
|
|
}
|
|
|
|
pool_init(&sc->sc_ccb_pool, sizeof(struct vscsi_ccb), 0, IPL_BIO, 0,
|
|
"vscsiccb", NULL);
|
|
|
|
/* we need to guarantee some ccbs will be available for the iopool */
|
|
rv = pool_prime(&sc->sc_ccb_pool, 8);
|
|
if (rv != 0) {
|
|
pool_destroy(&sc->sc_ccb_pool);
|
|
state = VSCSI_S_CLOSED;
|
|
}
|
|
|
|
/* commit changes */
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
sc->sc_state = state;
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
device_unref(&sc->sc_dev);
|
|
return (rv);
|
|
}
|
|
|
|
int
|
|
vscsiioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
|
|
{
|
|
struct vscsi_softc *sc = DEV2SC(dev);
|
|
int read = 0;
|
|
int err = 0;
|
|
|
|
if (sc == NULL)
|
|
return (ENXIO);
|
|
|
|
rw_enter_write(&sc->sc_ioc_lock);
|
|
|
|
switch (cmd) {
|
|
case VSCSI_I2T:
|
|
err = vscsi_i2t(sc, (struct vscsi_ioc_i2t *)addr);
|
|
break;
|
|
|
|
case VSCSI_DATA_READ:
|
|
read = 1;
|
|
case VSCSI_DATA_WRITE:
|
|
err = vscsi_data(sc, (struct vscsi_ioc_data *)addr, read);
|
|
break;
|
|
|
|
case VSCSI_T2I:
|
|
err = vscsi_t2i(sc, (struct vscsi_ioc_t2i *)addr);
|
|
break;
|
|
|
|
case VSCSI_REQPROBE:
|
|
case VSCSI_REQDETACH:
|
|
err = vscsi_devevent(sc, cmd,
|
|
(struct vscsi_ioc_devevent *)addr);
|
|
break;
|
|
|
|
default:
|
|
err = ENOTTY;
|
|
break;
|
|
}
|
|
|
|
rw_exit_write(&sc->sc_ioc_lock);
|
|
|
|
device_unref(&sc->sc_dev);
|
|
return (err);
|
|
}
|
|
|
|
int
|
|
vscsi_i2t(struct vscsi_softc *sc, struct vscsi_ioc_i2t *i2t)
|
|
{
|
|
struct vscsi_ccb *ccb;
|
|
struct scsi_xfer *xs;
|
|
struct scsi_link *link;
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
ccb = TAILQ_FIRST(&sc->sc_ccb_i2t);
|
|
if (ccb != NULL)
|
|
TAILQ_REMOVE(&sc->sc_ccb_i2t, ccb, ccb_entry);
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
if (ccb == NULL)
|
|
return (EAGAIN);
|
|
|
|
xs = ccb->ccb_xs;
|
|
link = xs->sc_link;
|
|
|
|
i2t->tag = ccb->ccb_tag;
|
|
i2t->target = link->target;
|
|
i2t->lun = link->lun;
|
|
memcpy(&i2t->cmd, &xs->cmd, xs->cmdlen);
|
|
i2t->cmdlen = xs->cmdlen;
|
|
i2t->datalen = xs->datalen;
|
|
|
|
switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
|
|
case SCSI_DATA_IN:
|
|
i2t->direction = VSCSI_DIR_READ;
|
|
break;
|
|
case SCSI_DATA_OUT:
|
|
i2t->direction = VSCSI_DIR_WRITE;
|
|
break;
|
|
default:
|
|
i2t->direction = VSCSI_DIR_NONE;
|
|
break;
|
|
}
|
|
|
|
TAILQ_INSERT_TAIL(&sc->sc_ccb_t2i, ccb, ccb_entry);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
vscsi_data(struct vscsi_softc *sc, struct vscsi_ioc_data *data, int read)
|
|
{
|
|
struct vscsi_ccb *ccb;
|
|
struct scsi_xfer *xs;
|
|
int xsread;
|
|
u_int8_t *buf;
|
|
int rv = EINVAL;
|
|
|
|
TAILQ_FOREACH(ccb, &sc->sc_ccb_t2i, ccb_entry) {
|
|
if (ccb->ccb_tag == data->tag)
|
|
break;
|
|
}
|
|
if (ccb == NULL)
|
|
return (EFAULT);
|
|
|
|
xs = ccb->ccb_xs;
|
|
|
|
if (data->datalen > xs->datalen - ccb->ccb_datalen)
|
|
return (ENOMEM);
|
|
|
|
switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
|
|
case SCSI_DATA_IN:
|
|
xsread = 1;
|
|
break;
|
|
case SCSI_DATA_OUT:
|
|
xsread = 0;
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
|
|
if (read != xsread)
|
|
return (EINVAL);
|
|
|
|
buf = xs->data;
|
|
buf += ccb->ccb_datalen;
|
|
|
|
if (read)
|
|
rv = copyin(data->data, buf, data->datalen);
|
|
else
|
|
rv = copyout(buf, data->data, data->datalen);
|
|
|
|
if (rv == 0)
|
|
ccb->ccb_datalen += data->datalen;
|
|
|
|
return (rv);
|
|
}
|
|
|
|
int
|
|
vscsi_t2i(struct vscsi_softc *sc, struct vscsi_ioc_t2i *t2i)
|
|
{
|
|
struct vscsi_ccb *ccb;
|
|
struct scsi_xfer *xs;
|
|
int rv = 0;
|
|
|
|
TAILQ_FOREACH(ccb, &sc->sc_ccb_t2i, ccb_entry) {
|
|
if (ccb->ccb_tag == t2i->tag)
|
|
break;
|
|
}
|
|
if (ccb == NULL)
|
|
return (EFAULT);
|
|
|
|
TAILQ_REMOVE(&sc->sc_ccb_t2i, ccb, ccb_entry);
|
|
|
|
xs = ccb->ccb_xs;
|
|
|
|
xs->resid = xs->datalen - ccb->ccb_datalen;
|
|
xs->status = SCSI_OK;
|
|
|
|
switch (t2i->status) {
|
|
case VSCSI_STAT_DONE:
|
|
xs->error = XS_NOERROR;
|
|
break;
|
|
case VSCSI_STAT_SENSE:
|
|
xs->error = XS_SENSE;
|
|
memcpy(&xs->sense, &t2i->sense, sizeof(xs->sense));
|
|
break;
|
|
case VSCSI_STAT_RESET:
|
|
xs->error = XS_RESET;
|
|
break;
|
|
case VSCSI_STAT_ERR:
|
|
default:
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
break;
|
|
}
|
|
|
|
vscsi_done(sc, ccb);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
struct vscsi_devevent_task {
|
|
struct vscsi_softc *sc;
|
|
struct task t;
|
|
struct vscsi_ioc_devevent de;
|
|
u_long cmd;
|
|
};
|
|
|
|
int
|
|
vscsi_devevent(struct vscsi_softc *sc, u_long cmd,
|
|
struct vscsi_ioc_devevent *de)
|
|
{
|
|
struct vscsi_devevent_task *dt;
|
|
|
|
dt = malloc(sizeof(*dt), M_TEMP, M_WAITOK | M_CANFAIL);
|
|
if (dt == NULL)
|
|
return (ENOMEM);
|
|
|
|
task_set(&dt->t, vscsi_devevent_task, dt);
|
|
dt->sc = sc;
|
|
dt->de = *de;
|
|
dt->cmd = cmd;
|
|
|
|
device_ref(&sc->sc_dev);
|
|
task_add(systq, &dt->t);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
vscsi_devevent_task(void *xdt)
|
|
{
|
|
struct vscsi_devevent_task *dt = xdt;
|
|
struct vscsi_softc *sc = dt->sc;
|
|
int state;
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
state = sc->sc_state;
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
if (state != VSCSI_S_RUNNING)
|
|
goto gone;
|
|
|
|
switch (dt->cmd) {
|
|
case VSCSI_REQPROBE:
|
|
scsi_probe(sc->sc_scsibus, dt->de.target, dt->de.lun);
|
|
break;
|
|
case VSCSI_REQDETACH:
|
|
scsi_detach(sc->sc_scsibus, dt->de.target, dt->de.lun,
|
|
DETACH_FORCE);
|
|
break;
|
|
#ifdef DIAGNOSTIC
|
|
default:
|
|
panic("unexpected vscsi_devevent cmd");
|
|
/* NOTREACHED */
|
|
#endif
|
|
}
|
|
|
|
gone:
|
|
device_unref(&sc->sc_dev);
|
|
|
|
free(dt, M_TEMP, sizeof(*dt));
|
|
}
|
|
|
|
int
|
|
vscsikqfilter(dev_t dev, struct knote *kn)
|
|
{
|
|
struct vscsi_softc *sc = DEV2SC(dev);
|
|
|
|
if (sc == NULL)
|
|
return (ENXIO);
|
|
|
|
switch (kn->kn_filter) {
|
|
case EVFILT_READ:
|
|
kn->kn_fop = &vscsi_filtops;
|
|
break;
|
|
default:
|
|
device_unref(&sc->sc_dev);
|
|
return (EINVAL);
|
|
}
|
|
|
|
kn->kn_hook = sc;
|
|
klist_insert(&sc->sc_klist, kn);
|
|
|
|
/* device ref is given to the knote in the klist */
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
filt_vscsidetach(struct knote *kn)
|
|
{
|
|
struct vscsi_softc *sc = kn->kn_hook;
|
|
|
|
klist_remove(&sc->sc_klist, kn);
|
|
device_unref(&sc->sc_dev);
|
|
}
|
|
|
|
int
|
|
filt_vscsiread(struct knote *kn, long hint)
|
|
{
|
|
struct vscsi_softc *sc = kn->kn_hook;
|
|
|
|
return (!TAILQ_EMPTY(&sc->sc_ccb_i2t));
|
|
}
|
|
|
|
int
|
|
filt_vscsimodify(struct kevent *kev, struct knote *kn)
|
|
{
|
|
struct vscsi_softc *sc = kn->kn_hook;
|
|
int active;
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
active = knote_modify(kev, kn);
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
return (active);
|
|
}
|
|
|
|
int
|
|
filt_vscsiprocess(struct knote *kn, struct kevent *kev)
|
|
{
|
|
struct vscsi_softc *sc = kn->kn_hook;
|
|
int active;
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
active = knote_process(kn, kev);
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
return (active);
|
|
}
|
|
|
|
int
|
|
vscsiclose(dev_t dev, int flags, int mode, struct proc *p)
|
|
{
|
|
struct vscsi_softc *sc = DEV2SC(dev);
|
|
struct vscsi_ccb *ccb;
|
|
|
|
if (sc == NULL)
|
|
return (ENXIO);
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
KASSERT(sc->sc_state == VSCSI_S_RUNNING);
|
|
sc->sc_state = VSCSI_S_CONFIG;
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
scsi_activate(sc->sc_scsibus, -1, -1, DVACT_DEACTIVATE);
|
|
|
|
while ((ccb = TAILQ_FIRST(&sc->sc_ccb_t2i)) != NULL) {
|
|
TAILQ_REMOVE(&sc->sc_ccb_t2i, ccb, ccb_entry);
|
|
ccb->ccb_xs->error = XS_RESET;
|
|
vscsi_done(sc, ccb);
|
|
}
|
|
|
|
while ((ccb = TAILQ_FIRST(&sc->sc_ccb_i2t)) != NULL) {
|
|
TAILQ_REMOVE(&sc->sc_ccb_i2t, ccb, ccb_entry);
|
|
ccb->ccb_xs->error = XS_RESET;
|
|
vscsi_done(sc, ccb);
|
|
}
|
|
|
|
scsi_req_detach(sc->sc_scsibus, -1, -1, DETACH_FORCE);
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
while (sc->sc_ref_count > 0) {
|
|
msleep_nsec(&sc->sc_ref_count, &sc->sc_state_mtx,
|
|
PRIBIO, "vscsiref", INFSLP);
|
|
}
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
pool_destroy(&sc->sc_ccb_pool);
|
|
|
|
mtx_enter(&sc->sc_state_mtx);
|
|
sc->sc_state = VSCSI_S_CLOSED;
|
|
mtx_leave(&sc->sc_state_mtx);
|
|
|
|
device_unref(&sc->sc_dev);
|
|
return (0);
|
|
}
|
|
|
|
void *
|
|
vscsi_ccb_get(void *cookie)
|
|
{
|
|
struct vscsi_softc *sc = cookie;
|
|
struct vscsi_ccb *ccb = NULL;
|
|
|
|
ccb = pool_get(&sc->sc_ccb_pool, PR_NOWAIT);
|
|
if (ccb != NULL) {
|
|
ccb->ccb_tag = sc->sc_ccb_tag++;
|
|
ccb->ccb_datalen = 0;
|
|
}
|
|
|
|
return (ccb);
|
|
}
|
|
|
|
void
|
|
vscsi_ccb_put(void *cookie, void *io)
|
|
{
|
|
struct vscsi_softc *sc = cookie;
|
|
struct vscsi_ccb *ccb = io;
|
|
|
|
pool_put(&sc->sc_ccb_pool, ccb);
|
|
}
|