summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2024-08-20 11:45:32 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2024-08-20 11:45:32 +0000
commit7f11f484cd38a7f6f7af94f14da66642ce0072a1 (patch)
tree48c757f6bc0a2f1639327b9d78c136c67f52f642
parentd62dc29abaf839ab8ef4ae92a88f4425804131de (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.c34
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;
/*