Skip to content

Commit d4cfb30

Browse files
committed
ALSA: pcm: Set per-card upper limit of PCM buffer allocations
Currently, the available buffer allocation size for a PCM stream depends on the preallocated size; when a buffer has been preallocated, the max buffer size is set to that size, so that application won't re-allocate too much memory. OTOH, when no preallocation is done, each substream may allocate arbitrary size of buffers as long as snd_pcm_hardware.buffer_bytes_max allows -- which can be quite high, HD-audio sets 1GB there. It means that the system may consume a high amount of pages for PCM buffers, and they are pinned and never swapped out. This can lead to OOM easily. For avoiding such a situation, this patch adds the upper limit per card. Each snd_pcm_lib_malloc_pages() and _free_pages() calls are tracked and it will return an error if the total amount of buffers goes over the defined upper limit. The default value is set to 32MB, which should be really large enough for usual operations. If larger buffers are needed for any specific usage, it can be adjusted (also dynamically) via snd_pcm.max_alloc_per_card option. Setting zero there means no chceck is performed, and again, unlimited amount of buffers are allowed. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 9d0af44 commit d4cfb30

File tree

3 files changed

+55
-18
lines changed

3 files changed

+55
-18
lines changed

include/sound/core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ struct snd_card {
120120
int sync_irq; /* assigned irq, used for PCM sync */
121121
wait_queue_head_t remove_sleep;
122122

123+
size_t total_pcm_alloc_bytes; /* total amount of allocated buffers */
124+
struct mutex memory_mutex; /* protection for the above */
125+
123126
#ifdef CONFIG_PM
124127
unsigned int power_state; /* power state */
125128
wait_queue_head_t power_sleep;

sound/core/init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
211211
INIT_LIST_HEAD(&card->ctl_files);
212212
spin_lock_init(&card->files_lock);
213213
INIT_LIST_HEAD(&card->files_list);
214+
mutex_init(&card->memory_mutex);
214215
#ifdef CONFIG_PM
215216
init_waitqueue_head(&card->power_sleep);
216217
#endif

sound/core/pcm_memory.c

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,38 @@ MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA m
2727

2828
static const size_t snd_minimum_buffer = 16384;
2929

30+
static unsigned long max_alloc_per_card = 32UL * 1024UL * 1024UL;
31+
module_param(max_alloc_per_card, ulong, 0644);
32+
MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");
33+
34+
static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
35+
size_t size, struct snd_dma_buffer *dmab)
36+
{
37+
int err;
38+
39+
if (max_alloc_per_card &&
40+
card->total_pcm_alloc_bytes + size > max_alloc_per_card)
41+
return -ENOMEM;
42+
err = snd_dma_alloc_pages(type, dev, size, dmab);
43+
if (!err) {
44+
mutex_lock(&card->memory_mutex);
45+
card->total_pcm_alloc_bytes += dmab->bytes;
46+
mutex_unlock(&card->memory_mutex);
47+
}
48+
return err;
49+
}
50+
51+
static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab)
52+
{
53+
if (!dmab->area)
54+
return;
55+
mutex_lock(&card->memory_mutex);
56+
WARN_ON(card->total_pcm_alloc_bytes < dmab->bytes);
57+
card->total_pcm_alloc_bytes -= dmab->bytes;
58+
mutex_unlock(&card->memory_mutex);
59+
snd_dma_free_pages(dmab);
60+
dmab->area = NULL;
61+
}
3062

3163
/*
3264
* try to allocate as the large pages as possible.
@@ -37,16 +69,15 @@ static const size_t snd_minimum_buffer = 16384;
3769
static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size)
3870
{
3971
struct snd_dma_buffer *dmab = &substream->dma_buffer;
72+
struct snd_card *card = substream->pcm->card;
4073
size_t orig_size = size;
4174
int err;
4275

4376
do {
44-
if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
45-
size, dmab)) < 0) {
46-
if (err != -ENOMEM)
47-
return err; /* fatal error */
48-
} else
49-
return 0;
77+
err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev,
78+
size, dmab);
79+
if (err != -ENOMEM)
80+
return err;
5081
size >>= 1;
5182
} while (size >= snd_minimum_buffer);
5283
dmab->bytes = 0; /* tell error */
@@ -62,10 +93,7 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz
6293
*/
6394
static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream)
6495
{
65-
if (substream->dma_buffer.area == NULL)
66-
return;
67-
snd_dma_free_pages(&substream->dma_buffer);
68-
substream->dma_buffer.area = NULL;
96+
do_free_pages(substream->pcm->card, &substream->dma_buffer);
6997
}
7098

7199
/**
@@ -130,6 +158,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
130158
struct snd_info_buffer *buffer)
131159
{
132160
struct snd_pcm_substream *substream = entry->private_data;
161+
struct snd_card *card = substream->pcm->card;
133162
char line[64], str[64];
134163
size_t size;
135164
struct snd_dma_buffer new_dmab;
@@ -150,9 +179,10 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
150179
memset(&new_dmab, 0, sizeof(new_dmab));
151180
new_dmab.dev = substream->dma_buffer.dev;
152181
if (size > 0) {
153-
if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
154-
substream->dma_buffer.dev.dev,
155-
size, &new_dmab) < 0) {
182+
if (do_alloc_pages(card,
183+
substream->dma_buffer.dev.type,
184+
substream->dma_buffer.dev.dev,
185+
size, &new_dmab) < 0) {
156186
buffer->error = -ENOMEM;
157187
return;
158188
}
@@ -161,7 +191,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
161191
substream->buffer_bytes_max = UINT_MAX;
162192
}
163193
if (substream->dma_buffer.area)
164-
snd_dma_free_pages(&substream->dma_buffer);
194+
do_free_pages(card, &substream->dma_buffer);
165195
substream->dma_buffer = new_dmab;
166196
} else {
167197
buffer->error = -EINVAL;
@@ -346,6 +376,7 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne
346376
*/
347377
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
348378
{
379+
struct snd_card *card = substream->pcm->card;
349380
struct snd_pcm_runtime *runtime;
350381
struct snd_dma_buffer *dmab = NULL;
351382

@@ -374,9 +405,10 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
374405
if (! dmab)
375406
return -ENOMEM;
376407
dmab->dev = substream->dma_buffer.dev;
377-
if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
378-
substream->dma_buffer.dev.dev,
379-
size, dmab) < 0) {
408+
if (do_alloc_pages(card,
409+
substream->dma_buffer.dev.type,
410+
substream->dma_buffer.dev.dev,
411+
size, dmab) < 0) {
380412
kfree(dmab);
381413
return -ENOMEM;
382414
}
@@ -397,6 +429,7 @@ EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
397429
*/
398430
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
399431
{
432+
struct snd_card *card = substream->pcm->card;
400433
struct snd_pcm_runtime *runtime;
401434

402435
if (PCM_RUNTIME_CHECK(substream))
@@ -406,7 +439,7 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
406439
return 0;
407440
if (runtime->dma_buffer_p != &substream->dma_buffer) {
408441
/* it's a newly allocated buffer. release it now. */
409-
snd_dma_free_pages(runtime->dma_buffer_p);
442+
do_free_pages(card, runtime->dma_buffer_p);
410443
kfree(runtime->dma_buffer_p);
411444
}
412445
snd_pcm_set_runtime_buffer(substream, NULL);

0 commit comments

Comments
 (0)