brintos

brintos / linux-shallow public Read only

0
0
Text · 15.1 KiB · ea3941f Raw
502 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Digital Audio (PCM) abstract layer4 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>5 */6 7#include <linux/io.h>8#include <linux/time.h>9#include <linux/init.h>10#include <linux/slab.h>11#include <linux/moduleparam.h>12#include <linux/export.h>13#include <sound/core.h>14#include <sound/pcm.h>15#include <sound/info.h>16#include <sound/initval.h>17#include "pcm_local.h"18 19static int preallocate_dma = 1;20module_param(preallocate_dma, int, 0444);21MODULE_PARM_DESC(preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized.");22 23static int maximum_substreams = 4;24module_param(maximum_substreams, int, 0444);25MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory.");26 27static const size_t snd_minimum_buffer = 16384;28 29static unsigned long max_alloc_per_card = 32UL * 1024UL * 1024UL;30module_param(max_alloc_per_card, ulong, 0644);31MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");32 33static void __update_allocated_size(struct snd_card *card, ssize_t bytes)34{35	card->total_pcm_alloc_bytes += bytes;36}37 38static void update_allocated_size(struct snd_card *card, ssize_t bytes)39{40	guard(mutex)(&card->memory_mutex);41	__update_allocated_size(card, bytes);42}43 44static void decrease_allocated_size(struct snd_card *card, size_t bytes)45{46	guard(mutex)(&card->memory_mutex);47	WARN_ON(card->total_pcm_alloc_bytes < bytes);48	__update_allocated_size(card, -(ssize_t)bytes);49}50 51static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,52			  int str, size_t size, struct snd_dma_buffer *dmab)53{54	enum dma_data_direction dir;55	int err;56 57	/* check and reserve the requested size */58	scoped_guard(mutex, &card->memory_mutex) {59		if (max_alloc_per_card &&60		    card->total_pcm_alloc_bytes + size > max_alloc_per_card)61			return -ENOMEM;62		__update_allocated_size(card, size);63	}64 65	if (str == SNDRV_PCM_STREAM_PLAYBACK)66		dir = DMA_TO_DEVICE;67	else68		dir = DMA_FROM_DEVICE;69	err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);70	if (!err) {71		/* the actual allocation size might be bigger than requested,72		 * and we need to correct the account73		 */74		if (dmab->bytes != size)75			update_allocated_size(card, dmab->bytes - size);76	} else {77		/* take back on allocation failure */78		decrease_allocated_size(card, size);79	}80	return err;81}82 83static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab)84{85	if (!dmab->area)86		return;87	decrease_allocated_size(card, dmab->bytes);88	snd_dma_free_pages(dmab);89	dmab->area = NULL;90}91 92/*93 * try to allocate as the large pages as possible.94 * stores the resultant memory size in *res_size.95 *96 * the minimum size is snd_minimum_buffer.  it should be power of 2.97 */98static int preallocate_pcm_pages(struct snd_pcm_substream *substream,99				 size_t size, bool no_fallback)100{101	struct snd_dma_buffer *dmab = &substream->dma_buffer;102	struct snd_card *card = substream->pcm->card;103	size_t orig_size = size;104	int err;105 106	do {107		err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev,108				     substream->stream, size, dmab);109		if (err != -ENOMEM)110			return err;111		if (no_fallback)112			break;113		size >>= 1;114	} while (size >= snd_minimum_buffer);115	dmab->bytes = 0; /* tell error */116	pr_warn("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",117		substream->pcm->card->number, substream->pcm->device,118		substream->stream ? 'c' : 'p', substream->number,119		substream->pcm->name, orig_size);120	return -ENOMEM;121}122 123/**124 * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream.125 * @substream: the pcm substream instance126 *127 * Releases the pre-allocated buffer of the given substream.128 */129void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)130{131	do_free_pages(substream->pcm->card, &substream->dma_buffer);132}133 134/**135 * snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm136 * @pcm: the pcm instance137 *138 * Releases all the pre-allocated buffers on the given pcm.139 */140void snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)141{142	struct snd_pcm_substream *substream;143	int stream;144 145	for_each_pcm_substream(pcm, stream, substream)146		snd_pcm_lib_preallocate_free(substream);147}148EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);149 150#ifdef CONFIG_SND_VERBOSE_PROCFS151/*152 * read callback for prealloc proc file153 *154 * prints the current allocated size in kB.155 */156static void snd_pcm_lib_preallocate_proc_read(struct snd_info_entry *entry,157					      struct snd_info_buffer *buffer)158{159	struct snd_pcm_substream *substream = entry->private_data;160	snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_buffer.bytes / 1024);161}162 163/*164 * read callback for prealloc_max proc file165 *166 * prints the maximum allowed size in kB.167 */168static void snd_pcm_lib_preallocate_max_proc_read(struct snd_info_entry *entry,169						  struct snd_info_buffer *buffer)170{171	struct snd_pcm_substream *substream = entry->private_data;172	snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_max / 1024);173}174 175/*176 * write callback for prealloc proc file177 *178 * accepts the preallocation size in kB.179 */180static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,181					       struct snd_info_buffer *buffer)182{183	struct snd_pcm_substream *substream = entry->private_data;184	struct snd_card *card = substream->pcm->card;185	char line[64], str[64];186	unsigned long size;187	struct snd_dma_buffer new_dmab;188 189	guard(mutex)(&substream->pcm->open_mutex);190	if (substream->runtime) {191		buffer->error = -EBUSY;192		return;193	}194	if (!snd_info_get_line(buffer, line, sizeof(line))) {195		snd_info_get_str(str, line, sizeof(str));196		buffer->error = kstrtoul(str, 10, &size);197		if (buffer->error != 0)198			return;199		size *= 1024;200		if ((size != 0 && size < 8192) || size > substream->dma_max) {201			buffer->error = -EINVAL;202			return;203		}204		if (substream->dma_buffer.bytes == size)205			return;206		memset(&new_dmab, 0, sizeof(new_dmab));207		new_dmab.dev = substream->dma_buffer.dev;208		if (size > 0) {209			if (do_alloc_pages(card,210					   substream->dma_buffer.dev.type,211					   substream->dma_buffer.dev.dev,212					   substream->stream,213					   size, &new_dmab) < 0) {214				buffer->error = -ENOMEM;215				pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %lu\n",216					 substream->pcm->card->number, substream->pcm->device,217					 substream->stream ? 'c' : 'p', substream->number,218					 substream->pcm->name, size);219				return;220			}221			substream->buffer_bytes_max = size;222		} else {223			substream->buffer_bytes_max = UINT_MAX;224		}225		if (substream->dma_buffer.area)226			do_free_pages(card, &substream->dma_buffer);227		substream->dma_buffer = new_dmab;228	} else {229		buffer->error = -EINVAL;230	}231}232 233static inline void preallocate_info_init(struct snd_pcm_substream *substream)234{235	struct snd_info_entry *entry;236 237	entry = snd_info_create_card_entry(substream->pcm->card, "prealloc",238					   substream->proc_root);239	if (entry) {240		snd_info_set_text_ops(entry, substream,241				      snd_pcm_lib_preallocate_proc_read);242		entry->c.text.write = snd_pcm_lib_preallocate_proc_write;243		entry->mode |= 0200;244	}245	entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max",246					   substream->proc_root);247	if (entry)248		snd_info_set_text_ops(entry, substream,249				      snd_pcm_lib_preallocate_max_proc_read);250}251 252#else /* !CONFIG_SND_VERBOSE_PROCFS */253static inline void preallocate_info_init(struct snd_pcm_substream *substream)254{255}256#endif /* CONFIG_SND_VERBOSE_PROCFS */257 258/*259 * pre-allocate the buffer and create a proc file for the substream260 */261static int preallocate_pages(struct snd_pcm_substream *substream,262			      int type, struct device *data,263			      size_t size, size_t max, bool managed)264{265	int err;266 267	if (snd_BUG_ON(substream->dma_buffer.dev.type))268		return -EINVAL;269 270	substream->dma_buffer.dev.type = type;271	substream->dma_buffer.dev.dev = data;272 273	if (size > 0) {274		if (!max) {275			/* no fallback, only also inform -ENOMEM */276			err = preallocate_pcm_pages(substream, size, true);277			if (err < 0)278				return err;279		} else if (preallocate_dma &&280			   substream->number < maximum_substreams) {281			err = preallocate_pcm_pages(substream, size, false);282			if (err < 0 && err != -ENOMEM)283				return err;284		}285	}286 287	if (substream->dma_buffer.bytes > 0)288		substream->buffer_bytes_max = substream->dma_buffer.bytes;289	substream->dma_max = max;290	if (max > 0)291		preallocate_info_init(substream);292	if (managed)293		substream->managed_buffer_alloc = 1;294	return 0;295}296 297static int preallocate_pages_for_all(struct snd_pcm *pcm, int type,298				      void *data, size_t size, size_t max,299				      bool managed)300{301	struct snd_pcm_substream *substream;302	int stream, err;303 304	for_each_pcm_substream(pcm, stream, substream) {305		err = preallocate_pages(substream, type, data, size, max, managed);306		if (err < 0)307			return err;308	}309	return 0;310}311 312/**313 * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type314 * @substream: the pcm substream instance315 * @type: DMA type (SNDRV_DMA_TYPE_*)316 * @data: DMA type dependent data317 * @size: the requested pre-allocation size in bytes318 * @max: the max. allowed pre-allocation size319 *320 * Do pre-allocation for the given DMA buffer type.321 */322void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,323				  int type, struct device *data,324				  size_t size, size_t max)325{326	preallocate_pages(substream, type, data, size, max, false);327}328EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);329 330/**331 * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams)332 * @pcm: the pcm instance333 * @type: DMA type (SNDRV_DMA_TYPE_*)334 * @data: DMA type dependent data335 * @size: the requested pre-allocation size in bytes336 * @max: the max. allowed pre-allocation size337 *338 * Do pre-allocation to all substreams of the given pcm for the339 * specified DMA type.340 */341void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,342					  int type, void *data,343					  size_t size, size_t max)344{345	preallocate_pages_for_all(pcm, type, data, size, max, false);346}347EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);348 349/**350 * snd_pcm_set_managed_buffer - set up buffer management for a substream351 * @substream: the pcm substream instance352 * @type: DMA type (SNDRV_DMA_TYPE_*)353 * @data: DMA type dependent data354 * @size: the requested pre-allocation size in bytes355 * @max: the max. allowed pre-allocation size356 *357 * Do pre-allocation for the given DMA buffer type, and set the managed358 * buffer allocation mode to the given substream.359 * In this mode, PCM core will allocate a buffer automatically before PCM360 * hw_params ops call, and release the buffer after PCM hw_free ops call361 * as well, so that the driver doesn't need to invoke the allocation and362 * the release explicitly in its callback.363 * When a buffer is actually allocated before the PCM hw_params call, it364 * turns on the runtime buffer_changed flag for drivers changing their h/w365 * parameters accordingly.366 *367 * When @size is non-zero and @max is zero, this tries to allocate for only368 * the exact buffer size without fallback, and may return -ENOMEM.369 * Otherwise, the function tries to allocate smaller chunks if the allocation370 * fails.  This is the behavior of snd_pcm_set_fixed_buffer().371 *372 * When both @size and @max are zero, the function only sets up the buffer373 * for later dynamic allocations. It's used typically for buffers with374 * SNDRV_DMA_TYPE_VMALLOC type.375 *376 * Upon successful buffer allocation and setup, the function returns 0.377 *378 * Return: zero if successful, or a negative error code379 */380int snd_pcm_set_managed_buffer(struct snd_pcm_substream *substream, int type,381				struct device *data, size_t size, size_t max)382{383	return preallocate_pages(substream, type, data, size, max, true);384}385EXPORT_SYMBOL(snd_pcm_set_managed_buffer);386 387/**388 * snd_pcm_set_managed_buffer_all - set up buffer management for all substreams389 *	for all substreams390 * @pcm: the pcm instance391 * @type: DMA type (SNDRV_DMA_TYPE_*)392 * @data: DMA type dependent data393 * @size: the requested pre-allocation size in bytes394 * @max: the max. allowed pre-allocation size395 *396 * Do pre-allocation to all substreams of the given pcm for the specified DMA397 * type and size, and set the managed_buffer_alloc flag to each substream.398 *399 * Return: zero if successful, or a negative error code400 */401int snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type,402				   struct device *data,403				   size_t size, size_t max)404{405	return preallocate_pages_for_all(pcm, type, data, size, max, true);406}407EXPORT_SYMBOL(snd_pcm_set_managed_buffer_all);408 409/**410 * snd_pcm_lib_malloc_pages - allocate the DMA buffer411 * @substream: the substream to allocate the DMA buffer to412 * @size: the requested buffer size in bytes413 *414 * Allocates the DMA buffer on the BUS type given earlier to415 * snd_pcm_lib_preallocate_xxx_pages().416 *417 * Return: 1 if the buffer is changed, 0 if not changed, or a negative418 * code on failure.419 */420int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)421{422	struct snd_card *card;423	struct snd_pcm_runtime *runtime;424	struct snd_dma_buffer *dmab = NULL;425 426	if (PCM_RUNTIME_CHECK(substream))427		return -EINVAL;428	if (snd_BUG_ON(substream->dma_buffer.dev.type ==429		       SNDRV_DMA_TYPE_UNKNOWN))430		return -EINVAL;431	runtime = substream->runtime;432	card = substream->pcm->card;433 434	if (runtime->dma_buffer_p) {435		/* perphaps, we might free the large DMA memory region436		   to save some space here, but the actual solution437		   costs us less time */438		if (runtime->dma_buffer_p->bytes >= size) {439			runtime->dma_bytes = size;440			return 0;	/* ok, do not change */441		}442		snd_pcm_lib_free_pages(substream);443	}444	if (substream->dma_buffer.area != NULL &&445	    substream->dma_buffer.bytes >= size) {446		dmab = &substream->dma_buffer; /* use the pre-allocated buffer */447	} else {448		/* dma_max=0 means the fixed size preallocation */449		if (substream->dma_buffer.area && !substream->dma_max)450			return -ENOMEM;451		dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);452		if (! dmab)453			return -ENOMEM;454		dmab->dev = substream->dma_buffer.dev;455		if (do_alloc_pages(card,456				   substream->dma_buffer.dev.type,457				   substream->dma_buffer.dev.dev,458				   substream->stream,459				   size, dmab) < 0) {460			kfree(dmab);461			pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",462				 substream->pcm->card->number, substream->pcm->device,463				 substream->stream ? 'c' : 'p', substream->number,464				 substream->pcm->name, size);465			return -ENOMEM;466		}467	}468	snd_pcm_set_runtime_buffer(substream, dmab);469	runtime->dma_bytes = size;470	return 1;			/* area was changed */471}472EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);473 474/**475 * snd_pcm_lib_free_pages - release the allocated DMA buffer.476 * @substream: the substream to release the DMA buffer477 *478 * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().479 *480 * Return: Zero if successful, or a negative error code on failure.481 */482int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)483{484	struct snd_pcm_runtime *runtime;485 486	if (PCM_RUNTIME_CHECK(substream))487		return -EINVAL;488	runtime = substream->runtime;489	if (runtime->dma_area == NULL)490		return 0;491	if (runtime->dma_buffer_p != &substream->dma_buffer) {492		struct snd_card *card = substream->pcm->card;493 494		/* it's a newly allocated buffer.  release it now. */495		do_free_pages(card, runtime->dma_buffer_p);496		kfree(runtime->dma_buffer_p);497	}498	snd_pcm_set_runtime_buffer(substream, NULL);499	return 0;500}501EXPORT_SYMBOL(snd_pcm_lib_free_pages);502