1 : /*
2 : * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 : * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
4 : *
5 : * This file is part of the device-mapper userspace tools.
6 : *
7 : * This copyrighted material is made available to anyone wishing to use,
8 : * modify, copy, or redistribute it subject to the terms and conditions
9 : * of the GNU Lesser General Public License v.2.1.
10 : *
11 : * You should have received a copy of the GNU Lesser General Public License
12 : * along with this program; if not, write to the Free Software Foundation,
13 : * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 : */
15 :
16 : #include "dmlib.h"
17 :
18 : struct chunk {
19 : char *begin, *end;
20 : struct chunk *prev;
21 : };
22 :
23 : struct dm_pool {
24 : struct dm_list list;
25 : struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free
26 : list to stop 'bobbling' */
27 : size_t chunk_size;
28 : size_t object_len;
29 : unsigned object_alignment;
30 : };
31 :
32 : void _align_chunk(struct chunk *c, unsigned alignment);
33 : struct chunk *_new_chunk(struct dm_pool *p, size_t s);
34 :
35 : /* by default things come out aligned for doubles */
36 : #define DEFAULT_ALIGNMENT __alignof__ (double)
37 :
38 29 : struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
39 : {
40 29 : size_t new_size = 1024;
41 29 : struct dm_pool *p = dm_malloc(sizeof(*p));
42 :
43 29 : if (!p) {
44 0 : log_error("Couldn't create memory pool %s (size %"
45 : PRIsize_t ")", name, sizeof(*p));
46 0 : return 0;
47 : }
48 29 : memset(p, 0, sizeof(*p));
49 :
50 : /* round chunk_hint up to the next power of 2 */
51 29 : p->chunk_size = chunk_hint + sizeof(struct chunk);
52 151 : while (new_size < p->chunk_size)
53 93 : new_size <<= 1;
54 29 : p->chunk_size = new_size;
55 29 : dm_list_add(&_dm_pools, &p->list);
56 29 : return p;
57 : }
58 :
59 29 : void dm_pool_destroy(struct dm_pool *p)
60 : {
61 : struct chunk *c, *pr;
62 29 : dm_free(p->spare_chunk);
63 29 : c = p->chunk;
64 1773 : while (c) {
65 1715 : pr = c->prev;
66 1715 : dm_free(c);
67 1715 : c = pr;
68 : }
69 :
70 29 : dm_list_del(&p->list);
71 29 : dm_free(p);
72 29 : }
73 :
74 90399 : void *dm_pool_alloc(struct dm_pool *p, size_t s)
75 : {
76 90399 : return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
77 : }
78 :
79 90399 : void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
80 : {
81 90399 : struct chunk *c = p->chunk;
82 : void *r;
83 :
84 : /* realign begin */
85 90399 : if (c)
86 90370 : _align_chunk(c, alignment);
87 :
88 : /* have we got room ? */
89 90399 : if (!c || (c->begin > c->end) || (c->end - c->begin < s)) {
90 : /* allocate new chunk */
91 1716 : size_t needed = s + alignment + sizeof(struct chunk);
92 1716 : c = _new_chunk(p, (needed > p->chunk_size) ?
93 1716 : needed : p->chunk_size);
94 :
95 1716 : if (!c)
96 0 : return NULL;
97 :
98 1716 : _align_chunk(c, alignment);
99 : }
100 :
101 90399 : r = c->begin;
102 90399 : c->begin += s;
103 90399 : return r;
104 : }
105 :
106 1 : void dm_pool_empty(struct dm_pool *p)
107 : {
108 : struct chunk *c;
109 :
110 1 : for (c = p->chunk; c && c->prev; c = c->prev)
111 : ;
112 :
113 1 : if (c)
114 1 : dm_pool_free(p, (char *) (c + 1));
115 1 : }
116 :
117 1 : void dm_pool_free(struct dm_pool *p, void *ptr)
118 : {
119 1 : struct chunk *c = p->chunk;
120 :
121 4 : while (c) {
122 4 : if (((char *) c < (char *) ptr) &&
123 1 : ((char *) c->end > (char *) ptr)) {
124 1 : c->begin = ptr;
125 1 : break;
126 : }
127 :
128 2 : if (p->spare_chunk)
129 1 : dm_free(p->spare_chunk);
130 2 : p->spare_chunk = c;
131 2 : c = c->prev;
132 : }
133 :
134 1 : if (!c)
135 0 : log_error(INTERNAL_ERROR "pool_free asked to free pointer "
136 : "not in pool");
137 : else
138 1 : p->chunk = c;
139 1 : }
140 :
141 1 : int dm_pool_begin_object(struct dm_pool *p, size_t hint)
142 : {
143 1 : struct chunk *c = p->chunk;
144 1 : const size_t align = DEFAULT_ALIGNMENT;
145 :
146 1 : p->object_len = 0;
147 1 : p->object_alignment = align;
148 :
149 1 : if (c)
150 1 : _align_chunk(c, align);
151 :
152 1 : if (!c || (c->begin > c->end) || (c->end - c->begin < hint)) {
153 : /* allocate a new chunk */
154 0 : c = _new_chunk(p,
155 0 : hint > (p->chunk_size - sizeof(struct chunk)) ?
156 0 : hint + sizeof(struct chunk) + align :
157 : p->chunk_size);
158 :
159 0 : if (!c)
160 0 : return 0;
161 :
162 0 : _align_chunk(c, align);
163 : }
164 :
165 1 : return 1;
166 : }
167 :
168 10 : int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
169 : {
170 10 : struct chunk *c = p->chunk, *nc;
171 :
172 10 : if (!delta)
173 0 : delta = strlen(extra);
174 :
175 10 : if (c->end - (c->begin + p->object_len) < delta) {
176 : /* move into a new chunk */
177 1 : if (p->object_len + delta > (p->chunk_size / 2))
178 1 : nc = _new_chunk(p, (p->object_len + delta) * 2);
179 : else
180 0 : nc = _new_chunk(p, p->chunk_size);
181 :
182 1 : if (!nc)
183 0 : return 0;
184 :
185 1 : _align_chunk(p->chunk, p->object_alignment);
186 1 : memcpy(p->chunk->begin, c->begin, p->object_len);
187 1 : c = p->chunk;
188 : }
189 :
190 10 : memcpy(c->begin + p->object_len, extra, delta);
191 10 : p->object_len += delta;
192 10 : return 1;
193 : }
194 :
195 1 : void *dm_pool_end_object(struct dm_pool *p)
196 : {
197 1 : struct chunk *c = p->chunk;
198 1 : void *r = c->begin;
199 1 : c->begin += p->object_len;
200 1 : p->object_len = 0u;
201 1 : p->object_alignment = DEFAULT_ALIGNMENT;
202 1 : return r;
203 : }
204 :
205 0 : void dm_pool_abandon_object(struct dm_pool *p)
206 : {
207 0 : p->object_len = 0;
208 0 : p->object_alignment = DEFAULT_ALIGNMENT;
209 0 : }
210 :
211 92088 : void _align_chunk(struct chunk *c, unsigned alignment)
212 : {
213 92088 : c->begin += alignment - ((unsigned long) c->begin & (alignment - 1));
214 92088 : }
215 :
216 1717 : struct chunk *_new_chunk(struct dm_pool *p, size_t s)
217 : {
218 : struct chunk *c;
219 :
220 1717 : if (p->spare_chunk &&
221 0 : ((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) {
222 : /* reuse old chunk */
223 0 : c = p->spare_chunk;
224 0 : p->spare_chunk = 0;
225 : } else {
226 1717 : if (!(c = dm_malloc(s))) {
227 0 : log_error("Out of memory. Requested %" PRIsize_t
228 : " bytes.", s);
229 0 : return NULL;
230 : }
231 :
232 1717 : c->end = (char *) c + s;
233 : }
234 :
235 1717 : c->prev = p->chunk;
236 1717 : c->begin = (char *) (c + 1);
237 1717 : p->chunk = c;
238 :
239 1717 : return c;
240 : }
|