From aa2d9ae8d22a0b68b2b39ccfd6e4396f39f68189 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Sun, 18 Jan 2009 11:14:38 +0800 Subject: [PATCH] busdma(9): Add bus_dmamap_load_mbuf_segment() See the comment in sys/bus_dma.h for detailed information about this function. Let bus_dmamap_load_mbuf() call this function with busdma tag's segments and nsegments. Inspired-by: FreeBSD's bus_dmamap_load_mbuf_sg() --- sys/platform/pc32/i386/busdma_machdep.c | 63 ++++++++++++++++++------- sys/sys/bus_dma.h | 14 ++++++ 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/sys/platform/pc32/i386/busdma_machdep.c b/sys/platform/pc32/i386/busdma_machdep.c index fed5e2fddb..02b2d18da0 100644 --- a/sys/platform/pc32/i386/busdma_machdep.c +++ b/sys/platform/pc32/i386/busdma_machdep.c @@ -539,6 +539,8 @@ static int _bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, bus_size_t buflen, + bus_dma_segment_t *segments, + int nsegments, pmap_t pmap, int flags, vm_paddr_t *lastpaddrp, @@ -607,9 +609,9 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dmat, BZ_UNLOCK(bz); } - KKASSERT(*segp >= 1 && *segp <= dmat->nsegments); + KKASSERT(*segp >= 1 && *segp <= nsegments); seg = *segp; - sg = &dmat->segments[seg - 1]; + sg = &segments[seg - 1]; vaddr = (vm_offset_t)buf; nextpaddr = *lastpaddrp; @@ -647,7 +649,7 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dmat, } else { sg++; seg++; - if (seg > dmat->nsegments) + if (seg > nsegments) break; sg->ds_addr = paddr; sg->ds_len = size; @@ -678,7 +680,7 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dmat, /* * Futz, split the data into a new segment. */ - if (seg >= dmat->nsegments) + if (seg >= nsegments) goto fail; sg[1].ds_len = sg[0].ds_len - tmpsize; sg[1].ds_addr = sg[0].ds_addr + tmpsize; @@ -734,6 +736,7 @@ bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, } error = _bus_dmamap_load_buffer(dmat, map, buf, buflen, + dmat->segments, dmat->nsegments, NULL, flags, &lastaddr, &nsegs, 1); if (error == EINPROGRESS) return error; @@ -753,8 +756,6 @@ bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, { int nsegs, error; - M_ASSERTPKTHDR(m0); - /* * XXX * Follow old semantics. Once all of the callers are fixed, @@ -763,12 +764,41 @@ bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, flags &= ~BUS_DMA_WAITOK; flags |= BUS_DMA_NOWAIT; + error = bus_dmamap_load_mbuf_segment(dmat, map, m0, + dmat->segments, dmat->nsegments, &nsegs, flags); + if (error) { + /* force "no valid mappings" in callback */ + callback(callback_arg, dmat->segments, 0, 0, error); + } else { + callback(callback_arg, dmat->segments, nsegs, + m0->m_pkthdr.len, error); + } + return error; +} + +int +bus_dmamap_load_mbuf_segment(bus_dma_tag_t dmat, bus_dmamap_t map, + struct mbuf *m0, + bus_dma_segment_t *segs, int maxsegs, + int *nsegs, int flags) +{ + int error; + + M_ASSERTPKTHDR(m0); + + KASSERT(maxsegs >= 1, ("invalid maxsegs %d\n", maxsegs)); + KASSERT(maxsegs <= dmat->nsegments, + ("%d too many segments, dmat only support %d segments\n", + maxsegs, dmat->nsegments)); + KASSERT(flags & BUS_DMA_NOWAIT, + ("only BUS_DMA_NOWAIT is supported\n")); + if (m0->m_pkthdr.len <= dmat->maxsize) { int first = 1; vm_paddr_t lastaddr = 0; struct mbuf *m; - nsegs = 1; + *nsegs = 1; error = 0; for (m = m0; m != NULL && error == 0; m = m->m_next) { if (m->m_len == 0) @@ -776,8 +806,9 @@ bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, error = _bus_dmamap_load_buffer(dmat, map, m->m_data, m->m_len, + segs, maxsegs, NULL, flags, &lastaddr, - &nsegs, first); + nsegs, first); if (error == ENOMEM && !first) { /* * Out of bounce pages due to too many @@ -788,18 +819,15 @@ bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, } first = 0; } +#ifdef INVARIANTS + if (!error) + KKASSERT(*nsegs <= maxsegs && *nsegs >= 1); +#endif } else { - nsegs = 0; + *nsegs = 0; error = EINVAL; } - - if (error) { - /* force "no valid mappings" in callback */ - callback(callback_arg, dmat->segments, 0, 0, error); - } else { - callback(callback_arg, dmat->segments, nsegs, - m0->m_pkthdr.len, error); - } + KKASSERT(error != EINPROGRESS); return error; } @@ -854,6 +882,7 @@ bus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, caddr_t addr = (caddr_t) iov[i].iov_base; error = _bus_dmamap_load_buffer(dmat, map, addr, minlen, + dmat->segments, dmat->nsegments, pmap, flags, &lastaddr, &nsegs, first); first = 0; diff --git a/sys/sys/bus_dma.h b/sys/sys/bus_dma.h index 49c96e8185..b1b3c03c25 100644 --- a/sys/sys/bus_dma.h +++ b/sys/sys/bus_dma.h @@ -242,6 +242,20 @@ int bus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmamap_callback2_t *callback, void *callback_arg, int flags); +/* + * Like bus_dmamap_load_mbuf without callback. + * Segmentation information are saved in 'segs' and 'nsegs' if + * the loading is successful. 'maxsegs' must be set by caller + * and must be at least 1 but less than 'dmat' nsegment. It + * indicates the number of elements in 'segs'. 'flags' must + * have BUS_DMA_NOWAIT turned on. + */ +int +bus_dmamap_load_mbuf_segment(bus_dma_tag_t dmat, bus_dmamap_t map, + struct mbuf *mbuf, + bus_dma_segment_t *segs, int maxsegs, + int *nsegs, int flags); + /* * Convenient function to create coherent busdma memory */ -- 2.41.0