summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Fritsch <sf@cvs.openbsd.org>2017-05-15 19:02:17 +0000
committerStefan Fritsch <sf@cvs.openbsd.org>2017-05-15 19:02:17 +0000
commit2ca8d835f9389430606a9d3304f42d05419d9d77 (patch)
treee9908c7319ae52ae33e0012d422be28dfdba7316
parent66258813280cd62a12660b149648681d1b962caa (diff)
vioscsi: Fix allocation of segments
Port the logic to calculate the number of segments in virtqueue and dmamaps from vioblk. This fixes the virtqueue indirect descriptors being two entries too small the dmamaps being much larger than necessary. If the device does not support the required number of segments, refuse to use it.
-rw-r--r--sys/dev/pv/vioscsi.c28
1 files changed, 20 insertions, 8 deletions
diff --git a/sys/dev/pv/vioscsi.c b/sys/dev/pv/vioscsi.c
index cb41a862413..1b8daab6502 100644
--- a/sys/dev/pv/vioscsi.c
+++ b/sys/dev/pv/vioscsi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vioscsi.c,v 1.6 2017/05/14 12:17:47 krw Exp $ */
+/* $OpenBSD: vioscsi.c,v 1.7 2017/05/15 19:02:16 sf Exp $ */
/*
* Copyright (c) 2013 Google Inc.
*
@@ -40,6 +40,12 @@ enum { vioscsi_debug = 0 };
} while (0)
+#define MAX_XFER MAX(MAXPHYS,MAXBSIZE)
+/* Number of DMA segments for buffers that the device must support */
+#define SEG_MAX (MAX_XFER/PAGE_SIZE + 1)
+/* In the virtqueue, we need space for header and footer, too */
+#define ALLOC_SEGS (SEG_MAX + 2)
+
enum vioscsi_req_state { FREE, ALLOC, INQUEUE, DONE };
struct vioscsi_req {
@@ -60,8 +66,6 @@ struct vioscsi_softc {
struct virtqueue sc_vqs[3];
struct vioscsi_req *sc_reqs;
bus_dma_segment_t sc_reqs_segs[1];
-
- u_int32_t sc_seg_max;
};
int vioscsi_match(struct device *, void *, void *);
@@ -137,11 +141,14 @@ vioscsi_attach(struct device *parent, struct device *self, void *aux)
uint16_t max_target = virtio_read_device_config_2(vsc,
VIRTIO_SCSI_CONFIG_MAX_TARGET);
- sc->sc_seg_max = seg_max;
+ if (seg_max < SEG_MAX) {
+ printf("\nMax number of segments %d too small\n", seg_max);
+ goto err;
+ }
for (i = 0; i < nitems(sc->sc_vqs); i++) {
- rv = virtio_alloc_vq(vsc, &sc->sc_vqs[i], i, MAXPHYS,
- 1 + howmany(MAXPHYS, NBPG), vioscsi_vq_names[i]);
+ rv = virtio_alloc_vq(vsc, &sc->sc_vqs[i], i, MAX_XFER,
+ ALLOC_SEGS, vioscsi_vq_names[i]);
if (rv) {
printf(": failed to allocate virtqueue %d\n", i);
return;
@@ -167,6 +174,11 @@ vioscsi_attach(struct device *parent, struct device *self, void *aux)
saa.saa_sc_link = &sc->sc_link;
sc->sc_scsibus = (struct scsibus *)config_found(self, &saa, scsiprint);
+ return;
+
+err:
+ vsc->sc_child = VIRTIO_CHILD_ERROR;
+ return;
}
void
@@ -442,8 +454,8 @@ vioscsi_alloc_reqs(struct vioscsi_softc *sc, struct virtio_softc *vsc,
printf("bus_dmamap_create vr_control failed, error %d\n", r);
return 1;
}
- r = bus_dmamap_create(vsc->sc_dmat, MAXPHYS, sc->sc_seg_max,
- MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &vr->vr_data);
+ r = bus_dmamap_create(vsc->sc_dmat, MAX_XFER, SEG_MAX,
+ MAX_XFER, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &vr->vr_data);
if (r != 0) {
printf("bus_dmamap_create vr_data failed, error %d\n", r );
return 1;