2 * libqos malloc support
7 * John Snow <jsnow@redhat.com>
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
13 #include "qemu/osdep.h"
14 #include "libqos/malloc.h"
15 #include "qemu-common.h"
16 #include "qemu/host-utils.h"
18 typedef struct MemBlock
{
19 QTAILQ_ENTRY(MemBlock
) MLIST_ENTNAME
;
24 #define DEFAULT_PAGE_SIZE 4096
26 static void mlist_delete(MemList
*list
, MemBlock
*node
)
28 g_assert(list
&& node
);
29 QTAILQ_REMOVE(list
, node
, MLIST_ENTNAME
);
33 static MemBlock
*mlist_find_key(MemList
*head
, uint64_t addr
)
36 QTAILQ_FOREACH(node
, head
, MLIST_ENTNAME
) {
37 if (node
->addr
== addr
) {
44 static MemBlock
*mlist_find_space(MemList
*head
, uint64_t size
)
48 QTAILQ_FOREACH(node
, head
, MLIST_ENTNAME
) {
49 if (node
->size
>= size
) {
56 static MemBlock
*mlist_sort_insert(MemList
*head
, MemBlock
*insr
)
59 g_assert(head
&& insr
);
61 QTAILQ_FOREACH(node
, head
, MLIST_ENTNAME
) {
62 if (insr
->addr
< node
->addr
) {
63 QTAILQ_INSERT_BEFORE(node
, insr
, MLIST_ENTNAME
);
68 QTAILQ_INSERT_TAIL(head
, insr
, MLIST_ENTNAME
);
72 static inline uint64_t mlist_boundary(MemBlock
*node
)
74 return node
->size
+ node
->addr
;
77 static MemBlock
*mlist_join(MemList
*head
, MemBlock
*left
, MemBlock
*right
)
79 g_assert(head
&& left
&& right
);
81 left
->size
+= right
->size
;
82 mlist_delete(head
, right
);
86 static void mlist_coalesce(MemList
*head
, MemBlock
*node
)
95 left
= QTAILQ_PREV(node
, MLIST_ENTNAME
);
96 right
= QTAILQ_NEXT(node
, MLIST_ENTNAME
);
98 /* clowns to the left of me */
99 if (left
&& mlist_boundary(left
) == node
->addr
) {
100 node
= mlist_join(head
, left
, node
);
104 /* jokers to the right */
105 if (right
&& mlist_boundary(node
) == right
->addr
) {
106 node
= mlist_join(head
, node
, right
);
113 static MemBlock
*mlist_new(uint64_t addr
, uint64_t size
)
120 block
= g_new0(MemBlock
, 1);
128 static uint64_t mlist_fulfill(QGuestAllocator
*s
, MemBlock
*freenode
,
135 g_assert_cmpint(freenode
->size
, >=, size
);
137 addr
= freenode
->addr
;
138 if (freenode
->size
== size
) {
139 /* re-use this freenode as our used node */
140 QTAILQ_REMOVE(s
->free
, freenode
, MLIST_ENTNAME
);
143 /* adjust the free node and create a new used node */
144 freenode
->addr
+= size
;
145 freenode
->size
-= size
;
146 usednode
= mlist_new(addr
, size
);
149 mlist_sort_insert(s
->used
, usednode
);
153 /* To assert the correctness of the list.
154 * Used only if ALLOC_PARANOID is set. */
155 static void mlist_check(QGuestAllocator
*s
)
158 uint64_t addr
= s
->start
> 0 ? s
->start
- 1 : 0;
159 uint64_t next
= s
->start
;
161 QTAILQ_FOREACH(node
, s
->free
, MLIST_ENTNAME
) {
162 g_assert_cmpint(node
->addr
, >, addr
);
163 g_assert_cmpint(node
->addr
, >=, next
);
165 next
= node
->addr
+ node
->size
;
168 addr
= s
->start
> 0 ? s
->start
- 1 : 0;
170 QTAILQ_FOREACH(node
, s
->used
, MLIST_ENTNAME
) {
171 g_assert_cmpint(node
->addr
, >, addr
);
172 g_assert_cmpint(node
->addr
, >=, next
);
174 next
= node
->addr
+ node
->size
;
178 static uint64_t mlist_alloc(QGuestAllocator
*s
, uint64_t size
)
182 node
= mlist_find_space(s
->free
, size
);
184 fprintf(stderr
, "Out of guest memory.\n");
185 g_assert_not_reached();
187 return mlist_fulfill(s
, node
, size
);
190 static void mlist_free(QGuestAllocator
*s
, uint64_t addr
)
198 node
= mlist_find_key(s
->used
, addr
);
200 fprintf(stderr
, "Error: no record found for an allocation at "
201 "0x%016" PRIx64
".\n",
203 g_assert_not_reached();
206 /* Rip it out of the used list and re-insert back into the free list. */
207 QTAILQ_REMOVE(s
->used
, node
, MLIST_ENTNAME
);
208 mlist_sort_insert(s
->free
, node
);
209 mlist_coalesce(s
->free
, node
);
213 * Mostly for valgrind happiness, but it does offer
214 * a chokepoint for debugging guest memory leaks, too.
216 void alloc_destroy(QGuestAllocator
*allocator
)
222 /* Check for guest leaks, and destroy the list. */
223 QTAILQ_FOREACH_SAFE(node
, allocator
->used
, MLIST_ENTNAME
, tmp
) {
224 if (allocator
->opts
& (ALLOC_LEAK_WARN
| ALLOC_LEAK_ASSERT
)) {
225 fprintf(stderr
, "guest malloc leak @ 0x%016" PRIx64
"; "
226 "size 0x%016" PRIx64
".\n",
227 node
->addr
, node
->size
);
229 if (allocator
->opts
& (ALLOC_LEAK_ASSERT
)) {
230 g_assert_not_reached();
235 /* If we have previously asserted that there are no leaks, then there
236 * should be only one node here with a specific address and size. */
237 mask
= ALLOC_LEAK_ASSERT
| ALLOC_PARANOID
;
238 QTAILQ_FOREACH_SAFE(node
, allocator
->free
, MLIST_ENTNAME
, tmp
) {
239 if ((allocator
->opts
& mask
) == mask
) {
240 if ((node
->addr
!= allocator
->start
) ||
241 (node
->size
!= allocator
->end
- allocator
->start
)) {
242 fprintf(stderr
, "Free list is corrupted.\n");
243 g_assert_not_reached();
250 g_free(allocator
->used
);
251 g_free(allocator
->free
);
254 uint64_t guest_alloc(QGuestAllocator
*allocator
, size_t size
)
256 uint64_t rsize
= size
;
263 rsize
+= (allocator
->page_size
- 1);
264 rsize
&= -allocator
->page_size
;
265 g_assert_cmpint((allocator
->start
+ rsize
), <=, allocator
->end
);
266 g_assert_cmpint(rsize
, >=, size
);
268 naddr
= mlist_alloc(allocator
, rsize
);
269 if (allocator
->opts
& ALLOC_PARANOID
) {
270 mlist_check(allocator
);
276 void guest_free(QGuestAllocator
*allocator
, uint64_t addr
)
281 mlist_free(allocator
, addr
);
282 if (allocator
->opts
& ALLOC_PARANOID
) {
283 mlist_check(allocator
);
287 void alloc_init(QGuestAllocator
*s
, QAllocOpts opts
,
288 uint64_t start
, uint64_t end
,
297 s
->used
= g_new(MemList
, 1);
298 s
->free
= g_new(MemList
, 1);
299 QTAILQ_INIT(s
->used
);
300 QTAILQ_INIT(s
->free
);
302 node
= mlist_new(s
->start
, s
->end
- s
->start
);
303 QTAILQ_INSERT_HEAD(s
->free
, node
, MLIST_ENTNAME
);
305 s
->page_size
= page_size
;
308 void alloc_set_flags(QGuestAllocator
*allocator
, QAllocOpts opts
)
310 allocator
->opts
|= opts
;
313 void migrate_allocator(QGuestAllocator
*src
,
314 QGuestAllocator
*dst
)
316 MemBlock
*node
, *tmp
;
317 MemList
*tmpused
, *tmpfree
;
319 /* The general memory layout should be equivalent,
320 * though opts can differ. */
321 g_assert_cmphex(src
->start
, ==, dst
->start
);
322 g_assert_cmphex(src
->end
, ==, dst
->end
);
324 /* Destroy (silently, regardless of options) the dest-list: */
325 QTAILQ_FOREACH_SAFE(node
, dst
->used
, MLIST_ENTNAME
, tmp
) {
328 QTAILQ_FOREACH_SAFE(node
, dst
->free
, MLIST_ENTNAME
, tmp
) {
335 /* Inherit the lists of the source allocator: */
336 dst
->used
= src
->used
;
337 dst
->free
= src
->free
;
339 /* Source is now re-initialized, the source memory is 'invalid' now: */
342 QTAILQ_INIT(src
->used
);
343 QTAILQ_INIT(src
->free
);
344 node
= mlist_new(src
->start
, src
->end
- src
->start
);
345 QTAILQ_INSERT_HEAD(src
->free
, node
, MLIST_ENTNAME
);