xref: /openbmc/qemu/hw/virtio/virtio-mem.c (revision cbad45511840077dafb6e1d1bc2e228baabecff5)
1910b2576SDavid Hildenbrand /*
2910b2576SDavid Hildenbrand  * Virtio MEM device
3910b2576SDavid Hildenbrand  *
4910b2576SDavid Hildenbrand  * Copyright (C) 2020 Red Hat, Inc.
5910b2576SDavid Hildenbrand  *
6910b2576SDavid Hildenbrand  * Authors:
7910b2576SDavid Hildenbrand  *  David Hildenbrand <david@redhat.com>
8910b2576SDavid Hildenbrand  *
9910b2576SDavid Hildenbrand  * This work is licensed under the terms of the GNU GPL, version 2.
10910b2576SDavid Hildenbrand  * See the COPYING file in the top-level directory.
11910b2576SDavid Hildenbrand  */
12910b2576SDavid Hildenbrand 
13910b2576SDavid Hildenbrand #include "qemu/osdep.h"
14910b2576SDavid Hildenbrand #include "qemu/iov.h"
15910b2576SDavid Hildenbrand #include "qemu/cutils.h"
16910b2576SDavid Hildenbrand #include "qemu/error-report.h"
17910b2576SDavid Hildenbrand #include "qemu/units.h"
18910b2576SDavid Hildenbrand #include "sysemu/numa.h"
19910b2576SDavid Hildenbrand #include "sysemu/sysemu.h"
20910b2576SDavid Hildenbrand #include "sysemu/reset.h"
21b01fd4b6SDavid Hildenbrand #include "sysemu/runstate.h"
22910b2576SDavid Hildenbrand #include "hw/virtio/virtio.h"
23910b2576SDavid Hildenbrand #include "hw/virtio/virtio-bus.h"
24910b2576SDavid Hildenbrand #include "hw/virtio/virtio-mem.h"
25910b2576SDavid Hildenbrand #include "qapi/error.h"
26910b2576SDavid Hildenbrand #include "qapi/visitor.h"
27910b2576SDavid Hildenbrand #include "exec/ram_addr.h"
28910b2576SDavid Hildenbrand #include "migration/misc.h"
29910b2576SDavid Hildenbrand #include "hw/boards.h"
30910b2576SDavid Hildenbrand #include "hw/qdev-properties.h"
312becc36aSPaolo Bonzini #include CONFIG_DEVICES
3243e54950SDavid Hildenbrand #include "trace.h"
33910b2576SDavid Hildenbrand 
343b95a71bSDavid Hildenbrand static const VMStateDescription vmstate_virtio_mem_device_early;
353b95a71bSDavid Hildenbrand 
36910b2576SDavid Hildenbrand /*
3723ad8decSDavid Hildenbrand  * We only had legacy x86 guests that did not support
3823ad8decSDavid Hildenbrand  * VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE. Other targets don't have legacy guests.
3923ad8decSDavid Hildenbrand  */
4023ad8decSDavid Hildenbrand #if defined(TARGET_X86_64) || defined(TARGET_I386)
4123ad8decSDavid Hildenbrand #define VIRTIO_MEM_HAS_LEGACY_GUESTS
4223ad8decSDavid Hildenbrand #endif
4323ad8decSDavid Hildenbrand 
4423ad8decSDavid Hildenbrand /*
45228957feSDavid Hildenbrand  * Let's not allow blocks smaller than 1 MiB, for example, to keep the tracking
46228957feSDavid Hildenbrand  * bitmap small.
47910b2576SDavid Hildenbrand  */
48228957feSDavid Hildenbrand #define VIRTIO_MEM_MIN_BLOCK_SIZE ((uint32_t)(1 * MiB))
49228957feSDavid Hildenbrand 
virtio_mem_default_thp_size(void)501263615eSGavin Shan static uint32_t virtio_mem_default_thp_size(void)
511263615eSGavin Shan {
521263615eSGavin Shan     uint32_t default_thp_size = VIRTIO_MEM_MIN_BLOCK_SIZE;
531263615eSGavin Shan 
541263615eSGavin Shan #if defined(__x86_64__) || defined(__arm__) || defined(__powerpc64__)
551263615eSGavin Shan     default_thp_size = 2 * MiB;
561263615eSGavin Shan #elif defined(__aarch64__)
578e3b0cbbSMarc-André Lureau     if (qemu_real_host_page_size() == 4 * KiB) {
581263615eSGavin Shan         default_thp_size = 2 * MiB;
598e3b0cbbSMarc-André Lureau     } else if (qemu_real_host_page_size() == 16 * KiB) {
601263615eSGavin Shan         default_thp_size = 32 * MiB;
618e3b0cbbSMarc-André Lureau     } else if (qemu_real_host_page_size() == 64 * KiB) {
621263615eSGavin Shan         default_thp_size = 512 * MiB;
631263615eSGavin Shan     }
64228957feSDavid Hildenbrand #endif
65228957feSDavid Hildenbrand 
661263615eSGavin Shan     return default_thp_size;
671263615eSGavin Shan }
681263615eSGavin Shan 
69228957feSDavid Hildenbrand /*
70177f9b1eSDavid Hildenbrand  * The minimum memslot size depends on this setting ("sane default"), the
71177f9b1eSDavid Hildenbrand  * device block size, and the memory backend page size. The last (or single)
72177f9b1eSDavid Hildenbrand  * memslot might be smaller than this constant.
73177f9b1eSDavid Hildenbrand  */
74177f9b1eSDavid Hildenbrand #define VIRTIO_MEM_MIN_MEMSLOT_SIZE (1 * GiB)
75177f9b1eSDavid Hildenbrand 
76177f9b1eSDavid Hildenbrand /*
77228957feSDavid Hildenbrand  * We want to have a reasonable default block size such that
78228957feSDavid Hildenbrand  * 1. We avoid splitting THPs when unplugging memory, which degrades
79228957feSDavid Hildenbrand  *    performance.
80228957feSDavid Hildenbrand  * 2. We avoid placing THPs for plugged blocks that also cover unplugged
81228957feSDavid Hildenbrand  *    blocks.
82228957feSDavid Hildenbrand  *
83228957feSDavid Hildenbrand  * The actual THP size might differ between Linux kernels, so we try to probe
84228957feSDavid Hildenbrand  * it. In the future (if we ever run into issues regarding 2.), we might want
85228957feSDavid Hildenbrand  * to disable THP in case we fail to properly probe the THP size, or if the
86228957feSDavid Hildenbrand  * block size is configured smaller than the THP size.
87228957feSDavid Hildenbrand  */
88228957feSDavid Hildenbrand static uint32_t thp_size;
89228957feSDavid Hildenbrand 
90228957feSDavid Hildenbrand #define HPAGE_PMD_SIZE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
9195b717a8SDavid Hildenbrand #define HPAGE_PATH "/sys/kernel/mm/transparent_hugepage/"
virtio_mem_thp_size(void)92228957feSDavid Hildenbrand static uint32_t virtio_mem_thp_size(void)
93228957feSDavid Hildenbrand {
94228957feSDavid Hildenbrand     gchar *content = NULL;
95228957feSDavid Hildenbrand     const char *endptr;
96228957feSDavid Hildenbrand     uint64_t tmp;
97228957feSDavid Hildenbrand 
98228957feSDavid Hildenbrand     if (thp_size) {
99228957feSDavid Hildenbrand         return thp_size;
100228957feSDavid Hildenbrand     }
101228957feSDavid Hildenbrand 
10295b717a8SDavid Hildenbrand     /* No THP -> no restrictions. */
10395b717a8SDavid Hildenbrand     if (!g_file_test(HPAGE_PATH, G_FILE_TEST_EXISTS)) {
10495b717a8SDavid Hildenbrand         thp_size = VIRTIO_MEM_MIN_BLOCK_SIZE;
10595b717a8SDavid Hildenbrand         return thp_size;
10695b717a8SDavid Hildenbrand     }
10795b717a8SDavid Hildenbrand 
108228957feSDavid Hildenbrand     /*
109228957feSDavid Hildenbrand      * Try to probe the actual THP size, fallback to (sane but eventually
110228957feSDavid Hildenbrand      * incorrect) default sizes.
111228957feSDavid Hildenbrand      */
112228957feSDavid Hildenbrand     if (g_file_get_contents(HPAGE_PMD_SIZE_PATH, &content, NULL, NULL) &&
113228957feSDavid Hildenbrand         !qemu_strtou64(content, &endptr, 0, &tmp) &&
114228957feSDavid Hildenbrand         (!endptr || *endptr == '\n')) {
1151263615eSGavin Shan         /* Sanity-check the value and fallback to something reasonable. */
1161263615eSGavin Shan         if (!tmp || !is_power_of_2(tmp)) {
117228957feSDavid Hildenbrand             warn_report("Read unsupported THP size: %" PRIx64, tmp);
118228957feSDavid Hildenbrand         } else {
119228957feSDavid Hildenbrand             thp_size = tmp;
120228957feSDavid Hildenbrand         }
121228957feSDavid Hildenbrand     }
122228957feSDavid Hildenbrand 
123228957feSDavid Hildenbrand     if (!thp_size) {
1241263615eSGavin Shan         thp_size = virtio_mem_default_thp_size();
125228957feSDavid Hildenbrand         warn_report("Could not detect THP size, falling back to %" PRIx64
126228957feSDavid Hildenbrand                     "  MiB.", thp_size / MiB);
127228957feSDavid Hildenbrand     }
128228957feSDavid Hildenbrand 
129228957feSDavid Hildenbrand     g_free(content);
130228957feSDavid Hildenbrand     return thp_size;
131228957feSDavid Hildenbrand }
132228957feSDavid Hildenbrand 
virtio_mem_default_block_size(RAMBlock * rb)133228957feSDavid Hildenbrand static uint64_t virtio_mem_default_block_size(RAMBlock *rb)
134228957feSDavid Hildenbrand {
135228957feSDavid Hildenbrand     const uint64_t page_size = qemu_ram_pagesize(rb);
136228957feSDavid Hildenbrand 
137228957feSDavid Hildenbrand     /* We can have hugetlbfs with a page size smaller than the THP size. */
1388e3b0cbbSMarc-André Lureau     if (page_size == qemu_real_host_page_size()) {
139228957feSDavid Hildenbrand         return MAX(page_size, virtio_mem_thp_size());
140228957feSDavid Hildenbrand     }
141228957feSDavid Hildenbrand     return MAX(page_size, VIRTIO_MEM_MIN_BLOCK_SIZE);
142228957feSDavid Hildenbrand }
143228957feSDavid Hildenbrand 
14423ad8decSDavid Hildenbrand #if defined(VIRTIO_MEM_HAS_LEGACY_GUESTS)
virtio_mem_has_shared_zeropage(RAMBlock * rb)14523ad8decSDavid Hildenbrand static bool virtio_mem_has_shared_zeropage(RAMBlock *rb)
14623ad8decSDavid Hildenbrand {
14723ad8decSDavid Hildenbrand     /*
14823ad8decSDavid Hildenbrand      * We only have a guaranteed shared zeropage on ordinary MAP_PRIVATE
14923ad8decSDavid Hildenbrand      * anonymous RAM. In any other case, reading unplugged *can* populate a
15023ad8decSDavid Hildenbrand      * fresh page, consuming actual memory.
15123ad8decSDavid Hildenbrand      */
15221e64350SPhilippe Mathieu-Daudé     return !qemu_ram_is_shared(rb) && qemu_ram_get_fd(rb) < 0 &&
1538e3b0cbbSMarc-André Lureau            qemu_ram_pagesize(rb) == qemu_real_host_page_size();
15423ad8decSDavid Hildenbrand }
15523ad8decSDavid Hildenbrand #endif /* VIRTIO_MEM_HAS_LEGACY_GUESTS */
15623ad8decSDavid Hildenbrand 
157910b2576SDavid Hildenbrand /*
158910b2576SDavid Hildenbrand  * Size the usable region bigger than the requested size if possible. Esp.
159910b2576SDavid Hildenbrand  * Linux guests will only add (aligned) memory blocks in case they fully
160910b2576SDavid Hildenbrand  * fit into the usable region, but plug+online only a subset of the pages.
161910b2576SDavid Hildenbrand  * The memory block size corresponds mostly to the section size.
162910b2576SDavid Hildenbrand  *
163910b2576SDavid Hildenbrand  * This allows e.g., to add 20MB with a section size of 128MB on x86_64, and
164b1b87327SGavin Shan  * a section size of 512MB on arm64 (as long as the start address is properly
165910b2576SDavid Hildenbrand  * aligned, similar to ordinary DIMMs).
166910b2576SDavid Hildenbrand  *
167910b2576SDavid Hildenbrand  * We can change this at any time and maybe even make it configurable if
168910b2576SDavid Hildenbrand  * necessary (as the section size can change). But it's more likely that the
169910b2576SDavid Hildenbrand  * section size will rather get smaller and not bigger over time.
170910b2576SDavid Hildenbrand  */
171910b2576SDavid Hildenbrand #if defined(TARGET_X86_64) || defined(TARGET_I386)
172910b2576SDavid Hildenbrand #define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB))
173b1b87327SGavin Shan #elif defined(TARGET_ARM)
174b1b87327SGavin Shan #define VIRTIO_MEM_USABLE_EXTENT (2 * (512 * MiB))
175910b2576SDavid Hildenbrand #else
176910b2576SDavid Hildenbrand #error VIRTIO_MEM_USABLE_EXTENT not defined
177910b2576SDavid Hildenbrand #endif
178910b2576SDavid Hildenbrand 
virtio_mem_is_busy(void)179910b2576SDavid Hildenbrand static bool virtio_mem_is_busy(void)
180910b2576SDavid Hildenbrand {
181910b2576SDavid Hildenbrand     /*
182910b2576SDavid Hildenbrand      * Postcopy cannot handle concurrent discards and we don't want to migrate
183910b2576SDavid Hildenbrand      * pages on-demand with stale content when plugging new blocks.
1840bc7806cSDavid Hildenbrand      *
1850bc7806cSDavid Hildenbrand      * For precopy, we don't want unplugged blocks in our migration stream, and
1860bc7806cSDavid Hildenbrand      * when plugging new blocks, the page content might differ between source
1870bc7806cSDavid Hildenbrand      * and destination (observable by the guest when not initializing pages
1880bc7806cSDavid Hildenbrand      * after plugging them) until we're running on the destination (as we didn't
1890bc7806cSDavid Hildenbrand      * migrate these blocks when they were unplugged).
190910b2576SDavid Hildenbrand      */
191*34a8892dSPeter Xu     return migration_in_incoming_postcopy() || migration_is_running();
192910b2576SDavid Hildenbrand }
193910b2576SDavid Hildenbrand 
194a45171dbSDavid Hildenbrand typedef int (*virtio_mem_range_cb)(VirtIOMEM *vmem, void *arg,
1957a9d5d02SDavid Hildenbrand                                    uint64_t offset, uint64_t size);
1967a9d5d02SDavid Hildenbrand 
virtio_mem_for_each_unplugged_range(VirtIOMEM * vmem,void * arg,virtio_mem_range_cb cb)197a45171dbSDavid Hildenbrand static int virtio_mem_for_each_unplugged_range(VirtIOMEM *vmem, void *arg,
1987a9d5d02SDavid Hildenbrand                                                virtio_mem_range_cb cb)
1997a9d5d02SDavid Hildenbrand {
2007a9d5d02SDavid Hildenbrand     unsigned long first_zero_bit, last_zero_bit;
2017a9d5d02SDavid Hildenbrand     uint64_t offset, size;
2027a9d5d02SDavid Hildenbrand     int ret = 0;
2037a9d5d02SDavid Hildenbrand 
2047a9d5d02SDavid Hildenbrand     first_zero_bit = find_first_zero_bit(vmem->bitmap, vmem->bitmap_size);
2057a9d5d02SDavid Hildenbrand     while (first_zero_bit < vmem->bitmap_size) {
2067a9d5d02SDavid Hildenbrand         offset = first_zero_bit * vmem->block_size;
2077a9d5d02SDavid Hildenbrand         last_zero_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size,
2087a9d5d02SDavid Hildenbrand                                       first_zero_bit + 1) - 1;
2097a9d5d02SDavid Hildenbrand         size = (last_zero_bit - first_zero_bit + 1) * vmem->block_size;
2107a9d5d02SDavid Hildenbrand 
2117a9d5d02SDavid Hildenbrand         ret = cb(vmem, arg, offset, size);
2127a9d5d02SDavid Hildenbrand         if (ret) {
2137a9d5d02SDavid Hildenbrand             break;
2147a9d5d02SDavid Hildenbrand         }
2157a9d5d02SDavid Hildenbrand         first_zero_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size,
2167a9d5d02SDavid Hildenbrand                                             last_zero_bit + 2);
2177a9d5d02SDavid Hildenbrand     }
2187a9d5d02SDavid Hildenbrand     return ret;
2197a9d5d02SDavid Hildenbrand }
2207a9d5d02SDavid Hildenbrand 
virtio_mem_for_each_plugged_range(VirtIOMEM * vmem,void * arg,virtio_mem_range_cb cb)221a45171dbSDavid Hildenbrand static int virtio_mem_for_each_plugged_range(VirtIOMEM *vmem, void *arg,
222d71920d4SDavid Hildenbrand                                              virtio_mem_range_cb cb)
223d71920d4SDavid Hildenbrand {
224d71920d4SDavid Hildenbrand     unsigned long first_bit, last_bit;
225d71920d4SDavid Hildenbrand     uint64_t offset, size;
226d71920d4SDavid Hildenbrand     int ret = 0;
227d71920d4SDavid Hildenbrand 
228d71920d4SDavid Hildenbrand     first_bit = find_first_bit(vmem->bitmap, vmem->bitmap_size);
229d71920d4SDavid Hildenbrand     while (first_bit < vmem->bitmap_size) {
230d71920d4SDavid Hildenbrand         offset = first_bit * vmem->block_size;
231d71920d4SDavid Hildenbrand         last_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size,
232d71920d4SDavid Hildenbrand                                       first_bit + 1) - 1;
233d71920d4SDavid Hildenbrand         size = (last_bit - first_bit + 1) * vmem->block_size;
234d71920d4SDavid Hildenbrand 
235d71920d4SDavid Hildenbrand         ret = cb(vmem, arg, offset, size);
236d71920d4SDavid Hildenbrand         if (ret) {
237d71920d4SDavid Hildenbrand             break;
238d71920d4SDavid Hildenbrand         }
239d71920d4SDavid Hildenbrand         first_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size,
240d71920d4SDavid Hildenbrand                                   last_bit + 2);
241d71920d4SDavid Hildenbrand     }
242d71920d4SDavid Hildenbrand     return ret;
243d71920d4SDavid Hildenbrand }
244d71920d4SDavid Hildenbrand 
2452044969fSDavid Hildenbrand /*
2462044969fSDavid Hildenbrand  * Adjust the memory section to cover the intersection with the given range.
2472044969fSDavid Hildenbrand  *
2482044969fSDavid Hildenbrand  * Returns false if the intersection is empty, otherwise returns true.
2492044969fSDavid Hildenbrand  */
virtio_mem_intersect_memory_section(MemoryRegionSection * s,uint64_t offset,uint64_t size)25082ba778eSPhilippe Mathieu-Daudé static bool virtio_mem_intersect_memory_section(MemoryRegionSection *s,
2512044969fSDavid Hildenbrand                                                 uint64_t offset, uint64_t size)
2522044969fSDavid Hildenbrand {
2532044969fSDavid Hildenbrand     uint64_t start = MAX(s->offset_within_region, offset);
2542044969fSDavid Hildenbrand     uint64_t end = MIN(s->offset_within_region + int128_get64(s->size),
2552044969fSDavid Hildenbrand                        offset + size);
2562044969fSDavid Hildenbrand 
2572044969fSDavid Hildenbrand     if (end <= start) {
2582044969fSDavid Hildenbrand         return false;
2592044969fSDavid Hildenbrand     }
2602044969fSDavid Hildenbrand 
2612044969fSDavid Hildenbrand     s->offset_within_address_space += start - s->offset_within_region;
2622044969fSDavid Hildenbrand     s->offset_within_region = start;
2632044969fSDavid Hildenbrand     s->size = int128_make64(end - start);
2642044969fSDavid Hildenbrand     return true;
2652044969fSDavid Hildenbrand }
2662044969fSDavid Hildenbrand 
2672044969fSDavid Hildenbrand typedef int (*virtio_mem_section_cb)(MemoryRegionSection *s, void *arg);
2682044969fSDavid Hildenbrand 
virtio_mem_for_each_plugged_section(const VirtIOMEM * vmem,MemoryRegionSection * s,void * arg,virtio_mem_section_cb cb)2692044969fSDavid Hildenbrand static int virtio_mem_for_each_plugged_section(const VirtIOMEM *vmem,
2702044969fSDavid Hildenbrand                                                MemoryRegionSection *s,
2712044969fSDavid Hildenbrand                                                void *arg,
2722044969fSDavid Hildenbrand                                                virtio_mem_section_cb cb)
2732044969fSDavid Hildenbrand {
2742044969fSDavid Hildenbrand     unsigned long first_bit, last_bit;
2752044969fSDavid Hildenbrand     uint64_t offset, size;
2762044969fSDavid Hildenbrand     int ret = 0;
2772044969fSDavid Hildenbrand 
278b11cf32eSChenyi Qiang     first_bit = s->offset_within_region / vmem->block_size;
2792044969fSDavid Hildenbrand     first_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, first_bit);
2802044969fSDavid Hildenbrand     while (first_bit < vmem->bitmap_size) {
2812044969fSDavid Hildenbrand         MemoryRegionSection tmp = *s;
2822044969fSDavid Hildenbrand 
2832044969fSDavid Hildenbrand         offset = first_bit * vmem->block_size;
2842044969fSDavid Hildenbrand         last_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size,
2852044969fSDavid Hildenbrand                                       first_bit + 1) - 1;
2862044969fSDavid Hildenbrand         size = (last_bit - first_bit + 1) * vmem->block_size;
2872044969fSDavid Hildenbrand 
28882ba778eSPhilippe Mathieu-Daudé         if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) {
2892044969fSDavid Hildenbrand             break;
2902044969fSDavid Hildenbrand         }
2912044969fSDavid Hildenbrand         ret = cb(&tmp, arg);
2922044969fSDavid Hildenbrand         if (ret) {
2932044969fSDavid Hildenbrand             break;
2942044969fSDavid Hildenbrand         }
2952044969fSDavid Hildenbrand         first_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size,
2962044969fSDavid Hildenbrand                                   last_bit + 2);
2972044969fSDavid Hildenbrand     }
2982044969fSDavid Hildenbrand     return ret;
2992044969fSDavid Hildenbrand }
3002044969fSDavid Hildenbrand 
virtio_mem_for_each_unplugged_section(const VirtIOMEM * vmem,MemoryRegionSection * s,void * arg,virtio_mem_section_cb cb)301372aa6fdSDavid Hildenbrand static int virtio_mem_for_each_unplugged_section(const VirtIOMEM *vmem,
302372aa6fdSDavid Hildenbrand                                                  MemoryRegionSection *s,
303372aa6fdSDavid Hildenbrand                                                  void *arg,
304372aa6fdSDavid Hildenbrand                                                  virtio_mem_section_cb cb)
305372aa6fdSDavid Hildenbrand {
306372aa6fdSDavid Hildenbrand     unsigned long first_bit, last_bit;
307372aa6fdSDavid Hildenbrand     uint64_t offset, size;
308372aa6fdSDavid Hildenbrand     int ret = 0;
309372aa6fdSDavid Hildenbrand 
310b11cf32eSChenyi Qiang     first_bit = s->offset_within_region / vmem->block_size;
311372aa6fdSDavid Hildenbrand     first_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, first_bit);
312372aa6fdSDavid Hildenbrand     while (first_bit < vmem->bitmap_size) {
313372aa6fdSDavid Hildenbrand         MemoryRegionSection tmp = *s;
314372aa6fdSDavid Hildenbrand 
315372aa6fdSDavid Hildenbrand         offset = first_bit * vmem->block_size;
316372aa6fdSDavid Hildenbrand         last_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size,
317372aa6fdSDavid Hildenbrand                                  first_bit + 1) - 1;
318372aa6fdSDavid Hildenbrand         size = (last_bit - first_bit + 1) * vmem->block_size;
319372aa6fdSDavid Hildenbrand 
32082ba778eSPhilippe Mathieu-Daudé         if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) {
321372aa6fdSDavid Hildenbrand             break;
322372aa6fdSDavid Hildenbrand         }
323372aa6fdSDavid Hildenbrand         ret = cb(&tmp, arg);
324372aa6fdSDavid Hildenbrand         if (ret) {
325372aa6fdSDavid Hildenbrand             break;
326372aa6fdSDavid Hildenbrand         }
327372aa6fdSDavid Hildenbrand         first_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size,
328372aa6fdSDavid Hildenbrand                                        last_bit + 2);
329372aa6fdSDavid Hildenbrand     }
330372aa6fdSDavid Hildenbrand     return ret;
331372aa6fdSDavid Hildenbrand }
332372aa6fdSDavid Hildenbrand 
virtio_mem_notify_populate_cb(MemoryRegionSection * s,void * arg)3332044969fSDavid Hildenbrand static int virtio_mem_notify_populate_cb(MemoryRegionSection *s, void *arg)
3342044969fSDavid Hildenbrand {
3352044969fSDavid Hildenbrand     RamDiscardListener *rdl = arg;
3362044969fSDavid Hildenbrand 
3372044969fSDavid Hildenbrand     return rdl->notify_populate(rdl, s);
3382044969fSDavid Hildenbrand }
3392044969fSDavid Hildenbrand 
virtio_mem_notify_discard_cb(MemoryRegionSection * s,void * arg)3402044969fSDavid Hildenbrand static int virtio_mem_notify_discard_cb(MemoryRegionSection *s, void *arg)
3412044969fSDavid Hildenbrand {
3422044969fSDavid Hildenbrand     RamDiscardListener *rdl = arg;
3432044969fSDavid Hildenbrand 
3442044969fSDavid Hildenbrand     rdl->notify_discard(rdl, s);
3452044969fSDavid Hildenbrand     return 0;
3462044969fSDavid Hildenbrand }
3472044969fSDavid Hildenbrand 
virtio_mem_notify_unplug(VirtIOMEM * vmem,uint64_t offset,uint64_t size)3482044969fSDavid Hildenbrand static void virtio_mem_notify_unplug(VirtIOMEM *vmem, uint64_t offset,
3492044969fSDavid Hildenbrand                                      uint64_t size)
3502044969fSDavid Hildenbrand {
3512044969fSDavid Hildenbrand     RamDiscardListener *rdl;
3522044969fSDavid Hildenbrand 
3532044969fSDavid Hildenbrand     QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
3542044969fSDavid Hildenbrand         MemoryRegionSection tmp = *rdl->section;
3552044969fSDavid Hildenbrand 
35682ba778eSPhilippe Mathieu-Daudé         if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) {
3572044969fSDavid Hildenbrand             continue;
3582044969fSDavid Hildenbrand         }
3592044969fSDavid Hildenbrand         rdl->notify_discard(rdl, &tmp);
3602044969fSDavid Hildenbrand     }
3612044969fSDavid Hildenbrand }
3622044969fSDavid Hildenbrand 
virtio_mem_notify_plug(VirtIOMEM * vmem,uint64_t offset,uint64_t size)3632044969fSDavid Hildenbrand static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset,
3642044969fSDavid Hildenbrand                                   uint64_t size)
3652044969fSDavid Hildenbrand {
3662044969fSDavid Hildenbrand     RamDiscardListener *rdl, *rdl2;
3672044969fSDavid Hildenbrand     int ret = 0;
3682044969fSDavid Hildenbrand 
3692044969fSDavid Hildenbrand     QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
3702044969fSDavid Hildenbrand         MemoryRegionSection tmp = *rdl->section;
3712044969fSDavid Hildenbrand 
37282ba778eSPhilippe Mathieu-Daudé         if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) {
3732044969fSDavid Hildenbrand             continue;
3742044969fSDavid Hildenbrand         }
3752044969fSDavid Hildenbrand         ret = rdl->notify_populate(rdl, &tmp);
3762044969fSDavid Hildenbrand         if (ret) {
3772044969fSDavid Hildenbrand             break;
3782044969fSDavid Hildenbrand         }
3792044969fSDavid Hildenbrand     }
3802044969fSDavid Hildenbrand 
3812044969fSDavid Hildenbrand     if (ret) {
3822044969fSDavid Hildenbrand         /* Notify all already-notified listeners. */
3832044969fSDavid Hildenbrand         QLIST_FOREACH(rdl2, &vmem->rdl_list, next) {
38429f1b328SChenyi Qiang             MemoryRegionSection tmp = *rdl2->section;
3852044969fSDavid Hildenbrand 
3862044969fSDavid Hildenbrand             if (rdl2 == rdl) {
3872044969fSDavid Hildenbrand                 break;
3882044969fSDavid Hildenbrand             }
38982ba778eSPhilippe Mathieu-Daudé             if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) {
3902044969fSDavid Hildenbrand                 continue;
3912044969fSDavid Hildenbrand             }
3922044969fSDavid Hildenbrand             rdl2->notify_discard(rdl2, &tmp);
3932044969fSDavid Hildenbrand         }
3942044969fSDavid Hildenbrand     }
3952044969fSDavid Hildenbrand     return ret;
3962044969fSDavid Hildenbrand }
3972044969fSDavid Hildenbrand 
virtio_mem_notify_unplug_all(VirtIOMEM * vmem)3982044969fSDavid Hildenbrand static void virtio_mem_notify_unplug_all(VirtIOMEM *vmem)
3992044969fSDavid Hildenbrand {
4002044969fSDavid Hildenbrand     RamDiscardListener *rdl;
4012044969fSDavid Hildenbrand 
4022044969fSDavid Hildenbrand     if (!vmem->size) {
4032044969fSDavid Hildenbrand         return;
4042044969fSDavid Hildenbrand     }
4052044969fSDavid Hildenbrand 
4062044969fSDavid Hildenbrand     QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
4072044969fSDavid Hildenbrand         if (rdl->double_discard_supported) {
4082044969fSDavid Hildenbrand             rdl->notify_discard(rdl, rdl->section);
4092044969fSDavid Hildenbrand         } else {
4102044969fSDavid Hildenbrand             virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
4112044969fSDavid Hildenbrand                                                 virtio_mem_notify_discard_cb);
4122044969fSDavid Hildenbrand         }
4132044969fSDavid Hildenbrand     }
4142044969fSDavid Hildenbrand }
4152044969fSDavid Hildenbrand 
virtio_mem_is_range_plugged(const VirtIOMEM * vmem,uint64_t start_gpa,uint64_t size)41625c89303SDavid Hildenbrand static bool virtio_mem_is_range_plugged(const VirtIOMEM *vmem,
41725c89303SDavid Hildenbrand                                         uint64_t start_gpa, uint64_t size)
418910b2576SDavid Hildenbrand {
419910b2576SDavid Hildenbrand     const unsigned long first_bit = (start_gpa - vmem->addr) / vmem->block_size;
420910b2576SDavid Hildenbrand     const unsigned long last_bit = first_bit + (size / vmem->block_size) - 1;
421910b2576SDavid Hildenbrand     unsigned long found_bit;
422910b2576SDavid Hildenbrand 
423910b2576SDavid Hildenbrand     /* We fake a shorter bitmap to avoid searching too far. */
424910b2576SDavid Hildenbrand     found_bit = find_next_zero_bit(vmem->bitmap, last_bit + 1, first_bit);
425910b2576SDavid Hildenbrand     return found_bit > last_bit;
426910b2576SDavid Hildenbrand }
427910b2576SDavid Hildenbrand 
virtio_mem_is_range_unplugged(const VirtIOMEM * vmem,uint64_t start_gpa,uint64_t size)42825c89303SDavid Hildenbrand static bool virtio_mem_is_range_unplugged(const VirtIOMEM *vmem,
42925c89303SDavid Hildenbrand                                           uint64_t start_gpa, uint64_t size)
43025c89303SDavid Hildenbrand {
43125c89303SDavid Hildenbrand     const unsigned long first_bit = (start_gpa - vmem->addr) / vmem->block_size;
43225c89303SDavid Hildenbrand     const unsigned long last_bit = first_bit + (size / vmem->block_size) - 1;
43325c89303SDavid Hildenbrand     unsigned long found_bit;
43425c89303SDavid Hildenbrand 
43525c89303SDavid Hildenbrand     /* We fake a shorter bitmap to avoid searching too far. */
43625c89303SDavid Hildenbrand     found_bit = find_next_bit(vmem->bitmap, last_bit + 1, first_bit);
43725c89303SDavid Hildenbrand     return found_bit > last_bit;
43825c89303SDavid Hildenbrand }
43925c89303SDavid Hildenbrand 
virtio_mem_set_range_plugged(VirtIOMEM * vmem,uint64_t start_gpa,uint64_t size)44025c89303SDavid Hildenbrand static void virtio_mem_set_range_plugged(VirtIOMEM *vmem, uint64_t start_gpa,
44125c89303SDavid Hildenbrand                                          uint64_t size)
442910b2576SDavid Hildenbrand {
443910b2576SDavid Hildenbrand     const unsigned long bit = (start_gpa - vmem->addr) / vmem->block_size;
444910b2576SDavid Hildenbrand     const unsigned long nbits = size / vmem->block_size;
445910b2576SDavid Hildenbrand 
446910b2576SDavid Hildenbrand     bitmap_set(vmem->bitmap, bit, nbits);
447910b2576SDavid Hildenbrand }
44825c89303SDavid Hildenbrand 
virtio_mem_set_range_unplugged(VirtIOMEM * vmem,uint64_t start_gpa,uint64_t size)44925c89303SDavid Hildenbrand static void virtio_mem_set_range_unplugged(VirtIOMEM *vmem, uint64_t start_gpa,
45025c89303SDavid Hildenbrand                                            uint64_t size)
45125c89303SDavid Hildenbrand {
45225c89303SDavid Hildenbrand     const unsigned long bit = (start_gpa - vmem->addr) / vmem->block_size;
45325c89303SDavid Hildenbrand     const unsigned long nbits = size / vmem->block_size;
45425c89303SDavid Hildenbrand 
45525c89303SDavid Hildenbrand     bitmap_clear(vmem->bitmap, bit, nbits);
456910b2576SDavid Hildenbrand }
457910b2576SDavid Hildenbrand 
virtio_mem_send_response(VirtIOMEM * vmem,VirtQueueElement * elem,struct virtio_mem_resp * resp)458910b2576SDavid Hildenbrand static void virtio_mem_send_response(VirtIOMEM *vmem, VirtQueueElement *elem,
459910b2576SDavid Hildenbrand                                      struct virtio_mem_resp *resp)
460910b2576SDavid Hildenbrand {
461910b2576SDavid Hildenbrand     VirtIODevice *vdev = VIRTIO_DEVICE(vmem);
462910b2576SDavid Hildenbrand     VirtQueue *vq = vmem->vq;
463910b2576SDavid Hildenbrand 
46443e54950SDavid Hildenbrand     trace_virtio_mem_send_response(le16_to_cpu(resp->type));
465910b2576SDavid Hildenbrand     iov_from_buf(elem->in_sg, elem->in_num, 0, resp, sizeof(*resp));
466910b2576SDavid Hildenbrand 
467910b2576SDavid Hildenbrand     virtqueue_push(vq, elem, sizeof(*resp));
468910b2576SDavid Hildenbrand     virtio_notify(vdev, vq);
469910b2576SDavid Hildenbrand }
470910b2576SDavid Hildenbrand 
virtio_mem_send_response_simple(VirtIOMEM * vmem,VirtQueueElement * elem,uint16_t type)471910b2576SDavid Hildenbrand static void virtio_mem_send_response_simple(VirtIOMEM *vmem,
472910b2576SDavid Hildenbrand                                             VirtQueueElement *elem,
473910b2576SDavid Hildenbrand                                             uint16_t type)
474910b2576SDavid Hildenbrand {
475910b2576SDavid Hildenbrand     struct virtio_mem_resp resp = {
476910b2576SDavid Hildenbrand         .type = cpu_to_le16(type),
477910b2576SDavid Hildenbrand     };
478910b2576SDavid Hildenbrand 
479910b2576SDavid Hildenbrand     virtio_mem_send_response(vmem, elem, &resp);
480910b2576SDavid Hildenbrand }
481910b2576SDavid Hildenbrand 
virtio_mem_valid_range(const VirtIOMEM * vmem,uint64_t gpa,uint64_t size)4822044969fSDavid Hildenbrand static bool virtio_mem_valid_range(const VirtIOMEM *vmem, uint64_t gpa,
4832044969fSDavid Hildenbrand                                    uint64_t size)
484910b2576SDavid Hildenbrand {
485910b2576SDavid Hildenbrand     if (!QEMU_IS_ALIGNED(gpa, vmem->block_size)) {
486910b2576SDavid Hildenbrand         return false;
487910b2576SDavid Hildenbrand     }
488910b2576SDavid Hildenbrand     if (gpa + size < gpa || !size) {
489910b2576SDavid Hildenbrand         return false;
490910b2576SDavid Hildenbrand     }
491910b2576SDavid Hildenbrand     if (gpa < vmem->addr || gpa >= vmem->addr + vmem->usable_region_size) {
492910b2576SDavid Hildenbrand         return false;
493910b2576SDavid Hildenbrand     }
494910b2576SDavid Hildenbrand     if (gpa + size > vmem->addr + vmem->usable_region_size) {
495910b2576SDavid Hildenbrand         return false;
496910b2576SDavid Hildenbrand     }
497910b2576SDavid Hildenbrand     return true;
498910b2576SDavid Hildenbrand }
499910b2576SDavid Hildenbrand 
virtio_mem_activate_memslot(VirtIOMEM * vmem,unsigned int idx)500177f9b1eSDavid Hildenbrand static void virtio_mem_activate_memslot(VirtIOMEM *vmem, unsigned int idx)
501177f9b1eSDavid Hildenbrand {
502177f9b1eSDavid Hildenbrand     const uint64_t memslot_offset = idx * vmem->memslot_size;
503177f9b1eSDavid Hildenbrand 
504177f9b1eSDavid Hildenbrand     assert(vmem->memslots);
505177f9b1eSDavid Hildenbrand 
506177f9b1eSDavid Hildenbrand     /*
507177f9b1eSDavid Hildenbrand      * Instead of enabling/disabling memslots, we add/remove them. This should
508177f9b1eSDavid Hildenbrand      * make address space updates faster, because we don't have to loop over
509177f9b1eSDavid Hildenbrand      * many disabled subregions.
510177f9b1eSDavid Hildenbrand      */
511177f9b1eSDavid Hildenbrand     if (memory_region_is_mapped(&vmem->memslots[idx])) {
512177f9b1eSDavid Hildenbrand         return;
513177f9b1eSDavid Hildenbrand     }
514177f9b1eSDavid Hildenbrand     memory_region_add_subregion(vmem->mr, memslot_offset, &vmem->memslots[idx]);
515177f9b1eSDavid Hildenbrand }
516177f9b1eSDavid Hildenbrand 
virtio_mem_deactivate_memslot(VirtIOMEM * vmem,unsigned int idx)517177f9b1eSDavid Hildenbrand static void virtio_mem_deactivate_memslot(VirtIOMEM *vmem, unsigned int idx)
518177f9b1eSDavid Hildenbrand {
519177f9b1eSDavid Hildenbrand     assert(vmem->memslots);
520177f9b1eSDavid Hildenbrand 
521177f9b1eSDavid Hildenbrand     if (!memory_region_is_mapped(&vmem->memslots[idx])) {
522177f9b1eSDavid Hildenbrand         return;
523177f9b1eSDavid Hildenbrand     }
524177f9b1eSDavid Hildenbrand     memory_region_del_subregion(vmem->mr, &vmem->memslots[idx]);
525177f9b1eSDavid Hildenbrand }
526177f9b1eSDavid Hildenbrand 
virtio_mem_activate_memslots_to_plug(VirtIOMEM * vmem,uint64_t offset,uint64_t size)527177f9b1eSDavid Hildenbrand static void virtio_mem_activate_memslots_to_plug(VirtIOMEM *vmem,
528177f9b1eSDavid Hildenbrand                                                  uint64_t offset, uint64_t size)
529177f9b1eSDavid Hildenbrand {
530177f9b1eSDavid Hildenbrand     const unsigned int start_idx = offset / vmem->memslot_size;
531177f9b1eSDavid Hildenbrand     const unsigned int end_idx = (offset + size + vmem->memslot_size - 1) /
532177f9b1eSDavid Hildenbrand                                  vmem->memslot_size;
533177f9b1eSDavid Hildenbrand     unsigned int idx;
534177f9b1eSDavid Hildenbrand 
535364eff68SDavid Hildenbrand     assert(vmem->dynamic_memslots);
536177f9b1eSDavid Hildenbrand 
537177f9b1eSDavid Hildenbrand     /* Activate all involved memslots in a single transaction. */
538177f9b1eSDavid Hildenbrand     memory_region_transaction_begin();
539177f9b1eSDavid Hildenbrand     for (idx = start_idx; idx < end_idx; idx++) {
540177f9b1eSDavid Hildenbrand         virtio_mem_activate_memslot(vmem, idx);
541177f9b1eSDavid Hildenbrand     }
542177f9b1eSDavid Hildenbrand     memory_region_transaction_commit();
543177f9b1eSDavid Hildenbrand }
544177f9b1eSDavid Hildenbrand 
virtio_mem_deactivate_unplugged_memslots(VirtIOMEM * vmem,uint64_t offset,uint64_t size)545177f9b1eSDavid Hildenbrand static void virtio_mem_deactivate_unplugged_memslots(VirtIOMEM *vmem,
546177f9b1eSDavid Hildenbrand                                                      uint64_t offset,
547177f9b1eSDavid Hildenbrand                                                      uint64_t size)
548177f9b1eSDavid Hildenbrand {
549177f9b1eSDavid Hildenbrand     const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
550177f9b1eSDavid Hildenbrand     const unsigned int start_idx = offset / vmem->memslot_size;
551177f9b1eSDavid Hildenbrand     const unsigned int end_idx = (offset + size + vmem->memslot_size - 1) /
552177f9b1eSDavid Hildenbrand                                  vmem->memslot_size;
553177f9b1eSDavid Hildenbrand     unsigned int idx;
554177f9b1eSDavid Hildenbrand 
555364eff68SDavid Hildenbrand     assert(vmem->dynamic_memslots);
556177f9b1eSDavid Hildenbrand 
557177f9b1eSDavid Hildenbrand     /* Deactivate all memslots with unplugged blocks in a single transaction. */
558177f9b1eSDavid Hildenbrand     memory_region_transaction_begin();
559177f9b1eSDavid Hildenbrand     for (idx = start_idx; idx < end_idx; idx++) {
560177f9b1eSDavid Hildenbrand         const uint64_t memslot_offset = idx * vmem->memslot_size;
561177f9b1eSDavid Hildenbrand         uint64_t memslot_size = vmem->memslot_size;
562177f9b1eSDavid Hildenbrand 
563177f9b1eSDavid Hildenbrand         /* The size of the last memslot might be smaller. */
564177f9b1eSDavid Hildenbrand         if (idx == vmem->nb_memslots - 1) {
565177f9b1eSDavid Hildenbrand             memslot_size = region_size - memslot_offset;
566177f9b1eSDavid Hildenbrand         }
567177f9b1eSDavid Hildenbrand 
568177f9b1eSDavid Hildenbrand         /*
569177f9b1eSDavid Hildenbrand          * Partially covered memslots might still have some blocks plugged and
570177f9b1eSDavid Hildenbrand          * have to remain active if that's the case.
571177f9b1eSDavid Hildenbrand          */
572177f9b1eSDavid Hildenbrand         if (offset > memslot_offset ||
573177f9b1eSDavid Hildenbrand             offset + size < memslot_offset + memslot_size) {
574177f9b1eSDavid Hildenbrand             const uint64_t gpa = vmem->addr + memslot_offset;
575177f9b1eSDavid Hildenbrand 
576177f9b1eSDavid Hildenbrand             if (!virtio_mem_is_range_unplugged(vmem, gpa, memslot_size)) {
577177f9b1eSDavid Hildenbrand                 continue;
578177f9b1eSDavid Hildenbrand             }
579177f9b1eSDavid Hildenbrand         }
580177f9b1eSDavid Hildenbrand 
581177f9b1eSDavid Hildenbrand         virtio_mem_deactivate_memslot(vmem, idx);
582177f9b1eSDavid Hildenbrand     }
583177f9b1eSDavid Hildenbrand     memory_region_transaction_commit();
584177f9b1eSDavid Hildenbrand }
585177f9b1eSDavid Hildenbrand 
virtio_mem_set_block_state(VirtIOMEM * vmem,uint64_t start_gpa,uint64_t size,bool plug)586910b2576SDavid Hildenbrand static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa,
587910b2576SDavid Hildenbrand                                       uint64_t size, bool plug)
588910b2576SDavid Hildenbrand {
589910b2576SDavid Hildenbrand     const uint64_t offset = start_gpa - vmem->addr;
5903aca6380SDavid Hildenbrand     RAMBlock *rb = vmem->memdev->mr.ram_block;
59125c89303SDavid Hildenbrand     int ret = 0;
592910b2576SDavid Hildenbrand 
593910b2576SDavid Hildenbrand     if (virtio_mem_is_busy()) {
594910b2576SDavid Hildenbrand         return -EBUSY;
595910b2576SDavid Hildenbrand     }
596910b2576SDavid Hildenbrand 
597910b2576SDavid Hildenbrand     if (!plug) {
5983aca6380SDavid Hildenbrand         if (ram_block_discard_range(rb, offset, size)) {
599910b2576SDavid Hildenbrand             return -EBUSY;
600910b2576SDavid Hildenbrand         }
6012044969fSDavid Hildenbrand         virtio_mem_notify_unplug(vmem, offset, size);
60225c89303SDavid Hildenbrand         virtio_mem_set_range_unplugged(vmem, start_gpa, size);
603177f9b1eSDavid Hildenbrand         /* Deactivate completely unplugged memslots after updating the state. */
604364eff68SDavid Hildenbrand         if (vmem->dynamic_memslots) {
605177f9b1eSDavid Hildenbrand             virtio_mem_deactivate_unplugged_memslots(vmem, offset, size);
606364eff68SDavid Hildenbrand         }
60725c89303SDavid Hildenbrand         return 0;
60825c89303SDavid Hildenbrand     }
60909b3b7e0SDavid Hildenbrand 
61009b3b7e0SDavid Hildenbrand     if (vmem->prealloc) {
61109b3b7e0SDavid Hildenbrand         void *area = memory_region_get_ram_ptr(&vmem->memdev->mr) + offset;
61209b3b7e0SDavid Hildenbrand         int fd = memory_region_get_fd(&vmem->memdev->mr);
61309b3b7e0SDavid Hildenbrand         Error *local_err = NULL;
61409b3b7e0SDavid Hildenbrand 
61504accf43SMark Kanda         if (!qemu_prealloc_mem(fd, area, size, 1, NULL, false, &local_err)) {
61609b3b7e0SDavid Hildenbrand             static bool warned;
61709b3b7e0SDavid Hildenbrand 
61809b3b7e0SDavid Hildenbrand             /*
61909b3b7e0SDavid Hildenbrand              * Warn only once, we don't want to fill the log with these
62009b3b7e0SDavid Hildenbrand              * warnings.
62109b3b7e0SDavid Hildenbrand              */
62209b3b7e0SDavid Hildenbrand             if (!warned) {
62309b3b7e0SDavid Hildenbrand                 warn_report_err(local_err);
62409b3b7e0SDavid Hildenbrand                 warned = true;
62509b3b7e0SDavid Hildenbrand             } else {
62609b3b7e0SDavid Hildenbrand                 error_free(local_err);
62709b3b7e0SDavid Hildenbrand             }
62809b3b7e0SDavid Hildenbrand             ret = -EBUSY;
62909b3b7e0SDavid Hildenbrand         }
63009b3b7e0SDavid Hildenbrand     }
63125c89303SDavid Hildenbrand 
63209b3b7e0SDavid Hildenbrand     if (!ret) {
633177f9b1eSDavid Hildenbrand         /*
634177f9b1eSDavid Hildenbrand          * Activate before notifying and rollback in case of any errors.
635177f9b1eSDavid Hildenbrand          *
636177f9b1eSDavid Hildenbrand          * When activating a yet inactive memslot, memory notifiers will get
637177f9b1eSDavid Hildenbrand          * notified about the added memory region and can register with the
638177f9b1eSDavid Hildenbrand          * RamDiscardManager; this will traverse all plugged blocks and skip the
639177f9b1eSDavid Hildenbrand          * blocks we are plugging here. The following notification will inform
640177f9b1eSDavid Hildenbrand          * registered listeners about the blocks we're plugging.
641177f9b1eSDavid Hildenbrand          */
642364eff68SDavid Hildenbrand         if (vmem->dynamic_memslots) {
643177f9b1eSDavid Hildenbrand             virtio_mem_activate_memslots_to_plug(vmem, offset, size);
644364eff68SDavid Hildenbrand         }
64509b3b7e0SDavid Hildenbrand         ret = virtio_mem_notify_plug(vmem, offset, size);
646364eff68SDavid Hildenbrand         if (ret && vmem->dynamic_memslots) {
647177f9b1eSDavid Hildenbrand             virtio_mem_deactivate_unplugged_memslots(vmem, offset, size);
648177f9b1eSDavid Hildenbrand         }
64909b3b7e0SDavid Hildenbrand     }
65009b3b7e0SDavid Hildenbrand     if (ret) {
65109b3b7e0SDavid Hildenbrand         /* Could be preallocation or a notifier populated memory. */
6522044969fSDavid Hildenbrand         ram_block_discard_range(vmem->memdev->mr.ram_block, offset, size);
6532044969fSDavid Hildenbrand         return -EBUSY;
654910b2576SDavid Hildenbrand     }
65525c89303SDavid Hildenbrand 
65625c89303SDavid Hildenbrand     virtio_mem_set_range_plugged(vmem, start_gpa, size);
657910b2576SDavid Hildenbrand     return 0;
658910b2576SDavid Hildenbrand }
659910b2576SDavid Hildenbrand 
virtio_mem_state_change_request(VirtIOMEM * vmem,uint64_t gpa,uint16_t nb_blocks,bool plug)660910b2576SDavid Hildenbrand static int virtio_mem_state_change_request(VirtIOMEM *vmem, uint64_t gpa,
661910b2576SDavid Hildenbrand                                            uint16_t nb_blocks, bool plug)
662910b2576SDavid Hildenbrand {
663910b2576SDavid Hildenbrand     const uint64_t size = nb_blocks * vmem->block_size;
664910b2576SDavid Hildenbrand     int ret;
665910b2576SDavid Hildenbrand 
666910b2576SDavid Hildenbrand     if (!virtio_mem_valid_range(vmem, gpa, size)) {
667910b2576SDavid Hildenbrand         return VIRTIO_MEM_RESP_ERROR;
668910b2576SDavid Hildenbrand     }
669910b2576SDavid Hildenbrand 
670910b2576SDavid Hildenbrand     if (plug && (vmem->size + size > vmem->requested_size)) {
671910b2576SDavid Hildenbrand         return VIRTIO_MEM_RESP_NACK;
672910b2576SDavid Hildenbrand     }
673910b2576SDavid Hildenbrand 
674910b2576SDavid Hildenbrand     /* test if really all blocks are in the opposite state */
67525c89303SDavid Hildenbrand     if ((plug && !virtio_mem_is_range_unplugged(vmem, gpa, size)) ||
67625c89303SDavid Hildenbrand         (!plug && !virtio_mem_is_range_plugged(vmem, gpa, size))) {
677910b2576SDavid Hildenbrand         return VIRTIO_MEM_RESP_ERROR;
678910b2576SDavid Hildenbrand     }
679910b2576SDavid Hildenbrand 
680910b2576SDavid Hildenbrand     ret = virtio_mem_set_block_state(vmem, gpa, size, plug);
681910b2576SDavid Hildenbrand     if (ret) {
682910b2576SDavid Hildenbrand         return VIRTIO_MEM_RESP_BUSY;
683910b2576SDavid Hildenbrand     }
684910b2576SDavid Hildenbrand     if (plug) {
685910b2576SDavid Hildenbrand         vmem->size += size;
686910b2576SDavid Hildenbrand     } else {
687910b2576SDavid Hildenbrand         vmem->size -= size;
688910b2576SDavid Hildenbrand     }
689c95b4437SDavid Hildenbrand     notifier_list_notify(&vmem->size_change_notifiers, &vmem->size);
690910b2576SDavid Hildenbrand     return VIRTIO_MEM_RESP_ACK;
691910b2576SDavid Hildenbrand }
692910b2576SDavid Hildenbrand 
virtio_mem_plug_request(VirtIOMEM * vmem,VirtQueueElement * elem,struct virtio_mem_req * req)693910b2576SDavid Hildenbrand static void virtio_mem_plug_request(VirtIOMEM *vmem, VirtQueueElement *elem,
694910b2576SDavid Hildenbrand                                     struct virtio_mem_req *req)
695910b2576SDavid Hildenbrand {
696910b2576SDavid Hildenbrand     const uint64_t gpa = le64_to_cpu(req->u.plug.addr);
697910b2576SDavid Hildenbrand     const uint16_t nb_blocks = le16_to_cpu(req->u.plug.nb_blocks);
698910b2576SDavid Hildenbrand     uint16_t type;
699910b2576SDavid Hildenbrand 
70043e54950SDavid Hildenbrand     trace_virtio_mem_plug_request(gpa, nb_blocks);
701910b2576SDavid Hildenbrand     type = virtio_mem_state_change_request(vmem, gpa, nb_blocks, true);
702910b2576SDavid Hildenbrand     virtio_mem_send_response_simple(vmem, elem, type);
703910b2576SDavid Hildenbrand }
704910b2576SDavid Hildenbrand 
virtio_mem_unplug_request(VirtIOMEM * vmem,VirtQueueElement * elem,struct virtio_mem_req * req)705910b2576SDavid Hildenbrand static void virtio_mem_unplug_request(VirtIOMEM *vmem, VirtQueueElement *elem,
706910b2576SDavid Hildenbrand                                       struct virtio_mem_req *req)
707910b2576SDavid Hildenbrand {
708910b2576SDavid Hildenbrand     const uint64_t gpa = le64_to_cpu(req->u.unplug.addr);
709910b2576SDavid Hildenbrand     const uint16_t nb_blocks = le16_to_cpu(req->u.unplug.nb_blocks);
710910b2576SDavid Hildenbrand     uint16_t type;
711910b2576SDavid Hildenbrand 
71243e54950SDavid Hildenbrand     trace_virtio_mem_unplug_request(gpa, nb_blocks);
713910b2576SDavid Hildenbrand     type = virtio_mem_state_change_request(vmem, gpa, nb_blocks, false);
714910b2576SDavid Hildenbrand     virtio_mem_send_response_simple(vmem, elem, type);
715910b2576SDavid Hildenbrand }
716910b2576SDavid Hildenbrand 
virtio_mem_resize_usable_region(VirtIOMEM * vmem,uint64_t requested_size,bool can_shrink)717910b2576SDavid Hildenbrand static void virtio_mem_resize_usable_region(VirtIOMEM *vmem,
718910b2576SDavid Hildenbrand                                             uint64_t requested_size,
719910b2576SDavid Hildenbrand                                             bool can_shrink)
720910b2576SDavid Hildenbrand {
721910b2576SDavid Hildenbrand     uint64_t newsize = MIN(memory_region_size(&vmem->memdev->mr),
722910b2576SDavid Hildenbrand                            requested_size + VIRTIO_MEM_USABLE_EXTENT);
723910b2576SDavid Hildenbrand 
7240aed2800SDavid Hildenbrand     /* The usable region size always has to be multiples of the block size. */
7250aed2800SDavid Hildenbrand     newsize = QEMU_ALIGN_UP(newsize, vmem->block_size);
7260aed2800SDavid Hildenbrand 
727910b2576SDavid Hildenbrand     if (!requested_size) {
728910b2576SDavid Hildenbrand         newsize = 0;
729910b2576SDavid Hildenbrand     }
730910b2576SDavid Hildenbrand 
731910b2576SDavid Hildenbrand     if (newsize < vmem->usable_region_size && !can_shrink) {
732910b2576SDavid Hildenbrand         return;
733910b2576SDavid Hildenbrand     }
734910b2576SDavid Hildenbrand 
73543e54950SDavid Hildenbrand     trace_virtio_mem_resized_usable_region(vmem->usable_region_size, newsize);
736910b2576SDavid Hildenbrand     vmem->usable_region_size = newsize;
737910b2576SDavid Hildenbrand }
738910b2576SDavid Hildenbrand 
virtio_mem_unplug_all(VirtIOMEM * vmem)739910b2576SDavid Hildenbrand static int virtio_mem_unplug_all(VirtIOMEM *vmem)
740910b2576SDavid Hildenbrand {
741177f9b1eSDavid Hildenbrand     const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
742910b2576SDavid Hildenbrand     RAMBlock *rb = vmem->memdev->mr.ram_block;
743910b2576SDavid Hildenbrand 
744836f657bSDavid Hildenbrand     if (vmem->size) {
745910b2576SDavid Hildenbrand         if (virtio_mem_is_busy()) {
746910b2576SDavid Hildenbrand             return -EBUSY;
747910b2576SDavid Hildenbrand         }
7483aca6380SDavid Hildenbrand         if (ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb))) {
749910b2576SDavid Hildenbrand             return -EBUSY;
750910b2576SDavid Hildenbrand         }
7512044969fSDavid Hildenbrand         virtio_mem_notify_unplug_all(vmem);
7522044969fSDavid Hildenbrand 
753910b2576SDavid Hildenbrand         bitmap_clear(vmem->bitmap, 0, vmem->bitmap_size);
754910b2576SDavid Hildenbrand         vmem->size = 0;
755c95b4437SDavid Hildenbrand         notifier_list_notify(&vmem->size_change_notifiers, &vmem->size);
756177f9b1eSDavid Hildenbrand 
757177f9b1eSDavid Hildenbrand         /* Deactivate all memslots after updating the state. */
758364eff68SDavid Hildenbrand         if (vmem->dynamic_memslots) {
759177f9b1eSDavid Hildenbrand             virtio_mem_deactivate_unplugged_memslots(vmem, 0, region_size);
760c95b4437SDavid Hildenbrand         }
761364eff68SDavid Hildenbrand     }
762836f657bSDavid Hildenbrand 
76343e54950SDavid Hildenbrand     trace_virtio_mem_unplugged_all();
764910b2576SDavid Hildenbrand     virtio_mem_resize_usable_region(vmem, vmem->requested_size, true);
765910b2576SDavid Hildenbrand     return 0;
766910b2576SDavid Hildenbrand }
767910b2576SDavid Hildenbrand 
virtio_mem_unplug_all_request(VirtIOMEM * vmem,VirtQueueElement * elem)768910b2576SDavid Hildenbrand static void virtio_mem_unplug_all_request(VirtIOMEM *vmem,
769910b2576SDavid Hildenbrand                                           VirtQueueElement *elem)
770910b2576SDavid Hildenbrand {
77143e54950SDavid Hildenbrand     trace_virtio_mem_unplug_all_request();
772910b2576SDavid Hildenbrand     if (virtio_mem_unplug_all(vmem)) {
773910b2576SDavid Hildenbrand         virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_BUSY);
774910b2576SDavid Hildenbrand     } else {
775910b2576SDavid Hildenbrand         virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_ACK);
776910b2576SDavid Hildenbrand     }
777910b2576SDavid Hildenbrand }
778910b2576SDavid Hildenbrand 
virtio_mem_state_request(VirtIOMEM * vmem,VirtQueueElement * elem,struct virtio_mem_req * req)779910b2576SDavid Hildenbrand static void virtio_mem_state_request(VirtIOMEM *vmem, VirtQueueElement *elem,
780910b2576SDavid Hildenbrand                                      struct virtio_mem_req *req)
781910b2576SDavid Hildenbrand {
782910b2576SDavid Hildenbrand     const uint16_t nb_blocks = le16_to_cpu(req->u.state.nb_blocks);
783910b2576SDavid Hildenbrand     const uint64_t gpa = le64_to_cpu(req->u.state.addr);
784910b2576SDavid Hildenbrand     const uint64_t size = nb_blocks * vmem->block_size;
785910b2576SDavid Hildenbrand     struct virtio_mem_resp resp = {
786910b2576SDavid Hildenbrand         .type = cpu_to_le16(VIRTIO_MEM_RESP_ACK),
787910b2576SDavid Hildenbrand     };
788910b2576SDavid Hildenbrand 
78943e54950SDavid Hildenbrand     trace_virtio_mem_state_request(gpa, nb_blocks);
790910b2576SDavid Hildenbrand     if (!virtio_mem_valid_range(vmem, gpa, size)) {
791910b2576SDavid Hildenbrand         virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_ERROR);
792910b2576SDavid Hildenbrand         return;
793910b2576SDavid Hildenbrand     }
794910b2576SDavid Hildenbrand 
79525c89303SDavid Hildenbrand     if (virtio_mem_is_range_plugged(vmem, gpa, size)) {
796910b2576SDavid Hildenbrand         resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_PLUGGED);
79725c89303SDavid Hildenbrand     } else if (virtio_mem_is_range_unplugged(vmem, gpa, size)) {
798910b2576SDavid Hildenbrand         resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_UNPLUGGED);
799910b2576SDavid Hildenbrand     } else {
800910b2576SDavid Hildenbrand         resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_MIXED);
801910b2576SDavid Hildenbrand     }
80243e54950SDavid Hildenbrand     trace_virtio_mem_state_response(le16_to_cpu(resp.u.state.state));
803910b2576SDavid Hildenbrand     virtio_mem_send_response(vmem, elem, &resp);
804910b2576SDavid Hildenbrand }
805910b2576SDavid Hildenbrand 
virtio_mem_handle_request(VirtIODevice * vdev,VirtQueue * vq)806910b2576SDavid Hildenbrand static void virtio_mem_handle_request(VirtIODevice *vdev, VirtQueue *vq)
807910b2576SDavid Hildenbrand {
808910b2576SDavid Hildenbrand     const int len = sizeof(struct virtio_mem_req);
809910b2576SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(vdev);
810910b2576SDavid Hildenbrand     VirtQueueElement *elem;
811910b2576SDavid Hildenbrand     struct virtio_mem_req req;
812910b2576SDavid Hildenbrand     uint16_t type;
813910b2576SDavid Hildenbrand 
814910b2576SDavid Hildenbrand     while (true) {
815910b2576SDavid Hildenbrand         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
816910b2576SDavid Hildenbrand         if (!elem) {
817910b2576SDavid Hildenbrand             return;
818910b2576SDavid Hildenbrand         }
819910b2576SDavid Hildenbrand 
820910b2576SDavid Hildenbrand         if (iov_to_buf(elem->out_sg, elem->out_num, 0, &req, len) < len) {
821910b2576SDavid Hildenbrand             virtio_error(vdev, "virtio-mem protocol violation: invalid request"
822910b2576SDavid Hildenbrand                          " size: %d", len);
8230c404e45SLi Qiang             virtqueue_detach_element(vq, elem, 0);
824910b2576SDavid Hildenbrand             g_free(elem);
825910b2576SDavid Hildenbrand             return;
826910b2576SDavid Hildenbrand         }
827910b2576SDavid Hildenbrand 
828910b2576SDavid Hildenbrand         if (iov_size(elem->in_sg, elem->in_num) <
829910b2576SDavid Hildenbrand             sizeof(struct virtio_mem_resp)) {
830910b2576SDavid Hildenbrand             virtio_error(vdev, "virtio-mem protocol violation: not enough space"
831910b2576SDavid Hildenbrand                          " for response: %zu",
832910b2576SDavid Hildenbrand                          iov_size(elem->in_sg, elem->in_num));
8330c404e45SLi Qiang             virtqueue_detach_element(vq, elem, 0);
834910b2576SDavid Hildenbrand             g_free(elem);
835910b2576SDavid Hildenbrand             return;
836910b2576SDavid Hildenbrand         }
837910b2576SDavid Hildenbrand 
838910b2576SDavid Hildenbrand         type = le16_to_cpu(req.type);
839910b2576SDavid Hildenbrand         switch (type) {
840910b2576SDavid Hildenbrand         case VIRTIO_MEM_REQ_PLUG:
841910b2576SDavid Hildenbrand             virtio_mem_plug_request(vmem, elem, &req);
842910b2576SDavid Hildenbrand             break;
843910b2576SDavid Hildenbrand         case VIRTIO_MEM_REQ_UNPLUG:
844910b2576SDavid Hildenbrand             virtio_mem_unplug_request(vmem, elem, &req);
845910b2576SDavid Hildenbrand             break;
846910b2576SDavid Hildenbrand         case VIRTIO_MEM_REQ_UNPLUG_ALL:
847910b2576SDavid Hildenbrand             virtio_mem_unplug_all_request(vmem, elem);
848910b2576SDavid Hildenbrand             break;
849910b2576SDavid Hildenbrand         case VIRTIO_MEM_REQ_STATE:
850910b2576SDavid Hildenbrand             virtio_mem_state_request(vmem, elem, &req);
851910b2576SDavid Hildenbrand             break;
852910b2576SDavid Hildenbrand         default:
853910b2576SDavid Hildenbrand             virtio_error(vdev, "virtio-mem protocol violation: unknown request"
854910b2576SDavid Hildenbrand                          " type: %d", type);
8550c404e45SLi Qiang             virtqueue_detach_element(vq, elem, 0);
856910b2576SDavid Hildenbrand             g_free(elem);
857910b2576SDavid Hildenbrand             return;
858910b2576SDavid Hildenbrand         }
859910b2576SDavid Hildenbrand 
860910b2576SDavid Hildenbrand         g_free(elem);
861910b2576SDavid Hildenbrand     }
862910b2576SDavid Hildenbrand }
863910b2576SDavid Hildenbrand 
virtio_mem_get_config(VirtIODevice * vdev,uint8_t * config_data)864910b2576SDavid Hildenbrand static void virtio_mem_get_config(VirtIODevice *vdev, uint8_t *config_data)
865910b2576SDavid Hildenbrand {
866910b2576SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(vdev);
867910b2576SDavid Hildenbrand     struct virtio_mem_config *config = (void *) config_data;
868910b2576SDavid Hildenbrand 
869910b2576SDavid Hildenbrand     config->block_size = cpu_to_le64(vmem->block_size);
870910b2576SDavid Hildenbrand     config->node_id = cpu_to_le16(vmem->node);
871910b2576SDavid Hildenbrand     config->requested_size = cpu_to_le64(vmem->requested_size);
872910b2576SDavid Hildenbrand     config->plugged_size = cpu_to_le64(vmem->size);
873910b2576SDavid Hildenbrand     config->addr = cpu_to_le64(vmem->addr);
874910b2576SDavid Hildenbrand     config->region_size = cpu_to_le64(memory_region_size(&vmem->memdev->mr));
875910b2576SDavid Hildenbrand     config->usable_region_size = cpu_to_le64(vmem->usable_region_size);
876910b2576SDavid Hildenbrand }
877910b2576SDavid Hildenbrand 
virtio_mem_get_features(VirtIODevice * vdev,uint64_t features,Error ** errp)878910b2576SDavid Hildenbrand static uint64_t virtio_mem_get_features(VirtIODevice *vdev, uint64_t features,
879910b2576SDavid Hildenbrand                                         Error **errp)
880910b2576SDavid Hildenbrand {
881910b2576SDavid Hildenbrand     MachineState *ms = MACHINE(qdev_get_machine());
88223ad8decSDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(vdev);
883910b2576SDavid Hildenbrand 
884910b2576SDavid Hildenbrand     if (ms->numa_state) {
885910b2576SDavid Hildenbrand #if defined(CONFIG_ACPI)
886910b2576SDavid Hildenbrand         virtio_add_feature(&features, VIRTIO_MEM_F_ACPI_PXM);
887910b2576SDavid Hildenbrand #endif
888910b2576SDavid Hildenbrand     }
88923ad8decSDavid Hildenbrand     assert(vmem->unplugged_inaccessible != ON_OFF_AUTO_AUTO);
89023ad8decSDavid Hildenbrand     if (vmem->unplugged_inaccessible == ON_OFF_AUTO_ON) {
89123ad8decSDavid Hildenbrand         virtio_add_feature(&features, VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE);
89223ad8decSDavid Hildenbrand     }
8931f5f4905SJuraj Marcin     if (qemu_wakeup_suspend_enabled()) {
8941f5f4905SJuraj Marcin         virtio_add_feature(&features, VIRTIO_MEM_F_PERSISTENT_SUSPEND);
8951f5f4905SJuraj Marcin     }
896910b2576SDavid Hildenbrand     return features;
897910b2576SDavid Hildenbrand }
898910b2576SDavid Hildenbrand 
virtio_mem_validate_features(VirtIODevice * vdev)89923ad8decSDavid Hildenbrand static int virtio_mem_validate_features(VirtIODevice *vdev)
90023ad8decSDavid Hildenbrand {
90123ad8decSDavid Hildenbrand     if (virtio_host_has_feature(vdev, VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE) &&
90223ad8decSDavid Hildenbrand         !virtio_vdev_has_feature(vdev, VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE)) {
90323ad8decSDavid Hildenbrand         return -EFAULT;
90423ad8decSDavid Hildenbrand     }
90523ad8decSDavid Hildenbrand     return 0;
90623ad8decSDavid Hildenbrand }
90723ad8decSDavid Hildenbrand 
virtio_mem_prepare_mr(VirtIOMEM * vmem)908177f9b1eSDavid Hildenbrand static void virtio_mem_prepare_mr(VirtIOMEM *vmem)
909177f9b1eSDavid Hildenbrand {
910177f9b1eSDavid Hildenbrand     const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
911177f9b1eSDavid Hildenbrand 
912177f9b1eSDavid Hildenbrand     assert(!vmem->mr && vmem->dynamic_memslots);
913177f9b1eSDavid Hildenbrand     vmem->mr = g_new0(MemoryRegion, 1);
914177f9b1eSDavid Hildenbrand     memory_region_init(vmem->mr, OBJECT(vmem), "virtio-mem",
915177f9b1eSDavid Hildenbrand                        region_size);
916177f9b1eSDavid Hildenbrand     vmem->mr->align = memory_region_get_alignment(&vmem->memdev->mr);
917177f9b1eSDavid Hildenbrand }
918177f9b1eSDavid Hildenbrand 
virtio_mem_prepare_memslots(VirtIOMEM * vmem)919177f9b1eSDavid Hildenbrand static void virtio_mem_prepare_memslots(VirtIOMEM *vmem)
920177f9b1eSDavid Hildenbrand {
921177f9b1eSDavid Hildenbrand     const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
922177f9b1eSDavid Hildenbrand     unsigned int idx;
923177f9b1eSDavid Hildenbrand 
924177f9b1eSDavid Hildenbrand     g_assert(!vmem->memslots && vmem->nb_memslots && vmem->dynamic_memslots);
925177f9b1eSDavid Hildenbrand     vmem->memslots = g_new0(MemoryRegion, vmem->nb_memslots);
926177f9b1eSDavid Hildenbrand 
927177f9b1eSDavid Hildenbrand     /* Initialize our memslots, but don't map them yet. */
928177f9b1eSDavid Hildenbrand     for (idx = 0; idx < vmem->nb_memslots; idx++) {
929177f9b1eSDavid Hildenbrand         const uint64_t memslot_offset = idx * vmem->memslot_size;
930177f9b1eSDavid Hildenbrand         uint64_t memslot_size = vmem->memslot_size;
931177f9b1eSDavid Hildenbrand         char name[20];
932177f9b1eSDavid Hildenbrand 
933177f9b1eSDavid Hildenbrand         /* The size of the last memslot might be smaller. */
934177f9b1eSDavid Hildenbrand         if (idx == vmem->nb_memslots - 1) {
935177f9b1eSDavid Hildenbrand             memslot_size = region_size - memslot_offset;
936177f9b1eSDavid Hildenbrand         }
937177f9b1eSDavid Hildenbrand 
938177f9b1eSDavid Hildenbrand         snprintf(name, sizeof(name), "memslot-%u", idx);
939177f9b1eSDavid Hildenbrand         memory_region_init_alias(&vmem->memslots[idx], OBJECT(vmem), name,
940177f9b1eSDavid Hildenbrand                                  &vmem->memdev->mr, memslot_offset,
941177f9b1eSDavid Hildenbrand                                  memslot_size);
942ee6398d8SDavid Hildenbrand         /*
943ee6398d8SDavid Hildenbrand          * We want to be able to atomically and efficiently activate/deactivate
944ee6398d8SDavid Hildenbrand          * individual memslots without affecting adjacent memslots in memory
945ee6398d8SDavid Hildenbrand          * notifiers.
946ee6398d8SDavid Hildenbrand          */
947ee6398d8SDavid Hildenbrand         memory_region_set_unmergeable(&vmem->memslots[idx], true);
948177f9b1eSDavid Hildenbrand     }
949177f9b1eSDavid Hildenbrand }
950177f9b1eSDavid Hildenbrand 
virtio_mem_device_realize(DeviceState * dev,Error ** errp)951910b2576SDavid Hildenbrand static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
952910b2576SDavid Hildenbrand {
953910b2576SDavid Hildenbrand     MachineState *ms = MACHINE(qdev_get_machine());
954910b2576SDavid Hildenbrand     int nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
955910b2576SDavid Hildenbrand     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
956910b2576SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(dev);
957910b2576SDavid Hildenbrand     uint64_t page_size;
958910b2576SDavid Hildenbrand     RAMBlock *rb;
959910b2576SDavid Hildenbrand     int ret;
960910b2576SDavid Hildenbrand 
961910b2576SDavid Hildenbrand     if (!vmem->memdev) {
962910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property is not set", VIRTIO_MEM_MEMDEV_PROP);
963910b2576SDavid Hildenbrand         return;
964910b2576SDavid Hildenbrand     } else if (host_memory_backend_is_mapped(vmem->memdev)) {
965910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property specifies a busy memdev: %s",
9667a309cc9SMarkus Armbruster                    VIRTIO_MEM_MEMDEV_PROP,
9677a309cc9SMarkus Armbruster                    object_get_canonical_path_component(OBJECT(vmem->memdev)));
968910b2576SDavid Hildenbrand         return;
969910b2576SDavid Hildenbrand     } else if (!memory_region_is_ram(&vmem->memdev->mr) ||
970910b2576SDavid Hildenbrand         memory_region_is_rom(&vmem->memdev->mr) ||
971910b2576SDavid Hildenbrand         !vmem->memdev->mr.ram_block) {
972910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property specifies an unsupported memdev",
973910b2576SDavid Hildenbrand                    VIRTIO_MEM_MEMDEV_PROP);
974910b2576SDavid Hildenbrand         return;
975ce1761f0SDavid Hildenbrand     } else if (vmem->memdev->prealloc) {
976ce1761f0SDavid Hildenbrand         error_setg(errp, "'%s' property specifies a memdev with preallocation"
977ce1761f0SDavid Hildenbrand                    " enabled: %s. Instead, specify 'prealloc=on' for the"
978ce1761f0SDavid Hildenbrand                    " virtio-mem device. ", VIRTIO_MEM_MEMDEV_PROP,
979ce1761f0SDavid Hildenbrand                    object_get_canonical_path_component(OBJECT(vmem->memdev)));
980ce1761f0SDavid Hildenbrand         return;
981910b2576SDavid Hildenbrand     }
982910b2576SDavid Hildenbrand 
983910b2576SDavid Hildenbrand     if ((nb_numa_nodes && vmem->node >= nb_numa_nodes) ||
984910b2576SDavid Hildenbrand         (!nb_numa_nodes && vmem->node)) {
985910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property has value '%" PRIu32 "', which exceeds"
986910b2576SDavid Hildenbrand                    "the number of numa nodes: %d", VIRTIO_MEM_NODE_PROP,
987910b2576SDavid Hildenbrand                    vmem->node, nb_numa_nodes ? nb_numa_nodes : 1);
988910b2576SDavid Hildenbrand         return;
989910b2576SDavid Hildenbrand     }
990910b2576SDavid Hildenbrand 
991910b2576SDavid Hildenbrand     if (enable_mlock) {
992910b2576SDavid Hildenbrand         error_setg(errp, "Incompatible with mlock");
993910b2576SDavid Hildenbrand         return;
994910b2576SDavid Hildenbrand     }
995910b2576SDavid Hildenbrand 
996910b2576SDavid Hildenbrand     rb = vmem->memdev->mr.ram_block;
997910b2576SDavid Hildenbrand     page_size = qemu_ram_pagesize(rb);
998910b2576SDavid Hildenbrand 
99923ad8decSDavid Hildenbrand #if defined(VIRTIO_MEM_HAS_LEGACY_GUESTS)
100023ad8decSDavid Hildenbrand     switch (vmem->unplugged_inaccessible) {
100123ad8decSDavid Hildenbrand     case ON_OFF_AUTO_AUTO:
100223ad8decSDavid Hildenbrand         if (virtio_mem_has_shared_zeropage(rb)) {
100323ad8decSDavid Hildenbrand             vmem->unplugged_inaccessible = ON_OFF_AUTO_OFF;
100423ad8decSDavid Hildenbrand         } else {
100523ad8decSDavid Hildenbrand             vmem->unplugged_inaccessible = ON_OFF_AUTO_ON;
100623ad8decSDavid Hildenbrand         }
100723ad8decSDavid Hildenbrand         break;
100823ad8decSDavid Hildenbrand     case ON_OFF_AUTO_OFF:
100923ad8decSDavid Hildenbrand         if (!virtio_mem_has_shared_zeropage(rb)) {
101023ad8decSDavid Hildenbrand             warn_report("'%s' property set to 'off' with a memdev that does"
101123ad8decSDavid Hildenbrand                         " not support the shared zeropage.",
101223ad8decSDavid Hildenbrand                         VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP);
101323ad8decSDavid Hildenbrand         }
101423ad8decSDavid Hildenbrand         break;
101523ad8decSDavid Hildenbrand     default:
101623ad8decSDavid Hildenbrand         break;
101723ad8decSDavid Hildenbrand     }
101823ad8decSDavid Hildenbrand #else /* VIRTIO_MEM_HAS_LEGACY_GUESTS */
101923ad8decSDavid Hildenbrand     vmem->unplugged_inaccessible = ON_OFF_AUTO_ON;
102023ad8decSDavid Hildenbrand #endif /* VIRTIO_MEM_HAS_LEGACY_GUESTS */
102123ad8decSDavid Hildenbrand 
1022177f9b1eSDavid Hildenbrand     if (vmem->dynamic_memslots &&
1023177f9b1eSDavid Hildenbrand         vmem->unplugged_inaccessible != ON_OFF_AUTO_ON) {
1024177f9b1eSDavid Hildenbrand         error_setg(errp, "'%s' property set to 'on' requires '%s' to be 'on'",
1025177f9b1eSDavid Hildenbrand                    VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP,
1026177f9b1eSDavid Hildenbrand                    VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP);
1027177f9b1eSDavid Hildenbrand         return;
1028177f9b1eSDavid Hildenbrand     }
1029177f9b1eSDavid Hildenbrand 
1030228957feSDavid Hildenbrand     /*
1031228957feSDavid Hildenbrand      * If the block size wasn't configured by the user, use a sane default. This
1032228957feSDavid Hildenbrand      * allows using hugetlbfs backends of any page size without manual
1033228957feSDavid Hildenbrand      * intervention.
1034228957feSDavid Hildenbrand      */
1035228957feSDavid Hildenbrand     if (!vmem->block_size) {
1036228957feSDavid Hildenbrand         vmem->block_size = virtio_mem_default_block_size(rb);
1037228957feSDavid Hildenbrand     }
1038228957feSDavid Hildenbrand 
1039910b2576SDavid Hildenbrand     if (vmem->block_size < page_size) {
1040910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property has to be at least the page size (0x%"
1041910b2576SDavid Hildenbrand                    PRIx64 ")", VIRTIO_MEM_BLOCK_SIZE_PROP, page_size);
1042910b2576SDavid Hildenbrand         return;
1043228957feSDavid Hildenbrand     } else if (vmem->block_size < virtio_mem_default_block_size(rb)) {
1044228957feSDavid Hildenbrand         warn_report("'%s' property is smaller than the default block size (%"
1045228957feSDavid Hildenbrand                     PRIx64 " MiB)", VIRTIO_MEM_BLOCK_SIZE_PROP,
1046228957feSDavid Hildenbrand                     virtio_mem_default_block_size(rb) / MiB);
10477656d9ceSDavid Hildenbrand     }
10487656d9ceSDavid Hildenbrand     if (!QEMU_IS_ALIGNED(vmem->requested_size, vmem->block_size)) {
1049910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property has to be multiples of '%s' (0x%" PRIx64
1050910b2576SDavid Hildenbrand                    ")", VIRTIO_MEM_REQUESTED_SIZE_PROP,
1051910b2576SDavid Hildenbrand                    VIRTIO_MEM_BLOCK_SIZE_PROP, vmem->block_size);
1052910b2576SDavid Hildenbrand         return;
1053d31992aeSDavid Hildenbrand     } else if (!QEMU_IS_ALIGNED(vmem->addr, vmem->block_size)) {
1054d31992aeSDavid Hildenbrand         error_setg(errp, "'%s' property has to be multiples of '%s' (0x%" PRIx64
1055d31992aeSDavid Hildenbrand                    ")", VIRTIO_MEM_ADDR_PROP, VIRTIO_MEM_BLOCK_SIZE_PROP,
1056d31992aeSDavid Hildenbrand                    vmem->block_size);
1057d31992aeSDavid Hildenbrand         return;
1058910b2576SDavid Hildenbrand     } else if (!QEMU_IS_ALIGNED(memory_region_size(&vmem->memdev->mr),
1059910b2576SDavid Hildenbrand                                 vmem->block_size)) {
1060910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property memdev size has to be multiples of"
1061910b2576SDavid Hildenbrand                    "'%s' (0x%" PRIx64 ")", VIRTIO_MEM_MEMDEV_PROP,
1062910b2576SDavid Hildenbrand                    VIRTIO_MEM_BLOCK_SIZE_PROP, vmem->block_size);
1063910b2576SDavid Hildenbrand         return;
1064910b2576SDavid Hildenbrand     }
1065910b2576SDavid Hildenbrand 
1066bc072ed4SDavid Hildenbrand     if (ram_block_coordinated_discard_require(true)) {
1067910b2576SDavid Hildenbrand         error_setg(errp, "Discarding RAM is disabled");
1068910b2576SDavid Hildenbrand         return;
1069910b2576SDavid Hildenbrand     }
1070910b2576SDavid Hildenbrand 
1071b01fd4b6SDavid Hildenbrand     /*
1072b01fd4b6SDavid Hildenbrand      * We don't know at this point whether shared RAM is migrated using
1073b01fd4b6SDavid Hildenbrand      * QEMU or migrated using the file content. "x-ignore-shared" will be
1074b01fd4b6SDavid Hildenbrand      * configured after realizing the device. So in case we have an
1075b01fd4b6SDavid Hildenbrand      * incoming migration, simply always skip the discard step.
1076b01fd4b6SDavid Hildenbrand      *
1077b01fd4b6SDavid Hildenbrand      * Otherwise, make sure that we start with a clean slate: either the
1078b01fd4b6SDavid Hildenbrand      * memory backend might get reused or the shared file might still have
1079b01fd4b6SDavid Hildenbrand      * memory allocated.
1080b01fd4b6SDavid Hildenbrand      */
1081b01fd4b6SDavid Hildenbrand     if (!runstate_check(RUN_STATE_INMIGRATE)) {
1082910b2576SDavid Hildenbrand         ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb));
1083910b2576SDavid Hildenbrand         if (ret) {
1084910b2576SDavid Hildenbrand             error_setg_errno(errp, -ret, "Unexpected error discarding RAM");
1085bc072ed4SDavid Hildenbrand             ram_block_coordinated_discard_require(false);
1086910b2576SDavid Hildenbrand             return;
1087910b2576SDavid Hildenbrand         }
1088b01fd4b6SDavid Hildenbrand     }
1089910b2576SDavid Hildenbrand 
1090910b2576SDavid Hildenbrand     virtio_mem_resize_usable_region(vmem, vmem->requested_size, true);
1091910b2576SDavid Hildenbrand 
1092910b2576SDavid Hildenbrand     vmem->bitmap_size = memory_region_size(&vmem->memdev->mr) /
1093910b2576SDavid Hildenbrand                         vmem->block_size;
1094910b2576SDavid Hildenbrand     vmem->bitmap = bitmap_new(vmem->bitmap_size);
1095910b2576SDavid Hildenbrand 
10963857cd5cSJonah Palmer     virtio_init(vdev, VIRTIO_ID_MEM, sizeof(struct virtio_mem_config));
1097910b2576SDavid Hildenbrand     vmem->vq = virtio_add_queue(vdev, 128, virtio_mem_handle_request);
1098910b2576SDavid Hildenbrand 
1099177f9b1eSDavid Hildenbrand     /*
1100177f9b1eSDavid Hildenbrand      * With "dynamic-memslots=off" (old behavior) we always map the whole
1101177f9b1eSDavid Hildenbrand      * RAM memory region directly.
1102177f9b1eSDavid Hildenbrand      */
1103177f9b1eSDavid Hildenbrand     if (vmem->dynamic_memslots) {
1104177f9b1eSDavid Hildenbrand         if (!vmem->mr) {
1105177f9b1eSDavid Hildenbrand             virtio_mem_prepare_mr(vmem);
1106177f9b1eSDavid Hildenbrand         }
1107177f9b1eSDavid Hildenbrand         if (vmem->nb_memslots <= 1) {
1108177f9b1eSDavid Hildenbrand             vmem->nb_memslots = 1;
1109177f9b1eSDavid Hildenbrand             vmem->memslot_size = memory_region_size(&vmem->memdev->mr);
1110177f9b1eSDavid Hildenbrand         }
1111177f9b1eSDavid Hildenbrand         if (!vmem->memslots) {
1112177f9b1eSDavid Hildenbrand             virtio_mem_prepare_memslots(vmem);
1113177f9b1eSDavid Hildenbrand         }
1114177f9b1eSDavid Hildenbrand     } else {
1115177f9b1eSDavid Hildenbrand         assert(!vmem->mr && !vmem->nb_memslots && !vmem->memslots);
1116177f9b1eSDavid Hildenbrand     }
1117177f9b1eSDavid Hildenbrand 
1118910b2576SDavid Hildenbrand     host_memory_backend_set_mapped(vmem->memdev, true);
1119910b2576SDavid Hildenbrand     vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem));
11203b95a71bSDavid Hildenbrand     if (vmem->early_migration) {
112199b16e8eSJuan Quintela         vmstate_register_any(VMSTATE_IF(vmem),
11223b95a71bSDavid Hildenbrand                              &vmstate_virtio_mem_device_early, vmem);
11233b95a71bSDavid Hildenbrand     }
1124c009a311SJuraj Marcin     qemu_register_resettable(OBJECT(vmem));
11252044969fSDavid Hildenbrand 
11262044969fSDavid Hildenbrand     /*
11272044969fSDavid Hildenbrand      * Set ourselves as RamDiscardManager before the plug handler maps the
11282044969fSDavid Hildenbrand      * memory region and exposes it via an address space.
11292044969fSDavid Hildenbrand      */
11302044969fSDavid Hildenbrand     memory_region_set_ram_discard_manager(&vmem->memdev->mr,
11312044969fSDavid Hildenbrand                                           RAM_DISCARD_MANAGER(vmem));
1132910b2576SDavid Hildenbrand }
1133910b2576SDavid Hildenbrand 
virtio_mem_device_unrealize(DeviceState * dev)1134910b2576SDavid Hildenbrand static void virtio_mem_device_unrealize(DeviceState *dev)
1135910b2576SDavid Hildenbrand {
1136910b2576SDavid Hildenbrand     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1137910b2576SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(dev);
1138910b2576SDavid Hildenbrand 
11392044969fSDavid Hildenbrand     /*
11402044969fSDavid Hildenbrand      * The unplug handler unmapped the memory region, it cannot be
11412044969fSDavid Hildenbrand      * found via an address space anymore. Unset ourselves.
11422044969fSDavid Hildenbrand      */
11432044969fSDavid Hildenbrand     memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
1144c009a311SJuraj Marcin     qemu_unregister_resettable(OBJECT(vmem));
11453b95a71bSDavid Hildenbrand     if (vmem->early_migration) {
11463b95a71bSDavid Hildenbrand         vmstate_unregister(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early,
11473b95a71bSDavid Hildenbrand                            vmem);
11483b95a71bSDavid Hildenbrand     }
1149910b2576SDavid Hildenbrand     vmstate_unregister_ram(&vmem->memdev->mr, DEVICE(vmem));
1150910b2576SDavid Hildenbrand     host_memory_backend_set_mapped(vmem->memdev, false);
1151910b2576SDavid Hildenbrand     virtio_del_queue(vdev, 0);
1152910b2576SDavid Hildenbrand     virtio_cleanup(vdev);
1153910b2576SDavid Hildenbrand     g_free(vmem->bitmap);
1154bc072ed4SDavid Hildenbrand     ram_block_coordinated_discard_require(false);
1155910b2576SDavid Hildenbrand }
1156910b2576SDavid Hildenbrand 
virtio_mem_discard_range_cb(VirtIOMEM * vmem,void * arg,uint64_t offset,uint64_t size)1157a45171dbSDavid Hildenbrand static int virtio_mem_discard_range_cb(VirtIOMEM *vmem, void *arg,
11587a9d5d02SDavid Hildenbrand                                        uint64_t offset, uint64_t size)
1159910b2576SDavid Hildenbrand {
1160910b2576SDavid Hildenbrand     RAMBlock *rb = vmem->memdev->mr.ram_block;
1161910b2576SDavid Hildenbrand 
11623aca6380SDavid Hildenbrand     return ram_block_discard_range(rb, offset, size) ? -EINVAL : 0;
1163910b2576SDavid Hildenbrand }
1164910b2576SDavid Hildenbrand 
virtio_mem_restore_unplugged(VirtIOMEM * vmem)11657a9d5d02SDavid Hildenbrand static int virtio_mem_restore_unplugged(VirtIOMEM *vmem)
11667a9d5d02SDavid Hildenbrand {
11677a9d5d02SDavid Hildenbrand     /* Make sure all memory is really discarded after migration. */
11687a9d5d02SDavid Hildenbrand     return virtio_mem_for_each_unplugged_range(vmem, NULL,
11697a9d5d02SDavid Hildenbrand                                                virtio_mem_discard_range_cb);
11707a9d5d02SDavid Hildenbrand }
11717a9d5d02SDavid Hildenbrand 
virtio_mem_activate_memslot_range_cb(VirtIOMEM * vmem,void * arg,uint64_t offset,uint64_t size)1172177f9b1eSDavid Hildenbrand static int virtio_mem_activate_memslot_range_cb(VirtIOMEM *vmem, void *arg,
1173177f9b1eSDavid Hildenbrand                                                 uint64_t offset, uint64_t size)
1174177f9b1eSDavid Hildenbrand {
1175177f9b1eSDavid Hildenbrand     virtio_mem_activate_memslots_to_plug(vmem, offset, size);
1176177f9b1eSDavid Hildenbrand     return 0;
1177177f9b1eSDavid Hildenbrand }
1178177f9b1eSDavid Hildenbrand 
virtio_mem_post_load_bitmap(VirtIOMEM * vmem)1179884a0c20SDavid Hildenbrand static int virtio_mem_post_load_bitmap(VirtIOMEM *vmem)
1180910b2576SDavid Hildenbrand {
11812044969fSDavid Hildenbrand     RamDiscardListener *rdl;
11822044969fSDavid Hildenbrand     int ret;
11832044969fSDavid Hildenbrand 
11842044969fSDavid Hildenbrand     /*
1185177f9b1eSDavid Hildenbrand      * We restored the bitmap and updated the requested size; activate all
1186177f9b1eSDavid Hildenbrand      * memslots (so listeners register) before notifying about plugged blocks.
1187177f9b1eSDavid Hildenbrand      */
1188177f9b1eSDavid Hildenbrand     if (vmem->dynamic_memslots) {
1189177f9b1eSDavid Hildenbrand         /*
1190177f9b1eSDavid Hildenbrand          * We don't expect any active memslots at this point to deactivate: no
1191177f9b1eSDavid Hildenbrand          * memory was plugged on the migration destination.
1192177f9b1eSDavid Hildenbrand          */
1193177f9b1eSDavid Hildenbrand         virtio_mem_for_each_plugged_range(vmem, NULL,
1194177f9b1eSDavid Hildenbrand                                           virtio_mem_activate_memslot_range_cb);
1195177f9b1eSDavid Hildenbrand     }
1196177f9b1eSDavid Hildenbrand 
1197177f9b1eSDavid Hildenbrand     /*
11982044969fSDavid Hildenbrand      * We started out with all memory discarded and our memory region is mapped
11992044969fSDavid Hildenbrand      * into an address space. Replay, now that we updated the bitmap.
12002044969fSDavid Hildenbrand      */
12012044969fSDavid Hildenbrand     QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
12022044969fSDavid Hildenbrand         ret = virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
12032044969fSDavid Hildenbrand                                                  virtio_mem_notify_populate_cb);
12042044969fSDavid Hildenbrand         if (ret) {
12052044969fSDavid Hildenbrand             return ret;
12062044969fSDavid Hildenbrand         }
12072044969fSDavid Hildenbrand     }
1208884a0c20SDavid Hildenbrand     return 0;
1209884a0c20SDavid Hildenbrand }
1210884a0c20SDavid Hildenbrand 
virtio_mem_post_load(void * opaque,int version_id)1211884a0c20SDavid Hildenbrand static int virtio_mem_post_load(void *opaque, int version_id)
1212884a0c20SDavid Hildenbrand {
1213884a0c20SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(opaque);
1214884a0c20SDavid Hildenbrand     int ret;
1215884a0c20SDavid Hildenbrand 
1216884a0c20SDavid Hildenbrand     if (!vmem->early_migration) {
1217884a0c20SDavid Hildenbrand         ret = virtio_mem_post_load_bitmap(vmem);
1218884a0c20SDavid Hildenbrand         if (ret) {
1219884a0c20SDavid Hildenbrand             return ret;
1220884a0c20SDavid Hildenbrand         }
1221884a0c20SDavid Hildenbrand     }
12222044969fSDavid Hildenbrand 
1223b01fd4b6SDavid Hildenbrand     /*
1224b01fd4b6SDavid Hildenbrand      * If shared RAM is migrated using the file content and not using QEMU,
1225b01fd4b6SDavid Hildenbrand      * don't mess with preallocation and postcopy.
1226b01fd4b6SDavid Hildenbrand      */
1227b01fd4b6SDavid Hildenbrand     if (migrate_ram_is_ignored(vmem->memdev->mr.ram_block)) {
1228b01fd4b6SDavid Hildenbrand         return 0;
1229b01fd4b6SDavid Hildenbrand     }
1230b01fd4b6SDavid Hildenbrand 
1231b01fd4b6SDavid Hildenbrand     if (vmem->prealloc && !vmem->early_migration) {
1232b01fd4b6SDavid Hildenbrand         warn_report("Proper preallocation with migration requires a newer QEMU machine");
1233b01fd4b6SDavid Hildenbrand     }
1234b01fd4b6SDavid Hildenbrand 
1235910b2576SDavid Hildenbrand     if (migration_in_incoming_postcopy()) {
1236910b2576SDavid Hildenbrand         return 0;
1237910b2576SDavid Hildenbrand     }
1238910b2576SDavid Hildenbrand 
12392044969fSDavid Hildenbrand     return virtio_mem_restore_unplugged(vmem);
1240910b2576SDavid Hildenbrand }
1241910b2576SDavid Hildenbrand 
virtio_mem_prealloc_range_cb(VirtIOMEM * vmem,void * arg,uint64_t offset,uint64_t size)1242a45171dbSDavid Hildenbrand static int virtio_mem_prealloc_range_cb(VirtIOMEM *vmem, void *arg,
1243d71920d4SDavid Hildenbrand                                         uint64_t offset, uint64_t size)
1244d71920d4SDavid Hildenbrand {
1245d71920d4SDavid Hildenbrand     void *area = memory_region_get_ram_ptr(&vmem->memdev->mr) + offset;
1246d71920d4SDavid Hildenbrand     int fd = memory_region_get_fd(&vmem->memdev->mr);
1247d71920d4SDavid Hildenbrand     Error *local_err = NULL;
1248d71920d4SDavid Hildenbrand 
124904accf43SMark Kanda     if (!qemu_prealloc_mem(fd, area, size, 1, NULL, false, &local_err)) {
1250d71920d4SDavid Hildenbrand         error_report_err(local_err);
1251d71920d4SDavid Hildenbrand         return -ENOMEM;
1252d71920d4SDavid Hildenbrand     }
1253d71920d4SDavid Hildenbrand     return 0;
1254d71920d4SDavid Hildenbrand }
1255d71920d4SDavid Hildenbrand 
virtio_mem_post_load_early(void * opaque,int version_id)1256d71920d4SDavid Hildenbrand static int virtio_mem_post_load_early(void *opaque, int version_id)
1257d71920d4SDavid Hildenbrand {
1258d71920d4SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(opaque);
1259d71920d4SDavid Hildenbrand     RAMBlock *rb = vmem->memdev->mr.ram_block;
1260d71920d4SDavid Hildenbrand     int ret;
1261d71920d4SDavid Hildenbrand 
1262d71920d4SDavid Hildenbrand     if (!vmem->prealloc) {
1263884a0c20SDavid Hildenbrand         goto post_load_bitmap;
1264d71920d4SDavid Hildenbrand     }
1265d71920d4SDavid Hildenbrand 
1266d71920d4SDavid Hildenbrand     /*
1267b01fd4b6SDavid Hildenbrand      * If shared RAM is migrated using the file content and not using QEMU,
1268b01fd4b6SDavid Hildenbrand      * don't mess with preallocation and postcopy.
1269b01fd4b6SDavid Hildenbrand      */
1270b01fd4b6SDavid Hildenbrand     if (migrate_ram_is_ignored(rb)) {
1271884a0c20SDavid Hildenbrand         goto post_load_bitmap;
1272b01fd4b6SDavid Hildenbrand     }
1273b01fd4b6SDavid Hildenbrand 
1274b01fd4b6SDavid Hildenbrand     /*
1275d71920d4SDavid Hildenbrand      * We restored the bitmap and verified that the basic properties
1276d71920d4SDavid Hildenbrand      * match on source and destination, so we can go ahead and preallocate
1277d71920d4SDavid Hildenbrand      * memory for all plugged memory blocks, before actual RAM migration starts
1278d71920d4SDavid Hildenbrand      * touching this memory.
1279d71920d4SDavid Hildenbrand      */
1280d71920d4SDavid Hildenbrand     ret = virtio_mem_for_each_plugged_range(vmem, NULL,
1281d71920d4SDavid Hildenbrand                                             virtio_mem_prealloc_range_cb);
1282d71920d4SDavid Hildenbrand     if (ret) {
1283d71920d4SDavid Hildenbrand         return ret;
1284d71920d4SDavid Hildenbrand     }
1285d71920d4SDavid Hildenbrand 
1286d71920d4SDavid Hildenbrand     /*
1287d71920d4SDavid Hildenbrand      * This is tricky: postcopy wants to start with a clean slate. On
1288d71920d4SDavid Hildenbrand      * POSTCOPY_INCOMING_ADVISE, postcopy code discards all (ordinarily
1289d71920d4SDavid Hildenbrand      * preallocated) RAM such that postcopy will work as expected later.
1290d71920d4SDavid Hildenbrand      *
1291d71920d4SDavid Hildenbrand      * However, we run after POSTCOPY_INCOMING_ADVISE -- but before actual
1292d71920d4SDavid Hildenbrand      * RAM migration. So let's discard all memory again. This looks like an
1293d71920d4SDavid Hildenbrand      * expensive NOP, but actually serves a purpose: we made sure that we
1294d71920d4SDavid Hildenbrand      * were able to allocate all required backend memory once. We cannot
1295d71920d4SDavid Hildenbrand      * guarantee that the backend memory we will free will remain free
1296d71920d4SDavid Hildenbrand      * until we need it during postcopy, but at least we can catch the
1297d71920d4SDavid Hildenbrand      * obvious setup issues this way.
1298d71920d4SDavid Hildenbrand      */
1299d71920d4SDavid Hildenbrand     if (migration_incoming_postcopy_advised()) {
1300d71920d4SDavid Hildenbrand         if (ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb))) {
1301d71920d4SDavid Hildenbrand             return -EBUSY;
1302d71920d4SDavid Hildenbrand         }
1303d71920d4SDavid Hildenbrand     }
1304884a0c20SDavid Hildenbrand 
1305884a0c20SDavid Hildenbrand post_load_bitmap:
1306884a0c20SDavid Hildenbrand     /* Finally, update any other state to be consistent with the new bitmap. */
1307884a0c20SDavid Hildenbrand     return virtio_mem_post_load_bitmap(vmem);
1308d71920d4SDavid Hildenbrand }
1309d71920d4SDavid Hildenbrand 
1310383ee445SDavid Hildenbrand typedef struct VirtIOMEMMigSanityChecks {
1311383ee445SDavid Hildenbrand     VirtIOMEM *parent;
1312383ee445SDavid Hildenbrand     uint64_t addr;
1313383ee445SDavid Hildenbrand     uint64_t region_size;
1314383ee445SDavid Hildenbrand     uint64_t block_size;
1315383ee445SDavid Hildenbrand     uint32_t node;
1316383ee445SDavid Hildenbrand } VirtIOMEMMigSanityChecks;
1317383ee445SDavid Hildenbrand 
virtio_mem_mig_sanity_checks_pre_save(void * opaque)1318383ee445SDavid Hildenbrand static int virtio_mem_mig_sanity_checks_pre_save(void *opaque)
1319383ee445SDavid Hildenbrand {
1320383ee445SDavid Hildenbrand     VirtIOMEMMigSanityChecks *tmp = opaque;
1321383ee445SDavid Hildenbrand     VirtIOMEM *vmem = tmp->parent;
1322383ee445SDavid Hildenbrand 
1323383ee445SDavid Hildenbrand     tmp->addr = vmem->addr;
1324383ee445SDavid Hildenbrand     tmp->region_size = memory_region_size(&vmem->memdev->mr);
1325383ee445SDavid Hildenbrand     tmp->block_size = vmem->block_size;
1326383ee445SDavid Hildenbrand     tmp->node = vmem->node;
1327383ee445SDavid Hildenbrand     return 0;
1328383ee445SDavid Hildenbrand }
1329383ee445SDavid Hildenbrand 
virtio_mem_mig_sanity_checks_post_load(void * opaque,int version_id)1330383ee445SDavid Hildenbrand static int virtio_mem_mig_sanity_checks_post_load(void *opaque, int version_id)
1331383ee445SDavid Hildenbrand {
1332383ee445SDavid Hildenbrand     VirtIOMEMMigSanityChecks *tmp = opaque;
1333383ee445SDavid Hildenbrand     VirtIOMEM *vmem = tmp->parent;
1334383ee445SDavid Hildenbrand     const uint64_t new_region_size = memory_region_size(&vmem->memdev->mr);
1335383ee445SDavid Hildenbrand 
1336383ee445SDavid Hildenbrand     if (tmp->addr != vmem->addr) {
1337383ee445SDavid Hildenbrand         error_report("Property '%s' changed from 0x%" PRIx64 " to 0x%" PRIx64,
1338383ee445SDavid Hildenbrand                      VIRTIO_MEM_ADDR_PROP, tmp->addr, vmem->addr);
1339383ee445SDavid Hildenbrand         return -EINVAL;
1340383ee445SDavid Hildenbrand     }
1341383ee445SDavid Hildenbrand     /*
13429b4b4e51SMichael Tokarev      * Note: Preparation for resizable memory regions. The maximum size
1343383ee445SDavid Hildenbrand      * of the memory region must not change during migration.
1344383ee445SDavid Hildenbrand      */
1345383ee445SDavid Hildenbrand     if (tmp->region_size != new_region_size) {
1346383ee445SDavid Hildenbrand         error_report("Property '%s' size changed from 0x%" PRIx64 " to 0x%"
1347383ee445SDavid Hildenbrand                      PRIx64, VIRTIO_MEM_MEMDEV_PROP, tmp->region_size,
1348383ee445SDavid Hildenbrand                      new_region_size);
1349383ee445SDavid Hildenbrand         return -EINVAL;
1350383ee445SDavid Hildenbrand     }
1351383ee445SDavid Hildenbrand     if (tmp->block_size != vmem->block_size) {
1352383ee445SDavid Hildenbrand         error_report("Property '%s' changed from 0x%" PRIx64 " to 0x%" PRIx64,
1353383ee445SDavid Hildenbrand                      VIRTIO_MEM_BLOCK_SIZE_PROP, tmp->block_size,
1354383ee445SDavid Hildenbrand                      vmem->block_size);
1355383ee445SDavid Hildenbrand         return -EINVAL;
1356383ee445SDavid Hildenbrand     }
1357383ee445SDavid Hildenbrand     if (tmp->node != vmem->node) {
1358383ee445SDavid Hildenbrand         error_report("Property '%s' changed from %" PRIu32 " to %" PRIu32,
1359383ee445SDavid Hildenbrand                      VIRTIO_MEM_NODE_PROP, tmp->node, vmem->node);
1360383ee445SDavid Hildenbrand         return -EINVAL;
1361383ee445SDavid Hildenbrand     }
1362383ee445SDavid Hildenbrand     return 0;
1363383ee445SDavid Hildenbrand }
1364383ee445SDavid Hildenbrand 
1365383ee445SDavid Hildenbrand static const VMStateDescription vmstate_virtio_mem_sanity_checks = {
1366383ee445SDavid Hildenbrand     .name = "virtio-mem-device/sanity-checks",
1367383ee445SDavid Hildenbrand     .pre_save = virtio_mem_mig_sanity_checks_pre_save,
1368383ee445SDavid Hildenbrand     .post_load = virtio_mem_mig_sanity_checks_post_load,
1369ca02a170SRichard Henderson     .fields = (const VMStateField[]) {
1370383ee445SDavid Hildenbrand         VMSTATE_UINT64(addr, VirtIOMEMMigSanityChecks),
1371383ee445SDavid Hildenbrand         VMSTATE_UINT64(region_size, VirtIOMEMMigSanityChecks),
1372383ee445SDavid Hildenbrand         VMSTATE_UINT64(block_size, VirtIOMEMMigSanityChecks),
1373383ee445SDavid Hildenbrand         VMSTATE_UINT32(node, VirtIOMEMMigSanityChecks),
1374383ee445SDavid Hildenbrand         VMSTATE_END_OF_LIST(),
1375383ee445SDavid Hildenbrand     },
1376383ee445SDavid Hildenbrand };
1377383ee445SDavid Hildenbrand 
virtio_mem_vmstate_field_exists(void * opaque,int version_id)13783b95a71bSDavid Hildenbrand static bool virtio_mem_vmstate_field_exists(void *opaque, int version_id)
13793b95a71bSDavid Hildenbrand {
13803b95a71bSDavid Hildenbrand     const VirtIOMEM *vmem = VIRTIO_MEM(opaque);
13813b95a71bSDavid Hildenbrand 
13823b95a71bSDavid Hildenbrand     /* With early migration, these fields were already migrated. */
13833b95a71bSDavid Hildenbrand     return !vmem->early_migration;
13843b95a71bSDavid Hildenbrand }
13853b95a71bSDavid Hildenbrand 
1386910b2576SDavid Hildenbrand static const VMStateDescription vmstate_virtio_mem_device = {
1387910b2576SDavid Hildenbrand     .name = "virtio-mem-device",
1388910b2576SDavid Hildenbrand     .minimum_version_id = 1,
1389910b2576SDavid Hildenbrand     .version_id = 1,
13900fd7616eSDavid Hildenbrand     .priority = MIG_PRI_VIRTIO_MEM,
1391910b2576SDavid Hildenbrand     .post_load = virtio_mem_post_load,
1392ca02a170SRichard Henderson     .fields = (const VMStateField[]) {
13933b95a71bSDavid Hildenbrand         VMSTATE_WITH_TMP_TEST(VirtIOMEM, virtio_mem_vmstate_field_exists,
13943b95a71bSDavid Hildenbrand                               VirtIOMEMMigSanityChecks,
1395383ee445SDavid Hildenbrand                               vmstate_virtio_mem_sanity_checks),
1396910b2576SDavid Hildenbrand         VMSTATE_UINT64(usable_region_size, VirtIOMEM),
13973b95a71bSDavid Hildenbrand         VMSTATE_UINT64_TEST(size, VirtIOMEM, virtio_mem_vmstate_field_exists),
1398910b2576SDavid Hildenbrand         VMSTATE_UINT64(requested_size, VirtIOMEM),
13993b95a71bSDavid Hildenbrand         VMSTATE_BITMAP_TEST(bitmap, VirtIOMEM, virtio_mem_vmstate_field_exists,
14003b95a71bSDavid Hildenbrand                             0, bitmap_size),
14013b95a71bSDavid Hildenbrand         VMSTATE_END_OF_LIST()
14023b95a71bSDavid Hildenbrand     },
14033b95a71bSDavid Hildenbrand };
14043b95a71bSDavid Hildenbrand 
14053b95a71bSDavid Hildenbrand /*
14063b95a71bSDavid Hildenbrand  * Transfer properties that are immutable while migration is active early,
14073b95a71bSDavid Hildenbrand  * such that we have have this information around before migrating any RAM
14083b95a71bSDavid Hildenbrand  * content.
14093b95a71bSDavid Hildenbrand  *
14103b95a71bSDavid Hildenbrand  * Note that virtio_mem_is_busy() makes sure these properties can no longer
14113b95a71bSDavid Hildenbrand  * change on the migration source until migration completed.
14123b95a71bSDavid Hildenbrand  *
14133b95a71bSDavid Hildenbrand  * With QEMU compat machines, we transmit these properties later, via
14143b95a71bSDavid Hildenbrand  * vmstate_virtio_mem_device instead -- see virtio_mem_vmstate_field_exists().
14153b95a71bSDavid Hildenbrand  */
14163b95a71bSDavid Hildenbrand static const VMStateDescription vmstate_virtio_mem_device_early = {
14173b95a71bSDavid Hildenbrand     .name = "virtio-mem-device-early",
14183b95a71bSDavid Hildenbrand     .minimum_version_id = 1,
14193b95a71bSDavid Hildenbrand     .version_id = 1,
14203b95a71bSDavid Hildenbrand     .early_setup = true,
1421d71920d4SDavid Hildenbrand     .post_load = virtio_mem_post_load_early,
1422ca02a170SRichard Henderson     .fields = (const VMStateField[]) {
14233b95a71bSDavid Hildenbrand         VMSTATE_WITH_TMP(VirtIOMEM, VirtIOMEMMigSanityChecks,
14243b95a71bSDavid Hildenbrand                          vmstate_virtio_mem_sanity_checks),
14253b95a71bSDavid Hildenbrand         VMSTATE_UINT64(size, VirtIOMEM),
1426910b2576SDavid Hildenbrand         VMSTATE_BITMAP(bitmap, VirtIOMEM, 0, bitmap_size),
1427910b2576SDavid Hildenbrand         VMSTATE_END_OF_LIST()
1428910b2576SDavid Hildenbrand     },
1429910b2576SDavid Hildenbrand };
1430910b2576SDavid Hildenbrand 
1431910b2576SDavid Hildenbrand static const VMStateDescription vmstate_virtio_mem = {
1432910b2576SDavid Hildenbrand     .name = "virtio-mem",
1433910b2576SDavid Hildenbrand     .minimum_version_id = 1,
1434910b2576SDavid Hildenbrand     .version_id = 1,
1435ca02a170SRichard Henderson     .fields = (const VMStateField[]) {
1436910b2576SDavid Hildenbrand         VMSTATE_VIRTIO_DEVICE,
1437910b2576SDavid Hildenbrand         VMSTATE_END_OF_LIST()
1438910b2576SDavid Hildenbrand     },
1439910b2576SDavid Hildenbrand };
1440910b2576SDavid Hildenbrand 
virtio_mem_fill_device_info(const VirtIOMEM * vmem,VirtioMEMDeviceInfo * vi)1441910b2576SDavid Hildenbrand static void virtio_mem_fill_device_info(const VirtIOMEM *vmem,
1442910b2576SDavid Hildenbrand                                         VirtioMEMDeviceInfo *vi)
1443910b2576SDavid Hildenbrand {
1444910b2576SDavid Hildenbrand     vi->memaddr = vmem->addr;
1445910b2576SDavid Hildenbrand     vi->node = vmem->node;
1446910b2576SDavid Hildenbrand     vi->requested_size = vmem->requested_size;
1447910b2576SDavid Hildenbrand     vi->size = vmem->size;
1448910b2576SDavid Hildenbrand     vi->max_size = memory_region_size(&vmem->memdev->mr);
1449910b2576SDavid Hildenbrand     vi->block_size = vmem->block_size;
1450910b2576SDavid Hildenbrand     vi->memdev = object_get_canonical_path(OBJECT(vmem->memdev));
1451910b2576SDavid Hildenbrand }
1452910b2576SDavid Hildenbrand 
virtio_mem_get_memory_region(VirtIOMEM * vmem,Error ** errp)1453910b2576SDavid Hildenbrand static MemoryRegion *virtio_mem_get_memory_region(VirtIOMEM *vmem, Error **errp)
1454910b2576SDavid Hildenbrand {
1455910b2576SDavid Hildenbrand     if (!vmem->memdev) {
1456910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property must be set", VIRTIO_MEM_MEMDEV_PROP);
1457910b2576SDavid Hildenbrand         return NULL;
1458177f9b1eSDavid Hildenbrand     } else if (vmem->dynamic_memslots) {
1459177f9b1eSDavid Hildenbrand         if (!vmem->mr) {
1460177f9b1eSDavid Hildenbrand             virtio_mem_prepare_mr(vmem);
1461177f9b1eSDavid Hildenbrand         }
1462177f9b1eSDavid Hildenbrand         return vmem->mr;
1463910b2576SDavid Hildenbrand     }
1464910b2576SDavid Hildenbrand 
1465910b2576SDavid Hildenbrand     return &vmem->memdev->mr;
1466910b2576SDavid Hildenbrand }
1467910b2576SDavid Hildenbrand 
virtio_mem_decide_memslots(VirtIOMEM * vmem,unsigned int limit)1468177f9b1eSDavid Hildenbrand static void virtio_mem_decide_memslots(VirtIOMEM *vmem, unsigned int limit)
1469177f9b1eSDavid Hildenbrand {
1470177f9b1eSDavid Hildenbrand     uint64_t region_size, memslot_size, min_memslot_size;
1471177f9b1eSDavid Hildenbrand     unsigned int memslots;
1472177f9b1eSDavid Hildenbrand     RAMBlock *rb;
1473177f9b1eSDavid Hildenbrand 
1474177f9b1eSDavid Hildenbrand     if (!vmem->dynamic_memslots) {
1475177f9b1eSDavid Hildenbrand         return;
1476177f9b1eSDavid Hildenbrand     }
1477177f9b1eSDavid Hildenbrand 
1478177f9b1eSDavid Hildenbrand     /* We're called exactly once, before realizing the device. */
1479177f9b1eSDavid Hildenbrand     assert(!vmem->nb_memslots);
1480177f9b1eSDavid Hildenbrand 
1481177f9b1eSDavid Hildenbrand     /* If realizing the device will fail, just assume a single memslot. */
1482177f9b1eSDavid Hildenbrand     if (limit <= 1 || !vmem->memdev || !vmem->memdev->mr.ram_block) {
1483177f9b1eSDavid Hildenbrand         vmem->nb_memslots = 1;
1484177f9b1eSDavid Hildenbrand         return;
1485177f9b1eSDavid Hildenbrand     }
1486177f9b1eSDavid Hildenbrand 
1487177f9b1eSDavid Hildenbrand     rb = vmem->memdev->mr.ram_block;
1488177f9b1eSDavid Hildenbrand     region_size = memory_region_size(&vmem->memdev->mr);
1489177f9b1eSDavid Hildenbrand 
1490177f9b1eSDavid Hildenbrand     /*
1491177f9b1eSDavid Hildenbrand      * Determine the default block size now, to determine the minimum memslot
1492177f9b1eSDavid Hildenbrand      * size. We want the minimum slot size to be at least the device block size.
1493177f9b1eSDavid Hildenbrand      */
1494177f9b1eSDavid Hildenbrand     if (!vmem->block_size) {
1495177f9b1eSDavid Hildenbrand         vmem->block_size = virtio_mem_default_block_size(rb);
1496177f9b1eSDavid Hildenbrand     }
1497177f9b1eSDavid Hildenbrand     /* If realizing the device will fail, just assume a single memslot. */
1498177f9b1eSDavid Hildenbrand     if (vmem->block_size < qemu_ram_pagesize(rb) ||
1499177f9b1eSDavid Hildenbrand         !QEMU_IS_ALIGNED(region_size, vmem->block_size)) {
1500177f9b1eSDavid Hildenbrand         vmem->nb_memslots = 1;
1501177f9b1eSDavid Hildenbrand         return;
1502177f9b1eSDavid Hildenbrand     }
1503177f9b1eSDavid Hildenbrand 
1504177f9b1eSDavid Hildenbrand     /*
1505177f9b1eSDavid Hildenbrand      * All memslots except the last one have a reasonable minimum size, and
1506177f9b1eSDavid Hildenbrand      * and all memslot sizes are aligned to the device block size.
1507177f9b1eSDavid Hildenbrand      */
1508177f9b1eSDavid Hildenbrand     memslot_size = QEMU_ALIGN_UP(region_size / limit, vmem->block_size);
1509177f9b1eSDavid Hildenbrand     min_memslot_size = MAX(vmem->block_size, VIRTIO_MEM_MIN_MEMSLOT_SIZE);
1510177f9b1eSDavid Hildenbrand     memslot_size = MAX(memslot_size, min_memslot_size);
1511177f9b1eSDavid Hildenbrand 
1512177f9b1eSDavid Hildenbrand     memslots = QEMU_ALIGN_UP(region_size, memslot_size) / memslot_size;
1513177f9b1eSDavid Hildenbrand     if (memslots != 1) {
1514177f9b1eSDavid Hildenbrand         vmem->memslot_size = memslot_size;
1515177f9b1eSDavid Hildenbrand     }
1516177f9b1eSDavid Hildenbrand     vmem->nb_memslots = memslots;
1517177f9b1eSDavid Hildenbrand }
1518177f9b1eSDavid Hildenbrand 
virtio_mem_get_memslots(VirtIOMEM * vmem)1519177f9b1eSDavid Hildenbrand static unsigned int virtio_mem_get_memslots(VirtIOMEM *vmem)
1520177f9b1eSDavid Hildenbrand {
1521177f9b1eSDavid Hildenbrand     if (!vmem->dynamic_memslots) {
1522177f9b1eSDavid Hildenbrand         /* Exactly one static RAM memory region. */
1523177f9b1eSDavid Hildenbrand         return 1;
1524177f9b1eSDavid Hildenbrand     }
1525177f9b1eSDavid Hildenbrand 
1526177f9b1eSDavid Hildenbrand     /* We're called after instructed to make a decision. */
1527177f9b1eSDavid Hildenbrand     g_assert(vmem->nb_memslots);
1528177f9b1eSDavid Hildenbrand     return vmem->nb_memslots;
1529177f9b1eSDavid Hildenbrand }
1530177f9b1eSDavid Hildenbrand 
virtio_mem_add_size_change_notifier(VirtIOMEM * vmem,Notifier * notifier)1531c95b4437SDavid Hildenbrand static void virtio_mem_add_size_change_notifier(VirtIOMEM *vmem,
1532c95b4437SDavid Hildenbrand                                                 Notifier *notifier)
1533c95b4437SDavid Hildenbrand {
1534c95b4437SDavid Hildenbrand     notifier_list_add(&vmem->size_change_notifiers, notifier);
1535c95b4437SDavid Hildenbrand }
1536c95b4437SDavid Hildenbrand 
virtio_mem_remove_size_change_notifier(VirtIOMEM * vmem,Notifier * notifier)1537c95b4437SDavid Hildenbrand static void virtio_mem_remove_size_change_notifier(VirtIOMEM *vmem,
1538c95b4437SDavid Hildenbrand                                                    Notifier *notifier)
1539c95b4437SDavid Hildenbrand {
1540c95b4437SDavid Hildenbrand     notifier_remove(notifier);
1541c95b4437SDavid Hildenbrand }
1542c95b4437SDavid Hildenbrand 
virtio_mem_get_size(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)1543910b2576SDavid Hildenbrand static void virtio_mem_get_size(Object *obj, Visitor *v, const char *name,
1544910b2576SDavid Hildenbrand                                 void *opaque, Error **errp)
1545910b2576SDavid Hildenbrand {
1546910b2576SDavid Hildenbrand     const VirtIOMEM *vmem = VIRTIO_MEM(obj);
1547910b2576SDavid Hildenbrand     uint64_t value = vmem->size;
1548910b2576SDavid Hildenbrand 
1549910b2576SDavid Hildenbrand     visit_type_size(v, name, &value, errp);
1550910b2576SDavid Hildenbrand }
1551910b2576SDavid Hildenbrand 
virtio_mem_get_requested_size(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)1552910b2576SDavid Hildenbrand static void virtio_mem_get_requested_size(Object *obj, Visitor *v,
1553910b2576SDavid Hildenbrand                                           const char *name, void *opaque,
1554910b2576SDavid Hildenbrand                                           Error **errp)
1555910b2576SDavid Hildenbrand {
1556910b2576SDavid Hildenbrand     const VirtIOMEM *vmem = VIRTIO_MEM(obj);
1557910b2576SDavid Hildenbrand     uint64_t value = vmem->requested_size;
1558910b2576SDavid Hildenbrand 
1559910b2576SDavid Hildenbrand     visit_type_size(v, name, &value, errp);
1560910b2576SDavid Hildenbrand }
1561910b2576SDavid Hildenbrand 
virtio_mem_set_requested_size(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)1562910b2576SDavid Hildenbrand static void virtio_mem_set_requested_size(Object *obj, Visitor *v,
1563910b2576SDavid Hildenbrand                                           const char *name, void *opaque,
1564910b2576SDavid Hildenbrand                                           Error **errp)
1565910b2576SDavid Hildenbrand {
1566910b2576SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(obj);
1567910b2576SDavid Hildenbrand     uint64_t value;
1568910b2576SDavid Hildenbrand 
1569d1c81c34SMarkus Armbruster     if (!visit_type_size(v, name, &value, errp)) {
1570910b2576SDavid Hildenbrand         return;
1571910b2576SDavid Hildenbrand     }
1572910b2576SDavid Hildenbrand 
1573910b2576SDavid Hildenbrand     /*
1574910b2576SDavid Hildenbrand      * The block size and memory backend are not fixed until the device was
1575910b2576SDavid Hildenbrand      * realized. realize() will verify these properties then.
1576910b2576SDavid Hildenbrand      */
1577910b2576SDavid Hildenbrand     if (DEVICE(obj)->realized) {
1578910b2576SDavid Hildenbrand         if (!QEMU_IS_ALIGNED(value, vmem->block_size)) {
1579910b2576SDavid Hildenbrand             error_setg(errp, "'%s' has to be multiples of '%s' (0x%" PRIx64
1580910b2576SDavid Hildenbrand                        ")", name, VIRTIO_MEM_BLOCK_SIZE_PROP,
1581910b2576SDavid Hildenbrand                        vmem->block_size);
1582910b2576SDavid Hildenbrand             return;
1583910b2576SDavid Hildenbrand         } else if (value > memory_region_size(&vmem->memdev->mr)) {
1584910b2576SDavid Hildenbrand             error_setg(errp, "'%s' cannot exceed the memory backend size"
1585910b2576SDavid Hildenbrand                        "(0x%" PRIx64 ")", name,
1586910b2576SDavid Hildenbrand                        memory_region_size(&vmem->memdev->mr));
1587910b2576SDavid Hildenbrand             return;
1588910b2576SDavid Hildenbrand         }
1589910b2576SDavid Hildenbrand 
1590910b2576SDavid Hildenbrand         if (value != vmem->requested_size) {
1591910b2576SDavid Hildenbrand             virtio_mem_resize_usable_region(vmem, value, false);
1592910b2576SDavid Hildenbrand             vmem->requested_size = value;
1593910b2576SDavid Hildenbrand         }
1594910b2576SDavid Hildenbrand         /*
1595910b2576SDavid Hildenbrand          * Trigger a config update so the guest gets notified. We trigger
1596910b2576SDavid Hildenbrand          * even if the size didn't change (especially helpful for debugging).
1597910b2576SDavid Hildenbrand          */
1598910b2576SDavid Hildenbrand         virtio_notify_config(VIRTIO_DEVICE(vmem));
1599910b2576SDavid Hildenbrand     } else {
1600910b2576SDavid Hildenbrand         vmem->requested_size = value;
1601910b2576SDavid Hildenbrand     }
1602910b2576SDavid Hildenbrand }
1603910b2576SDavid Hildenbrand 
virtio_mem_get_block_size(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)1604910b2576SDavid Hildenbrand static void virtio_mem_get_block_size(Object *obj, Visitor *v, const char *name,
1605910b2576SDavid Hildenbrand                                       void *opaque, Error **errp)
1606910b2576SDavid Hildenbrand {
1607910b2576SDavid Hildenbrand     const VirtIOMEM *vmem = VIRTIO_MEM(obj);
1608910b2576SDavid Hildenbrand     uint64_t value = vmem->block_size;
1609910b2576SDavid Hildenbrand 
1610228957feSDavid Hildenbrand     /*
1611228957feSDavid Hildenbrand      * If not configured by the user (and we're not realized yet), use the
1612228957feSDavid Hildenbrand      * default block size we would use with the current memory backend.
1613228957feSDavid Hildenbrand      */
1614228957feSDavid Hildenbrand     if (!value) {
1615228957feSDavid Hildenbrand         if (vmem->memdev && memory_region_is_ram(&vmem->memdev->mr)) {
1616228957feSDavid Hildenbrand             value = virtio_mem_default_block_size(vmem->memdev->mr.ram_block);
1617228957feSDavid Hildenbrand         } else {
1618228957feSDavid Hildenbrand             value = virtio_mem_thp_size();
1619228957feSDavid Hildenbrand         }
1620228957feSDavid Hildenbrand     }
1621228957feSDavid Hildenbrand 
1622910b2576SDavid Hildenbrand     visit_type_size(v, name, &value, errp);
1623910b2576SDavid Hildenbrand }
1624910b2576SDavid Hildenbrand 
virtio_mem_set_block_size(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)1625910b2576SDavid Hildenbrand static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name,
1626910b2576SDavid Hildenbrand                                       void *opaque, Error **errp)
1627910b2576SDavid Hildenbrand {
1628910b2576SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(obj);
1629910b2576SDavid Hildenbrand     uint64_t value;
1630910b2576SDavid Hildenbrand 
1631910b2576SDavid Hildenbrand     if (DEVICE(obj)->realized) {
1632910b2576SDavid Hildenbrand         error_setg(errp, "'%s' cannot be changed", name);
1633910b2576SDavid Hildenbrand         return;
1634910b2576SDavid Hildenbrand     }
1635910b2576SDavid Hildenbrand 
1636d1c81c34SMarkus Armbruster     if (!visit_type_size(v, name, &value, errp)) {
1637910b2576SDavid Hildenbrand         return;
1638910b2576SDavid Hildenbrand     }
1639910b2576SDavid Hildenbrand 
1640910b2576SDavid Hildenbrand     if (value < VIRTIO_MEM_MIN_BLOCK_SIZE) {
1641910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property has to be at least 0x%" PRIx32, name,
1642910b2576SDavid Hildenbrand                    VIRTIO_MEM_MIN_BLOCK_SIZE);
1643910b2576SDavid Hildenbrand         return;
1644910b2576SDavid Hildenbrand     } else if (!is_power_of_2(value)) {
1645910b2576SDavid Hildenbrand         error_setg(errp, "'%s' property has to be a power of two", name);
1646910b2576SDavid Hildenbrand         return;
1647910b2576SDavid Hildenbrand     }
1648910b2576SDavid Hildenbrand     vmem->block_size = value;
1649910b2576SDavid Hildenbrand }
1650910b2576SDavid Hildenbrand 
virtio_mem_instance_init(Object * obj)1651910b2576SDavid Hildenbrand static void virtio_mem_instance_init(Object *obj)
1652910b2576SDavid Hildenbrand {
1653910b2576SDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(obj);
1654910b2576SDavid Hildenbrand 
1655c95b4437SDavid Hildenbrand     notifier_list_init(&vmem->size_change_notifiers);
16562044969fSDavid Hildenbrand     QLIST_INIT(&vmem->rdl_list);
1657910b2576SDavid Hildenbrand 
1658910b2576SDavid Hildenbrand     object_property_add(obj, VIRTIO_MEM_SIZE_PROP, "size", virtio_mem_get_size,
1659910b2576SDavid Hildenbrand                         NULL, NULL, NULL);
1660910b2576SDavid Hildenbrand     object_property_add(obj, VIRTIO_MEM_REQUESTED_SIZE_PROP, "size",
1661910b2576SDavid Hildenbrand                         virtio_mem_get_requested_size,
1662910b2576SDavid Hildenbrand                         virtio_mem_set_requested_size, NULL, NULL);
1663910b2576SDavid Hildenbrand     object_property_add(obj, VIRTIO_MEM_BLOCK_SIZE_PROP, "size",
1664910b2576SDavid Hildenbrand                         virtio_mem_get_block_size, virtio_mem_set_block_size,
1665910b2576SDavid Hildenbrand                         NULL, NULL);
1666910b2576SDavid Hildenbrand }
1667910b2576SDavid Hildenbrand 
virtio_mem_instance_finalize(Object * obj)1668177f9b1eSDavid Hildenbrand static void virtio_mem_instance_finalize(Object *obj)
1669177f9b1eSDavid Hildenbrand {
1670177f9b1eSDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(obj);
1671177f9b1eSDavid Hildenbrand 
1672177f9b1eSDavid Hildenbrand     /*
1673177f9b1eSDavid Hildenbrand      * Note: the core already dropped the references on all memory regions
1674177f9b1eSDavid Hildenbrand      * (it's passed as the owner to memory_region_init_*()) and finalized
1675177f9b1eSDavid Hildenbrand      * these objects. We can simply free the memory.
1676177f9b1eSDavid Hildenbrand      */
1677177f9b1eSDavid Hildenbrand     g_free(vmem->memslots);
1678177f9b1eSDavid Hildenbrand     vmem->memslots = NULL;
1679177f9b1eSDavid Hildenbrand     g_free(vmem->mr);
1680177f9b1eSDavid Hildenbrand     vmem->mr = NULL;
1681177f9b1eSDavid Hildenbrand }
1682177f9b1eSDavid Hildenbrand 
1683910b2576SDavid Hildenbrand static Property virtio_mem_properties[] = {
1684910b2576SDavid Hildenbrand     DEFINE_PROP_UINT64(VIRTIO_MEM_ADDR_PROP, VirtIOMEM, addr, 0),
1685910b2576SDavid Hildenbrand     DEFINE_PROP_UINT32(VIRTIO_MEM_NODE_PROP, VirtIOMEM, node, 0),
168609b3b7e0SDavid Hildenbrand     DEFINE_PROP_BOOL(VIRTIO_MEM_PREALLOC_PROP, VirtIOMEM, prealloc, false),
1687910b2576SDavid Hildenbrand     DEFINE_PROP_LINK(VIRTIO_MEM_MEMDEV_PROP, VirtIOMEM, memdev,
1688910b2576SDavid Hildenbrand                      TYPE_MEMORY_BACKEND, HostMemoryBackend *),
168923ad8decSDavid Hildenbrand #if defined(VIRTIO_MEM_HAS_LEGACY_GUESTS)
169023ad8decSDavid Hildenbrand     DEFINE_PROP_ON_OFF_AUTO(VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP, VirtIOMEM,
1691d5cef025SDavid Hildenbrand                             unplugged_inaccessible, ON_OFF_AUTO_ON),
169223ad8decSDavid Hildenbrand #endif
16933b95a71bSDavid Hildenbrand     DEFINE_PROP_BOOL(VIRTIO_MEM_EARLY_MIGRATION_PROP, VirtIOMEM,
16943b95a71bSDavid Hildenbrand                      early_migration, true),
1695177f9b1eSDavid Hildenbrand     DEFINE_PROP_BOOL(VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP, VirtIOMEM,
1696177f9b1eSDavid Hildenbrand                      dynamic_memslots, false),
1697910b2576SDavid Hildenbrand     DEFINE_PROP_END_OF_LIST(),
1698910b2576SDavid Hildenbrand };
1699910b2576SDavid Hildenbrand 
virtio_mem_rdm_get_min_granularity(const RamDiscardManager * rdm,const MemoryRegion * mr)17002044969fSDavid Hildenbrand static uint64_t virtio_mem_rdm_get_min_granularity(const RamDiscardManager *rdm,
17012044969fSDavid Hildenbrand                                                    const MemoryRegion *mr)
17022044969fSDavid Hildenbrand {
17032044969fSDavid Hildenbrand     const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
17042044969fSDavid Hildenbrand 
17052044969fSDavid Hildenbrand     g_assert(mr == &vmem->memdev->mr);
17062044969fSDavid Hildenbrand     return vmem->block_size;
17072044969fSDavid Hildenbrand }
17082044969fSDavid Hildenbrand 
virtio_mem_rdm_is_populated(const RamDiscardManager * rdm,const MemoryRegionSection * s)17092044969fSDavid Hildenbrand static bool virtio_mem_rdm_is_populated(const RamDiscardManager *rdm,
17102044969fSDavid Hildenbrand                                         const MemoryRegionSection *s)
17112044969fSDavid Hildenbrand {
17122044969fSDavid Hildenbrand     const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
17132044969fSDavid Hildenbrand     uint64_t start_gpa = vmem->addr + s->offset_within_region;
17142044969fSDavid Hildenbrand     uint64_t end_gpa = start_gpa + int128_get64(s->size);
17152044969fSDavid Hildenbrand 
17162044969fSDavid Hildenbrand     g_assert(s->mr == &vmem->memdev->mr);
17172044969fSDavid Hildenbrand 
17182044969fSDavid Hildenbrand     start_gpa = QEMU_ALIGN_DOWN(start_gpa, vmem->block_size);
17192044969fSDavid Hildenbrand     end_gpa = QEMU_ALIGN_UP(end_gpa, vmem->block_size);
17202044969fSDavid Hildenbrand 
17212044969fSDavid Hildenbrand     if (!virtio_mem_valid_range(vmem, start_gpa, end_gpa - start_gpa)) {
17222044969fSDavid Hildenbrand         return false;
17232044969fSDavid Hildenbrand     }
17242044969fSDavid Hildenbrand 
172525c89303SDavid Hildenbrand     return virtio_mem_is_range_plugged(vmem, start_gpa, end_gpa - start_gpa);
17262044969fSDavid Hildenbrand }
17272044969fSDavid Hildenbrand 
17282044969fSDavid Hildenbrand struct VirtIOMEMReplayData {
17292044969fSDavid Hildenbrand     void *fn;
17302044969fSDavid Hildenbrand     void *opaque;
17312044969fSDavid Hildenbrand };
17322044969fSDavid Hildenbrand 
virtio_mem_rdm_replay_populated_cb(MemoryRegionSection * s,void * arg)17332044969fSDavid Hildenbrand static int virtio_mem_rdm_replay_populated_cb(MemoryRegionSection *s, void *arg)
17342044969fSDavid Hildenbrand {
17352044969fSDavid Hildenbrand     struct VirtIOMEMReplayData *data = arg;
17362044969fSDavid Hildenbrand 
17372044969fSDavid Hildenbrand     return ((ReplayRamPopulate)data->fn)(s, data->opaque);
17382044969fSDavid Hildenbrand }
17392044969fSDavid Hildenbrand 
virtio_mem_rdm_replay_populated(const RamDiscardManager * rdm,MemoryRegionSection * s,ReplayRamPopulate replay_fn,void * opaque)17402044969fSDavid Hildenbrand static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm,
17412044969fSDavid Hildenbrand                                            MemoryRegionSection *s,
17422044969fSDavid Hildenbrand                                            ReplayRamPopulate replay_fn,
17432044969fSDavid Hildenbrand                                            void *opaque)
17442044969fSDavid Hildenbrand {
17452044969fSDavid Hildenbrand     const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
17462044969fSDavid Hildenbrand     struct VirtIOMEMReplayData data = {
17472044969fSDavid Hildenbrand         .fn = replay_fn,
17482044969fSDavid Hildenbrand         .opaque = opaque,
17492044969fSDavid Hildenbrand     };
17502044969fSDavid Hildenbrand 
17512044969fSDavid Hildenbrand     g_assert(s->mr == &vmem->memdev->mr);
17522044969fSDavid Hildenbrand     return virtio_mem_for_each_plugged_section(vmem, s, &data,
17532044969fSDavid Hildenbrand                                             virtio_mem_rdm_replay_populated_cb);
17542044969fSDavid Hildenbrand }
17552044969fSDavid Hildenbrand 
virtio_mem_rdm_replay_discarded_cb(MemoryRegionSection * s,void * arg)1756372aa6fdSDavid Hildenbrand static int virtio_mem_rdm_replay_discarded_cb(MemoryRegionSection *s,
1757372aa6fdSDavid Hildenbrand                                               void *arg)
1758372aa6fdSDavid Hildenbrand {
1759372aa6fdSDavid Hildenbrand     struct VirtIOMEMReplayData *data = arg;
1760372aa6fdSDavid Hildenbrand 
1761372aa6fdSDavid Hildenbrand     ((ReplayRamDiscard)data->fn)(s, data->opaque);
1762372aa6fdSDavid Hildenbrand     return 0;
1763372aa6fdSDavid Hildenbrand }
1764372aa6fdSDavid Hildenbrand 
virtio_mem_rdm_replay_discarded(const RamDiscardManager * rdm,MemoryRegionSection * s,ReplayRamDiscard replay_fn,void * opaque)1765372aa6fdSDavid Hildenbrand static void virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm,
1766372aa6fdSDavid Hildenbrand                                             MemoryRegionSection *s,
1767372aa6fdSDavid Hildenbrand                                             ReplayRamDiscard replay_fn,
1768372aa6fdSDavid Hildenbrand                                             void *opaque)
1769372aa6fdSDavid Hildenbrand {
1770372aa6fdSDavid Hildenbrand     const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
1771372aa6fdSDavid Hildenbrand     struct VirtIOMEMReplayData data = {
1772372aa6fdSDavid Hildenbrand         .fn = replay_fn,
1773372aa6fdSDavid Hildenbrand         .opaque = opaque,
1774372aa6fdSDavid Hildenbrand     };
1775372aa6fdSDavid Hildenbrand 
1776372aa6fdSDavid Hildenbrand     g_assert(s->mr == &vmem->memdev->mr);
1777372aa6fdSDavid Hildenbrand     virtio_mem_for_each_unplugged_section(vmem, s, &data,
1778372aa6fdSDavid Hildenbrand                                           virtio_mem_rdm_replay_discarded_cb);
1779372aa6fdSDavid Hildenbrand }
1780372aa6fdSDavid Hildenbrand 
virtio_mem_rdm_register_listener(RamDiscardManager * rdm,RamDiscardListener * rdl,MemoryRegionSection * s)17812044969fSDavid Hildenbrand static void virtio_mem_rdm_register_listener(RamDiscardManager *rdm,
17822044969fSDavid Hildenbrand                                              RamDiscardListener *rdl,
17832044969fSDavid Hildenbrand                                              MemoryRegionSection *s)
17842044969fSDavid Hildenbrand {
17852044969fSDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(rdm);
17862044969fSDavid Hildenbrand     int ret;
17872044969fSDavid Hildenbrand 
17882044969fSDavid Hildenbrand     g_assert(s->mr == &vmem->memdev->mr);
17892044969fSDavid Hildenbrand     rdl->section = memory_region_section_new_copy(s);
17902044969fSDavid Hildenbrand 
17912044969fSDavid Hildenbrand     QLIST_INSERT_HEAD(&vmem->rdl_list, rdl, next);
17922044969fSDavid Hildenbrand     ret = virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
17932044969fSDavid Hildenbrand                                               virtio_mem_notify_populate_cb);
17942044969fSDavid Hildenbrand     if (ret) {
17952044969fSDavid Hildenbrand         error_report("%s: Replaying plugged ranges failed: %s", __func__,
17962044969fSDavid Hildenbrand                      strerror(-ret));
17972044969fSDavid Hildenbrand     }
17982044969fSDavid Hildenbrand }
17992044969fSDavid Hildenbrand 
virtio_mem_rdm_unregister_listener(RamDiscardManager * rdm,RamDiscardListener * rdl)18002044969fSDavid Hildenbrand static void virtio_mem_rdm_unregister_listener(RamDiscardManager *rdm,
18012044969fSDavid Hildenbrand                                                RamDiscardListener *rdl)
18022044969fSDavid Hildenbrand {
18032044969fSDavid Hildenbrand     VirtIOMEM *vmem = VIRTIO_MEM(rdm);
18042044969fSDavid Hildenbrand 
18052044969fSDavid Hildenbrand     g_assert(rdl->section->mr == &vmem->memdev->mr);
18062044969fSDavid Hildenbrand     if (vmem->size) {
18072044969fSDavid Hildenbrand         if (rdl->double_discard_supported) {
18082044969fSDavid Hildenbrand             rdl->notify_discard(rdl, rdl->section);
18092044969fSDavid Hildenbrand         } else {
18102044969fSDavid Hildenbrand             virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
18112044969fSDavid Hildenbrand                                                 virtio_mem_notify_discard_cb);
18122044969fSDavid Hildenbrand         }
18132044969fSDavid Hildenbrand     }
18142044969fSDavid Hildenbrand 
18152044969fSDavid Hildenbrand     memory_region_section_free_copy(rdl->section);
18162044969fSDavid Hildenbrand     rdl->section = NULL;
18172044969fSDavid Hildenbrand     QLIST_REMOVE(rdl, next);
18182044969fSDavid Hildenbrand }
18192044969fSDavid Hildenbrand 
virtio_mem_unplug_request_check(VirtIOMEM * vmem,Error ** errp)182092a8ee1bSDavid Hildenbrand static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp)
182192a8ee1bSDavid Hildenbrand {
182292a8ee1bSDavid Hildenbrand     if (vmem->unplugged_inaccessible == ON_OFF_AUTO_OFF) {
182392a8ee1bSDavid Hildenbrand         /*
182492a8ee1bSDavid Hildenbrand          * We could allow it with a usable region size of 0, but let's just
182592a8ee1bSDavid Hildenbrand          * not care about that legacy setting.
182692a8ee1bSDavid Hildenbrand          */
182792a8ee1bSDavid Hildenbrand         error_setg(errp, "virtio-mem device cannot get unplugged while"
182892a8ee1bSDavid Hildenbrand                    " '" VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP "' != 'on'");
182992a8ee1bSDavid Hildenbrand         return;
183092a8ee1bSDavid Hildenbrand     }
183192a8ee1bSDavid Hildenbrand 
183292a8ee1bSDavid Hildenbrand     if (vmem->size) {
18334d13ae45SDavid Hildenbrand         error_setg(errp, "virtio-mem device cannot get unplugged while some"
18344d13ae45SDavid Hildenbrand                    " of its memory is still plugged");
183592a8ee1bSDavid Hildenbrand         return;
183692a8ee1bSDavid Hildenbrand     }
183792a8ee1bSDavid Hildenbrand     if (vmem->requested_size) {
183892a8ee1bSDavid Hildenbrand         error_setg(errp, "virtio-mem device cannot get unplugged while"
183992a8ee1bSDavid Hildenbrand                    " '" VIRTIO_MEM_REQUESTED_SIZE_PROP "' != '0'");
184092a8ee1bSDavid Hildenbrand         return;
184192a8ee1bSDavid Hildenbrand     }
184292a8ee1bSDavid Hildenbrand }
184392a8ee1bSDavid Hildenbrand 
virtio_mem_get_reset_state(Object * obj)1844c009a311SJuraj Marcin static ResettableState *virtio_mem_get_reset_state(Object *obj)
1845c009a311SJuraj Marcin {
1846c009a311SJuraj Marcin     VirtIOMEM *vmem = VIRTIO_MEM(obj);
1847c009a311SJuraj Marcin     return &vmem->reset_state;
1848c009a311SJuraj Marcin }
1849c009a311SJuraj Marcin 
virtio_mem_system_reset_hold(Object * obj,ResetType type)1850c009a311SJuraj Marcin static void virtio_mem_system_reset_hold(Object *obj, ResetType type)
1851c009a311SJuraj Marcin {
1852c009a311SJuraj Marcin     VirtIOMEM *vmem = VIRTIO_MEM(obj);
1853c009a311SJuraj Marcin 
1854c009a311SJuraj Marcin     /*
18551f5f4905SJuraj Marcin      * When waking up from standby/suspend-to-ram, do not unplug any memory.
18561f5f4905SJuraj Marcin      */
18571f5f4905SJuraj Marcin     if (type == RESET_TYPE_WAKEUP) {
18581f5f4905SJuraj Marcin         return;
18591f5f4905SJuraj Marcin     }
18601f5f4905SJuraj Marcin 
18611f5f4905SJuraj Marcin     /*
1862c009a311SJuraj Marcin      * During usual resets, we will unplug all memory and shrink the usable
1863c009a311SJuraj Marcin      * region size. This is, however, not possible in all scenarios. Then,
1864c009a311SJuraj Marcin      * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL).
1865c009a311SJuraj Marcin      */
1866c009a311SJuraj Marcin     virtio_mem_unplug_all(vmem);
1867c009a311SJuraj Marcin }
1868c009a311SJuraj Marcin 
virtio_mem_class_init(ObjectClass * klass,void * data)1869910b2576SDavid Hildenbrand static void virtio_mem_class_init(ObjectClass *klass, void *data)
1870910b2576SDavid Hildenbrand {
1871910b2576SDavid Hildenbrand     DeviceClass *dc = DEVICE_CLASS(klass);
1872910b2576SDavid Hildenbrand     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
1873910b2576SDavid Hildenbrand     VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass);
18742044969fSDavid Hildenbrand     RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass);
1875c009a311SJuraj Marcin     ResettableClass *rc = RESETTABLE_CLASS(klass);
1876910b2576SDavid Hildenbrand 
1877910b2576SDavid Hildenbrand     device_class_set_props(dc, virtio_mem_properties);
1878910b2576SDavid Hildenbrand     dc->vmsd = &vmstate_virtio_mem;
1879910b2576SDavid Hildenbrand 
1880910b2576SDavid Hildenbrand     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1881910b2576SDavid Hildenbrand     vdc->realize = virtio_mem_device_realize;
1882910b2576SDavid Hildenbrand     vdc->unrealize = virtio_mem_device_unrealize;
1883910b2576SDavid Hildenbrand     vdc->get_config = virtio_mem_get_config;
1884910b2576SDavid Hildenbrand     vdc->get_features = virtio_mem_get_features;
188523ad8decSDavid Hildenbrand     vdc->validate_features = virtio_mem_validate_features;
1886910b2576SDavid Hildenbrand     vdc->vmsd = &vmstate_virtio_mem_device;
1887910b2576SDavid Hildenbrand 
1888910b2576SDavid Hildenbrand     vmc->fill_device_info = virtio_mem_fill_device_info;
1889910b2576SDavid Hildenbrand     vmc->get_memory_region = virtio_mem_get_memory_region;
1890177f9b1eSDavid Hildenbrand     vmc->decide_memslots = virtio_mem_decide_memslots;
1891177f9b1eSDavid Hildenbrand     vmc->get_memslots = virtio_mem_get_memslots;
1892c95b4437SDavid Hildenbrand     vmc->add_size_change_notifier = virtio_mem_add_size_change_notifier;
1893c95b4437SDavid Hildenbrand     vmc->remove_size_change_notifier = virtio_mem_remove_size_change_notifier;
189492a8ee1bSDavid Hildenbrand     vmc->unplug_request_check = virtio_mem_unplug_request_check;
18952044969fSDavid Hildenbrand 
18962044969fSDavid Hildenbrand     rdmc->get_min_granularity = virtio_mem_rdm_get_min_granularity;
18972044969fSDavid Hildenbrand     rdmc->is_populated = virtio_mem_rdm_is_populated;
18982044969fSDavid Hildenbrand     rdmc->replay_populated = virtio_mem_rdm_replay_populated;
1899372aa6fdSDavid Hildenbrand     rdmc->replay_discarded = virtio_mem_rdm_replay_discarded;
19002044969fSDavid Hildenbrand     rdmc->register_listener = virtio_mem_rdm_register_listener;
19012044969fSDavid Hildenbrand     rdmc->unregister_listener = virtio_mem_rdm_unregister_listener;
1902c009a311SJuraj Marcin 
1903c009a311SJuraj Marcin     rc->get_state = virtio_mem_get_reset_state;
1904c009a311SJuraj Marcin     rc->phases.hold = virtio_mem_system_reset_hold;
1905910b2576SDavid Hildenbrand }
1906910b2576SDavid Hildenbrand 
1907910b2576SDavid Hildenbrand static const TypeInfo virtio_mem_info = {
1908910b2576SDavid Hildenbrand     .name = TYPE_VIRTIO_MEM,
1909910b2576SDavid Hildenbrand     .parent = TYPE_VIRTIO_DEVICE,
1910910b2576SDavid Hildenbrand     .instance_size = sizeof(VirtIOMEM),
1911910b2576SDavid Hildenbrand     .instance_init = virtio_mem_instance_init,
1912177f9b1eSDavid Hildenbrand     .instance_finalize = virtio_mem_instance_finalize,
1913910b2576SDavid Hildenbrand     .class_init = virtio_mem_class_init,
1914910b2576SDavid Hildenbrand     .class_size = sizeof(VirtIOMEMClass),
19152044969fSDavid Hildenbrand     .interfaces = (InterfaceInfo[]) {
19162044969fSDavid Hildenbrand         { TYPE_RAM_DISCARD_MANAGER },
19172044969fSDavid Hildenbrand         { }
19182044969fSDavid Hildenbrand     },
1919910b2576SDavid Hildenbrand };
1920910b2576SDavid Hildenbrand 
virtio_register_types(void)1921910b2576SDavid Hildenbrand static void virtio_register_types(void)
1922910b2576SDavid Hildenbrand {
1923910b2576SDavid Hildenbrand     type_register_static(&virtio_mem_info);
1924910b2576SDavid Hildenbrand }
1925910b2576SDavid Hildenbrand 
1926910b2576SDavid Hildenbrand type_init(virtio_register_types)
1927