xref: /netbsd-src/external/gpl2/lvm2/dist/libdm/mm/pool-debug.c (revision 274254cdae52594c1aa480a736aef78313d15c9c)
1 /*	$NetBSD: pool-debug.c,v 1.1.1.1 2008/12/22 00:18:34 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 
20 struct block {
21 	struct block *next;
22 	size_t size;
23 	void *data;
24 };
25 
26 typedef struct {
27 	unsigned block_serialno;	/* Non-decreasing serialno of block */
28 	unsigned blocks_allocated;	/* Current number of blocks allocated */
29 	unsigned blocks_max;	/* Max no of concurrently-allocated blocks */
30 	unsigned int bytes, maxbytes;
31 } pool_stats;
32 
33 struct dm_pool {
34 	const char *name;
35 
36 	int begun;
37 	struct block *object;
38 
39 	struct block *blocks;
40 	struct block *tail;
41 
42 	pool_stats stats;
43 };
44 
45 /* by default things come out aligned for doubles */
46 #define DEFAULT_ALIGNMENT __alignof__ (double)
47 
48 struct pool *dm_pool_create(const char *name, size_t chunk_hint)
49 {
50 	struct dm_pool *mem = dm_malloc(sizeof(*mem));
51 
52 	if (!mem) {
53 		log_error("Couldn't create memory pool %s (size %"
54 			  PRIsize_t ")", name, sizeof(*mem));
55 		return NULL;
56 	}
57 
58 	mem->name = name;
59 	mem->begun = 0;
60 	mem->object = 0;
61 	mem->blocks = mem->tail = NULL;
62 
63 	mem->stats.block_serialno = 0;
64 	mem->stats.blocks_allocated = 0;
65 	mem->stats.blocks_max = 0;
66 	mem->stats.bytes = 0;
67 	mem->stats.maxbytes = 0;
68 
69 #ifdef DEBUG_POOL
70 	log_debug("Created mempool %s", name);
71 #endif
72 
73 	return mem;
74 }
75 
76 static void _free_blocks(struct dm_pool *p, struct block *b)
77 {
78 	struct block *n;
79 
80 	while (b) {
81 		p->stats.bytes -= b->size;
82 		p->stats.blocks_allocated--;
83 
84 		n = b->next;
85 		dm_free(b->data);
86 		dm_free(b);
87 		b = n;
88 	}
89 }
90 
91 static void _pool_stats(struct dm_pool *p, const char *action)
92 {
93 #ifdef DEBUG_POOL
94 	log_debug("%s mempool %s: %u/%u bytes, %u/%u blocks, "
95 		  "%u allocations)", action, p->name, p->stats.bytes,
96 		  p->stats.maxbytes, p->stats.blocks_allocated,
97 		  p->stats.blocks_max, p->stats.block_serialno);
98 #else
99 	;
100 #endif
101 }
102 
103 void dm_pool_destroy(struct dm_pool *p)
104 {
105 	_pool_stats(p, "Destroying");
106 	_free_blocks(p, p->blocks);
107 	dm_free(p);
108 }
109 
110 void *dm_pool_alloc(struct dm_pool *p, size_t s)
111 {
112 	return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
113 }
114 
115 static void _append_block(struct dm_pool *p, struct block *b)
116 {
117 	if (p->tail) {
118 		p->tail->next = b;
119 		p->tail = b;
120 	} else
121 		p->blocks = p->tail = b;
122 
123 	p->stats.block_serialno++;
124 	p->stats.blocks_allocated++;
125 	if (p->stats.blocks_allocated > p->stats.blocks_max)
126 		p->stats.blocks_max = p->stats.blocks_allocated;
127 
128 	p->stats.bytes += b->size;
129 	if (p->stats.bytes > p->stats.maxbytes)
130 		p->stats.maxbytes = p->stats.bytes;
131 }
132 
133 static struct block *_new_block(size_t s, unsigned alignment)
134 {
135 	static const char *_oom = "Out of memory";
136 
137 	/* FIXME: I'm currently ignoring the alignment arg. */
138 	size_t len = sizeof(struct block) + s;
139 	struct block *b = dm_malloc(len);
140 
141 	/*
142 	 * Too lazy to implement alignment for debug version, and
143 	 * I don't think LVM will use anything but default
144 	 * align.
145 	 */
146 	assert(alignment == DEFAULT_ALIGNMENT);
147 
148 	if (!b) {
149 		log_err(_oom);
150 		return NULL;
151 	}
152 
153 	if (!(b->data = dm_malloc(s))) {
154 		log_err(_oom);
155 		dm_free(b);
156 		return NULL;
157 	}
158 
159 	b->next = NULL;
160 	b->size = s;
161 
162 	return b;
163 }
164 
165 void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
166 {
167 	struct block *b = _new_block(s, alignment);
168 
169 	if (!b)
170 		return NULL;
171 
172 	_append_block(p, b);
173 
174 	return b->data;
175 }
176 
177 void dm_pool_empty(struct dm_pool *p)
178 {
179 	_pool_stats(p, "Emptying");
180 	_free_blocks(p, p->blocks);
181 	p->blocks = p->tail = NULL;
182 }
183 
184 void dm_pool_free(struct dm_pool *p, void *ptr)
185 {
186 	struct block *b, *prev = NULL;
187 
188 	_pool_stats(p, "Freeing (before)");
189 
190 	for (b = p->blocks; b; b = b->next) {
191 		if (b->data == ptr)
192 			break;
193 		prev = b;
194 	}
195 
196 	/*
197 	 * If this fires then you tried to free a
198 	 * pointer that either wasn't from this
199 	 * pool, or isn't the start of a block.
200 	 */
201 	assert(b);
202 
203 	_free_blocks(p, b);
204 
205 	if (prev) {
206 		p->tail = prev;
207 		prev->next = NULL;
208 	} else
209 		p->blocks = p->tail = NULL;
210 
211 	_pool_stats(p, "Freeing (after)");
212 }
213 
214 int dm_pool_begin_object(struct dm_pool *p, size_t init_size)
215 {
216 	assert(!p->begun);
217 	p->begun = 1;
218 	return 1;
219 }
220 
221 int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
222 {
223 	struct block *new;
224 	size_t size = delta ? : strlen(extra);
225 
226 	assert(p->begun);
227 
228 	if (p->object)
229 		size += p->object->size;
230 
231 	if (!(new = _new_block(size, DEFAULT_ALIGNMENT))) {
232 		log_err("Couldn't extend object.");
233 		return 0;
234 	}
235 
236 	if (p->object) {
237 		memcpy(new->data, p->object->data, p->object->size);
238 		dm_free(p->object->data);
239 		dm_free(p->object);
240 	}
241 	p->object = new;
242 
243 	memcpy(new->data + size - delta, extra, delta);
244 
245 	return 1;
246 }
247 
248 void *dm_pool_end_object(struct dm_pool *p)
249 {
250 	assert(p->begun);
251 	_append_block(p, p->object);
252 
253 	p->begun = 0;
254 	p->object = NULL;
255 	return p->tail->data;
256 }
257 
258 void dm_pool_abandon_object(struct dm_pool *p)
259 {
260 	assert(p->begun);
261 	dm_free(p->object);
262 	p->begun = 0;
263 	p->object = NULL;
264 }
265