xref: /qemu/hw/hyperv/hv-balloon.c (revision 0d9e8c0b670b7856d36ed155d43548d2491230e7)
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 
12*0d9e8c0bSMaciej S. Szmigiero #include "exec/address-spaces.h"
13*0d9e8c0bSMaciej S. Szmigiero #include "exec/cpu-common.h"
14*0d9e8c0bSMaciej S. Szmigiero #include "exec/ramblock.h"
15*0d9e8c0bSMaciej S. Szmigiero #include "hw/boards.h"
16*0d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/dynmem-proto.h"
17*0d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/hv-balloon.h"
18*0d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/vmbus.h"
19*0d9e8c0bSMaciej S. Szmigiero #include "hw/mem/memory-device.h"
20*0d9e8c0bSMaciej S. Szmigiero #include "hw/mem/pc-dimm.h"
21*0d9e8c0bSMaciej S. Szmigiero #include "hw/qdev-core.h"
22*0d9e8c0bSMaciej S. Szmigiero #include "hw/qdev-properties.h"
23*0d9e8c0bSMaciej S. Szmigiero #include "monitor/qdev.h"
24*0d9e8c0bSMaciej S. Szmigiero #include "qapi/error.h"
25*0d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-commands-machine.h"
26*0d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-events-machine.h"
27*0d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-types-machine.h"
28*0d9e8c0bSMaciej S. Szmigiero #include "qapi/qmp/qdict.h"
29*0d9e8c0bSMaciej S. Szmigiero #include "qapi/visitor.h"
30*0d9e8c0bSMaciej S. Szmigiero #include "qemu/error-report.h"
31*0d9e8c0bSMaciej S. Szmigiero #include "qemu/module.h"
32*0d9e8c0bSMaciej S. Szmigiero #include "qemu/units.h"
33*0d9e8c0bSMaciej S. Szmigiero #include "qemu/timer.h"
34*0d9e8c0bSMaciej S. Szmigiero #include "sysemu/balloon.h"
35*0d9e8c0bSMaciej S. Szmigiero #include "sysemu/hostmem.h"
36*0d9e8c0bSMaciej S. Szmigiero #include "sysemu/reset.h"
37*0d9e8c0bSMaciej S. Szmigiero #include "hv-balloon-page_range_tree.h"
38*0d9e8c0bSMaciej S. Szmigiero #include "trace.h"
39*0d9e8c0bSMaciej S. Szmigiero 
40*0d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_GUID "525074DC-8985-46e2-8057-A307DC18A502"
41*0d9e8c0bSMaciej S. Szmigiero 
42*0d9e8c0bSMaciej S. Szmigiero /*
43*0d9e8c0bSMaciej S. Szmigiero  * Some Windows versions (at least Server 2019) will crash with various
44*0d9e8c0bSMaciej S. Szmigiero  * error codes when receiving DM protocol requests (at least
45*0d9e8c0bSMaciej S. Szmigiero  * DM_MEM_HOT_ADD_REQUEST) immediately after boot.
46*0d9e8c0bSMaciej S. Szmigiero  *
47*0d9e8c0bSMaciej S. Szmigiero  * It looks like Hyper-V from Server 2016 uses a 50-second after-boot
48*0d9e8c0bSMaciej S. Szmigiero  * delay, probably to workaround this issue, so we'll use this value, too.
49*0d9e8c0bSMaciej S. Szmigiero  */
50*0d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_POST_INIT_WAIT (50 * 1000)
51*0d9e8c0bSMaciej S. Szmigiero 
52*0d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HA_CHUNK_SIZE (2 * GiB)
53*0d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HA_CHUNK_PAGES (HV_BALLOON_HA_CHUNK_SIZE / HV_BALLOON_PAGE_SIZE)
54*0d9e8c0bSMaciej S. Szmigiero 
55*0d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HR_CHUNK_PAGES 585728
56*0d9e8c0bSMaciej S. Szmigiero /*
57*0d9e8c0bSMaciej S. Szmigiero  *                                ^ that's the maximum number of pages
58*0d9e8c0bSMaciej S. Szmigiero  * that Windows returns in one hot remove response
59*0d9e8c0bSMaciej S. Szmigiero  *
60*0d9e8c0bSMaciej S. Szmigiero  * If the number requested is too high Windows will no longer honor
61*0d9e8c0bSMaciej S. Szmigiero  * these requests
62*0d9e8c0bSMaciej S. Szmigiero  */
63*0d9e8c0bSMaciej S. Szmigiero 
64*0d9e8c0bSMaciej S. Szmigiero struct HvBalloonClass {
65*0d9e8c0bSMaciej S. Szmigiero     VMBusDeviceClass parent_class;
66*0d9e8c0bSMaciej S. Szmigiero } HvBalloonClass;
67*0d9e8c0bSMaciej S. Szmigiero 
68*0d9e8c0bSMaciej S. Szmigiero typedef enum State {
69*0d9e8c0bSMaciej S. Szmigiero     /* not a real state */
70*0d9e8c0bSMaciej S. Szmigiero     S_NO_CHANGE = 0,
71*0d9e8c0bSMaciej S. Szmigiero 
72*0d9e8c0bSMaciej S. Szmigiero     S_WAIT_RESET,
73*0d9e8c0bSMaciej S. Szmigiero     S_POST_RESET_CLOSED,
74*0d9e8c0bSMaciej S. Szmigiero 
75*0d9e8c0bSMaciej S. Szmigiero     /* init flow */
76*0d9e8c0bSMaciej S. Szmigiero     S_VERSION,
77*0d9e8c0bSMaciej S. Szmigiero     S_CAPS,
78*0d9e8c0bSMaciej S. Szmigiero     S_POST_INIT_WAIT,
79*0d9e8c0bSMaciej S. Szmigiero 
80*0d9e8c0bSMaciej S. Szmigiero     S_IDLE,
81*0d9e8c0bSMaciej S. Szmigiero 
82*0d9e8c0bSMaciej S. Szmigiero     /* balloon op flow */
83*0d9e8c0bSMaciej S. Szmigiero     S_BALLOON_POSTING,
84*0d9e8c0bSMaciej S. Szmigiero     S_BALLOON_RB_WAIT,
85*0d9e8c0bSMaciej S. Szmigiero     S_BALLOON_REPLY_WAIT,
86*0d9e8c0bSMaciej S. Szmigiero 
87*0d9e8c0bSMaciej S. Szmigiero     /* unballoon + hot add ops flow */
88*0d9e8c0bSMaciej S. Szmigiero     S_UNBALLOON_POSTING,
89*0d9e8c0bSMaciej S. Szmigiero     S_UNBALLOON_RB_WAIT,
90*0d9e8c0bSMaciej S. Szmigiero     S_UNBALLOON_REPLY_WAIT,
91*0d9e8c0bSMaciej S. Szmigiero } State;
92*0d9e8c0bSMaciej S. Szmigiero 
93*0d9e8c0bSMaciej S. Szmigiero typedef struct StateDesc {
94*0d9e8c0bSMaciej S. Szmigiero     State state;
95*0d9e8c0bSMaciej S. Szmigiero     const char *desc;
96*0d9e8c0bSMaciej S. Szmigiero } StateDesc;
97*0d9e8c0bSMaciej S. Szmigiero 
98*0d9e8c0bSMaciej S. Szmigiero typedef struct HvBalloon {
99*0d9e8c0bSMaciej S. Szmigiero     VMBusDevice parent;
100*0d9e8c0bSMaciej S. Szmigiero     State state;
101*0d9e8c0bSMaciej S. Szmigiero 
102*0d9e8c0bSMaciej S. Szmigiero     union dm_version version;
103*0d9e8c0bSMaciej S. Szmigiero     union dm_caps caps;
104*0d9e8c0bSMaciej S. Szmigiero 
105*0d9e8c0bSMaciej S. Szmigiero     QEMUTimer post_init_timer;
106*0d9e8c0bSMaciej S. Szmigiero 
107*0d9e8c0bSMaciej S. Szmigiero     unsigned int trans_id;
108*0d9e8c0bSMaciej S. Szmigiero 
109*0d9e8c0bSMaciej S. Szmigiero     struct {
110*0d9e8c0bSMaciej S. Szmigiero         bool enabled;
111*0d9e8c0bSMaciej S. Szmigiero         bool received;
112*0d9e8c0bSMaciej S. Szmigiero         uint64_t committed;
113*0d9e8c0bSMaciej S. Szmigiero         uint64_t available;
114*0d9e8c0bSMaciej S. Szmigiero     } status_report;
115*0d9e8c0bSMaciej S. Szmigiero 
116*0d9e8c0bSMaciej S. Szmigiero     /* Guest target size */
117*0d9e8c0bSMaciej S. Szmigiero     uint64_t target;
118*0d9e8c0bSMaciej S. Szmigiero     bool target_changed;
119*0d9e8c0bSMaciej S. Szmigiero 
120*0d9e8c0bSMaciej S. Szmigiero     /* Current (un)balloon */
121*0d9e8c0bSMaciej S. Szmigiero     union {
122*0d9e8c0bSMaciej S. Szmigiero         uint64_t balloon_diff;
123*0d9e8c0bSMaciej S. Szmigiero 
124*0d9e8c0bSMaciej S. Szmigiero         struct {
125*0d9e8c0bSMaciej S. Szmigiero             uint64_t unballoon_diff;
126*0d9e8c0bSMaciej S. Szmigiero         };
127*0d9e8c0bSMaciej S. Szmigiero     };
128*0d9e8c0bSMaciej S. Szmigiero 
129*0d9e8c0bSMaciej S. Szmigiero     /* Nominal size of each memslot (the last one might be smaller) */
130*0d9e8c0bSMaciej S. Szmigiero     uint64_t memslot_size;
131*0d9e8c0bSMaciej S. Szmigiero 
132*0d9e8c0bSMaciej S. Szmigiero     PageRangeTree removed_guest, removed_both;
133*0d9e8c0bSMaciej S. Szmigiero 
134*0d9e8c0bSMaciej S. Szmigiero     uint64_t removed_guest_ctr, removed_both_ctr;
135*0d9e8c0bSMaciej S. Szmigiero } HvBalloon;
136*0d9e8c0bSMaciej S. Szmigiero 
137*0d9e8c0bSMaciej S. Szmigiero OBJECT_DEFINE_TYPE_WITH_INTERFACES(HvBalloon, hv_balloon, HV_BALLOON, VMBUS_DEVICE, \
138*0d9e8c0bSMaciej S. Szmigiero                                    { })
139*0d9e8c0bSMaciej S. Szmigiero 
140*0d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_SET_STATE(hvb, news)             \
141*0d9e8c0bSMaciej S. Szmigiero     do {                                            \
142*0d9e8c0bSMaciej S. Szmigiero         assert(news != S_NO_CHANGE);                \
143*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_state_set(hvb, news, # news);    \
144*0d9e8c0bSMaciej S. Szmigiero     } while (0)
145*0d9e8c0bSMaciej S. Szmigiero 
146*0d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_STATE_DESC_SET(stdesc, news)         \
147*0d9e8c0bSMaciej S. Szmigiero     _hv_balloon_state_desc_set(stdesc, news, # news)
148*0d9e8c0bSMaciej S. Szmigiero 
149*0d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_STATE_DESC_INIT \
150*0d9e8c0bSMaciej S. Szmigiero     {                              \
151*0d9e8c0bSMaciej S. Szmigiero         .state = S_NO_CHANGE,      \
152*0d9e8c0bSMaciej S. Szmigiero     }
153*0d9e8c0bSMaciej S. Szmigiero 
154*0d9e8c0bSMaciej S. Szmigiero typedef struct HvBalloonReq {
155*0d9e8c0bSMaciej S. Szmigiero     VMBusChanReq vmreq;
156*0d9e8c0bSMaciej S. Szmigiero } HvBalloonReq;
157*0d9e8c0bSMaciej S. Szmigiero 
158*0d9e8c0bSMaciej S. Szmigiero /* TODO: unify the code below with virtio-balloon and cache the value */
159*0d9e8c0bSMaciej S. Szmigiero static int build_dimm_list(Object *obj, void *opaque)
160*0d9e8c0bSMaciej S. Szmigiero {
161*0d9e8c0bSMaciej S. Szmigiero     GSList **list = opaque;
162*0d9e8c0bSMaciej S. Szmigiero 
163*0d9e8c0bSMaciej S. Szmigiero     if (object_dynamic_cast(obj, TYPE_PC_DIMM)) {
164*0d9e8c0bSMaciej S. Szmigiero         DeviceState *dev = DEVICE(obj);
165*0d9e8c0bSMaciej S. Szmigiero         if (dev->realized) { /* only realized DIMMs matter */
166*0d9e8c0bSMaciej S. Szmigiero             *list = g_slist_prepend(*list, dev);
167*0d9e8c0bSMaciej S. Szmigiero         }
168*0d9e8c0bSMaciej S. Szmigiero     }
169*0d9e8c0bSMaciej S. Szmigiero 
170*0d9e8c0bSMaciej S. Szmigiero     object_child_foreach(obj, build_dimm_list, opaque);
171*0d9e8c0bSMaciej S. Szmigiero     return 0;
172*0d9e8c0bSMaciej S. Szmigiero }
173*0d9e8c0bSMaciej S. Szmigiero 
174*0d9e8c0bSMaciej S. Szmigiero static ram_addr_t get_current_ram_size(void)
175*0d9e8c0bSMaciej S. Szmigiero {
176*0d9e8c0bSMaciej S. Szmigiero     GSList *list = NULL, *item;
177*0d9e8c0bSMaciej S. Szmigiero     ram_addr_t size = current_machine->ram_size;
178*0d9e8c0bSMaciej S. Szmigiero 
179*0d9e8c0bSMaciej S. Szmigiero     build_dimm_list(qdev_get_machine(), &list);
180*0d9e8c0bSMaciej S. Szmigiero     for (item = list; item; item = g_slist_next(item)) {
181*0d9e8c0bSMaciej S. Szmigiero         Object *obj = OBJECT(item->data);
182*0d9e8c0bSMaciej S. Szmigiero         if (!strcmp(object_get_typename(obj), TYPE_PC_DIMM))
183*0d9e8c0bSMaciej S. Szmigiero             size += object_property_get_int(obj, PC_DIMM_SIZE_PROP,
184*0d9e8c0bSMaciej S. Szmigiero                                             &error_abort);
185*0d9e8c0bSMaciej S. Szmigiero     }
186*0d9e8c0bSMaciej S. Szmigiero     g_slist_free(list);
187*0d9e8c0bSMaciej S. Szmigiero 
188*0d9e8c0bSMaciej S. Szmigiero     return size;
189*0d9e8c0bSMaciej S. Szmigiero }
190*0d9e8c0bSMaciej S. Szmigiero 
191*0d9e8c0bSMaciej S. Szmigiero /* total RAM includes memory currently removed from the guest */
192*0d9e8c0bSMaciej S. Szmigiero static uint64_t hv_balloon_total_ram(HvBalloon *balloon)
193*0d9e8c0bSMaciej S. Szmigiero {
194*0d9e8c0bSMaciej S. Szmigiero     ram_addr_t ram_size = get_current_ram_size();
195*0d9e8c0bSMaciej S. Szmigiero     uint64_t ram_size_pages = ram_size >> HV_BALLOON_PFN_SHIFT;
196*0d9e8c0bSMaciej S. Szmigiero 
197*0d9e8c0bSMaciej S. Szmigiero     assert(ram_size_pages > 0);
198*0d9e8c0bSMaciej S. Szmigiero 
199*0d9e8c0bSMaciej S. Szmigiero     return ram_size_pages;
200*0d9e8c0bSMaciej S. Szmigiero }
201*0d9e8c0bSMaciej S. Szmigiero 
202*0d9e8c0bSMaciej S. Szmigiero /*
203*0d9e8c0bSMaciej S. Szmigiero  * calculating the total RAM size is a slow operation,
204*0d9e8c0bSMaciej S. Szmigiero  * avoid it as much as possible
205*0d9e8c0bSMaciej S. Szmigiero  */
206*0d9e8c0bSMaciej S. Szmigiero static uint64_t hv_balloon_total_removed_rs(HvBalloon *balloon,
207*0d9e8c0bSMaciej S. Szmigiero                                             uint64_t ram_size_pages)
208*0d9e8c0bSMaciej S. Szmigiero {
209*0d9e8c0bSMaciej S. Szmigiero     uint64_t total_removed;
210*0d9e8c0bSMaciej S. Szmigiero 
211*0d9e8c0bSMaciej S. Szmigiero     total_removed = SUM_SATURATE_U64(balloon->removed_guest_ctr,
212*0d9e8c0bSMaciej S. Szmigiero                                      balloon->removed_both_ctr);
213*0d9e8c0bSMaciej S. Szmigiero 
214*0d9e8c0bSMaciej S. Szmigiero     /* possible if guest returns pages outside actual RAM */
215*0d9e8c0bSMaciej S. Szmigiero     if (total_removed > ram_size_pages) {
216*0d9e8c0bSMaciej S. Szmigiero         total_removed = ram_size_pages;
217*0d9e8c0bSMaciej S. Szmigiero     }
218*0d9e8c0bSMaciej S. Szmigiero 
219*0d9e8c0bSMaciej S. Szmigiero     return total_removed;
220*0d9e8c0bSMaciej S. Szmigiero }
221*0d9e8c0bSMaciej S. Szmigiero 
222*0d9e8c0bSMaciej S. Szmigiero /* Returns whether the state has actually changed */
223*0d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_state_set(HvBalloon *balloon,
224*0d9e8c0bSMaciej S. Szmigiero                                  State newst, const char *newststr)
225*0d9e8c0bSMaciej S. Szmigiero {
226*0d9e8c0bSMaciej S. Szmigiero     if (newst == S_NO_CHANGE || balloon->state == newst) {
227*0d9e8c0bSMaciej S. Szmigiero         return false;
228*0d9e8c0bSMaciej S. Szmigiero     }
229*0d9e8c0bSMaciej S. Szmigiero 
230*0d9e8c0bSMaciej S. Szmigiero     balloon->state = newst;
231*0d9e8c0bSMaciej S. Szmigiero     trace_hv_balloon_state_change(newststr);
232*0d9e8c0bSMaciej S. Szmigiero     return true;
233*0d9e8c0bSMaciej S. Szmigiero }
234*0d9e8c0bSMaciej S. Szmigiero 
235*0d9e8c0bSMaciej S. Szmigiero static void _hv_balloon_state_desc_set(StateDesc *stdesc,
236*0d9e8c0bSMaciej S. Szmigiero                                        State newst, const char *newststr)
237*0d9e8c0bSMaciej S. Szmigiero {
238*0d9e8c0bSMaciej S. Szmigiero     /* state setting is only permitted on a freshly init desc */
239*0d9e8c0bSMaciej S. Szmigiero     assert(stdesc->state == S_NO_CHANGE);
240*0d9e8c0bSMaciej S. Szmigiero 
241*0d9e8c0bSMaciej S. Szmigiero     assert(newst != S_NO_CHANGE);
242*0d9e8c0bSMaciej S. Szmigiero 
243*0d9e8c0bSMaciej S. Szmigiero     stdesc->state = newst;
244*0d9e8c0bSMaciej S. Szmigiero     stdesc->desc = newststr;
245*0d9e8c0bSMaciej S. Szmigiero }
246*0d9e8c0bSMaciej S. Szmigiero 
247*0d9e8c0bSMaciej S. Szmigiero static VMBusChannel *hv_balloon_get_channel_maybe(HvBalloon *balloon)
248*0d9e8c0bSMaciej S. Szmigiero {
249*0d9e8c0bSMaciej S. Szmigiero     return vmbus_device_channel(&balloon->parent, 0);
250*0d9e8c0bSMaciej S. Szmigiero }
251*0d9e8c0bSMaciej S. Szmigiero 
252*0d9e8c0bSMaciej S. Szmigiero static VMBusChannel *hv_balloon_get_channel(HvBalloon *balloon)
253*0d9e8c0bSMaciej S. Szmigiero {
254*0d9e8c0bSMaciej S. Szmigiero     VMBusChannel *chan;
255*0d9e8c0bSMaciej S. Szmigiero 
256*0d9e8c0bSMaciej S. Szmigiero     chan = hv_balloon_get_channel_maybe(balloon);
257*0d9e8c0bSMaciej S. Szmigiero     assert(chan != NULL);
258*0d9e8c0bSMaciej S. Szmigiero     return chan;
259*0d9e8c0bSMaciej S. Szmigiero }
260*0d9e8c0bSMaciej S. Szmigiero 
261*0d9e8c0bSMaciej S. Szmigiero static ssize_t hv_balloon_send_packet(VMBusChannel *chan,
262*0d9e8c0bSMaciej S. Szmigiero                                       struct dm_message *msg)
263*0d9e8c0bSMaciej S. Szmigiero {
264*0d9e8c0bSMaciej S. Szmigiero     int ret;
265*0d9e8c0bSMaciej S. Szmigiero 
266*0d9e8c0bSMaciej S. Szmigiero     ret = vmbus_channel_reserve(chan, 0, msg->hdr.size);
267*0d9e8c0bSMaciej S. Szmigiero     if (ret < 0) {
268*0d9e8c0bSMaciej S. Szmigiero         return ret;
269*0d9e8c0bSMaciej S. Szmigiero     }
270*0d9e8c0bSMaciej S. Szmigiero 
271*0d9e8c0bSMaciej S. Szmigiero     return vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND,
272*0d9e8c0bSMaciej S. Szmigiero                               NULL, 0, msg, msg->hdr.size, false,
273*0d9e8c0bSMaciej S. Szmigiero                               msg->hdr.trans_id);
274*0d9e8c0bSMaciej S. Szmigiero }
275*0d9e8c0bSMaciej S. Szmigiero 
276*0d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_unballoon_get_source(HvBalloon *balloon,
277*0d9e8c0bSMaciej S. Szmigiero                                             PageRangeTree *dtree,
278*0d9e8c0bSMaciej S. Szmigiero                                             uint64_t **dctr)
279*0d9e8c0bSMaciej S. Szmigiero {
280*0d9e8c0bSMaciej S. Szmigiero     if (g_tree_nnodes(balloon->removed_guest.t) > 0) {
281*0d9e8c0bSMaciej S. Szmigiero         *dtree = balloon->removed_guest;
282*0d9e8c0bSMaciej S. Szmigiero         *dctr = &balloon->removed_guest_ctr;
283*0d9e8c0bSMaciej S. Szmigiero     } else if (g_tree_nnodes(balloon->removed_both.t) > 0) {
284*0d9e8c0bSMaciej S. Szmigiero         *dtree = balloon->removed_both;
285*0d9e8c0bSMaciej S. Szmigiero         *dctr = &balloon->removed_both_ctr;
286*0d9e8c0bSMaciej S. Szmigiero     } else {
287*0d9e8c0bSMaciej S. Szmigiero         return false;
288*0d9e8c0bSMaciej S. Szmigiero     }
289*0d9e8c0bSMaciej S. Szmigiero 
290*0d9e8c0bSMaciej S. Szmigiero     return true;
291*0d9e8c0bSMaciej S. Szmigiero }
292*0d9e8c0bSMaciej S. Szmigiero 
293*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_unballoon_rb_wait(HvBalloon *balloon, StateDesc *stdesc)
294*0d9e8c0bSMaciej S. Szmigiero {
295*0d9e8c0bSMaciej S. Szmigiero     VMBusChannel *chan = hv_balloon_get_channel(balloon);
296*0d9e8c0bSMaciej S. Szmigiero     struct dm_unballoon_request *ur;
297*0d9e8c0bSMaciej S. Szmigiero     size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]);
298*0d9e8c0bSMaciej S. Szmigiero 
299*0d9e8c0bSMaciej S. Szmigiero     assert(balloon->state == S_UNBALLOON_RB_WAIT);
300*0d9e8c0bSMaciej S. Szmigiero 
301*0d9e8c0bSMaciej S. Szmigiero     if (vmbus_channel_reserve(chan, 0, ur_size) < 0) {
302*0d9e8c0bSMaciej S. Szmigiero         return;
303*0d9e8c0bSMaciej S. Szmigiero     }
304*0d9e8c0bSMaciej S. Szmigiero 
305*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_POSTING);
306*0d9e8c0bSMaciej S. Szmigiero }
307*0d9e8c0bSMaciej S. Szmigiero 
308*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc)
309*0d9e8c0bSMaciej S. Szmigiero {
310*0d9e8c0bSMaciej S. Szmigiero     VMBusChannel *chan = hv_balloon_get_channel(balloon);
311*0d9e8c0bSMaciej S. Szmigiero     PageRangeTree dtree;
312*0d9e8c0bSMaciej S. Szmigiero     uint64_t *dctr;
313*0d9e8c0bSMaciej S. Szmigiero     struct dm_unballoon_request *ur;
314*0d9e8c0bSMaciej S. Szmigiero     size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]);
315*0d9e8c0bSMaciej S. Szmigiero     PageRange range;
316*0d9e8c0bSMaciej S. Szmigiero     bool bret;
317*0d9e8c0bSMaciej S. Szmigiero     ssize_t ret;
318*0d9e8c0bSMaciej S. Szmigiero 
319*0d9e8c0bSMaciej S. Szmigiero     assert(balloon->state == S_UNBALLOON_POSTING);
320*0d9e8c0bSMaciej S. Szmigiero     assert(balloon->unballoon_diff > 0);
321*0d9e8c0bSMaciej S. Szmigiero 
322*0d9e8c0bSMaciej S. Szmigiero     if (!hv_balloon_unballoon_get_source(balloon, &dtree, &dctr)) {
323*0d9e8c0bSMaciej S. Szmigiero         error_report("trying to unballoon but nothing seems to be ballooned");
324*0d9e8c0bSMaciej S. Szmigiero         /*
325*0d9e8c0bSMaciej S. Szmigiero          * there is little we can do as we might have already
326*0d9e8c0bSMaciej S. Szmigiero          * sent the guest a partial request we can't cancel
327*0d9e8c0bSMaciej S. Szmigiero          */
328*0d9e8c0bSMaciej S. Szmigiero         return;
329*0d9e8c0bSMaciej S. Szmigiero     }
330*0d9e8c0bSMaciej S. Szmigiero 
331*0d9e8c0bSMaciej S. Szmigiero     assert(dtree.t);
332*0d9e8c0bSMaciej S. Szmigiero     assert(dctr);
333*0d9e8c0bSMaciej S. Szmigiero 
334*0d9e8c0bSMaciej S. Szmigiero     ur = alloca(ur_size);
335*0d9e8c0bSMaciej S. Szmigiero     memset(ur, 0, ur_size);
336*0d9e8c0bSMaciej S. Szmigiero     ur->hdr.type = DM_UNBALLOON_REQUEST;
337*0d9e8c0bSMaciej S. Szmigiero     ur->hdr.size = ur_size;
338*0d9e8c0bSMaciej S. Szmigiero     ur->hdr.trans_id = balloon->trans_id;
339*0d9e8c0bSMaciej S. Szmigiero 
340*0d9e8c0bSMaciej S. Szmigiero     bret = hvb_page_range_tree_pop(dtree, &range, MIN(balloon->unballoon_diff,
341*0d9e8c0bSMaciej S. Szmigiero                                                       HV_BALLOON_HA_CHUNK_PAGES));
342*0d9e8c0bSMaciej S. Szmigiero     assert(bret);
343*0d9e8c0bSMaciej S. Szmigiero     /* TODO: madvise? */
344*0d9e8c0bSMaciej S. Szmigiero 
345*0d9e8c0bSMaciej S. Szmigiero     *dctr -= range.count;
346*0d9e8c0bSMaciej S. Szmigiero     balloon->unballoon_diff -= range.count;
347*0d9e8c0bSMaciej S. Szmigiero 
348*0d9e8c0bSMaciej S. Szmigiero     ur->range_count = 1;
349*0d9e8c0bSMaciej S. Szmigiero     ur->range_array[0].finfo.start_page = range.start;
350*0d9e8c0bSMaciej S. Szmigiero     ur->range_array[0].finfo.page_cnt = range.count;
351*0d9e8c0bSMaciej S. Szmigiero     ur->more_pages = balloon->unballoon_diff > 0;
352*0d9e8c0bSMaciej S. Szmigiero 
353*0d9e8c0bSMaciej S. Szmigiero     trace_hv_balloon_outgoing_unballoon(ur->hdr.trans_id,
354*0d9e8c0bSMaciej S. Szmigiero                                         range.count, range.start,
355*0d9e8c0bSMaciej S. Szmigiero                                         balloon->unballoon_diff);
356*0d9e8c0bSMaciej S. Szmigiero 
357*0d9e8c0bSMaciej S. Szmigiero     if (ur->more_pages) {
358*0d9e8c0bSMaciej S. Szmigiero         HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT);
359*0d9e8c0bSMaciej S. Szmigiero     } else {
360*0d9e8c0bSMaciej S. Szmigiero         HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_REPLY_WAIT);
361*0d9e8c0bSMaciej S. Szmigiero     }
362*0d9e8c0bSMaciej S. Szmigiero 
363*0d9e8c0bSMaciej S. Szmigiero     ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND,
364*0d9e8c0bSMaciej S. Szmigiero                              NULL, 0, ur, ur_size, false,
365*0d9e8c0bSMaciej S. Szmigiero                              ur->hdr.trans_id);
366*0d9e8c0bSMaciej S. Szmigiero     if (ret <= 0) {
367*0d9e8c0bSMaciej S. Szmigiero         error_report("error %zd when posting unballoon msg, expect problems",
368*0d9e8c0bSMaciej S. Szmigiero                      ret);
369*0d9e8c0bSMaciej S. Szmigiero     }
370*0d9e8c0bSMaciej S. Szmigiero }
371*0d9e8c0bSMaciej S. Szmigiero 
372*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_balloon_rb_wait(HvBalloon *balloon, StateDesc *stdesc)
373*0d9e8c0bSMaciej S. Szmigiero {
374*0d9e8c0bSMaciej S. Szmigiero     VMBusChannel *chan = hv_balloon_get_channel(balloon);
375*0d9e8c0bSMaciej S. Szmigiero     size_t bl_size = sizeof(struct dm_balloon);
376*0d9e8c0bSMaciej S. Szmigiero 
377*0d9e8c0bSMaciej S. Szmigiero     assert(balloon->state == S_BALLOON_RB_WAIT);
378*0d9e8c0bSMaciej S. Szmigiero 
379*0d9e8c0bSMaciej S. Szmigiero     if (vmbus_channel_reserve(chan, 0, bl_size) < 0) {
380*0d9e8c0bSMaciej S. Szmigiero         return;
381*0d9e8c0bSMaciej S. Szmigiero     }
382*0d9e8c0bSMaciej S. Szmigiero 
383*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_POSTING);
384*0d9e8c0bSMaciej S. Szmigiero }
385*0d9e8c0bSMaciej S. Szmigiero 
386*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_balloon_posting(HvBalloon *balloon, StateDesc *stdesc)
387*0d9e8c0bSMaciej S. Szmigiero {
388*0d9e8c0bSMaciej S. Szmigiero     VMBusChannel *chan = hv_balloon_get_channel(balloon);
389*0d9e8c0bSMaciej S. Szmigiero     struct dm_balloon bl;
390*0d9e8c0bSMaciej S. Szmigiero     size_t bl_size = sizeof(bl);
391*0d9e8c0bSMaciej S. Szmigiero     ssize_t ret;
392*0d9e8c0bSMaciej S. Szmigiero 
393*0d9e8c0bSMaciej S. Szmigiero     assert(balloon->state == S_BALLOON_POSTING);
394*0d9e8c0bSMaciej S. Szmigiero     assert(balloon->balloon_diff > 0);
395*0d9e8c0bSMaciej S. Szmigiero 
396*0d9e8c0bSMaciej S. Szmigiero     memset(&bl, 0, sizeof(bl));
397*0d9e8c0bSMaciej S. Szmigiero     bl.hdr.type = DM_BALLOON_REQUEST;
398*0d9e8c0bSMaciej S. Szmigiero     bl.hdr.size = bl_size;
399*0d9e8c0bSMaciej S. Szmigiero     bl.hdr.trans_id = balloon->trans_id;
400*0d9e8c0bSMaciej S. Szmigiero     bl.num_pages = MIN(balloon->balloon_diff, HV_BALLOON_HR_CHUNK_PAGES);
401*0d9e8c0bSMaciej S. Szmigiero 
402*0d9e8c0bSMaciej S. Szmigiero     trace_hv_balloon_outgoing_balloon(bl.hdr.trans_id, bl.num_pages,
403*0d9e8c0bSMaciej S. Szmigiero                                       balloon->balloon_diff);
404*0d9e8c0bSMaciej S. Szmigiero 
405*0d9e8c0bSMaciej S. Szmigiero     ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND,
406*0d9e8c0bSMaciej S. Szmigiero                              NULL, 0, &bl, bl_size, false,
407*0d9e8c0bSMaciej S. Szmigiero                              bl.hdr.trans_id);
408*0d9e8c0bSMaciej S. Szmigiero     if (ret <= 0) {
409*0d9e8c0bSMaciej S. Szmigiero         error_report("error %zd when posting balloon msg, expect problems",
410*0d9e8c0bSMaciej S. Szmigiero                      ret);
411*0d9e8c0bSMaciej S. Szmigiero     }
412*0d9e8c0bSMaciej S. Szmigiero 
413*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_REPLY_WAIT);
414*0d9e8c0bSMaciej S. Szmigiero }
415*0d9e8c0bSMaciej S. Szmigiero 
416*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_idle_state_process_target(HvBalloon *balloon,
417*0d9e8c0bSMaciej S. Szmigiero                                                  StateDesc *stdesc)
418*0d9e8c0bSMaciej S. Szmigiero {
419*0d9e8c0bSMaciej S. Szmigiero     bool can_balloon = balloon->caps.cap_bits.balloon;
420*0d9e8c0bSMaciej S. Szmigiero     uint64_t ram_size_pages, total_removed;
421*0d9e8c0bSMaciej S. Szmigiero 
422*0d9e8c0bSMaciej S. Szmigiero     ram_size_pages = hv_balloon_total_ram(balloon);
423*0d9e8c0bSMaciej S. Szmigiero     total_removed = hv_balloon_total_removed_rs(balloon, ram_size_pages);
424*0d9e8c0bSMaciej S. Szmigiero 
425*0d9e8c0bSMaciej S. Szmigiero     /*
426*0d9e8c0bSMaciej S. Szmigiero      * we need to cache the values computed from the balloon target value when
427*0d9e8c0bSMaciej S. Szmigiero      * starting the adjustment procedure in case someone changes the target when
428*0d9e8c0bSMaciej S. Szmigiero      * the procedure is in progress
429*0d9e8c0bSMaciej S. Szmigiero      */
430*0d9e8c0bSMaciej S. Szmigiero     if (balloon->target > ram_size_pages - total_removed) {
431*0d9e8c0bSMaciej S. Szmigiero         uint64_t target_diff = balloon->target -
432*0d9e8c0bSMaciej S. Szmigiero             (ram_size_pages - total_removed);
433*0d9e8c0bSMaciej S. Szmigiero 
434*0d9e8c0bSMaciej S. Szmigiero         balloon->unballoon_diff = MIN(target_diff, total_removed);
435*0d9e8c0bSMaciej S. Szmigiero 
436*0d9e8c0bSMaciej S. Szmigiero         if (balloon->unballoon_diff > 0) {
437*0d9e8c0bSMaciej S. Szmigiero             assert(can_balloon);
438*0d9e8c0bSMaciej S. Szmigiero             HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT);
439*0d9e8c0bSMaciej S. Szmigiero         }
440*0d9e8c0bSMaciej S. Szmigiero     } else if (can_balloon &&
441*0d9e8c0bSMaciej S. Szmigiero                balloon->target < ram_size_pages - total_removed) {
442*0d9e8c0bSMaciej S. Szmigiero         balloon->balloon_diff = ram_size_pages - total_removed -
443*0d9e8c0bSMaciej S. Szmigiero             balloon->target;
444*0d9e8c0bSMaciej S. Szmigiero         HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT);
445*0d9e8c0bSMaciej S. Szmigiero     }
446*0d9e8c0bSMaciej S. Szmigiero }
447*0d9e8c0bSMaciej S. Szmigiero 
448*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_idle_state(HvBalloon *balloon,
449*0d9e8c0bSMaciej S. Szmigiero                                   StateDesc *stdesc)
450*0d9e8c0bSMaciej S. Szmigiero {
451*0d9e8c0bSMaciej S. Szmigiero     assert(balloon->state == S_IDLE);
452*0d9e8c0bSMaciej S. Szmigiero 
453*0d9e8c0bSMaciej S. Szmigiero     if (balloon->target_changed) {
454*0d9e8c0bSMaciej S. Szmigiero         balloon->target_changed = false;
455*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_idle_state_process_target(balloon, stdesc);
456*0d9e8c0bSMaciej S. Szmigiero         return;
457*0d9e8c0bSMaciej S. Szmigiero     }
458*0d9e8c0bSMaciej S. Szmigiero }
459*0d9e8c0bSMaciej S. Szmigiero 
460*0d9e8c0bSMaciej S. Szmigiero static const struct {
461*0d9e8c0bSMaciej S. Szmigiero     void (*handler)(HvBalloon *balloon, StateDesc *stdesc);
462*0d9e8c0bSMaciej S. Szmigiero } state_handlers[] = {
463*0d9e8c0bSMaciej S. Szmigiero     [S_IDLE].handler = hv_balloon_idle_state,
464*0d9e8c0bSMaciej S. Szmigiero     [S_BALLOON_POSTING].handler = hv_balloon_balloon_posting,
465*0d9e8c0bSMaciej S. Szmigiero     [S_BALLOON_RB_WAIT].handler = hv_balloon_balloon_rb_wait,
466*0d9e8c0bSMaciej S. Szmigiero     [S_UNBALLOON_POSTING].handler = hv_balloon_unballoon_posting,
467*0d9e8c0bSMaciej S. Szmigiero     [S_UNBALLOON_RB_WAIT].handler = hv_balloon_unballoon_rb_wait,
468*0d9e8c0bSMaciej S. Szmigiero };
469*0d9e8c0bSMaciej S. Szmigiero 
470*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_state(HvBalloon *balloon, StateDesc *stdesc)
471*0d9e8c0bSMaciej S. Szmigiero {
472*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state >= ARRAY_SIZE(state_handlers) ||
473*0d9e8c0bSMaciej S. Szmigiero         !state_handlers[balloon->state].handler) {
474*0d9e8c0bSMaciej S. Szmigiero         return;
475*0d9e8c0bSMaciej S. Szmigiero     }
476*0d9e8c0bSMaciej S. Szmigiero 
477*0d9e8c0bSMaciej S. Szmigiero     state_handlers[balloon->state].handler(balloon, stdesc);
478*0d9e8c0bSMaciej S. Szmigiero }
479*0d9e8c0bSMaciej S. Szmigiero 
480*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_insert_range(PageRangeTree tree,
481*0d9e8c0bSMaciej S. Szmigiero                                                     const PageRange *range,
482*0d9e8c0bSMaciej S. Szmigiero                                                     uint64_t *ctr1,
483*0d9e8c0bSMaciej S. Szmigiero                                                     uint64_t *ctr2,
484*0d9e8c0bSMaciej S. Szmigiero                                                     uint64_t *ctr3)
485*0d9e8c0bSMaciej S. Szmigiero {
486*0d9e8c0bSMaciej S. Szmigiero     uint64_t dupcount, effcount;
487*0d9e8c0bSMaciej S. Szmigiero 
488*0d9e8c0bSMaciej S. Szmigiero     if (range->count == 0) {
489*0d9e8c0bSMaciej S. Szmigiero         return;
490*0d9e8c0bSMaciej S. Szmigiero     }
491*0d9e8c0bSMaciej S. Szmigiero 
492*0d9e8c0bSMaciej S. Szmigiero     dupcount = 0;
493*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_insert(tree, range->start, range->count, &dupcount);
494*0d9e8c0bSMaciej S. Szmigiero 
495*0d9e8c0bSMaciej S. Szmigiero     assert(dupcount <= range->count);
496*0d9e8c0bSMaciej S. Szmigiero     effcount = range->count - dupcount;
497*0d9e8c0bSMaciej S. Szmigiero 
498*0d9e8c0bSMaciej S. Szmigiero     *ctr1 += effcount;
499*0d9e8c0bSMaciej S. Szmigiero     *ctr2 += effcount;
500*0d9e8c0bSMaciej S. Szmigiero     if (ctr3) {
501*0d9e8c0bSMaciej S. Szmigiero         *ctr3 += effcount;
502*0d9e8c0bSMaciej S. Szmigiero     }
503*0d9e8c0bSMaciej S. Szmigiero }
504*0d9e8c0bSMaciej S. Szmigiero 
505*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_handle_range(HvBalloon *balloon,
506*0d9e8c0bSMaciej S. Szmigiero                                                     PageRange *range,
507*0d9e8c0bSMaciej S. Szmigiero                                                     bool both,
508*0d9e8c0bSMaciej S. Szmigiero                                                     uint64_t *removedctr)
509*0d9e8c0bSMaciej S. Szmigiero {
510*0d9e8c0bSMaciej S. Szmigiero     PageRangeTree globaltree =
511*0d9e8c0bSMaciej S. Szmigiero         both ? balloon->removed_both : balloon->removed_guest;
512*0d9e8c0bSMaciej S. Szmigiero     uint64_t *globalctr =
513*0d9e8c0bSMaciej S. Szmigiero         both ? &balloon->removed_both_ctr : &balloon->removed_guest_ctr;
514*0d9e8c0bSMaciej S. Szmigiero 
515*0d9e8c0bSMaciej S. Szmigiero     trace_hv_balloon_remove_response(range->count, range->start, both);
516*0d9e8c0bSMaciej S. Szmigiero 
517*0d9e8c0bSMaciej S. Szmigiero     if (range->count > 0) {
518*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_remove_response_insert_range(globaltree, range,
519*0d9e8c0bSMaciej S. Szmigiero                                                 globalctr, removedctr, NULL);
520*0d9e8c0bSMaciej S. Szmigiero         trace_hv_balloon_remove_response_remainder(range->count, range->start,
521*0d9e8c0bSMaciej S. Szmigiero                                                    both);
522*0d9e8c0bSMaciej S. Szmigiero         range->count = 0;
523*0d9e8c0bSMaciej S. Szmigiero     }
524*0d9e8c0bSMaciej S. Szmigiero }
525*0d9e8c0bSMaciej S. Szmigiero 
526*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_handle_pages(HvBalloon *balloon,
527*0d9e8c0bSMaciej S. Szmigiero                                                     PageRange *range,
528*0d9e8c0bSMaciej S. Szmigiero                                                     uint64_t start,
529*0d9e8c0bSMaciej S. Szmigiero                                                     uint64_t count,
530*0d9e8c0bSMaciej S. Szmigiero                                                     bool both,
531*0d9e8c0bSMaciej S. Szmigiero                                                     uint64_t *removedctr)
532*0d9e8c0bSMaciej S. Szmigiero {
533*0d9e8c0bSMaciej S. Szmigiero     assert(count > 0);
534*0d9e8c0bSMaciej S. Szmigiero 
535*0d9e8c0bSMaciej S. Szmigiero     /*
536*0d9e8c0bSMaciej S. Szmigiero      * if there is an existing range that the new range can't be joined to
537*0d9e8c0bSMaciej S. Szmigiero      * dump it into tree(s)
538*0d9e8c0bSMaciej S. Szmigiero      */
539*0d9e8c0bSMaciej S. Szmigiero     if (range->count > 0 && !page_range_joinable(range, start, count)) {
540*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_remove_response_handle_range(balloon, range, both,
541*0d9e8c0bSMaciej S. Szmigiero                                                 removedctr);
542*0d9e8c0bSMaciej S. Szmigiero     }
543*0d9e8c0bSMaciej S. Szmigiero 
544*0d9e8c0bSMaciej S. Szmigiero     if (range->count == 0) {
545*0d9e8c0bSMaciej S. Szmigiero         range->start = start;
546*0d9e8c0bSMaciej S. Szmigiero         range->count = count;
547*0d9e8c0bSMaciej S. Szmigiero     } else if (page_range_joinable_left(range, start, count)) {
548*0d9e8c0bSMaciej S. Szmigiero         range->start = start;
549*0d9e8c0bSMaciej S. Szmigiero         range->count += count;
550*0d9e8c0bSMaciej S. Szmigiero     } else { /* page_range_joinable_right() */
551*0d9e8c0bSMaciej S. Szmigiero         range->count += count;
552*0d9e8c0bSMaciej S. Szmigiero     }
553*0d9e8c0bSMaciej S. Szmigiero }
554*0d9e8c0bSMaciej S. Szmigiero 
555*0d9e8c0bSMaciej S. Szmigiero static gboolean hv_balloon_handle_remove_host_addr_node(gpointer key,
556*0d9e8c0bSMaciej S. Szmigiero                                                         gpointer value,
557*0d9e8c0bSMaciej S. Szmigiero                                                         gpointer data)
558*0d9e8c0bSMaciej S. Szmigiero {
559*0d9e8c0bSMaciej S. Szmigiero     PageRange *range = value;
560*0d9e8c0bSMaciej S. Szmigiero     uint64_t pageoff;
561*0d9e8c0bSMaciej S. Szmigiero 
562*0d9e8c0bSMaciej S. Szmigiero     for (pageoff = 0; pageoff < range->count; ) {
563*0d9e8c0bSMaciej S. Szmigiero         uint64_t addr_64 = (range->start + pageoff) * HV_BALLOON_PAGE_SIZE;
564*0d9e8c0bSMaciej S. Szmigiero         void *addr;
565*0d9e8c0bSMaciej S. Szmigiero         RAMBlock *rb;
566*0d9e8c0bSMaciej S. Szmigiero         ram_addr_t rb_offset;
567*0d9e8c0bSMaciej S. Szmigiero         size_t rb_page_size;
568*0d9e8c0bSMaciej S. Szmigiero         size_t discard_size;
569*0d9e8c0bSMaciej S. Szmigiero 
570*0d9e8c0bSMaciej S. Szmigiero         assert(addr_64 <= UINTPTR_MAX);
571*0d9e8c0bSMaciej S. Szmigiero         addr = (void *)((uintptr_t)addr_64);
572*0d9e8c0bSMaciej S. Szmigiero         rb = qemu_ram_block_from_host(addr, false, &rb_offset);
573*0d9e8c0bSMaciej S. Szmigiero         rb_page_size = qemu_ram_pagesize(rb);
574*0d9e8c0bSMaciej S. Szmigiero 
575*0d9e8c0bSMaciej S. Szmigiero         if (rb_page_size != HV_BALLOON_PAGE_SIZE) {
576*0d9e8c0bSMaciej S. Szmigiero             /* TODO: these should end in "removed_guest" */
577*0d9e8c0bSMaciej S. Szmigiero             warn_report("guest reported removed page backed by unsupported page size %zu",
578*0d9e8c0bSMaciej S. Szmigiero                         rb_page_size);
579*0d9e8c0bSMaciej S. Szmigiero             pageoff++;
580*0d9e8c0bSMaciej S. Szmigiero             continue;
581*0d9e8c0bSMaciej S. Szmigiero         }
582*0d9e8c0bSMaciej S. Szmigiero 
583*0d9e8c0bSMaciej S. Szmigiero         discard_size = MIN(range->count - pageoff,
584*0d9e8c0bSMaciej S. Szmigiero                            (rb->max_length - rb_offset) /
585*0d9e8c0bSMaciej S. Szmigiero                            HV_BALLOON_PAGE_SIZE);
586*0d9e8c0bSMaciej S. Szmigiero         discard_size = MAX(discard_size, 1);
587*0d9e8c0bSMaciej S. Szmigiero 
588*0d9e8c0bSMaciej S. Szmigiero         if (ram_block_discard_range(rb, rb_offset, discard_size *
589*0d9e8c0bSMaciej S. Szmigiero                                     HV_BALLOON_PAGE_SIZE) != 0) {
590*0d9e8c0bSMaciej S. Szmigiero             warn_report("guest reported removed page failed discard");
591*0d9e8c0bSMaciej S. Szmigiero         }
592*0d9e8c0bSMaciej S. Szmigiero 
593*0d9e8c0bSMaciej S. Szmigiero         pageoff += discard_size;
594*0d9e8c0bSMaciej S. Szmigiero     }
595*0d9e8c0bSMaciej S. Szmigiero 
596*0d9e8c0bSMaciej S. Szmigiero     return false;
597*0d9e8c0bSMaciej S. Szmigiero }
598*0d9e8c0bSMaciej S. Szmigiero 
599*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_remove_host_addr_tree(PageRangeTree tree)
600*0d9e8c0bSMaciej S. Szmigiero {
601*0d9e8c0bSMaciej S. Szmigiero     g_tree_foreach(tree.t, hv_balloon_handle_remove_host_addr_node, NULL);
602*0d9e8c0bSMaciej S. Szmigiero }
603*0d9e8c0bSMaciej S. Szmigiero 
604*0d9e8c0bSMaciej S. Szmigiero static int hv_balloon_handle_remove_section(PageRangeTree tree,
605*0d9e8c0bSMaciej S. Szmigiero                                             const MemoryRegionSection *section,
606*0d9e8c0bSMaciej S. Szmigiero                                             uint64_t count)
607*0d9e8c0bSMaciej S. Szmigiero {
608*0d9e8c0bSMaciej S. Szmigiero     void *addr = memory_region_get_ram_ptr(section->mr) +
609*0d9e8c0bSMaciej S. Szmigiero         section->offset_within_region;
610*0d9e8c0bSMaciej S. Szmigiero     uint64_t addr_page;
611*0d9e8c0bSMaciej S. Szmigiero 
612*0d9e8c0bSMaciej S. Szmigiero     assert(count > 0);
613*0d9e8c0bSMaciej S. Szmigiero 
614*0d9e8c0bSMaciej S. Szmigiero     if ((uintptr_t)addr % HV_BALLOON_PAGE_SIZE) {
615*0d9e8c0bSMaciej S. Szmigiero         warn_report("guest reported removed pages at an unaligned host addr %p",
616*0d9e8c0bSMaciej S. Szmigiero                     addr);
617*0d9e8c0bSMaciej S. Szmigiero         return -EINVAL;
618*0d9e8c0bSMaciej S. Szmigiero     }
619*0d9e8c0bSMaciej S. Szmigiero 
620*0d9e8c0bSMaciej S. Szmigiero     addr_page = (uintptr_t)addr / HV_BALLOON_PAGE_SIZE;
621*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_insert(tree, addr_page, count, NULL);
622*0d9e8c0bSMaciej S. Szmigiero 
623*0d9e8c0bSMaciej S. Szmigiero     return 0;
624*0d9e8c0bSMaciej S. Szmigiero }
625*0d9e8c0bSMaciej S. Szmigiero 
626*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_remove_ranges(HvBalloon *balloon,
627*0d9e8c0bSMaciej S. Szmigiero                                             union dm_mem_page_range ranges[],
628*0d9e8c0bSMaciej S. Szmigiero                                             uint32_t count)
629*0d9e8c0bSMaciej S. Szmigiero {
630*0d9e8c0bSMaciej S. Szmigiero     uint64_t removedcnt;
631*0d9e8c0bSMaciej S. Szmigiero     PageRangeTree removed_host_addr;
632*0d9e8c0bSMaciej S. Szmigiero     PageRange range_guest, range_both;
633*0d9e8c0bSMaciej S. Szmigiero 
634*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_init(&removed_host_addr);
635*0d9e8c0bSMaciej S. Szmigiero     range_guest.count = range_both.count = removedcnt = 0;
636*0d9e8c0bSMaciej S. Szmigiero     for (unsigned int ctr = 0; ctr < count; ctr++) {
637*0d9e8c0bSMaciej S. Szmigiero         union dm_mem_page_range *mr = &ranges[ctr];
638*0d9e8c0bSMaciej S. Szmigiero         hwaddr pa;
639*0d9e8c0bSMaciej S. Szmigiero         MemoryRegionSection section;
640*0d9e8c0bSMaciej S. Szmigiero 
641*0d9e8c0bSMaciej S. Szmigiero         for (unsigned int offset = 0; offset < mr->finfo.page_cnt; ) {
642*0d9e8c0bSMaciej S. Szmigiero             int ret;
643*0d9e8c0bSMaciej S. Szmigiero             uint64_t pageno = mr->finfo.start_page + offset;
644*0d9e8c0bSMaciej S. Szmigiero             uint64_t pagecnt = 1;
645*0d9e8c0bSMaciej S. Szmigiero 
646*0d9e8c0bSMaciej S. Szmigiero             pa = (hwaddr)pageno << HV_BALLOON_PFN_SHIFT;
647*0d9e8c0bSMaciej S. Szmigiero             section = memory_region_find(get_system_memory(), pa,
648*0d9e8c0bSMaciej S. Szmigiero                                          (mr->finfo.page_cnt - offset) *
649*0d9e8c0bSMaciej S. Szmigiero                                          HV_BALLOON_PAGE_SIZE);
650*0d9e8c0bSMaciej S. Szmigiero             if (!section.mr) {
651*0d9e8c0bSMaciej S. Szmigiero                 warn_report("guest reported removed page %"PRIu64" not found in RAM",
652*0d9e8c0bSMaciej S. Szmigiero                             pageno);
653*0d9e8c0bSMaciej S. Szmigiero                 ret = -EINVAL;
654*0d9e8c0bSMaciej S. Szmigiero                 goto finish_page;
655*0d9e8c0bSMaciej S. Szmigiero             }
656*0d9e8c0bSMaciej S. Szmigiero 
657*0d9e8c0bSMaciej S. Szmigiero             pagecnt = int128_get64(section.size) / HV_BALLOON_PAGE_SIZE;
658*0d9e8c0bSMaciej S. Szmigiero             if (pagecnt <= 0) {
659*0d9e8c0bSMaciej S. Szmigiero                 warn_report("guest reported removed page %"PRIu64" in a section smaller than page size",
660*0d9e8c0bSMaciej S. Szmigiero                             pageno);
661*0d9e8c0bSMaciej S. Szmigiero                 pagecnt = 1; /* skip the whole page */
662*0d9e8c0bSMaciej S. Szmigiero                 ret = -EINVAL;
663*0d9e8c0bSMaciej S. Szmigiero                 goto finish_page;
664*0d9e8c0bSMaciej S. Szmigiero             }
665*0d9e8c0bSMaciej S. Szmigiero 
666*0d9e8c0bSMaciej S. Szmigiero             if (!memory_region_is_ram(section.mr) ||
667*0d9e8c0bSMaciej S. Szmigiero                 memory_region_is_rom(section.mr) ||
668*0d9e8c0bSMaciej S. Szmigiero                 memory_region_is_romd(section.mr)) {
669*0d9e8c0bSMaciej S. Szmigiero                 warn_report("guest reported removed page %"PRIu64" in a section that is not an ordinary RAM",
670*0d9e8c0bSMaciej S. Szmigiero                             pageno);
671*0d9e8c0bSMaciej S. Szmigiero                 ret = -EINVAL;
672*0d9e8c0bSMaciej S. Szmigiero                 goto finish_page;
673*0d9e8c0bSMaciej S. Szmigiero             }
674*0d9e8c0bSMaciej S. Szmigiero 
675*0d9e8c0bSMaciej S. Szmigiero             ret = hv_balloon_handle_remove_section(removed_host_addr, &section,
676*0d9e8c0bSMaciej S. Szmigiero                                                    pagecnt);
677*0d9e8c0bSMaciej S. Szmigiero 
678*0d9e8c0bSMaciej S. Szmigiero         finish_page:
679*0d9e8c0bSMaciej S. Szmigiero             if (ret == 0) {
680*0d9e8c0bSMaciej S. Szmigiero                 hv_balloon_remove_response_handle_pages(balloon,
681*0d9e8c0bSMaciej S. Szmigiero                                                         &range_both,
682*0d9e8c0bSMaciej S. Szmigiero                                                         pageno, pagecnt,
683*0d9e8c0bSMaciej S. Szmigiero                                                         true, &removedcnt);
684*0d9e8c0bSMaciej S. Szmigiero             } else {
685*0d9e8c0bSMaciej S. Szmigiero                 hv_balloon_remove_response_handle_pages(balloon,
686*0d9e8c0bSMaciej S. Szmigiero                                                         &range_guest,
687*0d9e8c0bSMaciej S. Szmigiero                                                         pageno, pagecnt,
688*0d9e8c0bSMaciej S. Szmigiero                                                         false, &removedcnt);
689*0d9e8c0bSMaciej S. Szmigiero             }
690*0d9e8c0bSMaciej S. Szmigiero 
691*0d9e8c0bSMaciej S. Szmigiero             if (section.mr) {
692*0d9e8c0bSMaciej S. Szmigiero                 memory_region_unref(section.mr);
693*0d9e8c0bSMaciej S. Szmigiero             }
694*0d9e8c0bSMaciej S. Szmigiero 
695*0d9e8c0bSMaciej S. Szmigiero             offset += pagecnt;
696*0d9e8c0bSMaciej S. Szmigiero         }
697*0d9e8c0bSMaciej S. Szmigiero     }
698*0d9e8c0bSMaciej S. Szmigiero 
699*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_remove_response_handle_range(balloon, &range_both, true,
700*0d9e8c0bSMaciej S. Szmigiero                                             &removedcnt);
701*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_remove_response_handle_range(balloon, &range_guest, false,
702*0d9e8c0bSMaciej S. Szmigiero                                             &removedcnt);
703*0d9e8c0bSMaciej S. Szmigiero 
704*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_handle_remove_host_addr_tree(removed_host_addr);
705*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_destroy(&removed_host_addr);
706*0d9e8c0bSMaciej S. Szmigiero 
707*0d9e8c0bSMaciej S. Szmigiero     if (removedcnt > balloon->balloon_diff) {
708*0d9e8c0bSMaciej S. Szmigiero         warn_report("guest reported more pages removed than currently pending (%"PRIu64" vs %"PRIu64")",
709*0d9e8c0bSMaciej S. Szmigiero                     removedcnt, balloon->balloon_diff);
710*0d9e8c0bSMaciej S. Szmigiero         balloon->balloon_diff = 0;
711*0d9e8c0bSMaciej S. Szmigiero     } else {
712*0d9e8c0bSMaciej S. Szmigiero         balloon->balloon_diff -= removedcnt;
713*0d9e8c0bSMaciej S. Szmigiero     }
714*0d9e8c0bSMaciej S. Szmigiero }
715*0d9e8c0bSMaciej S. Szmigiero 
716*0d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_handle_msg_size(HvBalloonReq *req, size_t minsize,
717*0d9e8c0bSMaciej S. Szmigiero                                        const char *msgname)
718*0d9e8c0bSMaciej S. Szmigiero {
719*0d9e8c0bSMaciej S. Szmigiero     VMBusChanReq *vmreq = &req->vmreq;
720*0d9e8c0bSMaciej S. Szmigiero     uint32_t msglen = vmreq->msglen;
721*0d9e8c0bSMaciej S. Szmigiero 
722*0d9e8c0bSMaciej S. Szmigiero     if (msglen >= minsize) {
723*0d9e8c0bSMaciej S. Szmigiero         return true;
724*0d9e8c0bSMaciej S. Szmigiero     }
725*0d9e8c0bSMaciej S. Szmigiero 
726*0d9e8c0bSMaciej S. Szmigiero     warn_report("%s message too short (%u vs %zu), ignoring", msgname,
727*0d9e8c0bSMaciej S. Szmigiero                 (unsigned int)msglen, minsize);
728*0d9e8c0bSMaciej S. Szmigiero     return false;
729*0d9e8c0bSMaciej S. Szmigiero }
730*0d9e8c0bSMaciej S. Szmigiero 
731*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_version_request(HvBalloon *balloon,
732*0d9e8c0bSMaciej S. Szmigiero                                               HvBalloonReq *req,
733*0d9e8c0bSMaciej S. Szmigiero                                               StateDesc *stdesc)
734*0d9e8c0bSMaciej S. Szmigiero {
735*0d9e8c0bSMaciej S. Szmigiero     VMBusChanReq *vmreq = &req->vmreq;
736*0d9e8c0bSMaciej S. Szmigiero     struct dm_version_request *msgVr = vmreq->msg;
737*0d9e8c0bSMaciej S. Szmigiero     struct dm_version_response respVr;
738*0d9e8c0bSMaciej S. Szmigiero 
739*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state != S_VERSION) {
740*0d9e8c0bSMaciej S. Szmigiero         warn_report("unexpected DM_VERSION_REQUEST in %d state",
741*0d9e8c0bSMaciej S. Szmigiero                     balloon->state);
742*0d9e8c0bSMaciej S. Szmigiero         return;
743*0d9e8c0bSMaciej S. Szmigiero     }
744*0d9e8c0bSMaciej S. Szmigiero 
745*0d9e8c0bSMaciej S. Szmigiero     if (!hv_balloon_handle_msg_size(req, sizeof(*msgVr),
746*0d9e8c0bSMaciej S. Szmigiero                                     "DM_VERSION_REQUEST")) {
747*0d9e8c0bSMaciej S. Szmigiero         return;
748*0d9e8c0bSMaciej S. Szmigiero     }
749*0d9e8c0bSMaciej S. Szmigiero 
750*0d9e8c0bSMaciej S. Szmigiero     trace_hv_balloon_incoming_version(msgVr->version.major_version,
751*0d9e8c0bSMaciej S. Szmigiero                                       msgVr->version.minor_version);
752*0d9e8c0bSMaciej S. Szmigiero 
753*0d9e8c0bSMaciej S. Szmigiero     memset(&respVr, 0, sizeof(respVr));
754*0d9e8c0bSMaciej S. Szmigiero     respVr.hdr.type = DM_VERSION_RESPONSE;
755*0d9e8c0bSMaciej S. Szmigiero     respVr.hdr.size = sizeof(respVr);
756*0d9e8c0bSMaciej S. Szmigiero     respVr.hdr.trans_id = msgVr->hdr.trans_id;
757*0d9e8c0bSMaciej S. Szmigiero     respVr.is_accepted = msgVr->version.version >= DYNMEM_PROTOCOL_VERSION_1 &&
758*0d9e8c0bSMaciej S. Szmigiero         msgVr->version.version <= DYNMEM_PROTOCOL_VERSION_3;
759*0d9e8c0bSMaciej S. Szmigiero 
760*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respVr);
761*0d9e8c0bSMaciej S. Szmigiero 
762*0d9e8c0bSMaciej S. Szmigiero     if (respVr.is_accepted) {
763*0d9e8c0bSMaciej S. Szmigiero         HV_BALLOON_STATE_DESC_SET(stdesc, S_CAPS);
764*0d9e8c0bSMaciej S. Szmigiero     }
765*0d9e8c0bSMaciej S. Szmigiero }
766*0d9e8c0bSMaciej S. Szmigiero 
767*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_caps_report(HvBalloon *balloon,
768*0d9e8c0bSMaciej S. Szmigiero                                           HvBalloonReq *req,
769*0d9e8c0bSMaciej S. Szmigiero                                           StateDesc *stdesc)
770*0d9e8c0bSMaciej S. Szmigiero {
771*0d9e8c0bSMaciej S. Szmigiero     VMBusChanReq *vmreq = &req->vmreq;
772*0d9e8c0bSMaciej S. Szmigiero     struct dm_capabilities *msgCap = vmreq->msg;
773*0d9e8c0bSMaciej S. Szmigiero     struct dm_capabilities_resp_msg respCap;
774*0d9e8c0bSMaciej S. Szmigiero 
775*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state != S_CAPS) {
776*0d9e8c0bSMaciej S. Szmigiero         warn_report("unexpected DM_CAPABILITIES_REPORT in %d state",
777*0d9e8c0bSMaciej S. Szmigiero                     balloon->state);
778*0d9e8c0bSMaciej S. Szmigiero         return;
779*0d9e8c0bSMaciej S. Szmigiero     }
780*0d9e8c0bSMaciej S. Szmigiero 
781*0d9e8c0bSMaciej S. Szmigiero     if (!hv_balloon_handle_msg_size(req, sizeof(*msgCap),
782*0d9e8c0bSMaciej S. Szmigiero                                     "DM_CAPABILITIES_REPORT")) {
783*0d9e8c0bSMaciej S. Szmigiero         return;
784*0d9e8c0bSMaciej S. Szmigiero     }
785*0d9e8c0bSMaciej S. Szmigiero 
786*0d9e8c0bSMaciej S. Szmigiero     trace_hv_balloon_incoming_caps(msgCap->caps.caps);
787*0d9e8c0bSMaciej S. Szmigiero     balloon->caps = msgCap->caps;
788*0d9e8c0bSMaciej S. Szmigiero 
789*0d9e8c0bSMaciej S. Szmigiero     memset(&respCap, 0, sizeof(respCap));
790*0d9e8c0bSMaciej S. Szmigiero     respCap.hdr.type = DM_CAPABILITIES_RESPONSE;
791*0d9e8c0bSMaciej S. Szmigiero     respCap.hdr.size = sizeof(respCap);
792*0d9e8c0bSMaciej S. Szmigiero     respCap.hdr.trans_id = msgCap->hdr.trans_id;
793*0d9e8c0bSMaciej S. Szmigiero     respCap.is_accepted = 1;
794*0d9e8c0bSMaciej S. Szmigiero     respCap.hot_remove = 1;
795*0d9e8c0bSMaciej S. Szmigiero     respCap.suppress_pressure_reports = !balloon->status_report.enabled;
796*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respCap);
797*0d9e8c0bSMaciej S. Szmigiero 
798*0d9e8c0bSMaciej S. Szmigiero     timer_mod(&balloon->post_init_timer,
799*0d9e8c0bSMaciej S. Szmigiero               qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
800*0d9e8c0bSMaciej S. Szmigiero               HV_BALLOON_POST_INIT_WAIT);
801*0d9e8c0bSMaciej S. Szmigiero 
802*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_STATE_DESC_SET(stdesc, S_POST_INIT_WAIT);
803*0d9e8c0bSMaciej S. Szmigiero }
804*0d9e8c0bSMaciej S. Szmigiero 
805*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_status_report(HvBalloon *balloon,
806*0d9e8c0bSMaciej S. Szmigiero                                             HvBalloonReq *req)
807*0d9e8c0bSMaciej S. Szmigiero {
808*0d9e8c0bSMaciej S. Szmigiero     VMBusChanReq *vmreq = &req->vmreq;
809*0d9e8c0bSMaciej S. Szmigiero     struct dm_status *msgStatus = vmreq->msg;
810*0d9e8c0bSMaciej S. Szmigiero 
811*0d9e8c0bSMaciej S. Szmigiero     if (!hv_balloon_handle_msg_size(req, sizeof(*msgStatus),
812*0d9e8c0bSMaciej S. Szmigiero                                     "DM_STATUS_REPORT")) {
813*0d9e8c0bSMaciej S. Szmigiero         return;
814*0d9e8c0bSMaciej S. Szmigiero     }
815*0d9e8c0bSMaciej S. Szmigiero 
816*0d9e8c0bSMaciej S. Szmigiero     if (!balloon->status_report.enabled) {
817*0d9e8c0bSMaciej S. Szmigiero         return;
818*0d9e8c0bSMaciej S. Szmigiero     }
819*0d9e8c0bSMaciej S. Szmigiero 
820*0d9e8c0bSMaciej S. Szmigiero     balloon->status_report.committed = msgStatus->num_committed;
821*0d9e8c0bSMaciej S. Szmigiero     balloon->status_report.committed *= HV_BALLOON_PAGE_SIZE;
822*0d9e8c0bSMaciej S. Szmigiero     balloon->status_report.available = msgStatus->num_avail;
823*0d9e8c0bSMaciej S. Szmigiero     balloon->status_report.available *= HV_BALLOON_PAGE_SIZE;
824*0d9e8c0bSMaciej S. Szmigiero     balloon->status_report.received = true;
825*0d9e8c0bSMaciej S. Szmigiero 
826*0d9e8c0bSMaciej S. Szmigiero     /* report event */
827*0d9e8c0bSMaciej S. Szmigiero }
828*0d9e8c0bSMaciej S. Szmigiero 
829*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_unballoon_response(HvBalloon *balloon,
830*0d9e8c0bSMaciej S. Szmigiero                                                  HvBalloonReq *req,
831*0d9e8c0bSMaciej S. Szmigiero                                                  StateDesc *stdesc)
832*0d9e8c0bSMaciej S. Szmigiero {
833*0d9e8c0bSMaciej S. Szmigiero     VMBusChanReq *vmreq = &req->vmreq;
834*0d9e8c0bSMaciej S. Szmigiero     struct dm_unballoon_response *msgUrR = vmreq->msg;
835*0d9e8c0bSMaciej S. Szmigiero 
836*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state != S_UNBALLOON_REPLY_WAIT) {
837*0d9e8c0bSMaciej S. Szmigiero         warn_report("unexpected DM_UNBALLOON_RESPONSE in %d state",
838*0d9e8c0bSMaciej S. Szmigiero                     balloon->state);
839*0d9e8c0bSMaciej S. Szmigiero         return;
840*0d9e8c0bSMaciej S. Szmigiero     }
841*0d9e8c0bSMaciej S. Szmigiero 
842*0d9e8c0bSMaciej S. Szmigiero     if (!hv_balloon_handle_msg_size(req, sizeof(*msgUrR),
843*0d9e8c0bSMaciej S. Szmigiero                                     "DM_UNBALLOON_RESPONSE"))
844*0d9e8c0bSMaciej S. Szmigiero         return;
845*0d9e8c0bSMaciej S. Szmigiero 
846*0d9e8c0bSMaciej S. Szmigiero     trace_hv_balloon_incoming_unballoon(msgUrR->hdr.trans_id);
847*0d9e8c0bSMaciej S. Szmigiero 
848*0d9e8c0bSMaciej S. Szmigiero     balloon->trans_id++;
849*0d9e8c0bSMaciej S. Szmigiero 
850*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE);
851*0d9e8c0bSMaciej S. Szmigiero }
852*0d9e8c0bSMaciej S. Szmigiero 
853*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_balloon_response(HvBalloon *balloon,
854*0d9e8c0bSMaciej S. Szmigiero                                                HvBalloonReq *req,
855*0d9e8c0bSMaciej S. Szmigiero                                                StateDesc *stdesc)
856*0d9e8c0bSMaciej S. Szmigiero {
857*0d9e8c0bSMaciej S. Szmigiero     VMBusChanReq *vmreq = &req->vmreq;
858*0d9e8c0bSMaciej S. Szmigiero     struct dm_balloon_response *msgBR = vmreq->msg;
859*0d9e8c0bSMaciej S. Szmigiero 
860*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state != S_BALLOON_REPLY_WAIT) {
861*0d9e8c0bSMaciej S. Szmigiero         warn_report("unexpected DM_BALLOON_RESPONSE in %d state",
862*0d9e8c0bSMaciej S. Szmigiero                     balloon->state);
863*0d9e8c0bSMaciej S. Szmigiero         return;
864*0d9e8c0bSMaciej S. Szmigiero     }
865*0d9e8c0bSMaciej S. Szmigiero 
866*0d9e8c0bSMaciej S. Szmigiero     if (!hv_balloon_handle_msg_size(req, sizeof(*msgBR),
867*0d9e8c0bSMaciej S. Szmigiero                                     "DM_BALLOON_RESPONSE"))
868*0d9e8c0bSMaciej S. Szmigiero         return;
869*0d9e8c0bSMaciej S. Szmigiero 
870*0d9e8c0bSMaciej S. Szmigiero     trace_hv_balloon_incoming_balloon(msgBR->hdr.trans_id, msgBR->range_count,
871*0d9e8c0bSMaciej S. Szmigiero                                       msgBR->more_pages);
872*0d9e8c0bSMaciej S. Szmigiero 
873*0d9e8c0bSMaciej S. Szmigiero     if (vmreq->msglen < sizeof(*msgBR) +
874*0d9e8c0bSMaciej S. Szmigiero         (uint64_t)sizeof(msgBR->range_array[0]) * msgBR->range_count) {
875*0d9e8c0bSMaciej S. Szmigiero         warn_report("DM_BALLOON_RESPONSE too short for the range count");
876*0d9e8c0bSMaciej S. Szmigiero         return;
877*0d9e8c0bSMaciej S. Szmigiero     }
878*0d9e8c0bSMaciej S. Szmigiero 
879*0d9e8c0bSMaciej S. Szmigiero     if (msgBR->range_count == 0) {
880*0d9e8c0bSMaciej S. Szmigiero         /* The guest is already at its minimum size */
881*0d9e8c0bSMaciej S. Szmigiero         balloon->balloon_diff = 0;
882*0d9e8c0bSMaciej S. Szmigiero         goto ret_end_trans;
883*0d9e8c0bSMaciej S. Szmigiero     } else {
884*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_handle_remove_ranges(balloon,
885*0d9e8c0bSMaciej S. Szmigiero                                         msgBR->range_array,
886*0d9e8c0bSMaciej S. Szmigiero                                         msgBR->range_count);
887*0d9e8c0bSMaciej S. Szmigiero     }
888*0d9e8c0bSMaciej S. Szmigiero 
889*0d9e8c0bSMaciej S. Szmigiero     /* More responses expected? */
890*0d9e8c0bSMaciej S. Szmigiero     if (msgBR->more_pages) {
891*0d9e8c0bSMaciej S. Szmigiero         return;
892*0d9e8c0bSMaciej S. Szmigiero     }
893*0d9e8c0bSMaciej S. Szmigiero 
894*0d9e8c0bSMaciej S. Szmigiero ret_end_trans:
895*0d9e8c0bSMaciej S. Szmigiero     balloon->trans_id++;
896*0d9e8c0bSMaciej S. Szmigiero 
897*0d9e8c0bSMaciej S. Szmigiero     if (balloon->balloon_diff > 0) {
898*0d9e8c0bSMaciej S. Szmigiero         HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT);
899*0d9e8c0bSMaciej S. Szmigiero     } else {
900*0d9e8c0bSMaciej S. Szmigiero         HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE);
901*0d9e8c0bSMaciej S. Szmigiero     }
902*0d9e8c0bSMaciej S. Szmigiero }
903*0d9e8c0bSMaciej S. Szmigiero 
904*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_packet(HvBalloon *balloon, HvBalloonReq *req,
905*0d9e8c0bSMaciej S. Szmigiero                                      StateDesc *stdesc)
906*0d9e8c0bSMaciej S. Szmigiero {
907*0d9e8c0bSMaciej S. Szmigiero     VMBusChanReq *vmreq = &req->vmreq;
908*0d9e8c0bSMaciej S. Szmigiero     struct dm_message *msg = vmreq->msg;
909*0d9e8c0bSMaciej S. Szmigiero 
910*0d9e8c0bSMaciej S. Szmigiero     if (vmreq->msglen < sizeof(msg->hdr)) {
911*0d9e8c0bSMaciej S. Szmigiero         return;
912*0d9e8c0bSMaciej S. Szmigiero     }
913*0d9e8c0bSMaciej S. Szmigiero 
914*0d9e8c0bSMaciej S. Szmigiero     switch (msg->hdr.type) {
915*0d9e8c0bSMaciej S. Szmigiero     case DM_VERSION_REQUEST:
916*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_handle_version_request(balloon, req, stdesc);
917*0d9e8c0bSMaciej S. Szmigiero         break;
918*0d9e8c0bSMaciej S. Szmigiero 
919*0d9e8c0bSMaciej S. Szmigiero     case DM_CAPABILITIES_REPORT:
920*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_handle_caps_report(balloon, req, stdesc);
921*0d9e8c0bSMaciej S. Szmigiero         break;
922*0d9e8c0bSMaciej S. Szmigiero 
923*0d9e8c0bSMaciej S. Szmigiero     case DM_STATUS_REPORT:
924*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_handle_status_report(balloon, req);
925*0d9e8c0bSMaciej S. Szmigiero         break;
926*0d9e8c0bSMaciej S. Szmigiero 
927*0d9e8c0bSMaciej S. Szmigiero     case DM_UNBALLOON_RESPONSE:
928*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_handle_unballoon_response(balloon, req, stdesc);
929*0d9e8c0bSMaciej S. Szmigiero         break;
930*0d9e8c0bSMaciej S. Szmigiero 
931*0d9e8c0bSMaciej S. Szmigiero     case DM_BALLOON_RESPONSE:
932*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_handle_balloon_response(balloon, req, stdesc);
933*0d9e8c0bSMaciej S. Szmigiero         break;
934*0d9e8c0bSMaciej S. Szmigiero 
935*0d9e8c0bSMaciej S. Szmigiero     default:
936*0d9e8c0bSMaciej S. Szmigiero         warn_report("unknown DM message %u", msg->hdr.type);
937*0d9e8c0bSMaciej S. Szmigiero         break;
938*0d9e8c0bSMaciej S. Szmigiero     }
939*0d9e8c0bSMaciej S. Szmigiero }
940*0d9e8c0bSMaciej S. Szmigiero 
941*0d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_recv_channel(HvBalloon *balloon, StateDesc *stdesc)
942*0d9e8c0bSMaciej S. Szmigiero {
943*0d9e8c0bSMaciej S. Szmigiero     VMBusChannel *chan;
944*0d9e8c0bSMaciej S. Szmigiero     HvBalloonReq *req;
945*0d9e8c0bSMaciej S. Szmigiero 
946*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state == S_WAIT_RESET ||
947*0d9e8c0bSMaciej S. Szmigiero         balloon->state == S_POST_RESET_CLOSED) {
948*0d9e8c0bSMaciej S. Szmigiero         return false;
949*0d9e8c0bSMaciej S. Szmigiero     }
950*0d9e8c0bSMaciej S. Szmigiero 
951*0d9e8c0bSMaciej S. Szmigiero     chan = hv_balloon_get_channel(balloon);
952*0d9e8c0bSMaciej S. Szmigiero     if (vmbus_channel_recv_start(chan)) {
953*0d9e8c0bSMaciej S. Szmigiero         return false;
954*0d9e8c0bSMaciej S. Szmigiero     }
955*0d9e8c0bSMaciej S. Szmigiero 
956*0d9e8c0bSMaciej S. Szmigiero     while ((req = vmbus_channel_recv_peek(chan, sizeof(*req)))) {
957*0d9e8c0bSMaciej S. Szmigiero         hv_balloon_handle_packet(balloon, req, stdesc);
958*0d9e8c0bSMaciej S. Szmigiero         vmbus_free_req(req);
959*0d9e8c0bSMaciej S. Szmigiero         vmbus_channel_recv_pop(chan);
960*0d9e8c0bSMaciej S. Szmigiero 
961*0d9e8c0bSMaciej S. Szmigiero         if (stdesc->state != S_NO_CHANGE) {
962*0d9e8c0bSMaciej S. Szmigiero             break;
963*0d9e8c0bSMaciej S. Szmigiero         }
964*0d9e8c0bSMaciej S. Szmigiero     }
965*0d9e8c0bSMaciej S. Szmigiero 
966*0d9e8c0bSMaciej S. Szmigiero     return vmbus_channel_recv_done(chan) > 0;
967*0d9e8c0bSMaciej S. Szmigiero }
968*0d9e8c0bSMaciej S. Szmigiero 
969*0d9e8c0bSMaciej S. Szmigiero /* old state handler -> new state transition (potential) */
970*0d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_event_loop_state(HvBalloon *balloon)
971*0d9e8c0bSMaciej S. Szmigiero {
972*0d9e8c0bSMaciej S. Szmigiero     StateDesc state_new = HV_BALLOON_STATE_DESC_INIT;
973*0d9e8c0bSMaciej S. Szmigiero 
974*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_handle_state(balloon, &state_new);
975*0d9e8c0bSMaciej S. Szmigiero     return hv_balloon_state_set(balloon, state_new.state, state_new.desc);
976*0d9e8c0bSMaciej S. Szmigiero }
977*0d9e8c0bSMaciej S. Szmigiero 
978*0d9e8c0bSMaciej S. Szmigiero /* VMBus message -> new state transition (potential) */
979*0d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_event_loop_recv(HvBalloon *balloon)
980*0d9e8c0bSMaciej S. Szmigiero {
981*0d9e8c0bSMaciej S. Szmigiero     StateDesc state_new = HV_BALLOON_STATE_DESC_INIT;
982*0d9e8c0bSMaciej S. Szmigiero     bool any_recv, state_changed;
983*0d9e8c0bSMaciej S. Szmigiero 
984*0d9e8c0bSMaciej S. Szmigiero     any_recv = hv_balloon_recv_channel(balloon, &state_new);
985*0d9e8c0bSMaciej S. Szmigiero     state_changed = hv_balloon_state_set(balloon,
986*0d9e8c0bSMaciej S. Szmigiero                                          state_new.state, state_new.desc);
987*0d9e8c0bSMaciej S. Szmigiero 
988*0d9e8c0bSMaciej S. Szmigiero     return state_changed || any_recv;
989*0d9e8c0bSMaciej S. Szmigiero }
990*0d9e8c0bSMaciej S. Szmigiero 
991*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_event_loop(HvBalloon *balloon)
992*0d9e8c0bSMaciej S. Szmigiero {
993*0d9e8c0bSMaciej S. Szmigiero     bool state_repeat, recv_repeat;
994*0d9e8c0bSMaciej S. Szmigiero 
995*0d9e8c0bSMaciej S. Szmigiero     do {
996*0d9e8c0bSMaciej S. Szmigiero         state_repeat = hv_balloon_event_loop_state(balloon);
997*0d9e8c0bSMaciej S. Szmigiero         recv_repeat = hv_balloon_event_loop_recv(balloon);
998*0d9e8c0bSMaciej S. Szmigiero     } while (state_repeat || recv_repeat);
999*0d9e8c0bSMaciej S. Szmigiero }
1000*0d9e8c0bSMaciej S. Szmigiero 
1001*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_chan_notify(VMBusChannel *chan)
1002*0d9e8c0bSMaciej S. Szmigiero {
1003*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan));
1004*0d9e8c0bSMaciej S. Szmigiero 
1005*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_event_loop(balloon);
1006*0d9e8c0bSMaciej S. Szmigiero }
1007*0d9e8c0bSMaciej S. Szmigiero 
1008*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_stat(void *opaque, BalloonInfo *info)
1009*0d9e8c0bSMaciej S. Szmigiero {
1010*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = opaque;
1011*0d9e8c0bSMaciej S. Szmigiero     info->actual = (hv_balloon_total_ram(balloon) - balloon->removed_both_ctr)
1012*0d9e8c0bSMaciej S. Szmigiero         << HV_BALLOON_PFN_SHIFT;
1013*0d9e8c0bSMaciej S. Szmigiero }
1014*0d9e8c0bSMaciej S. Szmigiero 
1015*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_to_target(void *opaque, ram_addr_t target)
1016*0d9e8c0bSMaciej S. Szmigiero {
1017*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = opaque;
1018*0d9e8c0bSMaciej S. Szmigiero     uint64_t target_pages = target >> HV_BALLOON_PFN_SHIFT;
1019*0d9e8c0bSMaciej S. Szmigiero 
1020*0d9e8c0bSMaciej S. Szmigiero     if (!target_pages) {
1021*0d9e8c0bSMaciej S. Szmigiero         return;
1022*0d9e8c0bSMaciej S. Szmigiero     }
1023*0d9e8c0bSMaciej S. Szmigiero 
1024*0d9e8c0bSMaciej S. Szmigiero     /*
1025*0d9e8c0bSMaciej S. Szmigiero      * always set target_changed, even with unchanged target, as the user
1026*0d9e8c0bSMaciej S. Szmigiero      * might be asking us to try again reaching it
1027*0d9e8c0bSMaciej S. Szmigiero      */
1028*0d9e8c0bSMaciej S. Szmigiero     balloon->target = target_pages;
1029*0d9e8c0bSMaciej S. Szmigiero     balloon->target_changed = true;
1030*0d9e8c0bSMaciej S. Szmigiero 
1031*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_event_loop(balloon);
1032*0d9e8c0bSMaciej S. Szmigiero }
1033*0d9e8c0bSMaciej S. Szmigiero 
1034*0d9e8c0bSMaciej S. Szmigiero static int hv_balloon_vmdev_open_channel(VMBusChannel *chan)
1035*0d9e8c0bSMaciej S. Szmigiero {
1036*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan));
1037*0d9e8c0bSMaciej S. Szmigiero 
1038*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state != S_POST_RESET_CLOSED) {
1039*0d9e8c0bSMaciej S. Szmigiero         warn_report("guest trying to open a DM channel in invalid %d state",
1040*0d9e8c0bSMaciej S. Szmigiero                     balloon->state);
1041*0d9e8c0bSMaciej S. Szmigiero         return -EINVAL;
1042*0d9e8c0bSMaciej S. Szmigiero     }
1043*0d9e8c0bSMaciej S. Szmigiero 
1044*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_SET_STATE(balloon, S_VERSION);
1045*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_event_loop(balloon);
1046*0d9e8c0bSMaciej S. Szmigiero 
1047*0d9e8c0bSMaciej S. Szmigiero     return 0;
1048*0d9e8c0bSMaciej S. Szmigiero }
1049*0d9e8c0bSMaciej S. Szmigiero 
1050*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_close_channel(VMBusChannel *chan)
1051*0d9e8c0bSMaciej S. Szmigiero {
1052*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan));
1053*0d9e8c0bSMaciej S. Szmigiero 
1054*0d9e8c0bSMaciej S. Szmigiero     timer_del(&balloon->post_init_timer);
1055*0d9e8c0bSMaciej S. Szmigiero 
1056*0d9e8c0bSMaciej S. Szmigiero     /* Don't report stale data */
1057*0d9e8c0bSMaciej S. Szmigiero     balloon->status_report.received = false;
1058*0d9e8c0bSMaciej S. Szmigiero 
1059*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_SET_STATE(balloon, S_WAIT_RESET);
1060*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_event_loop(balloon);
1061*0d9e8c0bSMaciej S. Szmigiero }
1062*0d9e8c0bSMaciej S. Szmigiero 
1063*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_post_init_timer(void *opaque)
1064*0d9e8c0bSMaciej S. Szmigiero {
1065*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = opaque;
1066*0d9e8c0bSMaciej S. Szmigiero 
1067*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state != S_POST_INIT_WAIT) {
1068*0d9e8c0bSMaciej S. Szmigiero         return;
1069*0d9e8c0bSMaciej S. Szmigiero     }
1070*0d9e8c0bSMaciej S. Szmigiero 
1071*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_SET_STATE(balloon, S_IDLE);
1072*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_event_loop(balloon);
1073*0d9e8c0bSMaciej S. Szmigiero }
1074*0d9e8c0bSMaciej S. Szmigiero 
1075*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_realize(VMBusDevice *vdev, Error **errp)
1076*0d9e8c0bSMaciej S. Szmigiero {
1077*0d9e8c0bSMaciej S. Szmigiero     ERRP_GUARD();
1078*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = HV_BALLOON(vdev);
1079*0d9e8c0bSMaciej S. Szmigiero     int ret;
1080*0d9e8c0bSMaciej S. Szmigiero 
1081*0d9e8c0bSMaciej S. Szmigiero     balloon->state = S_WAIT_RESET;
1082*0d9e8c0bSMaciej S. Szmigiero 
1083*0d9e8c0bSMaciej S. Szmigiero     ret = qemu_add_balloon_handler(hv_balloon_to_target, hv_balloon_stat,
1084*0d9e8c0bSMaciej S. Szmigiero                                    balloon);
1085*0d9e8c0bSMaciej S. Szmigiero     if (ret < 0) {
1086*0d9e8c0bSMaciej S. Szmigiero         /* This also protects against having multiple hv-balloon instances */
1087*0d9e8c0bSMaciej S. Szmigiero         error_setg(errp, "Only one balloon device is supported");
1088*0d9e8c0bSMaciej S. Szmigiero         return;
1089*0d9e8c0bSMaciej S. Szmigiero     }
1090*0d9e8c0bSMaciej S. Szmigiero 
1091*0d9e8c0bSMaciej S. Szmigiero     timer_init_ms(&balloon->post_init_timer, QEMU_CLOCK_VIRTUAL,
1092*0d9e8c0bSMaciej S. Szmigiero                   hv_balloon_post_init_timer, balloon);
1093*0d9e8c0bSMaciej S. Szmigiero }
1094*0d9e8c0bSMaciej S. Szmigiero 
1095*0d9e8c0bSMaciej S. Szmigiero /*
1096*0d9e8c0bSMaciej S. Szmigiero  * VMBus device reset has to be implemented in case the guest decides to
1097*0d9e8c0bSMaciej S. Szmigiero  * disconnect and reconnect to the VMBus without rebooting the whole system.
1098*0d9e8c0bSMaciej S. Szmigiero  */
1099*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_reset(VMBusDevice *vdev)
1100*0d9e8c0bSMaciej S. Szmigiero {
1101*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = HV_BALLOON(vdev);
1102*0d9e8c0bSMaciej S. Szmigiero 
1103*0d9e8c0bSMaciej S. Szmigiero     if (balloon->state == S_POST_RESET_CLOSED) {
1104*0d9e8c0bSMaciej S. Szmigiero         return;
1105*0d9e8c0bSMaciej S. Szmigiero     }
1106*0d9e8c0bSMaciej S. Szmigiero 
1107*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_destroy(&balloon->removed_guest);
1108*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_destroy(&balloon->removed_both);
1109*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_init(&balloon->removed_guest);
1110*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_init(&balloon->removed_both);
1111*0d9e8c0bSMaciej S. Szmigiero 
1112*0d9e8c0bSMaciej S. Szmigiero     balloon->trans_id = 0;
1113*0d9e8c0bSMaciej S. Szmigiero     balloon->removed_guest_ctr = 0;
1114*0d9e8c0bSMaciej S. Szmigiero     balloon->removed_both_ctr = 0;
1115*0d9e8c0bSMaciej S. Szmigiero 
1116*0d9e8c0bSMaciej S. Szmigiero     HV_BALLOON_SET_STATE(balloon, S_POST_RESET_CLOSED);
1117*0d9e8c0bSMaciej S. Szmigiero     hv_balloon_event_loop(balloon);
1118*0d9e8c0bSMaciej S. Szmigiero }
1119*0d9e8c0bSMaciej S. Szmigiero 
1120*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_unrealize(VMBusDevice *vdev)
1121*0d9e8c0bSMaciej S. Szmigiero {
1122*0d9e8c0bSMaciej S. Szmigiero     HvBalloon *balloon = HV_BALLOON(vdev);
1123*0d9e8c0bSMaciej S. Szmigiero 
1124*0d9e8c0bSMaciej S. Szmigiero     qemu_remove_balloon_handler(balloon);
1125*0d9e8c0bSMaciej S. Szmigiero 
1126*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_destroy(&balloon->removed_guest);
1127*0d9e8c0bSMaciej S. Szmigiero     hvb_page_range_tree_destroy(&balloon->removed_both);
1128*0d9e8c0bSMaciej S. Szmigiero }
1129*0d9e8c0bSMaciej S. Szmigiero 
1130*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_init(Object *obj)
1131*0d9e8c0bSMaciej S. Szmigiero {
1132*0d9e8c0bSMaciej S. Szmigiero }
1133*0d9e8c0bSMaciej S. Szmigiero 
1134*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_finalize(Object *obj)
1135*0d9e8c0bSMaciej S. Szmigiero {
1136*0d9e8c0bSMaciej S. Szmigiero }
1137*0d9e8c0bSMaciej S. Szmigiero 
1138*0d9e8c0bSMaciej S. Szmigiero static Property hv_balloon_properties[] = {
1139*0d9e8c0bSMaciej S. Szmigiero     DEFINE_PROP_BOOL("status-report", HvBalloon,
1140*0d9e8c0bSMaciej S. Szmigiero                      status_report.enabled, false),
1141*0d9e8c0bSMaciej S. Szmigiero 
1142*0d9e8c0bSMaciej S. Szmigiero     DEFINE_PROP_END_OF_LIST(),
1143*0d9e8c0bSMaciej S. Szmigiero };
1144*0d9e8c0bSMaciej S. Szmigiero 
1145*0d9e8c0bSMaciej S. Szmigiero static void hv_balloon_class_init(ObjectClass *klass, void *data)
1146*0d9e8c0bSMaciej S. Szmigiero {
1147*0d9e8c0bSMaciej S. Szmigiero     DeviceClass *dc = DEVICE_CLASS(klass);
1148*0d9e8c0bSMaciej S. Szmigiero     VMBusDeviceClass *vdc = VMBUS_DEVICE_CLASS(klass);
1149*0d9e8c0bSMaciej S. Szmigiero 
1150*0d9e8c0bSMaciej S. Szmigiero     device_class_set_props(dc, hv_balloon_properties);
1151*0d9e8c0bSMaciej S. Szmigiero     qemu_uuid_parse(HV_BALLOON_GUID, &vdc->classid);
1152*0d9e8c0bSMaciej S. Szmigiero     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1153*0d9e8c0bSMaciej S. Szmigiero 
1154*0d9e8c0bSMaciej S. Szmigiero     vdc->vmdev_realize = hv_balloon_vmdev_realize;
1155*0d9e8c0bSMaciej S. Szmigiero     vdc->vmdev_unrealize = hv_balloon_vmdev_unrealize;
1156*0d9e8c0bSMaciej S. Szmigiero     vdc->vmdev_reset = hv_balloon_vmdev_reset;
1157*0d9e8c0bSMaciej S. Szmigiero     vdc->open_channel = hv_balloon_vmdev_open_channel;
1158*0d9e8c0bSMaciej S. Szmigiero     vdc->close_channel = hv_balloon_vmdev_close_channel;
1159*0d9e8c0bSMaciej S. Szmigiero     vdc->chan_notify_cb = hv_balloon_vmdev_chan_notify;
1160*0d9e8c0bSMaciej S. Szmigiero }
1161