1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> 4 * 5 * Generic memory management routines for soundcard memory allocation 6 */ 7 8 #include <linux/mutex.h> 9 #include <linux/init.h> 10 #include <linux/slab.h> 11 #include <linux/module.h> 12 #include <sound/core.h> 13 #include <sound/util_mem.h> 14 15 MODULE_AUTHOR("Takashi Iwai"); 16 MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); 17 MODULE_LICENSE("GPL"); 18 19 #define get_memblk(p) list_entry(p, struct snd_util_memblk, list) 20 21 /* 22 * create a new memory manager 23 */ 24 struct snd_util_memhdr * 25 snd_util_memhdr_new(int memsize) 26 { 27 struct snd_util_memhdr *hdr; 28 29 hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); 30 if (hdr == NULL) 31 return NULL; 32 hdr->size = memsize; 33 mutex_init(&hdr->block_mutex); 34 INIT_LIST_HEAD(&hdr->block); 35 36 return hdr; 37 } 38 39 /* 40 * free a memory manager 41 */ 42 void snd_util_memhdr_free(struct snd_util_memhdr *hdr) 43 { 44 struct list_head *p; 45 46 if (!hdr) 47 return; 48 /* release all blocks */ 49 while ((p = hdr->block.next) != &hdr->block) { 50 list_del(p); 51 kfree(get_memblk(p)); 52 } 53 kfree(hdr); 54 } 55 56 /* 57 * allocate a memory block (without mutex) 58 */ 59 struct snd_util_memblk * 60 __snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size) 61 { 62 struct snd_util_memblk *blk; 63 unsigned int units, prev_offset; 64 struct list_head *p; 65 66 if (snd_BUG_ON(!hdr || size <= 0)) 67 return NULL; 68 69 /* word alignment */ 70 units = size; 71 if (units & 1) 72 units++; 73 if (units > hdr->size) 74 return NULL; 75 76 /* look for empty block */ 77 prev_offset = 0; 78 list_for_each(p, &hdr->block) { 79 blk = get_memblk(p); 80 if (blk->offset - prev_offset >= units) 81 goto __found; 82 prev_offset = blk->offset + blk->size; 83 } 84 if (hdr->size - prev_offset < units) 85 return NULL; 86 87 __found: 88 return __snd_util_memblk_new(hdr, units, p->prev); 89 } 90 91 92 /* 93 * create a new memory block with the given size 94 * the block is linked next to prev 95 */ 96 struct snd_util_memblk * 97 __snd_util_memblk_new(struct snd_util_memhdr *hdr, unsigned int units, 98 struct list_head *prev) 99 { 100 struct snd_util_memblk *blk; 101 102 blk = kmalloc(sizeof(struct snd_util_memblk) + hdr->block_extra_size, 103 GFP_KERNEL); 104 if (blk == NULL) 105 return NULL; 106 107 if (prev == &hdr->block) 108 blk->offset = 0; 109 else { 110 struct snd_util_memblk *p = get_memblk(prev); 111 blk->offset = p->offset + p->size; 112 } 113 blk->size = units; 114 list_add(&blk->list, prev); 115 hdr->nblocks++; 116 hdr->used += units; 117 return blk; 118 } 119 120 121 /* 122 * allocate a memory block (with mutex) 123 */ 124 struct snd_util_memblk * 125 snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size) 126 { 127 struct snd_util_memblk *blk; 128 mutex_lock(&hdr->block_mutex); 129 blk = __snd_util_mem_alloc(hdr, size); 130 mutex_unlock(&hdr->block_mutex); 131 return blk; 132 } 133 134 135 /* 136 * remove the block from linked-list and free resource 137 * (without mutex) 138 */ 139 void 140 __snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk) 141 { 142 list_del(&blk->list); 143 hdr->nblocks--; 144 hdr->used -= blk->size; 145 kfree(blk); 146 } 147 148 /* 149 * free a memory block (with mutex) 150 */ 151 int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk) 152 { 153 if (snd_BUG_ON(!hdr || !blk)) 154 return -EINVAL; 155 156 mutex_lock(&hdr->block_mutex); 157 __snd_util_mem_free(hdr, blk); 158 mutex_unlock(&hdr->block_mutex); 159 return 0; 160 } 161 162 /* 163 * return available memory size 164 */ 165 int snd_util_mem_avail(struct snd_util_memhdr *hdr) 166 { 167 unsigned int size; 168 mutex_lock(&hdr->block_mutex); 169 size = hdr->size - hdr->used; 170 mutex_unlock(&hdr->block_mutex); 171 return size; 172 } 173 174 175 EXPORT_SYMBOL(snd_util_memhdr_new); 176 EXPORT_SYMBOL(snd_util_memhdr_free); 177 EXPORT_SYMBOL(snd_util_mem_alloc); 178 EXPORT_SYMBOL(snd_util_mem_free); 179 EXPORT_SYMBOL(snd_util_mem_avail); 180 EXPORT_SYMBOL(__snd_util_mem_alloc); 181 EXPORT_SYMBOL(__snd_util_mem_free); 182 EXPORT_SYMBOL(__snd_util_memblk_new); 183