diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2024-08-20 11:45:32 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2024-08-20 11:45:32 +0000 |
commit | 7f11f484cd38a7f6f7af94f14da66642ce0072a1 (patch) | |
tree | 48c757f6bc0a2f1639327b9d78c136c67f52f642 | |
parent | d62dc29abaf839ab8ef4ae92a88f4425804131de (diff) |
Calculate used bounce buffers in amd64 bus dma correctly.
There was an off-by-one bug when comparing the used pages for bounce
buffers with the available pages. As a result _bus_dmamap_load_buffer()
returned ENOMEM although there was one buffer left.
Also the _dm_nused field was updated and never reset in case of an
error. Use a local variable to count the used pages and update
global map->_dm_nused only if _bus_dmamap_load_buffer() was successful.
This fixes hanging network transmits if bounce buffers are enforced
for vio(4).
OK sf@ hshoexer@
-rw-r--r-- | sys/arch/amd64/amd64/bus_dma.c | 34 |
1 files changed, 21 insertions, 13 deletions
diff --git a/sys/arch/amd64/amd64/bus_dma.c b/sys/arch/amd64/amd64/bus_dma.c index 6ad6583bce9..ac8fa405798 100644 --- a/sys/arch/amd64/amd64/bus_dma.c +++ b/sys/arch/amd64/amd64/bus_dma.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bus_dma.c,v 1.53 2024/08/18 21:04:29 bluhm Exp $ */ +/* $OpenBSD: bus_dma.c,v 1.54 2024/08/20 11:45:31 bluhm Exp $ */ /* $NetBSD: bus_dma.c,v 1.3 2003/05/07 21:33:58 fvdl Exp $ */ /*- @@ -102,7 +102,7 @@ #endif int _bus_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t, - struct proc *, int, paddr_t *, int *, int); + struct proc *, int, paddr_t *, int *, int *, int); /* * Common function for DMA map creation. May be called by bus-specific @@ -251,7 +251,7 @@ _bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, bus_size_t buflen, struct proc *p, int flags) { bus_addr_t lastaddr = 0; - int seg, error; + int seg, used, error; /* * Make sure that on error condition we return "no valid mappings". @@ -263,11 +263,13 @@ _bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, return (EINVAL); seg = 0; + used = 0; error = _bus_dmamap_load_buffer(t, map, buf, buflen, p, flags, - &lastaddr, &seg, 1); + &lastaddr, &seg, &used, 1); if (error == 0) { map->dm_mapsize = buflen; map->dm_nsegs = seg + 1; + map->_dm_nused = used; } return (error); } @@ -280,7 +282,7 @@ _bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0, int flags) { paddr_t lastaddr = 0; - int seg, error, first; + int seg, used, error, first; struct mbuf *m; /* @@ -299,17 +301,19 @@ _bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0, first = 1; seg = 0; + used = 0; error = 0; for (m = m0; m != NULL && error == 0; m = m->m_next) { if (m->m_len == 0) continue; error = _bus_dmamap_load_buffer(t, map, m->m_data, m->m_len, - NULL, flags, &lastaddr, &seg, first); + NULL, flags, &lastaddr, &seg, &used, first); first = 0; } if (error == 0) { map->dm_mapsize = m0->m_pkthdr.len; map->dm_nsegs = seg + 1; + map->_dm_nused = used; } return (error); } @@ -322,7 +326,7 @@ _bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, int flags) { paddr_t lastaddr = 0; - int seg, i, error, first; + int seg, used, i, error, first; bus_size_t minlen, resid; struct proc *p = NULL; struct iovec *iov; @@ -347,6 +351,7 @@ _bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, first = 1; seg = 0; + used = 0; error = 0; for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) { /* @@ -357,7 +362,7 @@ _bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, addr = (caddr_t)iov[i].iov_base; error = _bus_dmamap_load_buffer(t, map, addr, minlen, - p, flags, &lastaddr, &seg, first); + p, flags, &lastaddr, &seg, &used, first); first = 0; resid -= minlen; @@ -365,6 +370,7 @@ _bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, if (error == 0) { map->dm_mapsize = uio->uio_resid; map->dm_nsegs = seg + 1; + map->_dm_nused = used; } return (error); } @@ -683,8 +689,8 @@ _bus_dmamem_mmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs, off_t off, */ int _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, - bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp, int *segp, - int first) + bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp, + int *segp, int *usedp, int first) { bus_size_t sgsize; bus_addr_t curaddr, lastaddr, baddr, bmask; @@ -699,6 +705,7 @@ _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, else pmap = pmap_kernel(); + page = *usedp; lastaddr = *lastaddrp; bmask = ~(map->_dm_boundary - 1); @@ -714,14 +721,14 @@ _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, curaddr); if (use_bounce_buffer) { - if (map->_dm_nused + 1 >= map->_dm_npages) + if (page >= map->_dm_npages) return (ENOMEM); off = vaddr & PAGE_MASK; - pg = map->_dm_pages[page = map->_dm_nused++]; + pg = map->_dm_pages[page]; curaddr = VM_PAGE_TO_PHYS(pg) + off; - pgva = map->_dm_pgva + (page << PGSHIFT) + off; + page++; } /* @@ -774,6 +781,7 @@ _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, } *segp = seg; + *usedp = page; *lastaddrp = lastaddr; /* |