Module Name: src
Committed By: riastradh
Date: Wed Jun 12 16:51:54 UTC 2024
Modified Files:
src/sys/dev/pci: ld_virtio.c
Log Message:
ld@virtio(4): Fix maximum size parameters.
- SEG_MAX is the maximum number of segments.
- SIZE_MAX is the maximum number of bytes in a single segment.
The maximum transfer size is, therefore, SEG_MAX * SIZE_MAX.
=> Don't add two extra segments in the dmamap vr_payload for the
header and status -- we already have a separate dmamap vr_cmdsts
for that.
=> Don't recalculate payload dmamap parameters based on division by
NBPG, just use the ones specified by the host.
=> Allow SIZE_MAX below MAXPHYS as long as SIZE_MAX*SEG_MAX >=
MAXPHYS.
Even though ldattach clamps ld->sc_maxxfer to MAXPHYS, make sure to
clamp it in ld_virtio_attach before ld_virtio_alloc_reqs since it
determines the dmamap sizes and bounce buffer allocation and there's
no sense in allocating those larger than ld will use anyway.
PR kern/58338
To generate a diff of this commit:
cvs rdiff -u -r1.34 -r1.35 src/sys/dev/pci/ld_virtio.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/pci/ld_virtio.c
diff -u src/sys/dev/pci/ld_virtio.c:1.34 src/sys/dev/pci/ld_virtio.c:1.35
--- src/sys/dev/pci/ld_virtio.c:1.34 Sat Mar 9 11:04:22 2024
+++ src/sys/dev/pci/ld_virtio.c Wed Jun 12 16:51:53 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: ld_virtio.c,v 1.34 2024/03/09 11:04:22 isaki Exp $ */
+/* $NetBSD: ld_virtio.c,v 1.35 2024/06/12 16:51:53 riastradh Exp $ */
/*
* Copyright (c) 2010 Minoura Makoto.
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,v 1.34 2024/03/09 11:04:22 isaki Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,v 1.35 2024/06/12 16:51:53 riastradh Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -128,6 +128,9 @@ struct ld_virtio_softc {
struct ld_softc sc_ld;
device_t sc_dev;
+ uint32_t sc_seg_max; /* max number of segs in xfer */
+ uint32_t sc_size_max; /* max size of single seg */
+
struct virtio_softc *sc_virtio;
struct virtqueue sc_vq;
@@ -220,11 +223,10 @@ ld_virtio_alloc_reqs(struct ld_virtio_so
goto err_reqs;
}
r = bus_dmamap_create(virtio_dmat(sc->sc_virtio),
- ld->sc_maxxfer,
- (ld->sc_maxxfer / NBPG) +
- VIRTIO_BLK_CTRL_SEGMENTS,
- ld->sc_maxxfer,
- 0,
+ /*size*/ld->sc_maxxfer,
+ /*nseg*/sc->sc_seg_max,
+ /*maxsegsz*/sc->sc_size_max,
+ /*boundary*/0,
BUS_DMA_WAITOK|BUS_DMA_ALLOCNOW,
&vr->vr_payload);
if (r != 0) {
@@ -264,7 +266,7 @@ ld_virtio_attach(device_t parent, device
struct ld_softc *ld = &sc->sc_ld;
struct virtio_softc *vsc = device_private(parent);
uint64_t features;
- int qsize, maxxfersize, maxnsegs;
+ int qsize;
if (virtio_child(vsc) != NULL) {
aprint_normal(": child already attached for %s; "
@@ -296,46 +298,53 @@ ld_virtio_attach(device_t parent, device
} else
ld->sc_secsize = VIRTIO_BLK_BSIZE;
- /* At least genfs_io assumes maxxfer == MAXPHYS. */
- if (features & VIRTIO_BLK_F_SIZE_MAX) {
- maxxfersize = virtio_read_device_config_4(vsc,
- VIRTIO_BLK_CONFIG_SIZE_MAX);
- if (maxxfersize < MAXPHYS) {
- aprint_error_dev(sc->sc_dev,
- "Too small SIZE_MAX %dK minimum is %dK\n",
- maxxfersize / 1024, MAXPHYS / 1024);
- // goto err;
- maxxfersize = MAXPHYS;
- } else if (maxxfersize > MAXPHYS) {
- aprint_normal_dev(sc->sc_dev,
- "Clip SIZE_MAX from %dK to %dK\n",
- maxxfersize / 1024,
- MAXPHYS / 1024);
- maxxfersize = MAXPHYS;
- }
- } else
- maxxfersize = MAXPHYS;
-
if (features & VIRTIO_BLK_F_SEG_MAX) {
- maxnsegs = virtio_read_device_config_4(vsc,
+ sc->sc_seg_max = virtio_read_device_config_4(vsc,
VIRTIO_BLK_CONFIG_SEG_MAX);
- if (maxnsegs == 0) {
+ if (sc->sc_seg_max == 0) {
aprint_error_dev(sc->sc_dev,
- "Invalid SEG_MAX %d\n", maxnsegs);
+ "Invalid SEG_MAX %d\n", sc->sc_seg_max);
goto err;
}
- } else
- maxnsegs = maxxfersize / NBPG;
+ } else {
+ sc->sc_seg_max = 1;
+ aprint_verbose_dev(sc->sc_dev,
+ "Unknown SEG_MAX, assuming %"PRIu32"\n", sc->sc_seg_max);
+ }
- maxnsegs += VIRTIO_BLK_CTRL_SEGMENTS;
+ /* At least genfs_io assumes size_max*seg_max >= MAXPHYS. */
+ if (features & VIRTIO_BLK_F_SIZE_MAX) {
+ sc->sc_size_max = virtio_read_device_config_4(vsc,
+ VIRTIO_BLK_CONFIG_SIZE_MAX);
+ if (sc->sc_size_max < MAXPHYS/sc->sc_seg_max) {
+ aprint_error_dev(sc->sc_dev,
+ "Too small SIZE_MAX %d minimum is %d\n",
+ sc->sc_size_max, MAXPHYS/sc->sc_seg_max);
+ // goto err;
+ sc->sc_size_max = MAXPHYS/sc->sc_seg_max;
+ } else if (sc->sc_size_max > MAXPHYS) {
+ aprint_verbose_dev(sc->sc_dev,
+ "Clip SIZE_MAX from %d to %d\n",
+ sc->sc_size_max, MAXPHYS);
+ sc->sc_size_max = MAXPHYS;
+ }
+ } else {
+ sc->sc_size_max = MAXPHYS;
+ aprint_verbose_dev(sc->sc_dev,
+ "Unknown SIZE_MAX, assuming %"PRIu32"\n",
+ sc->sc_size_max);
+ }
+
+ aprint_normal_dev(sc->sc_dev, "max %"PRIu32" segs"
+ " of max %"PRIu32" bytes\n",
+ sc->sc_seg_max, sc->sc_size_max);
virtio_init_vq_vqdone(vsc, &sc->sc_vq, 0,
ld_virtio_vq_done);
- if (virtio_alloc_vq(vsc, &sc->sc_vq, maxxfersize, maxnsegs,
- "I/O request") != 0) {
+ if (virtio_alloc_vq(vsc, &sc->sc_vq, sc->sc_size_max,
+ sc->sc_seg_max + VIRTIO_BLK_CTRL_SEGMENTS, "I/O request") != 0)
goto err;
- }
qsize = sc->sc_vq.vq_num;
if (virtio_child_attach_finish(vsc, &sc->sc_vq, 1,
@@ -345,7 +354,15 @@ ld_virtio_attach(device_t parent, device
ld->sc_dv = self;
ld->sc_secperunit = virtio_read_device_config_8(vsc,
VIRTIO_BLK_CONFIG_CAPACITY) / (ld->sc_secsize / VIRTIO_BLK_BSIZE);
- ld->sc_maxxfer = maxxfersize;
+
+ /*
+ * Clamp ld->sc_maxxfer to MAXPHYS before ld_virtio_alloc_reqs
+ * allocates DMA maps of at most ld->sc_maxxfer bytes.
+ * ldattach will also clamp to MAXPHYS, but not until after
+ * ld_virtio_alloc_reqs is done, so that doesn't help.
+ */
+ ld->sc_maxxfer = MIN(MAXPHYS, sc->sc_size_max * sc->sc_seg_max);
+
if (features & VIRTIO_BLK_F_GEOMETRY) {
ld->sc_ncylinders = virtio_read_device_config_2(vsc,
VIRTIO_BLK_CONFIG_GEOMETRY_C);
@@ -410,6 +427,7 @@ ld_virtio_start(struct ld_softc *ld, str
return r;
}
+ KASSERT(vr->vr_payload->dm_nsegs <= sc->sc_seg_max);
r = virtio_enqueue_reserve(vsc, vq, slot, vr->vr_payload->dm_nsegs +
VIRTIO_BLK_CTRL_SEGMENTS);
if (r != 0) {