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