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, §ion, 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