linux/sound/core/memalloc.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   3 *                   Takashi Iwai <tiwai@suse.de>
   4 * 
   5 *  Generic memory allocators
   6 *
   7 *
   8 *   This program is free software; you can redistribute it and/or modify
   9 *   it under the terms of the GNU General Public License as published by
  10 *   the Free Software Foundation; either version 2 of the License, or
  11 *   (at your option) any later version.
  12 *
  13 *   This program is distributed in the hope that it will be useful,
  14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 *   GNU General Public License for more details.
  17 *
  18 *   You should have received a copy of the GNU General Public License
  19 *   along with this program; if not, write to the Free Software
  20 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  21 *
  22 */
  23
  24#include <linux/module.h>
  25#include <linux/proc_fs.h>
  26#include <linux/init.h>
  27#include <linux/pci.h>
  28#include <linux/slab.h>
  29#include <linux/mm.h>
  30#include <linux/seq_file.h>
  31#include <asm/uaccess.h>
  32#include <linux/dma-mapping.h>
  33#include <linux/moduleparam.h>
  34#include <linux/mutex.h>
  35#include <sound/memalloc.h>
  36
  37
  38MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>");
  39MODULE_DESCRIPTION("Memory allocator for ALSA system.");
  40MODULE_LICENSE("GPL");
  41
  42
  43/*
  44 */
  45
  46static DEFINE_MUTEX(list_mutex);
  47static LIST_HEAD(mem_list_head);
  48
  49/* buffer preservation list */
  50struct snd_mem_list {
  51        struct snd_dma_buffer buffer;
  52        unsigned int id;
  53        struct list_head list;
  54};
  55
  56/* id for pre-allocated buffers */
  57#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
  58
  59/*
  60 *
  61 *  Generic memory allocators
  62 *
  63 */
  64
  65static long snd_allocated_pages; /* holding the number of allocated pages */
  66
  67static inline void inc_snd_pages(int order)
  68{
  69        snd_allocated_pages += 1 << order;
  70}
  71
  72static inline void dec_snd_pages(int order)
  73{
  74        snd_allocated_pages -= 1 << order;
  75}
  76
  77/**
  78 * snd_malloc_pages - allocate pages with the given size
  79 * @size: the size to allocate in bytes
  80 * @gfp_flags: the allocation conditions, GFP_XXX
  81 *
  82 * Allocates the physically contiguous pages with the given size.
  83 *
  84 * Returns the pointer of the buffer, or NULL if no enoguh memory.
  85 */
  86void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
  87{
  88        int pg;
  89        void *res;
  90
  91        if (WARN_ON(!size))
  92                return NULL;
  93        if (WARN_ON(!gfp_flags))
  94                return NULL;
  95        gfp_flags |= __GFP_COMP;        /* compound page lets parts be mapped */
  96        pg = get_order(size);
  97        if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL)
  98                inc_snd_pages(pg);
  99        return res;
 100}
 101
 102/**
 103 * snd_free_pages - release the pages
 104 * @ptr: the buffer pointer to release
 105 * @size: the allocated buffer size
 106 *
 107 * Releases the buffer allocated via snd_malloc_pages().
 108 */
 109void snd_free_pages(void *ptr, size_t size)
 110{
 111        int pg;
 112
 113        if (ptr == NULL)
 114                return;
 115        pg = get_order(size);
 116        dec_snd_pages(pg);
 117        free_pages((unsigned long) ptr, pg);
 118}
 119
 120/*
 121 *
 122 *  Bus-specific memory allocators
 123 *
 124 */
 125
 126#ifdef CONFIG_HAS_DMA
 127/* allocate the coherent DMA pages */
 128static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
 129{
 130        int pg;
 131        void *res;
 132        gfp_t gfp_flags;
 133
 134        if (WARN_ON(!dma))
 135                return NULL;
 136        pg = get_order(size);
 137        gfp_flags = GFP_KERNEL
 138                | __GFP_COMP    /* compound page lets parts be mapped */
 139                | __GFP_NORETRY /* don't trigger OOM-killer */
 140                | __GFP_NOWARN; /* no stack trace print - this call is non-critical */
 141        res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
 142        if (res != NULL)
 143                inc_snd_pages(pg);
 144
 145        return res;
 146}
 147
 148/* free the coherent DMA pages */
 149static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
 150                               dma_addr_t dma)
 151{
 152        int pg;
 153
 154        if (ptr == NULL)
 155                return;
 156        pg = get_order(size);
 157        dec_snd_pages(pg);
 158        dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
 159}
 160#endif /* CONFIG_HAS_DMA */
 161
 162/*
 163 *
 164 *  ALSA generic memory management
 165 *
 166 */
 167
 168
 169/**
 170 * snd_dma_alloc_pages - allocate the buffer area according to the given type
 171 * @type: the DMA buffer type
 172 * @device: the device pointer
 173 * @size: the buffer size to allocate
 174 * @dmab: buffer allocation record to store the allocated data
 175 *
 176 * Calls the memory-allocator function for the corresponding
 177 * buffer type.
 178 * 
 179 * Returns zero if the buffer with the given size is allocated successfully,
 180 * other a negative value at error.
 181 */
 182int snd_dma_alloc_pages(int type, struct device *device, size_t size,
 183                        struct snd_dma_buffer *dmab)
 184{
 185        if (WARN_ON(!size))
 186                return -ENXIO;
 187        if (WARN_ON(!dmab))
 188                return -ENXIO;
 189
 190        dmab->dev.type = type;
 191        dmab->dev.dev = device;
 192        dmab->bytes = 0;
 193        switch (type) {
 194        case SNDRV_DMA_TYPE_CONTINUOUS:
 195                dmab->area = snd_malloc_pages(size,
 196                                        (__force gfp_t)(unsigned long)device);
 197                dmab->addr = 0;
 198                break;
 199#ifdef CONFIG_HAS_DMA
 200        case SNDRV_DMA_TYPE_DEV:
 201                dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
 202                break;
 203#endif
 204#ifdef CONFIG_SND_DMA_SGBUF
 205        case SNDRV_DMA_TYPE_DEV_SG:
 206                snd_malloc_sgbuf_pages(device, size, dmab, NULL);
 207                break;
 208#endif
 209        default:
 210                printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
 211                dmab->area = NULL;
 212                dmab->addr = 0;
 213                return -ENXIO;
 214        }
 215        if (! dmab->area)
 216                return -ENOMEM;
 217        dmab->bytes = size;
 218        return 0;
 219}
 220
 221/**
 222 * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
 223 * @type: the DMA buffer type
 224 * @device: the device pointer
 225 * @size: the buffer size to allocate
 226 * @dmab: buffer allocation record to store the allocated data
 227 *
 228 * Calls the memory-allocator function for the corresponding
 229 * buffer type.  When no space is left, this function reduces the size and
 230 * tries to allocate again.  The size actually allocated is stored in
 231 * res_size argument.
 232 * 
 233 * Returns zero if the buffer with the given size is allocated successfully,
 234 * other a negative value at error.
 235 */
 236int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
 237                                 struct snd_dma_buffer *dmab)
 238{
 239        int err;
 240
 241        while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
 242                size_t aligned_size;
 243                if (err != -ENOMEM)
 244                        return err;
 245                if (size <= PAGE_SIZE)
 246                        return -ENOMEM;
 247                aligned_size = PAGE_SIZE << get_order(size);
 248                if (size != aligned_size)
 249                        size = aligned_size;
 250                else
 251                        size >>= 1;
 252        }
 253        if (! dmab->area)
 254                return -ENOMEM;
 255        return 0;
 256}
 257
 258
 259/**
 260 * snd_dma_free_pages - release the allocated buffer
 261 * @dmab: the buffer allocation record to release
 262 *
 263 * Releases the allocated buffer via snd_dma_alloc_pages().
 264 */
 265void snd_dma_free_pages(struct snd_dma_buffer *dmab)
 266{
 267        switch (dmab->dev.type) {
 268        case SNDRV_DMA_TYPE_CONTINUOUS:
 269                snd_free_pages(dmab->area, dmab->bytes);
 270                break;
 271#ifdef CONFIG_HAS_DMA
 272        case SNDRV_DMA_TYPE_DEV:
 273                snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
 274                break;
 275#endif
 276#ifdef CONFIG_SND_DMA_SGBUF
 277        case SNDRV_DMA_TYPE_DEV_SG:
 278                snd_free_sgbuf_pages(dmab);
 279                break;
 280#endif
 281        default:
 282                printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type);
 283        }
 284}
 285
 286
 287/**
 288 * snd_dma_get_reserved - get the reserved buffer for the given device
 289 * @dmab: the buffer allocation record to store
 290 * @id: the buffer id
 291 *
 292 * Looks for the reserved-buffer list and re-uses if the same buffer
 293 * is found in the list.  When the buffer is found, it's removed from the free list.
 294 *
 295 * Returns the size of buffer if the buffer is found, or zero if not found.
 296 */
 297size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
 298{
 299        struct snd_mem_list *mem;
 300
 301        if (WARN_ON(!dmab))
 302                return 0;
 303
 304        mutex_lock(&list_mutex);
 305        list_for_each_entry(mem, &mem_list_head, list) {
 306                if (mem->id == id &&
 307                    (mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL ||
 308                     ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) {
 309                        struct device *dev = dmab->dev.dev;
 310                        list_del(&mem->list);
 311                        *dmab = mem->buffer;
 312                        if (dmab->dev.dev == NULL)
 313                                dmab->dev.dev = dev;
 314                        kfree(mem);
 315                        mutex_unlock(&list_mutex);
 316                        return dmab->bytes;
 317                }
 318        }
 319        mutex_unlock(&list_mutex);
 320        return 0;
 321}
 322
 323/**
 324 * snd_dma_reserve_buf - reserve the buffer
 325 * @dmab: the buffer to reserve
 326 * @id: the buffer id
 327 *
 328 * Reserves the given buffer as a reserved buffer.
 329 * 
 330 * Returns zero if successful, or a negative code at error.
 331 */
 332int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
 333{
 334        struct snd_mem_list *mem;
 335
 336        if (WARN_ON(!dmab))
 337                return -EINVAL;
 338        mem = kmalloc(sizeof(*mem), GFP_KERNEL);
 339        if (! mem)
 340                return -ENOMEM;
 341        mutex_lock(&list_mutex);
 342        mem->buffer = *dmab;
 343        mem->id = id;
 344        list_add_tail(&mem->list, &mem_list_head);
 345        mutex_unlock(&list_mutex);
 346        return 0;
 347}
 348
 349/*
 350 * purge all reserved buffers
 351 */
 352static void free_all_reserved_pages(void)
 353{
 354        struct list_head *p;
 355        struct snd_mem_list *mem;
 356
 357        mutex_lock(&list_mutex);
 358        while (! list_empty(&mem_list_head)) {
 359                p = mem_list_head.next;
 360                mem = list_entry(p, struct snd_mem_list, list);
 361                list_del(p);
 362                snd_dma_free_pages(&mem->buffer);
 363                kfree(mem);
 364        }
 365        mutex_unlock(&list_mutex);
 366}
 367
 368
 369#ifdef CONFIG_PROC_FS
 370/*
 371 * proc file interface
 372 */
 373#define SND_MEM_PROC_FILE       "driver/snd-page-alloc"
 374static struct proc_dir_entry *snd_mem_proc;
 375
 376static int snd_mem_proc_read(struct seq_file *seq, void *offset)
 377{
 378        long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
 379        struct snd_mem_list *mem;
 380        int devno;
 381        static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG" };
 382
 383        mutex_lock(&list_mutex);
 384        seq_printf(seq, "pages  : %li bytes (%li pages per %likB)\n",
 385                   pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
 386        devno = 0;
 387        list_for_each_entry(mem, &mem_list_head, list) {
 388                devno++;
 389                seq_printf(seq, "buffer %d : ID %08x : type %s\n",
 390                           devno, mem->id, types[mem->buffer.dev.type]);
 391                seq_printf(seq, "  addr = 0x%lx, size = %d bytes\n",
 392                           (unsigned long)mem->buffer.addr,
 393                           (int)mem->buffer.bytes);
 394        }
 395        mutex_unlock(&list_mutex);
 396        return 0;
 397}
 398
 399static int snd_mem_proc_open(struct inode *inode, struct file *file)
 400{
 401        return single_open(file, snd_mem_proc_read, NULL);
 402}
 403
 404/* FIXME: for pci only - other bus? */
 405#ifdef CONFIG_PCI
 406#define gettoken(bufp) strsep(bufp, " \t\n")
 407
 408static ssize_t snd_mem_proc_write(struct file *file, const char __user * buffer,
 409                                  size_t count, loff_t * ppos)
 410{
 411        char buf[128];
 412        char *token, *p;
 413
 414        if (count > sizeof(buf) - 1)
 415                return -EINVAL;
 416        if (copy_from_user(buf, buffer, count))
 417                return -EFAULT;
 418        buf[count] = '\0';
 419
 420        p = buf;
 421        token = gettoken(&p);
 422        if (! token || *token == '#')
 423                return count;
 424        if (strcmp(token, "add") == 0) {
 425                char *endp;
 426                int vendor, device, size, buffers;
 427                long mask;
 428                int i, alloced;
 429                struct pci_dev *pci;
 430
 431                if ((token = gettoken(&p)) == NULL ||
 432                    (vendor = simple_strtol(token, NULL, 0)) <= 0 ||
 433                    (token = gettoken(&p)) == NULL ||
 434                    (device = simple_strtol(token, NULL, 0)) <= 0 ||
 435                    (token = gettoken(&p)) == NULL ||
 436                    (mask = simple_strtol(token, NULL, 0)) < 0 ||
 437                    (token = gettoken(&p)) == NULL ||
 438                    (size = memparse(token, &endp)) < 64*1024 ||
 439                    size > 16*1024*1024 /* too big */ ||
 440                    (token = gettoken(&p)) == NULL ||
 441                    (buffers = simple_strtol(token, NULL, 0)) <= 0 ||
 442                    buffers > 4) {
 443                        printk(KERN_ERR "snd-page-alloc: invalid proc write format\n");
 444                        return count;
 445                }
 446                vendor &= 0xffff;
 447                device &= 0xffff;
 448
 449                alloced = 0;
 450                pci = NULL;
 451                while ((pci = pci_get_device(vendor, device, pci)) != NULL) {
 452                        if (mask > 0 && mask < 0xffffffff) {
 453                                if (pci_set_dma_mask(pci, mask) < 0 ||
 454                                    pci_set_consistent_dma_mask(pci, mask) < 0) {
 455                                        printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device);
 456                                        pci_dev_put(pci);
 457                                        return count;
 458                                }
 459                        }
 460                        for (i = 0; i < buffers; i++) {
 461                                struct snd_dma_buffer dmab;
 462                                memset(&dmab, 0, sizeof(dmab));
 463                                if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
 464                                                        size, &dmab) < 0) {
 465                                        printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
 466                                        pci_dev_put(pci);
 467                                        return count;
 468                                }
 469                                snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
 470                        }
 471                        alloced++;
 472                }
 473                if (! alloced) {
 474                        for (i = 0; i < buffers; i++) {
 475                                struct snd_dma_buffer dmab;
 476                                memset(&dmab, 0, sizeof(dmab));
 477                                /* FIXME: We can allocate only in ZONE_DMA
 478                                 * without a device pointer!
 479                                 */
 480                                if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, NULL,
 481                                                        size, &dmab) < 0) {
 482                                        printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
 483                                        break;
 484                                }
 485                                snd_dma_reserve_buf(&dmab, (unsigned int)((vendor << 16) | device));
 486                        }
 487                }
 488        } else if (strcmp(token, "erase") == 0)
 489                /* FIXME: need for releasing each buffer chunk? */
 490                free_all_reserved_pages();
 491        else
 492                printk(KERN_ERR "snd-page-alloc: invalid proc cmd\n");
 493        return count;
 494}
 495#endif /* CONFIG_PCI */
 496
 497static const struct file_operations snd_mem_proc_fops = {
 498        .owner          = THIS_MODULE,
 499        .open           = snd_mem_proc_open,
 500        .read           = seq_read,
 501#ifdef CONFIG_PCI
 502        .write          = snd_mem_proc_write,
 503#endif
 504        .llseek         = seq_lseek,
 505        .release        = single_release,
 506};
 507
 508#endif /* CONFIG_PROC_FS */
 509
 510/*
 511 * module entry
 512 */
 513
 514static int __init snd_mem_init(void)
 515{
 516#ifdef CONFIG_PROC_FS
 517        snd_mem_proc = proc_create(SND_MEM_PROC_FILE, 0644, NULL,
 518                                   &snd_mem_proc_fops);
 519#endif
 520        return 0;
 521}
 522
 523static void __exit snd_mem_exit(void)
 524{
 525        remove_proc_entry(SND_MEM_PROC_FILE, NULL);
 526        free_all_reserved_pages();
 527        if (snd_allocated_pages > 0)
 528                printk(KERN_ERR "snd-malloc: Memory leak?  pages not freed = %li\n", snd_allocated_pages);
 529}
 530
 531
 532module_init(snd_mem_init)
 533module_exit(snd_mem_exit)
 534
 535
 536/*
 537 * exports
 538 */
 539EXPORT_SYMBOL(snd_dma_alloc_pages);
 540EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
 541EXPORT_SYMBOL(snd_dma_free_pages);
 542
 543EXPORT_SYMBOL(snd_dma_get_reserved_buf);
 544EXPORT_SYMBOL(snd_dma_reserve_buf);
 545
 546EXPORT_SYMBOL(snd_malloc_pages);
 547EXPORT_SYMBOL(snd_free_pages);
 548
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.