-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge tag 'pull-hv-balloon-20231106' of https://github.com/maciejsszm…
…igiero/qemu into staging Hyper-V Dynamic Memory protocol driver. This driver is like virtio-balloon on steroids for Windows guests: it allows both changing the guest memory allocation via ballooning and inserting pieces of extra RAM into it on demand from a provided memory backend via Windows-native Hyper-V Dynamic Memory protocol. * Preparatory patches to support empty memory devices and ones with large alignment requirements. * Revert of recently added "hw/virtio/virtio-pmem: Replace impossible check by assertion" commit 5960f25 since this series makes this situation possible again. * Protocol definitions. * Hyper-V DM protocol driver (hv-balloon) base (ballooning only). * Hyper-V DM protocol driver (hv-balloon) hot-add support. * qapi query-memory-devices support for the driver. * qapi HV_BALLOON_STATUS_REPORT event. * The relevant PC machine plumbing. * New MAINTAINERS entry for the above. # -----BEGIN PGP SIGNATURE----- # # iQGzBAABCAAdFiEE4ndqq6COJv9aG0oJUrHW6VHQzgcFAmVI81IACgkQUrHW6VHQ # zgdzTgv+I5eV2R01YLOBBJhBjzxZ4/BUqkuUHNxHpfjuCqEIzPb7FIfoZ4ZyXZFT # YJdSE4lPeTZLrmmi/Nt6G0rUKDvdCeIgkS2VLHFSsTV8IzcT71BTRGzV0zAjUF5v # yDH6uzo6e9gmaziIalRjibUxSDjCQmoCifms2rS2DwazADudUp+naGfm+3uyA0gM # raOfBfRkNZsDqhXg2ayuqPIES75xQONoON9xYPKDAthS48POEbqtWBKuFopr3kXY # y0eph+NAw+RajCyLYKM3poIgaSu3l4WegInuKQffzqKR8dxrbwPdCmtgo6NSHx0W # uDfl7FUBnGzrR18VU4ZfTSrF5SVscGwF9EL7uocJen15inJjl1q3G53uZgyGzHLC # cw8fKMjucmE8njQR2qiMyX0b+T4+9nKO1rykBgTG/+c9prRUVoxYpFCF117Ei0U8 # QzLGACW1oK+LV41bekWAye7w9pShUtFaxffhPbJeZDDGh7q0x61R3Z3yKkA07p46 # /YWWFWUD # =RAb0 # -----END PGP SIGNATURE----- # gpg: Signature made Mon 06 Nov 2023 22:08:18 HKT # gpg: using RSA key E2776AABA08E26FF5A1B4A0952B1D6E951D0CE07 # gpg: Good signature from "Maciej S. Szmigiero <mail@maciej.szmigiero.name>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 727A 0D4D DB9E D9F6 039B ECEF 847F 5E37 90CE 0977 # Subkey fingerprint: E277 6AAB A08E 26FF 5A1B 4A09 52B1 D6E9 51D0 CE07 * tag 'pull-hv-balloon-20231106' of https://github.com/maciejsszmigiero/qemu: MAINTAINERS: Add an entry for Hyper-V Dynamic Memory Protocol hw/i386/pc: Support hv-balloon qapi: Add HV_BALLOON_STATUS_REPORT event and its QMP query command qapi: Add query-memory-devices support to hv-balloon Add Hyper-V Dynamic Memory Protocol driver (hv-balloon) hot-add support Add Hyper-V Dynamic Memory Protocol driver (hv-balloon) base Add Hyper-V Dynamic Memory Protocol definitions memory-device: Drop size alignment check Revert "hw/virtio/virtio-pmem: Replace impossible check by assertion" memory-device: Support empty memory devices Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
- Loading branch information
Showing
26 changed files
with
3,180 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,3 +46,6 @@ config FUZZ | |
config VFIO_USER_SERVER_ALLOWED | ||
bool | ||
imply VFIO_USER_SERVER | ||
|
||
config HV_BALLOON_POSSIBLE | ||
bool |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* QEMU Hyper-V Dynamic Memory Protocol driver | ||
* | ||
* Copyright (C) 2020-2023 Oracle and/or its affiliates. | ||
* | ||
* This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
* See the COPYING file in the top-level directory. | ||
*/ | ||
|
||
#ifndef HW_HYPERV_HV_BALLOON_INTERNAL_H | ||
#define HW_HYPERV_HV_BALLOON_INTERNAL_H | ||
|
||
#include "qemu/osdep.h" | ||
|
||
#define HV_BALLOON_PFN_SHIFT 12 | ||
#define HV_BALLOON_PAGE_SIZE (1 << HV_BALLOON_PFN_SHIFT) | ||
|
||
#define SUM_OVERFLOW_U64(in1, in2) ((in1) > UINT64_MAX - (in2)) | ||
#define SUM_SATURATE_U64(in1, in2) \ | ||
({ \ | ||
uint64_t _in1 = (in1), _in2 = (in2); \ | ||
uint64_t _result; \ | ||
\ | ||
if (!SUM_OVERFLOW_U64(_in1, _in2)) { \ | ||
_result = _in1 + _in2; \ | ||
} else { \ | ||
_result = UINT64_MAX; \ | ||
} \ | ||
\ | ||
_result; \ | ||
}) | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
/* | ||
* QEMU Hyper-V Dynamic Memory Protocol driver | ||
* | ||
* Copyright (C) 2020-2023 Oracle and/or its affiliates. | ||
* | ||
* This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
* See the COPYING file in the top-level directory. | ||
*/ | ||
|
||
#include "hv-balloon-internal.h" | ||
#include "hv-balloon-our_range_memslots.h" | ||
#include "trace.h" | ||
|
||
/* OurRange */ | ||
static void our_range_init(OurRange *our_range, uint64_t start, uint64_t count) | ||
{ | ||
assert(count <= UINT64_MAX - start); | ||
our_range->range.start = start; | ||
our_range->range.count = count; | ||
|
||
hvb_page_range_tree_init(&our_range->removed_guest); | ||
hvb_page_range_tree_init(&our_range->removed_both); | ||
|
||
/* mark the whole range as unused but for potential use */ | ||
our_range->added = 0; | ||
our_range->unusable_tail = 0; | ||
} | ||
|
||
static void our_range_destroy(OurRange *our_range) | ||
{ | ||
hvb_page_range_tree_destroy(&our_range->removed_guest); | ||
hvb_page_range_tree_destroy(&our_range->removed_both); | ||
} | ||
|
||
void hvb_our_range_clear_removed_trees(OurRange *our_range) | ||
{ | ||
hvb_page_range_tree_destroy(&our_range->removed_guest); | ||
hvb_page_range_tree_destroy(&our_range->removed_both); | ||
hvb_page_range_tree_init(&our_range->removed_guest); | ||
hvb_page_range_tree_init(&our_range->removed_both); | ||
} | ||
|
||
void hvb_our_range_mark_added(OurRange *our_range, uint64_t additional_size) | ||
{ | ||
assert(additional_size <= UINT64_MAX - our_range->added); | ||
|
||
our_range->added += additional_size; | ||
|
||
assert(our_range->added <= UINT64_MAX - our_range->unusable_tail); | ||
assert(our_range->added + our_range->unusable_tail <= | ||
our_range->range.count); | ||
} | ||
|
||
/* OurRangeMemslots */ | ||
static void our_range_memslots_init_slots(OurRangeMemslots *our_range, | ||
MemoryRegion *backing_mr, | ||
Object *memslot_owner) | ||
{ | ||
OurRangeMemslotsSlots *memslots = &our_range->slots; | ||
unsigned int idx; | ||
uint64_t memslot_offset; | ||
|
||
assert(memslots->count > 0); | ||
memslots->slots = g_new0(MemoryRegion, memslots->count); | ||
|
||
/* Initialize our memslots, but don't map them yet. */ | ||
assert(memslots->size_each > 0); | ||
for (idx = 0, memslot_offset = 0; idx < memslots->count; | ||
idx++, memslot_offset += memslots->size_each) { | ||
uint64_t memslot_size; | ||
g_autofree char *name = NULL; | ||
|
||
/* The size of the last memslot might be smaller. */ | ||
if (idx == memslots->count - 1) { | ||
uint64_t region_size; | ||
|
||
assert(our_range->mr); | ||
region_size = memory_region_size(our_range->mr); | ||
memslot_size = region_size - memslot_offset; | ||
} else { | ||
memslot_size = memslots->size_each; | ||
} | ||
|
||
name = g_strdup_printf("memslot-%u", idx); | ||
memory_region_init_alias(&memslots->slots[idx], memslot_owner, name, | ||
backing_mr, memslot_offset, memslot_size); | ||
/* | ||
* We want to be able to atomically and efficiently activate/deactivate | ||
* individual memslots without affecting adjacent memslots in memory | ||
* notifiers. | ||
*/ | ||
memory_region_set_unmergeable(&memslots->slots[idx], true); | ||
} | ||
|
||
memslots->mapped_count = 0; | ||
} | ||
|
||
OurRangeMemslots *hvb_our_range_memslots_new(uint64_t addr, | ||
MemoryRegion *parent_mr, | ||
MemoryRegion *backing_mr, | ||
Object *memslot_owner, | ||
unsigned int memslot_count, | ||
uint64_t memslot_size) | ||
{ | ||
OurRangeMemslots *our_range; | ||
|
||
our_range = g_malloc(sizeof(*our_range)); | ||
our_range_init(&our_range->range, | ||
addr / HV_BALLOON_PAGE_SIZE, | ||
memory_region_size(parent_mr) / HV_BALLOON_PAGE_SIZE); | ||
our_range->slots.size_each = memslot_size; | ||
our_range->slots.count = memslot_count; | ||
our_range->mr = parent_mr; | ||
our_range_memslots_init_slots(our_range, backing_mr, memslot_owner); | ||
|
||
return our_range; | ||
} | ||
|
||
static void our_range_memslots_free_memslots(OurRangeMemslots *our_range) | ||
{ | ||
OurRangeMemslotsSlots *memslots = &our_range->slots; | ||
unsigned int idx; | ||
uint64_t offset; | ||
|
||
memory_region_transaction_begin(); | ||
for (idx = 0, offset = 0; idx < memslots->mapped_count; | ||
idx++, offset += memslots->size_each) { | ||
trace_hv_balloon_unmap_slot(idx, memslots->count, offset); | ||
assert(memory_region_is_mapped(&memslots->slots[idx])); | ||
memory_region_del_subregion(our_range->mr, &memslots->slots[idx]); | ||
} | ||
memory_region_transaction_commit(); | ||
|
||
for (idx = 0; idx < memslots->count; idx++) { | ||
object_unparent(OBJECT(&memslots->slots[idx])); | ||
} | ||
|
||
g_clear_pointer(&our_range->slots.slots, g_free); | ||
} | ||
|
||
void hvb_our_range_memslots_free(OurRangeMemslots *our_range) | ||
{ | ||
OurRangeMemslotsSlots *memslots = &our_range->slots; | ||
MemoryRegion *hostmem_mr; | ||
RAMBlock *rb; | ||
|
||
assert(our_range->slots.count > 0); | ||
assert(our_range->slots.slots); | ||
|
||
hostmem_mr = memslots->slots[0].alias; | ||
rb = hostmem_mr->ram_block; | ||
ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); | ||
|
||
our_range_memslots_free_memslots(our_range); | ||
our_range_destroy(&our_range->range); | ||
g_free(our_range); | ||
} | ||
|
||
void hvb_our_range_memslots_ensure_mapped_additional(OurRangeMemslots *our_range, | ||
uint64_t additional_map_size) | ||
{ | ||
OurRangeMemslotsSlots *memslots = &our_range->slots; | ||
uint64_t total_map_size; | ||
unsigned int idx; | ||
uint64_t offset; | ||
|
||
total_map_size = (our_range->range.added + additional_map_size) * | ||
HV_BALLOON_PAGE_SIZE; | ||
idx = memslots->mapped_count; | ||
assert(memslots->size_each > 0); | ||
offset = idx * memslots->size_each; | ||
|
||
/* | ||
* Activate all memslots covered by the newly added region in a single | ||
* transaction. | ||
*/ | ||
memory_region_transaction_begin(); | ||
for ( ; idx < memslots->count; | ||
idx++, offset += memslots->size_each) { | ||
/* | ||
* If this memslot starts beyond or at the end of the range to map so | ||
* does every next one. | ||
*/ | ||
if (offset >= total_map_size) { | ||
break; | ||
} | ||
|
||
/* | ||
* Instead of enabling/disabling memslot, we add/remove them. This | ||
* should make address space updates faster, because we don't have to | ||
* loop over many disabled subregions. | ||
*/ | ||
trace_hv_balloon_map_slot(idx, memslots->count, offset); | ||
assert(!memory_region_is_mapped(&memslots->slots[idx])); | ||
memory_region_add_subregion(our_range->mr, offset, | ||
&memslots->slots[idx]); | ||
|
||
memslots->mapped_count++; | ||
} | ||
memory_region_transaction_commit(); | ||
} |
Oops, something went wrong.