2 * QEMU Hyper-V Dynamic Memory Protocol driver
4 * Copyright (C) 2020-2023 Oracle and/or its affiliates.
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
10 #include "qemu/osdep.h"
11 #include "hv-balloon-internal.h"
12 #include "hv-balloon-page_range_tree.h"
15 * temporarily avoid warnings about enhanced GTree API usage requiring a
16 * too recent Glib version until GLIB_VERSION_MAX_ALLOWED finally reaches
17 * the Glib version with this API
19 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
22 static gint
page_range_tree_key_compare(gconstpointer leftp
,
26 const uint64_t *left
= leftp
, *right
= rightp
;
30 } else if (*left
> *right
) {
32 } else { /* *left == *right */
37 static GTreeNode
*page_range_tree_insert_new(PageRangeTree tree
,
38 uint64_t start
, uint64_t count
)
40 uint64_t *key
= g_malloc(sizeof(*key
));
41 PageRange
*range
= g_malloc(sizeof(*range
));
45 *key
= range
->start
= start
;
48 return g_tree_insert_node(tree
.t
, key
, range
);
51 void hvb_page_range_tree_insert(PageRangeTree tree
,
52 uint64_t start
, uint64_t count
,
57 uint64_t intersection
;
60 assert(!SUM_OVERFLOW_U64(start
, count
));
65 node
= g_tree_upper_bound(tree
.t
, &start
);
67 node
= g_tree_node_previous(node
);
69 node
= g_tree_node_last(tree
.t
);
73 range
= g_tree_node_value(node
);
75 intersection
= page_range_intersection_size(range
, start
, count
);
76 joinable
= page_range_joinable_right(range
, start
, count
);
80 (!intersection
&& !joinable
)) {
82 * !node case: the tree is empty or the very first node in the tree
83 * already has a higher key (the start of its range).
84 * the other case: there is a gap in the tree between the new range
85 * and the previous one.
86 * anyway, let's just insert the new range into the tree.
88 node
= page_range_tree_insert_new(tree
, start
, count
);
90 range
= g_tree_node_value(node
);
94 * the previous range in the tree either partially covers the new
95 * range or ends just at its beginning - extend it
98 *dupcount
+= intersection
;
101 count
+= start
- range
->start
;
102 range
->count
= MAX(range
->count
, count
);
105 /* check next nodes for possible merging */
106 for (node
= g_tree_node_next(node
); node
; ) {
109 rangecur
= g_tree_node_value(node
);
112 intersection
= page_range_intersection_size(rangecur
,
113 range
->start
, range
->count
);
114 joinable
= page_range_joinable_left(rangecur
,
115 range
->start
, range
->count
);
116 if (!intersection
&& !joinable
) {
117 /* the current node is disjoint */
122 *dupcount
+= intersection
;
125 count
= rangecur
->count
+ (rangecur
->start
- range
->start
);
126 range
->count
= MAX(range
->count
, count
);
128 /* the current node was merged in, remove it */
129 start
= rangecur
->start
;
130 node
= g_tree_node_next(node
);
131 /* no hinted removal in GTree... */
132 g_tree_remove(tree
.t
, &start
);
136 bool hvb_page_range_tree_pop(PageRangeTree tree
, PageRange
*out
,
142 node
= g_tree_node_last(tree
.t
);
147 range
= g_tree_node_value(node
);
150 out
->start
= range
->start
;
152 /* can't modify range->start as it is the node key */
153 if (range
->count
> maxcount
) {
154 out
->start
+= range
->count
- maxcount
;
155 out
->count
= maxcount
;
156 range
->count
-= maxcount
;
158 out
->count
= range
->count
;
159 /* no hinted removal in GTree... */
160 g_tree_remove(tree
.t
, &out
->start
);
166 bool hvb_page_range_tree_intree_any(PageRangeTree tree
,
167 uint64_t start
, uint64_t count
)
175 /* find the first node that can possibly intersect our range */
176 node
= g_tree_upper_bound(tree
.t
, &start
);
179 * a NULL node below means that the very first node in the tree
180 * already has a higher key (the start of its range).
182 node
= g_tree_node_previous(node
);
184 /* a NULL node below means that the tree is empty */
185 node
= g_tree_node_last(tree
.t
);
187 /* node range start <= range start */
190 /* node range start > range start */
191 node
= g_tree_node_first(tree
.t
);
194 for ( ; node
; node
= g_tree_node_next(node
)) {
195 PageRange
*range
= g_tree_node_value(node
);
199 * if this node starts beyond or at the end of our range so does
202 if (range
->start
>= start
+ count
) {
206 if (page_range_intersection_size(range
, start
, count
) > 0) {
214 void hvb_page_range_tree_init(PageRangeTree
*tree
)
216 tree
->t
= g_tree_new_full(page_range_tree_key_compare
, NULL
,
220 void hvb_page_range_tree_destroy(PageRangeTree
*tree
)
222 /* g_tree_destroy() is not NULL-safe */
227 g_tree_destroy(tree
->t
);