162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Generic memory management routines for soundcard memory allocation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/mutex.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <sound/core.h> 1362306a36Sopenharmony_ci#include <sound/util_mem.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciMODULE_AUTHOR("Takashi Iwai"); 1662306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); 1762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define get_memblk(p) list_entry(p, struct snd_util_memblk, list) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * create a new memory manager 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistruct snd_util_memhdr * 2562306a36Sopenharmony_cisnd_util_memhdr_new(int memsize) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct snd_util_memhdr *hdr; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); 3062306a36Sopenharmony_ci if (hdr == NULL) 3162306a36Sopenharmony_ci return NULL; 3262306a36Sopenharmony_ci hdr->size = memsize; 3362306a36Sopenharmony_ci mutex_init(&hdr->block_mutex); 3462306a36Sopenharmony_ci INIT_LIST_HEAD(&hdr->block); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci return hdr; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * free a memory manager 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_civoid snd_util_memhdr_free(struct snd_util_memhdr *hdr) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct list_head *p; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!hdr) 4762306a36Sopenharmony_ci return; 4862306a36Sopenharmony_ci /* release all blocks */ 4962306a36Sopenharmony_ci while ((p = hdr->block.next) != &hdr->block) { 5062306a36Sopenharmony_ci list_del(p); 5162306a36Sopenharmony_ci kfree(get_memblk(p)); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci kfree(hdr); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * allocate a memory block (without mutex) 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistruct snd_util_memblk * 6062306a36Sopenharmony_ci__snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct snd_util_memblk *blk; 6362306a36Sopenharmony_ci unsigned int units, prev_offset; 6462306a36Sopenharmony_ci struct list_head *p; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (snd_BUG_ON(!hdr || size <= 0)) 6762306a36Sopenharmony_ci return NULL; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* word alignment */ 7062306a36Sopenharmony_ci units = size; 7162306a36Sopenharmony_ci if (units & 1) 7262306a36Sopenharmony_ci units++; 7362306a36Sopenharmony_ci if (units > hdr->size) 7462306a36Sopenharmony_ci return NULL; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* look for empty block */ 7762306a36Sopenharmony_ci prev_offset = 0; 7862306a36Sopenharmony_ci list_for_each(p, &hdr->block) { 7962306a36Sopenharmony_ci blk = get_memblk(p); 8062306a36Sopenharmony_ci if (blk->offset - prev_offset >= units) 8162306a36Sopenharmony_ci goto __found; 8262306a36Sopenharmony_ci prev_offset = blk->offset + blk->size; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci if (hdr->size - prev_offset < units) 8562306a36Sopenharmony_ci return NULL; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci__found: 8862306a36Sopenharmony_ci return __snd_util_memblk_new(hdr, units, p->prev); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * create a new memory block with the given size 9462306a36Sopenharmony_ci * the block is linked next to prev 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistruct snd_util_memblk * 9762306a36Sopenharmony_ci__snd_util_memblk_new(struct snd_util_memhdr *hdr, unsigned int units, 9862306a36Sopenharmony_ci struct list_head *prev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct snd_util_memblk *blk; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci blk = kmalloc(sizeof(struct snd_util_memblk) + hdr->block_extra_size, 10362306a36Sopenharmony_ci GFP_KERNEL); 10462306a36Sopenharmony_ci if (blk == NULL) 10562306a36Sopenharmony_ci return NULL; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (prev == &hdr->block) 10862306a36Sopenharmony_ci blk->offset = 0; 10962306a36Sopenharmony_ci else { 11062306a36Sopenharmony_ci struct snd_util_memblk *p = get_memblk(prev); 11162306a36Sopenharmony_ci blk->offset = p->offset + p->size; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci blk->size = units; 11462306a36Sopenharmony_ci list_add(&blk->list, prev); 11562306a36Sopenharmony_ci hdr->nblocks++; 11662306a36Sopenharmony_ci hdr->used += units; 11762306a36Sopenharmony_ci return blk; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * allocate a memory block (with mutex) 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistruct snd_util_memblk * 12562306a36Sopenharmony_cisnd_util_mem_alloc(struct snd_util_memhdr *hdr, int size) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct snd_util_memblk *blk; 12862306a36Sopenharmony_ci mutex_lock(&hdr->block_mutex); 12962306a36Sopenharmony_ci blk = __snd_util_mem_alloc(hdr, size); 13062306a36Sopenharmony_ci mutex_unlock(&hdr->block_mutex); 13162306a36Sopenharmony_ci return blk; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * remove the block from linked-list and free resource 13762306a36Sopenharmony_ci * (without mutex) 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_civoid 14062306a36Sopenharmony_ci__snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci list_del(&blk->list); 14362306a36Sopenharmony_ci hdr->nblocks--; 14462306a36Sopenharmony_ci hdr->used -= blk->size; 14562306a36Sopenharmony_ci kfree(blk); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * free a memory block (with mutex) 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ciint snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci if (snd_BUG_ON(!hdr || !blk)) 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci mutex_lock(&hdr->block_mutex); 15762306a36Sopenharmony_ci __snd_util_mem_free(hdr, blk); 15862306a36Sopenharmony_ci mutex_unlock(&hdr->block_mutex); 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* 16362306a36Sopenharmony_ci * return available memory size 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ciint snd_util_mem_avail(struct snd_util_memhdr *hdr) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci unsigned int size; 16862306a36Sopenharmony_ci mutex_lock(&hdr->block_mutex); 16962306a36Sopenharmony_ci size = hdr->size - hdr->used; 17062306a36Sopenharmony_ci mutex_unlock(&hdr->block_mutex); 17162306a36Sopenharmony_ci return size; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_util_memhdr_new); 17662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_util_memhdr_free); 17762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_util_mem_alloc); 17862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_util_mem_free); 17962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_util_mem_avail); 18062306a36Sopenharmony_ciEXPORT_SYMBOL(__snd_util_mem_alloc); 18162306a36Sopenharmony_ciEXPORT_SYMBOL(__snd_util_mem_free); 18262306a36Sopenharmony_ciEXPORT_SYMBOL(__snd_util_memblk_new); 183