linux/sound/core/pcm_memory.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Digital Audio (PCM) abstract layer
   4 *  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/vmalloc.h>
  13#include <linux/export.h>
  14#include <sound/core.h>
  15#include <sound/pcm.h>
  16#include <sound/info.h>
  17#include <sound/initval.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
  29
  30/*
  31 * try to allocate as the large pages as possible.
  32 * stores the resultant memory size in *res_size.
  33 *
  34 * the minimum size is snd_minimum_buffer.  it should be power of 2.
  35 */
  36static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size)
  37{
  38        struct snd_dma_buffer *dmab = &substream->dma_buffer;
  39        size_t orig_size = size;
  40        int err;
  41
  42        do {
  43                if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
  44                                               size, dmab)) < 0) {
  45                        if (err != -ENOMEM)
  46                                return err; /* fatal error */
  47                } else
  48                        return 0;
  49                size >>= 1;
  50        } while (size >= snd_minimum_buffer);
  51        dmab->bytes = 0; /* tell error */
  52        pr_warn("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
  53                substream->pcm->card->number, substream->pcm->device,
  54                substream->stream ? 'c' : 'p', substream->number,
  55                substream->pcm->name, orig_size);
  56        return 0;
  57}
  58
  59/*
  60 * release the preallocated buffer if not yet done.
  61 */
  62static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream)
  63{
  64        if (substream->dma_buffer.area == NULL)
  65                return;
  66        snd_dma_free_pages(&substream->dma_buffer);
  67        substream->dma_buffer.area = NULL;
  68}
  69
  70/**
  71 * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream.
  72 * @substream: the pcm substream instance
  73 *
  74 * Releases the pre-allocated buffer of the given substream.
  75 */
  76void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
  77{
  78        snd_pcm_lib_preallocate_dma_free(substream);
  79}
  80
  81/**
  82 * snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm
  83 * @pcm: the pcm instance
  84 *
  85 * Releases all the pre-allocated buffers on the given pcm.
  86 */
  87void snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
  88{
  89        struct snd_pcm_substream *substream;
  90        int stream;
  91
  92        for (stream = 0; stream < 2; stream++)
  93                for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
  94                        snd_pcm_lib_preallocate_free(substream);
  95}
  96EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
  97
  98#ifdef CONFIG_SND_VERBOSE_PROCFS
  99/*
 100 * read callback for prealloc proc file
 101 *
 102 * prints the current allocated size in kB.
 103 */
 104static void snd_pcm_lib_preallocate_proc_read(struct snd_info_entry *entry,
 105                                              struct snd_info_buffer *buffer)
 106{
 107        struct snd_pcm_substream *substream = entry->private_data;
 108        snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_buffer.bytes / 1024);
 109}
 110
 111/*
 112 * read callback for prealloc_max proc file
 113 *
 114 * prints the maximum allowed size in kB.
 115 */
 116static void snd_pcm_lib_preallocate_max_proc_read(struct snd_info_entry *entry,
 117                                                  struct snd_info_buffer *buffer)
 118{
 119        struct snd_pcm_substream *substream = entry->private_data;
 120        snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_max / 1024);
 121}
 122
 123/*
 124 * write callback for prealloc proc file
 125 *
 126 * accepts the preallocation size in kB.
 127 */
 128static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
 129                                               struct snd_info_buffer *buffer)
 130{
 131        struct snd_pcm_substream *substream = entry->private_data;
 132        char line[64], str[64];
 133        size_t size;
 134        struct snd_dma_buffer new_dmab;
 135
 136        if (substream->runtime) {
 137                buffer->error = -EBUSY;
 138                return;
 139        }
 140        if (!snd_info_get_line(buffer, line, sizeof(line))) {
 141                snd_info_get_str(str, line, sizeof(str));
 142                size = simple_strtoul(str, NULL, 10) * 1024;
 143                if ((size != 0 && size < 8192) || size > substream->dma_max) {
 144                        buffer->error = -EINVAL;
 145                        return;
 146                }
 147                if (substream->dma_buffer.bytes == size)
 148                        return;
 149                memset(&new_dmab, 0, sizeof(new_dmab));
 150                new_dmab.dev = substream->dma_buffer.dev;
 151                if (size > 0) {
 152                        if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
 153                                                substream->dma_buffer.dev.dev,
 154                                                size, &new_dmab) < 0) {
 155                                buffer->error = -ENOMEM;
 156                                return;
 157                        }
 158                        substream->buffer_bytes_max = size;
 159                } else {
 160                        substream->buffer_bytes_max = UINT_MAX;
 161                }
 162                if (substream->dma_buffer.area)
 163                        snd_dma_free_pages(&substream->dma_buffer);
 164                substream->dma_buffer = new_dmab;
 165        } else {
 166                buffer->error = -EINVAL;
 167        }
 168}
 169
 170static inline void preallocate_info_init(struct snd_pcm_substream *substream)
 171{
 172        struct snd_info_entry *entry;
 173
 174        entry = snd_info_create_card_entry(substream->pcm->card, "prealloc",
 175                                           substream->proc_root);
 176        if (entry) {
 177                snd_info_set_text_ops(entry, substream,
 178                                      snd_pcm_lib_preallocate_proc_read);
 179                entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
 180                entry->mode |= 0200;
 181        }
 182        entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max",
 183                                           substream->proc_root);
 184        if (entry)
 185                snd_info_set_text_ops(entry, substream,
 186                                      snd_pcm_lib_preallocate_max_proc_read);
 187}
 188
 189#else /* !CONFIG_SND_VERBOSE_PROCFS */
 190#define preallocate_info_init(s)
 191#endif /* CONFIG_SND_VERBOSE_PROCFS */
 192
 193/*
 194 * pre-allocate the buffer and create a proc file for the substream
 195 */
 196static void snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
 197                                          size_t size, size_t max)
 198{
 199
 200        if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
 201                preallocate_pcm_pages(substream, size);
 202
 203        if (substream->dma_buffer.bytes > 0)
 204                substream->buffer_bytes_max = substream->dma_buffer.bytes;
 205        substream->dma_max = max;
 206        preallocate_info_init(substream);
 207}
 208
 209
 210/**
 211 * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type
 212 * @substream: the pcm substream instance
 213 * @type: DMA type (SNDRV_DMA_TYPE_*)
 214 * @data: DMA type dependent data
 215 * @size: the requested pre-allocation size in bytes
 216 * @max: the max. allowed pre-allocation size
 217 *
 218 * Do pre-allocation for the given DMA buffer type.
 219 */
 220void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
 221                                  int type, struct device *data,
 222                                  size_t size, size_t max)
 223{
 224        substream->dma_buffer.dev.type = type;
 225        substream->dma_buffer.dev.dev = data;
 226        snd_pcm_lib_preallocate_pages1(substream, size, max);
 227}
 228EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
 229
 230/**
 231 * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams)
 232 * @pcm: the pcm instance
 233 * @type: DMA type (SNDRV_DMA_TYPE_*)
 234 * @data: DMA type dependent data
 235 * @size: the requested pre-allocation size in bytes
 236 * @max: the max. allowed pre-allocation size
 237 *
 238 * Do pre-allocation to all substreams of the given pcm for the
 239 * specified DMA type.
 240 */
 241void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
 242                                          int type, void *data,
 243                                          size_t size, size_t max)
 244{
 245        struct snd_pcm_substream *substream;
 246        int stream;
 247
 248        for (stream = 0; stream < 2; stream++)
 249                for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
 250                        snd_pcm_lib_preallocate_pages(substream, type, data, size, max);
 251}
 252EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
 253
 254#ifdef CONFIG_SND_DMA_SGBUF
 255/**
 256 * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
 257 * @substream: the pcm substream instance
 258 * @offset: the buffer offset
 259 *
 260 * Used as the page callback of PCM ops.
 261 *
 262 * Return: The page struct at the given buffer offset. %NULL on failure.
 263 */
 264struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
 265{
 266        struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 267
 268        unsigned int idx = offset >> PAGE_SHIFT;
 269        if (idx >= (unsigned int)sgbuf->pages)
 270                return NULL;
 271        return sgbuf->page_table[idx];
 272}
 273EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
 274#endif /* CONFIG_SND_DMA_SGBUF */
 275
 276/**
 277 * snd_pcm_lib_malloc_pages - allocate the DMA buffer
 278 * @substream: the substream to allocate the DMA buffer to
 279 * @size: the requested buffer size in bytes
 280 *
 281 * Allocates the DMA buffer on the BUS type given earlier to
 282 * snd_pcm_lib_preallocate_xxx_pages().
 283 *
 284 * Return: 1 if the buffer is changed, 0 if not changed, or a negative
 285 * code on failure.
 286 */
 287int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
 288{
 289        struct snd_pcm_runtime *runtime;
 290        struct snd_dma_buffer *dmab = NULL;
 291
 292        if (PCM_RUNTIME_CHECK(substream))
 293                return -EINVAL;
 294        if (snd_BUG_ON(substream->dma_buffer.dev.type ==
 295                       SNDRV_DMA_TYPE_UNKNOWN))
 296                return -EINVAL;
 297        runtime = substream->runtime;
 298
 299        if (runtime->dma_buffer_p) {
 300                /* perphaps, we might free the large DMA memory region
 301                   to save some space here, but the actual solution
 302                   costs us less time */
 303                if (runtime->dma_buffer_p->bytes >= size) {
 304                        runtime->dma_bytes = size;
 305                        return 0;       /* ok, do not change */
 306                }
 307                snd_pcm_lib_free_pages(substream);
 308        }
 309        if (substream->dma_buffer.area != NULL &&
 310            substream->dma_buffer.bytes >= size) {
 311                dmab = &substream->dma_buffer; /* use the pre-allocated buffer */
 312        } else {
 313                dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
 314                if (! dmab)
 315                        return -ENOMEM;
 316                dmab->dev = substream->dma_buffer.dev;
 317                if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
 318                                        substream->dma_buffer.dev.dev,
 319                                        size, dmab) < 0) {
 320                        kfree(dmab);
 321                        return -ENOMEM;
 322                }
 323        }
 324        snd_pcm_set_runtime_buffer(substream, dmab);
 325        runtime->dma_bytes = size;
 326        return 1;                       /* area was changed */
 327}
 328EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
 329
 330/**
 331 * snd_pcm_lib_free_pages - release the allocated DMA buffer.
 332 * @substream: the substream to release the DMA buffer
 333 *
 334 * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().
 335 *
 336 * Return: Zero if successful, or a negative error code on failure.
 337 */
 338int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
 339{
 340        struct snd_pcm_runtime *runtime;
 341
 342        if (PCM_RUNTIME_CHECK(substream))
 343                return -EINVAL;
 344        runtime = substream->runtime;
 345        if (runtime->dma_area == NULL)
 346                return 0;
 347        if (runtime->dma_buffer_p != &substream->dma_buffer) {
 348                /* it's a newly allocated buffer.  release it now. */
 349                snd_dma_free_pages(runtime->dma_buffer_p);
 350                kfree(runtime->dma_buffer_p);
 351        }
 352        snd_pcm_set_runtime_buffer(substream, NULL);
 353        return 0;
 354}
 355EXPORT_SYMBOL(snd_pcm_lib_free_pages);
 356
 357int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
 358                                      size_t size, gfp_t gfp_flags)
 359{
 360        struct snd_pcm_runtime *runtime;
 361
 362        if (PCM_RUNTIME_CHECK(substream))
 363                return -EINVAL;
 364        runtime = substream->runtime;
 365        if (runtime->dma_area) {
 366                if (runtime->dma_bytes >= size)
 367                        return 0; /* already large enough */
 368                vfree(runtime->dma_area);
 369        }
 370        runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
 371        if (!runtime->dma_area)
 372                return -ENOMEM;
 373        runtime->dma_bytes = size;
 374        return 1;
 375}
 376EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer);
 377
 378/**
 379 * snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer
 380 * @substream: the substream with a buffer allocated by
 381 *      snd_pcm_lib_alloc_vmalloc_buffer()
 382 *
 383 * Return: Zero if successful, or a negative error code on failure.
 384 */
 385int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream)
 386{
 387        struct snd_pcm_runtime *runtime;
 388
 389        if (PCM_RUNTIME_CHECK(substream))
 390                return -EINVAL;
 391        runtime = substream->runtime;
 392        vfree(runtime->dma_area);
 393        runtime->dma_area = NULL;
 394        return 0;
 395}
 396EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer);
 397
 398/**
 399 * snd_pcm_lib_get_vmalloc_page - map vmalloc buffer offset to page struct
 400 * @substream: the substream with a buffer allocated by
 401 *      snd_pcm_lib_alloc_vmalloc_buffer()
 402 * @offset: offset in the buffer
 403 *
 404 * This function is to be used as the page callback in the PCM ops.
 405 *
 406 * Return: The page struct, or %NULL on failure.
 407 */
 408struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
 409                                          unsigned long offset)
 410{
 411        return vmalloc_to_page(substream->runtime->dma_area + offset);
 412}
 413EXPORT_SYMBOL(snd_pcm_lib_get_vmalloc_page);
 414