1/*	$NetBSD: pool-debug.c,v 1.1.1.2 2009/12/02 00:26:09 haad Exp $	*/
2
3/*
4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
6 *
7 * This file is part of the device-mapper userspace tools.
8 *
9 * This copyrighted material is made available to anyone wishing to use,
10 * modify, copy, or redistribute it subject to the terms and conditions
11 * of the GNU Lesser General Public License v.2.1.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17
18#include "dmlib.h"
19#include <assert.h>
20
21struct block {
22	struct block *next;
23	size_t size;
24	void *data;
25};
26
27typedef struct {
28	unsigned block_serialno;	/* Non-decreasing serialno of block */
29	unsigned blocks_allocated;	/* Current number of blocks allocated */
30	unsigned blocks_max;	/* Max no of concurrently-allocated blocks */
31	unsigned int bytes, maxbytes;
32} pool_stats;
33
34struct dm_pool {
35	struct dm_list list;
36	const char *name;
37	void *orig_pool;	/* to pair it with first allocation call */
38
39	int begun;
40	struct block *object;
41
42	struct block *blocks;
43	struct block *tail;
44
45	pool_stats stats;
46};
47
48/* by default things come out aligned for doubles */
49#define DEFAULT_ALIGNMENT __alignof__ (double)
50
51struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
52{
53	struct dm_pool *mem = dm_malloc(sizeof(*mem));
54
55	if (!mem) {
56		log_error("Couldn't create memory pool %s (size %"
57			  PRIsize_t ")", name, sizeof(*mem));
58		return NULL;
59	}
60
61	mem->name = name;
62	mem->begun = 0;
63	mem->object = 0;
64	mem->blocks = mem->tail = NULL;
65
66	mem->stats.block_serialno = 0;
67	mem->stats.blocks_allocated = 0;
68	mem->stats.blocks_max = 0;
69	mem->stats.bytes = 0;
70	mem->stats.maxbytes = 0;
71
72	mem->orig_pool = mem;
73
74#ifdef DEBUG_POOL
75	log_debug("Created mempool %s", name);
76#endif
77
78	dm_list_add(&_dm_pools, &mem->list);
79	return mem;
80}
81
82static void _free_blocks(struct dm_pool *p, struct block *b)
83{
84	struct block *n;
85
86	while (b) {
87		p->stats.bytes -= b->size;
88		p->stats.blocks_allocated--;
89
90		n = b->next;
91		dm_free(b->data);
92		dm_free(b);
93		b = n;
94	}
95}
96
97static void _pool_stats(struct dm_pool *p, const char *action)
98{
99#ifdef DEBUG_POOL
100	log_debug("%s mempool %s: %u/%u bytes, %u/%u blocks, "
101		  "%u allocations)", action, p->name, p->stats.bytes,
102		  p->stats.maxbytes, p->stats.blocks_allocated,
103		  p->stats.blocks_max, p->stats.block_serialno);
104#else
105	;
106#endif
107}
108
109void dm_pool_destroy(struct dm_pool *p)
110{
111	_pool_stats(p, "Destroying");
112	_free_blocks(p, p->blocks);
113	dm_list_del(&p->list);
114	dm_free(p);
115}
116
117void *dm_pool_alloc(struct dm_pool *p, size_t s)
118{
119	return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
120}
121
122static void _append_block(struct dm_pool *p, struct block *b)
123{
124	if (p->tail) {
125		p->tail->next = b;
126		p->tail = b;
127	} else
128		p->blocks = p->tail = b;
129
130	p->stats.block_serialno++;
131	p->stats.blocks_allocated++;
132	if (p->stats.blocks_allocated > p->stats.blocks_max)
133		p->stats.blocks_max = p->stats.blocks_allocated;
134
135	p->stats.bytes += b->size;
136	if (p->stats.bytes > p->stats.maxbytes)
137		p->stats.maxbytes = p->stats.bytes;
138}
139
140static struct block *_new_block(size_t s, unsigned alignment)
141{
142	/* FIXME: I'm currently ignoring the alignment arg. */
143	size_t len = sizeof(struct block) + s;
144	struct block *b = dm_malloc(len);
145
146	/*
147	 * Too lazy to implement alignment for debug version, and
148	 * I don't think LVM will use anything but default
149	 * align.
150	 */
151	assert(alignment == DEFAULT_ALIGNMENT);
152
153	if (!b) {
154		log_error("Out of memory");
155		return NULL;
156	}
157
158	if (!(b->data = dm_malloc(s))) {
159		log_error("Out of memory");
160		dm_free(b);
161		return NULL;
162	}
163
164	b->next = NULL;
165	b->size = s;
166
167	return b;
168}
169
170void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
171{
172	struct block *b = _new_block(s, alignment);
173
174	if (!b)
175		return NULL;
176
177	_append_block(p, b);
178
179	return b->data;
180}
181
182void dm_pool_empty(struct dm_pool *p)
183{
184	_pool_stats(p, "Emptying");
185	_free_blocks(p, p->blocks);
186	p->blocks = p->tail = NULL;
187}
188
189void dm_pool_free(struct dm_pool *p, void *ptr)
190{
191	struct block *b, *prev = NULL;
192
193	_pool_stats(p, "Freeing (before)");
194
195	for (b = p->blocks; b; b = b->next) {
196		if (b->data == ptr)
197			break;
198		prev = b;
199	}
200
201	/*
202	 * If this fires then you tried to free a
203	 * pointer that either wasn't from this
204	 * pool, or isn't the start of a block.
205	 */
206	assert(b);
207
208	_free_blocks(p, b);
209
210	if (prev) {
211		p->tail = prev;
212		prev->next = NULL;
213	} else
214		p->blocks = p->tail = NULL;
215
216	_pool_stats(p, "Freeing (after)");
217}
218
219int dm_pool_begin_object(struct dm_pool *p, size_t init_size)
220{
221	assert(!p->begun);
222	p->begun = 1;
223	return 1;
224}
225
226int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
227{
228	struct block *new;
229	size_t new_size;
230
231	if (!delta)
232		delta = strlen(extra);
233
234	assert(p->begun);
235
236	if (p->object)
237		new_size = delta + p->object->size;
238	else
239		new_size = delta;
240
241	if (!(new = _new_block(new_size, DEFAULT_ALIGNMENT))) {
242		log_error("Couldn't extend object.");
243		return 0;
244	}
245
246	if (p->object) {
247		memcpy(new->data, p->object->data, p->object->size);
248		dm_free(p->object->data);
249		dm_free(p->object);
250	}
251	p->object = new;
252
253	memcpy(new->data + new_size - delta, extra, delta);
254
255	return 1;
256}
257
258void *dm_pool_end_object(struct dm_pool *p)
259{
260	assert(p->begun);
261	_append_block(p, p->object);
262
263	p->begun = 0;
264	p->object = NULL;
265	return p->tail->data;
266}
267
268void dm_pool_abandon_object(struct dm_pool *p)
269{
270	assert(p->begun);
271	dm_free(p->object);
272	p->begun = 0;
273	p->object = NULL;
274}
275