xref: /linux/sound/synth/util_mem.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
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 *
snd_util_memhdr_new(int memsize)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   */
snd_util_memhdr_free(struct snd_util_memhdr * hdr)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 *
__snd_util_mem_alloc(struct snd_util_memhdr * hdr,int size)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 *
__snd_util_memblk_new(struct snd_util_memhdr * hdr,unsigned int units,struct list_head * prev)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 *
snd_util_mem_alloc(struct snd_util_memhdr * hdr,int size)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
__snd_util_mem_free(struct snd_util_memhdr * hdr,struct snd_util_memblk * blk)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   */
snd_util_mem_free(struct snd_util_memhdr * hdr,struct snd_util_memblk * blk)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   */
snd_util_mem_avail(struct snd_util_memhdr * hdr)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);
183