diff options
Diffstat (limited to 'sound/core/memory.c')
-rw-r--r-- | sound/core/memory.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/sound/core/memory.c b/sound/core/memory.c new file mode 100644 index 00000000000..20860fec936 --- /dev/null +++ b/sound/core/memory.c @@ -0,0 +1,306 @@ +/* + * Copyright (c) by Jaroslav Kysela <perex@suse.cz> + * + * Memory allocation helpers. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <sound/driver.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/pci.h> +#include <sound/core.h> +#include <sound/info.h> + +/* + * memory allocation helpers and debug routines + */ + +#ifdef CONFIG_SND_DEBUG_MEMORY + +struct snd_alloc_track { + unsigned long magic; + void *caller; + size_t size; + struct list_head list; + long data[0]; +}; + +#define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data) + +static long snd_alloc_kmalloc; +static long snd_alloc_vmalloc; +static LIST_HEAD(snd_alloc_kmalloc_list); +static LIST_HEAD(snd_alloc_vmalloc_list); +static DEFINE_SPINLOCK(snd_alloc_kmalloc_lock); +static DEFINE_SPINLOCK(snd_alloc_vmalloc_lock); +#define KMALLOC_MAGIC 0x87654321 +#define VMALLOC_MAGIC 0x87654320 +static snd_info_entry_t *snd_memory_info_entry; + +void snd_memory_init(void) +{ + snd_alloc_kmalloc = 0; + snd_alloc_vmalloc = 0; +} + +void snd_memory_done(void) +{ + struct list_head *head; + struct snd_alloc_track *t; + + if (snd_alloc_kmalloc > 0) + snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); + if (snd_alloc_vmalloc > 0) + snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); + list_for_each_prev(head, &snd_alloc_kmalloc_list) { + t = list_entry(head, struct snd_alloc_track, list); + if (t->magic != KMALLOC_MAGIC) { + snd_printk(KERN_ERR "Corrupted kmalloc\n"); + break; + } + snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + } + list_for_each_prev(head, &snd_alloc_vmalloc_list) { + t = list_entry(head, struct snd_alloc_track, list); + if (t->magic != VMALLOC_MAGIC) { + snd_printk(KERN_ERR "Corrupted vmalloc\n"); + break; + } + snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + } +} + +static void *__snd_kmalloc(size_t size, int flags, void *caller) +{ + unsigned long cpu_flags; + struct snd_alloc_track *t; + void *ptr; + + ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags); + if (ptr != NULL) { + t = (struct snd_alloc_track *)ptr; + t->magic = KMALLOC_MAGIC; + t->caller = caller; + spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags); + list_add_tail(&t->list, &snd_alloc_kmalloc_list); + spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags); + t->size = size; + snd_alloc_kmalloc += size; + ptr = t->data; + } + return ptr; +} + +#define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0)); +void *snd_hidden_kmalloc(size_t size, int flags) +{ + return _snd_kmalloc(size, flags); +} + +void *snd_hidden_kcalloc(size_t n, size_t size, int flags) +{ + void *ret = NULL; + if (n != 0 && size > INT_MAX / n) + return ret; + ret = _snd_kmalloc(n * size, flags); + if (ret) + memset(ret, 0, n * size); + return ret; +} + +void snd_hidden_kfree(const void *obj) +{ + unsigned long flags; + struct snd_alloc_track *t; + if (obj == NULL) + return; + t = snd_alloc_track_entry(obj); + if (t->magic != KMALLOC_MAGIC) { + snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags); + list_del(&t->list); + spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags); + t->magic = 0; + snd_alloc_kmalloc -= t->size; + obj = t; + snd_wrapper_kfree(obj); +} + +void *snd_hidden_vmalloc(unsigned long size) +{ + void *ptr; + ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track)); + if (ptr) { + struct snd_alloc_track *t = (struct snd_alloc_track *)ptr; + t->magic = VMALLOC_MAGIC; + t->caller = __builtin_return_address(0); + spin_lock(&snd_alloc_vmalloc_lock); + list_add_tail(&t->list, &snd_alloc_vmalloc_list); + spin_unlock(&snd_alloc_vmalloc_lock); + t->size = size; + snd_alloc_vmalloc += size; + ptr = t->data; + } + return ptr; +} + +void snd_hidden_vfree(void *obj) +{ + struct snd_alloc_track *t; + if (obj == NULL) + return; + t = snd_alloc_track_entry(obj); + if (t->magic != VMALLOC_MAGIC) { + snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0)); + return; + } + spin_lock(&snd_alloc_vmalloc_lock); + list_del(&t->list); + spin_unlock(&snd_alloc_vmalloc_lock); + t->magic = 0; + snd_alloc_vmalloc -= t->size; + obj = t; + snd_wrapper_vfree(obj); +} + +static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc); + snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc); +} + +int __init snd_memory_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL); + if (entry) { + entry->c.text.read_size = 256; + entry->c.text.read = snd_memory_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_memory_info_entry = entry; + return 0; +} + +int __exit snd_memory_info_done(void) +{ + if (snd_memory_info_entry) + snd_info_unregister(snd_memory_info_entry); + return 0; +} + +#else + +#define _snd_kmalloc kmalloc + +#endif /* CONFIG_SND_DEBUG_MEMORY */ + +/** + * snd_kmalloc_strdup - copy the string + * @string: the original string + * @flags: allocation conditions, GFP_XXX + * + * Allocates a memory chunk via kmalloc() and copies the string to it. + * + * Returns the pointer, or NULL if no enoguh memory. + */ +char *snd_kmalloc_strdup(const char *string, int flags) +{ + size_t len; + char *ptr; + + if (!string) + return NULL; + len = strlen(string) + 1; + ptr = _snd_kmalloc(len, flags); + if (ptr) + memcpy(ptr, string, len); + return ptr; +} + +/** + * copy_to_user_fromio - copy data from mmio-space to user-space + * @dst: the destination pointer on user-space + * @src: the source pointer on mmio + * @count: the data size to copy in bytes + * + * Copies the data from mmio-space to user-space. + * + * Returns zero if successful, or non-zero on failure. + */ +int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count) +{ +#if defined(__i386__) || defined(CONFIG_SPARC32) + return copy_to_user(dst, (const void*)src, count) ? -EFAULT : 0; +#else + char buf[256]; + while (count) { + size_t c = count; + if (c > sizeof(buf)) + c = sizeof(buf); + memcpy_fromio(buf, (void __iomem *)src, c); + if (copy_to_user(dst, buf, c)) + return -EFAULT; + count -= c; + dst += c; + src += c; + } + return 0; +#endif +} + +/** + * copy_from_user_toio - copy data from user-space to mmio-space + * @dst: the destination pointer on mmio-space + * @src: the source pointer on user-space + * @count: the data size to copy in bytes + * + * Copies the data from user-space to mmio-space. + * + * Returns zero if successful, or non-zero on failure. + */ +int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count) +{ +#if defined(__i386__) || defined(CONFIG_SPARC32) + return copy_from_user((void*)dst, src, count) ? -EFAULT : 0; +#else + char buf[256]; + while (count) { + size_t c = count; + if (c > sizeof(buf)) + c = sizeof(buf); + if (copy_from_user(buf, src, c)) + return -EFAULT; + memcpy_toio(dst, buf, c); + count -= c; + dst += c; + src += c; + } + return 0; +#endif +} |