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 1005871c72SPeter Maydell #include "qemu/osdep.h" 110d9e8c0bSMaciej S. Szmigiero #include "hv-balloon-internal.h" 120d9e8c0bSMaciej S. Szmigiero 130d9e8c0bSMaciej S. Szmigiero #include "exec/address-spaces.h" 140d9e8c0bSMaciej S. Szmigiero #include "exec/cpu-common.h" 150d9e8c0bSMaciej S. Szmigiero #include "exec/ramblock.h" 160d9e8c0bSMaciej S. Szmigiero #include "hw/boards.h" 170d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/dynmem-proto.h" 180d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/hv-balloon.h" 190d9e8c0bSMaciej S. Szmigiero #include "hw/hyperv/vmbus.h" 200d9e8c0bSMaciej S. Szmigiero #include "hw/mem/memory-device.h" 210d9e8c0bSMaciej S. Szmigiero #include "hw/mem/pc-dimm.h" 220d9e8c0bSMaciej S. Szmigiero #include "hw/qdev-core.h" 230d9e8c0bSMaciej S. Szmigiero #include "hw/qdev-properties.h" 240d9e8c0bSMaciej S. Szmigiero #include "monitor/qdev.h" 250d9e8c0bSMaciej S. Szmigiero #include "qapi/error.h" 260d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-commands-machine.h" 270d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-events-machine.h" 280d9e8c0bSMaciej S. Szmigiero #include "qapi/qapi-types-machine.h" 290d9e8c0bSMaciej S. Szmigiero #include "qapi/qmp/qdict.h" 300d9e8c0bSMaciej S. Szmigiero #include "qapi/visitor.h" 310d9e8c0bSMaciej S. Szmigiero #include "qemu/error-report.h" 320d9e8c0bSMaciej S. Szmigiero #include "qemu/module.h" 330d9e8c0bSMaciej S. Szmigiero #include "qemu/units.h" 340d9e8c0bSMaciej S. Szmigiero #include "qemu/timer.h" 35*32cad1ffSPhilippe Mathieu-Daudé #include "system/balloon.h" 36*32cad1ffSPhilippe Mathieu-Daudé #include "system/hostmem.h" 37*32cad1ffSPhilippe Mathieu-Daudé #include "system/reset.h" 3899a4706aSMaciej S. Szmigiero #include "hv-balloon-our_range_memslots.h" 390d9e8c0bSMaciej S. Szmigiero #include "hv-balloon-page_range_tree.h" 400d9e8c0bSMaciej S. Szmigiero #include "trace.h" 410d9e8c0bSMaciej S. Szmigiero 4299a4706aSMaciej S. Szmigiero #define HV_BALLOON_ADDR_PROP "addr" 4399a4706aSMaciej S. Szmigiero #define HV_BALLOON_MEMDEV_PROP "memdev" 440d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_GUID "525074DC-8985-46e2-8057-A307DC18A502" 450d9e8c0bSMaciej S. Szmigiero 460d9e8c0bSMaciej S. Szmigiero /* 470d9e8c0bSMaciej S. Szmigiero * Some Windows versions (at least Server 2019) will crash with various 480d9e8c0bSMaciej S. Szmigiero * error codes when receiving DM protocol requests (at least 490d9e8c0bSMaciej S. Szmigiero * DM_MEM_HOT_ADD_REQUEST) immediately after boot. 500d9e8c0bSMaciej S. Szmigiero * 510d9e8c0bSMaciej S. Szmigiero * It looks like Hyper-V from Server 2016 uses a 50-second after-boot 520d9e8c0bSMaciej S. Szmigiero * delay, probably to workaround this issue, so we'll use this value, too. 530d9e8c0bSMaciej S. Szmigiero */ 540d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_POST_INIT_WAIT (50 * 1000) 550d9e8c0bSMaciej S. Szmigiero 560d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HA_CHUNK_SIZE (2 * GiB) 570d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HA_CHUNK_PAGES (HV_BALLOON_HA_CHUNK_SIZE / HV_BALLOON_PAGE_SIZE) 580d9e8c0bSMaciej S. Szmigiero 5999a4706aSMaciej S. Szmigiero #define HV_BALLOON_HA_MEMSLOT_SIZE_ALIGN (128 * MiB) 6099a4706aSMaciej S. Szmigiero 610d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_HR_CHUNK_PAGES 585728 620d9e8c0bSMaciej S. Szmigiero /* 630d9e8c0bSMaciej S. Szmigiero * ^ that's the maximum number of pages 640d9e8c0bSMaciej S. Szmigiero * that Windows returns in one hot remove response 650d9e8c0bSMaciej S. Szmigiero * 660d9e8c0bSMaciej S. Szmigiero * If the number requested is too high Windows will no longer honor 670d9e8c0bSMaciej S. Szmigiero * these requests 680d9e8c0bSMaciej S. Szmigiero */ 690d9e8c0bSMaciej S. Szmigiero 700d9e8c0bSMaciej S. Szmigiero struct HvBalloonClass { 710d9e8c0bSMaciej S. Szmigiero VMBusDeviceClass parent_class; 720d9e8c0bSMaciej S. Szmigiero } HvBalloonClass; 730d9e8c0bSMaciej S. Szmigiero 740d9e8c0bSMaciej S. Szmigiero typedef enum State { 750d9e8c0bSMaciej S. Szmigiero /* not a real state */ 760d9e8c0bSMaciej S. Szmigiero S_NO_CHANGE = 0, 770d9e8c0bSMaciej S. Szmigiero 780d9e8c0bSMaciej S. Szmigiero S_WAIT_RESET, 790d9e8c0bSMaciej S. Szmigiero S_POST_RESET_CLOSED, 800d9e8c0bSMaciej S. Szmigiero 810d9e8c0bSMaciej S. Szmigiero /* init flow */ 820d9e8c0bSMaciej S. Szmigiero S_VERSION, 830d9e8c0bSMaciej S. Szmigiero S_CAPS, 840d9e8c0bSMaciej S. Szmigiero S_POST_INIT_WAIT, 850d9e8c0bSMaciej S. Szmigiero 860d9e8c0bSMaciej S. Szmigiero S_IDLE, 870d9e8c0bSMaciej S. Szmigiero 880d9e8c0bSMaciej S. Szmigiero /* balloon op flow */ 890d9e8c0bSMaciej S. Szmigiero S_BALLOON_POSTING, 900d9e8c0bSMaciej S. Szmigiero S_BALLOON_RB_WAIT, 910d9e8c0bSMaciej S. Szmigiero S_BALLOON_REPLY_WAIT, 920d9e8c0bSMaciej S. Szmigiero 930d9e8c0bSMaciej S. Szmigiero /* unballoon + hot add ops flow */ 940d9e8c0bSMaciej S. Szmigiero S_UNBALLOON_POSTING, 950d9e8c0bSMaciej S. Szmigiero S_UNBALLOON_RB_WAIT, 960d9e8c0bSMaciej S. Szmigiero S_UNBALLOON_REPLY_WAIT, 9799a4706aSMaciej S. Szmigiero S_HOT_ADD_SETUP, 9899a4706aSMaciej S. Szmigiero S_HOT_ADD_RB_WAIT, 9999a4706aSMaciej S. Szmigiero S_HOT_ADD_POSTING, 10099a4706aSMaciej S. Szmigiero S_HOT_ADD_REPLY_WAIT, 1010d9e8c0bSMaciej S. Szmigiero } State; 1020d9e8c0bSMaciej S. Szmigiero 1030d9e8c0bSMaciej S. Szmigiero typedef struct StateDesc { 1040d9e8c0bSMaciej S. Szmigiero State state; 1050d9e8c0bSMaciej S. Szmigiero const char *desc; 1060d9e8c0bSMaciej S. Szmigiero } StateDesc; 1070d9e8c0bSMaciej S. Szmigiero 1080d9e8c0bSMaciej S. Szmigiero typedef struct HvBalloon { 1090d9e8c0bSMaciej S. Szmigiero VMBusDevice parent; 1100d9e8c0bSMaciej S. Szmigiero State state; 1110d9e8c0bSMaciej S. Szmigiero 1120d9e8c0bSMaciej S. Szmigiero union dm_version version; 1130d9e8c0bSMaciej S. Szmigiero union dm_caps caps; 1140d9e8c0bSMaciej S. Szmigiero 1150d9e8c0bSMaciej S. Szmigiero QEMUTimer post_init_timer; 1160d9e8c0bSMaciej S. Szmigiero 1170d9e8c0bSMaciej S. Szmigiero unsigned int trans_id; 1180d9e8c0bSMaciej S. Szmigiero 1190d9e8c0bSMaciej S. Szmigiero struct { 1200d9e8c0bSMaciej S. Szmigiero bool enabled; 1210d9e8c0bSMaciej S. Szmigiero bool received; 1220d9e8c0bSMaciej S. Szmigiero uint64_t committed; 1230d9e8c0bSMaciej S. Szmigiero uint64_t available; 1240d9e8c0bSMaciej S. Szmigiero } status_report; 1250d9e8c0bSMaciej S. Szmigiero 1260d9e8c0bSMaciej S. Szmigiero /* Guest target size */ 1270d9e8c0bSMaciej S. Szmigiero uint64_t target; 1280d9e8c0bSMaciej S. Szmigiero bool target_changed; 1290d9e8c0bSMaciej S. Szmigiero 13099a4706aSMaciej S. Szmigiero /* Current (un)balloon / hot-add operation parameters */ 1310d9e8c0bSMaciej S. Szmigiero union { 1320d9e8c0bSMaciej S. Szmigiero uint64_t balloon_diff; 1330d9e8c0bSMaciej S. Szmigiero 1340d9e8c0bSMaciej S. Szmigiero struct { 1350d9e8c0bSMaciej S. Szmigiero uint64_t unballoon_diff; 13699a4706aSMaciej S. Szmigiero uint64_t hot_add_diff; 13799a4706aSMaciej S. Szmigiero }; 13899a4706aSMaciej S. Szmigiero 13999a4706aSMaciej S. Szmigiero struct { 14099a4706aSMaciej S. Szmigiero PageRange hot_add_range; 14199a4706aSMaciej S. Szmigiero uint64_t ha_current_count; 1420d9e8c0bSMaciej S. Szmigiero }; 1430d9e8c0bSMaciej S. Szmigiero }; 1440d9e8c0bSMaciej S. Szmigiero 14599a4706aSMaciej S. Szmigiero OurRangeMemslots *our_range; 14699a4706aSMaciej S. Szmigiero 14799a4706aSMaciej S. Szmigiero /* Count of memslots covering our memory */ 14899a4706aSMaciej S. Szmigiero unsigned int memslot_count; 14999a4706aSMaciej S. Szmigiero 1500d9e8c0bSMaciej S. Szmigiero /* Nominal size of each memslot (the last one might be smaller) */ 1510d9e8c0bSMaciej S. Szmigiero uint64_t memslot_size; 1520d9e8c0bSMaciej S. Szmigiero 15399a4706aSMaciej S. Szmigiero /* Non-ours removed memory */ 1540d9e8c0bSMaciej S. Szmigiero PageRangeTree removed_guest, removed_both; 1550d9e8c0bSMaciej S. Szmigiero 15699a4706aSMaciej S. Szmigiero /* Grand totals of removed memory (both ours and non-ours) */ 1570d9e8c0bSMaciej S. Szmigiero uint64_t removed_guest_ctr, removed_both_ctr; 15899a4706aSMaciej S. Szmigiero 15999a4706aSMaciej S. Szmigiero /* MEMORY_DEVICE props */ 16099a4706aSMaciej S. Szmigiero uint64_t addr; 16199a4706aSMaciej S. Szmigiero HostMemoryBackend *hostmem; 16299a4706aSMaciej S. Szmigiero MemoryRegion *mr; 1630d9e8c0bSMaciej S. Szmigiero } HvBalloon; 1640d9e8c0bSMaciej S. Szmigiero 1650d9e8c0bSMaciej S. Szmigiero OBJECT_DEFINE_TYPE_WITH_INTERFACES(HvBalloon, hv_balloon, HV_BALLOON, VMBUS_DEVICE, \ 16699a4706aSMaciej S. Szmigiero { TYPE_MEMORY_DEVICE }, { }) 1670d9e8c0bSMaciej S. Szmigiero 1680d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_SET_STATE(hvb, news) \ 1690d9e8c0bSMaciej S. Szmigiero do { \ 1700d9e8c0bSMaciej S. Szmigiero assert(news != S_NO_CHANGE); \ 1710d9e8c0bSMaciej S. Szmigiero hv_balloon_state_set(hvb, news, # news); \ 1720d9e8c0bSMaciej S. Szmigiero } while (0) 1730d9e8c0bSMaciej S. Szmigiero 1740d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_STATE_DESC_SET(stdesc, news) \ 1750d9e8c0bSMaciej S. Szmigiero _hv_balloon_state_desc_set(stdesc, news, # news) 1760d9e8c0bSMaciej S. Szmigiero 1770d9e8c0bSMaciej S. Szmigiero #define HV_BALLOON_STATE_DESC_INIT \ 1780d9e8c0bSMaciej S. Szmigiero { \ 1790d9e8c0bSMaciej S. Szmigiero .state = S_NO_CHANGE, \ 1800d9e8c0bSMaciej S. Szmigiero } 1810d9e8c0bSMaciej S. Szmigiero 1820d9e8c0bSMaciej S. Szmigiero typedef struct HvBalloonReq { 1830d9e8c0bSMaciej S. Szmigiero VMBusChanReq vmreq; 1840d9e8c0bSMaciej S. Szmigiero } HvBalloonReq; 1850d9e8c0bSMaciej S. Szmigiero 18699a4706aSMaciej S. Szmigiero /* total our memory includes parts currently removed from the guest */ 18799a4706aSMaciej S. Szmigiero static uint64_t hv_balloon_total_our_ram(HvBalloon *balloon) 18899a4706aSMaciej S. Szmigiero { 18999a4706aSMaciej S. Szmigiero if (!balloon->our_range) { 19099a4706aSMaciej S. Szmigiero return 0; 19199a4706aSMaciej S. Szmigiero } 19299a4706aSMaciej S. Szmigiero 19399a4706aSMaciej S. Szmigiero return balloon->our_range->range.added; 19499a4706aSMaciej S. Szmigiero } 19599a4706aSMaciej S. Szmigiero 1960d9e8c0bSMaciej S. Szmigiero /* TODO: unify the code below with virtio-balloon and cache the value */ 1970d9e8c0bSMaciej S. Szmigiero static int build_dimm_list(Object *obj, void *opaque) 1980d9e8c0bSMaciej S. Szmigiero { 1990d9e8c0bSMaciej S. Szmigiero GSList **list = opaque; 2000d9e8c0bSMaciej S. Szmigiero 2010d9e8c0bSMaciej S. Szmigiero if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { 2020d9e8c0bSMaciej S. Szmigiero DeviceState *dev = DEVICE(obj); 2030d9e8c0bSMaciej S. Szmigiero if (dev->realized) { /* only realized DIMMs matter */ 2040d9e8c0bSMaciej S. Szmigiero *list = g_slist_prepend(*list, dev); 2050d9e8c0bSMaciej S. Szmigiero } 2060d9e8c0bSMaciej S. Szmigiero } 2070d9e8c0bSMaciej S. Szmigiero 2080d9e8c0bSMaciej S. Szmigiero object_child_foreach(obj, build_dimm_list, opaque); 2090d9e8c0bSMaciej S. Szmigiero return 0; 2100d9e8c0bSMaciej S. Szmigiero } 2110d9e8c0bSMaciej S. Szmigiero 2120d9e8c0bSMaciej S. Szmigiero static ram_addr_t get_current_ram_size(void) 2130d9e8c0bSMaciej S. Szmigiero { 2140d9e8c0bSMaciej S. Szmigiero GSList *list = NULL, *item; 2150d9e8c0bSMaciej S. Szmigiero ram_addr_t size = current_machine->ram_size; 2160d9e8c0bSMaciej S. Szmigiero 2170d9e8c0bSMaciej S. Szmigiero build_dimm_list(qdev_get_machine(), &list); 2180d9e8c0bSMaciej S. Szmigiero for (item = list; item; item = g_slist_next(item)) { 2190d9e8c0bSMaciej S. Szmigiero Object *obj = OBJECT(item->data); 2200d9e8c0bSMaciej S. Szmigiero if (!strcmp(object_get_typename(obj), TYPE_PC_DIMM)) 2210d9e8c0bSMaciej S. Szmigiero size += object_property_get_int(obj, PC_DIMM_SIZE_PROP, 2220d9e8c0bSMaciej S. Szmigiero &error_abort); 2230d9e8c0bSMaciej S. Szmigiero } 2240d9e8c0bSMaciej S. Szmigiero g_slist_free(list); 2250d9e8c0bSMaciej S. Szmigiero 2260d9e8c0bSMaciej S. Szmigiero return size; 2270d9e8c0bSMaciej S. Szmigiero } 2280d9e8c0bSMaciej S. Szmigiero 2290d9e8c0bSMaciej S. Szmigiero /* total RAM includes memory currently removed from the guest */ 2300d9e8c0bSMaciej S. Szmigiero static uint64_t hv_balloon_total_ram(HvBalloon *balloon) 2310d9e8c0bSMaciej S. Szmigiero { 2320d9e8c0bSMaciej S. Szmigiero ram_addr_t ram_size = get_current_ram_size(); 2330d9e8c0bSMaciej S. Szmigiero uint64_t ram_size_pages = ram_size >> HV_BALLOON_PFN_SHIFT; 23499a4706aSMaciej S. Szmigiero uint64_t our_ram_size_pages = hv_balloon_total_our_ram(balloon); 2350d9e8c0bSMaciej S. Szmigiero 2360d9e8c0bSMaciej S. Szmigiero assert(ram_size_pages > 0); 2370d9e8c0bSMaciej S. Szmigiero 23899a4706aSMaciej S. Szmigiero return SUM_SATURATE_U64(ram_size_pages, our_ram_size_pages); 2390d9e8c0bSMaciej S. Szmigiero } 2400d9e8c0bSMaciej S. Szmigiero 2410d9e8c0bSMaciej S. Szmigiero /* 2420d9e8c0bSMaciej S. Szmigiero * calculating the total RAM size is a slow operation, 2430d9e8c0bSMaciej S. Szmigiero * avoid it as much as possible 2440d9e8c0bSMaciej S. Szmigiero */ 2450d9e8c0bSMaciej S. Szmigiero static uint64_t hv_balloon_total_removed_rs(HvBalloon *balloon, 2460d9e8c0bSMaciej S. Szmigiero uint64_t ram_size_pages) 2470d9e8c0bSMaciej S. Szmigiero { 2480d9e8c0bSMaciej S. Szmigiero uint64_t total_removed; 2490d9e8c0bSMaciej S. Szmigiero 2500d9e8c0bSMaciej S. Szmigiero total_removed = SUM_SATURATE_U64(balloon->removed_guest_ctr, 2510d9e8c0bSMaciej S. Szmigiero balloon->removed_both_ctr); 2520d9e8c0bSMaciej S. Szmigiero 2530d9e8c0bSMaciej S. Szmigiero /* possible if guest returns pages outside actual RAM */ 2540d9e8c0bSMaciej S. Szmigiero if (total_removed > ram_size_pages) { 2550d9e8c0bSMaciej S. Szmigiero total_removed = ram_size_pages; 2560d9e8c0bSMaciej S. Szmigiero } 2570d9e8c0bSMaciej S. Szmigiero 2580d9e8c0bSMaciej S. Szmigiero return total_removed; 2590d9e8c0bSMaciej S. Szmigiero } 2600d9e8c0bSMaciej S. Szmigiero 2610d9e8c0bSMaciej S. Szmigiero /* Returns whether the state has actually changed */ 2620d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_state_set(HvBalloon *balloon, 2630d9e8c0bSMaciej S. Szmigiero State newst, const char *newststr) 2640d9e8c0bSMaciej S. Szmigiero { 2650d9e8c0bSMaciej S. Szmigiero if (newst == S_NO_CHANGE || balloon->state == newst) { 2660d9e8c0bSMaciej S. Szmigiero return false; 2670d9e8c0bSMaciej S. Szmigiero } 2680d9e8c0bSMaciej S. Szmigiero 2690d9e8c0bSMaciej S. Szmigiero balloon->state = newst; 2700d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_state_change(newststr); 2710d9e8c0bSMaciej S. Szmigiero return true; 2720d9e8c0bSMaciej S. Szmigiero } 2730d9e8c0bSMaciej S. Szmigiero 2740d9e8c0bSMaciej S. Szmigiero static void _hv_balloon_state_desc_set(StateDesc *stdesc, 2750d9e8c0bSMaciej S. Szmigiero State newst, const char *newststr) 2760d9e8c0bSMaciej S. Szmigiero { 2770d9e8c0bSMaciej S. Szmigiero /* state setting is only permitted on a freshly init desc */ 2780d9e8c0bSMaciej S. Szmigiero assert(stdesc->state == S_NO_CHANGE); 2790d9e8c0bSMaciej S. Szmigiero 2800d9e8c0bSMaciej S. Szmigiero assert(newst != S_NO_CHANGE); 2810d9e8c0bSMaciej S. Szmigiero 2820d9e8c0bSMaciej S. Szmigiero stdesc->state = newst; 2830d9e8c0bSMaciej S. Szmigiero stdesc->desc = newststr; 2840d9e8c0bSMaciej S. Szmigiero } 2850d9e8c0bSMaciej S. Szmigiero 2860d9e8c0bSMaciej S. Szmigiero static VMBusChannel *hv_balloon_get_channel_maybe(HvBalloon *balloon) 2870d9e8c0bSMaciej S. Szmigiero { 2880d9e8c0bSMaciej S. Szmigiero return vmbus_device_channel(&balloon->parent, 0); 2890d9e8c0bSMaciej S. Szmigiero } 2900d9e8c0bSMaciej S. Szmigiero 2910d9e8c0bSMaciej S. Szmigiero static VMBusChannel *hv_balloon_get_channel(HvBalloon *balloon) 2920d9e8c0bSMaciej S. Szmigiero { 2930d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan; 2940d9e8c0bSMaciej S. Szmigiero 2950d9e8c0bSMaciej S. Szmigiero chan = hv_balloon_get_channel_maybe(balloon); 2960d9e8c0bSMaciej S. Szmigiero assert(chan != NULL); 2970d9e8c0bSMaciej S. Szmigiero return chan; 2980d9e8c0bSMaciej S. Szmigiero } 2990d9e8c0bSMaciej S. Szmigiero 3000d9e8c0bSMaciej S. Szmigiero static ssize_t hv_balloon_send_packet(VMBusChannel *chan, 3010d9e8c0bSMaciej S. Szmigiero struct dm_message *msg) 3020d9e8c0bSMaciej S. Szmigiero { 3030d9e8c0bSMaciej S. Szmigiero int ret; 3040d9e8c0bSMaciej S. Szmigiero 3050d9e8c0bSMaciej S. Szmigiero ret = vmbus_channel_reserve(chan, 0, msg->hdr.size); 3060d9e8c0bSMaciej S. Szmigiero if (ret < 0) { 3070d9e8c0bSMaciej S. Szmigiero return ret; 3080d9e8c0bSMaciej S. Szmigiero } 3090d9e8c0bSMaciej S. Szmigiero 3100d9e8c0bSMaciej S. Szmigiero return vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, 3110d9e8c0bSMaciej S. Szmigiero NULL, 0, msg, msg->hdr.size, false, 3120d9e8c0bSMaciej S. Szmigiero msg->hdr.trans_id); 3130d9e8c0bSMaciej S. Szmigiero } 3140d9e8c0bSMaciej S. Szmigiero 3150d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_unballoon_get_source(HvBalloon *balloon, 3160d9e8c0bSMaciej S. Szmigiero PageRangeTree *dtree, 31799a4706aSMaciej S. Szmigiero uint64_t **dctr, 31899a4706aSMaciej S. Szmigiero bool *is_our_range) 3190d9e8c0bSMaciej S. Szmigiero { 32099a4706aSMaciej S. Szmigiero OurRange *our_range = OUR_RANGE(balloon->our_range); 32199a4706aSMaciej S. Szmigiero 32299a4706aSMaciej S. Szmigiero /* Try the boot memory first */ 3230d9e8c0bSMaciej S. Szmigiero if (g_tree_nnodes(balloon->removed_guest.t) > 0) { 3240d9e8c0bSMaciej S. Szmigiero *dtree = balloon->removed_guest; 3250d9e8c0bSMaciej S. Szmigiero *dctr = &balloon->removed_guest_ctr; 32699a4706aSMaciej S. Szmigiero *is_our_range = false; 3270d9e8c0bSMaciej S. Szmigiero } else if (g_tree_nnodes(balloon->removed_both.t) > 0) { 3280d9e8c0bSMaciej S. Szmigiero *dtree = balloon->removed_both; 3290d9e8c0bSMaciej S. Szmigiero *dctr = &balloon->removed_both_ctr; 33099a4706aSMaciej S. Szmigiero *is_our_range = false; 33199a4706aSMaciej S. Szmigiero } else if (!our_range) { 33299a4706aSMaciej S. Szmigiero return false; 33399a4706aSMaciej S. Szmigiero } else if (!our_range_is_removed_tree_empty(our_range, false)) { 33499a4706aSMaciej S. Szmigiero *dtree = our_range_get_removed_tree(our_range, false); 33599a4706aSMaciej S. Szmigiero *dctr = &balloon->removed_guest_ctr; 33699a4706aSMaciej S. Szmigiero *is_our_range = true; 33799a4706aSMaciej S. Szmigiero } else if (!our_range_is_removed_tree_empty(our_range, true)) { 33899a4706aSMaciej S. Szmigiero *dtree = our_range_get_removed_tree(our_range, true); 33999a4706aSMaciej S. Szmigiero *dctr = &balloon->removed_both_ctr; 34099a4706aSMaciej S. Szmigiero *is_our_range = true; 3410d9e8c0bSMaciej S. Szmigiero } else { 3420d9e8c0bSMaciej S. Szmigiero return false; 3430d9e8c0bSMaciej S. Szmigiero } 3440d9e8c0bSMaciej S. Szmigiero 3450d9e8c0bSMaciej S. Szmigiero return true; 3460d9e8c0bSMaciej S. Szmigiero } 3470d9e8c0bSMaciej S. Szmigiero 3480d9e8c0bSMaciej S. Szmigiero static void hv_balloon_unballoon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) 3490d9e8c0bSMaciej S. Szmigiero { 3500d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 3510d9e8c0bSMaciej S. Szmigiero struct dm_unballoon_request *ur; 3520d9e8c0bSMaciej S. Szmigiero size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); 3530d9e8c0bSMaciej S. Szmigiero 3540d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_UNBALLOON_RB_WAIT); 3550d9e8c0bSMaciej S. Szmigiero 3560d9e8c0bSMaciej S. Szmigiero if (vmbus_channel_reserve(chan, 0, ur_size) < 0) { 3570d9e8c0bSMaciej S. Szmigiero return; 3580d9e8c0bSMaciej S. Szmigiero } 3590d9e8c0bSMaciej S. Szmigiero 3600d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_POSTING); 3610d9e8c0bSMaciej S. Szmigiero } 3620d9e8c0bSMaciej S. Szmigiero 3630d9e8c0bSMaciej S. Szmigiero static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc) 3640d9e8c0bSMaciej S. Szmigiero { 3650d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 3660d9e8c0bSMaciej S. Szmigiero PageRangeTree dtree; 3670d9e8c0bSMaciej S. Szmigiero uint64_t *dctr; 36899a4706aSMaciej S. Szmigiero bool our_range; 3691d3b82eaSMaciej S. Szmigiero g_autofree struct dm_unballoon_request *ur = NULL; 3700d9e8c0bSMaciej S. Szmigiero size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); 3710d9e8c0bSMaciej S. Szmigiero PageRange range; 3720d9e8c0bSMaciej S. Szmigiero bool bret; 3730d9e8c0bSMaciej S. Szmigiero ssize_t ret; 3740d9e8c0bSMaciej S. Szmigiero 3750d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_UNBALLOON_POSTING); 3760d9e8c0bSMaciej S. Szmigiero assert(balloon->unballoon_diff > 0); 3770d9e8c0bSMaciej S. Szmigiero 37899a4706aSMaciej S. Szmigiero if (!hv_balloon_unballoon_get_source(balloon, &dtree, &dctr, &our_range)) { 3790d9e8c0bSMaciej S. Szmigiero error_report("trying to unballoon but nothing seems to be ballooned"); 3800d9e8c0bSMaciej S. Szmigiero /* 3810d9e8c0bSMaciej S. Szmigiero * there is little we can do as we might have already 3820d9e8c0bSMaciej S. Szmigiero * sent the guest a partial request we can't cancel 3830d9e8c0bSMaciej S. Szmigiero */ 3840d9e8c0bSMaciej S. Szmigiero return; 3850d9e8c0bSMaciej S. Szmigiero } 3860d9e8c0bSMaciej S. Szmigiero 38799a4706aSMaciej S. Szmigiero assert(balloon->our_range || !our_range); 3880d9e8c0bSMaciej S. Szmigiero assert(dtree.t); 3890d9e8c0bSMaciej S. Szmigiero assert(dctr); 3900d9e8c0bSMaciej S. Szmigiero 3911d3b82eaSMaciej S. Szmigiero ur = g_malloc0(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 42899a4706aSMaciej S. Szmigiero static bool hv_balloon_our_range_ensure(HvBalloon *balloon) 42999a4706aSMaciej S. Szmigiero { 43099a4706aSMaciej S. Szmigiero uint64_t align; 43199a4706aSMaciej S. Szmigiero MemoryRegion *hostmem_mr; 43299a4706aSMaciej S. Szmigiero g_autoptr(OurRangeMemslots) our_range_memslots = NULL; 43399a4706aSMaciej S. Szmigiero OurRange *our_range; 43499a4706aSMaciej S. Szmigiero 43599a4706aSMaciej S. Szmigiero if (balloon->our_range) { 43699a4706aSMaciej S. Szmigiero return true; 43799a4706aSMaciej S. Szmigiero } 43899a4706aSMaciej S. Szmigiero 43999a4706aSMaciej S. Szmigiero if (!balloon->hostmem) { 44099a4706aSMaciej S. Szmigiero return false; 44199a4706aSMaciej S. Szmigiero } 44299a4706aSMaciej S. Szmigiero 44399a4706aSMaciej S. Szmigiero align = (1 << balloon->caps.cap_bits.hot_add_alignment) * MiB; 44499a4706aSMaciej S. Szmigiero assert(QEMU_IS_ALIGNED(balloon->addr, align)); 44599a4706aSMaciej S. Szmigiero 44699a4706aSMaciej S. Szmigiero hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); 44799a4706aSMaciej S. Szmigiero 44899a4706aSMaciej S. Szmigiero our_range_memslots = hvb_our_range_memslots_new(balloon->addr, 44999a4706aSMaciej S. Szmigiero balloon->mr, hostmem_mr, 45099a4706aSMaciej S. Szmigiero OBJECT(balloon), 45199a4706aSMaciej S. Szmigiero balloon->memslot_count, 45299a4706aSMaciej S. Szmigiero balloon->memslot_size); 45399a4706aSMaciej S. Szmigiero our_range = OUR_RANGE(our_range_memslots); 45499a4706aSMaciej S. Szmigiero 45599a4706aSMaciej S. Szmigiero if (hvb_page_range_tree_intree_any(balloon->removed_guest, 45699a4706aSMaciej S. Szmigiero our_range->range.start, 45799a4706aSMaciej S. Szmigiero our_range->range.count) || 45899a4706aSMaciej S. Szmigiero hvb_page_range_tree_intree_any(balloon->removed_both, 45999a4706aSMaciej S. Szmigiero our_range->range.start, 46099a4706aSMaciej S. Szmigiero our_range->range.count)) { 46199a4706aSMaciej 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"); 46299a4706aSMaciej S. Szmigiero return false; 46399a4706aSMaciej S. Szmigiero } 46499a4706aSMaciej S. Szmigiero 46599a4706aSMaciej S. Szmigiero trace_hv_balloon_our_range_add(our_range->range.count, 46699a4706aSMaciej S. Szmigiero our_range->range.start); 46799a4706aSMaciej S. Szmigiero 46899a4706aSMaciej S. Szmigiero balloon->our_range = g_steal_pointer(&our_range_memslots); 46999a4706aSMaciej S. Szmigiero return true; 47099a4706aSMaciej S. Szmigiero } 47199a4706aSMaciej S. Szmigiero 47299a4706aSMaciej S. Szmigiero static void hv_balloon_hot_add_setup(HvBalloon *balloon, StateDesc *stdesc) 47399a4706aSMaciej S. Szmigiero { 47499a4706aSMaciej S. Szmigiero /* need to make copy since it is in union with hot_add_range */ 47599a4706aSMaciej S. Szmigiero uint64_t hot_add_diff = balloon->hot_add_diff; 47699a4706aSMaciej S. Szmigiero PageRange *hot_add_range = &balloon->hot_add_range; 47799a4706aSMaciej S. Szmigiero uint64_t align, our_range_remaining; 47899a4706aSMaciej S. Szmigiero OurRange *our_range; 47999a4706aSMaciej S. Szmigiero 48099a4706aSMaciej S. Szmigiero assert(balloon->state == S_HOT_ADD_SETUP); 48199a4706aSMaciej S. Szmigiero assert(hot_add_diff > 0); 48299a4706aSMaciej S. Szmigiero 48399a4706aSMaciej S. Szmigiero if (!hv_balloon_our_range_ensure(balloon)) { 48499a4706aSMaciej S. Szmigiero goto ret_idle; 48599a4706aSMaciej S. Szmigiero } 48699a4706aSMaciej S. Szmigiero 48799a4706aSMaciej S. Szmigiero our_range = OUR_RANGE(balloon->our_range); 48899a4706aSMaciej S. Szmigiero 48999a4706aSMaciej S. Szmigiero align = (1 << balloon->caps.cap_bits.hot_add_alignment) * 49099a4706aSMaciej S. Szmigiero (MiB / HV_BALLOON_PAGE_SIZE); 49199a4706aSMaciej S. Szmigiero 49299a4706aSMaciej S. Szmigiero /* Absolute GPA in pages */ 49399a4706aSMaciej S. Szmigiero hot_add_range->start = our_range_get_remaining_start(our_range); 49499a4706aSMaciej S. Szmigiero assert(QEMU_IS_ALIGNED(hot_add_range->start, align)); 49599a4706aSMaciej S. Szmigiero 49699a4706aSMaciej S. Szmigiero our_range_remaining = our_range_get_remaining_size(our_range); 49799a4706aSMaciej S. Szmigiero hot_add_range->count = MIN(our_range_remaining, hot_add_diff); 49899a4706aSMaciej S. Szmigiero hot_add_range->count = QEMU_ALIGN_DOWN(hot_add_range->count, align); 49999a4706aSMaciej S. Szmigiero if (hot_add_range->count == 0) { 50099a4706aSMaciej S. Szmigiero goto ret_idle; 50199a4706aSMaciej S. Szmigiero } 50299a4706aSMaciej S. Szmigiero 50399a4706aSMaciej S. Szmigiero hvb_our_range_memslots_ensure_mapped_additional(balloon->our_range, 50499a4706aSMaciej S. Szmigiero hot_add_range->count); 50599a4706aSMaciej S. Szmigiero 50699a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_RB_WAIT); 50799a4706aSMaciej S. Szmigiero return; 50899a4706aSMaciej S. Szmigiero 50999a4706aSMaciej S. Szmigiero ret_idle: 51099a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); 51199a4706aSMaciej S. Szmigiero } 51299a4706aSMaciej S. Szmigiero 51399a4706aSMaciej S. Szmigiero static void hv_balloon_hot_add_rb_wait(HvBalloon *balloon, StateDesc *stdesc) 51499a4706aSMaciej S. Szmigiero { 51599a4706aSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 51654698728SMaciej S. Szmigiero struct dm_hot_add_with_region *ha; 51754698728SMaciej S. Szmigiero size_t ha_size = sizeof(*ha); 51899a4706aSMaciej S. Szmigiero 51999a4706aSMaciej S. Szmigiero assert(balloon->state == S_HOT_ADD_RB_WAIT); 52099a4706aSMaciej S. Szmigiero 52199a4706aSMaciej S. Szmigiero if (vmbus_channel_reserve(chan, 0, ha_size) < 0) { 52299a4706aSMaciej S. Szmigiero return; 52399a4706aSMaciej S. Szmigiero } 52499a4706aSMaciej S. Szmigiero 52599a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_POSTING); 52699a4706aSMaciej S. Szmigiero } 52799a4706aSMaciej S. Szmigiero 52899a4706aSMaciej S. Szmigiero static void hv_balloon_hot_add_posting(HvBalloon *balloon, StateDesc *stdesc) 52999a4706aSMaciej S. Szmigiero { 53099a4706aSMaciej S. Szmigiero PageRange *hot_add_range = &balloon->hot_add_range; 53199a4706aSMaciej S. Szmigiero uint64_t *current_count = &balloon->ha_current_count; 53299a4706aSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 53354698728SMaciej S. Szmigiero g_autofree struct dm_hot_add_with_region *ha = NULL; 53454698728SMaciej S. Szmigiero size_t ha_size = sizeof(*ha); 53599a4706aSMaciej S. Szmigiero union dm_mem_page_range *ha_region; 53699a4706aSMaciej S. Szmigiero uint64_t align, chunk_max_size; 53799a4706aSMaciej S. Szmigiero ssize_t ret; 53899a4706aSMaciej S. Szmigiero 53999a4706aSMaciej S. Szmigiero assert(balloon->state == S_HOT_ADD_POSTING); 54099a4706aSMaciej S. Szmigiero assert(hot_add_range->count > 0); 54199a4706aSMaciej S. Szmigiero 54299a4706aSMaciej S. Szmigiero align = (1 << balloon->caps.cap_bits.hot_add_alignment) * 54399a4706aSMaciej S. Szmigiero (MiB / HV_BALLOON_PAGE_SIZE); 54499a4706aSMaciej S. Szmigiero if (align >= HV_BALLOON_HA_CHUNK_PAGES) { 54599a4706aSMaciej S. Szmigiero /* 54699a4706aSMaciej S. Szmigiero * If the required alignment is higher than the chunk size we let it 54799a4706aSMaciej S. Szmigiero * override that size. 54899a4706aSMaciej S. Szmigiero */ 54999a4706aSMaciej S. Szmigiero chunk_max_size = align; 55099a4706aSMaciej S. Szmigiero } else { 55199a4706aSMaciej S. Szmigiero chunk_max_size = QEMU_ALIGN_DOWN(HV_BALLOON_HA_CHUNK_PAGES, align); 55299a4706aSMaciej S. Szmigiero } 55399a4706aSMaciej S. Szmigiero 55499a4706aSMaciej S. Szmigiero /* 55599a4706aSMaciej S. Szmigiero * hot_add_range->count starts aligned in hv_balloon_hot_add_setup(), 55699a4706aSMaciej S. Szmigiero * then it is either reduced by subtracting aligned current_count or 55799a4706aSMaciej S. Szmigiero * further hot-adds are prevented by marking the whole remaining our range 55899a4706aSMaciej S. Szmigiero * as unusable in hv_balloon_handle_hot_add_response(). 55999a4706aSMaciej S. Szmigiero */ 56099a4706aSMaciej S. Szmigiero *current_count = MIN(hot_add_range->count, chunk_max_size); 56199a4706aSMaciej S. Szmigiero 5621d3b82eaSMaciej S. Szmigiero ha = g_malloc0(ha_size); 56354698728SMaciej S. Szmigiero ha_region = &ha->region; 56499a4706aSMaciej S. Szmigiero ha->hdr.type = DM_MEM_HOT_ADD_REQUEST; 56599a4706aSMaciej S. Szmigiero ha->hdr.size = ha_size; 56699a4706aSMaciej S. Szmigiero ha->hdr.trans_id = balloon->trans_id; 56799a4706aSMaciej S. Szmigiero 56899a4706aSMaciej S. Szmigiero ha->range.finfo.start_page = hot_add_range->start; 56999a4706aSMaciej S. Szmigiero ha->range.finfo.page_cnt = *current_count; 57099a4706aSMaciej S. Szmigiero ha_region->finfo.start_page = hot_add_range->start; 57199a4706aSMaciej S. Szmigiero ha_region->finfo.page_cnt = ha->range.finfo.page_cnt; 57299a4706aSMaciej S. Szmigiero 57399a4706aSMaciej S. Szmigiero trace_hv_balloon_outgoing_hot_add(ha->hdr.trans_id, 57499a4706aSMaciej S. Szmigiero *current_count, hot_add_range->start); 57599a4706aSMaciej S. Szmigiero 57699a4706aSMaciej S. Szmigiero ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, 57799a4706aSMaciej S. Szmigiero NULL, 0, ha, ha_size, false, 57899a4706aSMaciej S. Szmigiero ha->hdr.trans_id); 57999a4706aSMaciej S. Szmigiero if (ret <= 0) { 58099a4706aSMaciej S. Szmigiero error_report("error %zd when posting hot add msg, expect problems", 58199a4706aSMaciej S. Szmigiero ret); 58299a4706aSMaciej S. Szmigiero } 58399a4706aSMaciej S. Szmigiero 58499a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_REPLY_WAIT); 58599a4706aSMaciej S. Szmigiero } 58699a4706aSMaciej S. Szmigiero 5870d9e8c0bSMaciej S. Szmigiero static void hv_balloon_balloon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) 5880d9e8c0bSMaciej S. Szmigiero { 5890d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 5900d9e8c0bSMaciej S. Szmigiero size_t bl_size = sizeof(struct dm_balloon); 5910d9e8c0bSMaciej S. Szmigiero 5920d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_BALLOON_RB_WAIT); 5930d9e8c0bSMaciej S. Szmigiero 5940d9e8c0bSMaciej S. Szmigiero if (vmbus_channel_reserve(chan, 0, bl_size) < 0) { 5950d9e8c0bSMaciej S. Szmigiero return; 5960d9e8c0bSMaciej S. Szmigiero } 5970d9e8c0bSMaciej S. Szmigiero 5980d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_POSTING); 5990d9e8c0bSMaciej S. Szmigiero } 6000d9e8c0bSMaciej S. Szmigiero 6010d9e8c0bSMaciej S. Szmigiero static void hv_balloon_balloon_posting(HvBalloon *balloon, StateDesc *stdesc) 6020d9e8c0bSMaciej S. Szmigiero { 6030d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan = hv_balloon_get_channel(balloon); 6040d9e8c0bSMaciej S. Szmigiero struct dm_balloon bl; 6050d9e8c0bSMaciej S. Szmigiero size_t bl_size = sizeof(bl); 6060d9e8c0bSMaciej S. Szmigiero ssize_t ret; 6070d9e8c0bSMaciej S. Szmigiero 6080d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_BALLOON_POSTING); 6090d9e8c0bSMaciej S. Szmigiero assert(balloon->balloon_diff > 0); 6100d9e8c0bSMaciej S. Szmigiero 6110d9e8c0bSMaciej S. Szmigiero memset(&bl, 0, sizeof(bl)); 6120d9e8c0bSMaciej S. Szmigiero bl.hdr.type = DM_BALLOON_REQUEST; 6130d9e8c0bSMaciej S. Szmigiero bl.hdr.size = bl_size; 6140d9e8c0bSMaciej S. Szmigiero bl.hdr.trans_id = balloon->trans_id; 6150d9e8c0bSMaciej S. Szmigiero bl.num_pages = MIN(balloon->balloon_diff, HV_BALLOON_HR_CHUNK_PAGES); 6160d9e8c0bSMaciej S. Szmigiero 6170d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_outgoing_balloon(bl.hdr.trans_id, bl.num_pages, 6180d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff); 6190d9e8c0bSMaciej S. Szmigiero 6200d9e8c0bSMaciej S. Szmigiero ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, 6210d9e8c0bSMaciej S. Szmigiero NULL, 0, &bl, bl_size, false, 6220d9e8c0bSMaciej S. Szmigiero bl.hdr.trans_id); 6230d9e8c0bSMaciej S. Szmigiero if (ret <= 0) { 6240d9e8c0bSMaciej S. Szmigiero error_report("error %zd when posting balloon msg, expect problems", 6250d9e8c0bSMaciej S. Szmigiero ret); 6260d9e8c0bSMaciej S. Szmigiero } 6270d9e8c0bSMaciej S. Szmigiero 6280d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_REPLY_WAIT); 6290d9e8c0bSMaciej S. Szmigiero } 6300d9e8c0bSMaciej S. Szmigiero 6310d9e8c0bSMaciej S. Szmigiero static void hv_balloon_idle_state_process_target(HvBalloon *balloon, 6320d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 6330d9e8c0bSMaciej S. Szmigiero { 6340d9e8c0bSMaciej S. Szmigiero bool can_balloon = balloon->caps.cap_bits.balloon; 6350d9e8c0bSMaciej S. Szmigiero uint64_t ram_size_pages, total_removed; 6360d9e8c0bSMaciej S. Szmigiero 6370d9e8c0bSMaciej S. Szmigiero ram_size_pages = hv_balloon_total_ram(balloon); 6380d9e8c0bSMaciej S. Szmigiero total_removed = hv_balloon_total_removed_rs(balloon, ram_size_pages); 6390d9e8c0bSMaciej S. Szmigiero 6400d9e8c0bSMaciej S. Szmigiero /* 6410d9e8c0bSMaciej S. Szmigiero * we need to cache the values computed from the balloon target value when 6420d9e8c0bSMaciej S. Szmigiero * starting the adjustment procedure in case someone changes the target when 6430d9e8c0bSMaciej S. Szmigiero * the procedure is in progress 6440d9e8c0bSMaciej S. Szmigiero */ 6450d9e8c0bSMaciej S. Szmigiero if (balloon->target > ram_size_pages - total_removed) { 64699a4706aSMaciej S. Szmigiero bool can_hot_add = balloon->caps.cap_bits.hot_add; 6470d9e8c0bSMaciej S. Szmigiero uint64_t target_diff = balloon->target - 6480d9e8c0bSMaciej S. Szmigiero (ram_size_pages - total_removed); 6490d9e8c0bSMaciej S. Szmigiero 6500d9e8c0bSMaciej S. Szmigiero balloon->unballoon_diff = MIN(target_diff, total_removed); 6510d9e8c0bSMaciej S. Szmigiero 65299a4706aSMaciej S. Szmigiero if (can_hot_add) { 65399a4706aSMaciej S. Szmigiero balloon->hot_add_diff = target_diff - balloon->unballoon_diff; 65499a4706aSMaciej S. Szmigiero } else { 65599a4706aSMaciej S. Szmigiero balloon->hot_add_diff = 0; 65699a4706aSMaciej S. Szmigiero } 65799a4706aSMaciej S. Szmigiero 6580d9e8c0bSMaciej S. Szmigiero if (balloon->unballoon_diff > 0) { 6590d9e8c0bSMaciej S. Szmigiero assert(can_balloon); 6600d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT); 66199a4706aSMaciej S. Szmigiero } else if (balloon->hot_add_diff > 0) { 66299a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_SETUP); 6630d9e8c0bSMaciej S. Szmigiero } 6640d9e8c0bSMaciej S. Szmigiero } else if (can_balloon && 6650d9e8c0bSMaciej S. Szmigiero balloon->target < ram_size_pages - total_removed) { 6660d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff = ram_size_pages - total_removed - 6670d9e8c0bSMaciej S. Szmigiero balloon->target; 6680d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT); 6690d9e8c0bSMaciej S. Szmigiero } 6700d9e8c0bSMaciej S. Szmigiero } 6710d9e8c0bSMaciej S. Szmigiero 6720d9e8c0bSMaciej S. Szmigiero static void hv_balloon_idle_state(HvBalloon *balloon, 6730d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 6740d9e8c0bSMaciej S. Szmigiero { 6750d9e8c0bSMaciej S. Szmigiero assert(balloon->state == S_IDLE); 6760d9e8c0bSMaciej S. Szmigiero 6770d9e8c0bSMaciej S. Szmigiero if (balloon->target_changed) { 6780d9e8c0bSMaciej S. Szmigiero balloon->target_changed = false; 6790d9e8c0bSMaciej S. Szmigiero hv_balloon_idle_state_process_target(balloon, stdesc); 6800d9e8c0bSMaciej S. Szmigiero return; 6810d9e8c0bSMaciej S. Szmigiero } 6820d9e8c0bSMaciej S. Szmigiero } 6830d9e8c0bSMaciej S. Szmigiero 6840d9e8c0bSMaciej S. Szmigiero static const struct { 6850d9e8c0bSMaciej S. Szmigiero void (*handler)(HvBalloon *balloon, StateDesc *stdesc); 6860d9e8c0bSMaciej S. Szmigiero } state_handlers[] = { 6870d9e8c0bSMaciej S. Szmigiero [S_IDLE].handler = hv_balloon_idle_state, 6880d9e8c0bSMaciej S. Szmigiero [S_BALLOON_POSTING].handler = hv_balloon_balloon_posting, 6890d9e8c0bSMaciej S. Szmigiero [S_BALLOON_RB_WAIT].handler = hv_balloon_balloon_rb_wait, 6900d9e8c0bSMaciej S. Szmigiero [S_UNBALLOON_POSTING].handler = hv_balloon_unballoon_posting, 6910d9e8c0bSMaciej S. Szmigiero [S_UNBALLOON_RB_WAIT].handler = hv_balloon_unballoon_rb_wait, 69299a4706aSMaciej S. Szmigiero [S_HOT_ADD_SETUP].handler = hv_balloon_hot_add_setup, 69399a4706aSMaciej S. Szmigiero [S_HOT_ADD_RB_WAIT].handler = hv_balloon_hot_add_rb_wait, 69499a4706aSMaciej S. Szmigiero [S_HOT_ADD_POSTING].handler = hv_balloon_hot_add_posting, 6950d9e8c0bSMaciej S. Szmigiero }; 6960d9e8c0bSMaciej S. Szmigiero 6970d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_state(HvBalloon *balloon, StateDesc *stdesc) 6980d9e8c0bSMaciej S. Szmigiero { 6990d9e8c0bSMaciej S. Szmigiero if (balloon->state >= ARRAY_SIZE(state_handlers) || 7000d9e8c0bSMaciej S. Szmigiero !state_handlers[balloon->state].handler) { 7010d9e8c0bSMaciej S. Szmigiero return; 7020d9e8c0bSMaciej S. Szmigiero } 7030d9e8c0bSMaciej S. Szmigiero 7040d9e8c0bSMaciej S. Szmigiero state_handlers[balloon->state].handler(balloon, stdesc); 7050d9e8c0bSMaciej S. Szmigiero } 7060d9e8c0bSMaciej S. Szmigiero 7070d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_insert_range(PageRangeTree tree, 7080d9e8c0bSMaciej S. Szmigiero const PageRange *range, 7090d9e8c0bSMaciej S. Szmigiero uint64_t *ctr1, 7100d9e8c0bSMaciej S. Szmigiero uint64_t *ctr2, 7110d9e8c0bSMaciej S. Szmigiero uint64_t *ctr3) 7120d9e8c0bSMaciej S. Szmigiero { 7130d9e8c0bSMaciej S. Szmigiero uint64_t dupcount, effcount; 7140d9e8c0bSMaciej S. Szmigiero 7150d9e8c0bSMaciej S. Szmigiero if (range->count == 0) { 7160d9e8c0bSMaciej S. Szmigiero return; 7170d9e8c0bSMaciej S. Szmigiero } 7180d9e8c0bSMaciej S. Szmigiero 7190d9e8c0bSMaciej S. Szmigiero dupcount = 0; 7200d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_insert(tree, range->start, range->count, &dupcount); 7210d9e8c0bSMaciej S. Szmigiero 7220d9e8c0bSMaciej S. Szmigiero assert(dupcount <= range->count); 7230d9e8c0bSMaciej S. Szmigiero effcount = range->count - dupcount; 7240d9e8c0bSMaciej S. Szmigiero 7250d9e8c0bSMaciej S. Szmigiero *ctr1 += effcount; 7260d9e8c0bSMaciej S. Szmigiero *ctr2 += effcount; 7270d9e8c0bSMaciej S. Szmigiero if (ctr3) { 7280d9e8c0bSMaciej S. Szmigiero *ctr3 += effcount; 7290d9e8c0bSMaciej S. Szmigiero } 7300d9e8c0bSMaciej S. Szmigiero } 7310d9e8c0bSMaciej S. Szmigiero 7320d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_handle_range(HvBalloon *balloon, 7330d9e8c0bSMaciej S. Szmigiero PageRange *range, 7340d9e8c0bSMaciej S. Szmigiero bool both, 7350d9e8c0bSMaciej S. Szmigiero uint64_t *removedctr) 7360d9e8c0bSMaciej S. Szmigiero { 73799a4706aSMaciej S. Szmigiero OurRange *our_range = OUR_RANGE(balloon->our_range); 7380d9e8c0bSMaciej S. Szmigiero PageRangeTree globaltree = 7390d9e8c0bSMaciej S. Szmigiero both ? balloon->removed_both : balloon->removed_guest; 7400d9e8c0bSMaciej S. Szmigiero uint64_t *globalctr = 7410d9e8c0bSMaciej S. Szmigiero both ? &balloon->removed_both_ctr : &balloon->removed_guest_ctr; 74299a4706aSMaciej S. Szmigiero PageRange rangeeff; 74399a4706aSMaciej S. Szmigiero 74499a4706aSMaciej S. Szmigiero if (range->count == 0) { 74599a4706aSMaciej S. Szmigiero return; 74699a4706aSMaciej S. Szmigiero } 7470d9e8c0bSMaciej S. Szmigiero 7480d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_remove_response(range->count, range->start, both); 7490d9e8c0bSMaciej S. Szmigiero 75099a4706aSMaciej S. Szmigiero if (our_range) { 75199a4706aSMaciej S. Szmigiero /* Includes the not-yet-hot-added and unusable parts. */ 75299a4706aSMaciej S. Szmigiero rangeeff = our_range->range; 75399a4706aSMaciej S. Szmigiero } else { 75499a4706aSMaciej S. Szmigiero rangeeff.start = rangeeff.count = 0; 75599a4706aSMaciej S. Szmigiero } 75699a4706aSMaciej S. Szmigiero 75799a4706aSMaciej S. Szmigiero if (page_range_intersection_size(range, rangeeff.start, rangeeff.count) > 0) { 75899a4706aSMaciej S. Szmigiero PageRangeTree ourtree = our_range_get_removed_tree(our_range, both); 75999a4706aSMaciej S. Szmigiero PageRange rangehole, rangecommon; 76099a4706aSMaciej S. Szmigiero uint64_t ourremoved = 0; 76199a4706aSMaciej S. Szmigiero 76299a4706aSMaciej S. Szmigiero /* process the hole before our range, if it exists */ 76399a4706aSMaciej S. Szmigiero page_range_part_before(range, rangeeff.start, &rangehole); 76499a4706aSMaciej S. Szmigiero hv_balloon_remove_response_insert_range(globaltree, &rangehole, 76599a4706aSMaciej S. Szmigiero globalctr, removedctr, NULL); 76699a4706aSMaciej S. Szmigiero if (rangehole.count > 0) { 76799a4706aSMaciej S. Szmigiero trace_hv_balloon_remove_response_hole(rangehole.count, 76899a4706aSMaciej S. Szmigiero rangehole.start, 76999a4706aSMaciej S. Szmigiero range->count, range->start, 77099a4706aSMaciej S. Szmigiero rangeeff.start, both); 77199a4706aSMaciej S. Szmigiero } 77299a4706aSMaciej S. Szmigiero 77399a4706aSMaciej S. Szmigiero /* process our part */ 77499a4706aSMaciej S. Szmigiero page_range_intersect(range, rangeeff.start, rangeeff.count, 77599a4706aSMaciej S. Szmigiero &rangecommon); 77699a4706aSMaciej S. Szmigiero hv_balloon_remove_response_insert_range(ourtree, &rangecommon, 77799a4706aSMaciej S. Szmigiero globalctr, removedctr, 77899a4706aSMaciej S. Szmigiero &ourremoved); 77999a4706aSMaciej S. Szmigiero if (rangecommon.count > 0) { 78099a4706aSMaciej S. Szmigiero trace_hv_balloon_remove_response_common(rangecommon.count, 78199a4706aSMaciej S. Szmigiero rangecommon.start, 78299a4706aSMaciej S. Szmigiero range->count, range->start, 78399a4706aSMaciej S. Szmigiero rangeeff.count, 78499a4706aSMaciej S. Szmigiero rangeeff.start, ourremoved, 78599a4706aSMaciej S. Szmigiero both); 78699a4706aSMaciej S. Szmigiero } 78799a4706aSMaciej S. Szmigiero 78899a4706aSMaciej S. Szmigiero /* calculate what's left after our range */ 78999a4706aSMaciej S. Szmigiero rangecommon = *range; 79099a4706aSMaciej S. Szmigiero page_range_part_after(&rangecommon, rangeeff.start, rangeeff.count, 79199a4706aSMaciej S. Szmigiero range); 79299a4706aSMaciej S. Szmigiero } 79399a4706aSMaciej S. Szmigiero 79499a4706aSMaciej S. Szmigiero /* process the remainder of the range that lies after our range */ 7950d9e8c0bSMaciej S. Szmigiero if (range->count > 0) { 7960d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_insert_range(globaltree, range, 7970d9e8c0bSMaciej S. Szmigiero globalctr, removedctr, NULL); 7980d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_remove_response_remainder(range->count, range->start, 7990d9e8c0bSMaciej S. Szmigiero both); 8000d9e8c0bSMaciej S. Szmigiero range->count = 0; 8010d9e8c0bSMaciej S. Szmigiero } 8020d9e8c0bSMaciej S. Szmigiero } 8030d9e8c0bSMaciej S. Szmigiero 8040d9e8c0bSMaciej S. Szmigiero static void hv_balloon_remove_response_handle_pages(HvBalloon *balloon, 8050d9e8c0bSMaciej S. Szmigiero PageRange *range, 8060d9e8c0bSMaciej S. Szmigiero uint64_t start, 8070d9e8c0bSMaciej S. Szmigiero uint64_t count, 8080d9e8c0bSMaciej S. Szmigiero bool both, 8090d9e8c0bSMaciej S. Szmigiero uint64_t *removedctr) 8100d9e8c0bSMaciej S. Szmigiero { 8110d9e8c0bSMaciej S. Szmigiero assert(count > 0); 8120d9e8c0bSMaciej S. Szmigiero 8130d9e8c0bSMaciej S. Szmigiero /* 8140d9e8c0bSMaciej S. Szmigiero * if there is an existing range that the new range can't be joined to 8150d9e8c0bSMaciej S. Szmigiero * dump it into tree(s) 8160d9e8c0bSMaciej S. Szmigiero */ 8170d9e8c0bSMaciej S. Szmigiero if (range->count > 0 && !page_range_joinable(range, start, count)) { 8180d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_range(balloon, range, both, 8190d9e8c0bSMaciej S. Szmigiero removedctr); 8200d9e8c0bSMaciej S. Szmigiero } 8210d9e8c0bSMaciej S. Szmigiero 8220d9e8c0bSMaciej S. Szmigiero if (range->count == 0) { 8230d9e8c0bSMaciej S. Szmigiero range->start = start; 8240d9e8c0bSMaciej S. Szmigiero range->count = count; 8250d9e8c0bSMaciej S. Szmigiero } else if (page_range_joinable_left(range, start, count)) { 8260d9e8c0bSMaciej S. Szmigiero range->start = start; 8270d9e8c0bSMaciej S. Szmigiero range->count += count; 8280d9e8c0bSMaciej S. Szmigiero } else { /* page_range_joinable_right() */ 8290d9e8c0bSMaciej S. Szmigiero range->count += count; 8300d9e8c0bSMaciej S. Szmigiero } 8310d9e8c0bSMaciej S. Szmigiero } 8320d9e8c0bSMaciej S. Szmigiero 8330d9e8c0bSMaciej S. Szmigiero static gboolean hv_balloon_handle_remove_host_addr_node(gpointer key, 8340d9e8c0bSMaciej S. Szmigiero gpointer value, 8350d9e8c0bSMaciej S. Szmigiero gpointer data) 8360d9e8c0bSMaciej S. Szmigiero { 8370d9e8c0bSMaciej S. Szmigiero PageRange *range = value; 8380d9e8c0bSMaciej S. Szmigiero uint64_t pageoff; 8390d9e8c0bSMaciej S. Szmigiero 8400d9e8c0bSMaciej S. Szmigiero for (pageoff = 0; pageoff < range->count; ) { 8410d9e8c0bSMaciej S. Szmigiero uint64_t addr_64 = (range->start + pageoff) * HV_BALLOON_PAGE_SIZE; 8420d9e8c0bSMaciej S. Szmigiero void *addr; 8430d9e8c0bSMaciej S. Szmigiero RAMBlock *rb; 8440d9e8c0bSMaciej S. Szmigiero ram_addr_t rb_offset; 8450d9e8c0bSMaciej S. Szmigiero size_t rb_page_size; 8460d9e8c0bSMaciej S. Szmigiero size_t discard_size; 8470d9e8c0bSMaciej S. Szmigiero 8480d9e8c0bSMaciej S. Szmigiero assert(addr_64 <= UINTPTR_MAX); 8490d9e8c0bSMaciej S. Szmigiero addr = (void *)((uintptr_t)addr_64); 8500d9e8c0bSMaciej S. Szmigiero rb = qemu_ram_block_from_host(addr, false, &rb_offset); 8510d9e8c0bSMaciej S. Szmigiero rb_page_size = qemu_ram_pagesize(rb); 8520d9e8c0bSMaciej S. Szmigiero 8530d9e8c0bSMaciej S. Szmigiero if (rb_page_size != HV_BALLOON_PAGE_SIZE) { 8540d9e8c0bSMaciej S. Szmigiero /* TODO: these should end in "removed_guest" */ 8550d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page backed by unsupported page size %zu", 8560d9e8c0bSMaciej S. Szmigiero rb_page_size); 8570d9e8c0bSMaciej S. Szmigiero pageoff++; 8580d9e8c0bSMaciej S. Szmigiero continue; 8590d9e8c0bSMaciej S. Szmigiero } 8600d9e8c0bSMaciej S. Szmigiero 8610d9e8c0bSMaciej S. Szmigiero discard_size = MIN(range->count - pageoff, 8620d9e8c0bSMaciej S. Szmigiero (rb->max_length - rb_offset) / 8630d9e8c0bSMaciej S. Szmigiero HV_BALLOON_PAGE_SIZE); 8640d9e8c0bSMaciej S. Szmigiero discard_size = MAX(discard_size, 1); 8650d9e8c0bSMaciej S. Szmigiero 8660d9e8c0bSMaciej S. Szmigiero if (ram_block_discard_range(rb, rb_offset, discard_size * 8670d9e8c0bSMaciej S. Szmigiero HV_BALLOON_PAGE_SIZE) != 0) { 8680d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page failed discard"); 8690d9e8c0bSMaciej S. Szmigiero } 8700d9e8c0bSMaciej S. Szmigiero 8710d9e8c0bSMaciej S. Szmigiero pageoff += discard_size; 8720d9e8c0bSMaciej S. Szmigiero } 8730d9e8c0bSMaciej S. Szmigiero 8740d9e8c0bSMaciej S. Szmigiero return false; 8750d9e8c0bSMaciej S. Szmigiero } 8760d9e8c0bSMaciej S. Szmigiero 8770d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_remove_host_addr_tree(PageRangeTree tree) 8780d9e8c0bSMaciej S. Szmigiero { 8790d9e8c0bSMaciej S. Szmigiero g_tree_foreach(tree.t, hv_balloon_handle_remove_host_addr_node, NULL); 8800d9e8c0bSMaciej S. Szmigiero } 8810d9e8c0bSMaciej S. Szmigiero 8820d9e8c0bSMaciej S. Szmigiero static int hv_balloon_handle_remove_section(PageRangeTree tree, 8830d9e8c0bSMaciej S. Szmigiero const MemoryRegionSection *section, 8840d9e8c0bSMaciej S. Szmigiero uint64_t count) 8850d9e8c0bSMaciej S. Szmigiero { 8860d9e8c0bSMaciej S. Szmigiero void *addr = memory_region_get_ram_ptr(section->mr) + 8870d9e8c0bSMaciej S. Szmigiero section->offset_within_region; 8880d9e8c0bSMaciej S. Szmigiero uint64_t addr_page; 8890d9e8c0bSMaciej S. Szmigiero 8900d9e8c0bSMaciej S. Szmigiero assert(count > 0); 8910d9e8c0bSMaciej S. Szmigiero 8920d9e8c0bSMaciej S. Szmigiero if ((uintptr_t)addr % HV_BALLOON_PAGE_SIZE) { 8930d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed pages at an unaligned host addr %p", 8940d9e8c0bSMaciej S. Szmigiero addr); 8950d9e8c0bSMaciej S. Szmigiero return -EINVAL; 8960d9e8c0bSMaciej S. Szmigiero } 8970d9e8c0bSMaciej S. Szmigiero 8980d9e8c0bSMaciej S. Szmigiero addr_page = (uintptr_t)addr / HV_BALLOON_PAGE_SIZE; 8990d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_insert(tree, addr_page, count, NULL); 9000d9e8c0bSMaciej S. Szmigiero 9010d9e8c0bSMaciej S. Szmigiero return 0; 9020d9e8c0bSMaciej S. Szmigiero } 9030d9e8c0bSMaciej S. Szmigiero 9040d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_remove_ranges(HvBalloon *balloon, 9050d9e8c0bSMaciej S. Szmigiero union dm_mem_page_range ranges[], 9060d9e8c0bSMaciej S. Szmigiero uint32_t count) 9070d9e8c0bSMaciej S. Szmigiero { 9080d9e8c0bSMaciej S. Szmigiero uint64_t removedcnt; 9090d9e8c0bSMaciej S. Szmigiero PageRangeTree removed_host_addr; 9100d9e8c0bSMaciej S. Szmigiero PageRange range_guest, range_both; 9110d9e8c0bSMaciej S. Szmigiero 9120d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_init(&removed_host_addr); 9130d9e8c0bSMaciej S. Szmigiero range_guest.count = range_both.count = removedcnt = 0; 9140d9e8c0bSMaciej S. Szmigiero for (unsigned int ctr = 0; ctr < count; ctr++) { 9150d9e8c0bSMaciej S. Szmigiero union dm_mem_page_range *mr = &ranges[ctr]; 9160d9e8c0bSMaciej S. Szmigiero hwaddr pa; 9170d9e8c0bSMaciej S. Szmigiero MemoryRegionSection section; 9180d9e8c0bSMaciej S. Szmigiero 9190d9e8c0bSMaciej S. Szmigiero for (unsigned int offset = 0; offset < mr->finfo.page_cnt; ) { 9200d9e8c0bSMaciej S. Szmigiero int ret; 9210d9e8c0bSMaciej S. Szmigiero uint64_t pageno = mr->finfo.start_page + offset; 9220d9e8c0bSMaciej S. Szmigiero uint64_t pagecnt = 1; 9230d9e8c0bSMaciej S. Szmigiero 9240d9e8c0bSMaciej S. Szmigiero pa = (hwaddr)pageno << HV_BALLOON_PFN_SHIFT; 9250d9e8c0bSMaciej S. Szmigiero section = memory_region_find(get_system_memory(), pa, 9260d9e8c0bSMaciej S. Szmigiero (mr->finfo.page_cnt - offset) * 9270d9e8c0bSMaciej S. Szmigiero HV_BALLOON_PAGE_SIZE); 9280d9e8c0bSMaciej S. Szmigiero if (!section.mr) { 9290d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page %"PRIu64" not found in RAM", 9300d9e8c0bSMaciej S. Szmigiero pageno); 9310d9e8c0bSMaciej S. Szmigiero ret = -EINVAL; 9320d9e8c0bSMaciej S. Szmigiero goto finish_page; 9330d9e8c0bSMaciej S. Szmigiero } 9340d9e8c0bSMaciej S. Szmigiero 9350d9e8c0bSMaciej S. Szmigiero pagecnt = int128_get64(section.size) / HV_BALLOON_PAGE_SIZE; 9360d9e8c0bSMaciej S. Szmigiero if (pagecnt <= 0) { 9370d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page %"PRIu64" in a section smaller than page size", 9380d9e8c0bSMaciej S. Szmigiero pageno); 9390d9e8c0bSMaciej S. Szmigiero pagecnt = 1; /* skip the whole page */ 9400d9e8c0bSMaciej S. Szmigiero ret = -EINVAL; 9410d9e8c0bSMaciej S. Szmigiero goto finish_page; 9420d9e8c0bSMaciej S. Szmigiero } 9430d9e8c0bSMaciej S. Szmigiero 9440d9e8c0bSMaciej S. Szmigiero if (!memory_region_is_ram(section.mr) || 9450d9e8c0bSMaciej S. Szmigiero memory_region_is_rom(section.mr) || 9460d9e8c0bSMaciej S. Szmigiero memory_region_is_romd(section.mr)) { 9470d9e8c0bSMaciej S. Szmigiero warn_report("guest reported removed page %"PRIu64" in a section that is not an ordinary RAM", 9480d9e8c0bSMaciej S. Szmigiero pageno); 9490d9e8c0bSMaciej S. Szmigiero ret = -EINVAL; 9500d9e8c0bSMaciej S. Szmigiero goto finish_page; 9510d9e8c0bSMaciej S. Szmigiero } 9520d9e8c0bSMaciej S. Szmigiero 9530d9e8c0bSMaciej S. Szmigiero ret = hv_balloon_handle_remove_section(removed_host_addr, §ion, 9540d9e8c0bSMaciej S. Szmigiero pagecnt); 9550d9e8c0bSMaciej S. Szmigiero 9560d9e8c0bSMaciej S. Szmigiero finish_page: 9570d9e8c0bSMaciej S. Szmigiero if (ret == 0) { 9580d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_pages(balloon, 9590d9e8c0bSMaciej S. Szmigiero &range_both, 9600d9e8c0bSMaciej S. Szmigiero pageno, pagecnt, 9610d9e8c0bSMaciej S. Szmigiero true, &removedcnt); 9620d9e8c0bSMaciej S. Szmigiero } else { 9630d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_pages(balloon, 9640d9e8c0bSMaciej S. Szmigiero &range_guest, 9650d9e8c0bSMaciej S. Szmigiero pageno, pagecnt, 9660d9e8c0bSMaciej S. Szmigiero false, &removedcnt); 9670d9e8c0bSMaciej S. Szmigiero } 9680d9e8c0bSMaciej S. Szmigiero 9690d9e8c0bSMaciej S. Szmigiero if (section.mr) { 9700d9e8c0bSMaciej S. Szmigiero memory_region_unref(section.mr); 9710d9e8c0bSMaciej S. Szmigiero } 9720d9e8c0bSMaciej S. Szmigiero 9730d9e8c0bSMaciej S. Szmigiero offset += pagecnt; 9740d9e8c0bSMaciej S. Szmigiero } 9750d9e8c0bSMaciej S. Szmigiero } 9760d9e8c0bSMaciej S. Szmigiero 9770d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_range(balloon, &range_both, true, 9780d9e8c0bSMaciej S. Szmigiero &removedcnt); 9790d9e8c0bSMaciej S. Szmigiero hv_balloon_remove_response_handle_range(balloon, &range_guest, false, 9800d9e8c0bSMaciej S. Szmigiero &removedcnt); 9810d9e8c0bSMaciej S. Szmigiero 9820d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_remove_host_addr_tree(removed_host_addr); 9830d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&removed_host_addr); 9840d9e8c0bSMaciej S. Szmigiero 9850d9e8c0bSMaciej S. Szmigiero if (removedcnt > balloon->balloon_diff) { 9860d9e8c0bSMaciej S. Szmigiero warn_report("guest reported more pages removed than currently pending (%"PRIu64" vs %"PRIu64")", 9870d9e8c0bSMaciej S. Szmigiero removedcnt, balloon->balloon_diff); 9880d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff = 0; 9890d9e8c0bSMaciej S. Szmigiero } else { 9900d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff -= removedcnt; 9910d9e8c0bSMaciej S. Szmigiero } 9920d9e8c0bSMaciej S. Szmigiero } 9930d9e8c0bSMaciej S. Szmigiero 9940d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_handle_msg_size(HvBalloonReq *req, size_t minsize, 9950d9e8c0bSMaciej S. Szmigiero const char *msgname) 9960d9e8c0bSMaciej S. Szmigiero { 9970d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 9980d9e8c0bSMaciej S. Szmigiero uint32_t msglen = vmreq->msglen; 9990d9e8c0bSMaciej S. Szmigiero 10000d9e8c0bSMaciej S. Szmigiero if (msglen >= minsize) { 10010d9e8c0bSMaciej S. Szmigiero return true; 10020d9e8c0bSMaciej S. Szmigiero } 10030d9e8c0bSMaciej S. Szmigiero 10040d9e8c0bSMaciej S. Szmigiero warn_report("%s message too short (%u vs %zu), ignoring", msgname, 10050d9e8c0bSMaciej S. Szmigiero (unsigned int)msglen, minsize); 10060d9e8c0bSMaciej S. Szmigiero return false; 10070d9e8c0bSMaciej S. Szmigiero } 10080d9e8c0bSMaciej S. Szmigiero 10090d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_version_request(HvBalloon *balloon, 10100d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req, 10110d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 10120d9e8c0bSMaciej S. Szmigiero { 10130d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 10140d9e8c0bSMaciej S. Szmigiero struct dm_version_request *msgVr = vmreq->msg; 10150d9e8c0bSMaciej S. Szmigiero struct dm_version_response respVr; 10160d9e8c0bSMaciej S. Szmigiero 10170d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_VERSION) { 10180d9e8c0bSMaciej S. Szmigiero warn_report("unexpected DM_VERSION_REQUEST in %d state", 10190d9e8c0bSMaciej S. Szmigiero balloon->state); 10200d9e8c0bSMaciej S. Szmigiero return; 10210d9e8c0bSMaciej S. Szmigiero } 10220d9e8c0bSMaciej S. Szmigiero 10230d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgVr), 10240d9e8c0bSMaciej S. Szmigiero "DM_VERSION_REQUEST")) { 10250d9e8c0bSMaciej S. Szmigiero return; 10260d9e8c0bSMaciej S. Szmigiero } 10270d9e8c0bSMaciej S. Szmigiero 10280d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_incoming_version(msgVr->version.major_version, 10290d9e8c0bSMaciej S. Szmigiero msgVr->version.minor_version); 10300d9e8c0bSMaciej S. Szmigiero 10310d9e8c0bSMaciej S. Szmigiero memset(&respVr, 0, sizeof(respVr)); 10320d9e8c0bSMaciej S. Szmigiero respVr.hdr.type = DM_VERSION_RESPONSE; 10330d9e8c0bSMaciej S. Szmigiero respVr.hdr.size = sizeof(respVr); 10340d9e8c0bSMaciej S. Szmigiero respVr.hdr.trans_id = msgVr->hdr.trans_id; 10350d9e8c0bSMaciej S. Szmigiero respVr.is_accepted = msgVr->version.version >= DYNMEM_PROTOCOL_VERSION_1 && 10360d9e8c0bSMaciej S. Szmigiero msgVr->version.version <= DYNMEM_PROTOCOL_VERSION_3; 10370d9e8c0bSMaciej S. Szmigiero 10380d9e8c0bSMaciej S. Szmigiero hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respVr); 10390d9e8c0bSMaciej S. Szmigiero 10400d9e8c0bSMaciej S. Szmigiero if (respVr.is_accepted) { 10410d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_CAPS); 10420d9e8c0bSMaciej S. Szmigiero } 10430d9e8c0bSMaciej S. Szmigiero } 10440d9e8c0bSMaciej S. Szmigiero 10450d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_caps_report(HvBalloon *balloon, 10460d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req, 10470d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 10480d9e8c0bSMaciej S. Szmigiero { 10490d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 10500d9e8c0bSMaciej S. Szmigiero struct dm_capabilities *msgCap = vmreq->msg; 10510d9e8c0bSMaciej S. Szmigiero struct dm_capabilities_resp_msg respCap; 10520d9e8c0bSMaciej S. Szmigiero 10530d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_CAPS) { 10540d9e8c0bSMaciej S. Szmigiero warn_report("unexpected DM_CAPABILITIES_REPORT in %d state", 10550d9e8c0bSMaciej S. Szmigiero balloon->state); 10560d9e8c0bSMaciej S. Szmigiero return; 10570d9e8c0bSMaciej S. Szmigiero } 10580d9e8c0bSMaciej S. Szmigiero 10590d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgCap), 10600d9e8c0bSMaciej S. Szmigiero "DM_CAPABILITIES_REPORT")) { 10610d9e8c0bSMaciej S. Szmigiero return; 10620d9e8c0bSMaciej S. Szmigiero } 10630d9e8c0bSMaciej S. Szmigiero 10640d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_incoming_caps(msgCap->caps.caps); 10650d9e8c0bSMaciej S. Szmigiero balloon->caps = msgCap->caps; 10660d9e8c0bSMaciej S. Szmigiero 10670d9e8c0bSMaciej S. Szmigiero memset(&respCap, 0, sizeof(respCap)); 10680d9e8c0bSMaciej S. Szmigiero respCap.hdr.type = DM_CAPABILITIES_RESPONSE; 10690d9e8c0bSMaciej S. Szmigiero respCap.hdr.size = sizeof(respCap); 10700d9e8c0bSMaciej S. Szmigiero respCap.hdr.trans_id = msgCap->hdr.trans_id; 10710d9e8c0bSMaciej S. Szmigiero respCap.is_accepted = 1; 10720d9e8c0bSMaciej S. Szmigiero respCap.hot_remove = 1; 10730d9e8c0bSMaciej S. Szmigiero respCap.suppress_pressure_reports = !balloon->status_report.enabled; 10740d9e8c0bSMaciej S. Szmigiero hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respCap); 10750d9e8c0bSMaciej S. Szmigiero 10760d9e8c0bSMaciej S. Szmigiero timer_mod(&balloon->post_init_timer, 10770d9e8c0bSMaciej S. Szmigiero qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10780d9e8c0bSMaciej S. Szmigiero HV_BALLOON_POST_INIT_WAIT); 10790d9e8c0bSMaciej S. Szmigiero 10800d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_POST_INIT_WAIT); 10810d9e8c0bSMaciej S. Szmigiero } 10820d9e8c0bSMaciej S. Szmigiero 10830d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_status_report(HvBalloon *balloon, 10840d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req) 10850d9e8c0bSMaciej S. Szmigiero { 10860d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 10870d9e8c0bSMaciej S. Szmigiero struct dm_status *msgStatus = vmreq->msg; 10880d9e8c0bSMaciej S. Szmigiero 10890d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgStatus), 10900d9e8c0bSMaciej S. Szmigiero "DM_STATUS_REPORT")) { 10910d9e8c0bSMaciej S. Szmigiero return; 10920d9e8c0bSMaciej S. Szmigiero } 10930d9e8c0bSMaciej S. Szmigiero 10940d9e8c0bSMaciej S. Szmigiero if (!balloon->status_report.enabled) { 10950d9e8c0bSMaciej S. Szmigiero return; 10960d9e8c0bSMaciej S. Szmigiero } 10970d9e8c0bSMaciej S. Szmigiero 10980d9e8c0bSMaciej S. Szmigiero balloon->status_report.committed = msgStatus->num_committed; 10990d9e8c0bSMaciej S. Szmigiero balloon->status_report.committed *= HV_BALLOON_PAGE_SIZE; 11000d9e8c0bSMaciej S. Szmigiero balloon->status_report.available = msgStatus->num_avail; 11010d9e8c0bSMaciej S. Szmigiero balloon->status_report.available *= HV_BALLOON_PAGE_SIZE; 11020d9e8c0bSMaciej S. Szmigiero balloon->status_report.received = true; 11030d9e8c0bSMaciej S. Szmigiero 1104259ebed4SMaciej S. Szmigiero qapi_event_send_hv_balloon_status_report(balloon->status_report.committed, 1105259ebed4SMaciej S. Szmigiero balloon->status_report.available); 1106259ebed4SMaciej S. Szmigiero } 1107259ebed4SMaciej S. Szmigiero 1108259ebed4SMaciej S. Szmigiero HvBalloonInfo *qmp_query_hv_balloon_status_report(Error **errp) 1109259ebed4SMaciej S. Szmigiero { 1110259ebed4SMaciej S. Szmigiero HvBalloon *balloon; 1111259ebed4SMaciej S. Szmigiero HvBalloonInfo *info; 1112259ebed4SMaciej S. Szmigiero 1113259ebed4SMaciej S. Szmigiero balloon = HV_BALLOON(object_resolve_path_type("", TYPE_HV_BALLOON, NULL)); 1114259ebed4SMaciej S. Szmigiero if (!balloon) { 1115259ebed4SMaciej S. Szmigiero error_setg(errp, "no %s device present", TYPE_HV_BALLOON); 1116259ebed4SMaciej S. Szmigiero return NULL; 1117259ebed4SMaciej S. Szmigiero } 1118259ebed4SMaciej S. Szmigiero 1119259ebed4SMaciej S. Szmigiero if (!balloon->status_report.enabled) { 1120259ebed4SMaciej S. Szmigiero error_setg(errp, "guest memory status reporting not enabled"); 1121259ebed4SMaciej S. Szmigiero return NULL; 1122259ebed4SMaciej S. Szmigiero } 1123259ebed4SMaciej S. Szmigiero 1124259ebed4SMaciej S. Szmigiero if (!balloon->status_report.received) { 1125259ebed4SMaciej S. Szmigiero error_setg(errp, "no guest memory status report received yet"); 1126259ebed4SMaciej S. Szmigiero return NULL; 1127259ebed4SMaciej S. Szmigiero } 1128259ebed4SMaciej S. Szmigiero 1129259ebed4SMaciej S. Szmigiero info = g_malloc0(sizeof(*info)); 1130259ebed4SMaciej S. Szmigiero info->committed = balloon->status_report.committed; 1131259ebed4SMaciej S. Szmigiero info->available = balloon->status_report.available; 1132259ebed4SMaciej S. Szmigiero return info; 11330d9e8c0bSMaciej S. Szmigiero } 11340d9e8c0bSMaciej S. Szmigiero 11350d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_unballoon_response(HvBalloon *balloon, 11360d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req, 11370d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 11380d9e8c0bSMaciej S. Szmigiero { 11390d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 11400d9e8c0bSMaciej S. Szmigiero struct dm_unballoon_response *msgUrR = vmreq->msg; 11410d9e8c0bSMaciej S. Szmigiero 11420d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_UNBALLOON_REPLY_WAIT) { 11430d9e8c0bSMaciej S. Szmigiero warn_report("unexpected DM_UNBALLOON_RESPONSE in %d state", 11440d9e8c0bSMaciej S. Szmigiero balloon->state); 11450d9e8c0bSMaciej S. Szmigiero return; 11460d9e8c0bSMaciej S. Szmigiero } 11470d9e8c0bSMaciej S. Szmigiero 11480d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgUrR), 11490d9e8c0bSMaciej S. Szmigiero "DM_UNBALLOON_RESPONSE")) 11500d9e8c0bSMaciej S. Szmigiero return; 11510d9e8c0bSMaciej S. Szmigiero 11520d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_incoming_unballoon(msgUrR->hdr.trans_id); 11530d9e8c0bSMaciej S. Szmigiero 11540d9e8c0bSMaciej S. Szmigiero balloon->trans_id++; 11550d9e8c0bSMaciej S. Szmigiero 115699a4706aSMaciej S. Szmigiero if (balloon->hot_add_diff > 0) { 115799a4706aSMaciej S. Szmigiero bool can_hot_add = balloon->caps.cap_bits.hot_add; 115899a4706aSMaciej S. Szmigiero 115999a4706aSMaciej S. Szmigiero assert(can_hot_add); 116099a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_SETUP); 116199a4706aSMaciej S. Szmigiero } else { 116299a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); 116399a4706aSMaciej S. Szmigiero } 116499a4706aSMaciej S. Szmigiero } 116599a4706aSMaciej S. Szmigiero 116699a4706aSMaciej S. Szmigiero static void hv_balloon_handle_hot_add_response(HvBalloon *balloon, 116799a4706aSMaciej S. Szmigiero HvBalloonReq *req, 116899a4706aSMaciej S. Szmigiero StateDesc *stdesc) 116999a4706aSMaciej S. Szmigiero { 117099a4706aSMaciej S. Szmigiero PageRange *hot_add_range = &balloon->hot_add_range; 117199a4706aSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 117299a4706aSMaciej S. Szmigiero struct dm_hot_add_response *msgHaR = vmreq->msg; 117399a4706aSMaciej S. Szmigiero OurRange *our_range; 117499a4706aSMaciej S. Szmigiero 117599a4706aSMaciej S. Szmigiero if (balloon->state != S_HOT_ADD_REPLY_WAIT) { 117699a4706aSMaciej S. Szmigiero warn_report("unexpected DM_HOT_ADD_RESPONSE in %d state", 117799a4706aSMaciej S. Szmigiero balloon->state); 117899a4706aSMaciej S. Szmigiero return; 117999a4706aSMaciej S. Szmigiero } 118099a4706aSMaciej S. Szmigiero 118199a4706aSMaciej S. Szmigiero assert(balloon->our_range); 118299a4706aSMaciej S. Szmigiero our_range = OUR_RANGE(balloon->our_range); 118399a4706aSMaciej S. Szmigiero 118499a4706aSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgHaR), 118599a4706aSMaciej S. Szmigiero "DM_HOT_ADD_RESPONSE")) 118699a4706aSMaciej S. Szmigiero return; 118799a4706aSMaciej S. Szmigiero 118899a4706aSMaciej S. Szmigiero trace_hv_balloon_incoming_hot_add(msgHaR->hdr.trans_id, msgHaR->result, 118999a4706aSMaciej S. Szmigiero msgHaR->page_count); 119099a4706aSMaciej S. Szmigiero 119199a4706aSMaciej S. Szmigiero balloon->trans_id++; 119299a4706aSMaciej S. Szmigiero 119399a4706aSMaciej S. Szmigiero if (msgHaR->result) { 119499a4706aSMaciej S. Szmigiero if (msgHaR->page_count > balloon->ha_current_count) { 119599a4706aSMaciej S. Szmigiero warn_report("DM_HOT_ADD_RESPONSE page count higher than requested (%"PRIu32" vs %"PRIu64")", 119699a4706aSMaciej S. Szmigiero msgHaR->page_count, balloon->ha_current_count); 119799a4706aSMaciej S. Szmigiero msgHaR->page_count = balloon->ha_current_count; 119899a4706aSMaciej S. Szmigiero } 119999a4706aSMaciej S. Szmigiero 120099a4706aSMaciej S. Szmigiero hvb_our_range_mark_added(our_range, msgHaR->page_count); 120199a4706aSMaciej S. Szmigiero hot_add_range->start += msgHaR->page_count; 120299a4706aSMaciej S. Szmigiero hot_add_range->count -= msgHaR->page_count; 120399a4706aSMaciej S. Szmigiero } 120499a4706aSMaciej S. Szmigiero 120599a4706aSMaciej S. Szmigiero if (!msgHaR->result || msgHaR->page_count < balloon->ha_current_count) { 120699a4706aSMaciej S. Szmigiero /* 120799a4706aSMaciej S. Szmigiero * the current planned range was only partially hot-added, take note 120899a4706aSMaciej S. Szmigiero * how much of it remains and don't attempt any further hot adds 120999a4706aSMaciej S. Szmigiero */ 121099a4706aSMaciej S. Szmigiero our_range_mark_remaining_unusable(our_range); 121199a4706aSMaciej S. Szmigiero 121299a4706aSMaciej S. Szmigiero goto ret_idle; 121399a4706aSMaciej S. Szmigiero } 121499a4706aSMaciej S. Szmigiero 121599a4706aSMaciej S. Szmigiero /* any pages remaining to hot-add in our range? */ 121699a4706aSMaciej S. Szmigiero if (hot_add_range->count > 0) { 121799a4706aSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_RB_WAIT); 121899a4706aSMaciej S. Szmigiero return; 121999a4706aSMaciej S. Szmigiero } 122099a4706aSMaciej S. Szmigiero 122199a4706aSMaciej S. Szmigiero ret_idle: 12220d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); 12230d9e8c0bSMaciej S. Szmigiero } 12240d9e8c0bSMaciej S. Szmigiero 12250d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_balloon_response(HvBalloon *balloon, 12260d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req, 12270d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 12280d9e8c0bSMaciej S. Szmigiero { 12290d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 12300d9e8c0bSMaciej S. Szmigiero struct dm_balloon_response *msgBR = vmreq->msg; 12310d9e8c0bSMaciej S. Szmigiero 12320d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_BALLOON_REPLY_WAIT) { 12330d9e8c0bSMaciej S. Szmigiero warn_report("unexpected DM_BALLOON_RESPONSE in %d state", 12340d9e8c0bSMaciej S. Szmigiero balloon->state); 12350d9e8c0bSMaciej S. Szmigiero return; 12360d9e8c0bSMaciej S. Szmigiero } 12370d9e8c0bSMaciej S. Szmigiero 12380d9e8c0bSMaciej S. Szmigiero if (!hv_balloon_handle_msg_size(req, sizeof(*msgBR), 12390d9e8c0bSMaciej S. Szmigiero "DM_BALLOON_RESPONSE")) 12400d9e8c0bSMaciej S. Szmigiero return; 12410d9e8c0bSMaciej S. Szmigiero 12420d9e8c0bSMaciej S. Szmigiero trace_hv_balloon_incoming_balloon(msgBR->hdr.trans_id, msgBR->range_count, 12430d9e8c0bSMaciej S. Szmigiero msgBR->more_pages); 12440d9e8c0bSMaciej S. Szmigiero 12450d9e8c0bSMaciej S. Szmigiero if (vmreq->msglen < sizeof(*msgBR) + 12460d9e8c0bSMaciej S. Szmigiero (uint64_t)sizeof(msgBR->range_array[0]) * msgBR->range_count) { 12470d9e8c0bSMaciej S. Szmigiero warn_report("DM_BALLOON_RESPONSE too short for the range count"); 12480d9e8c0bSMaciej S. Szmigiero return; 12490d9e8c0bSMaciej S. Szmigiero } 12500d9e8c0bSMaciej S. Szmigiero 12510d9e8c0bSMaciej S. Szmigiero if (msgBR->range_count == 0) { 12520d9e8c0bSMaciej S. Szmigiero /* The guest is already at its minimum size */ 12530d9e8c0bSMaciej S. Szmigiero balloon->balloon_diff = 0; 12540d9e8c0bSMaciej S. Szmigiero goto ret_end_trans; 12550d9e8c0bSMaciej S. Szmigiero } else { 12560d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_remove_ranges(balloon, 12570d9e8c0bSMaciej S. Szmigiero msgBR->range_array, 12580d9e8c0bSMaciej S. Szmigiero msgBR->range_count); 12590d9e8c0bSMaciej S. Szmigiero } 12600d9e8c0bSMaciej S. Szmigiero 12610d9e8c0bSMaciej S. Szmigiero /* More responses expected? */ 12620d9e8c0bSMaciej S. Szmigiero if (msgBR->more_pages) { 12630d9e8c0bSMaciej S. Szmigiero return; 12640d9e8c0bSMaciej S. Szmigiero } 12650d9e8c0bSMaciej S. Szmigiero 12660d9e8c0bSMaciej S. Szmigiero ret_end_trans: 12670d9e8c0bSMaciej S. Szmigiero balloon->trans_id++; 12680d9e8c0bSMaciej S. Szmigiero 12690d9e8c0bSMaciej S. Szmigiero if (balloon->balloon_diff > 0) { 12700d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT); 12710d9e8c0bSMaciej S. Szmigiero } else { 12720d9e8c0bSMaciej S. Szmigiero HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); 12730d9e8c0bSMaciej S. Szmigiero } 12740d9e8c0bSMaciej S. Szmigiero } 12750d9e8c0bSMaciej S. Szmigiero 12760d9e8c0bSMaciej S. Szmigiero static void hv_balloon_handle_packet(HvBalloon *balloon, HvBalloonReq *req, 12770d9e8c0bSMaciej S. Szmigiero StateDesc *stdesc) 12780d9e8c0bSMaciej S. Szmigiero { 12790d9e8c0bSMaciej S. Szmigiero VMBusChanReq *vmreq = &req->vmreq; 12800d9e8c0bSMaciej S. Szmigiero struct dm_message *msg = vmreq->msg; 12810d9e8c0bSMaciej S. Szmigiero 12820d9e8c0bSMaciej S. Szmigiero if (vmreq->msglen < sizeof(msg->hdr)) { 12830d9e8c0bSMaciej S. Szmigiero return; 12840d9e8c0bSMaciej S. Szmigiero } 12850d9e8c0bSMaciej S. Szmigiero 12860d9e8c0bSMaciej S. Szmigiero switch (msg->hdr.type) { 12870d9e8c0bSMaciej S. Szmigiero case DM_VERSION_REQUEST: 12880d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_version_request(balloon, req, stdesc); 12890d9e8c0bSMaciej S. Szmigiero break; 12900d9e8c0bSMaciej S. Szmigiero 12910d9e8c0bSMaciej S. Szmigiero case DM_CAPABILITIES_REPORT: 12920d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_caps_report(balloon, req, stdesc); 12930d9e8c0bSMaciej S. Szmigiero break; 12940d9e8c0bSMaciej S. Szmigiero 12950d9e8c0bSMaciej S. Szmigiero case DM_STATUS_REPORT: 12960d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_status_report(balloon, req); 12970d9e8c0bSMaciej S. Szmigiero break; 12980d9e8c0bSMaciej S. Szmigiero 129999a4706aSMaciej S. Szmigiero case DM_MEM_HOT_ADD_RESPONSE: 130099a4706aSMaciej S. Szmigiero hv_balloon_handle_hot_add_response(balloon, req, stdesc); 130199a4706aSMaciej S. Szmigiero break; 130299a4706aSMaciej S. Szmigiero 13030d9e8c0bSMaciej S. Szmigiero case DM_UNBALLOON_RESPONSE: 13040d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_unballoon_response(balloon, req, stdesc); 13050d9e8c0bSMaciej S. Szmigiero break; 13060d9e8c0bSMaciej S. Szmigiero 13070d9e8c0bSMaciej S. Szmigiero case DM_BALLOON_RESPONSE: 13080d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_balloon_response(balloon, req, stdesc); 13090d9e8c0bSMaciej S. Szmigiero break; 13100d9e8c0bSMaciej S. Szmigiero 13110d9e8c0bSMaciej S. Szmigiero default: 13120d9e8c0bSMaciej S. Szmigiero warn_report("unknown DM message %u", msg->hdr.type); 13130d9e8c0bSMaciej S. Szmigiero break; 13140d9e8c0bSMaciej S. Szmigiero } 13150d9e8c0bSMaciej S. Szmigiero } 13160d9e8c0bSMaciej S. Szmigiero 13170d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_recv_channel(HvBalloon *balloon, StateDesc *stdesc) 13180d9e8c0bSMaciej S. Szmigiero { 13190d9e8c0bSMaciej S. Szmigiero VMBusChannel *chan; 13200d9e8c0bSMaciej S. Szmigiero HvBalloonReq *req; 13210d9e8c0bSMaciej S. Szmigiero 13220d9e8c0bSMaciej S. Szmigiero if (balloon->state == S_WAIT_RESET || 13230d9e8c0bSMaciej S. Szmigiero balloon->state == S_POST_RESET_CLOSED) { 13240d9e8c0bSMaciej S. Szmigiero return false; 13250d9e8c0bSMaciej S. Szmigiero } 13260d9e8c0bSMaciej S. Szmigiero 13270d9e8c0bSMaciej S. Szmigiero chan = hv_balloon_get_channel(balloon); 13280d9e8c0bSMaciej S. Szmigiero if (vmbus_channel_recv_start(chan)) { 13290d9e8c0bSMaciej S. Szmigiero return false; 13300d9e8c0bSMaciej S. Szmigiero } 13310d9e8c0bSMaciej S. Szmigiero 13320d9e8c0bSMaciej S. Szmigiero while ((req = vmbus_channel_recv_peek(chan, sizeof(*req)))) { 13330d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_packet(balloon, req, stdesc); 13340d9e8c0bSMaciej S. Szmigiero vmbus_free_req(req); 13350d9e8c0bSMaciej S. Szmigiero vmbus_channel_recv_pop(chan); 13360d9e8c0bSMaciej S. Szmigiero 13370d9e8c0bSMaciej S. Szmigiero if (stdesc->state != S_NO_CHANGE) { 13380d9e8c0bSMaciej S. Szmigiero break; 13390d9e8c0bSMaciej S. Szmigiero } 13400d9e8c0bSMaciej S. Szmigiero } 13410d9e8c0bSMaciej S. Szmigiero 13420d9e8c0bSMaciej S. Szmigiero return vmbus_channel_recv_done(chan) > 0; 13430d9e8c0bSMaciej S. Szmigiero } 13440d9e8c0bSMaciej S. Szmigiero 13450d9e8c0bSMaciej S. Szmigiero /* old state handler -> new state transition (potential) */ 13460d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_event_loop_state(HvBalloon *balloon) 13470d9e8c0bSMaciej S. Szmigiero { 13480d9e8c0bSMaciej S. Szmigiero StateDesc state_new = HV_BALLOON_STATE_DESC_INIT; 13490d9e8c0bSMaciej S. Szmigiero 13500d9e8c0bSMaciej S. Szmigiero hv_balloon_handle_state(balloon, &state_new); 13510d9e8c0bSMaciej S. Szmigiero return hv_balloon_state_set(balloon, state_new.state, state_new.desc); 13520d9e8c0bSMaciej S. Szmigiero } 13530d9e8c0bSMaciej S. Szmigiero 13540d9e8c0bSMaciej S. Szmigiero /* VMBus message -> new state transition (potential) */ 13550d9e8c0bSMaciej S. Szmigiero static bool hv_balloon_event_loop_recv(HvBalloon *balloon) 13560d9e8c0bSMaciej S. Szmigiero { 13570d9e8c0bSMaciej S. Szmigiero StateDesc state_new = HV_BALLOON_STATE_DESC_INIT; 13580d9e8c0bSMaciej S. Szmigiero bool any_recv, state_changed; 13590d9e8c0bSMaciej S. Szmigiero 13600d9e8c0bSMaciej S. Szmigiero any_recv = hv_balloon_recv_channel(balloon, &state_new); 13610d9e8c0bSMaciej S. Szmigiero state_changed = hv_balloon_state_set(balloon, 13620d9e8c0bSMaciej S. Szmigiero state_new.state, state_new.desc); 13630d9e8c0bSMaciej S. Szmigiero 13640d9e8c0bSMaciej S. Szmigiero return state_changed || any_recv; 13650d9e8c0bSMaciej S. Szmigiero } 13660d9e8c0bSMaciej S. Szmigiero 13670d9e8c0bSMaciej S. Szmigiero static void hv_balloon_event_loop(HvBalloon *balloon) 13680d9e8c0bSMaciej S. Szmigiero { 13690d9e8c0bSMaciej S. Szmigiero bool state_repeat, recv_repeat; 13700d9e8c0bSMaciej S. Szmigiero 13710d9e8c0bSMaciej S. Szmigiero do { 13720d9e8c0bSMaciej S. Szmigiero state_repeat = hv_balloon_event_loop_state(balloon); 13730d9e8c0bSMaciej S. Szmigiero recv_repeat = hv_balloon_event_loop_recv(balloon); 13740d9e8c0bSMaciej S. Szmigiero } while (state_repeat || recv_repeat); 13750d9e8c0bSMaciej S. Szmigiero } 13760d9e8c0bSMaciej S. Szmigiero 13770d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_chan_notify(VMBusChannel *chan) 13780d9e8c0bSMaciej S. Szmigiero { 13790d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); 13800d9e8c0bSMaciej S. Szmigiero 13810d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 13820d9e8c0bSMaciej S. Szmigiero } 13830d9e8c0bSMaciej S. Szmigiero 13840d9e8c0bSMaciej S. Szmigiero static void hv_balloon_stat(void *opaque, BalloonInfo *info) 13850d9e8c0bSMaciej S. Szmigiero { 13860d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = opaque; 13870d9e8c0bSMaciej S. Szmigiero info->actual = (hv_balloon_total_ram(balloon) - balloon->removed_both_ctr) 13880d9e8c0bSMaciej S. Szmigiero << HV_BALLOON_PFN_SHIFT; 13890d9e8c0bSMaciej S. Szmigiero } 13900d9e8c0bSMaciej S. Szmigiero 13910d9e8c0bSMaciej S. Szmigiero static void hv_balloon_to_target(void *opaque, ram_addr_t target) 13920d9e8c0bSMaciej S. Szmigiero { 13930d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = opaque; 13940d9e8c0bSMaciej S. Szmigiero uint64_t target_pages = target >> HV_BALLOON_PFN_SHIFT; 13950d9e8c0bSMaciej S. Szmigiero 13960d9e8c0bSMaciej S. Szmigiero if (!target_pages) { 13970d9e8c0bSMaciej S. Szmigiero return; 13980d9e8c0bSMaciej S. Szmigiero } 13990d9e8c0bSMaciej S. Szmigiero 14000d9e8c0bSMaciej S. Szmigiero /* 14010d9e8c0bSMaciej S. Szmigiero * always set target_changed, even with unchanged target, as the user 14020d9e8c0bSMaciej S. Szmigiero * might be asking us to try again reaching it 14030d9e8c0bSMaciej S. Szmigiero */ 14040d9e8c0bSMaciej S. Szmigiero balloon->target = target_pages; 14050d9e8c0bSMaciej S. Szmigiero balloon->target_changed = true; 14060d9e8c0bSMaciej S. Szmigiero 14070d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 14080d9e8c0bSMaciej S. Szmigiero } 14090d9e8c0bSMaciej S. Szmigiero 14100d9e8c0bSMaciej S. Szmigiero static int hv_balloon_vmdev_open_channel(VMBusChannel *chan) 14110d9e8c0bSMaciej S. Szmigiero { 14120d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); 14130d9e8c0bSMaciej S. Szmigiero 14140d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_POST_RESET_CLOSED) { 14150d9e8c0bSMaciej S. Szmigiero warn_report("guest trying to open a DM channel in invalid %d state", 14160d9e8c0bSMaciej S. Szmigiero balloon->state); 14170d9e8c0bSMaciej S. Szmigiero return -EINVAL; 14180d9e8c0bSMaciej S. Szmigiero } 14190d9e8c0bSMaciej S. Szmigiero 14200d9e8c0bSMaciej S. Szmigiero HV_BALLOON_SET_STATE(balloon, S_VERSION); 14210d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 14220d9e8c0bSMaciej S. Szmigiero 14230d9e8c0bSMaciej S. Szmigiero return 0; 14240d9e8c0bSMaciej S. Szmigiero } 14250d9e8c0bSMaciej S. Szmigiero 14260d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_close_channel(VMBusChannel *chan) 14270d9e8c0bSMaciej S. Szmigiero { 14280d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); 14290d9e8c0bSMaciej S. Szmigiero 14300d9e8c0bSMaciej S. Szmigiero timer_del(&balloon->post_init_timer); 14310d9e8c0bSMaciej S. Szmigiero 14320d9e8c0bSMaciej S. Szmigiero /* Don't report stale data */ 14330d9e8c0bSMaciej S. Szmigiero balloon->status_report.received = false; 14340d9e8c0bSMaciej S. Szmigiero 14350d9e8c0bSMaciej S. Szmigiero HV_BALLOON_SET_STATE(balloon, S_WAIT_RESET); 14360d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 14370d9e8c0bSMaciej S. Szmigiero } 14380d9e8c0bSMaciej S. Szmigiero 14390d9e8c0bSMaciej S. Szmigiero static void hv_balloon_post_init_timer(void *opaque) 14400d9e8c0bSMaciej S. Szmigiero { 14410d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = opaque; 14420d9e8c0bSMaciej S. Szmigiero 14430d9e8c0bSMaciej S. Szmigiero if (balloon->state != S_POST_INIT_WAIT) { 14440d9e8c0bSMaciej S. Szmigiero return; 14450d9e8c0bSMaciej S. Szmigiero } 14460d9e8c0bSMaciej S. Szmigiero 14470d9e8c0bSMaciej S. Szmigiero HV_BALLOON_SET_STATE(balloon, S_IDLE); 14480d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 14490d9e8c0bSMaciej S. Szmigiero } 14500d9e8c0bSMaciej S. Szmigiero 145199a4706aSMaciej S. Szmigiero static void hv_balloon_system_reset_unrealize_common(HvBalloon *balloon) 145299a4706aSMaciej S. Szmigiero { 145399a4706aSMaciej S. Szmigiero g_clear_pointer(&balloon->our_range, hvb_our_range_memslots_free); 145499a4706aSMaciej S. Szmigiero } 145599a4706aSMaciej S. Szmigiero 145699a4706aSMaciej S. Szmigiero static void hv_balloon_system_reset(void *opaque) 145799a4706aSMaciej S. Szmigiero { 145899a4706aSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(opaque); 145999a4706aSMaciej S. Szmigiero 146099a4706aSMaciej S. Szmigiero hv_balloon_system_reset_unrealize_common(balloon); 146199a4706aSMaciej S. Szmigiero } 146299a4706aSMaciej S. Szmigiero 146399a4706aSMaciej S. Szmigiero static void hv_balloon_ensure_mr(HvBalloon *balloon) 146499a4706aSMaciej S. Szmigiero { 146599a4706aSMaciej S. Szmigiero MemoryRegion *hostmem_mr; 146699a4706aSMaciej S. Szmigiero 146799a4706aSMaciej S. Szmigiero assert(balloon->hostmem); 146899a4706aSMaciej S. Szmigiero 146999a4706aSMaciej S. Szmigiero if (balloon->mr) { 147099a4706aSMaciej S. Szmigiero return; 147199a4706aSMaciej S. Szmigiero } 147299a4706aSMaciej S. Szmigiero 147399a4706aSMaciej S. Szmigiero hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); 147499a4706aSMaciej S. Szmigiero 147599a4706aSMaciej S. Szmigiero balloon->mr = g_new0(MemoryRegion, 1); 147699a4706aSMaciej S. Szmigiero memory_region_init(balloon->mr, OBJECT(balloon), TYPE_HV_BALLOON, 147799a4706aSMaciej S. Szmigiero memory_region_size(hostmem_mr)); 1478f77c5f38SDavid Hildenbrand balloon->mr->align = memory_region_get_alignment(hostmem_mr); 147999a4706aSMaciej S. Szmigiero } 148099a4706aSMaciej S. Szmigiero 148199a4706aSMaciej S. Szmigiero static void hv_balloon_free_mr(HvBalloon *balloon) 148299a4706aSMaciej S. Szmigiero { 148399a4706aSMaciej S. Szmigiero if (!balloon->mr) { 148499a4706aSMaciej S. Szmigiero return; 148599a4706aSMaciej S. Szmigiero } 148699a4706aSMaciej S. Szmigiero 148799a4706aSMaciej S. Szmigiero object_unparent(OBJECT(balloon->mr)); 148899a4706aSMaciej S. Szmigiero g_clear_pointer(&balloon->mr, g_free); 148999a4706aSMaciej S. Szmigiero } 149099a4706aSMaciej S. Szmigiero 14910d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_realize(VMBusDevice *vdev, Error **errp) 14920d9e8c0bSMaciej S. Szmigiero { 14930d9e8c0bSMaciej S. Szmigiero ERRP_GUARD(); 14940d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vdev); 14950d9e8c0bSMaciej S. Szmigiero int ret; 14960d9e8c0bSMaciej S. Szmigiero 14970d9e8c0bSMaciej S. Szmigiero balloon->state = S_WAIT_RESET; 14980d9e8c0bSMaciej S. Szmigiero 14990d9e8c0bSMaciej S. Szmigiero ret = qemu_add_balloon_handler(hv_balloon_to_target, hv_balloon_stat, 15000d9e8c0bSMaciej S. Szmigiero balloon); 15010d9e8c0bSMaciej S. Szmigiero if (ret < 0) { 15020d9e8c0bSMaciej S. Szmigiero /* This also protects against having multiple hv-balloon instances */ 15030d9e8c0bSMaciej S. Szmigiero error_setg(errp, "Only one balloon device is supported"); 15040d9e8c0bSMaciej S. Szmigiero return; 15050d9e8c0bSMaciej S. Szmigiero } 15060d9e8c0bSMaciej S. Szmigiero 150799a4706aSMaciej S. Szmigiero if (balloon->hostmem) { 150899a4706aSMaciej S. Szmigiero if (host_memory_backend_is_mapped(balloon->hostmem)) { 150999a4706aSMaciej S. Szmigiero Object *obj = OBJECT(balloon->hostmem); 151099a4706aSMaciej S. Szmigiero 151199a4706aSMaciej S. Szmigiero error_setg(errp, "'%s' property specifies a busy memdev: %s", 151299a4706aSMaciej S. Szmigiero HV_BALLOON_MEMDEV_PROP, 151399a4706aSMaciej S. Szmigiero object_get_canonical_path_component(obj)); 151499a4706aSMaciej S. Szmigiero goto out_balloon_handler; 151599a4706aSMaciej S. Szmigiero } 151699a4706aSMaciej S. Szmigiero 151799a4706aSMaciej S. Szmigiero hv_balloon_ensure_mr(balloon); 151899a4706aSMaciej S. Szmigiero 151999a4706aSMaciej S. Szmigiero /* This is rather unlikely to happen, but let's still check for it. */ 152099a4706aSMaciej S. Szmigiero if (!QEMU_IS_ALIGNED(memory_region_size(balloon->mr), 152199a4706aSMaciej S. Szmigiero HV_BALLOON_PAGE_SIZE)) { 152299a4706aSMaciej S. Szmigiero error_setg(errp, "'%s' property memdev size has to be a multiple of 0x%" PRIx64, 152399a4706aSMaciej S. Szmigiero HV_BALLOON_MEMDEV_PROP, (uint64_t)HV_BALLOON_PAGE_SIZE); 152499a4706aSMaciej S. Szmigiero goto out_balloon_handler; 152599a4706aSMaciej S. Szmigiero } 152699a4706aSMaciej S. Szmigiero 152799a4706aSMaciej S. Szmigiero host_memory_backend_set_mapped(balloon->hostmem, true); 152899a4706aSMaciej S. Szmigiero vmstate_register_ram(host_memory_backend_get_memory(balloon->hostmem), 152999a4706aSMaciej S. Szmigiero DEVICE(balloon)); 153099a4706aSMaciej S. Szmigiero } else if (balloon->addr) { 153199a4706aSMaciej S. Szmigiero error_setg(errp, "'%s' property must not be set without a memdev", 153299a4706aSMaciej S. Szmigiero HV_BALLOON_MEMDEV_PROP); 153399a4706aSMaciej S. Szmigiero goto out_balloon_handler; 153499a4706aSMaciej S. Szmigiero } 153599a4706aSMaciej S. Szmigiero 15360d9e8c0bSMaciej S. Szmigiero timer_init_ms(&balloon->post_init_timer, QEMU_CLOCK_VIRTUAL, 15370d9e8c0bSMaciej S. Szmigiero hv_balloon_post_init_timer, balloon); 153899a4706aSMaciej S. Szmigiero 153999a4706aSMaciej S. Szmigiero qemu_register_reset(hv_balloon_system_reset, balloon); 154099a4706aSMaciej S. Szmigiero 154199a4706aSMaciej S. Szmigiero return; 154299a4706aSMaciej S. Szmigiero 154399a4706aSMaciej S. Szmigiero out_balloon_handler: 154499a4706aSMaciej S. Szmigiero qemu_remove_balloon_handler(balloon); 15450d9e8c0bSMaciej S. Szmigiero } 15460d9e8c0bSMaciej S. Szmigiero 15470d9e8c0bSMaciej S. Szmigiero /* 15480d9e8c0bSMaciej S. Szmigiero * VMBus device reset has to be implemented in case the guest decides to 15490d9e8c0bSMaciej S. Szmigiero * disconnect and reconnect to the VMBus without rebooting the whole system. 155099a4706aSMaciej S. Szmigiero * 155199a4706aSMaciej S. Szmigiero * However, the hot-added memory can't be removed here as Windows keeps on using 155299a4706aSMaciej S. Szmigiero * it until the system is restarted, even after disconnecting from the VMBus. 15530d9e8c0bSMaciej S. Szmigiero */ 15540d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_reset(VMBusDevice *vdev) 15550d9e8c0bSMaciej S. Szmigiero { 15560d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vdev); 15570d9e8c0bSMaciej S. Szmigiero 15580d9e8c0bSMaciej S. Szmigiero if (balloon->state == S_POST_RESET_CLOSED) { 15590d9e8c0bSMaciej S. Szmigiero return; 15600d9e8c0bSMaciej S. Szmigiero } 15610d9e8c0bSMaciej S. Szmigiero 156299a4706aSMaciej S. Szmigiero if (balloon->our_range) { 156399a4706aSMaciej S. Szmigiero hvb_our_range_clear_removed_trees(OUR_RANGE(balloon->our_range)); 156499a4706aSMaciej S. Szmigiero } 156599a4706aSMaciej S. Szmigiero 15660d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&balloon->removed_guest); 15670d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&balloon->removed_both); 15680d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_init(&balloon->removed_guest); 15690d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_init(&balloon->removed_both); 15700d9e8c0bSMaciej S. Szmigiero 15710d9e8c0bSMaciej S. Szmigiero balloon->trans_id = 0; 15720d9e8c0bSMaciej S. Szmigiero balloon->removed_guest_ctr = 0; 15730d9e8c0bSMaciej S. Szmigiero balloon->removed_both_ctr = 0; 15740d9e8c0bSMaciej S. Szmigiero 15750d9e8c0bSMaciej S. Szmigiero HV_BALLOON_SET_STATE(balloon, S_POST_RESET_CLOSED); 15760d9e8c0bSMaciej S. Szmigiero hv_balloon_event_loop(balloon); 15770d9e8c0bSMaciej S. Szmigiero } 15780d9e8c0bSMaciej S. Szmigiero 157999a4706aSMaciej S. Szmigiero /* 158099a4706aSMaciej S. Szmigiero * Clean up things that were (possibly) allocated pre-realization, for example 158199a4706aSMaciej S. Szmigiero * from memory_device_pre_plug(), so we don't leak them if the device don't 158299a4706aSMaciej S. Szmigiero * actually get realized in the end. 158399a4706aSMaciej S. Szmigiero */ 158499a4706aSMaciej S. Szmigiero static void hv_balloon_unrealize_finalize_common(HvBalloon *balloon) 158599a4706aSMaciej S. Szmigiero { 158699a4706aSMaciej S. Szmigiero hv_balloon_free_mr(balloon); 158799a4706aSMaciej S. Szmigiero balloon->addr = 0; 158899a4706aSMaciej S. Szmigiero 158999a4706aSMaciej S. Szmigiero balloon->memslot_count = 0; 159099a4706aSMaciej S. Szmigiero } 159199a4706aSMaciej S. Szmigiero 15920d9e8c0bSMaciej S. Szmigiero static void hv_balloon_vmdev_unrealize(VMBusDevice *vdev) 15930d9e8c0bSMaciej S. Szmigiero { 15940d9e8c0bSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(vdev); 15950d9e8c0bSMaciej S. Szmigiero 159699a4706aSMaciej S. Szmigiero qemu_unregister_reset(hv_balloon_system_reset, balloon); 159799a4706aSMaciej S. Szmigiero 159899a4706aSMaciej S. Szmigiero hv_balloon_system_reset_unrealize_common(balloon); 159999a4706aSMaciej S. Szmigiero 16000d9e8c0bSMaciej S. Szmigiero qemu_remove_balloon_handler(balloon); 16010d9e8c0bSMaciej S. Szmigiero 160299a4706aSMaciej S. Szmigiero if (balloon->hostmem) { 160399a4706aSMaciej S. Szmigiero vmstate_unregister_ram(host_memory_backend_get_memory(balloon->hostmem), 160499a4706aSMaciej S. Szmigiero DEVICE(balloon)); 160599a4706aSMaciej S. Szmigiero host_memory_backend_set_mapped(balloon->hostmem, false); 160699a4706aSMaciej S. Szmigiero } 160799a4706aSMaciej S. Szmigiero 16080d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&balloon->removed_guest); 16090d9e8c0bSMaciej S. Szmigiero hvb_page_range_tree_destroy(&balloon->removed_both); 161099a4706aSMaciej S. Szmigiero 161199a4706aSMaciej S. Szmigiero hv_balloon_unrealize_finalize_common(balloon); 161299a4706aSMaciej S. Szmigiero } 161399a4706aSMaciej S. Szmigiero 161499a4706aSMaciej S. Szmigiero static uint64_t hv_balloon_md_get_addr(const MemoryDeviceState *md) 161599a4706aSMaciej S. Szmigiero { 161699a4706aSMaciej S. Szmigiero return object_property_get_uint(OBJECT(md), HV_BALLOON_ADDR_PROP, 161799a4706aSMaciej S. Szmigiero &error_abort); 161899a4706aSMaciej S. Szmigiero } 161999a4706aSMaciej S. Szmigiero 162099a4706aSMaciej S. Szmigiero static void hv_balloon_md_set_addr(MemoryDeviceState *md, uint64_t addr, 162199a4706aSMaciej S. Szmigiero Error **errp) 162299a4706aSMaciej S. Szmigiero { 162399a4706aSMaciej S. Szmigiero object_property_set_uint(OBJECT(md), HV_BALLOON_ADDR_PROP, addr, errp); 162499a4706aSMaciej S. Szmigiero } 162599a4706aSMaciej S. Szmigiero 162699a4706aSMaciej S. Szmigiero static MemoryRegion *hv_balloon_md_get_memory_region(MemoryDeviceState *md, 162799a4706aSMaciej S. Szmigiero Error **errp) 162899a4706aSMaciej S. Szmigiero { 162999a4706aSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(md); 163099a4706aSMaciej S. Szmigiero 163199a4706aSMaciej S. Szmigiero if (!balloon->hostmem) { 163299a4706aSMaciej S. Szmigiero return NULL; 163399a4706aSMaciej S. Szmigiero } 163499a4706aSMaciej S. Szmigiero 163599a4706aSMaciej S. Szmigiero hv_balloon_ensure_mr(balloon); 163699a4706aSMaciej S. Szmigiero 163799a4706aSMaciej S. Szmigiero return balloon->mr; 163899a4706aSMaciej S. Szmigiero } 163999a4706aSMaciej S. Szmigiero 1640f77c5f38SDavid Hildenbrand static uint64_t hv_balloon_md_get_min_alignment(const MemoryDeviceState *md) 1641f77c5f38SDavid Hildenbrand { 1642f77c5f38SDavid Hildenbrand /* 1643f77c5f38SDavid Hildenbrand * The VM can indicate an alignment up to 32 GiB. Memory device core can 1644f77c5f38SDavid Hildenbrand * usually only handle/guarantee 1 GiB alignment. The user will have to 1645f77c5f38SDavid Hildenbrand * specify a larger maxmem eventually. 1646f77c5f38SDavid Hildenbrand * 1647f77c5f38SDavid Hildenbrand * The memory device core will warn the user in case maxmem might have to be 1648f77c5f38SDavid Hildenbrand * increased and will fail plugging the device if there is not sufficient 1649f77c5f38SDavid Hildenbrand * space after alignment. 1650f77c5f38SDavid Hildenbrand * 1651f77c5f38SDavid Hildenbrand * TODO: we could do the alignment ourselves in a slightly bigger region. 1652f77c5f38SDavid Hildenbrand * But this feels better, although the warning might be annoying. Maybe 1653f77c5f38SDavid Hildenbrand * we can optimize that in the future (e.g., with such a device on the 1654f77c5f38SDavid Hildenbrand * cmdline place/size the device memory region differently. 1655f77c5f38SDavid Hildenbrand */ 1656f77c5f38SDavid Hildenbrand return 32 * GiB; 1657f77c5f38SDavid Hildenbrand } 1658f77c5f38SDavid Hildenbrand 165916dff2f9SMaciej S. Szmigiero static void hv_balloon_md_fill_device_info(const MemoryDeviceState *md, 166016dff2f9SMaciej S. Szmigiero MemoryDeviceInfo *info) 166116dff2f9SMaciej S. Szmigiero { 166216dff2f9SMaciej S. Szmigiero HvBalloonDeviceInfo *hi = g_new0(HvBalloonDeviceInfo, 1); 166316dff2f9SMaciej S. Szmigiero const HvBalloon *balloon = HV_BALLOON(md); 166416dff2f9SMaciej S. Szmigiero DeviceState *dev = DEVICE(md); 166516dff2f9SMaciej S. Szmigiero 166616dff2f9SMaciej S. Szmigiero if (dev->id) { 166716dff2f9SMaciej S. Szmigiero hi->id = g_strdup(dev->id); 166816dff2f9SMaciej S. Szmigiero } 166916dff2f9SMaciej S. Szmigiero 167016dff2f9SMaciej S. Szmigiero if (balloon->hostmem) { 167116dff2f9SMaciej S. Szmigiero hi->memdev = object_get_canonical_path(OBJECT(balloon->hostmem)); 167216dff2f9SMaciej S. Szmigiero hi->memaddr = balloon->addr; 167316dff2f9SMaciej S. Szmigiero hi->has_memaddr = true; 167416dff2f9SMaciej S. Szmigiero hi->max_size = memory_region_size(balloon->mr); 167516dff2f9SMaciej S. Szmigiero /* TODO: expose current provided size or something else? */ 167616dff2f9SMaciej S. Szmigiero } else { 167716dff2f9SMaciej S. Szmigiero hi->max_size = 0; 167816dff2f9SMaciej S. Szmigiero } 167916dff2f9SMaciej S. Szmigiero 168016dff2f9SMaciej S. Szmigiero info->u.hv_balloon.data = hi; 168116dff2f9SMaciej S. Szmigiero info->type = MEMORY_DEVICE_INFO_KIND_HV_BALLOON; 168216dff2f9SMaciej S. Szmigiero } 168316dff2f9SMaciej S. Szmigiero 168499a4706aSMaciej S. Szmigiero static void hv_balloon_decide_memslots(MemoryDeviceState *md, 168599a4706aSMaciej S. Szmigiero unsigned int limit) 168699a4706aSMaciej S. Szmigiero { 168799a4706aSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(md); 168899a4706aSMaciej S. Szmigiero MemoryRegion *hostmem_mr; 168999a4706aSMaciej S. Szmigiero uint64_t region_size, memslot_size, memslots; 169099a4706aSMaciej S. Szmigiero 169199a4706aSMaciej S. Szmigiero /* We're called exactly once, before realizing the device. */ 169299a4706aSMaciej S. Szmigiero assert(!balloon->memslot_count); 169399a4706aSMaciej S. Szmigiero 169499a4706aSMaciej S. Szmigiero /* We should not be called if we don't have a memory backend */ 169599a4706aSMaciej S. Szmigiero assert(balloon->hostmem); 169699a4706aSMaciej S. Szmigiero 169799a4706aSMaciej S. Szmigiero hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); 169899a4706aSMaciej S. Szmigiero region_size = memory_region_size(hostmem_mr); 169999a4706aSMaciej S. Szmigiero 170099a4706aSMaciej S. Szmigiero assert(region_size > 0); 170199a4706aSMaciej S. Szmigiero memslot_size = QEMU_ALIGN_UP(region_size / limit, 170299a4706aSMaciej S. Szmigiero HV_BALLOON_HA_MEMSLOT_SIZE_ALIGN); 170399a4706aSMaciej S. Szmigiero memslots = QEMU_ALIGN_UP(region_size, memslot_size) / memslot_size; 170499a4706aSMaciej S. Szmigiero 170599a4706aSMaciej S. Szmigiero if (memslots > 1) { 170699a4706aSMaciej S. Szmigiero balloon->memslot_size = memslot_size; 170799a4706aSMaciej S. Szmigiero } else { 170899a4706aSMaciej S. Szmigiero balloon->memslot_size = region_size; 170999a4706aSMaciej S. Szmigiero } 171099a4706aSMaciej S. Szmigiero 171199a4706aSMaciej S. Szmigiero assert(memslots <= UINT_MAX); 171299a4706aSMaciej S. Szmigiero balloon->memslot_count = memslots; 171399a4706aSMaciej S. Szmigiero } 171499a4706aSMaciej S. Szmigiero 171599a4706aSMaciej S. Szmigiero static unsigned int hv_balloon_get_memslots(MemoryDeviceState *md) 171699a4706aSMaciej S. Szmigiero { 171799a4706aSMaciej S. Szmigiero const HvBalloon *balloon = HV_BALLOON(md); 171899a4706aSMaciej S. Szmigiero 171999a4706aSMaciej S. Szmigiero /* We're called after setting the suggested limit. */ 172099a4706aSMaciej S. Szmigiero assert(balloon->memslot_count > 0); 172199a4706aSMaciej S. Szmigiero 172299a4706aSMaciej S. Szmigiero return balloon->memslot_count; 17230d9e8c0bSMaciej S. Szmigiero } 17240d9e8c0bSMaciej S. Szmigiero 17250d9e8c0bSMaciej S. Szmigiero static void hv_balloon_init(Object *obj) 17260d9e8c0bSMaciej S. Szmigiero { 17270d9e8c0bSMaciej S. Szmigiero } 17280d9e8c0bSMaciej S. Szmigiero 17290d9e8c0bSMaciej S. Szmigiero static void hv_balloon_finalize(Object *obj) 17300d9e8c0bSMaciej S. Szmigiero { 173199a4706aSMaciej S. Szmigiero HvBalloon *balloon = HV_BALLOON(obj); 173299a4706aSMaciej S. Szmigiero 173399a4706aSMaciej S. Szmigiero hv_balloon_unrealize_finalize_common(balloon); 17340d9e8c0bSMaciej S. Szmigiero } 17350d9e8c0bSMaciej S. Szmigiero 1736e923f5e1SRichard Henderson static const Property hv_balloon_properties[] = { 17370d9e8c0bSMaciej S. Szmigiero DEFINE_PROP_BOOL("status-report", HvBalloon, 17380d9e8c0bSMaciej S. Szmigiero status_report.enabled, false), 17390d9e8c0bSMaciej S. Szmigiero 174099a4706aSMaciej S. Szmigiero /* MEMORY_DEVICE props */ 174199a4706aSMaciej S. Szmigiero DEFINE_PROP_LINK(HV_BALLOON_MEMDEV_PROP, HvBalloon, hostmem, 174299a4706aSMaciej S. Szmigiero TYPE_MEMORY_BACKEND, HostMemoryBackend *), 174399a4706aSMaciej S. Szmigiero DEFINE_PROP_UINT64(HV_BALLOON_ADDR_PROP, HvBalloon, addr, 0), 174499a4706aSMaciej S. Szmigiero 17450d9e8c0bSMaciej S. Szmigiero DEFINE_PROP_END_OF_LIST(), 17460d9e8c0bSMaciej S. Szmigiero }; 17470d9e8c0bSMaciej S. Szmigiero 17480d9e8c0bSMaciej S. Szmigiero static void hv_balloon_class_init(ObjectClass *klass, void *data) 17490d9e8c0bSMaciej S. Szmigiero { 17500d9e8c0bSMaciej S. Szmigiero DeviceClass *dc = DEVICE_CLASS(klass); 17510d9e8c0bSMaciej S. Szmigiero VMBusDeviceClass *vdc = VMBUS_DEVICE_CLASS(klass); 175299a4706aSMaciej S. Szmigiero MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass); 17530d9e8c0bSMaciej S. Szmigiero 17540d9e8c0bSMaciej S. Szmigiero device_class_set_props(dc, hv_balloon_properties); 17550d9e8c0bSMaciej S. Szmigiero qemu_uuid_parse(HV_BALLOON_GUID, &vdc->classid); 17560d9e8c0bSMaciej S. Szmigiero set_bit(DEVICE_CATEGORY_MISC, dc->categories); 17570d9e8c0bSMaciej S. Szmigiero 17580d9e8c0bSMaciej S. Szmigiero vdc->vmdev_realize = hv_balloon_vmdev_realize; 17590d9e8c0bSMaciej S. Szmigiero vdc->vmdev_unrealize = hv_balloon_vmdev_unrealize; 17600d9e8c0bSMaciej S. Szmigiero vdc->vmdev_reset = hv_balloon_vmdev_reset; 17610d9e8c0bSMaciej S. Szmigiero vdc->open_channel = hv_balloon_vmdev_open_channel; 17620d9e8c0bSMaciej S. Szmigiero vdc->close_channel = hv_balloon_vmdev_close_channel; 17630d9e8c0bSMaciej S. Szmigiero vdc->chan_notify_cb = hv_balloon_vmdev_chan_notify; 176499a4706aSMaciej S. Szmigiero 176599a4706aSMaciej S. Szmigiero mdc->get_addr = hv_balloon_md_get_addr; 176699a4706aSMaciej S. Szmigiero mdc->set_addr = hv_balloon_md_set_addr; 176799a4706aSMaciej S. Szmigiero mdc->get_plugged_size = memory_device_get_region_size; 176899a4706aSMaciej S. Szmigiero mdc->get_memory_region = hv_balloon_md_get_memory_region; 176999a4706aSMaciej S. Szmigiero mdc->decide_memslots = hv_balloon_decide_memslots; 177099a4706aSMaciej S. Szmigiero mdc->get_memslots = hv_balloon_get_memslots; 1771f77c5f38SDavid Hildenbrand mdc->get_min_alignment = hv_balloon_md_get_min_alignment; 177216dff2f9SMaciej S. Szmigiero mdc->fill_device_info = hv_balloon_md_fill_device_info; 17730d9e8c0bSMaciej S. Szmigiero } 1774