xref: /dflybsd-src/contrib/lvm2/dist/libdm/mm/pool-fast.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /*	$NetBSD: pool-fast.c,v 1.1.1.2 2009/12/02 00:26:09 haad Exp $	*/
286d7f5d3SJohn Marino 
386d7f5d3SJohn Marino /*
486d7f5d3SJohn Marino  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
586d7f5d3SJohn Marino  * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
686d7f5d3SJohn Marino  *
786d7f5d3SJohn Marino  * This file is part of the device-mapper userspace tools.
886d7f5d3SJohn Marino  *
986d7f5d3SJohn Marino  * This copyrighted material is made available to anyone wishing to use,
1086d7f5d3SJohn Marino  * modify, copy, or redistribute it subject to the terms and conditions
1186d7f5d3SJohn Marino  * of the GNU Lesser General Public License v.2.1.
1286d7f5d3SJohn Marino  *
1386d7f5d3SJohn Marino  * You should have received a copy of the GNU Lesser General Public License
1486d7f5d3SJohn Marino  * along with this program; if not, write to the Free Software Foundation,
1586d7f5d3SJohn Marino  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1686d7f5d3SJohn Marino  */
1786d7f5d3SJohn Marino 
1886d7f5d3SJohn Marino #include "dmlib.h"
1986d7f5d3SJohn Marino 
2086d7f5d3SJohn Marino struct chunk {
2186d7f5d3SJohn Marino 	char *begin, *end;
2286d7f5d3SJohn Marino 	struct chunk *prev;
2386d7f5d3SJohn Marino };
2486d7f5d3SJohn Marino 
2586d7f5d3SJohn Marino struct dm_pool {
2686d7f5d3SJohn Marino 	struct dm_list list;
2786d7f5d3SJohn Marino 	struct chunk *chunk, *spare_chunk;	/* spare_chunk is a one entry free
2886d7f5d3SJohn Marino 						   list to stop 'bobbling' */
2986d7f5d3SJohn Marino 	size_t chunk_size;
3086d7f5d3SJohn Marino 	size_t object_len;
3186d7f5d3SJohn Marino 	unsigned object_alignment;
3286d7f5d3SJohn Marino };
3386d7f5d3SJohn Marino 
3486d7f5d3SJohn Marino void _align_chunk(struct chunk *c, unsigned alignment);
3586d7f5d3SJohn Marino struct chunk *_new_chunk(struct dm_pool *p, size_t s);
3686d7f5d3SJohn Marino 
3786d7f5d3SJohn Marino /* by default things come out aligned for doubles */
3886d7f5d3SJohn Marino #define DEFAULT_ALIGNMENT __alignof__ (double)
3986d7f5d3SJohn Marino 
dm_pool_create(const char * name,size_t chunk_hint)4086d7f5d3SJohn Marino struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
4186d7f5d3SJohn Marino {
4286d7f5d3SJohn Marino 	size_t new_size = 1024;
4386d7f5d3SJohn Marino 	struct dm_pool *p = dm_malloc(sizeof(*p));
4486d7f5d3SJohn Marino 
4586d7f5d3SJohn Marino 	if (!p) {
4686d7f5d3SJohn Marino 		log_error("Couldn't create memory pool %s (size %"
4786d7f5d3SJohn Marino 			  PRIsize_t ")", name, sizeof(*p));
4886d7f5d3SJohn Marino 		return 0;
4986d7f5d3SJohn Marino 	}
5086d7f5d3SJohn Marino 	memset(p, 0, sizeof(*p));
5186d7f5d3SJohn Marino 
5286d7f5d3SJohn Marino 	/* round chunk_hint up to the next power of 2 */
5386d7f5d3SJohn Marino 	p->chunk_size = chunk_hint + sizeof(struct chunk);
5486d7f5d3SJohn Marino 	while (new_size < p->chunk_size)
5586d7f5d3SJohn Marino 		new_size <<= 1;
5686d7f5d3SJohn Marino 	p->chunk_size = new_size;
5786d7f5d3SJohn Marino 	dm_list_add(&_dm_pools, &p->list);
5886d7f5d3SJohn Marino 	return p;
5986d7f5d3SJohn Marino }
6086d7f5d3SJohn Marino 
dm_pool_destroy(struct dm_pool * p)6186d7f5d3SJohn Marino void dm_pool_destroy(struct dm_pool *p)
6286d7f5d3SJohn Marino {
6386d7f5d3SJohn Marino 	struct chunk *c, *pr;
6486d7f5d3SJohn Marino 	dm_free(p->spare_chunk);
6586d7f5d3SJohn Marino 	c = p->chunk;
6686d7f5d3SJohn Marino 	while (c) {
6786d7f5d3SJohn Marino 		pr = c->prev;
6886d7f5d3SJohn Marino 		dm_free(c);
6986d7f5d3SJohn Marino 		c = pr;
7086d7f5d3SJohn Marino 	}
7186d7f5d3SJohn Marino 
7286d7f5d3SJohn Marino 	dm_list_del(&p->list);
7386d7f5d3SJohn Marino 	dm_free(p);
7486d7f5d3SJohn Marino }
7586d7f5d3SJohn Marino 
dm_pool_alloc(struct dm_pool * p,size_t s)7686d7f5d3SJohn Marino void *dm_pool_alloc(struct dm_pool *p, size_t s)
7786d7f5d3SJohn Marino {
7886d7f5d3SJohn Marino 	return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
7986d7f5d3SJohn Marino }
8086d7f5d3SJohn Marino 
dm_pool_alloc_aligned(struct dm_pool * p,size_t s,unsigned alignment)8186d7f5d3SJohn Marino void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
8286d7f5d3SJohn Marino {
8386d7f5d3SJohn Marino 	struct chunk *c = p->chunk;
8486d7f5d3SJohn Marino 	void *r;
8586d7f5d3SJohn Marino 
8686d7f5d3SJohn Marino 	/* realign begin */
8786d7f5d3SJohn Marino 	if (c)
8886d7f5d3SJohn Marino 		_align_chunk(c, alignment);
8986d7f5d3SJohn Marino 
9086d7f5d3SJohn Marino 	/* have we got room ? */
9186d7f5d3SJohn Marino 	if (!c || (c->begin > c->end) || (c->end - c->begin < s)) {
9286d7f5d3SJohn Marino 		/* allocate new chunk */
9386d7f5d3SJohn Marino 		size_t needed = s + alignment + sizeof(struct chunk);
9486d7f5d3SJohn Marino 		c = _new_chunk(p, (needed > p->chunk_size) ?
9586d7f5d3SJohn Marino 			       needed : p->chunk_size);
9686d7f5d3SJohn Marino 
9786d7f5d3SJohn Marino 		if (!c)
9886d7f5d3SJohn Marino 			return NULL;
9986d7f5d3SJohn Marino 
10086d7f5d3SJohn Marino 		_align_chunk(c, alignment);
10186d7f5d3SJohn Marino 	}
10286d7f5d3SJohn Marino 
10386d7f5d3SJohn Marino 	r = c->begin;
10486d7f5d3SJohn Marino 	c->begin += s;
10586d7f5d3SJohn Marino 	return r;
10686d7f5d3SJohn Marino }
10786d7f5d3SJohn Marino 
dm_pool_empty(struct dm_pool * p)10886d7f5d3SJohn Marino void dm_pool_empty(struct dm_pool *p)
10986d7f5d3SJohn Marino {
11086d7f5d3SJohn Marino 	struct chunk *c;
11186d7f5d3SJohn Marino 
11286d7f5d3SJohn Marino 	for (c = p->chunk; c && c->prev; c = c->prev)
11386d7f5d3SJohn Marino 		;
11486d7f5d3SJohn Marino 
11586d7f5d3SJohn Marino 	if (c)
11686d7f5d3SJohn Marino 		dm_pool_free(p, (char *) (c + 1));
11786d7f5d3SJohn Marino }
11886d7f5d3SJohn Marino 
dm_pool_free(struct dm_pool * p,void * ptr)11986d7f5d3SJohn Marino void dm_pool_free(struct dm_pool *p, void *ptr)
12086d7f5d3SJohn Marino {
12186d7f5d3SJohn Marino 	struct chunk *c = p->chunk;
12286d7f5d3SJohn Marino 
12386d7f5d3SJohn Marino 	while (c) {
12486d7f5d3SJohn Marino 		if (((char *) c < (char *) ptr) &&
12586d7f5d3SJohn Marino 		    ((char *) c->end > (char *) ptr)) {
12686d7f5d3SJohn Marino 			c->begin = ptr;
12786d7f5d3SJohn Marino 			break;
12886d7f5d3SJohn Marino 		}
12986d7f5d3SJohn Marino 
13086d7f5d3SJohn Marino 		if (p->spare_chunk)
13186d7f5d3SJohn Marino 			dm_free(p->spare_chunk);
13286d7f5d3SJohn Marino 		p->spare_chunk = c;
13386d7f5d3SJohn Marino 		c = c->prev;
13486d7f5d3SJohn Marino 	}
13586d7f5d3SJohn Marino 
13686d7f5d3SJohn Marino 	if (!c)
13786d7f5d3SJohn Marino 		log_error("Internal error: pool_free asked to free pointer "
13886d7f5d3SJohn Marino 			  "not in pool");
13986d7f5d3SJohn Marino 	else
14086d7f5d3SJohn Marino 		p->chunk = c;
14186d7f5d3SJohn Marino }
14286d7f5d3SJohn Marino 
dm_pool_begin_object(struct dm_pool * p,size_t hint)14386d7f5d3SJohn Marino int dm_pool_begin_object(struct dm_pool *p, size_t hint)
14486d7f5d3SJohn Marino {
14586d7f5d3SJohn Marino 	struct chunk *c = p->chunk;
14686d7f5d3SJohn Marino 	const size_t align = DEFAULT_ALIGNMENT;
14786d7f5d3SJohn Marino 
14886d7f5d3SJohn Marino 	p->object_len = 0;
14986d7f5d3SJohn Marino 	p->object_alignment = align;
15086d7f5d3SJohn Marino 
15186d7f5d3SJohn Marino 	if (c)
15286d7f5d3SJohn Marino 		_align_chunk(c, align);
15386d7f5d3SJohn Marino 
15486d7f5d3SJohn Marino 	if (!c || (c->begin > c->end) || (c->end - c->begin < hint)) {
15586d7f5d3SJohn Marino 		/* allocate a new chunk */
15686d7f5d3SJohn Marino 		c = _new_chunk(p,
15786d7f5d3SJohn Marino 			       hint > (p->chunk_size - sizeof(struct chunk)) ?
15886d7f5d3SJohn Marino 			       hint + sizeof(struct chunk) + align :
15986d7f5d3SJohn Marino 			       p->chunk_size);
16086d7f5d3SJohn Marino 
16186d7f5d3SJohn Marino 		if (!c)
16286d7f5d3SJohn Marino 			return 0;
16386d7f5d3SJohn Marino 
16486d7f5d3SJohn Marino 		_align_chunk(c, align);
16586d7f5d3SJohn Marino 	}
16686d7f5d3SJohn Marino 
16786d7f5d3SJohn Marino 	return 1;
16886d7f5d3SJohn Marino }
16986d7f5d3SJohn Marino 
dm_pool_grow_object(struct dm_pool * p,const void * extra,size_t delta)17086d7f5d3SJohn Marino int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
17186d7f5d3SJohn Marino {
17286d7f5d3SJohn Marino 	struct chunk *c = p->chunk, *nc;
17386d7f5d3SJohn Marino 
17486d7f5d3SJohn Marino 	if (!delta)
17586d7f5d3SJohn Marino 		delta = strlen(extra);
17686d7f5d3SJohn Marino 
17786d7f5d3SJohn Marino 	if (c->end - (c->begin + p->object_len) < delta) {
17886d7f5d3SJohn Marino 		/* move into a new chunk */
17986d7f5d3SJohn Marino 		if (p->object_len + delta > (p->chunk_size / 2))
18086d7f5d3SJohn Marino 			nc = _new_chunk(p, (p->object_len + delta) * 2);
18186d7f5d3SJohn Marino 		else
18286d7f5d3SJohn Marino 			nc = _new_chunk(p, p->chunk_size);
18386d7f5d3SJohn Marino 
18486d7f5d3SJohn Marino 		if (!nc)
18586d7f5d3SJohn Marino 			return 0;
18686d7f5d3SJohn Marino 
18786d7f5d3SJohn Marino 		_align_chunk(p->chunk, p->object_alignment);
18886d7f5d3SJohn Marino 		memcpy(p->chunk->begin, c->begin, p->object_len);
18986d7f5d3SJohn Marino 		c = p->chunk;
19086d7f5d3SJohn Marino 	}
19186d7f5d3SJohn Marino 
19286d7f5d3SJohn Marino 	memcpy(c->begin + p->object_len, extra, delta);
19386d7f5d3SJohn Marino 	p->object_len += delta;
19486d7f5d3SJohn Marino 	return 1;
19586d7f5d3SJohn Marino }
19686d7f5d3SJohn Marino 
dm_pool_end_object(struct dm_pool * p)19786d7f5d3SJohn Marino void *dm_pool_end_object(struct dm_pool *p)
19886d7f5d3SJohn Marino {
19986d7f5d3SJohn Marino 	struct chunk *c = p->chunk;
20086d7f5d3SJohn Marino 	void *r = c->begin;
20186d7f5d3SJohn Marino 	c->begin += p->object_len;
20286d7f5d3SJohn Marino 	p->object_len = 0u;
20386d7f5d3SJohn Marino 	p->object_alignment = DEFAULT_ALIGNMENT;
20486d7f5d3SJohn Marino 	return r;
20586d7f5d3SJohn Marino }
20686d7f5d3SJohn Marino 
dm_pool_abandon_object(struct dm_pool * p)20786d7f5d3SJohn Marino void dm_pool_abandon_object(struct dm_pool *p)
20886d7f5d3SJohn Marino {
20986d7f5d3SJohn Marino 	p->object_len = 0;
21086d7f5d3SJohn Marino 	p->object_alignment = DEFAULT_ALIGNMENT;
21186d7f5d3SJohn Marino }
21286d7f5d3SJohn Marino 
_align_chunk(struct chunk * c,unsigned alignment)21386d7f5d3SJohn Marino void _align_chunk(struct chunk *c, unsigned alignment)
21486d7f5d3SJohn Marino {
21586d7f5d3SJohn Marino 	c->begin += alignment - ((unsigned long) c->begin & (alignment - 1));
21686d7f5d3SJohn Marino }
21786d7f5d3SJohn Marino 
_new_chunk(struct dm_pool * p,size_t s)21886d7f5d3SJohn Marino struct chunk *_new_chunk(struct dm_pool *p, size_t s)
21986d7f5d3SJohn Marino {
22086d7f5d3SJohn Marino 	struct chunk *c;
22186d7f5d3SJohn Marino 
22286d7f5d3SJohn Marino 	if (p->spare_chunk &&
22386d7f5d3SJohn Marino 	    ((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) {
22486d7f5d3SJohn Marino 		/* reuse old chunk */
22586d7f5d3SJohn Marino 		c = p->spare_chunk;
22686d7f5d3SJohn Marino 		p->spare_chunk = 0;
22786d7f5d3SJohn Marino 	} else {
22886d7f5d3SJohn Marino 		if (!(c = dm_malloc(s))) {
22986d7f5d3SJohn Marino 			log_error("Out of memory.  Requested %" PRIsize_t
23086d7f5d3SJohn Marino 				  " bytes.", s);
23186d7f5d3SJohn Marino 			return NULL;
23286d7f5d3SJohn Marino 		}
23386d7f5d3SJohn Marino 
23486d7f5d3SJohn Marino 		c->end = (char *) c + s;
23586d7f5d3SJohn Marino 	}
23686d7f5d3SJohn Marino 
23786d7f5d3SJohn Marino 	c->prev = p->chunk;
23886d7f5d3SJohn Marino 	c->begin = (char *) (c + 1);
23986d7f5d3SJohn Marino 	p->chunk = c;
24086d7f5d3SJohn Marino 
24186d7f5d3SJohn Marino 	return c;
24286d7f5d3SJohn Marino }
243