File: seq_memory.c

package info (click to toggle)
alsadriver 0.2.0-pre8-3
  • links: PTS
  • area: main
  • in suites: slink
  • size: 2,808 kB
  • ctags: 6,550
  • sloc: ansic: 43,490; sh: 916; makefile: 759; perl: 54
file content (338 lines) | stat: -rw-r--r-- 8,950 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/*
 *  ALSA sequencer Memory Manager
 *  Copyright (c) 1998 by Frank van de Pol <F.K.W.van.de.Pol@inter.nl.net>
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#include "driver.h"

#include "seq_memory.h"
#include "seq_info.h"

/* define to keep statistics on successful allocations (for debugging) */
#define TRACK_SUCCESS


/* design note: the pool is a contigious block of memory, if we dynamicly
   want to add additional cells to the pool be better store this in another
   pool as we need to know the base address of the pool when releasing
   memory. */

typedef struct {
	snd_seq_event_cell_t *ptr;	/* pointer to start of pool */
	snd_seq_event_cell_t *free;	/* pointer to the head of the free list */

	int total_elements;
	int available;

	/* statistics */
	int min_available;

	  snd_spin_define(lock);
} pool_t;

/* pool of free event cells */
static pool_t *free_pool;

/* counter for tracking memory leaks for 'external' data */
static int ext_alloc = 0;
static int ext_alloc_max = 0;
static unsigned long ext_alloc_bytes = 0;
static unsigned long ext_alloc_max_bytes = 0;
static unsigned long ext_alloc_biggest_alloc = 0;

/* keep track of failures */
static int event_alloc_failures = 0;
static int ext_alloc_failures = 0;

#ifdef TRACK_SUCCESS
/* keep track of successfull allocations */
static int event_alloc_success = 0;
static int ext_alloc_success = 0;

#endif


/* release this cell */
void snd_seq_cell_free(snd_seq_event_cell_t * cell)
{
	unsigned long flags;

	snd_spin_lock(free_pool, lock, &flags);
	if (cell != NULL) {
		if (free_pool->free != NULL) {
			/* normal situation */
			cell->ptr_l = free_pool->free;	/* chain in old element */
			cell->ptr_r = NULL;
			free_pool->free = cell;
		} else {
			/* first element */
			free_pool->free = cell;
			cell->ptr_l = NULL;
			cell->ptr_r = NULL;
		}
		free_pool->available++;
	}
	snd_spin_unlock(free_pool, lock, &flags);

#if 0
	/* debug... */
	snd_printk("Seq: memory total=%d avail=%d\n",
		   free_pool->total_elements,
		   free_pool->available);
#endif
}


/* return pointer to cell. NULL on failure */
snd_seq_event_cell_t *snd_seq_cell_alloc(void)
{
	snd_seq_event_cell_t *cell;
	unsigned long flags;

	snd_spin_lock(free_pool, lock, &flags);
	if (free_pool->free != NULL) {
		cell = free_pool->free;
		free_pool->free = cell->ptr_l;
		free_pool->available--;
		if (free_pool->available < free_pool->min_available)
			free_pool->min_available = free_pool->available;

		/* clear cell pointers */
		cell->ptr_l = NULL;
		cell->ptr_r = NULL;
	} else {
		/* no element available... */
		snd_printk("Seq: cell_alloc failed: no cells available\n");
		cell = NULL;
	}
	snd_spin_lock(free_pool, lock, &flags);

#if 0
	/* debug... */
	snd_printk("Seq: memory total=%d avail=%d\n",
		   free_pool->total_elements,
		   free_pool->available);
#endif
	if (cell == NULL)
		event_alloc_failures++;
#ifdef TRACK_SUCCESS
	else
		event_alloc_success++;
#endif

	return cell;
}


/* duplicate event, NULL on failure */
extern snd_seq_event_cell_t *snd_seq_event_dup(snd_seq_event_t * event)
{
	snd_seq_event_cell_t *new_cell;

	if (event == NULL)
		return NULL;

	new_cell = snd_seq_cell_alloc();
	if (new_cell) {
		memcpy(&new_cell->event, event, sizeof(snd_seq_event_t));
	}
	return new_cell;
}

extern snd_seq_event_cell_t *snd_seq_event_dup_from_user(snd_seq_event_t * event)
{
	snd_seq_event_cell_t *new_cell;

	if (event == NULL)
		return NULL;

	new_cell = snd_seq_cell_alloc();
	if (new_cell) {
		copy_from_user(&new_cell->event, event, sizeof(snd_seq_event_t));
	}
	return new_cell;
}


/* duplicate event cell, NULL on failure */
extern snd_seq_event_cell_t *snd_seq_cell_dup(snd_seq_event_cell_t * cell)
{
	snd_seq_event_cell_t *new_cell;

	if (cell == NULL)
		return 0;

	new_cell = snd_seq_cell_alloc();
	if (new_cell) {
		memcpy(&new_cell->event, &cell->event, sizeof(snd_seq_event_t));
	}
	return new_cell;
}


/* return number of unused (free) cells */
int snd_seq_unused_cells(void)
{
	return (free_pool->available);
}


/* return total number of allocated cells */
int snd_seq_total_cells(void)
{
	return (free_pool->total_elements);
}



