10d9e8c0bSMaciej S. Szmigiero /* 20d9e8c0bSMaciej S. Szmigiero * QEMU Hyper-V Dynamic Memory Protocol driver 30d9e8c0bSMaciej S. Szmigiero * 40d9e8c0bSMaciej S. Szmigiero * Copyright (C) 2020-2023 Oracle and/or its affiliates. 50d9e8c0bSMaciej S. Szmigiero * 60d9e8c0bSMaciej S. Szmigiero * This work is licensed under the terms of the GNU GPL, version 2 or later. 70d9e8c0bSMaciej S. Szmigiero * See the COPYING file in the top-level directory. 80d9e8c0bSMaciej S. Szmigiero */ 90d9e8c0bSMaciej S. Szmigiero 100d9e8c0bSMaciej S. Szmigiero #include "hv-balloon-internal.h" 110d9e8c0bSMaciej S. Szmigiero 120d9e8c0bSMaciej S. Szmigiero #include "exec/address-spaces.h" 130d9e8c0bSMaciej S. Szmigiero #include "exec/cpu-common.h" 140d9e8c0bSMaciej S. Szmigiero #include "exec/ramblock.h" 150d9e8c0bSMaciej S. Szmigiero #include "hw/boards.h" 160d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/dynmem-proto.h" 170d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/hv-balloon.h" 180d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/vmbus.h" 190d9e8c0bSMaciej S. Szmigiero #include "hw/mem/memory-device.h" 200d9e8c0bSMaciej S. Szmigiero #include "hw/mem/pc-dimm.h" 210d9e8c0bSMaciej S. Szmigiero #include "hw/qdev-core.h" 220d9e8c0bSMaciej S. Szmigiero #include "hw/qdev-properties.h" 230d9e8c0bSMaciej S. Szmigiero #include "monitor/qdev.h" 240d9e8c0bSMaciej S. Szmigiero #include "qapi/error.h" 250d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-commands-machine.h" 260d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-events-machine.h" 270d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-types-machine.h" 280d9e8c0bSMaciej S. Szmigiero #include "qapi/qmp/qdict.h" 290d9e8c0bSMaciej S. Szmigiero #include "qapi/visitor.h" 300d9e8c0bSMaciej S. Szmigiero #include "qemu/error-report.h" 310d9e8c0bSMaciej S. Szmigiero #include "qemu/module.h" 320d9e8c0bSMaciej S. Szmigiero #include "qemu/units.h" 330d9e8c0bSMaciej S. Szmigiero #include "qemu/timer.h" 340d9e8c0bSMaciej S. Szmigiero #include "sysemu/balloon.h" 350d9e8c0bSMaciej S. Szmigiero #include "sysemu/hostmem.h" 360d9e8c0bSMaciej S. Szmigiero #include "sysemu/reset.h" 37*99a4706aSMaciej S. Szmigiero #include "hv-balloon-our_range_memslots.h" 380d9e8c0bSMaciej S. Szmigiero #include "hv-balloon-page_range_tree.h" 390d9e8c0bSMaciej S. Szmigiero #include "trace.h" 400d9e8c0bSMaciej S. Szmigiero 41*99a4706aSMaciej S. Szmigiero #define HV_BALLOON_ADDR_PROP "addr" 42*99a4706aSMaciej S. Szmigiero #define HV_BALLOON_MEMDEV_PROP "memdev" 430d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_GUID "525074DC-8985-46e2-8057-A307DC18A502" 440d9e8c0bSMaciej S. Szmigiero 450d9e8c0bSMaciej S. Szmigiero /* 460d9e8c0bSMaciej S. Szmigiero * Some Windows versions (at least Server 2019) will crash with various 470d9e8c0bSMaciej S. Szmigiero * error codes when receiving DM protocol requests (at least 480d9e8c0bSMaciej S. Szmigiero * DM_MEM_HOT_ADD_REQUEST) immediately after boot. 490d9e8c0bSMaciej S. Szmigiero * 500d9e8c0bSMaciej S. Szmigiero * It looks like Hyper-V from Server 2016 uses a 50-second after-boot 510d9e8c0bSMaciej S. Szmigiero * delay, probably to workaround this issue, so we'll use this value, too. 520d9e8c0bSMaciej S. Szmigiero */ 530d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_POST_INIT_WAIT (50 * 1000) 540d9e8c0bSMaciej S. Szmigiero 550d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HA_CHUNK_SIZE (2 * GiB) 560d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HA_CHUNK_PAGES (HV_BALLOON_HA_CHUNK_SIZE / HV_BALLOON_PAGE_SIZE) 570d9e8c0bSMaciej S. Szmigiero 58*99a4706aSMaciej S. Szmigiero #define HV_BALLOON_HA_MEMSLOT_SIZE_ALIGN (128 * MiB) 59*99a4706aSMaciej S. Szmigiero 600d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HR_CHUNK_PAGES 585728 610d9e8c0bSMaciej S. Szmigiero /* 620d9e8c0bSMaciej S. Szmigiero * ^ that's the maximum number of pages 630d9e8c0bSMaciej S. Szmigiero * that Windows returns in one hot remove response 640d9e8c0bSMaciej S. Szmigiero * 650d9e8c0bSMaciej S. Szmigiero * If the number requested is too high Windows will no longer honor 660d9e8c0bSMaciej S. Szmigiero * these requests 670d9e8c0bSMaciej S. Szmigiero */ 680d9e8c0bSMaciej S. Szmigiero 690d9e8c0bSMaciej S. Szmigiero struct HvBalloonClass { 700d9e8c0bSMaciej S. Szmigiero VMBusDeviceClass parent_class; 710d9e8c0bSMaciej S. Szmigiero } HvBalloonClass; 720d9e8c0bSMaciej S. Szmigiero 730d9e8c0bSMaciej S. Szmigiero typedef enum State { 740d9e8c0bSMaciej S. Szmigiero /* not a real state */ 750d9e8c0bSMaciej S. Szmigiero S_NO_CHANGE = 0, 760d9e8c0bSMaciej S. Szmigiero 770d9e8c0bSMaciej S. Szmigiero S_WAIT_RESET, 780d9e8c0bSMaciej S. Szmigiero S_POST_RESET_CLOSED, 790d9e8c0bSMaciej S. Szmigiero 800d9e8c0bSMaciej S. Szmigiero /* init flow */ 810d9e8c0bSMaciej S. Szmigiero S_VERSION, 820d9e8c0bSMaciej S. Szmigiero S_CAPS, 830d9e8c0bSMaciej S. Szmigiero S_POST_INIT_WAIT, 840d9e8c0bSMaciej S. Szmigiero 850d9e8c0bSMaciej S. Szmigiero S_IDLE, 860d9e8c0bSMaciej S. Szmigiero 870d9e8c0bSMaciej S. Szmigiero /* balloon op flow */ 880d9e8c0bSMaciej S. Szmigiero S_BALLOON_POSTING, 890d9e8c0bSMaciej S. Szmigiero S_BALLOON_RB_WAIT, 900d9e8c0bSMaciej S. Szmigiero S_BALLOON_REPLY_WAIT, 910d9e8c0bSMaciej S. Szmigiero 920d9e8c0bSMaciej S. Szmigiero /* unballoon + hot add ops flow */ 930d9e8c0bSMaciej S. Szmigiero S_UNBALLOON_POSTING, 940d9e8c0bSMaciej S. Szmigiero S_UNBALLOON_RB_WAIT, 950d9e8c0bSMaciej S. Szmigiero S_UNBALLOON_REPLY_WAIT, 96*99a4706aSMaciej S. Szmigiero S_HOT_ADD_SETUP, 97*99a4706aSMaciej S. Szmigiero S_HOT_ADD_RB_WAIT, 98*99a4706aSMaciej S. Szmigiero S_HOT_ADD_POSTING, 99*99a4706aSMaciej S. Szmigiero S_HOT_ADD_REPLY_WAIT, 1000d9e8c0bSMaciej S. Szmigiero } State; 1010d9e8c0bSMaciej S. Szmigiero 1020d9e8c0bSMaciej S. Szmigiero typedef struct StateDesc { 1030d9e8c0bSMaciej S. Szmigiero State state; 1040d9e8c0bSMaciej S. Szmigiero const char *desc; 1050d9e8c0bSMaciej S. Szmigiero } StateDesc; 1060d9e8c0bSMaciej S. Szmigiero 1070d9e8c0bSMaciej S. Szmigiero typedef struct HvBalloon { 1080d9e8c0bSMaciej S. Szmigiero VMBusDevice parent; 1090d9e8c0bSMaciej S. Szmigiero State state; 1100d9e8c0bSMaciej S. Szmigiero 1110d9e8c0bSMaciej S. Szmigiero union dm_version version; 1120d9e8c0bSMaciej S. Szmigiero union dm_caps caps; 1130d9e8c0bSMaciej S. Szmigiero 1140d9e8c0bSMaciej S. Szmigiero QEMUTimer post_init_timer; 1150d9e8c0bSMaciej S. Szmigiero 1160d9e8c0bSMaciej S. Szmigiero unsigned int trans_id; 1170d9e8c0bSMaciej S. Szmigiero 1180d9e8c0bSMaciej S. Szmigiero struct { 1190d9e8c0bSMaciej S. Szmigiero bool enabled; 1200d9e8c0bSMaciej S. Szmigiero bool received; 1210d9e8c0bSMaciej S. Szmigiero uint64_t committed; 1220d9e8c0bSMaciej S. Szmigiero uint64_t available; 1230d9e8c0bSMaciej S. Szmigiero } status_report; 1240d9e8c0bSMaciej S. Szmigiero 1250d9e8c0bSMaciej S. Szmigiero /* Guest target size */ 1260d9e8c0bSMaciej S. Szmigiero uint64_t target; 1270d9e8c0bSMaciej S. Szmigiero bool target_changed; 1280d9e8c0bSMaciej S. Szmigiero 129*99a4706aSMaciej S. Szmigiero /* Current (un)balloon / hot-add operation parameters */ 1300d9e8c0bSMaciej S. Szmigiero union { 1310d9e8c0bSMaciej S. Szmigiero uint64_t balloon_diff; 1320d9e8c0bSMaciej S. Szmigiero 1330d9e8c0bSMaciej S. Szmigiero struct { 1340d9e8c0bSMaciej S. Szmigiero uint64_t unballoon_diff; 135*99a4706aSMaciej S. Szmigiero uint64_t hot_add_diff; 136*99a4706aSMaciej S. Szmigiero }; 137*99a4706aSMaciej S. Szmigiero 138*99a4706aSMaciej S. Szmigiero struct { 139*99a4706aSMaciej S. Szmigiero PageRange hot_add_range; 140*99a4706aSMaciej S. Szmigiero uint64_t ha_current_count; 1410d9e8c0bSMaciej S. Szmigiero }; 1420d9e8c0bSMaciej S. Szmigiero }; 1430d9e8c0bSMaciej S. Szmigiero 144*99a4706aSMaciej S. Szmigiero OurRangeMemslots *our_range; 145*99a4706aSMaciej S. Szmigiero 146*99a4706aSMaciej S. Szmigiero /* Count of memslots covering our memory */ 147*99a4706aSMaciej S. Szmigiero unsigned int memslot_count; 148*99a4706aSMaciej S. Szmigiero 1490d9e8c0bSMaciej S. Szmigiero /* Nominal size of each memslot (the last one might be smaller) */ 1500d9e8c0bSMaciej S. Szmigiero uint64_t memslot_size; 1510d9e8c0bSMaciej S. Szmigiero 152*99a4706aSMaciej S. Szmigiero /* Non-ours removed memory */ 1530d9e8c0bSMaciej S. Szmigiero PageRangeTree removed_guest, removed_both; 1540d9e8c0bSMaciej S. Szmigiero 155*99a4706aSMaciej S. Szmigiero /* Grand totals of removed memory (both ours and non-ours) */ 1560d9e8c0bSMaciej S. Szmigiero uint64_t removed_guest_ctr, removed_both_ctr; 157*99a4706aSMaciej S. Szmigiero 158*99a4706aSMaciej S. Szmigiero /* MEMORY_DEVICE props */ 159*99a4706aSMaciej S. Szmigiero uint64_t addr; 160*99a4706aSMaciej S. Szmigiero HostMemoryBackend *hostmem; 161*99a4706aSMaciej S. Szmigiero MemoryRegion *mr; 1620d9e8c0bSMaciej S. Szmigiero } HvBalloon; 1630d9e8c0bSMaciej S. Szmigiero 1640d9e8c0bSMaciej S. Szmigiero OBJECT_DEFINE_TYPE_WITH_INTERFACES(HvBalloon, hv_balloon, HV_BALLOON, VMBUS_DEVICE, \ 165*99a4706aSMaciej S. Szmigiero { TYPE_MEMORY_DEVICE }, { }) 1660d9e8c0bSMaciej S. Szmigiero 1670d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_SET_STATE(hvb, news) \ 1680d9e8c0bSMaciej S. Szmigiero do { \ 1690d9e8c0bSMaciej S. Szmigiero assert(news != S_NO_CHANGE); \ 1700d9e8c0bSMaciej S. Szmigiero hv_balloon_state_set(hvb, news, # news); \ 1710d9e8c0bSMaciej S. Szmigiero } while (0) 1720d9e8c0bSMaciej S. Szmigiero 1730d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_STATE_DESC_SET(stdesc, news) \ 1740d9e8c0bSMaciej S. Szmigiero _hv_balloon_state_desc_set(stdesc, news, # news) 1750d9e8c0bSMaciej S. Szmigiero 1760d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_STATE_DESC_INIT \ 1770d9e8c0bSMaciej S. Szmigiero { \ 1780d9e8c0bSMaciej S. Szmigiero .state = S_NO_CHANGE, \ 1790d9e8c0bSMaciej S. Szmigiero } 1800d9e8c0bSMaciej S. Szmigiero 1810d9e8c0bSMaciej S. Szmigiero typedef struct HvBalloonReq { 1820d9e8c0bSMaciej S. Szmigiero VMBusChanReq vmreq; 1830d9e8c0bSMaciej S. Szmigiero } HvBalloonReq; 1840d9e8c0bSMaciej S. Szmigiero 185*99a4706aSMaciej S. Szmigiero /* total our memory includes parts currently removed from the guest */ 186*99a4706aSMaciej S. Szmigiero static uint64_t hv_balloon_total_our_ram(HvBalloon *balloon) 187*99a4706aSMaciej S. Szmigiero { 188*99a4706aSMaciej S. Szmigiero if (!balloon->our_range) { 189*99a4706aSMaciej S. Szmigiero return 0; 190*99a4706aSMaciej S. Szmigiero } 191*99a4706aSMaciej S. Szmigiero 192*99a4706aSMaciej S. Szmigiero return balloon->our_range->range.added; 193*99a4706aSMaciej S. Szmigiero } 194*99a4706aSMaciej S. Szmigiero 1950d9e8c0bSMaciej S. Szmigiero /* TODO: unify the code below with virtio-balloon and cache the value */ 1960d9e8c0bSMaciej S. Szmigiero static int build_dimm_list(Object *obj, void *opaque) 1970d9e8c0bSMaciej S. Szmigiero { 1980d9e8c0bSMaciej S. Szmigiero GSList **list = opaque; 1990d9e8c0bSMaciej S. Szmigiero 2000d9e8c0bSMaciej S. Szmigiero if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { 2010d9e8c0bSMaciej S. Szmigiero DeviceState *dev = DEVICE(obj); 2020d9e8c0bSMaciej S. Szmigiero if (dev->realized) { /* only realized DIMMs matter */ 2030d9e8c0bSMaciej S. Szmigiero *list = g_slist_prepend(*list, dev); 2040d9e8c0bSMaciej S. Szmigiero } 2050d9e8c0bSMaciej S. Szmigiero } 2060d9e8c0bSMaciej S. Szmigiero 2070d9e8c0bSMaciej S. Szmigiero object_child_foreach(obj, build_dimm_list, opaque); 2080d9e8c0bSMaciej S. Szmigiero return 0; 2090d9e8c0bSMaciej S. Szmigiero } 2100d9e8c0bSMaciej S. Szmigiero 2110d9e8c0bSMaciej S. Szmigiero static ram_addr_t get_current_ram_size(void) 2120d9e8c0bSMaciej S. Szmigiero { 2130d9e8c0bSMaciej S. Szmigiero GSList *list = NULL, *item; 2140d9e8c0bSMaciej S. Szmigiero ram_addr_t size = current_machine->ram_size; 2150d9e8c0bSMaciej S. Szmigiero 2160d9e8c0bSMaciej S. Szmigiero build_dimm_list(qdev_get_machine(), &list); 2170d9e8c0bSMaciej S. Szmigiero for (item = list; item; item = g_slist_next(item)) { 2180d9e8c0bSMaciej S. Szmigiero Object *obj = OBJECT(item->data); 2190d9e8c0bSMaciej S. Szmigiero if (!strcmp(object_get_typename(obj), TYPE_PC_DIMM)) 2200d9e8c0bSMaciej S. Szmigiero size += object_property_get_int(obj, PC_DIMM_SIZE_PROP, 2210d9e8c0bSMaciej S. Szmigiero &error_abort); 2220d9e8c0bSMaciej S. Szmigiero } 2230d9e8c0bSMaciej S. Szmigiero g_slist_free(list); 2240d9e8c0bSMaciej S. Szmigiero 2250d9e8c0bSMaciej S. Szmigiero return size; 2260d9e8c0bSMaciej S. Szmigiero } 2270d9e8c0bSMaciej S. Szmigiero 2280d9e8c0bSMaciej S. Szmigiero /* total RAM includes memory currently removed from the guest */ 2290d9e8c0bSMaciej S. Szmigiero static uint64_t hv_balloon_total_ram(HvBalloon *balloon) 2300d9e8c0bSMaciej S. Szmigiero { 2310d9e8c0bSMaciej S. Szmigiero ram_addr_t ram_size = get_current_ram_size(); 2320d9e8c0bSMaciej S. Szmigiero uint64_t ram_size_pages = ram_size >> HV_BALLOON_PFN_SHIFT; 233*99a4706aSMaciej S. Szmigiero uint64_t our_ram_size_pages = hv_balloon_total_our_ram(balloon); 2340d9e8c0bSMaciej S. Szmigiero 2350d9e8c0bSMaciej S. Szmigiero assert(ram_size_pages > 0); 2360d9e8c0bSMaciej S. Szmigiero 237*99a4706aSMaciej S. Szmigiero return SUM_SATURATE_U64(ram_size_pages, our_ram_size_pages); 2380d9e8c0bSMaciej S. Szmigiero } 2390d9e8c0bSMaciej S. Szmigiero 2400d9e8c0bSMaciej S. Szmigiero /* 2410d9e8c0bSMaciej S. Szmigiero * calculating the total RAM size is a slow operation, 2420d9e8c0bSMaciej S. Szmigiero * avoid it as much as possible 2430d9e8c0bSMaciej S. Szmigiero */ 2440d9e8c0bSMaciej S. Szmigiero static uint64_t hv_balloon_total_removed_rs(HvBalloon *balloon, 2450d9e8c0bSMaciej S. Szmigiero uint64_t ram_size_pages) 2460d9e8c0bSMaciej S. Szmigiero { 2470d9e8c0bSMaciej S. Szmigiero uint64_t total_removed; 2480d9e8c0bSMaciej S. Szmigiero 2490d9e8c0bSMaciej S. Szmigiero total_removed = SUM_SATURATE_U64(balloon->removed_guest_ctr, 2500d9e8c0bSMaciej S. Szmigiero balloon->removed_both_ctr); 2510d9e8c0bSMaciej S. Szmigiero 2520d9e8c0bSMaciej S. Szmigiero /* possible if guest returns pages outside actual RAM */ 2530d9e8c0bSMaciej S. Szmigiero if (total_removed > ram_size_pages) { 2540d9e8c0bSMaciej S. Szmigiero total_removed = ram_size_pages; 2550d9e8c0bSMaciej S. Szmigiero } 2560d9e8c0bSMaciej S. Szmigiero 2570d9e8c0bSMaciej S. Szmigiero return total_removed; 2580d9e8c0bSMaciej S. Szmigiero } 2590d9e8c0bSMaciej S. Szmigiero 2600d9e8c0bSMaciej S. Szmigiero /* Returns whether the state has actually changed */ 2610d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_state_set(HvBalloon *balloon, 2620d9e8c0bSMaciej S. Szmigiero State newst, const char *newststr) 2630d9e8c0bSMaciej S. Szmigiero { 2640d9e8c0bSMaciej S. Szmigiero if (newst == S_NO_CHANGE || balloon->state == newst) { 2650d9e8c0bSMaciej S. Szmigiero return false; 2660d9e8c0bSMaciej S. Szmigiero } 2670d9e8c0bSMaciej S. Szmigiero 2680d9e8c0bSMaciej S. Szmigiero balloon->state = newst; 2690d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_state_change(newststr); 2700d9e8c0bSMaciej S. Szmigiero return true; 2710d9e8c0bSMaciej S. Szmigiero } 2720d9e8c0bSMaciej S. Szmigiero 2730d9e8c0bSMaciej S. Szmigiero static void _hv_balloon_state_desc_set(StateDesc *stdesc, 2740d9e8c0bSMaciej S. Szmigiero State newst, const char *newststr) 2750d9e8c0bSMaciej S. Szmigiero { 2760d9e8c0bSMaciej S. Szmigiero /* state setting is only permitted on a freshly init desc */ 2770d9e8c0bSMaciej S. Szmigiero assert(stdesc->state == S_NO_CHANGE); 2780d9e8c0bSMaciej S. Szmigiero 2790d9e8c0bSMaciej S. Szmigiero assert(newst != S_NO_CHANGE); 2800d9e8c0bSMaciej S. Szmigiero 2810d9e8c0bSMaciej S. Szmigiero stdesc->state = newst; 2820d9e8c0bSMaciej S. Szmigiero stdesc->desc = newststr; 2830d9e8c0bSMaciej S. Szmigiero } 2840d9e8c0bSMaciej S. Szmigiero 2850d9e8c0bSMaciej S. Szmigiero static VMBusChannel *hv_balloon_get_channel_maybe(HvBalloon *balloon) 2860d9e8c0bSMaciej S. Szmigiero { 2870d9e8c0bSMaciej S. Szmigiero return vmbus_device_channel(&balloon->parent, 0); 2880d9e8c0bSMaciej S. Szmigiero } 2890d9e8c0bSMaciej S. Szmigiero 2900d9e8c0bSMaciej S. Szmigiero static VMBusChannel *hv_balloon_get_channel(HvBalloon *balloon) 2910d9e8c0bSMaciej S. Szmigiero { 2920d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan; 2930d9e8c0bSMaciej S. Szmigiero 2940d9e8c0bSMaciej S. Szmigiero chan = hv_balloon_get_channel_maybe(balloon); 2950d9e8c0bSMaciej S. Szmigiero assert(chan != NULL); 2960d9e8c0bSMaciej S. Szmigiero return chan; 2970d9e8c0bSMaciej S. Szmigiero } 2980d9e8c0bSMaciej S. Szmigiero 2990d9e8c0bSMaciej S. Szmigiero static ssize_t hv_balloon_send_packet(VMBusChannel *chan, 3000d9e8c0bSMaciej S. Szmigiero struct dm_message *msg) 3010d9e8c0bSMaciej S. Szmigiero { 3020d9e8c0bSMaciej S. Szmigiero int ret; 3030d9e8c0bSMaciej S. Szmigiero 3040d9e8c0bSMaciej S. Szmigiero ret = vmbus_channel_reserve(chan, 0, msg->hdr.size); 3050d9e8c0bSMaciej S. Szmigiero if (ret < 0) { 3060d9e8c0bSMaciej S. Szmigiero return ret; 3070d9e8c0bSMaciej S. Szmigiero } 3080d9e8c0bSMaciej S. Szmigiero 3090d9e8c0bSMaciej S. Szmigiero return vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, 3100d9e8c0bSMaciej S. Szmigiero NULL, 0, msg, msg->hdr.size, false, 3110d9e8c0bSMaciej S. Szmigiero msg->hdr.trans_id); 3120d9e8c0bSMaciej S. Szmigiero } 3130d9e8c0bSMaciej S. Szmigiero 3140d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_unballoon_get_source(HvBalloon *balloon, 3150d9e8c0bSMaciej S. Szmigiero PageRangeTree *dtree, 316*99a4706aSMaciej S. Szmigiero uint64_t **dctr, 317*99a4706aSMaciej S. Szmigiero bool *is_our_range) 3180d9e8c0bSMaciej S. Szmigiero { 319*99a4706aSMaciej S. Szmigiero OurRange *our_range = OUR_RANGE(balloon->our_range); 320*99a4706aSMaciej S. Szmigiero 321*99a4706aSMaciej S. Szmigiero /* Try the boot memory first */ 3220d9e8c0bSMaciej S. Szmigiero if (g_tree_nnodes(balloon->removed_guest.t) > 0) { 3230d9e8c0bSMaciej S. Szmigiero *dtree = balloon->removed_guest; 3240d9e8c0bSMaciej S. Szmigiero *dctr = &balloon->removed_guest_ctr; 325*99a4706aSMaciej S. Szmigiero *is_our_range = false; 3260d9e8c0bSMaciej S. Szmigiero } else if (g_tree_nnodes(balloon->removed_both.t) > 0) { 3270d9e8c0bSMaciej S. Szmigiero *dtree = balloon->removed_both; 3280d9e8c0bSMaciej S. Szmigiero *dctr = &balloon->removed_both_ctr; 329*99a4706aSMaciej S. Szmigiero *is_our_range = false; 330*99a4706aSMaciej S. Szmigiero } else if (!our_range) { 331*99a4706aSMaciej S. Szmigiero return false; 332*99a4706aSMaciej S. Szmigiero } else if (!our_range_is_removed_tree_empty(our_range, false)) { 333*99a4706aSMaciej S. Szmigiero *dtree = our_range_get_removed_tree(our_range, false); 334*99a4706aSMaciej S. Szmigiero *dctr = &balloon->removed_guest_ctr; 335*99a4706aSMaciej S. Szmigiero *is_our_range = true; 336*99a4706aSMaciej S. Szmigiero } else if (!our_range_is_removed_tree_empty(our_range, true)) { 337*99a4706aSMaciej S. Szmigiero *dtree = our_range_get_removed_tree(our_range, true); 338*99a4706aSMaciej S. Szmigiero *dctr = &balloon->removed_both_ctr; 339*99a4706aSMaciej S. Szmigiero *is_our_range = true; 3400d9e8c0bSMaciej S. Szmigiero } else { 3410d9e8c0bSMaciej S. Szmigiero return false; 3420d9e8c0bSMaciej S. Szmigiero } 3430d9e8c0bSMaciej S. Szmigiero 3440d9e8c0bSMaciej S. Szmigiero return true; 3450d9e8c0bSMaciej S. Szmigiero } 3460d9e8c0bSMaciej S. Szmigiero 3470d9e8c0bSMaciej S. Szmigiero static void hv_balloon_unballoon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) 3480d9e8c0bSMaciej S. Szmigiero { 3490d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 3500d9e8c0bSMaciej S. Szmigiero struct dm_unballoon_request *ur; 3510d9e8c0bSMaciej S. Szmigiero size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); 3520d9e8c0bSMaciej S. Szmigiero 3530d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_UNBALLOON_RB_WAIT); 3540d9e8c0bSMaciej S. Szmigiero 3550d9e8c0bSMaciej S. Szmigiero if (vmbus_channel_reserve(chan, 0, ur_size) < 0) { 3560d9e8c0bSMaciej S. Szmigiero return; 3570d9e8c0bSMaciej S. Szmigiero } 3580d9e8c0bSMaciej S. Szmigiero 3590d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_POSTING); 3600d9e8c0bSMaciej S. Szmigiero } 3610d9e8c0bSMaciej S. Szmigiero 3620d9e8c0bSMaciej S. Szmigiero static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc) 3630d9e8c0bSMaciej S. Szmigiero { 3640d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 3650d9e8c0bSMaciej S. Szmigiero PageRangeTree dtree; 3660d9e8c0bSMaciej S. Szmigiero uint64_t *dctr; 367*99a4706aSMaciej S. Szmigiero bool our_range; 3680d9e8c0bSMaciej S. Szmigiero struct dm_unballoon_request *ur; 3690d9e8c0bSMaciej S. Szmigiero size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); 3700d9e8c0bSMaciej S. Szmigiero PageRange range; 3710d9e8c0bSMaciej S. Szmigiero bool bret; 3720d9e8c0bSMaciej S. Szmigiero ssize_t ret; 3730d9e8c0bSMaciej S. Szmigiero 3740d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_UNBALLOON_POSTING); 3750d9e8c0bSMaciej S. Szmigiero assert(balloon->unballoon_diff > 0); 3760d9e8c0bSMaciej S. Szmigiero 377*99a4706aSMaciej S. Szmigiero if (!hv_balloon_unballoon_get_source(balloon, &dtree, &dctr, &our_range)) { 3780d9e8c0bSMaciej S. Szmigiero error_report("trying to unballoon but nothing seems to be ballooned"); 3790d9e8c0bSMaciej S. Szmigiero /* 3800d9e8c0bSMaciej S. Szmigiero * there is little we can do as we might have already 3810d9e8c0bSMaciej S. Szmigiero * sent the guest a partial request we can't cancel 3820d9e8c0bSMaciej S. Szmigiero */ 3830d9e8c0bSMaciej S. Szmigiero return; 3840d9e8c0bSMaciej S. Szmigiero } 3850d9e8c0bSMaciej S. Szmigiero 386*99a4706aSMaciej S. Szmigiero assert(balloon->our_range || !our_range); 3870d9e8c0bSMaciej S. Szmigiero assert(dtree.t); 3880d9e8c0bSMaciej S. Szmigiero assert(dctr); 3890d9e8c0bSMaciej S. Szmigiero 3900d9e8c0bSMaciej S. Szmigiero ur = alloca(ur_size); 3910d9e8c0bSMaciej S. Szmigiero memset(ur, 0, ur_size); 3920d9e8c0bSMaciej S. Szmigiero ur->hdr.type = DM_UNBALLOON_REQUEST; 3930d9e8c0bSMaciej S. Szmigiero ur->hdr.size = ur_size; 3940d9e8c0bSMaciej S. Szmigiero ur->hdr.trans_id = balloon->trans_id; 3950d9e8c0bSMaciej S. Szmigiero 3960d9e8c0bSMaciej S. Szmigiero bret = hvb_page_range_tree_pop(dtree, &range, MIN(balloon->unballoon_diff, 3970d9e8c0bSMaciej S. Szmigiero HV_BALLOON_HA_CHUNK_PAGES)); 3980d9e8c0bSMaciej S. Szmigiero assert(bret); 3990d9e8c0bSMaciej S. Szmigiero /* TODO: madvise? */ 4000d9e8c0bSMaciej S. Szmigiero 4010d9e8c0bSMaciej S. Szmigiero *dctr -= range.count; 4020d9e8c0bSMaciej S. Szmigiero balloon->unballoon_diff -= range.count; 4030d9e8c0bSMaciej S. Szmigiero 4040d9e8c0bSMaciej S. Szmigiero ur->range_count = 1; 4050d9e8c0bSMaciej S. Szmigiero ur->range_array[0].finfo.start_page = range.start; 4060d9e8c0bSMaciej S. Szmigiero ur->range_array[0].finfo.page_cnt = range.count; 4070d9e8c0bSMaciej S. Szmigiero ur->more_pages = balloon->unballoon_diff > 0; 4080d9e8c0bSMaciej S. Szmigiero 4090d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_outgoing_unballoon(ur->hdr.trans_id, 4100d9e8c0bSMaciej S. Szmigiero range.count, range.start, 4110d9e8c0bSMaciej S. Szmigiero balloon->unballoon_diff); 4120d9e8c0bSMaciej S. Szmigiero 4130d9e8c0bSMaciej S. Szmigiero if (ur->more_pages) { 4140d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT); 4150d9e8c0bSMaciej S. Szmigiero } else { 4160d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_REPLY_WAIT); 4170d9e8c0bSMaciej S. Szmigiero } 4180d9e8c0bSMaciej S. Szmigiero 4190d9e8c0bSMaciej S. Szmigiero ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, 4200d9e8c0bSMaciej S. Szmigiero NULL, 0, ur, ur_size, false, 4210d9e8c0bSMaciej S. Szmigiero ur->hdr.trans_id); 4220d9e8c0bSMaciej S. Szmigiero if (ret <= 0) { 4230d9e8c0bSMaciej S. Szmigiero error_report("error %zd when posting unballoon msg, expect problems", 4240d9e8c0bSMaciej S. Szmigiero ret); 4250d9e8c0bSMaciej S. Szmigiero } 4260d9e8c0bSMaciej S. Szmigiero } 4270d9e8c0bSMaciej S. Szmigiero 428*99a4706aSMaciej S. Szmigiero static bool hv_balloon_our_range_ensure(HvBalloon *balloon) 429*99a4706aSMaciej S. Szmigiero { 430*99a4706aSMaciej S. Szmigiero uint64_t align; 431*99a4706aSMaciej S. Szmigiero MemoryRegion *hostmem_mr; 432*99a4706aSMaciej S. Szmigiero g_autoptr(OurRangeMemslots) our_range_memslots = NULL; 433*99a4706aSMaciej S. Szmigiero OurRange *our_range; 434*99a4706aSMaciej S. Szmigiero 435*99a4706aSMaciej S. Szmigiero if (balloon->our_range) { 436*99a4706aSMaciej S. Szmigiero return true; 437*99a4706aSMaciej S. Szmigiero } 438*99a4706aSMaciej S. Szmigiero 439*99a4706aSMaciej S. Szmigiero if (!balloon->hostmem) { 440*99a4706aSMaciej S. Szmigiero return false; 441*99a4706aSMaciej S. Szmigiero } 442*99a4706aSMaciej S. Szmigiero 443*99a4706aSMaciej S. Szmigiero align = (1 << balloon->caps.cap_bits.hot_add_alignment) * MiB; 444*99a4706aSMaciej S. Szmigiero assert(QEMU_IS_ALIGNED(balloon->addr, align)); 445*99a4706aSMaciej S. Szmigiero 446*99a4706aSMaciej S. Szmigiero hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); 447*99a4706aSMaciej S. Szmigiero 448*99a4706aSMaciej S. Szmigiero our_range_memslots = hvb_our_range_memslots_new(balloon->addr, 449*99a4706aSMaciej S. Szmigiero balloon->mr, hostmem_mr, 450*99a4706aSMaciej S. Szmigiero OBJECT(balloon), 451*99a4706aSMaciej S. Szmigiero balloon->memslot_count, 452*99a4706aSMaciej S. Szmigiero balloon->memslot_size); 453*99a4706aSMaciej S. Szmigiero our_range = OUR_RANGE(our_range_memslots); 454*99a4706aSMaciej S. Szmigiero 455*99a4706aSMaciej S. Szmigiero if (hvb_page_range_tree_intree_any(balloon->removed_guest, 456*99a4706aSMaciej S. Szmigiero our_range->range.start, 457*99a4706aSMaciej S. Szmigiero our_range->range.count) || 458*99a4706aSMaciej S. Szmigiero hvb_page_range_tree_intree_any(balloon->removed_both, 459*99a4706aSMaciej S. Szmigiero our_range->range.start, 460*99a4706aSMaciej S. Szmigiero our_range->range.count)) { 461*99a4706aSMaciej S. Szmigiero error_report("some parts of the memory backend were already returned by the guest. this should not happen, please reboot the guest and try again"); 462*99a4706aSMaciej S. Szmigiero return false; 463*99a4706aSMaciej S. Szmigiero } 464*99a4706aSMaciej S. Szmigiero 465*99a4706aSMaciej S. Szmigiero trace_hv_balloon_our_range_add(our_range->range.count, 466*99a4706aSMaciej S. Szmigiero our_range->range.start); 467*99a4706aSMaciej S. Szmigiero 468*99a4706aSMaciej S. Szmigiero balloon->our_range = g_steal_pointer(&our_range_memslots); 469*99a4706aSMaciej S. Szmigiero return true; 470*99a4706aSMaciej S. Szmigiero } 471*99a4706aSMaciej S. Szmigiero 472*99a4706aSMaciej S. Szmigiero static void hv_balloon_hot_add_setup(HvBalloon *balloon, StateDesc *stdesc) 473*99a4706aSMaciej S. Szmigiero { 474*99a4706aSMaciej S. Szmigiero /* need to make copy since it is in union with hot_add_range */ 475*99a4706aSMaciej S. Szmigiero uint64_t hot_add_diff = balloon->hot_add_diff; 476*99a4706aSMaciej S. Szmigiero PageRange *hot_add_range = &balloon->hot_add_range; 477*99a4706aSMaciej S. Szmigiero uint64_t align, our_range_remaining; 478*99a4706aSMaciej S. Szmigiero OurRange *our_range; 479*99a4706aSMaciej S. Szmigiero 480*99a4706aSMaciej S. Szmigiero assert(balloon->state == S_HOT_ADD_SETUP); 481*99a4706aSMaciej S. Szmigiero assert(hot_add_diff > 0); 482*99a4706aSMaciej S. Szmigiero 483*99a4706aSMaciej S. Szmigiero if (!hv_balloon_our_range_ensure(balloon)) { 484*99a4706aSMaciej S. Szmigiero goto ret_idle; 485*99a4706aSMaciej S. Szmigiero } 486*99a4706aSMaciej S. Szmigiero 487*99a4706aSMaciej S. Szmigiero our_range = OUR_RANGE(balloon->our_range); 488*99a4706aSMaciej S. Szmigiero 489*99a4706aSMaciej S. Szmigiero align = (1 << balloon->caps.cap_bits.hot_add_alignment) * 490*99a4706aSMaciej S. Szmigiero (MiB / HV_BALLOON_PAGE_SIZE); 491*99a4706aSMaciej S. Szmigiero 492*99a4706aSMaciej S. Szmigiero /* Absolute GPA in pages */ 493*99a4706aSMaciej S. Szmigiero hot_add_range->start = our_range_get_remaining_start(our_range); 494*99a4706aSMaciej S. Szmigiero assert(QEMU_IS_ALIGNED(hot_add_range->start, align)); 495*99a4706aSMaciej S. Szmigiero 496*99a4706aSMaciej S. Szmigiero our_range_remaining = our_range_get_remaining_size(our_range); 497*99a4706aSMaciej S. Szmigiero hot_add_range->count = MIN(our_range_remaining, hot_add_diff); 498*99a4706aSMaciej S. Szmigiero hot_add_range->count = QEMU_ALIGN_DOWN(hot_add_range->count, align); 499*99a4706aSMaciej S. Szmigiero if (hot_add_range->count == 0) { 500*99a4706aSMaciej S. Szmigiero goto ret_idle; 501*99a4706aSMaciej S. Szmigiero } 502*99a4706aSMaciej S. Szmigiero 503*99a4706aSMaciej S. Szmigiero hvb_our_range_memslots_ensure_mapped_additional(balloon->our_range, 504*99a4706aSMaciej S. Szmigiero hot_add_range->count); 505*99a4706aSMaciej S. Szmigiero 506*99a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_RB_WAIT); 507*99a4706aSMaciej S. Szmigiero return; 508*99a4706aSMaciej S. Szmigiero 509*99a4706aSMaciej S. Szmigiero ret_idle: 510*99a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); 511*99a4706aSMaciej S. Szmigiero } 512*99a4706aSMaciej S. Szmigiero 513*99a4706aSMaciej S. Szmigiero static void hv_balloon_hot_add_rb_wait(HvBalloon *balloon, StateDesc *stdesc) 514*99a4706aSMaciej S. Szmigiero { 515*99a4706aSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 516*99a4706aSMaciej S. Szmigiero struct dm_hot_add *ha; 517*99a4706aSMaciej S. Szmigiero size_t ha_size = sizeof(*ha) + sizeof(ha->range); 518*99a4706aSMaciej S. Szmigiero 519*99a4706aSMaciej S. Szmigiero assert(balloon->state == S_HOT_ADD_RB_WAIT); 520*99a4706aSMaciej S. Szmigiero 521*99a4706aSMaciej S. Szmigiero if (vmbus_channel_reserve(chan, 0, ha_size) < 0) { 522*99a4706aSMaciej S. Szmigiero return; 523*99a4706aSMaciej S. Szmigiero } 524*99a4706aSMaciej S. Szmigiero 525*99a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_POSTING); 526*99a4706aSMaciej S. Szmigiero } 527*99a4706aSMaciej S. Szmigiero 528*99a4706aSMaciej S. Szmigiero static void hv_balloon_hot_add_posting(HvBalloon *balloon, StateDesc *stdesc) 529*99a4706aSMaciej S. Szmigiero { 530*99a4706aSMaciej S. Szmigiero PageRange *hot_add_range = &balloon->hot_add_range; 531*99a4706aSMaciej S. Szmigiero uint64_t *current_count = &balloon->ha_current_count; 532*99a4706aSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 533*99a4706aSMaciej S. Szmigiero struct dm_hot_add *ha; 534*99a4706aSMaciej S. Szmigiero size_t ha_size = sizeof(*ha) + sizeof(ha->range); 535*99a4706aSMaciej S. Szmigiero union dm_mem_page_range *ha_region; 536*99a4706aSMaciej S. Szmigiero uint64_t align, chunk_max_size; 537*99a4706aSMaciej S. Szmigiero ssize_t ret; 538*99a4706aSMaciej S. Szmigiero 539*99a4706aSMaciej S. Szmigiero assert(balloon->state == S_HOT_ADD_POSTING); 540*99a4706aSMaciej S. Szmigiero assert(hot_add_range->count > 0); 541*99a4706aSMaciej S. Szmigiero 542*99a4706aSMaciej S. Szmigiero align = (1 << balloon->caps.cap_bits.hot_add_alignment) * 543*99a4706aSMaciej S. Szmigiero (MiB / HV_BALLOON_PAGE_SIZE); 544*99a4706aSMaciej S. Szmigiero if (align >= HV_BALLOON_HA_CHUNK_PAGES) { 545*99a4706aSMaciej S. Szmigiero /* 546*99a4706aSMaciej S. Szmigiero * If the required alignment is higher than the chunk size we let it 547*99a4706aSMaciej S. Szmigiero * override that size. 548*99a4706aSMaciej S. Szmigiero */ 549*99a4706aSMaciej S. Szmigiero chunk_max_size = align; 550*99a4706aSMaciej S. Szmigiero } else { 551*99a4706aSMaciej S. Szmigiero chunk_max_size = QEMU_ALIGN_DOWN(HV_BALLOON_HA_CHUNK_PAGES, align); 552*99a4706aSMaciej S. Szmigiero } 553*99a4706aSMaciej S. Szmigiero 554*99a4706aSMaciej S. Szmigiero /* 555*99a4706aSMaciej S. Szmigiero * hot_add_range->count starts aligned in hv_balloon_hot_add_setup(), 556*99a4706aSMaciej S. Szmigiero * then it is either reduced by subtracting aligned current_count or 557*99a4706aSMaciej S. Szmigiero * further hot-adds are prevented by marking the whole remaining our range 558*99a4706aSMaciej S. Szmigiero * as unusable in hv_balloon_handle_hot_add_response(). 559*99a4706aSMaciej S. Szmigiero */ 560*99a4706aSMaciej S. Szmigiero *current_count = MIN(hot_add_range->count, chunk_max_size); 561*99a4706aSMaciej S. Szmigiero 562*99a4706aSMaciej S. Szmigiero ha = alloca(ha_size); 563*99a4706aSMaciej S. Szmigiero ha_region = &(&ha->range)[1]; 564*99a4706aSMaciej S. Szmigiero memset(ha, 0, ha_size); 565*99a4706aSMaciej S. Szmigiero ha->hdr.type = DM_MEM_HOT_ADD_REQUEST; 566*99a4706aSMaciej S. Szmigiero ha->hdr.size = ha_size; 567*99a4706aSMaciej S. Szmigiero ha->hdr.trans_id = balloon->trans_id; 568*99a4706aSMaciej S. Szmigiero 569*99a4706aSMaciej S. Szmigiero ha->range.finfo.start_page = hot_add_range->start; 570*99a4706aSMaciej S. Szmigiero ha->range.finfo.page_cnt = *current_count; 571*99a4706aSMaciej S. Szmigiero ha_region->finfo.start_page = hot_add_range->start; 572*99a4706aSMaciej S. Szmigiero ha_region->finfo.page_cnt = ha->range.finfo.page_cnt; 573*99a4706aSMaciej S. Szmigiero 574*99a4706aSMaciej S. Szmigiero trace_hv_balloon_outgoing_hot_add(ha->hdr.trans_id, 575*99a4706aSMaciej S. Szmigiero *current_count, hot_add_range->start); 576*99a4706aSMaciej S. Szmigiero 577*99a4706aSMaciej S. Szmigiero ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, 578*99a4706aSMaciej S. Szmigiero NULL, 0, ha, ha_size, false, 579*99a4706aSMaciej S. Szmigiero ha->hdr.trans_id); 580*99a4706aSMaciej S. Szmigiero if (ret <= 0) { 581*99a4706aSMaciej S. Szmigiero error_report("error %zd when posting hot add msg, expect problems", 582*99a4706aSMaciej S. Szmigiero ret); 583*99a4706aSMaciej S. Szmigiero } 584*99a4706aSMaciej S. Szmigiero 585*99a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_REPLY_WAIT); 586*99a4706aSMaciej S. Szmigiero } 587*99a4706aSMaciej S. Szmigiero 5880d9e8c0bSMaciej S. Szmigiero static void hv_balloon_balloon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) 5890d9e8c0bSMaciej S. Szmigiero { 5900d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 5910d9e8c0bSMaciej S. Szmigiero size_t bl_size = sizeof(struct dm_balloon); 5920d9e8c0bSMaciej S. Szmigiero 5930d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_BALLOON_RB_WAIT); 5940d9e8c0bSMaciej S. Szmigiero 5950d9e8c0bSMaciej S. Szmigiero if (vmbus_channel_reserve(chan, 0, bl_size) < 0) { 5960d9e8c0bSMaciej S. Szmigiero return; 5970d9e8c0bSMaciej S. Szmigiero } 5980d9e8c0bSMaciej S. Szmigiero 5990d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_POSTING); 6000d9e8c0bSMaciej S. Szmigiero } 6010d9e8c0bSMaciej S. Szmigiero 6020d9e8c0bSMaciej S. Szmigiero static void hv_balloon_balloon_posting(HvBalloon *balloon, StateDesc *stdesc) 6030d9e8c0bSMaciej S. Szmigiero { 6040d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 6050d9e8c0bSMaciej S. Szmigiero struct dm_balloon bl; 6060d9e8c0bSMaciej S. Szmigiero size_t bl_size = sizeof(bl); 6070d9e8c0bSMaciej S. Szmigiero ssize_t ret; 6080d9e8c0bSMaciej S. Szmigiero 6090d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_BALLOON_POSTING); 6100d9e8c0bSMaciej S. Szmigiero assert(balloon->balloon_diff > 0); 6110d9e8c0bSMaciej S. Szmigiero 6120d9e8c0bSMaciej S. Szmigiero memset(&bl, 0, sizeof(bl)); 6130d9e8c0bSMaciej S. Szmigiero bl.hdr.type = DM_BALLOON_REQUEST; 6140d9e8c0bSMaciej S. Szmigiero bl.hdr.size = bl_size; 6150d9e8c0bSMaciej S. Szmigiero bl.hdr.trans_id = balloon->trans_id; 6160d9e8c0bSMaciej S. Szmigiero bl.num_pages = MIN(balloon->balloon_diff, HV_BALLOON_HR_CHUNK_PAGES); 6170d9e8c0bSMaciej S. Szmigiero 6180d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_outgoing_balloon(bl.hdr.trans_id, bl.num_pages, 6190d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff); 6200d9e8c0bSMaciej S. Szmigiero 6210d9e8c0bSMaciej S. Szmigiero ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, 6220d9e8c0bSMaciej S. Szmigiero NULL, 0, &bl, bl_size, false, 6230d9e8c0bSMaciej S. Szmigiero bl.hdr.trans_id); 6240d9e8c0bSMaciej S. Szmigiero if (ret <= 0) { 6250d9e8c0bSMaciej S. Szmigiero error_report("error %zd when posting balloon msg, expect problems", 6260d9e8c0bSMaciej S. Szmigiero ret); 6270d9e8c0bSMaciej S. Szmigiero } 6280d9e8c0bSMaciej S. Szmigiero 6290d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_REPLY_WAIT); 6300d9e8c0bSMaciej S. Szmigiero } 6310d9e8c0bSMaciej S. Szmigiero 6320d9e8c0bSMaciej S. Szmigiero static void hv_balloon_idle_state_process_target(HvBalloon *balloon, 6330d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 6340d9e8c0bSMaciej S. Szmigiero { 6350d9e8c0bSMaciej S. Szmigiero bool can_balloon = balloon->caps.cap_bits.balloon; 6360d9e8c0bSMaciej S. Szmigiero uint64_t ram_size_pages, total_removed; 6370d9e8c0bSMaciej S. Szmigiero 6380d9e8c0bSMaciej S. Szmigiero ram_size_pages = hv_balloon_total_ram(balloon); 6390d9e8c0bSMaciej S. Szmigiero total_removed = hv_balloon_total_removed_rs(balloon, ram_size_pages); 6400d9e8c0bSMaciej S. Szmigiero 6410d9e8c0bSMaciej S. Szmigiero /* 6420d9e8c0bSMaciej S. Szmigiero * we need to cache the values computed from the balloon target value when 6430d9e8c0bSMaciej S. Szmigiero * starting the adjustment procedure in case someone changes the target when 6440d9e8c0bSMaciej S. Szmigiero * the procedure is in progress 6450d9e8c0bSMaciej S. Szmigiero */ 6460d9e8c0bSMaciej S. Szmigiero if (balloon->target > ram_size_pages - total_removed) { 647*99a4706aSMaciej S. Szmigiero bool can_hot_add = balloon->caps.cap_bits.hot_add; 6480d9e8c0bSMaciej S. Szmigiero uint64_t target_diff = balloon->target - 6490d9e8c0bSMaciej S. Szmigiero (ram_size_pages - total_removed); 6500d9e8c0bSMaciej S. Szmigiero 6510d9e8c0bSMaciej S. Szmigiero balloon->unballoon_diff = MIN(target_diff, total_removed); 6520d9e8c0bSMaciej S. Szmigiero 653*99a4706aSMaciej S. Szmigiero if (can_hot_add) { 654*99a4706aSMaciej S. Szmigiero balloon->hot_add_diff = target_diff - balloon->unballoon_diff; 655*99a4706aSMaciej S. Szmigiero } else { 656*99a4706aSMaciej S. Szmigiero balloon->hot_add_diff = 0; 657*99a4706aSMaciej S. Szmigiero } 658*99a4706aSMaciej S. Szmigiero 6590d9e8c0bSMaciej S. Szmigiero if (balloon->unballoon_diff > 0) { 6600d9e8c0bSMaciej S. Szmigiero assert(can_balloon); 6610d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT); 662*99a4706aSMaciej S. Szmigiero } else if (balloon->hot_add_diff > 0) { 663*99a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_SETUP); 6640d9e8c0bSMaciej S. Szmigiero } 6650d9e8c0bSMaciej S. Szmigiero } else if (can_balloon && 6660d9e8c0bSMaciej S. Szmigiero balloon->target < ram_size_pages - total_removed) { 6670d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff = ram_size_pages - total_removed - 6680d9e8c0bSMaciej S. Szmigiero balloon->target; 6690d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT); 6700d9e8c0bSMaciej S. Szmigiero } 6710d9e8c0bSMaciej S. Szmigiero } 6720d9e8c0bSMaciej S. Szmigiero 6730d9e8c0bSMaciej S. Szmigiero static void hv_balloon_idle_state(HvBalloon *balloon, 6740d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 6750d9e8c0bSMaciej S. Szmigiero { 6760d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_IDLE); 6770d9e8c0bSMaciej S. Szmigiero 6780d9e8c0bSMaciej S. Szmigiero if (balloon->target_changed) { 6790d9e8c0bSMaciej S. Szmigiero balloon->target_changed = false; 6800d9e8c0bSMaciej S. Szmigiero hv_balloon_idle_state_process_target(balloon, stdesc); 6810d9e8c0bSMaciej S. Szmigiero return; 6820d9e8c0bSMaciej S. Szmigiero } 6830d9e8c0bSMaciej S. Szmigiero } 6840d9e8c0bSMaciej S. Szmigiero 6850d9e8c0bSMaciej S. Szmigiero static const struct { 6860d9e8c0bSMaciej S. Szmigiero void (*handler)(HvBalloon *balloon, StateDesc *stdesc); 6870d9e8c0bSMaciej S. Szmigiero } state_handlers[] = { 6880d9e8c0bSMaciej S. Szmigiero [S_IDLE].handler = hv_balloon_idle_state, 6890d9e8c0bSMaciej S. Szmigiero [S_BALLOON_POSTING].handler = hv_balloon_balloon_posting, 6900d9e8c0bSMaciej S. Szmigiero [S_BALLOON_RB_WAIT].handler = hv_balloon_balloon_rb_wait, 6910d9e8c0bSMaciej S. Szmigiero [S_UNBALLOON_POSTING].handler = hv_balloon_unballoon_posting, 6920d9e8c0bSMaciej S. Szmigiero [S_UNBALLOON_RB_WAIT].handler = hv_balloon_unballoon_rb_wait, 693*99a4706aSMaciej S. Szmigiero [S_HOT_ADD_SETUP].handler = hv_balloon_hot_add_setup, 694*99a4706aSMaciej S. Szmigiero [S_HOT_ADD_RB_WAIT].handler = hv_balloon_hot_add_rb_wait, 695*99a4706aSMaciej S. Szmigiero [S_HOT_ADD_POSTING].handler = hv_balloon_hot_add_posting, 6960d9e8c0bSMaciej S. Szmigiero }; 6970d9e8c0bSMaciej S. Szmigiero 6980d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_state(HvBalloon *balloon, StateDesc *stdesc) 6990d9e8c0bSMaciej S. Szmigiero { 7000d9e8c0bSMaciej S. Szmigiero if (balloon->state >= ARRAY_SIZE(state_handlers) || 7010d9e8c0bSMaciej S. Szmigiero !state_handlers[balloon->state].handler) { 7020d9e8c0bSMaciej S. Szmigiero return; 7030d9e8c0bSMaciej S. Szmigiero } 7040d9e8c0bSMaciej S. Szmigiero 7050d9e8c0bSMaciej S. Szmigiero state_handlers[balloon->state].handler(balloon, stdesc); 7060d9e8c0bSMaciej S. Szmigiero } 7070d9e8c0bSMaciej S. Szmigiero 7080d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_insert_range(PageRangeTree tree, 7090d9e8c0bSMaciej S. Szmigiero const PageRange *range, 7100d9e8c0bSMaciej S. Szmigiero uint64_t *ctr1, 7110d9e8c0bSMaciej S. Szmigiero uint64_t *ctr2, 7120d9e8c0bSMaciej S. Szmigiero uint64_t *ctr3) 7130d9e8c0bSMaciej S. Szmigiero { 7140d9e8c0bSMaciej S. Szmigiero uint64_t dupcount, effcount; 7150d9e8c0bSMaciej S. Szmigiero 7160d9e8c0bSMaciej S. Szmigiero if (range->count == 0) { 7170d9e8c0bSMaciej S. Szmigiero return; 7180d9e8c0bSMaciej S. Szmigiero } 7190d9e8c0bSMaciej S. Szmigiero 7200d9e8c0bSMaciej S. Szmigiero dupcount = 0; 7210d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_insert(tree, range->start, range->count, &dupcount); 7220d9e8c0bSMaciej S. Szmigiero 7230d9e8c0bSMaciej S. Szmigiero assert(dupcount <= range->count); 7240d9e8c0bSMaciej S. Szmigiero effcount = range->count - dupcount; 7250d9e8c0bSMaciej S. Szmigiero 7260d9e8c0bSMaciej S. Szmigiero *ctr1 += effcount; 7270d9e8c0bSMaciej S. Szmigiero *ctr2 += effcount; 7280d9e8c0bSMaciej S. Szmigiero if (ctr3) { 7290d9e8c0bSMaciej S. Szmigiero *ctr3 += effcount; 7300d9e8c0bSMaciej S. Szmigiero } 7310d9e8c0bSMaciej S. Szmigiero } 7320d9e8c0bSMaciej S. Szmigiero 7330d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_handle_range(HvBalloon *balloon, 7340d9e8c0bSMaciej S. Szmigiero PageRange *range, 7350d9e8c0bSMaciej S. Szmigiero bool both, 7360d9e8c0bSMaciej S. Szmigiero uint64_t *removedctr) 7370d9e8c0bSMaciej S. Szmigiero { 738*99a4706aSMaciej S. Szmigiero OurRange *our_range = OUR_RANGE(balloon->our_range); 7390d9e8c0bSMaciej S. Szmigiero PageRangeTree globaltree = 7400d9e8c0bSMaciej S. Szmigiero both ? balloon->removed_both : balloon->removed_guest; 7410d9e8c0bSMaciej S. Szmigiero uint64_t *globalctr = 7420d9e8c0bSMaciej S. Szmigiero both ? &balloon->removed_both_ctr : &balloon->removed_guest_ctr; 743*99a4706aSMaciej S. Szmigiero PageRange rangeeff; 744*99a4706aSMaciej S. Szmigiero 745*99a4706aSMaciej S. Szmigiero if (range->count == 0) { 746*99a4706aSMaciej S. Szmigiero return; 747*99a4706aSMaciej S. Szmigiero } 7480d9e8c0bSMaciej S. Szmigiero 7490d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_remove_response(range->count, range->start, both); 7500d9e8c0bSMaciej S. Szmigiero 751*99a4706aSMaciej S. Szmigiero if (our_range) { 752*99a4706aSMaciej S. Szmigiero /* Includes the not-yet-hot-added and unusable parts. */ 753*99a4706aSMaciej S. Szmigiero rangeeff = our_range->range; 754*99a4706aSMaciej S. Szmigiero } else { 755*99a4706aSMaciej S. Szmigiero rangeeff.start = rangeeff.count = 0; 756*99a4706aSMaciej S. Szmigiero } 757*99a4706aSMaciej S. Szmigiero 758*99a4706aSMaciej S. Szmigiero if (page_range_intersection_size(range, rangeeff.start, rangeeff.count) > 0) { 759*99a4706aSMaciej S. Szmigiero PageRangeTree ourtree = our_range_get_removed_tree(our_range, both); 760*99a4706aSMaciej S. Szmigiero PageRange rangehole, rangecommon; 761*99a4706aSMaciej S. Szmigiero uint64_t ourremoved = 0; 762*99a4706aSMaciej S. Szmigiero 763*99a4706aSMaciej S. Szmigiero /* process the hole before our range, if it exists */ 764*99a4706aSMaciej S. Szmigiero page_range_part_before(range, rangeeff.start, &rangehole); 765*99a4706aSMaciej S. Szmigiero hv_balloon_remove_response_insert_range(globaltree, &rangehole, 766*99a4706aSMaciej S. Szmigiero globalctr, removedctr, NULL); 767*99a4706aSMaciej S. Szmigiero if (rangehole.count > 0) { 768*99a4706aSMaciej S. Szmigiero trace_hv_balloon_remove_response_hole(rangehole.count, 769*99a4706aSMaciej S. Szmigiero rangehole.start, 770*99a4706aSMaciej S. Szmigiero range->count, range->start, 771*99a4706aSMaciej S. Szmigiero rangeeff.start, both); 772*99a4706aSMaciej S. Szmigiero } 773*99a4706aSMaciej S. Szmigiero 774*99a4706aSMaciej S. Szmigiero /* process our part */ 775*99a4706aSMaciej S. Szmigiero page_range_intersect(range, rangeeff.start, rangeeff.count, 776*99a4706aSMaciej S. Szmigiero &rangecommon); 777*99a4706aSMaciej S. Szmigiero hv_balloon_remove_response_insert_range(ourtree, &rangecommon, 778*99a4706aSMaciej S. Szmigiero globalctr, removedctr, 779*99a4706aSMaciej S. Szmigiero &ourremoved); 780*99a4706aSMaciej S. Szmigiero if (rangecommon.count > 0) { 781*99a4706aSMaciej S. Szmigiero trace_hv_balloon_remove_response_common(rangecommon.count, 782*99a4706aSMaciej S. Szmigiero rangecommon.start, 783*99a4706aSMaciej S. Szmigiero range->count, range->start, 784*99a4706aSMaciej S. Szmigiero rangeeff.count, 785*99a4706aSMaciej S. Szmigiero rangeeff.start, ourremoved, 786*99a4706aSMaciej S. Szmigiero both); 787*99a4706aSMaciej S. Szmigiero } 788*99a4706aSMaciej S. Szmigiero 789*99a4706aSMaciej S. Szmigiero /* calculate what's left after our range */ 790*99a4706aSMaciej S. Szmigiero rangecommon = *range; 791*99a4706aSMaciej S. Szmigiero page_range_part_after(&rangecommon, rangeeff.start, rangeeff.count, 792*99a4706aSMaciej S. Szmigiero range); 793*99a4706aSMaciej S. Szmigiero } 794*99a4706aSMaciej S. Szmigiero 795*99a4706aSMaciej S. Szmigiero /* process the remainder of the range that lies after our range */ 7960d9e8c0bSMaciej S. Szmigiero if (range->count > 0) { 7970d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_insert_range(globaltree, range, 7980d9e8c0bSMaciej S. Szmigiero globalctr, removedctr, NULL); 7990d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_remove_response_remainder(range->count, range->start, 8000d9e8c0bSMaciej S. Szmigiero both); 8010d9e8c0bSMaciej S. Szmigiero range->count = 0; 8020d9e8c0bSMaciej S. Szmigiero } 8030d9e8c0bSMaciej S. Szmigiero } 8040d9e8c0bSMaciej S. Szmigiero 8050d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_handle_pages(HvBalloon *balloon, 8060d9e8c0bSMaciej S. Szmigiero PageRange *range, 8070d9e8c0bSMaciej S. Szmigiero uint64_t start, 8080d9e8c0bSMaciej S. Szmigiero uint64_t count, 8090d9e8c0bSMaciej S. Szmigiero bool both, 8100d9e8c0bSMaciej S. Szmigiero uint64_t *removedctr) 8110d9e8c0bSMaciej S. Szmigiero { 8120d9e8c0bSMaciej S. Szmigiero assert(count > 0); 8130d9e8c0bSMaciej S. Szmigiero 8140d9e8c0bSMaciej S. Szmigiero /* 8150d9e8c0bSMaciej S. Szmigiero * if there is an existing range that the new range can't be joined to 8160d9e8c0bSMaciej S. Szmigiero * dump it into tree(s) 8170d9e8c0bSMaciej S. Szmigiero */ 8180d9e8c0bSMaciej S. Szmigiero if (range->count > 0 && !page_range_joinable(range, start, count)) { 8190d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_range(balloon, range, both, 8200d9e8c0bSMaciej S. Szmigiero removedctr); 8210d9e8c0bSMaciej S. Szmigiero } 8220d9e8c0bSMaciej S. Szmigiero 8230d9e8c0bSMaciej S. Szmigiero if (range->count == 0) { 8240d9e8c0bSMaciej S. Szmigiero range->start = start; 8250d9e8c0bSMaciej S. Szmigiero range->count = count; 8260d9e8c0bSMaciej S. Szmigiero } else if (page_range_joinable_left(range, start, count)) { 8270d9e8c0bSMaciej S. Szmigiero range->start = start; 8280d9e8c0bSMaciej S. Szmigiero range->count += count; 8290d9e8c0bSMaciej S. Szmigiero } else { /* page_range_joinable_right() */ 8300d9e8c0bSMaciej S. Szmigiero range->count += count; 8310d9e8c0bSMaciej S. Szmigiero } 8320d9e8c0bSMaciej S. Szmigiero } 8330d9e8c0bSMaciej S. Szmigiero 8340d9e8c0bSMaciej S. Szmigiero static gboolean hv_balloon_handle_remove_host_addr_node(gpointer key, 8350d9e8c0bSMaciej S. Szmigiero gpointer value, 8360d9e8c0bSMaciej S. Szmigiero gpointer data) 8370d9e8c0bSMaciej S. Szmigiero { 8380d9e8c0bSMaciej S. Szmigiero PageRange *range = value; 8390d9e8c0bSMaciej S. Szmigiero uint64_t pageoff; 8400d9e8c0bSMaciej S. Szmigiero 8410d9e8c0bSMaciej S. Szmigiero for (pageoff = 0; pageoff < range->count; ) { 8420d9e8c0bSMaciej S. Szmigiero uint64_t addr_64 = (range->start + pageoff) * HV_BALLOON_PAGE_SIZE; 8430d9e8c0bSMaciej S. Szmigiero void *addr; 8440d9e8c0bSMaciej S. Szmigiero RAMBlock *rb; 8450d9e8c0bSMaciej S. Szmigiero ram_addr_t rb_offset; 8460d9e8c0bSMaciej S. Szmigiero size_t rb_page_size; 8470d9e8c0bSMaciej S. Szmigiero size_t discard_size; 8480d9e8c0bSMaciej S. Szmigiero 8490d9e8c0bSMaciej S. Szmigiero assert(addr_64 <= UINTPTR_MAX); 8500d9e8c0bSMaciej S. Szmigiero addr = (void *)((uintptr_t)addr_64); 8510d9e8c0bSMaciej S. Szmigiero rb = qemu_ram_block_from_host(addr, false, &rb_offset); 8520d9e8c0bSMaciej S. Szmigiero rb_page_size = qemu_ram_pagesize(rb); 8530d9e8c0bSMaciej S. Szmigiero 8540d9e8c0bSMaciej S. Szmigiero if (rb_page_size != HV_BALLOON_PAGE_SIZE) { 8550d9e8c0bSMaciej S. Szmigiero /* TODO: these should end in "removed_guest" */ 8560d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page backed by unsupported page size %zu", 8570d9e8c0bSMaciej S. Szmigiero rb_page_size); 8580d9e8c0bSMaciej S. Szmigiero pageoff++; 8590d9e8c0bSMaciej S. Szmigiero continue; 8600d9e8c0bSMaciej S. Szmigiero } 8610d9e8c0bSMaciej S. Szmigiero 8620d9e8c0bSMaciej S. Szmigiero discard_size = MIN(range->count - pageoff, 8630d9e8c0bSMaciej S. Szmigiero (rb->max_length - rb_offset) / 8640d9e8c0bSMaciej S. Szmigiero HV_BALLOON_PAGE_SIZE); 8650d9e8c0bSMaciej S. Szmigiero discard_size = MAX(discard_size, 1); 8660d9e8c0bSMaciej S. Szmigiero 8670d9e8c0bSMaciej S. Szmigiero if (ram_block_discard_range(rb, rb_offset, discard_size * 8680d9e8c0bSMaciej S. Szmigiero HV_BALLOON_PAGE_SIZE) != 0) { 8690d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page failed discard"); 8700d9e8c0bSMaciej S. Szmigiero } 8710d9e8c0bSMaciej S. Szmigiero 8720d9e8c0bSMaciej S. Szmigiero pageoff += discard_size; 8730d9e8c0bSMaciej S. Szmigiero } 8740d9e8c0bSMaciej S. Szmigiero 8750d9e8c0bSMaciej S. Szmigiero return false; 8760d9e8c0bSMaciej S. Szmigiero } 8770d9e8c0bSMaciej S. Szmigiero 8780d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_remove_host_addr_tree(PageRangeTree tree) 8790d9e8c0bSMaciej S. Szmigiero { 8800d9e8c0bSMaciej S. Szmigiero g_tree_foreach(tree.t, hv_balloon_handle_remove_host_addr_node, NULL); 8810d9e8c0bSMaciej S. Szmigiero } 8820d9e8c0bSMaciej S. Szmigiero 8830d9e8c0bSMaciej S. Szmigiero static int hv_balloon_handle_remove_section(PageRangeTree tree, 8840d9e8c0bSMaciej S. Szmigiero const MemoryRegionSection *section, 8850d9e8c0bSMaciej S. Szmigiero uint64_t count) 8860d9e8c0bSMaciej S. Szmigiero { 8870d9e8c0bSMaciej S. Szmigiero void *addr = memory_region_get_ram_ptr(section->mr) + 8880d9e8c0bSMaciej S. Szmigiero section->offset_within_region; 8890d9e8c0bSMaciej S. Szmigiero uint64_t addr_page; 8900d9e8c0bSMaciej S. Szmigiero 8910d9e8c0bSMaciej S. Szmigiero assert(count > 0); 8920d9e8c0bSMaciej S. Szmigiero 8930d9e8c0bSMaciej S. Szmigiero if ((uintptr_t)addr % HV_BALLOON_PAGE_SIZE) { 8940d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed pages at an unaligned host addr %p", 8950d9e8c0bSMaciej S. Szmigiero addr); 8960d9e8c0bSMaciej S. Szmigiero return -EINVAL; 8970d9e8c0bSMaciej S. Szmigiero } 8980d9e8c0bSMaciej S. Szmigiero 8990d9e8c0bSMaciej S. Szmigiero addr_page = (uintptr_t)addr / HV_BALLOON_PAGE_SIZE; 9000d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_insert(tree, addr_page, count, NULL); 9010d9e8c0bSMaciej S. Szmigiero 9020d9e8c0bSMaciej S. Szmigiero return 0; 9030d9e8c0bSMaciej S. Szmigiero } 9040d9e8c0bSMaciej S. Szmigiero 9050d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_remove_ranges(HvBalloon *balloon, 9060d9e8c0bSMaciej S. Szmigiero union dm_mem_page_range ranges[], 9070d9e8c0bSMaciej S. Szmigiero uint32_t count) 9080d9e8c0bSMaciej S. Szmigiero { 9090d9e8c0bSMaciej S. Szmigiero uint64_t removedcnt; 9100d9e8c0bSMaciej S. Szmigiero PageRangeTree removed_host_addr; 9110d9e8c0bSMaciej S. Szmigiero PageRange range_guest, range_both; 9120d9e8c0bSMaciej S. Szmigiero 9130d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_init(&removed_host_addr); 9140d9e8c0bSMaciej S. Szmigiero range_guest.count = range_both.count = removedcnt = 0; 9150d9e8c0bSMaciej S. Szmigiero for (unsigned int ctr = 0; ctr < count; ctr++) { 9160d9e8c0bSMaciej S. Szmigiero union dm_mem_page_range *mr = &ranges[ctr]; 9170d9e8c0bSMaciej S. Szmigiero hwaddr pa; 9180d9e8c0bSMaciej S. Szmigiero MemoryRegionSection section; 9190d9e8c0bSMaciej S. Szmigiero 9200d9e8c0bSMaciej S. Szmigiero for (unsigned int offset = 0; offset < mr->finfo.page_cnt; ) { 9210d9e8c0bSMaciej S. Szmigiero int ret; 9220d9e8c0bSMaciej S. Szmigiero uint64_t pageno = mr->finfo.start_page + offset; 9230d9e8c0bSMaciej S. Szmigiero uint64_t pagecnt = 1; 9240d9e8c0bSMaciej S. Szmigiero 9250d9e8c0bSMaciej S. Szmigiero pa = (hwaddr)pageno << HV_BALLOON_PFN_SHIFT; 9260d9e8c0bSMaciej S. Szmigiero section = memory_region_find(get_system_memory(), pa, 9270d9e8c0bSMaciej S. Szmigiero (mr->finfo.page_cnt - offset) * 9280d9e8c0bSMaciej S. Szmigiero HV_BALLOON_PAGE_SIZE); 9290d9e8c0bSMaciej S. Szmigiero if (!section.mr) { 9300d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page %"PRIu64" not found in RAM", 9310d9e8c0bSMaciej S. Szmigiero pageno); 9320d9e8c0bSMaciej S. Szmigiero ret = -EINVAL; 9330d9e8c0bSMaciej S. Szmigiero goto finish_page; 9340d9e8c0bSMaciej S. Szmigiero } 9350d9e8c0bSMaciej S. Szmigiero 9360d9e8c0bSMaciej S. Szmigiero pagecnt = int128_get64(section.size) / HV_BALLOON_PAGE_SIZE; 9370d9e8c0bSMaciej S. Szmigiero if (pagecnt <= 0) { 9380d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page %"PRIu64" in a section smaller than page size", 9390d9e8c0bSMaciej S. Szmigiero pageno); 9400d9e8c0bSMaciej S. Szmigiero pagecnt = 1; /* skip the whole page */ 9410d9e8c0bSMaciej S. Szmigiero ret = -EINVAL; 9420d9e8c0bSMaciej S. Szmigiero goto finish_page; 9430d9e8c0bSMaciej S. Szmigiero } 9440d9e8c0bSMaciej S. Szmigiero 9450d9e8c0bSMaciej S. Szmigiero if (!memory_region_is_ram(section.mr) || 9460d9e8c0bSMaciej S. Szmigiero memory_region_is_rom(section.mr) || 9470d9e8c0bSMaciej S. Szmigiero memory_region_is_romd(section.mr)) { 9480d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page %"PRIu64" in a section that is not an ordinary RAM", 9490d9e8c0bSMaciej S. Szmigiero pageno); 9500d9e8c0bSMaciej S. Szmigiero ret = -EINVAL; 9510d9e8c0bSMaciej S. Szmigiero goto finish_page; 9520d9e8c0bSMaciej S. Szmigiero } 9530d9e8c0bSMaciej S. Szmigiero 9540d9e8c0bSMaciej S. Szmigiero ret = hv_balloon_handle_remove_section(removed_host_addr, §ion, 9550d9e8c0bSMaciej S. Szmigiero pagecnt); 9560d9e8c0bSMaciej S. Szmigiero 9570d9e8c0bSMaciej S. Szmigiero finish_page: 9580d9e8c0bSMaciej S. Szmigiero if (ret == 0) { 9590d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_pages(balloon, 9600d9e8c0bSMaciej S. Szmigiero &range_both, 9610d9e8c0bSMaciej S. Szmigiero pageno, pagecnt, 9620d9e8c0bSMaciej S. Szmigiero true, &removedcnt); 9630d9e8c0bSMaciej S. Szmigiero } else { 9640d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_pages(balloon, 9650d9e8c0bSMaciej S. Szmigiero &range_guest, 9660d9e8c0bSMaciej S. Szmigiero pageno, pagecnt, 9670d9e8c0bSMaciej S. Szmigiero false, &removedcnt); 9680d9e8c0bSMaciej S. Szmigiero } 9690d9e8c0bSMaciej S. Szmigiero 9700d9e8c0bSMaciej S. Szmigiero if (section.mr) { 9710d9e8c0bSMaciej S. Szmigiero memory_region_unref(section.mr); 9720d9e8c0bSMaciej S. Szmigiero } 9730d9e8c0bSMaciej S. Szmigiero 9740d9e8c0bSMaciej S. Szmigiero offset += pagecnt; 9750d9e8c0bSMaciej S. Szmigiero } 9760d9e8c0bSMaciej S. Szmigiero } 9770d9e8c0bSMaciej S. Szmigiero 9780d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_range(balloon, &range_both, true, 9790d9e8c0bSMaciej S. Szmigiero &removedcnt); 9800d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_range(balloon, &range_guest, false, 9810d9e8c0bSMaciej S. Szmigiero &removedcnt); 9820d9e8c0bSMaciej S. Szmigiero 9830d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_remove_host_addr_tree(removed_host_addr); 9840d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&removed_host_addr); 9850d9e8c0bSMaciej S. Szmigiero 9860d9e8c0bSMaciej S. Szmigiero if (removedcnt > balloon->balloon_diff) { 9870d9e8c0bSMaciej S. Szmigiero warn_report("guest reported more pages removed than currently pending (%"PRIu64" vs %"PRIu64")", 9880d9e8c0bSMaciej S. Szmigiero removedcnt, balloon->balloon_diff); 9890d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff = 0; 9900d9e8c0bSMaciej S. Szmigiero } else { 9910d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff -= removedcnt; 9920d9e8c0bSMaciej S. Szmigiero } 9930d9e8c0bSMaciej S. Szmigiero } 9940d9e8c0bSMaciej S. Szmigiero 9950d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_handle_msg_size(HvBalloonReq *req, size_t minsize, 9960d9e8c0bSMaciej S. Szmigiero const char *msgname) 9970d9e8c0bSMaciej S. Szmigiero { 9980d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 9990d9e8c0bSMaciej S. Szmigiero uint32_t msglen = vmreq->msglen; 10000d9e8c0bSMaciej S. Szmigiero 10010d9e8c0bSMaciej S. Szmigiero if (msglen >= minsize) { 10020d9e8c0bSMaciej S. Szmigiero return true; 10030d9e8c0bSMaciej S. Szmigiero } 10040d9e8c0bSMaciej S. Szmigiero 10050d9e8c0bSMaciej S. Szmigiero warn_report("%s message too short (%u vs %zu), ignoring", msgname, 10060d9e8c0bSMaciej S. Szmigiero (unsigned int)msglen, minsize); 10070d9e8c0bSMaciej S. Szmigiero return false; 10080d9e8c0bSMaciej S. Szmigiero } 10090d9e8c0bSMaciej S. Szmigiero 10100d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_version_request(HvBalloon *balloon, 10110d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req, 10120d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 10130d9e8c0bSMaciej S. Szmigiero { 10140d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 10150d9e8c0bSMaciej S. Szmigiero struct dm_version_request *msgVr = vmreq->msg; 10160d9e8c0bSMaciej S. Szmigiero struct dm_version_response respVr; 10170d9e8c0bSMaciej S. Szmigiero 10180d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_VERSION) { 10190d9e8c0bSMaciej S. Szmigiero warn_report("unexpected DM_VERSION_REQUEST in %d state", 10200d9e8c0bSMaciej S. Szmigiero balloon->state); 10210d9e8c0bSMaciej S. Szmigiero return; 10220d9e8c0bSMaciej S. Szmigiero } 10230d9e8c0bSMaciej S. Szmigiero 10240d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgVr), 10250d9e8c0bSMaciej S. Szmigiero "DM_VERSION_REQUEST")) { 10260d9e8c0bSMaciej S. Szmigiero return; 10270d9e8c0bSMaciej S. Szmigiero } 10280d9e8c0bSMaciej S. Szmigiero 10290d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_incoming_version(msgVr->version.major_version, 10300d9e8c0bSMaciej S. Szmigiero msgVr->version.minor_version); 10310d9e8c0bSMaciej S. Szmigiero 10320d9e8c0bSMaciej S. Szmigiero memset(&respVr, 0, sizeof(respVr)); 10330d9e8c0bSMaciej S. Szmigiero respVr.hdr.type = DM_VERSION_RESPONSE; 10340d9e8c0bSMaciej S. Szmigiero respVr.hdr.size = sizeof(respVr); 10350d9e8c0bSMaciej S. Szmigiero respVr.hdr.trans_id = msgVr->hdr.trans_id; 10360d9e8c0bSMaciej S. Szmigiero respVr.is_accepted = msgVr->version.version >= DYNMEM_PROTOCOL_VERSION_1 && 10370d9e8c0bSMaciej S. Szmigiero msgVr->version.version <= DYNMEM_PROTOCOL_VERSION_3; 10380d9e8c0bSMaciej S. Szmigiero 10390d9e8c0bSMaciej S. Szmigiero hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respVr); 10400d9e8c0bSMaciej S. Szmigiero 10410d9e8c0bSMaciej S. Szmigiero if (respVr.is_accepted) { 10420d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_CAPS); 10430d9e8c0bSMaciej S. Szmigiero } 10440d9e8c0bSMaciej S. Szmigiero } 10450d9e8c0bSMaciej S. Szmigiero 10460d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_caps_report(HvBalloon *balloon, 10470d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req, 10480d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 10490d9e8c0bSMaciej S. Szmigiero { 10500d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 10510d9e8c0bSMaciej S. Szmigiero struct dm_capabilities *msgCap = vmreq->msg; 10520d9e8c0bSMaciej S. Szmigiero struct dm_capabilities_resp_msg respCap; 10530d9e8c0bSMaciej S. Szmigiero 10540d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_CAPS) { 10550d9e8c0bSMaciej S. Szmigiero warn_report("unexpected DM_CAPABILITIES_REPORT in %d state", 10560d9e8c0bSMaciej S. Szmigiero balloon->state); 10570d9e8c0bSMaciej S. Szmigiero return; 10580d9e8c0bSMaciej S. Szmigiero } 10590d9e8c0bSMaciej S. Szmigiero 10600d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgCap), 10610d9e8c0bSMaciej S. Szmigiero "DM_CAPABILITIES_REPORT")) { 10620d9e8c0bSMaciej S. Szmigiero return; 10630d9e8c0bSMaciej S. Szmigiero } 10640d9e8c0bSMaciej S. Szmigiero 10650d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_incoming_caps(msgCap->caps.caps); 10660d9e8c0bSMaciej S. Szmigiero balloon->caps = msgCap->caps; 10670d9e8c0bSMaciej S. Szmigiero 10680d9e8c0bSMaciej S. Szmigiero memset(&respCap, 0, sizeof(respCap)); 10690d9e8c0bSMaciej S. Szmigiero respCap.hdr.type = DM_CAPABILITIES_RESPONSE; 10700d9e8c0bSMaciej S. Szmigiero respCap.hdr.size = sizeof(respCap); 10710d9e8c0bSMaciej S. Szmigiero respCap.hdr.trans_id = msgCap->hdr.trans_id; 10720d9e8c0bSMaciej S. Szmigiero respCap.is_accepted = 1; 10730d9e8c0bSMaciej S. Szmigiero respCap.hot_remove = 1; 10740d9e8c0bSMaciej S. Szmigiero respCap.suppress_pressure_reports = !balloon->status_report.enabled; 10750d9e8c0bSMaciej S. Szmigiero hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respCap); 10760d9e8c0bSMaciej S. Szmigiero 10770d9e8c0bSMaciej S. Szmigiero timer_mod(&balloon->post_init_timer, 10780d9e8c0bSMaciej S. Szmigiero qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10790d9e8c0bSMaciej S. Szmigiero HV_BALLOON_POST_INIT_WAIT); 10800d9e8c0bSMaciej S. Szmigiero 10810d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_POST_INIT_WAIT); 10820d9e8c0bSMaciej S. Szmigiero } 10830d9e8c0bSMaciej S. Szmigiero 10840d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_status_report(HvBalloon *balloon, 10850d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req) 10860d9e8c0bSMaciej S. Szmigiero { 10870d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 10880d9e8c0bSMaciej S. Szmigiero struct dm_status *msgStatus = vmreq->msg; 10890d9e8c0bSMaciej S. Szmigiero 10900d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgStatus), 10910d9e8c0bSMaciej S. Szmigiero "DM_STATUS_REPORT")) { 10920d9e8c0bSMaciej S. Szmigiero return; 10930d9e8c0bSMaciej S. Szmigiero } 10940d9e8c0bSMaciej S. Szmigiero 10950d9e8c0bSMaciej S. Szmigiero if (!balloon->status_report.enabled) { 10960d9e8c0bSMaciej S. Szmigiero return; 10970d9e8c0bSMaciej S. Szmigiero } 10980d9e8c0bSMaciej S. Szmigiero 10990d9e8c0bSMaciej S. Szmigiero balloon->status_report.committed = msgStatus->num_committed; 11000d9e8c0bSMaciej S. Szmigiero balloon->status_report.committed *= HV_BALLOON_PAGE_SIZE; 11010d9e8c0bSMaciej S. Szmigiero balloon->status_report.available = msgStatus->num_avail; 11020d9e8c0bSMaciej S. Szmigiero balloon->status_report.available *= HV_BALLOON_PAGE_SIZE; 11030d9e8c0bSMaciej S. Szmigiero balloon->status_report.received = true; 11040d9e8c0bSMaciej S. Szmigiero 11050d9e8c0bSMaciej S. Szmigiero /* report event */ 11060d9e8c0bSMaciej S. Szmigiero } 11070d9e8c0bSMaciej S. Szmigiero 11080d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_unballoon_response(HvBalloon *balloon, 11090d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req, 11100d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 11110d9e8c0bSMaciej S. Szmigiero { 11120d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 11130d9e8c0bSMaciej S. Szmigiero struct dm_unballoon_response *msgUrR = vmreq->msg; 11140d9e8c0bSMaciej S. Szmigiero 11150d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_UNBALLOON_REPLY_WAIT) { 11160d9e8c0bSMaciej S. Szmigiero warn_report("unexpected DM_UNBALLOON_RESPONSE in %d state", 11170d9e8c0bSMaciej S. Szmigiero balloon->state); 11180d9e8c0bSMaciej S. Szmigiero return; 11190d9e8c0bSMaciej S. Szmigiero } 11200d9e8c0bSMaciej S. Szmigiero 11210d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgUrR), 11220d9e8c0bSMaciej S. Szmigiero "DM_UNBALLOON_RESPONSE")) 11230d9e8c0bSMaciej S. Szmigiero return; 11240d9e8c0bSMaciej S. Szmigiero 11250d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_incoming_unballoon(msgUrR->hdr.trans_id); 11260d9e8c0bSMaciej S. Szmigiero 11270d9e8c0bSMaciej S. Szmigiero balloon->trans_id++; 11280d9e8c0bSMaciej S. Szmigiero 1129*99a4706aSMaciej S. Szmigiero if (balloon->hot_add_diff > 0) { 1130*99a4706aSMaciej S. Szmigiero bool can_hot_add = balloon->caps.cap_bits.hot_add; 1131*99a4706aSMaciej S. Szmigiero 1132*99a4706aSMaciej S. Szmigiero assert(can_hot_add); 1133*99a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_SETUP); 1134*99a4706aSMaciej S. Szmigiero } else { 1135*99a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); 1136*99a4706aSMaciej S. Szmigiero } 1137*99a4706aSMaciej S. Szmigiero } 1138*99a4706aSMaciej S. Szmigiero 1139*99a4706aSMaciej S. Szmigiero static void hv_balloon_handle_hot_add_response(HvBalloon *balloon, 1140*99a4706aSMaciej S. Szmigiero HvBalloonReq *req, 1141*99a4706aSMaciej S. Szmigiero StateDesc *stdesc) 1142*99a4706aSMaciej S. Szmigiero { 1143*99a4706aSMaciej S. Szmigiero PageRange *hot_add_range = &balloon->hot_add_range; 1144*99a4706aSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 1145*99a4706aSMaciej S. Szmigiero struct dm_hot_add_response *msgHaR = vmreq->msg; 1146*99a4706aSMaciej S. Szmigiero OurRange *our_range; 1147*99a4706aSMaciej S. Szmigiero 1148*99a4706aSMaciej S. Szmigiero if (balloon->state != S_HOT_ADD_REPLY_WAIT) { 1149*99a4706aSMaciej S. Szmigiero warn_report("unexpected DM_HOT_ADD_RESPONSE in %d state", 1150*99a4706aSMaciej S. Szmigiero balloon->state); 1151*99a4706aSMaciej S. Szmigiero return; 1152*99a4706aSMaciej S. Szmigiero } 1153*99a4706aSMaciej S. Szmigiero 1154*99a4706aSMaciej S. Szmigiero assert(balloon->our_range); 1155*99a4706aSMaciej S. Szmigiero our_range = OUR_RANGE(balloon->our_range); 1156*99a4706aSMaciej S. Szmigiero 1157*99a4706aSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgHaR), 1158*99a4706aSMaciej S. Szmigiero "DM_HOT_ADD_RESPONSE")) 1159*99a4706aSMaciej S. Szmigiero return; 1160*99a4706aSMaciej S. Szmigiero 1161*99a4706aSMaciej S. Szmigiero trace_hv_balloon_incoming_hot_add(msgHaR->hdr.trans_id, msgHaR->result, 1162*99a4706aSMaciej S. Szmigiero msgHaR->page_count); 1163*99a4706aSMaciej S. Szmigiero 1164*99a4706aSMaciej S. Szmigiero balloon->trans_id++; 1165*99a4706aSMaciej S. Szmigiero 1166*99a4706aSMaciej S. Szmigiero if (msgHaR->result) { 1167*99a4706aSMaciej S. Szmigiero if (msgHaR->page_count > balloon->ha_current_count) { 1168*99a4706aSMaciej S. Szmigiero warn_report("DM_HOT_ADD_RESPONSE page count higher than requested (%"PRIu32" vs %"PRIu64")", 1169*99a4706aSMaciej S. Szmigiero msgHaR->page_count, balloon->ha_current_count); 1170*99a4706aSMaciej S. Szmigiero msgHaR->page_count = balloon->ha_current_count; 1171*99a4706aSMaciej S. Szmigiero } 1172*99a4706aSMaciej S. Szmigiero 1173*99a4706aSMaciej S. Szmigiero hvb_our_range_mark_added(our_range, msgHaR->page_count); 1174*99a4706aSMaciej S. Szmigiero hot_add_range->start += msgHaR->page_count; 1175*99a4706aSMaciej S. Szmigiero hot_add_range->count -= msgHaR->page_count; 1176*99a4706aSMaciej S. Szmigiero } 1177*99a4706aSMaciej S. Szmigiero 1178*99a4706aSMaciej S. Szmigiero if (!msgHaR->result || msgHaR->page_count < balloon->ha_current_count) { 1179*99a4706aSMaciej S. Szmigiero /* 1180*99a4706aSMaciej S. Szmigiero * the current planned range was only partially hot-added, take note 1181*99a4706aSMaciej S. Szmigiero * how much of it remains and don't attempt any further hot adds 1182*99a4706aSMaciej S. Szmigiero */ 1183*99a4706aSMaciej S. Szmigiero our_range_mark_remaining_unusable(our_range); 1184*99a4706aSMaciej S. Szmigiero 1185*99a4706aSMaciej S. Szmigiero goto ret_idle; 1186*99a4706aSMaciej S. Szmigiero } 1187*99a4706aSMaciej S. Szmigiero 1188*99a4706aSMaciej S. Szmigiero /* any pages remaining to hot-add in our range? */ 1189*99a4706aSMaciej S. Szmigiero if (hot_add_range->count > 0) { 1190*99a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_RB_WAIT); 1191*99a4706aSMaciej S. Szmigiero return; 1192*99a4706aSMaciej S. Szmigiero } 1193*99a4706aSMaciej S. Szmigiero 1194*99a4706aSMaciej S. Szmigiero ret_idle: 11950d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); 11960d9e8c0bSMaciej S. Szmigiero } 11970d9e8c0bSMaciej S. Szmigiero 11980d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_balloon_response(HvBalloon *balloon, 11990d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req, 12000d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 12010d9e8c0bSMaciej S. Szmigiero { 12020d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 12030d9e8c0bSMaciej S. Szmigiero struct dm_balloon_response *msgBR = vmreq->msg; 12040d9e8c0bSMaciej S. Szmigiero 12050d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_BALLOON_REPLY_WAIT) { 12060d9e8c0bSMaciej S. Szmigiero warn_report("unexpected DM_BALLOON_RESPONSE in %d state", 12070d9e8c0bSMaciej S. Szmigiero balloon->state); 12080d9e8c0bSMaciej S. Szmigiero return; 12090d9e8c0bSMaciej S. Szmigiero } 12100d9e8c0bSMaciej S. Szmigiero 12110d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgBR), 12120d9e8c0bSMaciej S. Szmigiero "DM_BALLOON_RESPONSE")) 12130d9e8c0bSMaciej S. Szmigiero return; 12140d9e8c0bSMaciej S. Szmigiero 12150d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_incoming_balloon(msgBR->hdr.trans_id, msgBR->range_count, 12160d9e8c0bSMaciej S. Szmigiero msgBR->more_pages); 12170d9e8c0bSMaciej S. Szmigiero 12180d9e8c0bSMaciej S. Szmigiero if (vmreq->msglen < sizeof(*msgBR) + 12190d9e8c0bSMaciej S. Szmigiero (uint64_t)sizeof(msgBR->range_array[0]) * msgBR->range_count) { 12200d9e8c0bSMaciej S. Szmigiero warn_report("DM_BALLOON_RESPONSE too short for the range count"); 12210d9e8c0bSMaciej S. Szmigiero return; 12220d9e8c0bSMaciej S. Szmigiero } 12230d9e8c0bSMaciej S. Szmigiero 12240d9e8c0bSMaciej S. Szmigiero if (msgBR->range_count == 0) { 12250d9e8c0bSMaciej S. Szmigiero /* The guest is already at its minimum size */ 12260d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff = 0; 12270d9e8c0bSMaciej S. Szmigiero goto ret_end_trans; 12280d9e8c0bSMaciej S. Szmigiero } else { 12290d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_remove_ranges(balloon, 12300d9e8c0bSMaciej S. Szmigiero msgBR->range_array, 12310d9e8c0bSMaciej S. Szmigiero msgBR->range_count); 12320d9e8c0bSMaciej S. Szmigiero } 12330d9e8c0bSMaciej S. Szmigiero 12340d9e8c0bSMaciej S. Szmigiero /* More responses expected? */ 12350d9e8c0bSMaciej S. Szmigiero if (msgBR->more_pages) { 12360d9e8c0bSMaciej S. Szmigiero return; 12370d9e8c0bSMaciej S. Szmigiero } 12380d9e8c0bSMaciej S. Szmigiero 12390d9e8c0bSMaciej S. Szmigiero ret_end_trans: 12400d9e8c0bSMaciej S. Szmigiero balloon->trans_id++; 12410d9e8c0bSMaciej S. Szmigiero 12420d9e8c0bSMaciej S. Szmigiero if (balloon->balloon_diff > 0) { 12430d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT); 12440d9e8c0bSMaciej S. Szmigiero } else { 12450d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); 12460d9e8c0bSMaciej S. Szmigiero } 12470d9e8c0bSMaciej S. Szmigiero } 12480d9e8c0bSMaciej S. Szmigiero 12490d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_packet(HvBalloon *balloon, HvBalloonReq *req, 12500d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 12510d9e8c0bSMaciej S. Szmigiero { 12520d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 12530d9e8c0bSMaciej S. Szmigiero struct dm_message *msg = vmreq->msg; 12540d9e8c0bSMaciej S. Szmigiero 12550d9e8c0bSMaciej S. Szmigiero if (vmreq->msglen < sizeof(msg->hdr)) { 12560d9e8c0bSMaciej S. Szmigiero return; 12570d9e8c0bSMaciej S. Szmigiero } 12580d9e8c0bSMaciej S. Szmigiero 12590d9e8c0bSMaciej S. Szmigiero switch (msg->hdr.type) { 12600d9e8c0bSMaciej S. Szmigiero case DM_VERSION_REQUEST: 12610d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_version_request(balloon, req, stdesc); 12620d9e8c0bSMaciej S. Szmigiero break; 12630d9e8c0bSMaciej S. Szmigiero 12640d9e8c0bSMaciej S. Szmigiero case DM_CAPABILITIES_REPORT: 12650d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_caps_report(balloon, req, stdesc); 12660d9e8c0bSMaciej S. Szmigiero break; 12670d9e8c0bSMaciej S. Szmigiero 12680d9e8c0bSMaciej S. Szmigiero case DM_STATUS_REPORT: 12690d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_status_report(balloon, req); 12700d9e8c0bSMaciej S. Szmigiero break; 12710d9e8c0bSMaciej S. Szmigiero 1272*99a4706aSMaciej S. Szmigiero case DM_MEM_HOT_ADD_RESPONSE: 1273*99a4706aSMaciej S. Szmigiero hv_balloon_handle_hot_add_response(balloon, req, stdesc); 1274*99a4706aSMaciej S. Szmigiero break; 1275*99a4706aSMaciej S. Szmigiero 12760d9e8c0bSMaciej S. Szmigiero case DM_UNBALLOON_RESPONSE: 12770d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_unballoon_response(balloon, req, stdesc); 12780d9e8c0bSMaciej S. Szmigiero break; 12790d9e8c0bSMaciej S. Szmigiero 12800d9e8c0bSMaciej S. Szmigiero case DM_BALLOON_RESPONSE: 12810d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_balloon_response(balloon, req, stdesc); 12820d9e8c0bSMaciej S. Szmigiero break; 12830d9e8c0bSMaciej S. Szmigiero 12840d9e8c0bSMaciej S. Szmigiero default: 12850d9e8c0bSMaciej S. Szmigiero warn_report("unknown DM message %u", msg->hdr.type); 12860d9e8c0bSMaciej S. Szmigiero break; 12870d9e8c0bSMaciej S. Szmigiero } 12880d9e8c0bSMaciej S. Szmigiero } 12890d9e8c0bSMaciej S. Szmigiero 12900d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_recv_channel(HvBalloon *balloon, StateDesc *stdesc) 12910d9e8c0bSMaciej S. Szmigiero { 12920d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan; 12930d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req; 12940d9e8c0bSMaciej S. Szmigiero 12950d9e8c0bSMaciej S. Szmigiero if (balloon->state == S_WAIT_RESET || 12960d9e8c0bSMaciej S. Szmigiero balloon->state == S_POST_RESET_CLOSED) { 12970d9e8c0bSMaciej S. Szmigiero return false; 12980d9e8c0bSMaciej S. Szmigiero } 12990d9e8c0bSMaciej S. Szmigiero 13000d9e8c0bSMaciej S. Szmigiero chan = hv_balloon_get_channel(balloon); 13010d9e8c0bSMaciej S. Szmigiero if (vmbus_channel_recv_start(chan)) { 13020d9e8c0bSMaciej S. Szmigiero return false; 13030d9e8c0bSMaciej S. Szmigiero } 13040d9e8c0bSMaciej S. Szmigiero 13050d9e8c0bSMaciej S. Szmigiero while ((req = vmbus_channel_recv_peek(chan, sizeof(*req)))) { 13060d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_packet(balloon, req, stdesc); 13070d9e8c0bSMaciej S. Szmigiero vmbus_free_req(req); 13080d9e8c0bSMaciej S. Szmigiero vmbus_channel_recv_pop(chan); 13090d9e8c0bSMaciej S. Szmigiero 13100d9e8c0bSMaciej S. Szmigiero if (stdesc->state != S_NO_CHANGE) { 13110d9e8c0bSMaciej S. Szmigiero break; 13120d9e8c0bSMaciej S. Szmigiero } 13130d9e8c0bSMaciej S. Szmigiero } 13140d9e8c0bSMaciej S. Szmigiero 13150d9e8c0bSMaciej S. Szmigiero return vmbus_channel_recv_done(chan) > 0; 13160d9e8c0bSMaciej S. Szmigiero } 13170d9e8c0bSMaciej S. Szmigiero 13180d9e8c0bSMaciej S. Szmigiero /* old state handler -> new state transition (potential) */ 13190d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_event_loop_state(HvBalloon *balloon) 13200d9e8c0bSMaciej S. Szmigiero { 13210d9e8c0bSMaciej S. Szmigiero StateDesc state_new = HV_BALLOON_STATE_DESC_INIT; 13220d9e8c0bSMaciej S. Szmigiero 13230d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_state(balloon, &state_new); 13240d9e8c0bSMaciej S. Szmigiero return hv_balloon_state_set(balloon, state_new.state, state_new.desc); 13250d9e8c0bSMaciej S. Szmigiero } 13260d9e8c0bSMaciej S. Szmigiero 13270d9e8c0bSMaciej S. Szmigiero /* VMBus message -> new state transition (potential) */ 13280d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_event_loop_recv(HvBalloon *balloon) 13290d9e8c0bSMaciej S. Szmigiero { 13300d9e8c0bSMaciej S. Szmigiero StateDesc state_new = HV_BALLOON_STATE_DESC_INIT; 13310d9e8c0bSMaciej S. Szmigiero bool any_recv, state_changed; 13320d9e8c0bSMaciej S. Szmigiero 13330d9e8c0bSMaciej S. Szmigiero any_recv = hv_balloon_recv_channel(balloon, &state_new); 13340d9e8c0bSMaciej S. Szmigiero state_changed = hv_balloon_state_set(balloon, 13350d9e8c0bSMaciej S. Szmigiero state_new.state, state_new.desc); 13360d9e8c0bSMaciej S. Szmigiero 13370d9e8c0bSMaciej S. Szmigiero return state_changed || any_recv; 13380d9e8c0bSMaciej S. Szmigiero } 13390d9e8c0bSMaciej S. Szmigiero 13400d9e8c0bSMaciej S. Szmigiero static void hv_balloon_event_loop(HvBalloon *balloon) 13410d9e8c0bSMaciej S. Szmigiero { 13420d9e8c0bSMaciej S. Szmigiero bool state_repeat, recv_repeat; 13430d9e8c0bSMaciej S. Szmigiero 13440d9e8c0bSMaciej S. Szmigiero do { 13450d9e8c0bSMaciej S. Szmigiero state_repeat = hv_balloon_event_loop_state(balloon); 13460d9e8c0bSMaciej S. Szmigiero recv_repeat = hv_balloon_event_loop_recv(balloon); 13470d9e8c0bSMaciej S. Szmigiero } while (state_repeat || recv_repeat); 13480d9e8c0bSMaciej S. Szmigiero } 13490d9e8c0bSMaciej S. Szmigiero 13500d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_chan_notify(VMBusChannel *chan) 13510d9e8c0bSMaciej S. Szmigiero { 13520d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); 13530d9e8c0bSMaciej S. Szmigiero 13540d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 13550d9e8c0bSMaciej S. Szmigiero } 13560d9e8c0bSMaciej S. Szmigiero 13570d9e8c0bSMaciej S. Szmigiero static void hv_balloon_stat(void *opaque, BalloonInfo *info) 13580d9e8c0bSMaciej S. Szmigiero { 13590d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = opaque; 13600d9e8c0bSMaciej S. Szmigiero info->actual = (hv_balloon_total_ram(balloon) - balloon->removed_both_ctr) 13610d9e8c0bSMaciej S. Szmigiero << HV_BALLOON_PFN_SHIFT; 13620d9e8c0bSMaciej S. Szmigiero } 13630d9e8c0bSMaciej S. Szmigiero 13640d9e8c0bSMaciej S. Szmigiero static void hv_balloon_to_target(void *opaque, ram_addr_t target) 13650d9e8c0bSMaciej S. Szmigiero { 13660d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = opaque; 13670d9e8c0bSMaciej S. Szmigiero uint64_t target_pages = target >> HV_BALLOON_PFN_SHIFT; 13680d9e8c0bSMaciej S. Szmigiero 13690d9e8c0bSMaciej S. Szmigiero if (!target_pages) { 13700d9e8c0bSMaciej S. Szmigiero return; 13710d9e8c0bSMaciej S. Szmigiero } 13720d9e8c0bSMaciej S. Szmigiero 13730d9e8c0bSMaciej S. Szmigiero /* 13740d9e8c0bSMaciej S. Szmigiero * always set target_changed, even with unchanged target, as the user 13750d9e8c0bSMaciej S. Szmigiero * might be asking us to try again reaching it 13760d9e8c0bSMaciej S. Szmigiero */ 13770d9e8c0bSMaciej S. Szmigiero balloon->target = target_pages; 13780d9e8c0bSMaciej S. Szmigiero balloon->target_changed = true; 13790d9e8c0bSMaciej S. Szmigiero 13800d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 13810d9e8c0bSMaciej S. Szmigiero } 13820d9e8c0bSMaciej S. Szmigiero 13830d9e8c0bSMaciej S. Szmigiero static int hv_balloon_vmdev_open_channel(VMBusChannel *chan) 13840d9e8c0bSMaciej S. Szmigiero { 13850d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); 13860d9e8c0bSMaciej S. Szmigiero 13870d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_POST_RESET_CLOSED) { 13880d9e8c0bSMaciej S. Szmigiero warn_report("guest trying to open a DM channel in invalid %d state", 13890d9e8c0bSMaciej S. Szmigiero balloon->state); 13900d9e8c0bSMaciej S. Szmigiero return -EINVAL; 13910d9e8c0bSMaciej S. Szmigiero } 13920d9e8c0bSMaciej S. Szmigiero 13930d9e8c0bSMaciej S. Szmigiero HV_BALLOON_SET_STATE(balloon, S_VERSION); 13940d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 13950d9e8c0bSMaciej S. Szmigiero 13960d9e8c0bSMaciej S. Szmigiero return 0; 13970d9e8c0bSMaciej S. Szmigiero } 13980d9e8c0bSMaciej S. Szmigiero 13990d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_close_channel(VMBusChannel *chan) 14000d9e8c0bSMaciej S. Szmigiero { 14010d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); 14020d9e8c0bSMaciej S. Szmigiero 14030d9e8c0bSMaciej S. Szmigiero timer_del(&balloon->post_init_timer); 14040d9e8c0bSMaciej S. Szmigiero 14050d9e8c0bSMaciej S. Szmigiero /* Don't report stale data */ 14060d9e8c0bSMaciej S. Szmigiero balloon->status_report.received = false; 14070d9e8c0bSMaciej S. Szmigiero 14080d9e8c0bSMaciej S. Szmigiero HV_BALLOON_SET_STATE(balloon, S_WAIT_RESET); 14090d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 14100d9e8c0bSMaciej S. Szmigiero } 14110d9e8c0bSMaciej S. Szmigiero 14120d9e8c0bSMaciej S. Szmigiero static void hv_balloon_post_init_timer(void *opaque) 14130d9e8c0bSMaciej S. Szmigiero { 14140d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = opaque; 14150d9e8c0bSMaciej S. Szmigiero 14160d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_POST_INIT_WAIT) { 14170d9e8c0bSMaciej S. Szmigiero return; 14180d9e8c0bSMaciej S. Szmigiero } 14190d9e8c0bSMaciej S. Szmigiero 14200d9e8c0bSMaciej S. Szmigiero HV_BALLOON_SET_STATE(balloon, S_IDLE); 14210d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 14220d9e8c0bSMaciej S. Szmigiero } 14230d9e8c0bSMaciej S. Szmigiero 1424*99a4706aSMaciej S. Szmigiero static void hv_balloon_system_reset_unrealize_common(HvBalloon *balloon) 1425*99a4706aSMaciej S. Szmigiero { 1426*99a4706aSMaciej S. Szmigiero g_clear_pointer(&balloon->our_range, hvb_our_range_memslots_free); 1427*99a4706aSMaciej S. Szmigiero } 1428*99a4706aSMaciej S. Szmigiero 1429*99a4706aSMaciej S. Szmigiero static void hv_balloon_system_reset(void *opaque) 1430*99a4706aSMaciej S. Szmigiero { 1431*99a4706aSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(opaque); 1432*99a4706aSMaciej S. Szmigiero 1433*99a4706aSMaciej S. Szmigiero hv_balloon_system_reset_unrealize_common(balloon); 1434*99a4706aSMaciej S. Szmigiero } 1435*99a4706aSMaciej S. Szmigiero 1436*99a4706aSMaciej S. Szmigiero static void hv_balloon_ensure_mr(HvBalloon *balloon) 1437*99a4706aSMaciej S. Szmigiero { 1438*99a4706aSMaciej S. Szmigiero MemoryRegion *hostmem_mr; 1439*99a4706aSMaciej S. Szmigiero 1440*99a4706aSMaciej S. Szmigiero assert(balloon->hostmem); 1441*99a4706aSMaciej S. Szmigiero 1442*99a4706aSMaciej S. Szmigiero if (balloon->mr) { 1443*99a4706aSMaciej S. Szmigiero return; 1444*99a4706aSMaciej S. Szmigiero } 1445*99a4706aSMaciej S. Szmigiero 1446*99a4706aSMaciej S. Szmigiero hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); 1447*99a4706aSMaciej S. Szmigiero 1448*99a4706aSMaciej S. Szmigiero balloon->mr = g_new0(MemoryRegion, 1); 1449*99a4706aSMaciej S. Szmigiero memory_region_init(balloon->mr, OBJECT(balloon), TYPE_HV_BALLOON, 1450*99a4706aSMaciej S. Szmigiero memory_region_size(hostmem_mr)); 1451*99a4706aSMaciej S. Szmigiero 1452*99a4706aSMaciej S. Szmigiero /* 1453*99a4706aSMaciej S. Szmigiero * The VM can indicate an alignment up to 32 GiB. Memory device core can 1454*99a4706aSMaciej S. Szmigiero * usually only handle/guarantee 1 GiB alignment. The user will have to 1455*99a4706aSMaciej S. Szmigiero * specify a larger maxmem eventually. 1456*99a4706aSMaciej S. Szmigiero * 1457*99a4706aSMaciej S. Szmigiero * The memory device core will warn the user in case maxmem might have to be 1458*99a4706aSMaciej S. Szmigiero * increased and will fail plugging the device if there is not sufficient 1459*99a4706aSMaciej S. Szmigiero * space after alignment. 1460*99a4706aSMaciej S. Szmigiero * 1461*99a4706aSMaciej S. Szmigiero * TODO: we could do the alignment ourselves in a slightly bigger region. 1462*99a4706aSMaciej S. Szmigiero * But this feels better, although the warning might be annoying. Maybe 1463*99a4706aSMaciej S. Szmigiero * we can optimize that in the future (e.g., with such a device on the 1464*99a4706aSMaciej S. Szmigiero * cmdline place/size the device memory region differently. 1465*99a4706aSMaciej S. Szmigiero */ 1466*99a4706aSMaciej S. Szmigiero balloon->mr->align = MAX(32 * GiB, memory_region_get_alignment(hostmem_mr)); 1467*99a4706aSMaciej S. Szmigiero } 1468*99a4706aSMaciej S. Szmigiero 1469*99a4706aSMaciej S. Szmigiero static void hv_balloon_free_mr(HvBalloon *balloon) 1470*99a4706aSMaciej S. Szmigiero { 1471*99a4706aSMaciej S. Szmigiero if (!balloon->mr) { 1472*99a4706aSMaciej S. Szmigiero return; 1473*99a4706aSMaciej S. Szmigiero } 1474*99a4706aSMaciej S. Szmigiero 1475*99a4706aSMaciej S. Szmigiero object_unparent(OBJECT(balloon->mr)); 1476*99a4706aSMaciej S. Szmigiero g_clear_pointer(&balloon->mr, g_free); 1477*99a4706aSMaciej S. Szmigiero } 1478*99a4706aSMaciej S. Szmigiero 14790d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_realize(VMBusDevice *vdev, Error **errp) 14800d9e8c0bSMaciej S. Szmigiero { 14810d9e8c0bSMaciej S. Szmigiero ERRP_GUARD(); 14820d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vdev); 14830d9e8c0bSMaciej S. Szmigiero int ret; 14840d9e8c0bSMaciej S. Szmigiero 14850d9e8c0bSMaciej S. Szmigiero balloon->state = S_WAIT_RESET; 14860d9e8c0bSMaciej S. Szmigiero 14870d9e8c0bSMaciej S. Szmigiero ret = qemu_add_balloon_handler(hv_balloon_to_target, hv_balloon_stat, 14880d9e8c0bSMaciej S. Szmigiero balloon); 14890d9e8c0bSMaciej S. Szmigiero if (ret < 0) { 14900d9e8c0bSMaciej S. Szmigiero /* This also protects against having multiple hv-balloon instances */ 14910d9e8c0bSMaciej S. Szmigiero error_setg(errp, "Only one balloon device is supported"); 14920d9e8c0bSMaciej S. Szmigiero return; 14930d9e8c0bSMaciej S. Szmigiero } 14940d9e8c0bSMaciej S. Szmigiero 1495*99a4706aSMaciej S. Szmigiero if (balloon->hostmem) { 1496*99a4706aSMaciej S. Szmigiero if (host_memory_backend_is_mapped(balloon->hostmem)) { 1497*99a4706aSMaciej S. Szmigiero Object *obj = OBJECT(balloon->hostmem); 1498*99a4706aSMaciej S. Szmigiero 1499*99a4706aSMaciej S. Szmigiero error_setg(errp, "'%s' property specifies a busy memdev: %s", 1500*99a4706aSMaciej S. Szmigiero HV_BALLOON_MEMDEV_PROP, 1501*99a4706aSMaciej S. Szmigiero object_get_canonical_path_component(obj)); 1502*99a4706aSMaciej S. Szmigiero goto out_balloon_handler; 1503*99a4706aSMaciej S. Szmigiero } 1504*99a4706aSMaciej S. Szmigiero 1505*99a4706aSMaciej S. Szmigiero hv_balloon_ensure_mr(balloon); 1506*99a4706aSMaciej S. Szmigiero 1507*99a4706aSMaciej S. Szmigiero /* This is rather unlikely to happen, but let's still check for it. */ 1508*99a4706aSMaciej S. Szmigiero if (!QEMU_IS_ALIGNED(memory_region_size(balloon->mr), 1509*99a4706aSMaciej S. Szmigiero HV_BALLOON_PAGE_SIZE)) { 1510*99a4706aSMaciej S. Szmigiero error_setg(errp, "'%s' property memdev size has to be a multiple of 0x%" PRIx64, 1511*99a4706aSMaciej S. Szmigiero HV_BALLOON_MEMDEV_PROP, (uint64_t)HV_BALLOON_PAGE_SIZE); 1512*99a4706aSMaciej S. Szmigiero goto out_balloon_handler; 1513*99a4706aSMaciej S. Szmigiero } 1514*99a4706aSMaciej S. Szmigiero 1515*99a4706aSMaciej S. Szmigiero host_memory_backend_set_mapped(balloon->hostmem, true); 1516*99a4706aSMaciej S. Szmigiero vmstate_register_ram(host_memory_backend_get_memory(balloon->hostmem), 1517*99a4706aSMaciej S. Szmigiero DEVICE(balloon)); 1518*99a4706aSMaciej S. Szmigiero } else if (balloon->addr) { 1519*99a4706aSMaciej S. Szmigiero error_setg(errp, "'%s' property must not be set without a memdev", 1520*99a4706aSMaciej S. Szmigiero HV_BALLOON_MEMDEV_PROP); 1521*99a4706aSMaciej S. Szmigiero goto out_balloon_handler; 1522*99a4706aSMaciej S. Szmigiero } 1523*99a4706aSMaciej S. Szmigiero 15240d9e8c0bSMaciej S. Szmigiero timer_init_ms(&balloon->post_init_timer, QEMU_CLOCK_VIRTUAL, 15250d9e8c0bSMaciej S. Szmigiero hv_balloon_post_init_timer, balloon); 1526*99a4706aSMaciej S. Szmigiero 1527*99a4706aSMaciej S. Szmigiero qemu_register_reset(hv_balloon_system_reset, balloon); 1528*99a4706aSMaciej S. Szmigiero 1529*99a4706aSMaciej S. Szmigiero return; 1530*99a4706aSMaciej S. Szmigiero 1531*99a4706aSMaciej S. Szmigiero out_balloon_handler: 1532*99a4706aSMaciej S. Szmigiero qemu_remove_balloon_handler(balloon); 15330d9e8c0bSMaciej S. Szmigiero } 15340d9e8c0bSMaciej S. Szmigiero 15350d9e8c0bSMaciej S. Szmigiero /* 15360d9e8c0bSMaciej S. Szmigiero * VMBus device reset has to be implemented in case the guest decides to 15370d9e8c0bSMaciej S. Szmigiero * disconnect and reconnect to the VMBus without rebooting the whole system. 1538*99a4706aSMaciej S. Szmigiero * 1539*99a4706aSMaciej S. Szmigiero * However, the hot-added memory can't be removed here as Windows keeps on using 1540*99a4706aSMaciej S. Szmigiero * it until the system is restarted, even after disconnecting from the VMBus. 15410d9e8c0bSMaciej S. Szmigiero */ 15420d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_reset(VMBusDevice *vdev) 15430d9e8c0bSMaciej S. Szmigiero { 15440d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vdev); 15450d9e8c0bSMaciej S. Szmigiero 15460d9e8c0bSMaciej S. Szmigiero if (balloon->state == S_POST_RESET_CLOSED) { 15470d9e8c0bSMaciej S. Szmigiero return; 15480d9e8c0bSMaciej S. Szmigiero } 15490d9e8c0bSMaciej S. Szmigiero 1550*99a4706aSMaciej S. Szmigiero if (balloon->our_range) { 1551*99a4706aSMaciej S. Szmigiero hvb_our_range_clear_removed_trees(OUR_RANGE(balloon->our_range)); 1552*99a4706aSMaciej S. Szmigiero } 1553*99a4706aSMaciej S. Szmigiero 15540d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&balloon->removed_guest); 15550d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&balloon->removed_both); 15560d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_init(&balloon->removed_guest); 15570d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_init(&balloon->removed_both); 15580d9e8c0bSMaciej S. Szmigiero 15590d9e8c0bSMaciej S. Szmigiero balloon->trans_id = 0; 15600d9e8c0bSMaciej S. Szmigiero balloon->removed_guest_ctr = 0; 15610d9e8c0bSMaciej S. Szmigiero balloon->removed_both_ctr = 0; 15620d9e8c0bSMaciej S. Szmigiero 15630d9e8c0bSMaciej S. Szmigiero HV_BALLOON_SET_STATE(balloon, S_POST_RESET_CLOSED); 15640d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 15650d9e8c0bSMaciej S. Szmigiero } 15660d9e8c0bSMaciej S. Szmigiero 1567*99a4706aSMaciej S. Szmigiero /* 1568*99a4706aSMaciej S. Szmigiero * Clean up things that were (possibly) allocated pre-realization, for example 1569*99a4706aSMaciej S. Szmigiero * from memory_device_pre_plug(), so we don't leak them if the device don't 1570*99a4706aSMaciej S. Szmigiero * actually get realized in the end. 1571*99a4706aSMaciej S. Szmigiero */ 1572*99a4706aSMaciej S. Szmigiero static void hv_balloon_unrealize_finalize_common(HvBalloon *balloon) 1573*99a4706aSMaciej S. Szmigiero { 1574*99a4706aSMaciej S. Szmigiero hv_balloon_free_mr(balloon); 1575*99a4706aSMaciej S. Szmigiero balloon->addr = 0; 1576*99a4706aSMaciej S. Szmigiero 1577*99a4706aSMaciej S. Szmigiero balloon->memslot_count = 0; 1578*99a4706aSMaciej S. Szmigiero } 1579*99a4706aSMaciej S. Szmigiero 15800d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_unrealize(VMBusDevice *vdev) 15810d9e8c0bSMaciej S. Szmigiero { 15820d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vdev); 15830d9e8c0bSMaciej S. Szmigiero 1584*99a4706aSMaciej S. Szmigiero qemu_unregister_reset(hv_balloon_system_reset, balloon); 1585*99a4706aSMaciej S. Szmigiero 1586*99a4706aSMaciej S. Szmigiero hv_balloon_system_reset_unrealize_common(balloon); 1587*99a4706aSMaciej S. Szmigiero 15880d9e8c0bSMaciej S. Szmigiero qemu_remove_balloon_handler(balloon); 15890d9e8c0bSMaciej S. Szmigiero 1590*99a4706aSMaciej S. Szmigiero if (balloon->hostmem) { 1591*99a4706aSMaciej S. Szmigiero vmstate_unregister_ram(host_memory_backend_get_memory(balloon->hostmem), 1592*99a4706aSMaciej S. Szmigiero DEVICE(balloon)); 1593*99a4706aSMaciej S. Szmigiero host_memory_backend_set_mapped(balloon->hostmem, false); 1594*99a4706aSMaciej S. Szmigiero } 1595*99a4706aSMaciej S. Szmigiero 15960d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&balloon->removed_guest); 15970d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&balloon->removed_both); 1598*99a4706aSMaciej S. Szmigiero 1599*99a4706aSMaciej S. Szmigiero hv_balloon_unrealize_finalize_common(balloon); 1600*99a4706aSMaciej S. Szmigiero } 1601*99a4706aSMaciej S. Szmigiero 1602*99a4706aSMaciej S. Szmigiero static uint64_t hv_balloon_md_get_addr(const MemoryDeviceState *md) 1603*99a4706aSMaciej S. Szmigiero { 1604*99a4706aSMaciej S. Szmigiero return object_property_get_uint(OBJECT(md), HV_BALLOON_ADDR_PROP, 1605*99a4706aSMaciej S. Szmigiero &error_abort); 1606*99a4706aSMaciej S. Szmigiero } 1607*99a4706aSMaciej S. Szmigiero 1608*99a4706aSMaciej S. Szmigiero static void hv_balloon_md_set_addr(MemoryDeviceState *md, uint64_t addr, 1609*99a4706aSMaciej S. Szmigiero Error **errp) 1610*99a4706aSMaciej S. Szmigiero { 1611*99a4706aSMaciej S. Szmigiero object_property_set_uint(OBJECT(md), HV_BALLOON_ADDR_PROP, addr, errp); 1612*99a4706aSMaciej S. Szmigiero } 1613*99a4706aSMaciej S. Szmigiero 1614*99a4706aSMaciej S. Szmigiero static MemoryRegion *hv_balloon_md_get_memory_region(MemoryDeviceState *md, 1615*99a4706aSMaciej S. Szmigiero Error **errp) 1616*99a4706aSMaciej S. Szmigiero { 1617*99a4706aSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(md); 1618*99a4706aSMaciej S. Szmigiero 1619*99a4706aSMaciej S. Szmigiero if (!balloon->hostmem) { 1620*99a4706aSMaciej S. Szmigiero return NULL; 1621*99a4706aSMaciej S. Szmigiero } 1622*99a4706aSMaciej S. Szmigiero 1623*99a4706aSMaciej S. Szmigiero hv_balloon_ensure_mr(balloon); 1624*99a4706aSMaciej S. Szmigiero 1625*99a4706aSMaciej S. Szmigiero return balloon->mr; 1626*99a4706aSMaciej S. Szmigiero } 1627*99a4706aSMaciej S. Szmigiero 1628*99a4706aSMaciej S. Szmigiero static void hv_balloon_decide_memslots(MemoryDeviceState *md, 1629*99a4706aSMaciej S. Szmigiero unsigned int limit) 1630*99a4706aSMaciej S. Szmigiero { 1631*99a4706aSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(md); 1632*99a4706aSMaciej S. Szmigiero MemoryRegion *hostmem_mr; 1633*99a4706aSMaciej S. Szmigiero uint64_t region_size, memslot_size, memslots; 1634*99a4706aSMaciej S. Szmigiero 1635*99a4706aSMaciej S. Szmigiero /* We're called exactly once, before realizing the device. */ 1636*99a4706aSMaciej S. Szmigiero assert(!balloon->memslot_count); 1637*99a4706aSMaciej S. Szmigiero 1638*99a4706aSMaciej S. Szmigiero /* We should not be called if we don't have a memory backend */ 1639*99a4706aSMaciej S. Szmigiero assert(balloon->hostmem); 1640*99a4706aSMaciej S. Szmigiero 1641*99a4706aSMaciej S. Szmigiero hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); 1642*99a4706aSMaciej S. Szmigiero region_size = memory_region_size(hostmem_mr); 1643*99a4706aSMaciej S. Szmigiero 1644*99a4706aSMaciej S. Szmigiero assert(region_size > 0); 1645*99a4706aSMaciej S. Szmigiero memslot_size = QEMU_ALIGN_UP(region_size / limit, 1646*99a4706aSMaciej S. Szmigiero HV_BALLOON_HA_MEMSLOT_SIZE_ALIGN); 1647*99a4706aSMaciej S. Szmigiero memslots = QEMU_ALIGN_UP(region_size, memslot_size) / memslot_size; 1648*99a4706aSMaciej S. Szmigiero 1649*99a4706aSMaciej S. Szmigiero if (memslots > 1) { 1650*99a4706aSMaciej S. Szmigiero balloon->memslot_size = memslot_size; 1651*99a4706aSMaciej S. Szmigiero } else { 1652*99a4706aSMaciej S. Szmigiero balloon->memslot_size = region_size; 1653*99a4706aSMaciej S. Szmigiero } 1654*99a4706aSMaciej S. Szmigiero 1655*99a4706aSMaciej S. Szmigiero assert(memslots <= UINT_MAX); 1656*99a4706aSMaciej S. Szmigiero balloon->memslot_count = memslots; 1657*99a4706aSMaciej S. Szmigiero } 1658*99a4706aSMaciej S. Szmigiero 1659*99a4706aSMaciej S. Szmigiero static unsigned int hv_balloon_get_memslots(MemoryDeviceState *md) 1660*99a4706aSMaciej S. Szmigiero { 1661*99a4706aSMaciej S. Szmigiero const HvBalloon *balloon = HV_BALLOON(md); 1662*99a4706aSMaciej S. Szmigiero 1663*99a4706aSMaciej S. Szmigiero /* We're called after setting the suggested limit. */ 1664*99a4706aSMaciej S. Szmigiero assert(balloon->memslot_count > 0); 1665*99a4706aSMaciej S. Szmigiero 1666*99a4706aSMaciej S. Szmigiero return balloon->memslot_count; 16670d9e8c0bSMaciej S. Szmigiero } 16680d9e8c0bSMaciej S. Szmigiero 16690d9e8c0bSMaciej S. Szmigiero static void hv_balloon_init(Object *obj) 16700d9e8c0bSMaciej S. Szmigiero { 16710d9e8c0bSMaciej S. Szmigiero } 16720d9e8c0bSMaciej S. Szmigiero 16730d9e8c0bSMaciej S. Szmigiero static void hv_balloon_finalize(Object *obj) 16740d9e8c0bSMaciej S. Szmigiero { 1675*99a4706aSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(obj); 1676*99a4706aSMaciej S. Szmigiero 1677*99a4706aSMaciej S. Szmigiero hv_balloon_unrealize_finalize_common(balloon); 16780d9e8c0bSMaciej S. Szmigiero } 16790d9e8c0bSMaciej S. Szmigiero 16800d9e8c0bSMaciej S. Szmigiero static Property hv_balloon_properties[] = { 16810d9e8c0bSMaciej S. Szmigiero DEFINE_PROP_BOOL("status-report", HvBalloon, 16820d9e8c0bSMaciej S. Szmigiero status_report.enabled, false), 16830d9e8c0bSMaciej S. Szmigiero 1684*99a4706aSMaciej S. Szmigiero /* MEMORY_DEVICE props */ 1685*99a4706aSMaciej S. Szmigiero DEFINE_PROP_LINK(HV_BALLOON_MEMDEV_PROP, HvBalloon, hostmem, 1686*99a4706aSMaciej S. Szmigiero TYPE_MEMORY_BACKEND, HostMemoryBackend *), 1687*99a4706aSMaciej S. Szmigiero DEFINE_PROP_UINT64(HV_BALLOON_ADDR_PROP, HvBalloon, addr, 0), 1688*99a4706aSMaciej S. Szmigiero 16890d9e8c0bSMaciej S. Szmigiero DEFINE_PROP_END_OF_LIST(), 16900d9e8c0bSMaciej S. Szmigiero }; 16910d9e8c0bSMaciej S. Szmigiero 16920d9e8c0bSMaciej S. Szmigiero static void hv_balloon_class_init(ObjectClass *klass, void *data) 16930d9e8c0bSMaciej S. Szmigiero { 16940d9e8c0bSMaciej S. Szmigiero DeviceClass *dc = DEVICE_CLASS(klass); 16950d9e8c0bSMaciej S. Szmigiero VMBusDeviceClass *vdc = VMBUS_DEVICE_CLASS(klass); 1696*99a4706aSMaciej S. Szmigiero MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass); 16970d9e8c0bSMaciej S. Szmigiero 16980d9e8c0bSMaciej S. Szmigiero device_class_set_props(dc, hv_balloon_properties); 16990d9e8c0bSMaciej S. Szmigiero qemu_uuid_parse(HV_BALLOON_GUID, &vdc->classid); 17000d9e8c0bSMaciej S. Szmigiero set_bit(DEVICE_CATEGORY_MISC, dc->categories); 17010d9e8c0bSMaciej S. Szmigiero 17020d9e8c0bSMaciej S. Szmigiero vdc->vmdev_realize = hv_balloon_vmdev_realize; 17030d9e8c0bSMaciej S. Szmigiero vdc->vmdev_unrealize = hv_balloon_vmdev_unrealize; 17040d9e8c0bSMaciej S. Szmigiero vdc->vmdev_reset = hv_balloon_vmdev_reset; 17050d9e8c0bSMaciej S. Szmigiero vdc->open_channel = hv_balloon_vmdev_open_channel; 17060d9e8c0bSMaciej S. Szmigiero vdc->close_channel = hv_balloon_vmdev_close_channel; 17070d9e8c0bSMaciej S. Szmigiero vdc->chan_notify_cb = hv_balloon_vmdev_chan_notify; 1708*99a4706aSMaciej S. Szmigiero 1709*99a4706aSMaciej S. Szmigiero mdc->get_addr = hv_balloon_md_get_addr; 1710*99a4706aSMaciej S. Szmigiero mdc->set_addr = hv_balloon_md_set_addr; 1711*99a4706aSMaciej S. Szmigiero mdc->get_plugged_size = memory_device_get_region_size; 1712*99a4706aSMaciej S. Szmigiero mdc->get_memory_region = hv_balloon_md_get_memory_region; 1713*99a4706aSMaciej S. Szmigiero mdc->decide_memslots = hv_balloon_decide_memslots; 1714*99a4706aSMaciej S. Szmigiero mdc->get_memslots = hv_balloon_get_memslots; 1715*99a4706aSMaciej S. Szmigiero /* implement fill_device_info */ 17160d9e8c0bSMaciej S. Szmigiero } 1717