1 /* $OpenBSD: vscsi.c,v 1.63 2024/05/13 01:15:50 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2008 David Gwynne <dlg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/malloc.h>
23 #include <sys/device.h>
24 #include <sys/conf.h>
25 #include <sys/queue.h>
26 #include <sys/rwlock.h>
27 #include <sys/pool.h>
28 #include <sys/task.h>
29 #include <sys/ioctl.h>
30 #include <sys/event.h>
31
32 #include <scsi/scsi_all.h>
33 #include <scsi/scsiconf.h>
34
35 #include <dev/vscsivar.h>
36
37 /*
38 * Locks used to protect struct members and global data
39 * s sc_state_mtx
40 */
41
42 int vscsi_match(struct device *, void *, void *);
43 void vscsi_attach(struct device *, struct device *, void *);
44
45 struct vscsi_ccb {
46 TAILQ_ENTRY(vscsi_ccb) ccb_entry;
47 int ccb_tag;
48 struct scsi_xfer *ccb_xs;
49 size_t ccb_datalen;
50 };
51
52 TAILQ_HEAD(vscsi_ccb_list, vscsi_ccb);
53
54 enum vscsi_state {
55 VSCSI_S_CLOSED,
56 VSCSI_S_CONFIG,
57 VSCSI_S_RUNNING
58 };
59
60 struct vscsi_softc {
61 struct device sc_dev;
62 struct scsibus_softc *sc_scsibus;
63
64 struct mutex sc_state_mtx;
65 enum vscsi_state sc_state;
66 u_int sc_ref_count;
67 struct pool sc_ccb_pool;
68
69 struct scsi_iopool sc_iopool;
70
71 struct vscsi_ccb_list sc_ccb_i2t; /* [s] */
72 struct vscsi_ccb_list sc_ccb_t2i;
73 int sc_ccb_tag;
74 struct mutex sc_poll_mtx;
75 struct rwlock sc_ioc_lock;
76
77 struct klist sc_klist; /* [s] */
78 };
79
80 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
81 #define DEV2SC(_d) ((struct vscsi_softc *)device_lookup(&vscsi_cd, minor(_d)))
82
83 const struct cfattach vscsi_ca = {
84 sizeof(struct vscsi_softc),
85 vscsi_match,
86 vscsi_attach
87 };
88
89 struct cfdriver vscsi_cd = {
90 NULL,
91 "vscsi",
92 DV_DULL
93 };
94
95 void vscsi_cmd(struct scsi_xfer *);
96 int vscsi_probe(struct scsi_link *);
97 void vscsi_free(struct scsi_link *);
98
99 const struct scsi_adapter vscsi_switch = {
100 vscsi_cmd, NULL, vscsi_probe, vscsi_free, NULL
101 };
102
103 int vscsi_i2t(struct vscsi_softc *, struct vscsi_ioc_i2t *);
104 int vscsi_data(struct vscsi_softc *, struct vscsi_ioc_data *, int);
105 int vscsi_t2i(struct vscsi_softc *, struct vscsi_ioc_t2i *);
106 int vscsi_devevent(struct vscsi_softc *, u_long,
107 struct vscsi_ioc_devevent *);
108 void vscsi_devevent_task(void *);
109 void vscsi_done(struct vscsi_softc *, struct vscsi_ccb *);
110
111 void * vscsi_ccb_get(void *);
112 void vscsi_ccb_put(void *, void *);
113
114 void filt_vscsidetach(struct knote *);
115 int filt_vscsiread(struct knote *, long);
116 int filt_vscsimodify(struct kevent *, struct knote *);
117 int filt_vscsiprocess(struct knote *, struct kevent *);
118
119 const struct filterops vscsi_filtops = {
120 .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
121 .f_attach = NULL,
122 .f_detach = filt_vscsidetach,
123 .f_event = filt_vscsiread,
124 .f_modify = filt_vscsimodify,
125 .f_process = filt_vscsiprocess,
126 };
127
128
129 int
vscsi_match(struct device * parent,void * match,void * aux)130 vscsi_match(struct device *parent, void *match, void *aux)
131 {
132 return (1);
133 }
134
135 void
vscsi_attach(struct device * parent,struct device * self,void * aux)136 vscsi_attach(struct device *parent, struct device *self, void *aux)
137 {
138 struct vscsi_softc *sc = (struct vscsi_softc *)self;
139 struct scsibus_attach_args saa;
140
141 printf("\n");
142
143 mtx_init(&sc->sc_state_mtx, IPL_MPFLOOR);
144 sc->sc_state = VSCSI_S_CLOSED;
145
146 TAILQ_INIT(&sc->sc_ccb_i2t);
147 TAILQ_INIT(&sc->sc_ccb_t2i);
148 mtx_init(&sc->sc_poll_mtx, IPL_BIO);
149 rw_init(&sc->sc_ioc_lock, "vscsiioc");
150 scsi_iopool_init(&sc->sc_iopool, sc, vscsi_ccb_get, vscsi_ccb_put);
151 klist_init_mutex(&sc->sc_klist, &sc->sc_state_mtx);
152
153 saa.saa_adapter = &vscsi_switch;
154 saa.saa_adapter_softc = sc;
155 saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
156 saa.saa_adapter_buswidth = 256;
157 saa.saa_luns = 8;
158 saa.saa_openings = 16;
159 saa.saa_pool = &sc->sc_iopool;
160 saa.saa_quirks = saa.saa_flags = 0;
161 saa.saa_wwpn = saa.saa_wwnn = 0;
162
163 sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
164 &saa, scsiprint);
165 }
166
167 void
vscsi_cmd(struct scsi_xfer * xs)168 vscsi_cmd(struct scsi_xfer *xs)
169 {
170 struct scsi_link *link = xs->sc_link;
171 struct vscsi_softc *sc = link->bus->sb_adapter_softc;
172 struct vscsi_ccb *ccb = xs->io;
173 int polled = ISSET(xs->flags, SCSI_POLL);
174 int running = 0;
175
176 if (ISSET(xs->flags, SCSI_POLL) && ISSET(xs->flags, SCSI_NOSLEEP)) {
177 printf("%s: POLL && NOSLEEP for 0x%02x\n", DEVNAME(sc),
178 xs->cmd.opcode);
179 xs->error = XS_DRIVER_STUFFUP;
180 scsi_done(xs);
181 return;
182 }
183
184 ccb->ccb_xs = xs;
185
186 mtx_enter(&sc->sc_state_mtx);
187 if (sc->sc_state == VSCSI_S_RUNNING) {
188 running = 1;
189 TAILQ_INSERT_TAIL(&sc->sc_ccb_i2t, ccb, ccb_entry);
190 }
191 knote_locked(&sc->sc_klist, 0);
192 mtx_leave(&sc->sc_state_mtx);
193
194 if (!running) {
195 xs->error = XS_DRIVER_STUFFUP;
196 scsi_done(xs);
197 return;
198 }
199
200 if (polled) {
201 mtx_enter(&sc->sc_poll_mtx);
202 while (ccb->ccb_xs != NULL)
203 msleep_nsec(ccb, &sc->sc_poll_mtx, PRIBIO, "vscsipoll",
204 INFSLP);
205 mtx_leave(&sc->sc_poll_mtx);
206 scsi_done(xs);
207 }
208 }
209
210 void
vscsi_done(struct vscsi_softc * sc,struct vscsi_ccb * ccb)211 vscsi_done(struct vscsi_softc *sc, struct vscsi_ccb *ccb)
212 {
213 struct scsi_xfer *xs = ccb->ccb_xs;
214
215 if (ISSET(xs->flags, SCSI_POLL)) {
216 mtx_enter(&sc->sc_poll_mtx);
217 ccb->ccb_xs = NULL;
218 wakeup(ccb);
219 mtx_leave(&sc->sc_poll_mtx);
220 } else
221 scsi_done(xs);
222 }
223
224 int
vscsi_probe(struct scsi_link * link)225 vscsi_probe(struct scsi_link *link)
226 {
227 struct vscsi_softc *sc = link->bus->sb_adapter_softc;
228 int rv = 0;
229
230 mtx_enter(&sc->sc_state_mtx);
231 if (sc->sc_state == VSCSI_S_RUNNING)
232 sc->sc_ref_count++;
233 else
234 rv = ENXIO;
235 mtx_leave(&sc->sc_state_mtx);
236
237 return (rv);
238 }
239
240 void
vscsi_free(struct scsi_link * link)241 vscsi_free(struct scsi_link *link)
242 {
243 struct vscsi_softc *sc = link->bus->sb_adapter_softc;
244
245 mtx_enter(&sc->sc_state_mtx);
246 sc->sc_ref_count--;
247 if (sc->sc_state != VSCSI_S_RUNNING && sc->sc_ref_count == 0)
248 wakeup(&sc->sc_ref_count);
249 mtx_leave(&sc->sc_state_mtx);
250 }
251
252 int
vscsiopen(dev_t dev,int flags,int mode,struct proc * p)253 vscsiopen(dev_t dev, int flags, int mode, struct proc *p)
254 {
255 struct vscsi_softc *sc = DEV2SC(dev);
256 enum vscsi_state state = VSCSI_S_RUNNING;
257 int rv = 0;
258
259 if (sc == NULL)
260 return (ENXIO);
261
262 mtx_enter(&sc->sc_state_mtx);
263 if (sc->sc_state != VSCSI_S_CLOSED)
264 rv = EBUSY;
265 else
266 sc->sc_state = VSCSI_S_CONFIG;
267 mtx_leave(&sc->sc_state_mtx);
268
269 if (rv != 0) {
270 device_unref(&sc->sc_dev);
271 return (rv);
272 }
273
274 pool_init(&sc->sc_ccb_pool, sizeof(struct vscsi_ccb), 0, IPL_BIO, 0,
275 "vscsiccb", NULL);
276
277 /* we need to guarantee some ccbs will be available for the iopool */
278 rv = pool_prime(&sc->sc_ccb_pool, 8);
279 if (rv != 0) {
280 pool_destroy(&sc->sc_ccb_pool);
281 state = VSCSI_S_CLOSED;
282 }
283
284 /* commit changes */
285 mtx_enter(&sc->sc_state_mtx);
286 sc->sc_state = state;
287 mtx_leave(&sc->sc_state_mtx);
288
289 device_unref(&sc->sc_dev);
290 return (rv);
291 }
292
293 int
vscsiioctl(dev_t dev,u_long cmd,caddr_t addr,int flags,struct proc * p)294 vscsiioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
295 {
296 struct vscsi_softc *sc = DEV2SC(dev);
297 int read = 0;
298 int err = 0;
299
300 if (sc == NULL)
301 return (ENXIO);
302
303 rw_enter_write(&sc->sc_ioc_lock);
304
305 switch (cmd) {
306 case VSCSI_I2T:
307 err = vscsi_i2t(sc, (struct vscsi_ioc_i2t *)addr);
308 break;
309
310 case VSCSI_DATA_READ:
311 read = 1;
312 case VSCSI_DATA_WRITE:
313 err = vscsi_data(sc, (struct vscsi_ioc_data *)addr, read);
314 break;
315
316 case VSCSI_T2I:
317 err = vscsi_t2i(sc, (struct vscsi_ioc_t2i *)addr);
318 break;
319
320 case VSCSI_REQPROBE:
321 case VSCSI_REQDETACH:
322 err = vscsi_devevent(sc, cmd,
323 (struct vscsi_ioc_devevent *)addr);
324 break;
325
326 default:
327 err = ENOTTY;
328 break;
329 }
330
331 rw_exit_write(&sc->sc_ioc_lock);
332
333 device_unref(&sc->sc_dev);
334 return (err);
335 }
336
337 int
vscsi_i2t(struct vscsi_softc * sc,struct vscsi_ioc_i2t * i2t)338 vscsi_i2t(struct vscsi_softc *sc, struct vscsi_ioc_i2t *i2t)
339 {
340 struct vscsi_ccb *ccb;
341 struct scsi_xfer *xs;
342 struct scsi_link *link;
343
344 mtx_enter(&sc->sc_state_mtx);
345 ccb = TAILQ_FIRST(&sc->sc_ccb_i2t);
346 if (ccb != NULL)
347 TAILQ_REMOVE(&sc->sc_ccb_i2t, ccb, ccb_entry);
348 mtx_leave(&sc->sc_state_mtx);
349
350 if (ccb == NULL)
351 return (EAGAIN);
352
353 xs = ccb->ccb_xs;
354 link = xs->sc_link;
355
356 i2t->tag = ccb->ccb_tag;
357 i2t->target = link->target;
358 i2t->lun = link->lun;
359 memcpy(&i2t->cmd, &xs->cmd, xs->cmdlen);
360 i2t->cmdlen = xs->cmdlen;
361 i2t->datalen = xs->datalen;
362
363 switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
364 case SCSI_DATA_IN:
365 i2t->direction = VSCSI_DIR_READ;
366 break;
367 case SCSI_DATA_OUT:
368 i2t->direction = VSCSI_DIR_WRITE;
369 break;
370 default:
371 i2t->direction = VSCSI_DIR_NONE;
372 break;
373 }
374
375 TAILQ_INSERT_TAIL(&sc->sc_ccb_t2i, ccb, ccb_entry);
376
377 return (0);
378 }
379
380 int
vscsi_data(struct vscsi_softc * sc,struct vscsi_ioc_data * data,int read)381 vscsi_data(struct vscsi_softc *sc, struct vscsi_ioc_data *data, int read)
382 {
383 struct vscsi_ccb *ccb;
384 struct scsi_xfer *xs;
385 int xsread;
386 u_int8_t *buf;
387 int rv = EINVAL;
388
389 TAILQ_FOREACH(ccb, &sc->sc_ccb_t2i, ccb_entry) {
390 if (ccb->ccb_tag == data->tag)
391 break;
392 }
393 if (ccb == NULL)
394 return (EFAULT);
395
396 xs = ccb->ccb_xs;
397
398 if (data->datalen > xs->datalen - ccb->ccb_datalen)
399 return (ENOMEM);
400
401 switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
402 case SCSI_DATA_IN:
403 xsread = 1;
404 break;
405 case SCSI_DATA_OUT:
406 xsread = 0;
407 break;
408 default:
409 return (EINVAL);
410 }
411
412 if (read != xsread)
413 return (EINVAL);
414
415 buf = xs->data;
416 buf += ccb->ccb_datalen;
417
418 if (read)
419 rv = copyin(data->data, buf, data->datalen);
420 else
421 rv = copyout(buf, data->data, data->datalen);
422
423 if (rv == 0)
424 ccb->ccb_datalen += data->datalen;
425
426 return (rv);
427 }
428
429 int
vscsi_t2i(struct vscsi_softc * sc,struct vscsi_ioc_t2i * t2i)430 vscsi_t2i(struct vscsi_softc *sc, struct vscsi_ioc_t2i *t2i)
431 {
432 struct vscsi_ccb *ccb;
433 struct scsi_xfer *xs;
434 int rv = 0;
435
436 TAILQ_FOREACH(ccb, &sc->sc_ccb_t2i, ccb_entry) {
437 if (ccb->ccb_tag == t2i->tag)
438 break;
439 }
440 if (ccb == NULL)
441 return (EFAULT);
442
443 TAILQ_REMOVE(&sc->sc_ccb_t2i, ccb, ccb_entry);
444
445 xs = ccb->ccb_xs;
446
447 xs->resid = xs->datalen - ccb->ccb_datalen;
448 xs->status = SCSI_OK;
449
450 switch (t2i->status) {
451 case VSCSI_STAT_DONE:
452 xs->error = XS_NOERROR;
453 break;
454 case VSCSI_STAT_SENSE:
455 xs->error = XS_SENSE;
456 memcpy(&xs->sense, &t2i->sense, sizeof(xs->sense));
457 break;
458 case VSCSI_STAT_RESET:
459 xs->error = XS_RESET;
460 break;
461 case VSCSI_STAT_ERR:
462 default:
463 xs->error = XS_DRIVER_STUFFUP;
464 break;
465 }
466
467 vscsi_done(sc, ccb);
468
469 return (rv);
470 }
471
472 struct vscsi_devevent_task {
473 struct vscsi_softc *sc;
474 struct task t;
475 struct vscsi_ioc_devevent de;
476 u_long cmd;
477 };
478
479 int
vscsi_devevent(struct vscsi_softc * sc,u_long cmd,struct vscsi_ioc_devevent * de)480 vscsi_devevent(struct vscsi_softc *sc, u_long cmd,
481 struct vscsi_ioc_devevent *de)
482 {
483 struct vscsi_devevent_task *dt;
484
485 dt = malloc(sizeof(*dt), M_TEMP, M_WAITOK | M_CANFAIL);
486 if (dt == NULL)
487 return (ENOMEM);
488
489 task_set(&dt->t, vscsi_devevent_task, dt);
490 dt->sc = sc;
491 dt->de = *de;
492 dt->cmd = cmd;
493
494 device_ref(&sc->sc_dev);
495 task_add(systq, &dt->t);
496
497 return (0);
498 }
499
500 void
vscsi_devevent_task(void * xdt)501 vscsi_devevent_task(void *xdt)
502 {
503 struct vscsi_devevent_task *dt = xdt;
504 struct vscsi_softc *sc = dt->sc;
505 int state;
506
507 mtx_enter(&sc->sc_state_mtx);
508 state = sc->sc_state;
509 mtx_leave(&sc->sc_state_mtx);
510
511 if (state != VSCSI_S_RUNNING)
512 goto gone;
513
514 switch (dt->cmd) {
515 case VSCSI_REQPROBE:
516 scsi_probe(sc->sc_scsibus, dt->de.target, dt->de.lun);
517 break;
518 case VSCSI_REQDETACH:
519 scsi_detach(sc->sc_scsibus, dt->de.target, dt->de.lun,
520 DETACH_FORCE);
521 break;
522 #ifdef DIAGNOSTIC
523 default:
524 panic("unexpected vscsi_devevent cmd");
525 /* NOTREACHED */
526 #endif
527 }
528
529 gone:
530 device_unref(&sc->sc_dev);
531
532 free(dt, M_TEMP, sizeof(*dt));
533 }
534
535 int
vscsikqfilter(dev_t dev,struct knote * kn)536 vscsikqfilter(dev_t dev, struct knote *kn)
537 {
538 struct vscsi_softc *sc = DEV2SC(dev);
539
540 if (sc == NULL)
541 return (ENXIO);
542
543 switch (kn->kn_filter) {
544 case EVFILT_READ:
545 kn->kn_fop = &vscsi_filtops;
546 break;
547 default:
548 device_unref(&sc->sc_dev);
549 return (EINVAL);
550 }
551
552 kn->kn_hook = sc;
553 klist_insert(&sc->sc_klist, kn);
554
555 /* device ref is given to the knote in the klist */
556
557 return (0);
558 }
559
560 void
filt_vscsidetach(struct knote * kn)561 filt_vscsidetach(struct knote *kn)
562 {
563 struct vscsi_softc *sc = kn->kn_hook;
564
565 klist_remove(&sc->sc_klist, kn);
566 device_unref(&sc->sc_dev);
567 }
568
569 int
filt_vscsiread(struct knote * kn,long hint)570 filt_vscsiread(struct knote *kn, long hint)
571 {
572 struct vscsi_softc *sc = kn->kn_hook;
573
574 return (!TAILQ_EMPTY(&sc->sc_ccb_i2t));
575 }
576
577 int
filt_vscsimodify(struct kevent * kev,struct knote * kn)578 filt_vscsimodify(struct kevent *kev, struct knote *kn)
579 {
580 struct vscsi_softc *sc = kn->kn_hook;
581 int active;
582
583 mtx_enter(&sc->sc_state_mtx);
584 active = knote_modify(kev, kn);
585 mtx_leave(&sc->sc_state_mtx);
586
587 return (active);
588 }
589
590 int
filt_vscsiprocess(struct knote * kn,struct kevent * kev)591 filt_vscsiprocess(struct knote *kn, struct kevent *kev)
592 {
593 struct vscsi_softc *sc = kn->kn_hook;
594 int active;
595
596 mtx_enter(&sc->sc_state_mtx);
597 active = knote_process(kn, kev);
598 mtx_leave(&sc->sc_state_mtx);
599
600 return (active);
601 }
602
603 int
vscsiclose(dev_t dev,int flags,int mode,struct proc * p)604 vscsiclose(dev_t dev, int flags, int mode, struct proc *p)
605 {
606 struct vscsi_softc *sc = DEV2SC(dev);
607 struct vscsi_ccb *ccb;
608
609 if (sc == NULL)
610 return (ENXIO);
611
612 mtx_enter(&sc->sc_state_mtx);
613 KASSERT(sc->sc_state == VSCSI_S_RUNNING);
614 sc->sc_state = VSCSI_S_CONFIG;
615 mtx_leave(&sc->sc_state_mtx);
616
617 scsi_activate(sc->sc_scsibus, -1, -1, DVACT_DEACTIVATE);
618
619 while ((ccb = TAILQ_FIRST(&sc->sc_ccb_t2i)) != NULL) {
620 TAILQ_REMOVE(&sc->sc_ccb_t2i, ccb, ccb_entry);
621 ccb->ccb_xs->error = XS_RESET;
622 vscsi_done(sc, ccb);
623 }
624
625 while ((ccb = TAILQ_FIRST(&sc->sc_ccb_i2t)) != NULL) {
626 TAILQ_REMOVE(&sc->sc_ccb_i2t, ccb, ccb_entry);
627 ccb->ccb_xs->error = XS_RESET;
628 vscsi_done(sc, ccb);
629 }
630
631 scsi_req_detach(sc->sc_scsibus, -1, -1, DETACH_FORCE);
632
633 mtx_enter(&sc->sc_state_mtx);
634 while (sc->sc_ref_count > 0) {
635 msleep_nsec(&sc->sc_ref_count, &sc->sc_state_mtx,
636 PRIBIO, "vscsiref", INFSLP);
637 }
638 mtx_leave(&sc->sc_state_mtx);
639
640 pool_destroy(&sc->sc_ccb_pool);
641
642 mtx_enter(&sc->sc_state_mtx);
643 sc->sc_state = VSCSI_S_CLOSED;
644 mtx_leave(&sc->sc_state_mtx);
645
646 device_unref(&sc->sc_dev);
647 return (0);
648 }
649
650 void *
vscsi_ccb_get(void * cookie)651 vscsi_ccb_get(void *cookie)
652 {
653 struct vscsi_softc *sc = cookie;
654 struct vscsi_ccb *ccb = NULL;
655
656 ccb = pool_get(&sc->sc_ccb_pool, PR_NOWAIT);
657 if (ccb != NULL) {
658 ccb->ccb_tag = sc->sc_ccb_tag++;
659 ccb->ccb_datalen = 0;
660 }
661
662 return (ccb);
663 }
664
665 void
vscsi_ccb_put(void * cookie,void * io)666 vscsi_ccb_put(void *cookie, void *io)
667 {
668 struct vscsi_softc *sc = cookie;
669 struct vscsi_ccb *ccb = io;
670
671 pool_put(&sc->sc_ccb_pool, ccb);
672 }
673