/* init memory, allocate room specified number of events */
void snd_sequencer_memory_init(int events)
{
	int cell;
	snd_seq_event_cell_t *cellptr;
	unsigned long flags;

	/* create pool block */
	free_pool = snd_malloc(sizeof(pool_t));
	if (!free_pool) {
		snd_printk("Seq: malloc failed for free_pool\n");
		return;
	}
	snd_spin_prepare(free_pool, lock);
	snd_spin_lock(free_pool, lock, &flags);
	free_pool->ptr = NULL;
	free_pool->free = NULL;
	free_pool->total_elements = 0;
	free_pool->available = 0;
	snd_spin_unlock(free_pool, lock, &flags);


	/* alloc memory */
	free_pool->ptr = snd_malloc(sizeof(snd_seq_event_cell_t) * events);
	if (free_pool->ptr) {
		memset(free_pool->ptr, 0, sizeof(snd_seq_event_cell_t) * events);
		free_pool->total_elements = events;

		snd_printk("Seq: memory init, %d events, base at 0x%p\n", events, free_pool->ptr);
		snd_printk("Seq: size of element = %d\n", sizeof(snd_seq_event_cell_t));

		/* add the new cell's to the free cell list by calling the free()
		   function for each one */
		for (cell = 0; cell < events; cell++) {
			cellptr = &free_pool->ptr[cell];
			snd_seq_cell_free(cellptr);
		}
	} else {
		snd_printk("Seq: malloc for sequencer events failed\n");
	}

	/* init statistics */
	free_pool->min_available = free_pool->available;
}


/* release event memory */
void snd_sequencer_memory_done(void)
{
	if (free_pool) {
		if (free_pool->ptr != NULL) {
			snd_printk("Seq: memory done, total=%d avail=%d min_avail=%d\n",
				   free_pool->total_elements,
				   free_pool->available,
				   free_pool->min_available);

			snd_free(free_pool->ptr, sizeof(snd_seq_event_cell_t) * free_pool->total_elements);
			free_pool->ptr = NULL;
			free_pool->free = NULL;
			free_pool->total_elements = 0;
			free_pool->available = 0;
		}
		snd_free(free_pool, sizeof(pool_t));
	}
	if (ext_alloc > 0) {
		snd_printk("seq: memory leak alert, still %d blocks of external data allocated\n", ext_alloc);
	}
}


/* wrapper for allocating and freeing 'external' data (eg. sysex, meta
   events etc.) for now it is just passed to the snd_malloc and snd_free
   calls, but in a later stadium a different allocation method could be
   used. */

void *snd_seq_ext_malloc(unsigned long size)
{
	void *obj;

	obj = snd_malloc(size);
	if (obj) {
		ext_alloc++;
		ext_alloc_bytes += size;
		if (ext_alloc > ext_alloc_max)
			ext_alloc_max = ext_alloc;
		if (ext_alloc_bytes > ext_alloc_max_bytes)
			ext_alloc_max_bytes = ext_alloc_bytes;
		if (size > ext_alloc_biggest_alloc)
			ext_alloc_biggest_alloc = size;
#ifdef TRACK_SUCCESS
		ext_alloc_success++;
#endif
	} else {
		ext_alloc_failures++;
	}

	return obj;
}

void snd_seq_ext_free(void *obj, unsigned long size)
{
	ext_alloc--;
	ext_alloc_bytes -= size;

	if (ext_alloc < 0) {
		snd_printk("seq: whoops, more ext data free()s than malloc()...\n");
	}
	snd_free(obj, size);
}



/* exported to seq_info.c */
void snd_seq_info_memory_read(snd_info_buffer_t * buffer, void *private_data)
{
	snd_iprintf(buffer, "Events\n");
	snd_iprintf(buffer, "  Pool size          : %d\n", snd_seq_total_cells());
	snd_iprintf(buffer, "  Available cells    : %d\n", snd_seq_unused_cells());
	snd_iprintf(buffer, "  Cells in use       : %d\n", snd_seq_total_cells() - snd_seq_unused_cells());
	snd_iprintf(buffer, "  Peak cells in use  : %d\n", snd_seq_total_cells() - free_pool->min_available);
#ifdef TRACK_SUCCESS
	snd_iprintf(buffer, "  Alloc success      : %d\n", event_alloc_success);
#endif
	snd_iprintf(buffer, "  Alloc failures     : %d\n", event_alloc_failures);
	snd_iprintf(buffer, "\n");
	snd_iprintf(buffer, "External data\n");
	snd_iprintf(buffer, "  Blocks in use      : %d\n", ext_alloc);
	snd_iprintf(buffer, "  Bytes in use       : %d\n", ext_alloc_bytes);
	snd_iprintf(buffer, "  Peak blocks in use : %d\n", ext_alloc_max);
	snd_iprintf(buffer, "  Peak bytes in use  : %d\n", ext_alloc_max_bytes);
	snd_iprintf(buffer, "  Largest allocation : %d\n", ext_alloc_biggest_alloc);
#ifdef TRACK_SUCCESS
	snd_iprintf(buffer, "  Alloc success      : %d\n", ext_alloc_success);
#endif
	snd_iprintf(buffer, "  Alloc failures     : %d\n", ext_alloc_failures);
}