123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- /*
- * 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 "qemu/osdep.h"
- #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();
- }
|