/* * QEMU Hyper-V Dynamic Memory Protocol driver * * Copyright (C) 2020-2023 Oracle and/or its affiliates. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include "hv-balloon-internal.h" #include "hv-balloon-page_range_tree.h" /* * temporarily avoid warnings about enhanced GTree API usage requiring a * too recent Glib version until GLIB_VERSION_MAX_ALLOWED finally reaches * the Glib version with this API */ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* PageRangeTree */ static gint page_range_tree_key_compare(gconstpointer leftp, gconstpointer rightp, gpointer user_data) { const uint64_t *left = leftp, *right = rightp; if (*left < *right) { return -1; } else if (*left > *right) { return 1; } else { /* *left == *right */ return 0; } } static GTreeNode *page_range_tree_insert_new(PageRangeTree tree, uint64_t start, uint64_t count) { uint64_t *key = g_malloc(sizeof(*key)); PageRange *range = g_malloc(sizeof(*range)); assert(count > 0); *key = range->start = start; range->count = count; return g_tree_insert_node(tree.t, key, range); } void hvb_page_range_tree_insert(PageRangeTree tree, uint64_t start, uint64_t count, uint64_t *dupcount) { GTreeNode *node; bool joinable; uint64_t intersection; PageRange *range; assert(!SUM_OVERFLOW_U64(start, count)); if (count == 0) { return; } node = g_tree_upper_bound(tree.t, &start); if (node) { node = g_tree_node_previous(node); } else { node = g_tree_node_last(tree.t); } if (node) { range = g_tree_node_value(node); assert(range); intersection = page_range_intersection_size(range, start, count); joinable = page_range_joinable_right(range, start, count); } if (!node || (!intersection && !joinable)) { /* * !node case: the tree is empty or the very first node in the tree * already has a higher key (the start of its range). * the other case: there is a gap in the tree between the new range * and the previous one. * anyway, let's just insert the new range into the tree. */ node = page_range_tree_insert_new(tree, start, count); assert(node); range = g_tree_node_value(node); assert(range); } else { /* * the previous range in the tree either partially covers the new * range or ends just at its beginning - extend it */ if (dupcount) { *dupcount += intersection; } count += start - range->start; range->count = MAX(range->count, count); } /* check next nodes for possible merging */ for (node = g_tree_node_next(node); node; ) { PageRange *rangecur; rangecur = g_tree_node_value(node); assert(rangecur); intersection = page_range_intersection_size(rangecur, range->start, range->count); joinable = page_range_joinable_left(rangecur, range->start, range->count); if (!intersection && !joinable) { /* the current node is disjoint */ break; } if (dupcount) { *dupcount += intersection; } count = rangecur->count + (rangecur->start - range->start); range->count = MAX(range->count, count); /* the current node was merged in, remove it */ start = rangecur->start; node = g_tree_node_next(node); /* no hinted removal in GTree... */ g_tree_remove(tree.t, &start); } } bool hvb_page_range_tree_pop(PageRangeTree tree, PageRange *out, uint64_t maxcount) { GTreeNode *node; PageRange *range; node = g_tree_node_last(tree.t); if (!node) { return false; } range = g_tree_node_value(node); assert(range); out->start = range->start; /* can't modify range->start as it is the node key */ if (range->count > maxcount) { out->start += range->count - maxcount; out->count = maxcount; range->count -= maxcount; } else { out->count = range->count; /* no hinted removal in GTree... */ g_tree_remove(tree.t, &out->start); } return true; } bool hvb_page_range_tree_intree_any(PageRangeTree tree, uint64_t start, uint64_t count) { GTreeNode *node; if (count == 0) { return false; } /* find the first node that can possibly intersect our range */ node = g_tree_upper_bound(tree.t, &start); if (node) { /* * a NULL node below means that the very first node in the tree * already has a higher key (the start of its range). */ node = g_tree_node_previous(node); } else { /* a NULL node below means that the tree is empty */ node = g_tree_node_last(tree.t); } /* node range start <= range start */ if (!node) { /* node range start > range start */ node = g_tree_node_first(tree.t); } for ( ; node; node = g_tree_node_next(node)) { PageRange *range = g_tree_node_value(node); assert(range); /* * if this node starts beyond or at the end of our range so does * every next one */ if (range->start >= start + count) { break; } if (page_range_intersection_size(range, start, count) > 0) { return true; } } return false; } void hvb_page_range_tree_init(PageRangeTree *tree) { tree->t = g_tree_new_full(page_range_tree_key_compare, NULL, g_free, g_free); } void hvb_page_range_tree_destroy(PageRangeTree *tree) { /* g_tree_destroy() is not NULL-safe */ if (!tree->t) { return; } g_tree_destroy(tree->t); tree->t = NULL; }