util_mem.c (3776B)
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 15MODULE_AUTHOR("Takashi Iwai"); 16MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); 17MODULE_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 */ 24struct snd_util_memhdr * 25snd_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 */ 42void 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 */ 59struct 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 */ 96struct 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 */ 124struct snd_util_memblk * 125snd_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 */ 139void 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 */ 151int 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 */ 165int 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 175EXPORT_SYMBOL(snd_util_memhdr_new); 176EXPORT_SYMBOL(snd_util_memhdr_free); 177EXPORT_SYMBOL(snd_util_mem_alloc); 178EXPORT_SYMBOL(snd_util_mem_free); 179EXPORT_SYMBOL(snd_util_mem_avail); 180EXPORT_SYMBOL(__snd_util_mem_alloc); 181EXPORT_SYMBOL(__snd_util_mem_free); 182EXPORT_SYMBOL(__snd_util_memblk_new);