1 /* 2 * Copyright (c) by Jaroslav Kysela <perex@suse.cz> 3 * 4 * Memory allocation helpers. 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 */ 22 23 #include <sound/driver.h> 24 #include <asm/io.h> 25 #include <asm/uaccess.h> 26 #include <linux/init.h> 27 #include <linux/slab.h> 28 #include <linux/time.h> 29 #include <linux/pci.h> 30 #include <sound/core.h> 31 #include <sound/info.h> 32 33 /* 34 * memory allocation helpers and debug routines 35 */ 36 37 #ifdef CONFIG_SND_DEBUG_MEMORY 38 39 struct snd_alloc_track { 40 unsigned long magic; 41 void *caller; 42 size_t size; 43 struct list_head list; 44 long data[0]; 45 }; 46 47 #define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data) 48 49 static long snd_alloc_kmalloc; 50 static long snd_alloc_vmalloc; 51 static LIST_HEAD(snd_alloc_kmalloc_list); 52 static LIST_HEAD(snd_alloc_vmalloc_list); 53 static DEFINE_SPINLOCK(snd_alloc_kmalloc_lock); 54 static DEFINE_SPINLOCK(snd_alloc_vmalloc_lock); 55 #define KMALLOC_MAGIC 0x87654321 56 #define VMALLOC_MAGIC 0x87654320 57 static snd_info_entry_t *snd_memory_info_entry; 58 59 void __init snd_memory_init(void) 60 { 61 snd_alloc_kmalloc = 0; 62 snd_alloc_vmalloc = 0; 63 } 64 65 void snd_memory_done(void) 66 { 67 struct list_head *head; 68 struct snd_alloc_track *t; 69 70 if (snd_alloc_kmalloc > 0) 71 snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); 72 if (snd_alloc_vmalloc > 0) 73 snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); 74 list_for_each_prev(head, &snd_alloc_kmalloc_list) { 75 t = list_entry(head, struct snd_alloc_track, list); 76 if (t->magic != KMALLOC_MAGIC) { 77 snd_printk(KERN_ERR "Corrupted kmalloc\n"); 78 break; 79 } 80 snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); 81 } 82 list_for_each_prev(head, &snd_alloc_vmalloc_list) { 83 t = list_entry(head, struct snd_alloc_track, list); 84 if (t->magic != VMALLOC_MAGIC) { 85 snd_printk(KERN_ERR "Corrupted vmalloc\n"); 86 break; 87 } 88 snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); 89 } 90 } 91 92 static void *__snd_kmalloc(size_t size, gfp_t flags, void *caller) 93 { 94 unsigned long cpu_flags; 95 struct snd_alloc_track *t; 96 void *ptr; 97 98 ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags); 99 if (ptr != NULL) { 100 t = (struct snd_alloc_track *)ptr; 101 t->magic = KMALLOC_MAGIC; 102 t->caller = caller; 103 spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags); 104 list_add_tail(&t->list, &snd_alloc_kmalloc_list); 105 spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags); 106 t->size = size; 107 snd_alloc_kmalloc += size; 108 ptr = t->data; 109 } 110 return ptr; 111 } 112 113 #define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0)); 114 void *snd_hidden_kmalloc(size_t size, gfp_t flags) 115 { 116 return _snd_kmalloc(size, flags); 117 } 118 119 void *snd_hidden_kzalloc(size_t size, gfp_t flags) 120 { 121 void *ret = _snd_kmalloc(size, flags); 122 if (ret) 123 memset(ret, 0, size); 124 return ret; 125 } 126 EXPORT_SYMBOL(snd_hidden_kzalloc); 127 128 void *snd_hidden_kcalloc(size_t n, size_t size, gfp_t flags) 129 { 130 void *ret = NULL; 131 if (n != 0 && size > INT_MAX / n) 132 return ret; 133 return snd_hidden_kzalloc(n * size, flags); 134 } 135 136 void snd_hidden_kfree(const void *obj) 137 { 138 unsigned long flags; 139 struct snd_alloc_track *t; 140 if (obj == NULL) 141 return; 142 t = snd_alloc_track_entry(obj); 143 if (t->magic != KMALLOC_MAGIC) { 144 snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0)); 145 return; 146 } 147 spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags); 148 list_del(&t->list); 149 spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags); 150 t->magic = 0; 151 snd_alloc_kmalloc -= t->size; 152 obj = t; 153 snd_wrapper_kfree(obj); 154 } 155 156 void *snd_hidden_vmalloc(unsigned long size) 157 { 158 void *ptr; 159 ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track)); 160 if (ptr) { 161 struct snd_alloc_track *t = (struct snd_alloc_track *)ptr; 162 t->magic = VMALLOC_MAGIC; 163 t->caller = __builtin_return_address(0); 164 spin_lock(&snd_alloc_vmalloc_lock); 165 list_add_tail(&t->list, &snd_alloc_vmalloc_list); 166 spin_unlock(&snd_alloc_vmalloc_lock); 167 t->size = size; 168 snd_alloc_vmalloc += size; 169 ptr = t->data; 170 } 171 return ptr; 172 } 173 174 void snd_hidden_vfree(void *obj) 175 { 176 struct snd_alloc_track *t; 177 if (obj == NULL) 178 return; 179 t = snd_alloc_track_entry(obj); 180 if (t->magic != VMALLOC_MAGIC) { 181 snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0)); 182 return; 183 } 184 spin_lock(&snd_alloc_vmalloc_lock); 185 list_del(&t->list); 186 spin_unlock(&snd_alloc_vmalloc_lock); 187 t->magic = 0; 188 snd_alloc_vmalloc -= t->size; 189 obj = t; 190 snd_wrapper_vfree(obj); 191 } 192 193 char *snd_hidden_kstrdup(const char *s, gfp_t flags) 194 { 195 int len; 196 char *buf; 197 198 if (!s) return NULL; 199 200 len = strlen(s) + 1; 201 buf = _snd_kmalloc(len, flags); 202 if (buf) 203 memcpy(buf, s, len); 204 return buf; 205 } 206 207 static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) 208 { 209 snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc); 210 snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc); 211 } 212 213 int __init snd_memory_info_init(void) 214 { 215 snd_info_entry_t *entry; 216 217 entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL); 218 if (entry) { 219 entry->c.text.read_size = 256; 220 entry->c.text.read = snd_memory_info_read; 221 if (snd_info_register(entry) < 0) { 222 snd_info_free_entry(entry); 223 entry = NULL; 224 } 225 } 226 snd_memory_info_entry = entry; 227 return 0; 228 } 229 230 int __exit snd_memory_info_done(void) 231 { 232 if (snd_memory_info_entry) 233 snd_info_unregister(snd_memory_info_entry); 234 return 0; 235 } 236 237 #endif /* CONFIG_SND_DEBUG_MEMORY */ 238 239 /** 240 * copy_to_user_fromio - copy data from mmio-space to user-space 241 * @dst: the destination pointer on user-space 242 * @src: the source pointer on mmio 243 * @count: the data size to copy in bytes 244 * 245 * Copies the data from mmio-space to user-space. 246 * 247 * Returns zero if successful, or non-zero on failure. 248 */ 249 int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count) 250 { 251 #if defined(__i386__) || defined(CONFIG_SPARC32) 252 return copy_to_user(dst, (const void __force*)src, count) ? -EFAULT : 0; 253 #else 254 char buf[256]; 255 while (count) { 256 size_t c = count; 257 if (c > sizeof(buf)) 258 c = sizeof(buf); 259 memcpy_fromio(buf, (void __iomem *)src, c); 260 if (copy_to_user(dst, buf, c)) 261 return -EFAULT; 262 count -= c; 263 dst += c; 264 src += c; 265 } 266 return 0; 267 #endif 268 } 269 270 /** 271 * copy_from_user_toio - copy data from user-space to mmio-space 272 * @dst: the destination pointer on mmio-space 273 * @src: the source pointer on user-space 274 * @count: the data size to copy in bytes 275 * 276 * Copies the data from user-space to mmio-space. 277 * 278 * Returns zero if successful, or non-zero on failure. 279 */ 280 int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count) 281 { 282 #if defined(__i386__) || defined(CONFIG_SPARC32) 283 return copy_from_user((void __force *)dst, src, count) ? -EFAULT : 0; 284 #else 285 char buf[256]; 286 while (count) { 287 size_t c = count; 288 if (c > sizeof(buf)) 289 c = sizeof(buf); 290 if (copy_from_user(buf, src, c)) 291 return -EFAULT; 292 memcpy_toio(dst, buf, c); 293 count -= c; 294 dst += c; 295 src += c; 296 } 297 return 0; 298 #endif 299 } 300