183 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>4 *5 * Generic memory management routines for soundcard memory allocation6 */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 manager23 */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 manager41 */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 size94 * the block is linked next to prev95 */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 resource137 * (without mutex)138 */139void140__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 size164 */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);183