123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- /* $OpenBSD: vscsi.c,v 1.38 2015/03/14 03:38:46 jsg 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/poll.h>
- #include <sys/selinfo.h>
- #include <scsi/scsi_all.h>
- #include <scsi/scsiconf.h>
- #include <dev/vscsivar.h>
- 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 scsi_link sc_link;
- 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;
- struct vscsi_ccb_list sc_ccb_t2i;
- int sc_ccb_tag;
- struct mutex sc_poll_mtx;
- struct rwlock sc_ioc_lock;
- struct selinfo sc_sel;
- struct mutex sc_sel_mtx;
- };
- #define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
- #define DEV2SC(_d) ((struct vscsi_softc *)device_lookup(&vscsi_cd, minor(_d)))
- 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 *);
- struct scsi_adapter vscsi_switch = {
- vscsi_cmd,
- scsi_minphys,
- vscsi_probe,
- vscsi_free
- };
- 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);
-
- struct filterops vscsi_filtops = {
- 1,
- NULL,
- filt_vscsidetach,
- filt_vscsiread
- };
- 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_BIO);
- 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);
- mtx_init(&sc->sc_sel_mtx, IPL_BIO);
- rw_init(&sc->sc_ioc_lock, "vscsiioc");
- scsi_iopool_init(&sc->sc_iopool, sc, vscsi_ccb_get, vscsi_ccb_put);
- sc->sc_link.adapter = &vscsi_switch;
- sc->sc_link.adapter_softc = sc;
- sc->sc_link.adapter_target = 256;
- sc->sc_link.adapter_buswidth = 256;
- sc->sc_link.openings = 16;
- sc->sc_link.pool = &sc->sc_iopool;
- memset(&saa, 0, sizeof(saa));
- saa.saa_sc_link = &sc->sc_link;
- 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->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);
- }
- mtx_leave(&sc->sc_state_mtx);
- if (!running) {
- xs->error = XS_DRIVER_STUFFUP;
- scsi_done(xs);
- return;
- }
- selwakeup(&sc->sc_sel);
- if (polled) {
- mtx_enter(&sc->sc_poll_mtx);
- while (ccb->ccb_xs != NULL)
- msleep(ccb, &sc->sc_poll_mtx, PRIBIO, "vscsipoll", 0);
- 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->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->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, 0, 0,
- "vscsiccb", NULL);
- pool_setipl(&sc->sc_ccb_pool, IPL_BIO);
- /* 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;
- struct scsi_link *link;
- 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;
- link = xs->sc_link;
- 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, 0);
- }
- int
- vscsipoll(dev_t dev, int events, struct proc *p)
- {
- struct vscsi_softc *sc = DEV2SC(dev);
- int revents = 0;
- if (sc == NULL)
- return (ENXIO);
- if (events & (POLLIN | POLLRDNORM)) {
- mtx_enter(&sc->sc_state_mtx);
- if (!TAILQ_EMPTY(&sc->sc_ccb_i2t))
- revents |= events & (POLLIN | POLLRDNORM);
- mtx_leave(&sc->sc_state_mtx);
- }
- if (revents == 0) {
- if (events & (POLLIN | POLLRDNORM))
- selrecord(p, &sc->sc_sel);
- }
- device_unref(&sc->sc_dev);
- return (revents);
- }
- int
- vscsikqfilter(dev_t dev, struct knote *kn)
- {
- struct vscsi_softc *sc = DEV2SC(dev);
- struct klist *klist;
- if (sc == NULL)
- return (ENXIO);
- klist = &sc->sc_sel.si_note;
- 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;
- mtx_enter(&sc->sc_sel_mtx);
- SLIST_INSERT_HEAD(klist, kn, kn_selnext);
- mtx_leave(&sc->sc_sel_mtx);
- /* 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;
- struct klist *klist = &sc->sc_sel.si_note;
-
- mtx_enter(&sc->sc_sel_mtx);
- SLIST_REMOVE(klist, kn, knote, kn_selnext);
- mtx_leave(&sc->sc_sel_mtx);
- device_unref(&sc->sc_dev);
- }
- int
- filt_vscsiread(struct knote *kn, long hint)
- {
- struct vscsi_softc *sc = kn->kn_hook;
- int event = 0;
- mtx_enter(&sc->sc_state_mtx);
- if (!TAILQ_EMPTY(&sc->sc_ccb_i2t))
- event = 1;
- mtx_leave(&sc->sc_state_mtx);
- return (event);
- }
- 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(&sc->sc_ref_count, &sc->sc_state_mtx,
- PRIBIO, "vscsiref", 0);
- }
- 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);
- }
|