xref: /netbsd-src/external/gpl2/lvm2/dist/libdm/mm/pool-fast.c (revision 962766853c385b86328bab806c19ccdf4e22f287)
1 /*	$NetBSD: pool-fast.c,v 1.1.1.1 2008/12/22 00:18:35 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2006 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 chunk {
21 	char *begin, *end;
22 	struct chunk *prev;
23 };
24 
25 struct dm_pool {
26 	struct chunk *chunk, *spare_chunk;	/* spare_chunk is a one entry free
27 						   list to stop 'bobbling' */
28 	size_t chunk_size;
29 	size_t object_len;
30 	unsigned object_alignment;
31 };
32 
33 void _align_chunk(struct chunk *c, unsigned alignment);
34 struct chunk *_new_chunk(struct dm_pool *p, size_t s);
35 
36 /* by default things come out aligned for doubles */
37 #define DEFAULT_ALIGNMENT __alignof__ (double)
38 
39 struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
40 {
41 	size_t new_size = 1024;
42 	struct dm_pool *p = dm_malloc(sizeof(*p));
43 
44 	if (!p) {
45 		log_error("Couldn't create memory pool %s (size %"
46 			  PRIsize_t ")", name, sizeof(*p));
47 		return 0;
48 	}
49 	memset(p, 0, sizeof(*p));
50 
51 	/* round chunk_hint up to the next power of 2 */
52 	p->chunk_size = chunk_hint + sizeof(struct chunk);
53 	while (new_size < p->chunk_size)
54 		new_size <<= 1;
55 	p->chunk_size = new_size;
56 	return p;
57 }
58 
59 void dm_pool_destroy(struct dm_pool *p)
60 {
61 	struct chunk *c, *pr;
62 	dm_free(p->spare_chunk);
63 	c = p->chunk;
64 	while (c) {
65 		pr = c->prev;
66 		dm_free(c);
67 		c = pr;
68 	}
69 
70 	dm_free(p);
71 }
72 
73 void *dm_pool_alloc(struct dm_pool *p, size_t s)
74 {
75 	return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
76 }
77 
78 void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
79 {
80 	struct chunk *c = p->chunk;
81 	void *r;
82 
83 	/* realign begin */
84 	if (c)
85 		_align_chunk(c, alignment);
86 
87 	/* have we got room ? */
88 	if (!c || (c->begin > c->end) || (c->end - c->begin < s)) {
89 		/* allocate new chunk */
90 		size_t needed = s + alignment + sizeof(struct chunk);
91 		c = _new_chunk(p, (needed > p->chunk_size) ?
92 			       needed : p->chunk_size);
93 
94 		if (!c)
95 			return NULL;
96 
97 		_align_chunk(c, alignment);
98 	}
99 
100 	r = c->begin;
101 	c->begin += s;
102 	return r;
103 }
104 
105 void dm_pool_empty(struct dm_pool *p)
106 {
107 	struct chunk *c;
108 
109 	for (c = p->chunk; c && c->prev; c = c->prev)
110 		;
111 
112 	if (c)
113 		dm_pool_free(p, (char *) (c + 1));
114 }
115 
116 void dm_pool_free(struct dm_pool *p, void *ptr)
117 {
118 	struct chunk *c = p->chunk;
119 
120 	while (c) {
121 		if (((char *) c < (char *) ptr) &&
122 		    ((char *) c->end > (char *) ptr)) {
123 			c->begin = ptr;
124 			break;
125 		}
126 
127 		if (p->spare_chunk)
128 			dm_free(p->spare_chunk);
129 		p->spare_chunk = c;
130 		c = c->prev;
131 	}
132 
133 	if (!c)
134 		log_error("Internal error: pool_free asked to free pointer "
135 			  "not in pool");
136 	else
137 		p->chunk = c;
138 }
139 
140 int dm_pool_begin_object(struct dm_pool *p, size_t hint)
141 {
142 	struct chunk *c = p->chunk;
143 	const size_t align = DEFAULT_ALIGNMENT;
144 
145 	p->object_len = 0;
146 	p->object_alignment = align;
147 
148 	if (c)
149 		_align_chunk(c, align);
150 
151 	if (!c || (c->begin > c->end) || (c->end - c->begin < hint)) {
152 		/* allocate a new chunk */
153 		c = _new_chunk(p,
154 			       hint > (p->chunk_size - sizeof(struct chunk)) ?
155 			       hint + sizeof(struct chunk) + align :
156 			       p->chunk_size);
157 
158 		if (!c)
159 			return 0;
160 
161 		_align_chunk(c, align);
162 	}
163 
164 	return 1;
165 }
166 
167 int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
168 {
169 	struct chunk *c = p->chunk, *nc;
170 
171 	if (!delta)
172 		delta = strlen(extra);
173 
174 	if (c->end - (c->begin + p->object_len) < delta) {
175 		/* move into a new chunk */
176 		if (p->object_len + delta > (p->chunk_size / 2))
177 			nc = _new_chunk(p, (p->object_len + delta) * 2);
178 		else
179 			nc = _new_chunk(p, p->chunk_size);
180 
181 		if (!nc)
182 			return 0;
183 
184 		_align_chunk(p->chunk, p->object_alignment);
185 		memcpy(p->chunk->begin, c->begin, p->object_len);
186 		c = p->chunk;
187 	}
188 
189 	memcpy(c->begin + p->object_len, extra, delta);
190 	p->object_len += delta;
191 	return 1;
192 }
193 
194 void *dm_pool_end_object(struct dm_pool *p)
195 {
196 	struct chunk *c = p->chunk;
197 	void *r = c->begin;
198 	c->begin += p->object_len;
199 	p->object_len = 0u;
200 	p->object_alignment = DEFAULT_ALIGNMENT;
201 	return r;
202 }
203 
204 void dm_pool_abandon_object(struct dm_pool *p)
205 {
206 	p->object_len = 0;
207 	p->object_alignment = DEFAULT_ALIGNMENT;
208 }
209 
210 void _align_chunk(struct chunk *c, unsigned alignment)
211 {
212 	c->begin += alignment - ((unsigned long) c->begin & (alignment - 1));
213 }
214 
215 struct chunk *_new_chunk(struct dm_pool *p, size_t s)
216 {
217 	struct chunk *c;
218 
219 	if (p->spare_chunk &&
220 	    ((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) {
221 		/* reuse old chunk */
222 		c = p->spare_chunk;
223 		p->spare_chunk = 0;
224 	} else {
225 		if (!(c = dm_malloc(s))) {
226 			log_error("Out of memory.  Requested %" PRIsize_t
227 				  " bytes.", s);
228 			return NULL;
229 		}
230 
231 		c->end = (char *) c + s;
232 	}
233 
234 	c->prev = p->chunk;
235 	c->begin = (char *) (c + 1);
236 	p->chunk = c;
237 
238 	return c;
239 }
240