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) {