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
dm_pool_create(const char * name,size_t chunk_hint)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
_free_blocks(struct dm_pool * p,struct block * b)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
_pool_stats(struct dm_pool * p,const char * action)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
dm_pool_destroy(struct dm_pool * p)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
dm_pool_alloc(struct dm_pool * p,size_t s)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
_append_block(struct dm_pool * p,struct block * b)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
_new_block(size_t s,unsigned alignment)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
dm_pool_alloc_aligned(struct dm_pool * p,size_t s,unsigned alignment)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
dm_pool_empty(struct dm_pool * p)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
dm_pool_free(struct dm_pool * p,void * ptr)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
dm_pool_begin_object(struct dm_pool * p,size_t init_size)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
dm_pool_grow_object(struct dm_pool * p,const void * extra,size_t delta)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
dm_pool_end_object(struct dm_pool * p)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
dm_pool_abandon_object(struct dm_pool * p)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