diff --git a/sys/platform/pc64/x86_64/busdma_machdep.c b/sys/platform/pc64/x86_64/busdma_machdep.c --- a/sys/platform/pc64/x86_64/busdma_machdep.c +++ b/sys/platform/pc64/x86_64/busdma_machdep.c @@ -938,8 +938,6 @@ pmap_t pmap; bus_dma_segment_t cache_segments[BUS_DMA_CACHE_SEGMENTS]; bus_dma_segment_t *segments; - bus_dma_segment_t *segs; - int nsegs_left; if (dmat->nsegments <= BUS_DMA_CACHE_SEGMENTS) segments = cache_segments; @@ -958,8 +956,6 @@ resid = (bus_size_t)uio->uio_resid; iov = uio->uio_iov; - segs = segments; - nsegs_left = dmat->nsegments; if (uio->uio_segflg == UIO_USERSPACE) { struct thread *td; @@ -986,29 +982,39 @@ caddr_t addr = (caddr_t) iov[i].iov_base; error = _bus_dmamap_load_buffer(dmat, map, addr, minlen, - segs, nsegs_left, + segments, dmat->nsegments, pmap, flags, &lastaddr, &nsegs, first); first = 0; resid -= minlen; - if (error == 0) { - nsegs_left -= nsegs; - segs += nsegs; - } + /* + * _bus_dmamap_load_buffer treats *segp (= &nsegs) as a + * 1-based ABSOLUTE index into the array passed via the + * 'segments' parameter (line 664: sg = &segments[seg-1]). + * We must therefore pass the CONSTANT segments pointer and + * dmat->nsegments on every call, exactly like + * bus_dmamap_load_mbuf_segment() does. The previous code + * advanced segs and shrank nsegs_left between iovecs but + * kept *segp absolute, which tripped the KKASSERT at line + * 662 (INVARIANTS) or wrote past the segments array (noinv). + * _bus_dmamap_load_buffer returns EFBIG when it runs out of + * segments, so we still terminate cleanly. + */ } + /* - * Minimum one DMA segment, even if 0-length buffer. + * nsegs is the absolute 1-based end-index of the last segment + * written (set by _bus_dmamap_load_buffer via *segp), so it is + * always >= 1, satisfying the historical "minimum one DMA segment + * even if 0-length buffer" requirement without further arithmetic. */ - if (nsegs_left == dmat->nsegments) - --nsegs_left; - if (error) { /* force "no valid mappings" in callback */ callback(callback_arg, segments, 0, 0, error); } else { - callback(callback_arg, segments, dmat->nsegments - nsegs_left, + callback(callback_arg, segments, nsegs, (bus_size_t)uio->uio_resid, error); } if (dmat->nsegments > BUS_DMA_CACHE_SEGMENTS)