1*0d9e8c0bSMaciej S. Szmigiero /* 2*0d9e8c0bSMaciej S. Szmigiero * QEMU Hyper-V Dynamic Memory Protocol driver 3*0d9e8c0bSMaciej S. Szmigiero * 4*0d9e8c0bSMaciej S. Szmigiero * Copyright (C) 2020-2023 Oracle and/or its affiliates. 5*0d9e8c0bSMaciej S. Szmigiero * 6*0d9e8c0bSMaciej S. Szmigiero * This work is licensed under the terms of the GNU GPL, version 2 or later. 7*0d9e8c0bSMaciej S. Szmigiero * See the COPYING file in the top-level directory. 8*0d9e8c0bSMaciej S. Szmigiero */ 9*0d9e8c0bSMaciej S. Szmigiero 10*0d9e8c0bSMaciej S. Szmigiero #include "hv-balloon-internal.h" 11*0d9e8c0bSMaciej S. Szmigiero #include "hv-balloon-page_range_tree.h" 12*0d9e8c0bSMaciej S. Szmigiero 13*0d9e8c0bSMaciej S. Szmigiero /* 14*0d9e8c0bSMaciej S. Szmigiero * temporarily avoid warnings about enhanced GTree API usage requiring a 15*0d9e8c0bSMaciej S. Szmigiero * too recent Glib version until GLIB_VERSION_MAX_ALLOWED finally reaches 16*0d9e8c0bSMaciej S. Szmigiero * the Glib version with this API 17*0d9e8c0bSMaciej S. Szmigiero */ 18*0d9e8c0bSMaciej S. Szmigiero #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 19*0d9e8c0bSMaciej S. Szmigiero 20*0d9e8c0bSMaciej S. Szmigiero /* PageRangeTree */ 21*0d9e8c0bSMaciej S. Szmigiero static gint page_range_tree_key_compare(gconstpointer leftp, 22*0d9e8c0bSMaciej S. Szmigiero gconstpointer rightp, 23*0d9e8c0bSMaciej S. Szmigiero gpointer user_data) 24*0d9e8c0bSMaciej S. Szmigiero { 25*0d9e8c0bSMaciej S. Szmigiero const uint64_t *left = leftp, *right = rightp; 26*0d9e8c0bSMaciej S. Szmigiero 27*0d9e8c0bSMaciej S. Szmigiero if (*left < *right) { 28*0d9e8c0bSMaciej S. Szmigiero return -1; 29*0d9e8c0bSMaciej S. Szmigiero } else if (*left > *right) { 30*0d9e8c0bSMaciej S. Szmigiero return 1; 31*0d9e8c0bSMaciej S. Szmigiero } else { /* *left == *right */ 32*0d9e8c0bSMaciej S. Szmigiero return 0; 33*0d9e8c0bSMaciej S. Szmigiero } 34*0d9e8c0bSMaciej S. Szmigiero } 35*0d9e8c0bSMaciej S. Szmigiero 36*0d9e8c0bSMaciej S. Szmigiero static GTreeNode *page_range_tree_insert_new(PageRangeTree tree, 37*0d9e8c0bSMaciej S. Szmigiero uint64_t start, uint64_t count) 38*0d9e8c0bSMaciej S. Szmigiero { 39*0d9e8c0bSMaciej S. Szmigiero uint64_t *key = g_malloc(sizeof(*key)); 40*0d9e8c0bSMaciej S. Szmigiero PageRange *range = g_malloc(sizeof(*range)); 41*0d9e8c0bSMaciej S. Szmigiero 42*0d9e8c0bSMaciej S. Szmigiero assert(count > 0); 43*0d9e8c0bSMaciej S. Szmigiero 44*0d9e8c0bSMaciej S. Szmigiero *key = range->start = start; 45*0d9e8c0bSMaciej S. Szmigiero range->count = count; 46*0d9e8c0bSMaciej S. Szmigiero 47*0d9e8c0bSMaciej S. Szmigiero return g_tree_insert_node(tree.t, key, range); 48*0d9e8c0bSMaciej S. Szmigiero } 49*0d9e8c0bSMaciej S. Szmigiero 50*0d9e8c0bSMaciej S. Szmigiero void hvb_page_range_tree_insert(PageRangeTree tree, 51*0d9e8c0bSMaciej S. Szmigiero uint64_t start, uint64_t count, 52*0d9e8c0bSMaciej S. Szmigiero uint64_t *dupcount) 53*0d9e8c0bSMaciej S. Szmigiero { 54*0d9e8c0bSMaciej S. Szmigiero GTreeNode *node; 55*0d9e8c0bSMaciej S. Szmigiero bool joinable; 56*0d9e8c0bSMaciej S. Szmigiero uint64_t intersection; 57*0d9e8c0bSMaciej S. Szmigiero PageRange *range; 58*0d9e8c0bSMaciej S. Szmigiero 59*0d9e8c0bSMaciej S. Szmigiero assert(!SUM_OVERFLOW_U64(start, count)); 60*0d9e8c0bSMaciej S. Szmigiero if (count == 0) { 61*0d9e8c0bSMaciej S. Szmigiero return; 62*0d9e8c0bSMaciej S. Szmigiero } 63*0d9e8c0bSMaciej S. Szmigiero 64*0d9e8c0bSMaciej S. Szmigiero node = g_tree_upper_bound(tree.t, &start); 65*0d9e8c0bSMaciej S. Szmigiero if (node) { 66*0d9e8c0bSMaciej S. Szmigiero node = g_tree_node_previous(node); 67*0d9e8c0bSMaciej S. Szmigiero } else { 68*0d9e8c0bSMaciej S. Szmigiero node = g_tree_node_last(tree.t); 69*0d9e8c0bSMaciej S. Szmigiero } 70*0d9e8c0bSMaciej S. Szmigiero 71*0d9e8c0bSMaciej S. Szmigiero if (node) { 72*0d9e8c0bSMaciej S. Szmigiero range = g_tree_node_value(node); 73*0d9e8c0bSMaciej S. Szmigiero assert(range); 74*0d9e8c0bSMaciej S. Szmigiero intersection = page_range_intersection_size(range, start, count); 75*0d9e8c0bSMaciej S. Szmigiero joinable = page_range_joinable_right(range, start, count); 76*0d9e8c0bSMaciej S. Szmigiero } 77*0d9e8c0bSMaciej S. Szmigiero 78*0d9e8c0bSMaciej S. Szmigiero if (!node || 79*0d9e8c0bSMaciej S. Szmigiero (!intersection && !joinable)) { 80*0d9e8c0bSMaciej S. Szmigiero /* 81*0d9e8c0bSMaciej S. Szmigiero * !node case: the tree is empty or the very first node in the tree 82*0d9e8c0bSMaciej S. Szmigiero * already has a higher key (the start of its range). 83*0d9e8c0bSMaciej S. Szmigiero * the other case: there is a gap in the tree between the new range 84*0d9e8c0bSMaciej S. Szmigiero * and the previous one. 85*0d9e8c0bSMaciej S. Szmigiero * anyway, let's just insert the new range into the tree. 86*0d9e8c0bSMaciej S. Szmigiero */ 87*0d9e8c0bSMaciej S. Szmigiero node = page_range_tree_insert_new(tree, start, count); 88*0d9e8c0bSMaciej S. Szmigiero assert(node); 89*0d9e8c0bSMaciej S. Szmigiero range = g_tree_node_value(node); 90*0d9e8c0bSMaciej S. Szmigiero assert(range); 91*0d9e8c0bSMaciej S. Szmigiero } else { 92*0d9e8c0bSMaciej S. Szmigiero /* 93*0d9e8c0bSMaciej S. Szmigiero * the previous range in the tree either partially covers the new 94*0d9e8c0bSMaciej S. Szmigiero * range or ends just at its beginning - extend it 95*0d9e8c0bSMaciej S. Szmigiero */ 96*0d9e8c0bSMaciej S. Szmigiero if (dupcount) { 97*0d9e8c0bSMaciej S. Szmigiero *dupcount += intersection; 98*0d9e8c0bSMaciej S. Szmigiero } 99*0d9e8c0bSMaciej S. Szmigiero 100*0d9e8c0bSMaciej S. Szmigiero count += start - range->start; 101*0d9e8c0bSMaciej S. Szmigiero range->count = MAX(range->count, count); 102*0d9e8c0bSMaciej S. Szmigiero } 103*0d9e8c0bSMaciej S. Szmigiero 104*0d9e8c0bSMaciej S. Szmigiero /* check next nodes for possible merging */ 105*0d9e8c0bSMaciej S. Szmigiero for (node = g_tree_node_next(node); node; ) { 106*0d9e8c0bSMaciej S. Szmigiero PageRange *rangecur; 107*0d9e8c0bSMaciej S. Szmigiero 108*0d9e8c0bSMaciej S. Szmigiero rangecur = g_tree_node_value(node); 109*0d9e8c0bSMaciej S. Szmigiero assert(rangecur); 110*0d9e8c0bSMaciej S. Szmigiero 111*0d9e8c0bSMaciej S. Szmigiero intersection = page_range_intersection_size(rangecur, 112*0d9e8c0bSMaciej S. Szmigiero range->start, range->count); 113*0d9e8c0bSMaciej S. Szmigiero joinable = page_range_joinable_left(rangecur, 114*0d9e8c0bSMaciej S. Szmigiero range->start, range->count); 115*0d9e8c0bSMaciej S. Szmigiero if (!intersection && !joinable) { 116*0d9e8c0bSMaciej S. Szmigiero /* the current node is disjoint */ 117*0d9e8c0bSMaciej S. Szmigiero break; 118*0d9e8c0bSMaciej S. Szmigiero } 119*0d9e8c0bSMaciej S. Szmigiero 120*0d9e8c0bSMaciej S. Szmigiero if (dupcount) { 121*0d9e8c0bSMaciej S. Szmigiero *dupcount += intersection; 122*0d9e8c0bSMaciej S. Szmigiero } 123*0d9e8c0bSMaciej S. Szmigiero 124*0d9e8c0bSMaciej S. Szmigiero count = rangecur->count + (rangecur->start - range->start); 125*0d9e8c0bSMaciej S. Szmigiero range->count = MAX(range->count, count); 126*0d9e8c0bSMaciej S. Szmigiero 127*0d9e8c0bSMaciej S. Szmigiero /* the current node was merged in, remove it */ 128*0d9e8c0bSMaciej S. Szmigiero start = rangecur->start; 129*0d9e8c0bSMaciej S. Szmigiero node = g_tree_node_next(node); 130*0d9e8c0bSMaciej S. Szmigiero /* no hinted removal in GTree... */ 131*0d9e8c0bSMaciej S. Szmigiero g_tree_remove(tree.t, &start); 132*0d9e8c0bSMaciej S. Szmigiero } 133*0d9e8c0bSMaciej S. Szmigiero } 134*0d9e8c0bSMaciej S. Szmigiero 135*0d9e8c0bSMaciej S. Szmigiero bool hvb_page_range_tree_pop(PageRangeTree tree, PageRange *out, 136*0d9e8c0bSMaciej S. Szmigiero uint64_t maxcount) 137*0d9e8c0bSMaciej S. Szmigiero { 138*0d9e8c0bSMaciej S. Szmigiero GTreeNode *node; 139*0d9e8c0bSMaciej S. Szmigiero PageRange *range; 140*0d9e8c0bSMaciej S. Szmigiero 141*0d9e8c0bSMaciej S. Szmigiero node = g_tree_node_last(tree.t); 142*0d9e8c0bSMaciej S. Szmigiero if (!node) { 143*0d9e8c0bSMaciej S. Szmigiero return false; 144*0d9e8c0bSMaciej S. Szmigiero } 145*0d9e8c0bSMaciej S. Szmigiero 146*0d9e8c0bSMaciej S. Szmigiero range = g_tree_node_value(node); 147*0d9e8c0bSMaciej S. Szmigiero assert(range); 148*0d9e8c0bSMaciej S. Szmigiero 149*0d9e8c0bSMaciej S. Szmigiero out->start = range->start; 150*0d9e8c0bSMaciej S. Szmigiero 151*0d9e8c0bSMaciej S. Szmigiero /* can't modify range->start as it is the node key */ 152*0d9e8c0bSMaciej S. Szmigiero if (range->count > maxcount) { 153*0d9e8c0bSMaciej S. Szmigiero out->start += range->count - maxcount; 154*0d9e8c0bSMaciej S. Szmigiero out->count = maxcount; 155*0d9e8c0bSMaciej S. Szmigiero range->count -= maxcount; 156*0d9e8c0bSMaciej S. Szmigiero } else { 157*0d9e8c0bSMaciej S. Szmigiero out->count = range->count; 158*0d9e8c0bSMaciej S. Szmigiero /* no hinted removal in GTree... */ 159*0d9e8c0bSMaciej S. Szmigiero g_tree_remove(tree.t, &out->start); 160*0d9e8c0bSMaciej S. Szmigiero } 161*0d9e8c0bSMaciej S. Szmigiero 162*0d9e8c0bSMaciej S. Szmigiero return true; 163*0d9e8c0bSMaciej S. Szmigiero } 164*0d9e8c0bSMaciej S. Szmigiero 165*0d9e8c0bSMaciej S. Szmigiero bool hvb_page_range_tree_intree_any(PageRangeTree tree, 166*0d9e8c0bSMaciej S. Szmigiero uint64_t start, uint64_t count) 167*0d9e8c0bSMaciej S. Szmigiero { 168*0d9e8c0bSMaciej S. Szmigiero GTreeNode *node; 169*0d9e8c0bSMaciej S. Szmigiero 170*0d9e8c0bSMaciej S. Szmigiero if (count == 0) { 171*0d9e8c0bSMaciej S. Szmigiero return false; 172*0d9e8c0bSMaciej S. Szmigiero } 173*0d9e8c0bSMaciej S. Szmigiero 174*0d9e8c0bSMaciej S. Szmigiero /* find the first node that can possibly intersect our range */ 175*0d9e8c0bSMaciej S. Szmigiero node = g_tree_upper_bound(tree.t, &start); 176*0d9e8c0bSMaciej S. Szmigiero if (node) { 177*0d9e8c0bSMaciej S. Szmigiero /* 178*0d9e8c0bSMaciej S. Szmigiero * a NULL node below means that the very first node in the tree 179*0d9e8c0bSMaciej S. Szmigiero * already has a higher key (the start of its range). 180*0d9e8c0bSMaciej S. Szmigiero */ 181*0d9e8c0bSMaciej S. Szmigiero node = g_tree_node_previous(node); 182*0d9e8c0bSMaciej S. Szmigiero } else { 183*0d9e8c0bSMaciej S. Szmigiero /* a NULL node below means that the tree is empty */ 184*0d9e8c0bSMaciej S. Szmigiero node = g_tree_node_last(tree.t); 185*0d9e8c0bSMaciej S. Szmigiero } 186*0d9e8c0bSMaciej S. Szmigiero /* node range start <= range start */ 187*0d9e8c0bSMaciej S. Szmigiero 188*0d9e8c0bSMaciej S. Szmigiero if (!node) { 189*0d9e8c0bSMaciej S. Szmigiero /* node range start > range start */ 190*0d9e8c0bSMaciej S. Szmigiero node = g_tree_node_first(tree.t); 191*0d9e8c0bSMaciej S. Szmigiero } 192*0d9e8c0bSMaciej S. Szmigiero 193*0d9e8c0bSMaciej S. Szmigiero for ( ; node; node = g_tree_node_next(node)) { 194*0d9e8c0bSMaciej S. Szmigiero PageRange *range = g_tree_node_value(node); 195*0d9e8c0bSMaciej S. Szmigiero 196*0d9e8c0bSMaciej S. Szmigiero assert(range); 197*0d9e8c0bSMaciej S. Szmigiero /* 198*0d9e8c0bSMaciej S. Szmigiero * if this node starts beyond or at the end of our range so does 199*0d9e8c0bSMaciej S. Szmigiero * every next one 200*0d9e8c0bSMaciej S. Szmigiero */ 201*0d9e8c0bSMaciej S. Szmigiero if (range->start >= start + count) { 202*0d9e8c0bSMaciej S. Szmigiero break; 203*0d9e8c0bSMaciej S. Szmigiero } 204*0d9e8c0bSMaciej S. Szmigiero 205*0d9e8c0bSMaciej S. Szmigiero if (page_range_intersection_size(range, start, count) > 0) { 206*0d9e8c0bSMaciej S. Szmigiero return true; 207*0d9e8c0bSMaciej S. Szmigiero } 208*0d9e8c0bSMaciej S. Szmigiero } 209*0d9e8c0bSMaciej S. Szmigiero 210*0d9e8c0bSMaciej S. Szmigiero return false; 211*0d9e8c0bSMaciej S. Szmigiero } 212*0d9e8c0bSMaciej S. Szmigiero 213*0d9e8c0bSMaciej S. Szmigiero void hvb_page_range_tree_init(PageRangeTree *tree) 214*0d9e8c0bSMaciej S. Szmigiero { 215*0d9e8c0bSMaciej S. Szmigiero tree->t = g_tree_new_full(page_range_tree_key_compare, NULL, 216*0d9e8c0bSMaciej S. Szmigiero g_free, g_free); 217*0d9e8c0bSMaciej S. Szmigiero } 218*0d9e8c0bSMaciej S. Szmigiero 219*0d9e8c0bSMaciej S. Szmigiero void hvb_page_range_tree_destroy(PageRangeTree *tree) 220*0d9e8c0bSMaciej S. Szmigiero { 221*0d9e8c0bSMaciej S. Szmigiero /* g_tree_destroy() is not NULL-safe */ 222*0d9e8c0bSMaciej S. Szmigiero if (!tree->t) { 223*0d9e8c0bSMaciej S. Szmigiero return; 224*0d9e8c0bSMaciej S. Szmigiero } 225*0d9e8c0bSMaciej S. Szmigiero 226*0d9e8c0bSMaciej S. Szmigiero g_tree_destroy(tree->t); 227*0d9e8c0bSMaciej S. Szmigiero tree->t = NULL; 228*0d9e8c0bSMaciej S. Szmigiero } 229