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.)
This commit is contained in:
waddlesplash 2019-12-02 20:37:52 -05:00
parent 70cdd7d4f5
commit 7100b1e1f5
1 changed files with 11 additions and 8 deletions

View File

@ -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;
}