1292be092SMarc Marí /* 2292be092SMarc Marí * libqos malloc support 3292be092SMarc Marí * 4292be092SMarc Marí * Copyright (c) 2014 5292be092SMarc Marí * 6292be092SMarc Marí * Author: 7292be092SMarc Marí * John Snow <jsnow@redhat.com> 8292be092SMarc Marí * 9292be092SMarc Marí * This work is licensed under the terms of the GNU GPL, version 2 or later. 10292be092SMarc Marí * See the COPYING file in the top-level directory. 11292be092SMarc Marí */ 12292be092SMarc Marí 13681c28a3SPeter Maydell #include "qemu/osdep.h" 14292be092SMarc Marí #include "libqos/malloc.h" 15292be092SMarc Marí #include "qemu-common.h" 1687776ab7SPaolo Bonzini #include "qemu/host-utils.h" 17292be092SMarc Marí 18f6f363c1SJohn Snow typedef struct MemBlock { 19f6f363c1SJohn Snow QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME; 20f6f363c1SJohn Snow uint64_t size; 21f6f363c1SJohn Snow uint64_t addr; 22f6f363c1SJohn Snow } MemBlock; 23f6f363c1SJohn Snow 24f6f363c1SJohn Snow #define DEFAULT_PAGE_SIZE 4096 25f6f363c1SJohn Snow 26292be092SMarc Marí static void mlist_delete(MemList *list, MemBlock *node) 27292be092SMarc Marí { 28292be092SMarc Marí g_assert(list && node); 29292be092SMarc Marí QTAILQ_REMOVE(list, node, MLIST_ENTNAME); 30292be092SMarc Marí g_free(node); 31292be092SMarc Marí } 32292be092SMarc Marí 33292be092SMarc Marí static MemBlock *mlist_find_key(MemList *head, uint64_t addr) 34292be092SMarc Marí { 35292be092SMarc Marí MemBlock *node; 36292be092SMarc Marí QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { 37292be092SMarc Marí if (node->addr == addr) { 38292be092SMarc Marí return node; 39292be092SMarc Marí } 40292be092SMarc Marí } 41292be092SMarc Marí return NULL; 42292be092SMarc Marí } 43292be092SMarc Marí 44292be092SMarc Marí static MemBlock *mlist_find_space(MemList *head, uint64_t size) 45292be092SMarc Marí { 46292be092SMarc Marí MemBlock *node; 47292be092SMarc Marí 48292be092SMarc Marí QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { 49292be092SMarc Marí if (node->size >= size) { 50292be092SMarc Marí return node; 51292be092SMarc Marí } 52292be092SMarc Marí } 53292be092SMarc Marí return NULL; 54292be092SMarc Marí } 55292be092SMarc Marí 56292be092SMarc Marí static MemBlock *mlist_sort_insert(MemList *head, MemBlock *insr) 57292be092SMarc Marí { 58292be092SMarc Marí MemBlock *node; 59292be092SMarc Marí g_assert(head && insr); 60292be092SMarc Marí 61292be092SMarc Marí QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { 62292be092SMarc Marí if (insr->addr < node->addr) { 63292be092SMarc Marí QTAILQ_INSERT_BEFORE(node, insr, MLIST_ENTNAME); 64292be092SMarc Marí return insr; 65292be092SMarc Marí } 66292be092SMarc Marí } 67292be092SMarc Marí 68292be092SMarc Marí QTAILQ_INSERT_TAIL(head, insr, MLIST_ENTNAME); 69292be092SMarc Marí return insr; 70292be092SMarc Marí } 71292be092SMarc Marí 72292be092SMarc Marí static inline uint64_t mlist_boundary(MemBlock *node) 73292be092SMarc Marí { 74292be092SMarc Marí return node->size + node->addr; 75292be092SMarc Marí } 76292be092SMarc Marí 77292be092SMarc Marí static MemBlock *mlist_join(MemList *head, MemBlock *left, MemBlock *right) 78292be092SMarc Marí { 79292be092SMarc Marí g_assert(head && left && right); 80292be092SMarc Marí 81292be092SMarc Marí left->size += right->size; 82292be092SMarc Marí mlist_delete(head, right); 83292be092SMarc Marí return left; 84292be092SMarc Marí } 85292be092SMarc Marí 86292be092SMarc Marí static void mlist_coalesce(MemList *head, MemBlock *node) 87292be092SMarc Marí { 88292be092SMarc Marí g_assert(node); 89292be092SMarc Marí MemBlock *left; 90292be092SMarc Marí MemBlock *right; 91292be092SMarc Marí char merge; 92292be092SMarc Marí 93292be092SMarc Marí do { 94292be092SMarc Marí merge = 0; 95eae3eb3eSPaolo Bonzini left = QTAILQ_PREV(node, MLIST_ENTNAME); 96292be092SMarc Marí right = QTAILQ_NEXT(node, MLIST_ENTNAME); 97292be092SMarc Marí 98292be092SMarc Marí /* clowns to the left of me */ 99292be092SMarc Marí if (left && mlist_boundary(left) == node->addr) { 100292be092SMarc Marí node = mlist_join(head, left, node); 101292be092SMarc Marí merge = 1; 102292be092SMarc Marí } 103292be092SMarc Marí 104292be092SMarc Marí /* jokers to the right */ 105292be092SMarc Marí if (right && mlist_boundary(node) == right->addr) { 106292be092SMarc Marí node = mlist_join(head, node, right); 107292be092SMarc Marí merge = 1; 108292be092SMarc Marí } 109292be092SMarc Marí 110292be092SMarc Marí } while (merge); 111292be092SMarc Marí } 112292be092SMarc Marí 113f6f363c1SJohn Snow static MemBlock *mlist_new(uint64_t addr, uint64_t size) 114f6f363c1SJohn Snow { 115f6f363c1SJohn Snow MemBlock *block; 116f6f363c1SJohn Snow 117f6f363c1SJohn Snow if (!size) { 118f6f363c1SJohn Snow return NULL; 119f6f363c1SJohn Snow } 120790bbb97SMarc-André Lureau block = g_new0(MemBlock, 1); 121f6f363c1SJohn Snow 122f6f363c1SJohn Snow block->addr = addr; 123f6f363c1SJohn Snow block->size = size; 124f6f363c1SJohn Snow 125f6f363c1SJohn Snow return block; 126f6f363c1SJohn Snow } 127f6f363c1SJohn Snow 128292be092SMarc Marí static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode, 129292be092SMarc Marí uint64_t size) 130292be092SMarc Marí { 131292be092SMarc Marí uint64_t addr; 132292be092SMarc Marí MemBlock *usednode; 133292be092SMarc Marí 134292be092SMarc Marí g_assert(freenode); 135292be092SMarc Marí g_assert_cmpint(freenode->size, >=, size); 136292be092SMarc Marí 137292be092SMarc Marí addr = freenode->addr; 138292be092SMarc Marí if (freenode->size == size) { 139292be092SMarc Marí /* re-use this freenode as our used node */ 140085248aeSJohn Snow QTAILQ_REMOVE(s->free, freenode, MLIST_ENTNAME); 141292be092SMarc Marí usednode = freenode; 142292be092SMarc Marí } else { 143292be092SMarc Marí /* adjust the free node and create a new used node */ 144292be092SMarc Marí freenode->addr += size; 145292be092SMarc Marí freenode->size -= size; 146292be092SMarc Marí usednode = mlist_new(addr, size); 147292be092SMarc Marí } 148292be092SMarc Marí 149085248aeSJohn Snow mlist_sort_insert(s->used, usednode); 150292be092SMarc Marí return addr; 151292be092SMarc Marí } 152292be092SMarc Marí 153292be092SMarc Marí /* To assert the correctness of the list. 154292be092SMarc Marí * Used only if ALLOC_PARANOID is set. */ 155292be092SMarc Marí static void mlist_check(QGuestAllocator *s) 156292be092SMarc Marí { 157292be092SMarc Marí MemBlock *node; 158292be092SMarc Marí uint64_t addr = s->start > 0 ? s->start - 1 : 0; 159292be092SMarc Marí uint64_t next = s->start; 160292be092SMarc Marí 161085248aeSJohn Snow QTAILQ_FOREACH(node, s->free, MLIST_ENTNAME) { 162292be092SMarc Marí g_assert_cmpint(node->addr, >, addr); 163292be092SMarc Marí g_assert_cmpint(node->addr, >=, next); 164292be092SMarc Marí addr = node->addr; 165292be092SMarc Marí next = node->addr + node->size; 166292be092SMarc Marí } 167292be092SMarc Marí 168292be092SMarc Marí addr = s->start > 0 ? s->start - 1 : 0; 169292be092SMarc Marí next = s->start; 170085248aeSJohn Snow QTAILQ_FOREACH(node, s->used, MLIST_ENTNAME) { 171292be092SMarc Marí g_assert_cmpint(node->addr, >, addr); 172292be092SMarc Marí g_assert_cmpint(node->addr, >=, next); 173292be092SMarc Marí addr = node->addr; 174292be092SMarc Marí next = node->addr + node->size; 175292be092SMarc Marí } 176292be092SMarc Marí } 177292be092SMarc Marí 178292be092SMarc Marí static uint64_t mlist_alloc(QGuestAllocator *s, uint64_t size) 179292be092SMarc Marí { 180292be092SMarc Marí MemBlock *node; 181292be092SMarc Marí 182085248aeSJohn Snow node = mlist_find_space(s->free, size); 183292be092SMarc Marí if (!node) { 184292be092SMarc Marí fprintf(stderr, "Out of guest memory.\n"); 185292be092SMarc Marí g_assert_not_reached(); 186292be092SMarc Marí } 187292be092SMarc Marí return mlist_fulfill(s, node, size); 188292be092SMarc Marí } 189292be092SMarc Marí 190292be092SMarc Marí static void mlist_free(QGuestAllocator *s, uint64_t addr) 191292be092SMarc Marí { 192292be092SMarc Marí MemBlock *node; 193292be092SMarc Marí 194292be092SMarc Marí if (addr == 0) { 195292be092SMarc Marí return; 196292be092SMarc Marí } 197292be092SMarc Marí 198085248aeSJohn Snow node = mlist_find_key(s->used, addr); 199292be092SMarc Marí if (!node) { 200292be092SMarc Marí fprintf(stderr, "Error: no record found for an allocation at " 201292be092SMarc Marí "0x%016" PRIx64 ".\n", 202292be092SMarc Marí addr); 203292be092SMarc Marí g_assert_not_reached(); 204292be092SMarc Marí } 205292be092SMarc Marí 206292be092SMarc Marí /* Rip it out of the used list and re-insert back into the free list. */ 207085248aeSJohn Snow QTAILQ_REMOVE(s->used, node, MLIST_ENTNAME); 208085248aeSJohn Snow mlist_sort_insert(s->free, node); 209085248aeSJohn Snow mlist_coalesce(s->free, node); 210292be092SMarc Marí } 211292be092SMarc Marí 212292be092SMarc Marí /* 213292be092SMarc Marí * Mostly for valgrind happiness, but it does offer 214292be092SMarc Marí * a chokepoint for debugging guest memory leaks, too. 215292be092SMarc Marí */ 216*eb5937baSPaolo Bonzini void alloc_destroy(QGuestAllocator *allocator) 217292be092SMarc Marí { 218292be092SMarc Marí MemBlock *node; 219292be092SMarc Marí MemBlock *tmp; 220292be092SMarc Marí QAllocOpts mask; 221292be092SMarc Marí 222292be092SMarc Marí /* Check for guest leaks, and destroy the list. */ 223085248aeSJohn Snow QTAILQ_FOREACH_SAFE(node, allocator->used, MLIST_ENTNAME, tmp) { 224292be092SMarc Marí if (allocator->opts & (ALLOC_LEAK_WARN | ALLOC_LEAK_ASSERT)) { 225292be092SMarc Marí fprintf(stderr, "guest malloc leak @ 0x%016" PRIx64 "; " 226292be092SMarc Marí "size 0x%016" PRIx64 ".\n", 227292be092SMarc Marí node->addr, node->size); 228292be092SMarc Marí } 229292be092SMarc Marí if (allocator->opts & (ALLOC_LEAK_ASSERT)) { 230292be092SMarc Marí g_assert_not_reached(); 231292be092SMarc Marí } 232292be092SMarc Marí g_free(node); 233292be092SMarc Marí } 234292be092SMarc Marí 235292be092SMarc Marí /* If we have previously asserted that there are no leaks, then there 236292be092SMarc Marí * should be only one node here with a specific address and size. */ 237292be092SMarc Marí mask = ALLOC_LEAK_ASSERT | ALLOC_PARANOID; 238085248aeSJohn Snow QTAILQ_FOREACH_SAFE(node, allocator->free, MLIST_ENTNAME, tmp) { 239292be092SMarc Marí if ((allocator->opts & mask) == mask) { 240292be092SMarc Marí if ((node->addr != allocator->start) || 241292be092SMarc Marí (node->size != allocator->end - allocator->start)) { 242292be092SMarc Marí fprintf(stderr, "Free list is corrupted.\n"); 243292be092SMarc Marí g_assert_not_reached(); 244292be092SMarc Marí } 245292be092SMarc Marí } 246292be092SMarc Marí 247292be092SMarc Marí g_free(node); 248292be092SMarc Marí } 249292be092SMarc Marí 250085248aeSJohn Snow g_free(allocator->used); 251085248aeSJohn Snow g_free(allocator->free); 252292be092SMarc Marí } 253292be092SMarc Marí 254292be092SMarc Marí uint64_t guest_alloc(QGuestAllocator *allocator, size_t size) 255292be092SMarc Marí { 256292be092SMarc Marí uint64_t rsize = size; 257292be092SMarc Marí uint64_t naddr; 258292be092SMarc Marí 259b1b66c3bSJohn Snow if (!size) { 260b1b66c3bSJohn Snow return 0; 261b1b66c3bSJohn Snow } 262b1b66c3bSJohn Snow 263292be092SMarc Marí rsize += (allocator->page_size - 1); 264292be092SMarc Marí rsize &= -allocator->page_size; 265292be092SMarc Marí g_assert_cmpint((allocator->start + rsize), <=, allocator->end); 266292be092SMarc Marí g_assert_cmpint(rsize, >=, size); 267292be092SMarc Marí 268292be092SMarc Marí naddr = mlist_alloc(allocator, rsize); 269292be092SMarc Marí if (allocator->opts & ALLOC_PARANOID) { 270292be092SMarc Marí mlist_check(allocator); 271292be092SMarc Marí } 272292be092SMarc Marí 273292be092SMarc Marí return naddr; 274292be092SMarc Marí } 275292be092SMarc Marí 276292be092SMarc Marí void guest_free(QGuestAllocator *allocator, uint64_t addr) 277292be092SMarc Marí { 27828452758SFam Zheng if (!addr) { 27928452758SFam Zheng return; 28028452758SFam Zheng } 281292be092SMarc Marí mlist_free(allocator, addr); 282292be092SMarc Marí if (allocator->opts & ALLOC_PARANOID) { 283292be092SMarc Marí mlist_check(allocator); 284292be092SMarc Marí } 285292be092SMarc Marí } 286af77f2cdSJohn Snow 287*eb5937baSPaolo Bonzini void alloc_init(QGuestAllocator *s, QAllocOpts opts, 288*eb5937baSPaolo Bonzini uint64_t start, uint64_t end, 289*eb5937baSPaolo Bonzini size_t page_size) 290af77f2cdSJohn Snow { 291af77f2cdSJohn Snow MemBlock *node; 292af77f2cdSJohn Snow 293*eb5937baSPaolo Bonzini s->opts = opts; 294af77f2cdSJohn Snow s->start = start; 295af77f2cdSJohn Snow s->end = end; 296af77f2cdSJohn Snow 297790bbb97SMarc-André Lureau s->used = g_new(MemList, 1); 298790bbb97SMarc-André Lureau s->free = g_new(MemList, 1); 299085248aeSJohn Snow QTAILQ_INIT(s->used); 300085248aeSJohn Snow QTAILQ_INIT(s->free); 301af77f2cdSJohn Snow 302af77f2cdSJohn Snow node = mlist_new(s->start, s->end - s->start); 303085248aeSJohn Snow QTAILQ_INSERT_HEAD(s->free, node, MLIST_ENTNAME); 304af77f2cdSJohn Snow 305*eb5937baSPaolo Bonzini s->page_size = page_size; 306f6f363c1SJohn Snow } 307259342d3SJohn Snow 308259342d3SJohn Snow void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts) 309259342d3SJohn Snow { 310259342d3SJohn Snow allocator->opts |= opts; 311259342d3SJohn Snow } 312085248aeSJohn Snow 313085248aeSJohn Snow void migrate_allocator(QGuestAllocator *src, 314085248aeSJohn Snow QGuestAllocator *dst) 315085248aeSJohn Snow { 316085248aeSJohn Snow MemBlock *node, *tmp; 317085248aeSJohn Snow MemList *tmpused, *tmpfree; 318085248aeSJohn Snow 319085248aeSJohn Snow /* The general memory layout should be equivalent, 320085248aeSJohn Snow * though opts can differ. */ 321085248aeSJohn Snow g_assert_cmphex(src->start, ==, dst->start); 322085248aeSJohn Snow g_assert_cmphex(src->end, ==, dst->end); 323085248aeSJohn Snow 324085248aeSJohn Snow /* Destroy (silently, regardless of options) the dest-list: */ 325085248aeSJohn Snow QTAILQ_FOREACH_SAFE(node, dst->used, MLIST_ENTNAME, tmp) { 326085248aeSJohn Snow g_free(node); 327085248aeSJohn Snow } 328085248aeSJohn Snow QTAILQ_FOREACH_SAFE(node, dst->free, MLIST_ENTNAME, tmp) { 329085248aeSJohn Snow g_free(node); 330085248aeSJohn Snow } 331085248aeSJohn Snow 332085248aeSJohn Snow tmpused = dst->used; 333085248aeSJohn Snow tmpfree = dst->free; 334085248aeSJohn Snow 335085248aeSJohn Snow /* Inherit the lists of the source allocator: */ 336085248aeSJohn Snow dst->used = src->used; 337085248aeSJohn Snow dst->free = src->free; 338085248aeSJohn Snow 339085248aeSJohn Snow /* Source is now re-initialized, the source memory is 'invalid' now: */ 340085248aeSJohn Snow src->used = tmpused; 341085248aeSJohn Snow src->free = tmpfree; 342085248aeSJohn Snow QTAILQ_INIT(src->used); 343085248aeSJohn Snow QTAILQ_INIT(src->free); 344085248aeSJohn Snow node = mlist_new(src->start, src->end - src->start); 345085248aeSJohn Snow QTAILQ_INSERT_HEAD(src->free, node, MLIST_ENTNAME); 346085248aeSJohn Snow return; 347085248aeSJohn Snow } 348