1*0f9e9ec2Sjsg /* $OpenBSD: vscsi.c,v 1.63 2024/05/13 01:15:50 jsg Exp $ */
2bfea8c94Sdlg
3bfea8c94Sdlg /*
4bfea8c94Sdlg * Copyright (c) 2008 David Gwynne <dlg@openbsd.org>
5bfea8c94Sdlg *
6bfea8c94Sdlg * Permission to use, copy, modify, and distribute this software for any
7bfea8c94Sdlg * purpose with or without fee is hereby granted, provided that the above
8bfea8c94Sdlg * copyright notice and this permission notice appear in all copies.
9bfea8c94Sdlg *
10bfea8c94Sdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11bfea8c94Sdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12bfea8c94Sdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13bfea8c94Sdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14bfea8c94Sdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15bfea8c94Sdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16bfea8c94Sdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bfea8c94Sdlg */
18bfea8c94Sdlg
19bfea8c94Sdlg #include <sys/param.h>
20bfea8c94Sdlg #include <sys/systm.h>
21bfea8c94Sdlg #include <sys/kernel.h>
22bfea8c94Sdlg #include <sys/malloc.h>
23bfea8c94Sdlg #include <sys/device.h>
24bfea8c94Sdlg #include <sys/conf.h>
25bfea8c94Sdlg #include <sys/queue.h>
26bfea8c94Sdlg #include <sys/rwlock.h>
27bfea8c94Sdlg #include <sys/pool.h>
28e4be0aa5Sdlg #include <sys/task.h>
29bfea8c94Sdlg #include <sys/ioctl.h>
3026a3a4bdSmvs #include <sys/event.h>
31bfea8c94Sdlg
32bfea8c94Sdlg #include <scsi/scsi_all.h>
33bfea8c94Sdlg #include <scsi/scsiconf.h>
34bfea8c94Sdlg
35bfea8c94Sdlg #include <dev/vscsivar.h>
36bfea8c94Sdlg
3726a3a4bdSmvs /*
3826a3a4bdSmvs * Locks used to protect struct members and global data
3926a3a4bdSmvs * s sc_state_mtx
4026a3a4bdSmvs */
4126a3a4bdSmvs
42bfea8c94Sdlg int vscsi_match(struct device *, void *, void *);
43bfea8c94Sdlg void vscsi_attach(struct device *, struct device *, void *);
44bfea8c94Sdlg
45bfea8c94Sdlg struct vscsi_ccb {
46bfea8c94Sdlg TAILQ_ENTRY(vscsi_ccb) ccb_entry;
47bfea8c94Sdlg int ccb_tag;
48bfea8c94Sdlg struct scsi_xfer *ccb_xs;
49bfea8c94Sdlg size_t ccb_datalen;
50bfea8c94Sdlg };
51bfea8c94Sdlg
52bfea8c94Sdlg TAILQ_HEAD(vscsi_ccb_list, vscsi_ccb);
53bfea8c94Sdlg
5432d02b2cSdlg enum vscsi_state {
5532d02b2cSdlg VSCSI_S_CLOSED,
5632d02b2cSdlg VSCSI_S_CONFIG,
5732d02b2cSdlg VSCSI_S_RUNNING
5832d02b2cSdlg };
5932d02b2cSdlg
60bfea8c94Sdlg struct vscsi_softc {
61bfea8c94Sdlg struct device sc_dev;
62bfea8c94Sdlg struct scsibus_softc *sc_scsibus;
63bfea8c94Sdlg
6432d02b2cSdlg struct mutex sc_state_mtx;
6532d02b2cSdlg enum vscsi_state sc_state;
663ae005b6Sdlg u_int sc_ref_count;
67bfea8c94Sdlg struct pool sc_ccb_pool;
6832d02b2cSdlg
6932d02b2cSdlg struct scsi_iopool sc_iopool;
7032d02b2cSdlg
7126a3a4bdSmvs struct vscsi_ccb_list sc_ccb_i2t; /* [s] */
72bfea8c94Sdlg struct vscsi_ccb_list sc_ccb_t2i;
73bfea8c94Sdlg int sc_ccb_tag;
7432d02b2cSdlg struct mutex sc_poll_mtx;
7532d02b2cSdlg struct rwlock sc_ioc_lock;
76bfea8c94Sdlg
7726a3a4bdSmvs struct klist sc_klist; /* [s] */
78bfea8c94Sdlg };
79bfea8c94Sdlg
80bfea8c94Sdlg #define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
81bfea8c94Sdlg #define DEV2SC(_d) ((struct vscsi_softc *)device_lookup(&vscsi_cd, minor(_d)))
82bfea8c94Sdlg
83471aeecfSnaddy const struct cfattach vscsi_ca = {
84bfea8c94Sdlg sizeof(struct vscsi_softc),
85bfea8c94Sdlg vscsi_match,
86bfea8c94Sdlg vscsi_attach
87bfea8c94Sdlg };
88bfea8c94Sdlg
89bfea8c94Sdlg struct cfdriver vscsi_cd = {
90bfea8c94Sdlg NULL,
91bfea8c94Sdlg "vscsi",
92bfea8c94Sdlg DV_DULL
93bfea8c94Sdlg };
94bfea8c94Sdlg
95bae03be6Skrw void vscsi_cmd(struct scsi_xfer *);
96bfea8c94Sdlg int vscsi_probe(struct scsi_link *);
973ae005b6Sdlg void vscsi_free(struct scsi_link *);
98bfea8c94Sdlg
99a454aff3Snaddy const struct scsi_adapter vscsi_switch = {
10021ceeee0Skrw vscsi_cmd, NULL, vscsi_probe, vscsi_free, NULL
101bfea8c94Sdlg };
102bfea8c94Sdlg
103bfea8c94Sdlg int vscsi_i2t(struct vscsi_softc *, struct vscsi_ioc_i2t *);
104bfea8c94Sdlg int vscsi_data(struct vscsi_softc *, struct vscsi_ioc_data *, int);
105bfea8c94Sdlg int vscsi_t2i(struct vscsi_softc *, struct vscsi_ioc_t2i *);
106e4be0aa5Sdlg int vscsi_devevent(struct vscsi_softc *, u_long,
107e4be0aa5Sdlg struct vscsi_ioc_devevent *);
108e4195480Sdlg void vscsi_devevent_task(void *);
10932d02b2cSdlg void vscsi_done(struct vscsi_softc *, struct vscsi_ccb *);
11032d02b2cSdlg
11132d02b2cSdlg void * vscsi_ccb_get(void *);
11232d02b2cSdlg void vscsi_ccb_put(void *, void *);
113bfea8c94Sdlg
114bfea8c94Sdlg void filt_vscsidetach(struct knote *);
115bfea8c94Sdlg int filt_vscsiread(struct knote *, long);
11626a3a4bdSmvs int filt_vscsimodify(struct kevent *, struct knote *);
11726a3a4bdSmvs int filt_vscsiprocess(struct knote *, struct kevent *);
118bfea8c94Sdlg
11994321eb4Svisa const struct filterops vscsi_filtops = {
12026a3a4bdSmvs .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
12194321eb4Svisa .f_attach = NULL,
12294321eb4Svisa .f_detach = filt_vscsidetach,
12394321eb4Svisa .f_event = filt_vscsiread,
12426a3a4bdSmvs .f_modify = filt_vscsimodify,
12526a3a4bdSmvs .f_process = filt_vscsiprocess,
126bfea8c94Sdlg };
127bfea8c94Sdlg
128bfea8c94Sdlg
129bfea8c94Sdlg int
vscsi_match(struct device * parent,void * match,void * aux)130bfea8c94Sdlg vscsi_match(struct device *parent, void *match, void *aux)
131bfea8c94Sdlg {
132bfea8c94Sdlg return (1);
133bfea8c94Sdlg }
134bfea8c94Sdlg
135bfea8c94Sdlg void
vscsi_attach(struct device * parent,struct device * self,void * aux)136bfea8c94Sdlg vscsi_attach(struct device *parent, struct device *self, void *aux)
137bfea8c94Sdlg {
138bfea8c94Sdlg struct vscsi_softc *sc = (struct vscsi_softc *)self;
139bfea8c94Sdlg struct scsibus_attach_args saa;
140bfea8c94Sdlg
141bfea8c94Sdlg printf("\n");
142bfea8c94Sdlg
14326a3a4bdSmvs mtx_init(&sc->sc_state_mtx, IPL_MPFLOOR);
14432d02b2cSdlg sc->sc_state = VSCSI_S_CLOSED;
14532d02b2cSdlg
14632d02b2cSdlg TAILQ_INIT(&sc->sc_ccb_i2t);
14732d02b2cSdlg TAILQ_INIT(&sc->sc_ccb_t2i);
14832d02b2cSdlg mtx_init(&sc->sc_poll_mtx, IPL_BIO);
14932d02b2cSdlg rw_init(&sc->sc_ioc_lock, "vscsiioc");
15032d02b2cSdlg scsi_iopool_init(&sc->sc_iopool, sc, vscsi_ccb_get, vscsi_ccb_put);
15126a3a4bdSmvs klist_init_mutex(&sc->sc_klist, &sc->sc_state_mtx);
152bfea8c94Sdlg
153ead808c4Skrw saa.saa_adapter = &vscsi_switch;
154ead808c4Skrw saa.saa_adapter_softc = sc;
155ead808c4Skrw saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
156ead808c4Skrw saa.saa_adapter_buswidth = 256;
157ead808c4Skrw saa.saa_luns = 8;
158e5eae15dSkrw saa.saa_openings = 16;
159e5eae15dSkrw saa.saa_pool = &sc->sc_iopool;
160e5eae15dSkrw saa.saa_quirks = saa.saa_flags = 0;
161e5eae15dSkrw saa.saa_wwpn = saa.saa_wwnn = 0;
162bfea8c94Sdlg
163bfea8c94Sdlg sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
164bfea8c94Sdlg &saa, scsiprint);
165bfea8c94Sdlg }
166bfea8c94Sdlg
167bae03be6Skrw void
vscsi_cmd(struct scsi_xfer * xs)168bfea8c94Sdlg vscsi_cmd(struct scsi_xfer *xs)
169bfea8c94Sdlg {
170bfea8c94Sdlg struct scsi_link *link = xs->sc_link;
1710b29cb40Skrw struct vscsi_softc *sc = link->bus->sb_adapter_softc;
17232d02b2cSdlg struct vscsi_ccb *ccb = xs->io;
173bfea8c94Sdlg int polled = ISSET(xs->flags, SCSI_POLL);
1741b1a1ad0Smatthew int running = 0;
175bfea8c94Sdlg
176bfea8c94Sdlg if (ISSET(xs->flags, SCSI_POLL) && ISSET(xs->flags, SCSI_NOSLEEP)) {
177bfea8c94Sdlg printf("%s: POLL && NOSLEEP for 0x%02x\n", DEVNAME(sc),
178664c6166Skrw xs->cmd.opcode);
17932d02b2cSdlg xs->error = XS_DRIVER_STUFFUP;
18032d02b2cSdlg scsi_done(xs);
1811b1a1ad0Smatthew return;
182bfea8c94Sdlg }
183bfea8c94Sdlg
184bfea8c94Sdlg ccb->ccb_xs = xs;
185d2628efdSdlg
186d2628efdSdlg mtx_enter(&sc->sc_state_mtx);
1871b1a1ad0Smatthew if (sc->sc_state == VSCSI_S_RUNNING) {
1881b1a1ad0Smatthew running = 1;
189bfea8c94Sdlg TAILQ_INSERT_TAIL(&sc->sc_ccb_i2t, ccb, ccb_entry);
1901b1a1ad0Smatthew }
19126a3a4bdSmvs knote_locked(&sc->sc_klist, 0);
192d2628efdSdlg mtx_leave(&sc->sc_state_mtx);
193d2628efdSdlg
194d2628efdSdlg if (!running) {
195d2628efdSdlg xs->error = XS_DRIVER_STUFFUP;
196d2628efdSdlg scsi_done(xs);
197d2628efdSdlg return;
198d2628efdSdlg }
199bfea8c94Sdlg
200bfea8c94Sdlg if (polled) {
20132d02b2cSdlg mtx_enter(&sc->sc_poll_mtx);
202bfea8c94Sdlg while (ccb->ccb_xs != NULL)
20303604742Smpi msleep_nsec(ccb, &sc->sc_poll_mtx, PRIBIO, "vscsipoll",
20403604742Smpi INFSLP);
20532d02b2cSdlg mtx_leave(&sc->sc_poll_mtx);
20632d02b2cSdlg scsi_done(xs);
207bfea8c94Sdlg }
208bfea8c94Sdlg }
209bfea8c94Sdlg
210bfea8c94Sdlg void
vscsi_done(struct vscsi_softc * sc,struct vscsi_ccb * ccb)21132d02b2cSdlg vscsi_done(struct vscsi_softc *sc, struct vscsi_ccb *ccb)
212bfea8c94Sdlg {
21332d02b2cSdlg struct scsi_xfer *xs = ccb->ccb_xs;
21432d02b2cSdlg
21532d02b2cSdlg if (ISSET(xs->flags, SCSI_POLL)) {
21632d02b2cSdlg mtx_enter(&sc->sc_poll_mtx);
21732d02b2cSdlg ccb->ccb_xs = NULL;
21832d02b2cSdlg wakeup(ccb);
21932d02b2cSdlg mtx_leave(&sc->sc_poll_mtx);
22032d02b2cSdlg } else
221bfea8c94Sdlg scsi_done(xs);
222bfea8c94Sdlg }
223bfea8c94Sdlg
224bfea8c94Sdlg int
vscsi_probe(struct scsi_link * link)225bfea8c94Sdlg vscsi_probe(struct scsi_link *link)
226bfea8c94Sdlg {
2270b29cb40Skrw struct vscsi_softc *sc = link->bus->sb_adapter_softc;
2283ae005b6Sdlg int rv = 0;
229bfea8c94Sdlg
230d2628efdSdlg mtx_enter(&sc->sc_state_mtx);
2313ae005b6Sdlg if (sc->sc_state == VSCSI_S_RUNNING)
2323ae005b6Sdlg sc->sc_ref_count++;
2333ae005b6Sdlg else
2343ae005b6Sdlg rv = ENXIO;
235d2628efdSdlg mtx_leave(&sc->sc_state_mtx);
236bfea8c94Sdlg
237d2628efdSdlg return (rv);
238bfea8c94Sdlg }
239bfea8c94Sdlg
2403ae005b6Sdlg void
vscsi_free(struct scsi_link * link)2413ae005b6Sdlg vscsi_free(struct scsi_link *link)
2423ae005b6Sdlg {
2430b29cb40Skrw struct vscsi_softc *sc = link->bus->sb_adapter_softc;
2443ae005b6Sdlg
2453ae005b6Sdlg mtx_enter(&sc->sc_state_mtx);
2463ae005b6Sdlg sc->sc_ref_count--;
2473ae005b6Sdlg if (sc->sc_state != VSCSI_S_RUNNING && sc->sc_ref_count == 0)
2483ae005b6Sdlg wakeup(&sc->sc_ref_count);
2493ae005b6Sdlg mtx_leave(&sc->sc_state_mtx);
2503ae005b6Sdlg }
2513ae005b6Sdlg
252bfea8c94Sdlg int
vscsiopen(dev_t dev,int flags,int mode,struct proc * p)253bfea8c94Sdlg vscsiopen(dev_t dev, int flags, int mode, struct proc *p)
254bfea8c94Sdlg {
255bfea8c94Sdlg struct vscsi_softc *sc = DEV2SC(dev);
25632d02b2cSdlg enum vscsi_state state = VSCSI_S_RUNNING;
25732d02b2cSdlg int rv = 0;
258bfea8c94Sdlg
259bfea8c94Sdlg if (sc == NULL)
260bfea8c94Sdlg return (ENXIO);
261bfea8c94Sdlg
26232d02b2cSdlg mtx_enter(&sc->sc_state_mtx);
26332d02b2cSdlg if (sc->sc_state != VSCSI_S_CLOSED)
26432d02b2cSdlg rv = EBUSY;
26532d02b2cSdlg else
26632d02b2cSdlg sc->sc_state = VSCSI_S_CONFIG;
26732d02b2cSdlg mtx_leave(&sc->sc_state_mtx);
26832d02b2cSdlg
269198400c0Smatthew if (rv != 0) {
270198400c0Smatthew device_unref(&sc->sc_dev);
271bfea8c94Sdlg return (rv);
272198400c0Smatthew }
273bfea8c94Sdlg
2741378bae2Sdlg pool_init(&sc->sc_ccb_pool, sizeof(struct vscsi_ccb), 0, IPL_BIO, 0,
275bfea8c94Sdlg "vscsiccb", NULL);
276bfea8c94Sdlg
27732d02b2cSdlg /* we need to guarantee some ccbs will be available for the iopool */
27832d02b2cSdlg rv = pool_prime(&sc->sc_ccb_pool, 8);
27932d02b2cSdlg if (rv != 0) {
28032d02b2cSdlg pool_destroy(&sc->sc_ccb_pool);
28132d02b2cSdlg state = VSCSI_S_CLOSED;
28232d02b2cSdlg }
283bfea8c94Sdlg
28432d02b2cSdlg /* commit changes */
28532d02b2cSdlg mtx_enter(&sc->sc_state_mtx);
28632d02b2cSdlg sc->sc_state = state;
28732d02b2cSdlg mtx_leave(&sc->sc_state_mtx);
28832d02b2cSdlg
289198400c0Smatthew device_unref(&sc->sc_dev);
29032d02b2cSdlg return (rv);
291bfea8c94Sdlg }
292bfea8c94Sdlg
293bfea8c94Sdlg int
vscsiioctl(dev_t dev,u_long cmd,caddr_t addr,int flags,struct proc * p)294bfea8c94Sdlg vscsiioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
295bfea8c94Sdlg {
296bfea8c94Sdlg struct vscsi_softc *sc = DEV2SC(dev);
297bfea8c94Sdlg int read = 0;
298bfea8c94Sdlg int err = 0;
299bfea8c94Sdlg
300b6616845Smikeb if (sc == NULL)
301b6616845Smikeb return (ENXIO);
302b6616845Smikeb
30332d02b2cSdlg rw_enter_write(&sc->sc_ioc_lock);
30432d02b2cSdlg
305bfea8c94Sdlg switch (cmd) {
306bfea8c94Sdlg case VSCSI_I2T:
307bfea8c94Sdlg err = vscsi_i2t(sc, (struct vscsi_ioc_i2t *)addr);
308bfea8c94Sdlg break;
309bfea8c94Sdlg
310bfea8c94Sdlg case VSCSI_DATA_READ:
311bfea8c94Sdlg read = 1;
312bfea8c94Sdlg case VSCSI_DATA_WRITE:
313bfea8c94Sdlg err = vscsi_data(sc, (struct vscsi_ioc_data *)addr, read);
314bfea8c94Sdlg break;
315bfea8c94Sdlg
316bfea8c94Sdlg case VSCSI_T2I:
317bfea8c94Sdlg err = vscsi_t2i(sc, (struct vscsi_ioc_t2i *)addr);
318bfea8c94Sdlg break;
319bfea8c94Sdlg
320ee9d3df0Sdlg case VSCSI_REQPROBE:
321ee9d3df0Sdlg case VSCSI_REQDETACH:
322e4be0aa5Sdlg err = vscsi_devevent(sc, cmd,
323e4be0aa5Sdlg (struct vscsi_ioc_devevent *)addr);
324ee9d3df0Sdlg break;
325ee9d3df0Sdlg
326bfea8c94Sdlg default:
327bfea8c94Sdlg err = ENOTTY;
328bfea8c94Sdlg break;
329bfea8c94Sdlg }
330bfea8c94Sdlg
33132d02b2cSdlg rw_exit_write(&sc->sc_ioc_lock);
33232d02b2cSdlg
333198400c0Smatthew device_unref(&sc->sc_dev);
334bfea8c94Sdlg return (err);
335bfea8c94Sdlg }
336bfea8c94Sdlg
337bfea8c94Sdlg int
vscsi_i2t(struct vscsi_softc * sc,struct vscsi_ioc_i2t * i2t)338bfea8c94Sdlg vscsi_i2t(struct vscsi_softc *sc, struct vscsi_ioc_i2t *i2t)
339bfea8c94Sdlg {
340bfea8c94Sdlg struct vscsi_ccb *ccb;
341bfea8c94Sdlg struct scsi_xfer *xs;
342bfea8c94Sdlg struct scsi_link *link;
343bfea8c94Sdlg
344d2628efdSdlg mtx_enter(&sc->sc_state_mtx);
345bfea8c94Sdlg ccb = TAILQ_FIRST(&sc->sc_ccb_i2t);
346bfea8c94Sdlg if (ccb != NULL)
347bfea8c94Sdlg TAILQ_REMOVE(&sc->sc_ccb_i2t, ccb, ccb_entry);
348d2628efdSdlg mtx_leave(&sc->sc_state_mtx);
349bfea8c94Sdlg
350bfea8c94Sdlg if (ccb == NULL)
351bfea8c94Sdlg return (EAGAIN);
352bfea8c94Sdlg
353bfea8c94Sdlg xs = ccb->ccb_xs;
354bfea8c94Sdlg link = xs->sc_link;
355bfea8c94Sdlg
356bfea8c94Sdlg i2t->tag = ccb->ccb_tag;
357bfea8c94Sdlg i2t->target = link->target;
358bfea8c94Sdlg i2t->lun = link->lun;
359664c6166Skrw memcpy(&i2t->cmd, &xs->cmd, xs->cmdlen);
360bfea8c94Sdlg i2t->cmdlen = xs->cmdlen;
361bfea8c94Sdlg i2t->datalen = xs->datalen;
362bfea8c94Sdlg
363bfea8c94Sdlg switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
364bfea8c94Sdlg case SCSI_DATA_IN:
365bfea8c94Sdlg i2t->direction = VSCSI_DIR_READ;
366bfea8c94Sdlg break;
367bfea8c94Sdlg case SCSI_DATA_OUT:
368bfea8c94Sdlg i2t->direction = VSCSI_DIR_WRITE;
369bfea8c94Sdlg break;
370bfea8c94Sdlg default:
371bfea8c94Sdlg i2t->direction = VSCSI_DIR_NONE;
372bfea8c94Sdlg break;
373bfea8c94Sdlg }
374bfea8c94Sdlg
375bfea8c94Sdlg TAILQ_INSERT_TAIL(&sc->sc_ccb_t2i, ccb, ccb_entry);
376bfea8c94Sdlg
377bfea8c94Sdlg return (0);
378bfea8c94Sdlg }
379bfea8c94Sdlg
380bfea8c94Sdlg int
vscsi_data(struct vscsi_softc * sc,struct vscsi_ioc_data * data,int read)381bfea8c94Sdlg vscsi_data(struct vscsi_softc *sc, struct vscsi_ioc_data *data, int read)
382bfea8c94Sdlg {
383bfea8c94Sdlg struct vscsi_ccb *ccb;
384bfea8c94Sdlg struct scsi_xfer *xs;
385bfea8c94Sdlg int xsread;
386bfea8c94Sdlg u_int8_t *buf;
387bfea8c94Sdlg int rv = EINVAL;
388bfea8c94Sdlg
389bfea8c94Sdlg TAILQ_FOREACH(ccb, &sc->sc_ccb_t2i, ccb_entry) {
390bfea8c94Sdlg if (ccb->ccb_tag == data->tag)
391bfea8c94Sdlg break;
392bfea8c94Sdlg }
393bfea8c94Sdlg if (ccb == NULL)
394bfea8c94Sdlg return (EFAULT);
395bfea8c94Sdlg
396bfea8c94Sdlg xs = ccb->ccb_xs;
397bfea8c94Sdlg
39886de6fecSdlg if (data->datalen > xs->datalen - ccb->ccb_datalen)
399bfea8c94Sdlg return (ENOMEM);
400bfea8c94Sdlg
401bfea8c94Sdlg switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
402bfea8c94Sdlg case SCSI_DATA_IN:
403bfea8c94Sdlg xsread = 1;
404bfea8c94Sdlg break;
405bfea8c94Sdlg case SCSI_DATA_OUT:
406bfea8c94Sdlg xsread = 0;
407bfea8c94Sdlg break;
408bfea8c94Sdlg default:
409bfea8c94Sdlg return (EINVAL);
410bfea8c94Sdlg }
411bfea8c94Sdlg
412bfea8c94Sdlg if (read != xsread)
413bfea8c94Sdlg return (EINVAL);
414bfea8c94Sdlg
415bfea8c94Sdlg buf = xs->data;
416bfea8c94Sdlg buf += ccb->ccb_datalen;
417bfea8c94Sdlg
418bfea8c94Sdlg if (read)
419bfea8c94Sdlg rv = copyin(data->data, buf, data->datalen);
420bfea8c94Sdlg else
421bfea8c94Sdlg rv = copyout(buf, data->data, data->datalen);
422bfea8c94Sdlg
423bfea8c94Sdlg if (rv == 0)
424bfea8c94Sdlg ccb->ccb_datalen += data->datalen;
425bfea8c94Sdlg
426bfea8c94Sdlg return (rv);
427bfea8c94Sdlg }
428bfea8c94Sdlg
429bfea8c94Sdlg int
vscsi_t2i(struct vscsi_softc * sc,struct vscsi_ioc_t2i * t2i)430bfea8c94Sdlg vscsi_t2i(struct vscsi_softc *sc, struct vscsi_ioc_t2i *t2i)
431bfea8c94Sdlg {
432bfea8c94Sdlg struct vscsi_ccb *ccb;
433bfea8c94Sdlg struct scsi_xfer *xs;
434bfea8c94Sdlg int rv = 0;
435bfea8c94Sdlg
436bfea8c94Sdlg TAILQ_FOREACH(ccb, &sc->sc_ccb_t2i, ccb_entry) {
437bfea8c94Sdlg if (ccb->ccb_tag == t2i->tag)
438bfea8c94Sdlg break;
439bfea8c94Sdlg }
440bfea8c94Sdlg if (ccb == NULL)
441bfea8c94Sdlg return (EFAULT);
442bfea8c94Sdlg
443bfea8c94Sdlg TAILQ_REMOVE(&sc->sc_ccb_t2i, ccb, ccb_entry);
444bfea8c94Sdlg
445bfea8c94Sdlg xs = ccb->ccb_xs;
446bfea8c94Sdlg
447bfea8c94Sdlg xs->resid = xs->datalen - ccb->ccb_datalen;
448bfea8c94Sdlg xs->status = SCSI_OK;
449bfea8c94Sdlg
450bfea8c94Sdlg switch (t2i->status) {
451bfea8c94Sdlg case VSCSI_STAT_DONE:
452bfea8c94Sdlg xs->error = XS_NOERROR;
453bfea8c94Sdlg break;
454bfea8c94Sdlg case VSCSI_STAT_SENSE:
455bfea8c94Sdlg xs->error = XS_SENSE;
456bea3491dStedu memcpy(&xs->sense, &t2i->sense, sizeof(xs->sense));
457bfea8c94Sdlg break;
458d1c34fb1Sdlg case VSCSI_STAT_RESET:
459d1c34fb1Sdlg xs->error = XS_RESET;
460d1c34fb1Sdlg break;
461bfea8c94Sdlg case VSCSI_STAT_ERR:
462bfea8c94Sdlg default:
463bfea8c94Sdlg xs->error = XS_DRIVER_STUFFUP;
464bfea8c94Sdlg break;
465bfea8c94Sdlg }
466bfea8c94Sdlg
46732d02b2cSdlg vscsi_done(sc, ccb);
468bfea8c94Sdlg
469bfea8c94Sdlg return (rv);
470bfea8c94Sdlg }
471bfea8c94Sdlg
472e4be0aa5Sdlg struct vscsi_devevent_task {
4734123c26bSdlg struct vscsi_softc *sc;
474e4be0aa5Sdlg struct task t;
475e4be0aa5Sdlg struct vscsi_ioc_devevent de;
476e4be0aa5Sdlg u_long cmd;
477e4be0aa5Sdlg };
478e4be0aa5Sdlg
479e4be0aa5Sdlg int
vscsi_devevent(struct vscsi_softc * sc,u_long cmd,struct vscsi_ioc_devevent * de)480e4be0aa5Sdlg vscsi_devevent(struct vscsi_softc *sc, u_long cmd,
481e4be0aa5Sdlg struct vscsi_ioc_devevent *de)
482e4be0aa5Sdlg {
483e4be0aa5Sdlg struct vscsi_devevent_task *dt;
484e4be0aa5Sdlg
485e4be0aa5Sdlg dt = malloc(sizeof(*dt), M_TEMP, M_WAITOK | M_CANFAIL);
486e4be0aa5Sdlg if (dt == NULL)
487e4be0aa5Sdlg return (ENOMEM);
488e4be0aa5Sdlg
489e4195480Sdlg task_set(&dt->t, vscsi_devevent_task, dt);
4904123c26bSdlg dt->sc = sc;
491e4be0aa5Sdlg dt->de = *de;
492e4be0aa5Sdlg dt->cmd = cmd;
493e4be0aa5Sdlg
494e4be0aa5Sdlg device_ref(&sc->sc_dev);
495e4be0aa5Sdlg task_add(systq, &dt->t);
496e4be0aa5Sdlg
497e4be0aa5Sdlg return (0);
498e4be0aa5Sdlg }
499e4be0aa5Sdlg
500e4be0aa5Sdlg void
vscsi_devevent_task(void * xdt)501e4195480Sdlg vscsi_devevent_task(void *xdt)
502e4be0aa5Sdlg {
503e4be0aa5Sdlg struct vscsi_devevent_task *dt = xdt;
5044123c26bSdlg struct vscsi_softc *sc = dt->sc;
505e4be0aa5Sdlg int state;
506e4be0aa5Sdlg
507e4be0aa5Sdlg mtx_enter(&sc->sc_state_mtx);
508e4be0aa5Sdlg state = sc->sc_state;
509e4be0aa5Sdlg mtx_leave(&sc->sc_state_mtx);
510e4be0aa5Sdlg
511e4be0aa5Sdlg if (state != VSCSI_S_RUNNING)
512e4be0aa5Sdlg goto gone;
513e4be0aa5Sdlg
514e4be0aa5Sdlg switch (dt->cmd) {
515e4be0aa5Sdlg case VSCSI_REQPROBE:
516e4be0aa5Sdlg scsi_probe(sc->sc_scsibus, dt->de.target, dt->de.lun);
517e4be0aa5Sdlg break;
518e4be0aa5Sdlg case VSCSI_REQDETACH:
519e4be0aa5Sdlg scsi_detach(sc->sc_scsibus, dt->de.target, dt->de.lun,
520e4be0aa5Sdlg DETACH_FORCE);
521e4be0aa5Sdlg break;
522e4be0aa5Sdlg #ifdef DIAGNOSTIC
523e4be0aa5Sdlg default:
524e4be0aa5Sdlg panic("unexpected vscsi_devevent cmd");
525e4be0aa5Sdlg /* NOTREACHED */
526e4be0aa5Sdlg #endif
5278cf76a44Sjmatthew }
528e4be0aa5Sdlg
529e4be0aa5Sdlg gone:
530e4be0aa5Sdlg device_unref(&sc->sc_dev);
531e4be0aa5Sdlg
53217d22f03Sderaadt free(dt, M_TEMP, sizeof(*dt));
533e4be0aa5Sdlg }
534e4be0aa5Sdlg
535bfea8c94Sdlg int
vscsikqfilter(dev_t dev,struct knote * kn)536bfea8c94Sdlg vscsikqfilter(dev_t dev, struct knote *kn)
537bfea8c94Sdlg {
538bfea8c94Sdlg struct vscsi_softc *sc = DEV2SC(dev);
539b6616845Smikeb
540b6616845Smikeb if (sc == NULL)
541b6616845Smikeb return (ENXIO);
542b6616845Smikeb
543bfea8c94Sdlg switch (kn->kn_filter) {
544bfea8c94Sdlg case EVFILT_READ:
545bfea8c94Sdlg kn->kn_fop = &vscsi_filtops;
546bfea8c94Sdlg break;
547bfea8c94Sdlg default:
548198400c0Smatthew device_unref(&sc->sc_dev);
549b8d5a5fbSnicm return (EINVAL);
550bfea8c94Sdlg }
551bfea8c94Sdlg
55268d1e1f0Sdlg kn->kn_hook = sc;
55326a3a4bdSmvs klist_insert(&sc->sc_klist, kn);
554bfea8c94Sdlg
555c81c7152Sdlg /* device ref is given to the knote in the klist */
556c81c7152Sdlg
557bfea8c94Sdlg return (0);
558bfea8c94Sdlg }
559bfea8c94Sdlg
560bfea8c94Sdlg void
filt_vscsidetach(struct knote * kn)561bfea8c94Sdlg filt_vscsidetach(struct knote *kn)
562bfea8c94Sdlg {
56368d1e1f0Sdlg struct vscsi_softc *sc = kn->kn_hook;
564bfea8c94Sdlg
56526a3a4bdSmvs klist_remove(&sc->sc_klist, kn);
566c81c7152Sdlg device_unref(&sc->sc_dev);
567bfea8c94Sdlg }
568bfea8c94Sdlg
569bfea8c94Sdlg int
filt_vscsiread(struct knote * kn,long hint)570bfea8c94Sdlg filt_vscsiread(struct knote *kn, long hint)
571bfea8c94Sdlg {
57268d1e1f0Sdlg struct vscsi_softc *sc = kn->kn_hook;
57326a3a4bdSmvs
57426a3a4bdSmvs return (!TAILQ_EMPTY(&sc->sc_ccb_i2t));
57526a3a4bdSmvs }
57626a3a4bdSmvs
57726a3a4bdSmvs int
filt_vscsimodify(struct kevent * kev,struct knote * kn)57826a3a4bdSmvs filt_vscsimodify(struct kevent *kev, struct knote *kn)
57926a3a4bdSmvs {
58026a3a4bdSmvs struct vscsi_softc *sc = kn->kn_hook;
58126a3a4bdSmvs int active;
582bfea8c94Sdlg
583d2628efdSdlg mtx_enter(&sc->sc_state_mtx);
58426a3a4bdSmvs active = knote_modify(kev, kn);
585d2628efdSdlg mtx_leave(&sc->sc_state_mtx);
586bfea8c94Sdlg
58726a3a4bdSmvs return (active);
58826a3a4bdSmvs }
58926a3a4bdSmvs
59026a3a4bdSmvs int
filt_vscsiprocess(struct knote * kn,struct kevent * kev)59126a3a4bdSmvs filt_vscsiprocess(struct knote *kn, struct kevent *kev)
59226a3a4bdSmvs {
59326a3a4bdSmvs struct vscsi_softc *sc = kn->kn_hook;
59426a3a4bdSmvs int active;
59526a3a4bdSmvs
59626a3a4bdSmvs mtx_enter(&sc->sc_state_mtx);
59726a3a4bdSmvs active = knote_process(kn, kev);
59826a3a4bdSmvs mtx_leave(&sc->sc_state_mtx);
59926a3a4bdSmvs
60026a3a4bdSmvs return (active);
601bfea8c94Sdlg }
602bfea8c94Sdlg
603bfea8c94Sdlg int
vscsiclose(dev_t dev,int flags,int mode,struct proc * p)604bfea8c94Sdlg vscsiclose(dev_t dev, int flags, int mode, struct proc *p)
605bfea8c94Sdlg {
606bfea8c94Sdlg struct vscsi_softc *sc = DEV2SC(dev);
607bfea8c94Sdlg struct vscsi_ccb *ccb;
608bfea8c94Sdlg
609b6616845Smikeb if (sc == NULL)
610b6616845Smikeb return (ENXIO);
611b6616845Smikeb
61232d02b2cSdlg mtx_enter(&sc->sc_state_mtx);
61332d02b2cSdlg KASSERT(sc->sc_state == VSCSI_S_RUNNING);
61432d02b2cSdlg sc->sc_state = VSCSI_S_CONFIG;
61532d02b2cSdlg mtx_leave(&sc->sc_state_mtx);
616bfea8c94Sdlg
617b4647ae4Sdlg scsi_activate(sc->sc_scsibus, -1, -1, DVACT_DEACTIVATE);
618b4647ae4Sdlg
619bfea8c94Sdlg while ((ccb = TAILQ_FIRST(&sc->sc_ccb_t2i)) != NULL) {
620b30d1400Sdlg TAILQ_REMOVE(&sc->sc_ccb_t2i, ccb, ccb_entry);
6216c51f94eSdlg ccb->ccb_xs->error = XS_RESET;
62232d02b2cSdlg vscsi_done(sc, ccb);
623bfea8c94Sdlg }
624bfea8c94Sdlg
625bfea8c94Sdlg while ((ccb = TAILQ_FIRST(&sc->sc_ccb_i2t)) != NULL) {
626bfea8c94Sdlg TAILQ_REMOVE(&sc->sc_ccb_i2t, ccb, ccb_entry);
6276c51f94eSdlg ccb->ccb_xs->error = XS_RESET;
62832d02b2cSdlg vscsi_done(sc, ccb);
629bfea8c94Sdlg }
630bfea8c94Sdlg
6313ae005b6Sdlg scsi_req_detach(sc->sc_scsibus, -1, -1, DETACH_FORCE);
6323ae005b6Sdlg
63332d02b2cSdlg mtx_enter(&sc->sc_state_mtx);
6343ae005b6Sdlg while (sc->sc_ref_count > 0) {
635541245b6Sjsg msleep_nsec(&sc->sc_ref_count, &sc->sc_state_mtx,
636541245b6Sjsg PRIBIO, "vscsiref", INFSLP);
63732d02b2cSdlg }
63832d02b2cSdlg mtx_leave(&sc->sc_state_mtx);
63932d02b2cSdlg
640bfea8c94Sdlg pool_destroy(&sc->sc_ccb_pool);
641bfea8c94Sdlg
64232d02b2cSdlg mtx_enter(&sc->sc_state_mtx);
64332d02b2cSdlg sc->sc_state = VSCSI_S_CLOSED;
64432d02b2cSdlg mtx_leave(&sc->sc_state_mtx);
645bfea8c94Sdlg
646198400c0Smatthew device_unref(&sc->sc_dev);
647bfea8c94Sdlg return (0);
648bfea8c94Sdlg }
649bfea8c94Sdlg
65032d02b2cSdlg void *
vscsi_ccb_get(void * cookie)65132d02b2cSdlg vscsi_ccb_get(void *cookie)
652bfea8c94Sdlg {
65332d02b2cSdlg struct vscsi_softc *sc = cookie;
65432d02b2cSdlg struct vscsi_ccb *ccb = NULL;
655bfea8c94Sdlg
6563ae005b6Sdlg ccb = pool_get(&sc->sc_ccb_pool, PR_NOWAIT);
6573ae005b6Sdlg if (ccb != NULL) {
658bfea8c94Sdlg ccb->ccb_tag = sc->sc_ccb_tag++;
659bfea8c94Sdlg ccb->ccb_datalen = 0;
66032d02b2cSdlg }
66132d02b2cSdlg
662bfea8c94Sdlg return (ccb);
663bfea8c94Sdlg }
66432d02b2cSdlg
66532d02b2cSdlg void
vscsi_ccb_put(void * cookie,void * io)66632d02b2cSdlg vscsi_ccb_put(void *cookie, void *io)
66732d02b2cSdlg {
66832d02b2cSdlg struct vscsi_softc *sc = cookie;
66932d02b2cSdlg struct vscsi_ccb *ccb = io;
67032d02b2cSdlg
67132d02b2cSdlg pool_put(&sc->sc_ccb_pool, ccb);
67232d02b2cSdlg }
673