/* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. * * This file is part of the device-mapper userspace tools. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License v.2.1. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libdm/misc/dmlib.h" #include <assert.h> struct block { struct block *next; size_t size; void *data; }; typedef struct { unsigned block_serialno; /* Non-decreasing serialno of block */ unsigned blocks_allocated; /* Current number of blocks allocated */ unsigned blocks_max; /* Max no of concurrently-allocated blocks */ unsigned int bytes, maxbytes; } pool_stats; struct dm_pool { struct dm_list list; const char *name; void *orig_pool; /* to pair it with first allocation call */ unsigned locked; long crc; int begun; struct block *object; struct block *blocks; struct block *tail; pool_stats stats; }; /* by default things come out aligned for doubles */ #define DEFAULT_ALIGNMENT __alignof__ (double) struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint) { struct dm_pool *mem = dm_zalloc(sizeof(*mem)); if (!mem) { log_error("Couldn't create memory pool %s (size %" PRIsize_t ")", name, sizeof(*mem)); return NULL; } mem->name = name; mem->orig_pool = mem; #ifdef DEBUG_POOL log_debug_mem("Created mempool %s at %p", name, mem); #endif dm_list_add(&_dm_pools, &mem->list); return mem; } static void _free_blocks(struct dm_pool *p, struct block *b) { struct block *n; if (p->locked) log_error(INTERNAL_ERROR "_free_blocks from locked pool %s", p->name); while (b) { p->stats.bytes -= b->size; p->stats.blocks_allocated--; n = b->next; dm_free(b->data); dm_free(b); b = n; } } static void _pool_stats(struct dm_pool *p, const char *action) { #ifdef DEBUG_POOL log_debug_mem("%s mempool %s at %p: %u/%u bytes, %u/%u blocks, " "%u allocations)", action, p->name, p, p->stats.bytes, p->stats.maxbytes, p->stats.blocks_allocated, p->stats.blocks_max, p->stats.block_serialno); #else ; #endif } void dm_pool_destroy(struct dm_pool *p) { _pool_stats(p, "Destroying"); _free_blocks(p, p->blocks); dm_list_del(&p->list); dm_free(p); } void *dm_pool_alloc(struct dm_pool *p, size_t s) { return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT); } static void _append_block(struct dm_pool *p, struct block *b) { if (p->locked) log_error(INTERNAL_ERROR "_append_blocks to locked pool %s", p->name); if (p->tail) { p->tail->next = b; p->tail = b; } else p->blocks = p->tail = b; p->stats.block_serialno++; p->stats.blocks_allocated++; if (p->stats.blocks_allocated > p->stats.blocks_max) p->stats.blocks_max = p->stats.blocks_allocated; p->stats.bytes += b->size; if (p->stats.bytes > p->stats.maxbytes) p->stats.maxbytes = p->stats.bytes; } static struct block *_new_block(size_t s, unsigned alignment) { /* FIXME: I'm currently ignoring the alignment arg. */ size_t len = sizeof(struct block) + s; struct block *b = dm_malloc(len); /* * Too lazy to implement alignment for debug version, and * I don't think LVM will use anything but default * align. */ assert(alignment <= DEFAULT_ALIGNMENT); if (!b) { log_error("Out of memory"); return NULL; } if (!(b->data = dm_malloc(s))) { log_error("Out of memory"); dm_free(b); return NULL; } b->next = NULL; b->size = s; return b; } void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment) { struct block *b = _new_block(s, alignment); if (!b) return_NULL; _append_block(p, b); return b->data; } void dm_pool_empty(struct dm_pool *p) { _pool_stats(p, "Emptying"); _free_blocks(p, p->blocks); p->blocks = p->tail = NULL; } void dm_pool_free(struct dm_pool *p, void *ptr) { struct block *b, *prev = NULL; _pool_stats(p, "Freeing (before)"); for (b = p->blocks; b; b = b->next) { if (b->data == ptr) break; prev = b; } /* * If this fires then you tried to free a * pointer that either wasn't from this * pool, or isn't the start of a block. */ assert(b); _free_blocks(p, b); if (prev) { p->tail = prev; prev->next = NULL; } else p->blocks = p->tail = NULL; _pool_stats(p, "Freeing (after)"); } int dm_pool_begin_object(struct dm_pool *p, size_t init_size) { assert(!p->begun); p->begun = 1; return 1; } int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta) { struct block *new; size_t new_size; if (p->locked) log_error(INTERNAL_ERROR "Grow objects in locked pool %s", p->name); if (!delta) delta = strlen(extra); assert(p->begun); if (p->object) new_size = delta + p->object->size; else new_size = delta; if (!(new = _new_block(new_size, DEFAULT_ALIGNMENT))) { log_error("Couldn't extend object."); return 0; } if (p->object) { memcpy(new->data, p->object->data, p->object->size); dm_free(p->object->data); dm_free(p->object); } p->object = new; memcpy((char*)new->data + new_size - delta, extra, delta); return 1; } void *dm_pool_end_object(struct dm_pool *p) { assert(p->begun); _append_block(p, p->object); p->begun = 0; p->object = NULL; return p->tail->data; } void dm_pool_abandon_object(struct dm_pool *p) { assert(p->begun); dm_free(p->object); p->begun = 0; p->object = NULL; } static long _pool_crc(const struct dm_pool *p) { #ifndef DEBUG_ENFORCE_POOL_LOCKING #warning pool crc not implemented with pool debug #endif return 0; } static int _pool_protect(struct dm_pool *p, int prot) { #ifdef DEBUG_ENFORCE_POOL_LOCKING #warning pool mprotect not implemented with pool debug #endif return 1; }