Skip to content

Commit

Permalink
ALSA: memalloc: Allocate more contiguous pages for fallback case
Browse files Browse the repository at this point in the history
[ Upstream commit cc26516 ]

Currently the fallback SG allocation tries to allocate each single
page, and this tends to result in the reverse order of memory
addresses when large space is available at boot, as the kernel takes a
free page from the top to the bottom in the zone.  The end result
looks as if non-contiguous (although it actually is).  What's worse is
that it leads to an overflow of BDL entries for HD-audio.

For avoiding such a problem, this patch modifies the allocation code
slightly; now it tries to allocate the larger contiguous chunks as
much as possible, then reduces to the smaller chunks only if the
allocation failed -- a similar strategy as the existing
snd_dma_alloc_pages_fallback() function.

Along with the trick, drop the unused address array from
snd_dma_sg_fallback object.  It was needed in the past when
dma_alloc_coherent() was used, but with the standard page allocator,
it became superfluous and never referred.

Fixes: a8d302a ("ALSA: memalloc: Revive x86-specific WC page allocations again")
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20221114141658.29620-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
tiwai authored and gregkh committed Dec 31, 2022
1 parent 0a730f5 commit accc799
Showing 1 changed file with 27 additions and 17 deletions.
44 changes: 27 additions & 17 deletions sound/core/memalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,6 @@ static const struct snd_malloc_ops snd_dma_sg_wc_ops = {
struct snd_dma_sg_fallback {
size_t count;
struct page **pages;
dma_addr_t *addrs;
};

static void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab,
Expand All @@ -732,38 +731,49 @@ static void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab,
for (i = 0; i < sgbuf->count && sgbuf->pages[i]; i++)
do_free_pages(page_address(sgbuf->pages[i]), PAGE_SIZE, wc);
kvfree(sgbuf->pages);
kvfree(sgbuf->addrs);
kfree(sgbuf);
}

static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
{
struct snd_dma_sg_fallback *sgbuf;
struct page **pages;
size_t i, count;
struct page **pagep, *curp;
size_t chunk, npages;
dma_addr_t addr;
void *p;
bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK;

sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
if (!sgbuf)
return NULL;
count = PAGE_ALIGN(size) >> PAGE_SHIFT;
pages = kvcalloc(count, sizeof(*pages), GFP_KERNEL);
if (!pages)
goto error;
sgbuf->pages = pages;
sgbuf->addrs = kvcalloc(count, sizeof(*sgbuf->addrs), GFP_KERNEL);
if (!sgbuf->addrs)
size = PAGE_ALIGN(size);
sgbuf->count = size >> PAGE_SHIFT;
sgbuf->pages = kvcalloc(sgbuf->count, sizeof(*sgbuf->pages), GFP_KERNEL);
if (!sgbuf->pages)
goto error;

for (i = 0; i < count; sgbuf->count++, i++) {
p = do_alloc_pages(dmab->dev.dev, PAGE_SIZE, &sgbuf->addrs[i], wc);
if (!p)
goto error;
sgbuf->pages[i] = virt_to_page(p);
pagep = sgbuf->pages;
chunk = size;
while (size > 0) {
chunk = min(size, chunk);
p = do_alloc_pages(dmab->dev.dev, chunk, &addr, wc);
if (!p) {
if (chunk <= PAGE_SIZE)
goto error;
chunk >>= 1;
chunk = PAGE_SIZE << get_order(chunk);
continue;
}

size -= chunk;
/* fill pages */
npages = chunk >> PAGE_SHIFT;
curp = virt_to_page(p);
while (npages--)
*pagep++ = curp++;
}

p = vmap(pages, count, VM_MAP, PAGE_KERNEL);
p = vmap(sgbuf->pages, sgbuf->count, VM_MAP, PAGE_KERNEL);
if (!p)
goto error;
dmab->private_data = sgbuf;
Expand Down

0 comments on commit accc799

Please sign in to comment.