| 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);
 | 
|---|