Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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/20200120124423.11862-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>

+55 -18
+3
include/sound/core.h
··· 120 120 int sync_irq; /* assigned irq, used for PCM sync */ 121 121 wait_queue_head_t remove_sleep; 122 122 123 + size_t total_pcm_alloc_bytes; /* total amount of allocated buffers */ 124 + struct mutex memory_mutex; /* protection for the above */ 125 + 123 126 #ifdef CONFIG_PM 124 127 unsigned int power_state; /* power state */ 125 128 wait_queue_head_t power_sleep;
+1
sound/core/init.c
··· 211 211 INIT_LIST_HEAD(&card->ctl_files); 212 212 spin_lock_init(&card->files_lock); 213 213 INIT_LIST_HEAD(&card->files_list); 214 + mutex_init(&card->memory_mutex); 214 215 #ifdef CONFIG_PM 215 216 init_waitqueue_head(&card->power_sleep); 216 217 #endif
+51 -18
sound/core/pcm_memory.c
··· 27 27 28 28 static const size_t snd_minimum_buffer = 16384; 29 29 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 + } 30 62 31 63 /* 32 64 * try to allocate as the large pages as possible. ··· 69 37 static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size) 70 38 { 71 39 struct snd_dma_buffer *dmab = &substream->dma_buffer; 40 + struct snd_card *card = substream->pcm->card; 72 41 size_t orig_size = size; 73 42 int err; 74 43 75 44 do { 76 - if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, 77 - size, dmab)) < 0) { 78 - if (err != -ENOMEM) 79 - return err; /* fatal error */ 80 - } else 81 - return 0; 45 + err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev, 46 + size, dmab); 47 + if (err != -ENOMEM) 48 + return err; 82 49 size >>= 1; 83 50 } while (size >= snd_minimum_buffer); 84 51 dmab->bytes = 0; /* tell error */ ··· 93 62 */ 94 63 static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream) 95 64 { 96 - if (substream->dma_buffer.area == NULL) 97 - return; 98 - snd_dma_free_pages(&substream->dma_buffer); 99 - substream->dma_buffer.area = NULL; 65 + do_free_pages(substream->pcm->card, &substream->dma_buffer); 100 66 } 101 67 102 68 /** ··· 158 130 struct snd_info_buffer *buffer) 159 131 { 160 132 struct snd_pcm_substream *substream = entry->private_data; 133 + struct snd_card *card = substream->pcm->card; 161 134 char line[64], str[64]; 162 135 size_t size; 163 136 struct snd_dma_buffer new_dmab; ··· 179 150 memset(&new_dmab, 0, sizeof(new_dmab)); 180 151 new_dmab.dev = substream->dma_buffer.dev; 181 152 if (size > 0) { 182 - if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, 183 - substream->dma_buffer.dev.dev, 184 - size, &new_dmab) < 0) { 153 + if (do_alloc_pages(card, 154 + substream->dma_buffer.dev.type, 155 + substream->dma_buffer.dev.dev, 156 + size, &new_dmab) < 0) { 185 157 buffer->error = -ENOMEM; 186 158 return; 187 159 } ··· 191 161 substream->buffer_bytes_max = UINT_MAX; 192 162 } 193 163 if (substream->dma_buffer.area) 194 - snd_dma_free_pages(&substream->dma_buffer); 164 + do_free_pages(card, &substream->dma_buffer); 195 165 substream->dma_buffer = new_dmab; 196 166 } else { 197 167 buffer->error = -EINVAL; ··· 376 346 */ 377 347 int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) 378 348 { 349 + struct snd_card *card = substream->pcm->card; 379 350 struct snd_pcm_runtime *runtime; 380 351 struct snd_dma_buffer *dmab = NULL; 381 352 ··· 405 374 if (! dmab) 406 375 return -ENOMEM; 407 376 dmab->dev = substream->dma_buffer.dev; 408 - if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, 409 - substream->dma_buffer.dev.dev, 410 - size, dmab) < 0) { 377 + if (do_alloc_pages(card, 378 + substream->dma_buffer.dev.type, 379 + substream->dma_buffer.dev.dev, 380 + size, dmab) < 0) { 411 381 kfree(dmab); 412 382 return -ENOMEM; 413 383 } ··· 429 397 */ 430 398 int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) 431 399 { 400 + struct snd_card *card = substream->pcm->card; 432 401 struct snd_pcm_runtime *runtime; 433 402 434 403 if (PCM_RUNTIME_CHECK(substream)) ··· 439 406 return 0; 440 407 if (runtime->dma_buffer_p != &substream->dma_buffer) { 441 408 /* it's a newly allocated buffer. release it now. */ 442 - snd_dma_free_pages(runtime->dma_buffer_p); 409 + do_free_pages(card, runtime->dma_buffer_p); 443 410 kfree(runtime->dma_buffer_p); 444 411 } 445 412 snd_pcm_set_runtime_buffer(substream, NULL);