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 
21 struct block {
22 	struct block *next;
23 	size_t size;
24 	void *data;
25 };
26 
27 typedef 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 
34 struct 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 
51 struct 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 
82 static 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 
97 static 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 
109 void 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 
117 void *dm_pool_alloc(struct dm_pool *p, size_t s)
118 {
119 	return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
120 }
121 
122 static 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 
140 static 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 
170 void *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 
182 void 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 
189 void 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 
219 int 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 
226 int 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 
258 void *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 
268 void 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