lx_emul: comply boundary constraints of dma pool

Several DMA pools of the EHCI/UHCI USB host controller driver declare
that buffers should not cross 4K boundaries. If this property is not met
fatal errors like NMIs may happen during USB operation.

Discussed in issue #5000
This commit is contained in:
Christian Helmuth 2023-11-08 17:12:26 +01:00
parent e337f2cb0f
commit 963b8b0607

View File

@ -20,6 +20,8 @@ struct dma_pool
{ {
size_t size; size_t size;
size_t align; size_t align;
unsigned boundary; /* power-of-two */
char name[32];
}; };
void * dma_pool_alloc(struct dma_pool * pool, gfp_t mem_flags, dma_addr_t * handle) void * dma_pool_alloc(struct dma_pool * pool, gfp_t mem_flags, dma_addr_t * handle)
@ -31,6 +33,16 @@ void * dma_pool_alloc(struct dma_pool * pool, gfp_t mem_flags, dma_addr_t * hand
void * ret = void * ret =
lx_emul_mem_alloc_aligned_uncached(pool->size, pool->align); lx_emul_mem_alloc_aligned_uncached(pool->size, pool->align);
#endif #endif
if (pool->boundary && ret) {
unsigned long b = ((unsigned long)ret) >> pool->boundary;
unsigned long e = ((unsigned long)ret + pool->size - 1) >> pool->boundary;
if (b != e)
printk("%s: allocation crosses %s pool boundary of %#lx bytes\n",
__func__, pool->name, 1ul << pool->boundary);
}
*handle = lx_emul_mem_dma_addr(ret); *handle = lx_emul_mem_dma_addr(ret);
return ret; return ret;
} }
@ -48,8 +60,16 @@ struct dma_pool * dma_pool_create(const char * name,
/* TODO check if it makes sense to add min(align, PAGE_SIZE) check */ /* TODO check if it makes sense to add min(align, PAGE_SIZE) check */
pool->size = size; /* ensure allocations do not cross the given boundary */
pool->align = align; if (boundary)
align = max_t(size_t, roundup_pow_of_two(size), align);
pool->size = size;
pool->align = align;
pool->boundary = order_base_2(boundary);
strscpy(pool->name, name, sizeof(pool->name));
return pool; return pool;
} }
@ -68,15 +88,17 @@ struct dma_pool *dmam_pool_create(const char *name,
} }
/*
* Caller guarantees that no more memory from the pool is in use,
* and that nothing will try to use the pool after this call.
*/
void dma_pool_destroy(struct dma_pool * pool) void dma_pool_destroy(struct dma_pool * pool)
{ {
kfree(pool); kfree(pool);
} }
void dma_pool_free(struct dma_pool * pool,void * vaddr,dma_addr_t dma) void dma_pool_free(struct dma_pool * pool,void * vaddr,dma_addr_t dma)
{ {
lx_emul_mem_free(vaddr); lx_emul_mem_free(vaddr);
} }