xref: /openbmc/linux/sound/synth/util_mem.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Generic memory management routines for soundcard memory allocation
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
8ef9f0a42SIngo Molnar #include <linux/mutex.h>
91da177e4SLinus Torvalds #include <linux/init.h>
101da177e4SLinus Torvalds #include <linux/slab.h>
11da155d5bSPaul Gortmaker #include <linux/module.h>
121da177e4SLinus Torvalds #include <sound/core.h>
131da177e4SLinus Torvalds #include <sound/util_mem.h>
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds MODULE_AUTHOR("Takashi Iwai");
161da177e4SLinus Torvalds MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation");
171da177e4SLinus Torvalds MODULE_LICENSE("GPL");
181da177e4SLinus Torvalds 
1903da312aSTakashi Iwai #define get_memblk(p)	list_entry(p, struct snd_util_memblk, list)
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds /*
221da177e4SLinus Torvalds  * create a new memory manager
231da177e4SLinus Torvalds  */
2403da312aSTakashi Iwai struct snd_util_memhdr *
snd_util_memhdr_new(int memsize)251da177e4SLinus Torvalds snd_util_memhdr_new(int memsize)
261da177e4SLinus Torvalds {
2703da312aSTakashi Iwai 	struct snd_util_memhdr *hdr;
281da177e4SLinus Torvalds 
29561b220aSTakashi Iwai 	hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
301da177e4SLinus Torvalds 	if (hdr == NULL)
311da177e4SLinus Torvalds 		return NULL;
321da177e4SLinus Torvalds 	hdr->size = memsize;
33ef9f0a42SIngo Molnar 	mutex_init(&hdr->block_mutex);
341da177e4SLinus Torvalds 	INIT_LIST_HEAD(&hdr->block);
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds 	return hdr;
371da177e4SLinus Torvalds }
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds /*
401da177e4SLinus Torvalds  * free a memory manager
411da177e4SLinus Torvalds  */
snd_util_memhdr_free(struct snd_util_memhdr * hdr)4203da312aSTakashi Iwai void snd_util_memhdr_free(struct snd_util_memhdr *hdr)
431da177e4SLinus Torvalds {
441da177e4SLinus Torvalds 	struct list_head *p;
451da177e4SLinus Torvalds 
465e246b85STakashi Iwai 	if (!hdr)
475e246b85STakashi Iwai 		return;
481da177e4SLinus Torvalds 	/* release all blocks */
491da177e4SLinus Torvalds 	while ((p = hdr->block.next) != &hdr->block) {
501da177e4SLinus Torvalds 		list_del(p);
511da177e4SLinus Torvalds 		kfree(get_memblk(p));
521da177e4SLinus Torvalds 	}
531da177e4SLinus Torvalds 	kfree(hdr);
541da177e4SLinus Torvalds }
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /*
571da177e4SLinus Torvalds  * allocate a memory block (without mutex)
581da177e4SLinus Torvalds  */
5903da312aSTakashi Iwai struct snd_util_memblk *
__snd_util_mem_alloc(struct snd_util_memhdr * hdr,int size)6003da312aSTakashi Iwai __snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
611da177e4SLinus Torvalds {
6203da312aSTakashi Iwai 	struct snd_util_memblk *blk;
6303da312aSTakashi Iwai 	unsigned int units, prev_offset;
641da177e4SLinus Torvalds 	struct list_head *p;
651da177e4SLinus Torvalds 
665e246b85STakashi Iwai 	if (snd_BUG_ON(!hdr || size <= 0))
675e246b85STakashi Iwai 		return NULL;
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds 	/* word alignment */
701da177e4SLinus Torvalds 	units = size;
711da177e4SLinus Torvalds 	if (units & 1)
721da177e4SLinus Torvalds 		units++;
731da177e4SLinus Torvalds 	if (units > hdr->size)
741da177e4SLinus Torvalds 		return NULL;
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	/* look for empty block */
771da177e4SLinus Torvalds 	prev_offset = 0;
781da177e4SLinus Torvalds 	list_for_each(p, &hdr->block) {
791da177e4SLinus Torvalds 		blk = get_memblk(p);
801da177e4SLinus Torvalds 		if (blk->offset - prev_offset >= units)
811da177e4SLinus Torvalds 			goto __found;
821da177e4SLinus Torvalds 		prev_offset = blk->offset + blk->size;
831da177e4SLinus Torvalds 	}
841da177e4SLinus Torvalds 	if (hdr->size - prev_offset < units)
851da177e4SLinus Torvalds 		return NULL;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds __found:
881da177e4SLinus Torvalds 	return __snd_util_memblk_new(hdr, units, p->prev);
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds /*
931da177e4SLinus Torvalds  * create a new memory block with the given size
941da177e4SLinus Torvalds  * the block is linked next to prev
951da177e4SLinus Torvalds  */
9603da312aSTakashi Iwai struct snd_util_memblk *
__snd_util_memblk_new(struct snd_util_memhdr * hdr,unsigned int units,struct list_head * prev)9703da312aSTakashi Iwai __snd_util_memblk_new(struct snd_util_memhdr *hdr, unsigned int units,
981da177e4SLinus Torvalds 		      struct list_head *prev)
991da177e4SLinus Torvalds {
10003da312aSTakashi Iwai 	struct snd_util_memblk *blk;
1011da177e4SLinus Torvalds 
10203da312aSTakashi Iwai 	blk = kmalloc(sizeof(struct snd_util_memblk) + hdr->block_extra_size,
10303da312aSTakashi Iwai 		      GFP_KERNEL);
1041da177e4SLinus Torvalds 	if (blk == NULL)
1051da177e4SLinus Torvalds 		return NULL;
1061da177e4SLinus Torvalds 
1078e6c962cSAdrian Bunk 	if (prev == &hdr->block)
1081da177e4SLinus Torvalds 		blk->offset = 0;
1091da177e4SLinus Torvalds 	else {
11003da312aSTakashi Iwai 		struct snd_util_memblk *p = get_memblk(prev);
1111da177e4SLinus Torvalds 		blk->offset = p->offset + p->size;
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 	blk->size = units;
1141da177e4SLinus Torvalds 	list_add(&blk->list, prev);
1151da177e4SLinus Torvalds 	hdr->nblocks++;
1161da177e4SLinus Torvalds 	hdr->used += units;
1171da177e4SLinus Torvalds 	return blk;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds /*
1221da177e4SLinus Torvalds  * allocate a memory block (with mutex)
1231da177e4SLinus Torvalds  */
12403da312aSTakashi Iwai struct snd_util_memblk *
snd_util_mem_alloc(struct snd_util_memhdr * hdr,int size)12503da312aSTakashi Iwai snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
1261da177e4SLinus Torvalds {
12703da312aSTakashi Iwai 	struct snd_util_memblk *blk;
128ef9f0a42SIngo Molnar 	mutex_lock(&hdr->block_mutex);
1291da177e4SLinus Torvalds 	blk = __snd_util_mem_alloc(hdr, size);
130ef9f0a42SIngo Molnar 	mutex_unlock(&hdr->block_mutex);
1311da177e4SLinus Torvalds 	return blk;
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds /*
1361da177e4SLinus Torvalds  * remove the block from linked-list and free resource
1371da177e4SLinus Torvalds  * (without mutex)
1381da177e4SLinus Torvalds  */
1391da177e4SLinus Torvalds void
__snd_util_mem_free(struct snd_util_memhdr * hdr,struct snd_util_memblk * blk)14003da312aSTakashi Iwai __snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds 	list_del(&blk->list);
1431da177e4SLinus Torvalds 	hdr->nblocks--;
1441da177e4SLinus Torvalds 	hdr->used -= blk->size;
1451da177e4SLinus Torvalds 	kfree(blk);
1461da177e4SLinus Torvalds }
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds /*
1491da177e4SLinus Torvalds  * free a memory block (with mutex)
1501da177e4SLinus Torvalds  */
snd_util_mem_free(struct snd_util_memhdr * hdr,struct snd_util_memblk * blk)15103da312aSTakashi Iwai int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
1521da177e4SLinus Torvalds {
1535e246b85STakashi Iwai 	if (snd_BUG_ON(!hdr || !blk))
1545e246b85STakashi Iwai 		return -EINVAL;
1551da177e4SLinus Torvalds 
156ef9f0a42SIngo Molnar 	mutex_lock(&hdr->block_mutex);
1571da177e4SLinus Torvalds 	__snd_util_mem_free(hdr, blk);
158ef9f0a42SIngo Molnar 	mutex_unlock(&hdr->block_mutex);
1591da177e4SLinus Torvalds 	return 0;
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds /*
1631da177e4SLinus Torvalds  * return available memory size
1641da177e4SLinus Torvalds  */
snd_util_mem_avail(struct snd_util_memhdr * hdr)16503da312aSTakashi Iwai int snd_util_mem_avail(struct snd_util_memhdr *hdr)
1661da177e4SLinus Torvalds {
1671da177e4SLinus Torvalds 	unsigned int size;
168ef9f0a42SIngo Molnar 	mutex_lock(&hdr->block_mutex);
1691da177e4SLinus Torvalds 	size = hdr->size - hdr->used;
170ef9f0a42SIngo Molnar 	mutex_unlock(&hdr->block_mutex);
1711da177e4SLinus Torvalds 	return size;
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds EXPORT_SYMBOL(snd_util_memhdr_new);
1761da177e4SLinus Torvalds EXPORT_SYMBOL(snd_util_memhdr_free);
1771da177e4SLinus Torvalds EXPORT_SYMBOL(snd_util_mem_alloc);
1781da177e4SLinus Torvalds EXPORT_SYMBOL(snd_util_mem_free);
1791da177e4SLinus Torvalds EXPORT_SYMBOL(snd_util_mem_avail);
1801da177e4SLinus Torvalds EXPORT_SYMBOL(__snd_util_mem_alloc);
1811da177e4SLinus Torvalds EXPORT_SYMBOL(__snd_util_mem_free);
1821da177e4SLinus Torvalds EXPORT_SYMBOL(__snd_util_memblk_new);
183