1 1.36 mlelstv /* $NetBSD: vioscsi.c,v 1.36 2023/03/25 11:04:34 mlelstv Exp $ */ 2 1.1 christos /* $OpenBSD: vioscsi.c,v 1.3 2015/03/14 03:38:49 jsg Exp $ */ 3 1.1 christos 4 1.1 christos /* 5 1.1 christos * Copyright (c) 2013 Google Inc. 6 1.1 christos * 7 1.1 christos * Permission to use, copy, modify, and distribute this software for any 8 1.1 christos * purpose with or without fee is hereby granted, provided that the above 9 1.1 christos * copyright notice and this permission notice appear in all copies. 10 1.1 christos * 11 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 1.1 christos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 1.1 christos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 1.1 christos */ 19 1.1 christos 20 1.1 christos #include <sys/cdefs.h> 21 1.36 mlelstv __KERNEL_RCSID(0, "$NetBSD: vioscsi.c,v 1.36 2023/03/25 11:04:34 mlelstv Exp $"); 22 1.1 christos 23 1.1 christos #include <sys/param.h> 24 1.1 christos #include <sys/systm.h> 25 1.1 christos #include <sys/device.h> 26 1.1 christos #include <sys/bus.h> 27 1.1 christos #include <sys/buf.h> 28 1.15 jdolecek #include <sys/module.h> 29 1.1 christos 30 1.1 christos #include <dev/pci/vioscsireg.h> 31 1.1 christos #include <dev/pci/virtiovar.h> 32 1.1 christos 33 1.1 christos #include <dev/scsipi/scsi_all.h> 34 1.1 christos #include <dev/scsipi/scsiconf.h> 35 1.1 christos 36 1.1 christos #ifdef VIOSCSI_DEBUG 37 1.1 christos static int vioscsi_debug = 1; 38 1.1 christos #define DPRINTF(f) do { if (vioscsi_debug) printf f; } while (/*CONSTCOND*/0) 39 1.1 christos #else 40 1.1 christos #define DPRINTF(f) ((void)0) 41 1.1 christos #endif 42 1.1 christos 43 1.1 christos struct vioscsi_req { 44 1.1 christos struct virtio_scsi_req_hdr vr_req; 45 1.1 christos struct virtio_scsi_res_hdr vr_res; 46 1.1 christos struct scsipi_xfer *vr_xs; 47 1.1 christos bus_dmamap_t vr_control; 48 1.1 christos bus_dmamap_t vr_data; 49 1.1 christos }; 50 1.1 christos 51 1.1 christos struct vioscsi_softc { 52 1.1 christos device_t sc_dev; 53 1.1 christos struct scsipi_adapter sc_adapter; 54 1.1 christos struct scsipi_channel sc_channel; 55 1.1 christos 56 1.1 christos struct virtqueue sc_vqs[3]; 57 1.10 jdolecek #define VIOSCSI_VQ_CONTROL 0 58 1.10 jdolecek #define VIOSCSI_VQ_EVENT 1 59 1.10 jdolecek #define VIOSCSI_VQ_REQUEST 2 60 1.10 jdolecek 61 1.1 christos struct vioscsi_req *sc_reqs; 62 1.9 jdolecek int sc_nreqs; 63 1.1 christos bus_dma_segment_t sc_reqs_segs[1]; 64 1.1 christos 65 1.1 christos u_int32_t sc_seg_max; 66 1.15 jdolecek 67 1.15 jdolecek kmutex_t sc_mutex; 68 1.1 christos }; 69 1.1 christos 70 1.28 skrll /* 71 1.2 christos * Each block request uses at least two segments - one for the header 72 1.2 christos * and one for the status. 73 1.2 christos */ 74 1.2 christos #define VIRTIO_SCSI_MIN_SEGMENTS 2 75 1.2 christos 76 1.1 christos static int vioscsi_match(device_t, cfdata_t, void *); 77 1.1 christos static void vioscsi_attach(device_t, device_t, void *); 78 1.15 jdolecek static int vioscsi_detach(device_t, int); 79 1.1 christos 80 1.1 christos static int vioscsi_alloc_reqs(struct vioscsi_softc *, 81 1.15 jdolecek struct virtio_softc *, int); 82 1.12 jdolecek static void vioscsi_free_reqs(struct vioscsi_softc *, 83 1.12 jdolecek struct virtio_softc *); 84 1.1 christos static void vioscsi_scsipi_request(struct scsipi_channel *, 85 1.1 christos scsipi_adapter_req_t, void *); 86 1.1 christos static int vioscsi_vq_done(struct virtqueue *); 87 1.1 christos static void vioscsi_req_done(struct vioscsi_softc *, struct virtio_softc *, 88 1.19 jdolecek struct vioscsi_req *, struct virtqueue *, int); 89 1.1 christos static struct vioscsi_req *vioscsi_req_get(struct vioscsi_softc *); 90 1.15 jdolecek static void vioscsi_bad_target(struct scsipi_xfer *); 91 1.1 christos 92 1.1 christos static const char *const vioscsi_vq_names[] = { 93 1.1 christos "control", 94 1.1 christos "event", 95 1.1 christos "request", 96 1.1 christos }; 97 1.1 christos 98 1.15 jdolecek CFATTACH_DECL3_NEW(vioscsi, sizeof(struct vioscsi_softc), 99 1.15 jdolecek vioscsi_match, vioscsi_attach, vioscsi_detach, NULL, NULL, NULL, 100 1.15 jdolecek DVF_DETACH_SHUTDOWN); 101 1.1 christos 102 1.1 christos static int 103 1.1 christos vioscsi_match(device_t parent, cfdata_t match, void *aux) 104 1.1 christos { 105 1.12 jdolecek struct virtio_attach_args *va = aux; 106 1.1 christos 107 1.25 reinoud if (va->sc_childdevid == VIRTIO_DEVICE_ID_SCSI) 108 1.1 christos return 1; 109 1.12 jdolecek 110 1.1 christos return 0; 111 1.1 christos } 112 1.1 christos 113 1.1 christos static void 114 1.1 christos vioscsi_attach(device_t parent, device_t self, void *aux) 115 1.1 christos { 116 1.1 christos struct vioscsi_softc *sc = device_private(self); 117 1.1 christos struct virtio_softc *vsc = device_private(parent); 118 1.1 christos struct scsipi_adapter *adapt = &sc->sc_adapter; 119 1.1 christos struct scsipi_channel *chan = &sc->sc_channel; 120 1.12 jdolecek int rv, qsize = 0, i = 0; 121 1.12 jdolecek int ipl = IPL_BIO; 122 1.1 christos 123 1.12 jdolecek if (virtio_child(vsc) != NULL) { 124 1.1 christos aprint_error(": parent %s already has a child\n", 125 1.1 christos device_xname(parent)); 126 1.1 christos return; 127 1.1 christos } 128 1.1 christos 129 1.1 christos sc->sc_dev = self; 130 1.1 christos 131 1.31 yamaguch virtio_child_attach_start(vsc, self, ipl, 132 1.12 jdolecek 0, VIRTIO_COMMON_FLAG_BITS); 133 1.1 christos 134 1.15 jdolecek mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, ipl); 135 1.15 jdolecek 136 1.1 christos uint32_t cmd_per_lun = virtio_read_device_config_4(vsc, 137 1.1 christos VIRTIO_SCSI_CONFIG_CMD_PER_LUN); 138 1.1 christos 139 1.1 christos uint32_t seg_max = virtio_read_device_config_4(vsc, 140 1.1 christos VIRTIO_SCSI_CONFIG_SEG_MAX); 141 1.1 christos 142 1.1 christos uint16_t max_target = virtio_read_device_config_2(vsc, 143 1.1 christos VIRTIO_SCSI_CONFIG_MAX_TARGET); 144 1.1 christos 145 1.1 christos uint32_t max_lun = virtio_read_device_config_4(vsc, 146 1.1 christos VIRTIO_SCSI_CONFIG_MAX_LUN); 147 1.1 christos 148 1.1 christos sc->sc_seg_max = seg_max; 149 1.1 christos 150 1.15 jdolecek for(i=0; i < __arraycount(sc->sc_vqs); i++) { 151 1.32 yamaguch virtio_init_vq_vqdone(vsc, &sc->sc_vqs[i], i, 152 1.32 yamaguch vioscsi_vq_done); 153 1.32 yamaguch rv = virtio_alloc_vq(vsc, &sc->sc_vqs[i], MAXPHYS, 154 1.17 jdolecek VIRTIO_SCSI_MIN_SEGMENTS + howmany(MAXPHYS, NBPG), 155 1.15 jdolecek vioscsi_vq_names[i]); 156 1.1 christos if (rv) { 157 1.1 christos aprint_error_dev(sc->sc_dev, 158 1.15 jdolecek "failed to allocate virtqueue %d\n", i); 159 1.15 jdolecek goto err; 160 1.1 christos } 161 1.10 jdolecek 162 1.10 jdolecek if (i == VIOSCSI_VQ_REQUEST) 163 1.10 jdolecek sc->sc_vqs[i].vq_done = vioscsi_vq_done; 164 1.1 christos } 165 1.1 christos 166 1.34 mlelstv qsize = sc->sc_vqs[VIOSCSI_VQ_REQUEST].vq_num; 167 1.15 jdolecek if (vioscsi_alloc_reqs(sc, vsc, qsize)) 168 1.15 jdolecek goto err; 169 1.1 christos 170 1.12 jdolecek aprint_normal_dev(sc->sc_dev, 171 1.16 christos "cmd_per_lun %u qsize %d seg_max %u max_target %hu" 172 1.16 christos " max_lun %u\n", 173 1.12 jdolecek cmd_per_lun, qsize, seg_max, max_target, max_lun); 174 1.12 jdolecek 175 1.31 yamaguch if (virtio_child_attach_finish(vsc, sc->sc_vqs, 176 1.36 mlelstv __arraycount(sc->sc_vqs), NULL, 177 1.36 mlelstv VIRTIO_F_INTR_MSIX | VIRTIO_F_INTR_MPSAFE) != 0) 178 1.12 jdolecek goto err; 179 1.10 jdolecek 180 1.1 christos /* 181 1.1 christos * Fill in the scsipi_adapter. 182 1.1 christos */ 183 1.1 christos memset(adapt, 0, sizeof(*adapt)); 184 1.1 christos adapt->adapt_dev = sc->sc_dev; 185 1.14 jdolecek adapt->adapt_nchannels = 1; 186 1.13 jdolecek adapt->adapt_openings = MIN(qsize, cmd_per_lun); 187 1.1 christos adapt->adapt_max_periph = adapt->adapt_openings; 188 1.1 christos adapt->adapt_request = vioscsi_scsipi_request; 189 1.1 christos adapt->adapt_minphys = minphys; 190 1.36 mlelstv adapt->adapt_flags = SCSIPI_ADAPT_MPSAFE; 191 1.1 christos 192 1.1 christos /* 193 1.1 christos * Fill in the scsipi_channel. 194 1.1 christos */ 195 1.1 christos memset(chan, 0, sizeof(*chan)); 196 1.1 christos chan->chan_adapter = adapt; 197 1.1 christos chan->chan_bustype = &scsi_bustype; 198 1.1 christos chan->chan_channel = 0; 199 1.29 jakllsch chan->chan_ntargets = MIN(1 + max_target, 256); /* cap reasonably */ 200 1.29 jakllsch chan->chan_nluns = MIN(1 + max_lun, 16384); /* cap reasonably */ 201 1.29 jakllsch chan->chan_id = max_target + 1; 202 1.6 pooka chan->chan_flags = SCSIPI_CHAN_NOSETTLE; 203 1.1 christos 204 1.27 thorpej config_found(self, &sc->sc_channel, scsiprint, CFARGS_NONE); 205 1.12 jdolecek return; 206 1.12 jdolecek 207 1.12 jdolecek err: 208 1.12 jdolecek if (qsize > 0) 209 1.12 jdolecek vioscsi_free_reqs(sc, vsc); 210 1.12 jdolecek 211 1.12 jdolecek for (i=0; i < __arraycount(sc->sc_vqs); i++) { 212 1.33 yamaguch virtio_free_vq(vsc, &sc->sc_vqs[i]); 213 1.12 jdolecek } 214 1.12 jdolecek 215 1.12 jdolecek virtio_child_attach_failed(vsc); 216 1.15 jdolecek } 217 1.15 jdolecek 218 1.15 jdolecek static int 219 1.15 jdolecek vioscsi_detach(device_t self, int flags) 220 1.15 jdolecek { 221 1.15 jdolecek struct vioscsi_softc *sc = device_private(self); 222 1.15 jdolecek struct virtio_softc *vsc = device_private(device_parent(sc->sc_dev)); 223 1.15 jdolecek int rc, i; 224 1.15 jdolecek 225 1.15 jdolecek /* 226 1.15 jdolecek * Dequeue all pending finished requests. Must be done 227 1.15 jdolecek * before we try to detach children so that we process 228 1.15 jdolecek * their pending requests while they still exist. 229 1.15 jdolecek */ 230 1.15 jdolecek if (sc->sc_vqs[VIOSCSI_VQ_REQUEST].vq_num > 0) 231 1.15 jdolecek vioscsi_vq_done(&sc->sc_vqs[VIOSCSI_VQ_REQUEST]); 232 1.12 jdolecek 233 1.15 jdolecek if ((rc = config_detach_children(self, flags)) != 0) 234 1.15 jdolecek return rc; 235 1.15 jdolecek 236 1.15 jdolecek virtio_reset(vsc); 237 1.15 jdolecek 238 1.15 jdolecek for (i = 0; i < __arraycount(sc->sc_vqs); i++) { 239 1.15 jdolecek if (sc->sc_vqs[i].vq_num > 0) 240 1.15 jdolecek virtio_free_vq(vsc, &sc->sc_vqs[i]); 241 1.15 jdolecek } 242 1.15 jdolecek 243 1.15 jdolecek vioscsi_free_reqs(sc, vsc); 244 1.15 jdolecek 245 1.15 jdolecek virtio_child_detach(vsc); 246 1.15 jdolecek 247 1.15 jdolecek mutex_destroy(&sc->sc_mutex); 248 1.15 jdolecek 249 1.15 jdolecek return 0; 250 1.1 christos } 251 1.1 christos 252 1.1 christos #define XS2DMA(xs) \ 253 1.1 christos ((((xs)->xs_control & XS_CTL_DATA_IN) ? BUS_DMA_READ : BUS_DMA_WRITE) | \ 254 1.1 christos (((xs)->xs_control & XS_CTL_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK) | \ 255 1.1 christos BUS_DMA_STREAMING) 256 1.1 christos 257 1.1 christos #define XS2DMAPRE(xs) (((xs)->xs_control & XS_CTL_DATA_IN) ? \ 258 1.1 christos BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE) 259 1.1 christos 260 1.1 christos #define XS2DMAPOST(xs) (((xs)->xs_control & XS_CTL_DATA_IN) ? \ 261 1.1 christos BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE) 262 1.1 christos 263 1.1 christos static void 264 1.1 christos vioscsi_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t 265 1.1 christos request, void *arg) 266 1.1 christos { 267 1.1 christos struct vioscsi_softc *sc = 268 1.1 christos device_private(chan->chan_adapter->adapt_dev); 269 1.1 christos struct virtio_softc *vsc = device_private(device_parent(sc->sc_dev)); 270 1.28 skrll struct scsipi_xfer *xs; 271 1.1 christos struct scsipi_periph *periph; 272 1.1 christos struct vioscsi_req *vr; 273 1.1 christos struct virtio_scsi_req_hdr *req; 274 1.11 jdolecek struct virtqueue *vq = &sc->sc_vqs[VIOSCSI_VQ_REQUEST]; 275 1.1 christos int slot, error; 276 1.21 maxv bool dopoll; 277 1.1 christos 278 1.1 christos DPRINTF(("%s: enter\n", __func__)); 279 1.1 christos 280 1.8 jdolecek switch (request) { 281 1.8 jdolecek case ADAPTER_REQ_RUN_XFER: 282 1.8 jdolecek break; 283 1.8 jdolecek case ADAPTER_REQ_SET_XFER_MODE: 284 1.8 jdolecek { 285 1.8 jdolecek struct scsipi_xfer_mode *xm = arg; 286 1.8 jdolecek xm->xm_mode = PERIPH_CAP_TQING; 287 1.8 jdolecek xm->xm_period = 0; 288 1.8 jdolecek xm->xm_offset = 0; 289 1.8 jdolecek scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm); 290 1.8 jdolecek return; 291 1.8 jdolecek } 292 1.8 jdolecek default: 293 1.1 christos DPRINTF(("%s: unhandled %d\n", __func__, request)); 294 1.1 christos return; 295 1.1 christos } 296 1.28 skrll 297 1.1 christos xs = arg; 298 1.1 christos periph = xs->xs_periph; 299 1.1 christos 300 1.1 christos /* 301 1.7 jdolecek * This can happen when we run out of queue slots. 302 1.1 christos */ 303 1.10 jdolecek vr = vioscsi_req_get(sc); 304 1.1 christos if (vr == NULL) { 305 1.35 mlelstv xs->error = XS_BUSY; 306 1.7 jdolecek scsipi_done(xs); 307 1.7 jdolecek return; 308 1.1 christos } 309 1.7 jdolecek 310 1.1 christos req = &vr->vr_req; 311 1.1 christos slot = vr - sc->sc_reqs; 312 1.1 christos 313 1.1 christos /* 314 1.1 christos * "The only supported format for the LUN field is: first byte set to 315 1.1 christos * 1, second byte set to target, third and fourth byte representing a 316 1.1 christos * single level LUN structure, followed by four zero bytes." 317 1.1 christos */ 318 1.15 jdolecek if (periph->periph_target >= 256 || periph->periph_lun >= 16384 319 1.15 jdolecek || periph->periph_target < 0 || periph->periph_lun < 0) { 320 1.1 christos goto stuffup; 321 1.1 christos } 322 1.15 jdolecek 323 1.1 christos req->lun[0] = 1; 324 1.22 kim req->lun[1] = periph->periph_target; 325 1.10 jdolecek req->lun[2] = 0x40 | ((periph->periph_lun >> 8) & 0x3F); 326 1.10 jdolecek req->lun[3] = periph->periph_lun & 0xFF; 327 1.1 christos memset(req->lun + 4, 0, 4); 328 1.15 jdolecek DPRINTF(("%s: command %p for %d:%d at slot %d\n", __func__, 329 1.15 jdolecek xs, periph->periph_target, periph->periph_lun, slot)); 330 1.1 christos 331 1.8 jdolecek /* tag */ 332 1.8 jdolecek switch (XS_CTL_TAGTYPE(xs)) { 333 1.8 jdolecek case XS_CTL_HEAD_TAG: 334 1.8 jdolecek req->task_attr = VIRTIO_SCSI_S_HEAD; 335 1.8 jdolecek break; 336 1.8 jdolecek 337 1.8 jdolecek #if 0 /* XXX */ 338 1.8 jdolecek case XS_CTL_ACA_TAG: 339 1.8 jdolecek req->task_attr = VIRTIO_SCSI_S_ACA; 340 1.8 jdolecek break; 341 1.8 jdolecek #endif 342 1.8 jdolecek 343 1.8 jdolecek case XS_CTL_ORDERED_TAG: 344 1.8 jdolecek req->task_attr = VIRTIO_SCSI_S_ORDERED; 345 1.8 jdolecek break; 346 1.8 jdolecek 347 1.8 jdolecek case XS_CTL_SIMPLE_TAG: 348 1.8 jdolecek default: 349 1.8 jdolecek req->task_attr = VIRTIO_SCSI_S_SIMPLE; 350 1.8 jdolecek break; 351 1.8 jdolecek } 352 1.25 reinoud req->id = virtio_rw64(vsc, slot); 353 1.8 jdolecek 354 1.2 christos if ((size_t)xs->cmdlen > sizeof(req->cdb)) { 355 1.2 christos DPRINTF(("%s: bad cmdlen %zu > %zu\n", __func__, 356 1.2 christos (size_t)xs->cmdlen, sizeof(req->cdb))); 357 1.1 christos goto stuffup; 358 1.2 christos } 359 1.2 christos 360 1.1 christos memset(req->cdb, 0, sizeof(req->cdb)); 361 1.1 christos memcpy(req->cdb, xs->cmd, xs->cmdlen); 362 1.1 christos 363 1.12 jdolecek error = bus_dmamap_load(virtio_dmat(vsc), vr->vr_data, 364 1.1 christos xs->data, xs->datalen, NULL, XS2DMA(xs)); 365 1.18 jdolecek if (error) { 366 1.18 jdolecek aprint_error_dev(sc->sc_dev, "%s: error %d loading DMA map\n", 367 1.18 jdolecek __func__, error); 368 1.18 jdolecek 369 1.18 jdolecek if (error == ENOMEM || error == EAGAIN) { 370 1.18 jdolecek /* 371 1.18 jdolecek * Map is allocated with ALLOCNOW, so this should 372 1.18 jdolecek * actually never ever happen. 373 1.18 jdolecek */ 374 1.18 jdolecek xs->error = XS_RESOURCE_SHORTAGE; 375 1.18 jdolecek } else { 376 1.11 jdolecek stuffup: 377 1.18 jdolecek /* not a temporary condition */ 378 1.18 jdolecek xs->error = XS_DRIVER_STUFFUP; 379 1.18 jdolecek } 380 1.18 jdolecek 381 1.18 jdolecek virtio_enqueue_abort(vsc, vq, slot); 382 1.1 christos scsipi_done(xs); 383 1.1 christos return; 384 1.1 christos } 385 1.1 christos 386 1.2 christos int nsegs = VIRTIO_SCSI_MIN_SEGMENTS; 387 1.2 christos if ((xs->xs_control & (XS_CTL_DATA_IN|XS_CTL_DATA_OUT)) != 0) 388 1.2 christos nsegs += vr->vr_data->dm_nsegs; 389 1.2 christos 390 1.2 christos error = virtio_enqueue_reserve(vsc, vq, slot, nsegs); 391 1.1 christos if (error) { 392 1.12 jdolecek bus_dmamap_unload(virtio_dmat(vsc), vr->vr_data); 393 1.18 jdolecek /* slot already freed by virtio_enqueue_reserve() */ 394 1.35 mlelstv xs->error = XS_BUSY; 395 1.18 jdolecek scsipi_done(xs); 396 1.18 jdolecek return; 397 1.1 christos } 398 1.1 christos 399 1.15 jdolecek vr->vr_xs = xs; 400 1.15 jdolecek 401 1.12 jdolecek bus_dmamap_sync(virtio_dmat(vsc), vr->vr_control, 402 1.1 christos offsetof(struct vioscsi_req, vr_req), 403 1.1 christos sizeof(struct virtio_scsi_req_hdr), 404 1.1 christos BUS_DMASYNC_PREWRITE); 405 1.12 jdolecek bus_dmamap_sync(virtio_dmat(vsc), vr->vr_control, 406 1.1 christos offsetof(struct vioscsi_req, vr_res), 407 1.1 christos sizeof(struct virtio_scsi_res_hdr), 408 1.1 christos BUS_DMASYNC_PREREAD); 409 1.2 christos if ((xs->xs_control & (XS_CTL_DATA_IN|XS_CTL_DATA_OUT)) != 0) 410 1.12 jdolecek bus_dmamap_sync(virtio_dmat(vsc), vr->vr_data, 0, xs->datalen, 411 1.2 christos XS2DMAPRE(xs)); 412 1.1 christos 413 1.1 christos virtio_enqueue_p(vsc, vq, slot, vr->vr_control, 414 1.1 christos offsetof(struct vioscsi_req, vr_req), 415 1.1 christos sizeof(struct virtio_scsi_req_hdr), 1); 416 1.1 christos if (xs->xs_control & XS_CTL_DATA_OUT) 417 1.1 christos virtio_enqueue(vsc, vq, slot, vr->vr_data, 1); 418 1.1 christos virtio_enqueue_p(vsc, vq, slot, vr->vr_control, 419 1.1 christos offsetof(struct vioscsi_req, vr_res), 420 1.1 christos sizeof(struct virtio_scsi_res_hdr), 0); 421 1.1 christos if (xs->xs_control & XS_CTL_DATA_IN) 422 1.1 christos virtio_enqueue(vsc, vq, slot, vr->vr_data, 0); 423 1.21 maxv dopoll = (xs->xs_control & XS_CTL_POLL) != 0; 424 1.1 christos virtio_enqueue_commit(vsc, vq, slot, 1); 425 1.1 christos 426 1.21 maxv if (!dopoll) 427 1.1 christos return; 428 1.1 christos 429 1.1 christos DPRINTF(("%s: polling...\n", __func__)); 430 1.1 christos // XXX: do this better. 431 1.1 christos int timeout = 1000; 432 1.1 christos do { 433 1.12 jdolecek virtio_intrhand(vsc); 434 1.1 christos if (vr->vr_xs != xs) 435 1.1 christos break; 436 1.1 christos delay(1000); 437 1.1 christos } while (--timeout > 0); 438 1.1 christos 439 1.1 christos if (vr->vr_xs == xs) { 440 1.1 christos // XXX: Abort! 441 1.1 christos xs->error = XS_TIMEOUT; 442 1.1 christos xs->resid = xs->datalen; 443 1.1 christos DPRINTF(("%s: polling timeout\n", __func__)); 444 1.1 christos scsipi_done(xs); 445 1.1 christos } 446 1.15 jdolecek DPRINTF(("%s: command %p done (timeout=%d)\n", __func__, 447 1.15 jdolecek xs, timeout)); 448 1.1 christos } 449 1.1 christos 450 1.1 christos static void 451 1.1 christos vioscsi_req_done(struct vioscsi_softc *sc, struct virtio_softc *vsc, 452 1.19 jdolecek struct vioscsi_req *vr, struct virtqueue *vq, int slot) 453 1.1 christos { 454 1.1 christos struct scsipi_xfer *xs = vr->vr_xs; 455 1.4 christos size_t sense_len; 456 1.1 christos 457 1.1 christos DPRINTF(("%s: enter\n", __func__)); 458 1.1 christos 459 1.12 jdolecek bus_dmamap_sync(virtio_dmat(vsc), vr->vr_control, 460 1.1 christos offsetof(struct vioscsi_req, vr_req), 461 1.1 christos sizeof(struct virtio_scsi_req_hdr), 462 1.1 christos BUS_DMASYNC_POSTWRITE); 463 1.12 jdolecek bus_dmamap_sync(virtio_dmat(vsc), vr->vr_control, 464 1.1 christos offsetof(struct vioscsi_req, vr_res), 465 1.1 christos sizeof(struct virtio_scsi_res_hdr), 466 1.1 christos BUS_DMASYNC_POSTREAD); 467 1.25 reinoud if (xs->datalen) 468 1.25 reinoud bus_dmamap_sync(virtio_dmat(vsc), vr->vr_data, 0, xs->datalen, 469 1.25 reinoud XS2DMAPOST(xs)); 470 1.1 christos 471 1.11 jdolecek xs->status = vr->vr_res.status; 472 1.25 reinoud xs->resid = virtio_rw32(vsc, vr->vr_res.residual); 473 1.11 jdolecek 474 1.4 christos switch (vr->vr_res.response) { 475 1.4 christos case VIRTIO_SCSI_S_OK: 476 1.25 reinoud sense_len = MIN(sizeof(xs->sense), 477 1.25 reinoud virtio_rw32(vsc, vr->vr_res.sense_len)); 478 1.4 christos memcpy(&xs->sense, vr->vr_res.sense, sense_len); 479 1.4 christos xs->error = (sense_len == 0) ? XS_NOERROR : XS_SENSE; 480 1.4 christos break; 481 1.4 christos case VIRTIO_SCSI_S_BAD_TARGET: 482 1.15 jdolecek vioscsi_bad_target(xs); 483 1.4 christos break; 484 1.4 christos default: 485 1.4 christos DPRINTF(("%s: stuffup: %d\n", __func__, vr->vr_res.response)); 486 1.1 christos xs->error = XS_DRIVER_STUFFUP; 487 1.1 christos xs->resid = xs->datalen; 488 1.4 christos break; 489 1.1 christos } 490 1.1 christos 491 1.15 jdolecek DPRINTF(("%s: command %p done %d, %d, %d\n", __func__, 492 1.15 jdolecek xs, xs->error, xs->status, xs->resid)); 493 1.1 christos 494 1.12 jdolecek bus_dmamap_unload(virtio_dmat(vsc), vr->vr_data); 495 1.1 christos vr->vr_xs = NULL; 496 1.11 jdolecek 497 1.19 jdolecek virtio_dequeue_commit(vsc, vq, slot); 498 1.19 jdolecek 499 1.15 jdolecek mutex_exit(&sc->sc_mutex); 500 1.1 christos scsipi_done(xs); 501 1.15 jdolecek mutex_enter(&sc->sc_mutex); 502 1.15 jdolecek } 503 1.15 jdolecek 504 1.15 jdolecek static void 505 1.15 jdolecek vioscsi_bad_target(struct scsipi_xfer *xs) 506 1.15 jdolecek { 507 1.15 jdolecek struct scsi_sense_data *sense = &xs->sense.scsi_sense; 508 1.15 jdolecek 509 1.15 jdolecek DPRINTF(("%s: bad target %d:%d\n", __func__, 510 1.15 jdolecek xs->xs_periph->periph_target, xs->xs_periph->periph_lun)); 511 1.15 jdolecek 512 1.15 jdolecek memset(sense, 0, sizeof(*sense)); 513 1.15 jdolecek sense->response_code = 0x70; 514 1.15 jdolecek sense->flags = SKEY_ILLEGAL_REQUEST; 515 1.15 jdolecek xs->error = XS_SENSE; 516 1.15 jdolecek xs->status = 0; 517 1.15 jdolecek xs->resid = 0; 518 1.1 christos } 519 1.1 christos 520 1.1 christos static int 521 1.1 christos vioscsi_vq_done(struct virtqueue *vq) 522 1.1 christos { 523 1.1 christos struct virtio_softc *vsc = vq->vq_owner; 524 1.12 jdolecek struct vioscsi_softc *sc = device_private(virtio_child(vsc)); 525 1.1 christos int ret = 0; 526 1.1 christos 527 1.15 jdolecek DPRINTF(("%s: enter %d\n", __func__, vq->vq_index)); 528 1.15 jdolecek 529 1.15 jdolecek mutex_enter(&sc->sc_mutex); 530 1.1 christos 531 1.1 christos for (;;) { 532 1.1 christos int r, slot; 533 1.15 jdolecek 534 1.1 christos r = virtio_dequeue(vsc, vq, &slot, NULL); 535 1.1 christos if (r != 0) 536 1.1 christos break; 537 1.1 christos 538 1.1 christos DPRINTF(("%s: slot=%d\n", __func__, slot)); 539 1.11 jdolecek 540 1.19 jdolecek vioscsi_req_done(sc, vsc, &sc->sc_reqs[slot], vq, slot); 541 1.11 jdolecek 542 1.1 christos ret = 1; 543 1.1 christos } 544 1.1 christos 545 1.15 jdolecek mutex_exit(&sc->sc_mutex); 546 1.15 jdolecek 547 1.15 jdolecek DPRINTF(("%s: exit %d: %d\n", __func__, vq->vq_index, ret)); 548 1.1 christos 549 1.1 christos return ret; 550 1.1 christos } 551 1.1 christos 552 1.1 christos static struct vioscsi_req * 553 1.1 christos vioscsi_req_get(struct vioscsi_softc *sc) 554 1.1 christos { 555 1.1 christos struct virtio_softc *vsc = device_private(device_parent(sc->sc_dev)); 556 1.10 jdolecek struct virtqueue *vq = &sc->sc_vqs[VIOSCSI_VQ_REQUEST]; 557 1.15 jdolecek struct vioscsi_req *vr = NULL; 558 1.1 christos int r, slot; 559 1.1 christos 560 1.15 jdolecek mutex_enter(&sc->sc_mutex); 561 1.15 jdolecek 562 1.1 christos if ((r = virtio_enqueue_prep(vsc, vq, &slot)) != 0) { 563 1.1 christos DPRINTF(("%s: virtio_enqueue_get error %d\n", __func__, r)); 564 1.15 jdolecek goto out; 565 1.1 christos } 566 1.9 jdolecek KASSERT(slot < sc->sc_nreqs); 567 1.1 christos vr = &sc->sc_reqs[slot]; 568 1.1 christos 569 1.1 christos DPRINTF(("%s: %p, %d\n", __func__, vr, slot)); 570 1.1 christos 571 1.15 jdolecek out: 572 1.15 jdolecek mutex_exit(&sc->sc_mutex); 573 1.15 jdolecek 574 1.1 christos return vr; 575 1.1 christos } 576 1.1 christos 577 1.15 jdolecek static int 578 1.1 christos vioscsi_alloc_reqs(struct vioscsi_softc *sc, struct virtio_softc *vsc, 579 1.15 jdolecek int qsize) 580 1.1 christos { 581 1.1 christos size_t allocsize; 582 1.9 jdolecek int r, rsegs, slot; 583 1.1 christos void *vaddr; 584 1.9 jdolecek struct vioscsi_req *vr; 585 1.1 christos 586 1.1 christos allocsize = qsize * sizeof(struct vioscsi_req); 587 1.12 jdolecek r = bus_dmamem_alloc(virtio_dmat(vsc), allocsize, 0, 0, 588 1.1 christos &sc->sc_reqs_segs[0], 1, &rsegs, BUS_DMA_NOWAIT); 589 1.1 christos if (r != 0) { 590 1.1 christos aprint_error_dev(sc->sc_dev, 591 1.1 christos "%s: bus_dmamem_alloc, size %zu, error %d\n", __func__, 592 1.1 christos allocsize, r); 593 1.9 jdolecek return r; 594 1.1 christos } 595 1.12 jdolecek r = bus_dmamem_map(virtio_dmat(vsc), &sc->sc_reqs_segs[0], 1, 596 1.1 christos allocsize, &vaddr, BUS_DMA_NOWAIT); 597 1.1 christos if (r != 0) { 598 1.1 christos aprint_error_dev(sc->sc_dev, 599 1.1 christos "%s: bus_dmamem_map failed, error %d\n", __func__, r); 600 1.12 jdolecek bus_dmamem_free(virtio_dmat(vsc), &sc->sc_reqs_segs[0], 1); 601 1.9 jdolecek return r; 602 1.1 christos } 603 1.9 jdolecek memset(vaddr, 0, allocsize); 604 1.9 jdolecek 605 1.1 christos sc->sc_reqs = vaddr; 606 1.9 jdolecek sc->sc_nreqs = qsize; 607 1.9 jdolecek 608 1.28 skrll /* Prepare maps for the requests */ 609 1.9 jdolecek for (slot=0; slot < qsize; slot++) { 610 1.9 jdolecek vr = &sc->sc_reqs[slot]; 611 1.9 jdolecek 612 1.12 jdolecek r = bus_dmamap_create(virtio_dmat(vsc), 613 1.9 jdolecek offsetof(struct vioscsi_req, vr_xs), 1, 614 1.9 jdolecek offsetof(struct vioscsi_req, vr_xs), 0, 615 1.9 jdolecek BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &vr->vr_control); 616 1.9 jdolecek if (r != 0) { 617 1.9 jdolecek aprint_error_dev(sc->sc_dev, 618 1.30 andvar "%s: bus_dmamap_create ctrl failed, error %d\n", 619 1.9 jdolecek __func__, r); 620 1.9 jdolecek goto cleanup; 621 1.9 jdolecek } 622 1.9 jdolecek 623 1.12 jdolecek r = bus_dmamap_create(virtio_dmat(vsc), MAXPHYS, sc->sc_seg_max, 624 1.9 jdolecek MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &vr->vr_data); 625 1.9 jdolecek if (r != 0) { 626 1.9 jdolecek aprint_error_dev(sc->sc_dev, 627 1.30 andvar "%s: bus_dmamap_create data failed, error %d\n", 628 1.9 jdolecek __func__, r); 629 1.9 jdolecek goto cleanup; 630 1.9 jdolecek } 631 1.9 jdolecek 632 1.12 jdolecek r = bus_dmamap_load(virtio_dmat(vsc), vr->vr_control, 633 1.9 jdolecek vr, offsetof(struct vioscsi_req, vr_xs), NULL, 634 1.9 jdolecek BUS_DMA_NOWAIT); 635 1.9 jdolecek if (r != 0) { 636 1.9 jdolecek aprint_error_dev(sc->sc_dev, 637 1.15 jdolecek "%s: bus_dmamap_load ctrl error %d\n", 638 1.9 jdolecek __func__, r); 639 1.9 jdolecek goto cleanup; 640 1.9 jdolecek } 641 1.9 jdolecek } 642 1.9 jdolecek 643 1.1 christos return 0; 644 1.9 jdolecek 645 1.9 jdolecek cleanup: 646 1.9 jdolecek for (; slot > 0; slot--) { 647 1.9 jdolecek vr = &sc->sc_reqs[slot]; 648 1.9 jdolecek 649 1.9 jdolecek if (vr->vr_control) { 650 1.9 jdolecek /* this will also unload the mapping if loaded */ 651 1.12 jdolecek bus_dmamap_destroy(virtio_dmat(vsc), vr->vr_control); 652 1.9 jdolecek vr->vr_control = NULL; 653 1.9 jdolecek } 654 1.9 jdolecek 655 1.9 jdolecek if (vr->vr_data) { 656 1.12 jdolecek bus_dmamap_destroy(virtio_dmat(vsc), vr->vr_data); 657 1.9 jdolecek vr->vr_data = NULL; 658 1.9 jdolecek } 659 1.9 jdolecek } 660 1.9 jdolecek 661 1.12 jdolecek bus_dmamem_unmap(virtio_dmat(vsc), vaddr, allocsize); 662 1.12 jdolecek bus_dmamem_free(virtio_dmat(vsc), &sc->sc_reqs_segs[0], 1); 663 1.9 jdolecek 664 1.9 jdolecek return r; 665 1.1 christos } 666 1.12 jdolecek 667 1.12 jdolecek static void 668 1.12 jdolecek vioscsi_free_reqs(struct vioscsi_softc *sc, struct virtio_softc *vsc) 669 1.12 jdolecek { 670 1.12 jdolecek int slot; 671 1.12 jdolecek struct vioscsi_req *vr; 672 1.12 jdolecek 673 1.12 jdolecek if (sc->sc_nreqs == 0) { 674 1.12 jdolecek /* Not allocated */ 675 1.12 jdolecek return; 676 1.12 jdolecek } 677 1.12 jdolecek 678 1.28 skrll /* Free request maps */ 679 1.12 jdolecek for (slot=0; slot < sc->sc_nreqs; slot++) { 680 1.12 jdolecek vr = &sc->sc_reqs[slot]; 681 1.12 jdolecek 682 1.12 jdolecek bus_dmamap_destroy(virtio_dmat(vsc), vr->vr_control); 683 1.12 jdolecek bus_dmamap_destroy(virtio_dmat(vsc), vr->vr_data); 684 1.12 jdolecek } 685 1.12 jdolecek 686 1.12 jdolecek bus_dmamem_unmap(virtio_dmat(vsc), sc->sc_reqs, 687 1.12 jdolecek sc->sc_nreqs * sizeof(struct vioscsi_req)); 688 1.12 jdolecek bus_dmamem_free(virtio_dmat(vsc), &sc->sc_reqs_segs[0], 1); 689 1.12 jdolecek } 690 1.12 jdolecek 691 1.15 jdolecek MODULE(MODULE_CLASS_DRIVER, vioscsi, "virtio"); 692 1.15 jdolecek 693 1.15 jdolecek #ifdef _MODULE 694 1.15 jdolecek #include "ioconf.c" 695 1.15 jdolecek #endif 696 1.15 jdolecek 697 1.15 jdolecek static int 698 1.15 jdolecek vioscsi_modcmd(modcmd_t cmd, void *opaque) 699 1.15 jdolecek { 700 1.15 jdolecek int error = 0; 701 1.15 jdolecek 702 1.15 jdolecek #ifdef _MODULE 703 1.15 jdolecek switch (cmd) { 704 1.15 jdolecek case MODULE_CMD_INIT: 705 1.15 jdolecek error = config_init_component(cfdriver_ioconf_vioscsi, 706 1.15 jdolecek cfattach_ioconf_vioscsi, cfdata_ioconf_vioscsi); 707 1.15 jdolecek break; 708 1.15 jdolecek case MODULE_CMD_FINI: 709 1.15 jdolecek error = config_fini_component(cfdriver_ioconf_vioscsi, 710 1.15 jdolecek cfattach_ioconf_vioscsi, cfdata_ioconf_vioscsi); 711 1.15 jdolecek break; 712 1.15 jdolecek default: 713 1.15 jdolecek error = ENOTTY; 714 1.15 jdolecek break; 715 1.15 jdolecek } 716 1.15 jdolecek #endif 717 1.15 jdolecek 718 1.15 jdolecek return error; 719 1.15 jdolecek } 720