From 7100b1e1f545c900db7e27ba61242002a91279cf Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Mon, 2 Dec 2019 20:37:52 -0500 Subject: [PATCH] freebsd_network: Move segment array allocation in bus_dma to dmamap_create(). It seems that some drivers (e.g. broadcom43xx) create a parent DMA tag with nsegments set to BUS_SPACE_UNRESTRICTED, i.e. MAX_INT, which of course fails allocation, expecting to never allocate memory for this tag, only for child tags. So in order to handle this, we have to delay allocating the segment array until we are certain that the nsegments value is the "real deal". Doing it in dmamap_load would be fine, but as there is more than one entry point to that, we would have to allocate this in multiple places. dmamap_create() must be called and there is only one way through it, so put the allocation there. Fixes #15500 (i.e. both the KDL and the underlying problem that led to it; it only crashed because the wrong pointer was passed to kernel_free, whoops.) --- src/libs/compat/freebsd_network/bus_dma.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/libs/compat/freebsd_network/bus_dma.cpp b/src/libs/compat/freebsd_network/bus_dma.cpp index 6b588fadf5..e2bce61aca 100644 --- a/src/libs/compat/freebsd_network/bus_dma.cpp +++ b/src/libs/compat/freebsd_network/bus_dma.cpp @@ -84,14 +84,6 @@ bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, bus_size_t bounda newtag->maxsegsz = maxsegsz; newtag->ref_count = 1; - newtag->segments = (bus_dma_segment_t*)kernel_malloc( - sizeof(bus_dma_segment_t) * newtag->nsegments, M_DEVBUF, - M_NOWAIT); - if (newtag->segments == NULL) { - kernel_free(dmat, M_DEVBUF); - return ENOMEM; - } - if (newtag->parent != NULL) { atomic_add(&parent->ref_count, 1); @@ -147,6 +139,17 @@ bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t* mapp) { // We never bounce, so we do not need maps. *mapp = NULL; + + // However, since bus_dmamap_create() must be called before buffers + // are loaded, we allocate the "segments" field (if not yet done.) + if (dmat->segments == NULL) { + dmat->segments = (bus_dma_segment_t*)kernel_malloc( + sizeof(bus_dma_segment_t) * dmat->nsegments, M_DEVBUF, + M_ZERO | M_NOWAIT); + if (dmat->segments == NULL) + return ENOMEM; + } + return 0; }