* Mon Jun 08 2026 Jon Maloy <jmaloy@redhat.com> - 10.1.0-21

- kvm-scsi-adjust-error_prepend-formatting.patch [RHEL-158243]
- kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch [RHEL-158243]
- kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch [RHEL-158243]
- kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch [RHEL-177969]
- kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch [RHEL-177969]
- Resolves: RHEL-158243
  (live migration failed the VM just register key only [rhel-9.9])
- Resolves: RHEL-177969
  (MSHV backport onto QEMU 10.1.0 is not able to launch MSHV guests)
This commit is contained in:
Jon Maloy 2026-06-08 13:57:29 -04:00
parent 594e611186
commit 8d37b1bdba
6 changed files with 946 additions and 1 deletions

View File

@ -0,0 +1,690 @@
From f46de1630eddac4328b462070613235aca5e1149 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 13 Jan 2026 16:37:08 +0100
Subject: [PATCH 4/5] accel/mshv: Remove remap overlapping mappings code
RH-Author: Magnus Kulke <None>
RH-MergeRequest: 491: accel/mshv: backport required patches for mshv guests
RH-Jira: RHEL-177969
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
RH-Commit: [1/2] 78f5dc7530c84062f799cdc286f6cbb97b50b105 (mkulke/qemu-kvm)
This change removes userland code that worked around a restriction
in the mshv driver in the 6.18 kernel: regions from userland
couldn't be mapped to multiple regions in the kernel. We maintained a
shadow mapping table in qemu and used a heuristic to swap in a requested
region in case of UNMAPPED_GPA exits.
However, this heuristic wasn't reliable in all cases, since HyperV
behaviour is not 100% reliable across versions. HyperV itself doesn't
prohibit to map regions at multiple places into the guest, so the
restriction has been removed in the mshv driver.
Hence we can remove the remapping code. Effectively this will mandate a
6.19 kernel, if the workload attempt to map e.g. BIOS to multiple
reagions. I still think it's the right call to remove this logic:
- The workaround only seems to work reliably with a certain revision
of HyperV as a nested hypervisor.
- We expect Direct Virtualization (L1VH) to be the main platform for
the mshv accelerator, which also requires a 6.19 kernel
This reverts commit efc4093358511a58846a409b965213aa1bb9f31a.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Acked-by: Wei Liu (Microsoft) <wei.liu@kernel.org>
Tested-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Link: https://lore.kernel.org/r/20260113153708.448968-1-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 626e5dc999353d2c4a89febd46301bfec6daa3a7)
---
accel/mshv/mem.c | 406 +++---------------------------------
accel/mshv/mshv-all.c | 2 -
accel/mshv/trace-events | 5 -
include/system/mshv_int.h | 22 +-
target/i386/mshv/mshv-cpu.c | 43 ----
5 files changed, 30 insertions(+), 448 deletions(-)
diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c
index 0e2164af3e..e55c38d4db 100644
--- a/accel/mshv/mem.c
+++ b/accel/mshv/mem.c
@@ -11,9 +11,7 @@
*/
#include "qemu/osdep.h"
-#include "qemu/lockable.h"
#include "qemu/error-report.h"
-#include "qemu/rcu.h"
#include "linux/mshv.h"
#include "system/address-spaces.h"
#include "system/mshv.h"
@@ -22,137 +20,6 @@
#include <sys/ioctl.h>
#include "trace.h"
-typedef struct SlotsRCUReclaim {
- struct rcu_head rcu;
- GList *old_head;
- MshvMemorySlot *removed_slot;
-} SlotsRCUReclaim;
-
-static void rcu_reclaim_slotlist(struct rcu_head *rcu)
-{
- SlotsRCUReclaim *r = container_of(rcu, SlotsRCUReclaim, rcu);
- g_list_free(r->old_head);
- g_free(r->removed_slot);
- g_free(r);
-}
-
-static void publish_slots(GList *new_head, GList *old_head,
- MshvMemorySlot *removed_slot)
-{
- MshvMemorySlotManager *manager = &mshv_state->msm;
-
- assert(manager);
- qatomic_store_release(&manager->slots, new_head);
-
- SlotsRCUReclaim *r = g_new(SlotsRCUReclaim, 1);
- r->old_head = old_head;
- r->removed_slot = removed_slot;
-
- call_rcu1(&r->rcu, rcu_reclaim_slotlist);
-}
-
-/* Needs to be called with mshv_state->msm.mutex held */
-static int remove_slot(MshvMemorySlot *slot)
-{
- GList *old_head, *new_head;
- MshvMemorySlotManager *manager = &mshv_state->msm;
-
- assert(manager);
- old_head = qatomic_load_acquire(&manager->slots);
-
- if (!g_list_find(old_head, slot)) {
- error_report("slot requested for removal not found");
- return -1;
- }
-
- new_head = g_list_copy(old_head);
- new_head = g_list_remove(new_head, slot);
- manager->n_slots--;
-
- publish_slots(new_head, old_head, slot);
-
- return 0;
-}
-
-/* Needs to be called with mshv_state->msm.mutex held */
-static MshvMemorySlot *append_slot(uint64_t gpa, uint64_t userspace_addr,
- uint64_t size, bool readonly)
-{
- GList *old_head, *new_head;
- MshvMemorySlot *slot;
- MshvMemorySlotManager *manager = &mshv_state->msm;
-
- assert(manager);
-
- old_head = qatomic_load_acquire(&manager->slots);
-
- if (manager->n_slots >= MSHV_MAX_MEM_SLOTS) {
- error_report("no free memory slots available");
- return NULL;
- }
-
- slot = g_new0(MshvMemorySlot, 1);
- slot->guest_phys_addr = gpa;
- slot->userspace_addr = userspace_addr;
- slot->memory_size = size;
- slot->readonly = readonly;
-
- new_head = g_list_copy(old_head);
- new_head = g_list_append(new_head, slot);
- manager->n_slots++;
-
- publish_slots(new_head, old_head, NULL);
-
- return slot;
-}
-
-static int slot_overlaps(const MshvMemorySlot *slot1,
- const MshvMemorySlot *slot2)
-{
- uint64_t start_1 = slot1->userspace_addr,
- start_2 = slot2->userspace_addr;
- size_t len_1 = slot1->memory_size,
- len_2 = slot2->memory_size;
-
- if (slot1 == slot2) {
- return -1;
- }
-
- return ranges_overlap(start_1, len_1, start_2, len_2) ? 0 : -1;
-}
-
-static bool is_mapped(MshvMemorySlot *slot)
-{
- /* Subsequent reads of mapped field see a fully-initialized slot */
- return qatomic_load_acquire(&slot->mapped);
-}
-
-/*
- * Find slot that is:
- * - overlapping in userspace
- * - currently mapped in the guest
- *
- * Needs to be called with mshv_state->msm.mutex or RCU read lock held.
- */
-static MshvMemorySlot *find_overlap_mem_slot(GList *head, MshvMemorySlot *slot)
-{
- GList *found;
- MshvMemorySlot *overlap_slot;
-
- found = g_list_find_custom(head, slot, (GCompareFunc) slot_overlaps);
-
- if (!found) {
- return NULL;
- }
-
- overlap_slot = found->data;
- if (!overlap_slot || !is_mapped(overlap_slot)) {
- return NULL;
- }
-
- return overlap_slot;
-}
-
static int set_guest_memory(int vm_fd,
const struct mshv_user_mem_region *region)
{
@@ -160,169 +27,38 @@ static int set_guest_memory(int vm_fd,
ret = ioctl(vm_fd, MSHV_SET_GUEST_MEMORY, region);
if (ret < 0) {
- error_report("failed to set guest memory: %s", strerror(errno));
- return -1;
+ error_report("failed to set guest memory");
+ return -errno;
}
return 0;
}
-static int map_or_unmap(int vm_fd, const MshvMemorySlot *slot, bool map)
+static int map_or_unmap(int vm_fd, const MshvMemoryRegion *mr, bool map)
{
struct mshv_user_mem_region region = {0};
- region.guest_pfn = slot->guest_phys_addr >> MSHV_PAGE_SHIFT;
- region.size = slot->memory_size;
- region.userspace_addr = slot->userspace_addr;
+ region.guest_pfn = mr->guest_phys_addr >> MSHV_PAGE_SHIFT;
+ region.size = mr->memory_size;
+ region.userspace_addr = mr->userspace_addr;
if (!map) {
region.flags |= (1 << MSHV_SET_MEM_BIT_UNMAP);
- trace_mshv_unmap_memory(slot->userspace_addr, slot->guest_phys_addr,
- slot->memory_size);
+ trace_mshv_unmap_memory(mr->userspace_addr, mr->guest_phys_addr,
+ mr->memory_size);
return set_guest_memory(vm_fd, &region);
}
region.flags = BIT(MSHV_SET_MEM_BIT_EXECUTABLE);
- if (!slot->readonly) {
+ if (!mr->readonly) {
region.flags |= BIT(MSHV_SET_MEM_BIT_WRITABLE);
}
- trace_mshv_map_memory(slot->userspace_addr, slot->guest_phys_addr,
- slot->memory_size);
+ trace_mshv_map_memory(mr->userspace_addr, mr->guest_phys_addr,
+ mr->memory_size);
return set_guest_memory(vm_fd, &region);
}
-static int slot_matches_region(const MshvMemorySlot *slot1,
- const MshvMemorySlot *slot2)
-{
- return (slot1->guest_phys_addr == slot2->guest_phys_addr &&
- slot1->userspace_addr == slot2->userspace_addr &&
- slot1->memory_size == slot2->memory_size) ? 0 : -1;
-}
-
-/* Needs to be called with mshv_state->msm.mutex held */
-static MshvMemorySlot *find_mem_slot_by_region(uint64_t gpa, uint64_t size,
- uint64_t userspace_addr)
-{
- MshvMemorySlot ref_slot = {
- .guest_phys_addr = gpa,
- .userspace_addr = userspace_addr,
- .memory_size = size,
- };
- GList *found;
- MshvMemorySlotManager *manager = &mshv_state->msm;
-
- assert(manager);
- found = g_list_find_custom(manager->slots, &ref_slot,
- (GCompareFunc) slot_matches_region);
-
- return found ? found->data : NULL;
-}
-
-static int slot_covers_gpa(const MshvMemorySlot *slot, uint64_t *gpa_p)
-{
- uint64_t gpa_offset, gpa = *gpa_p;
-
- gpa_offset = gpa - slot->guest_phys_addr;
- return (slot->guest_phys_addr <= gpa && gpa_offset < slot->memory_size)
- ? 0 : -1;
-}
-
-/* Needs to be called with mshv_state->msm.mutex or RCU read lock held */
-static MshvMemorySlot *find_mem_slot_by_gpa(GList *head, uint64_t gpa)
-{
- GList *found;
- MshvMemorySlot *slot;
-
- trace_mshv_find_slot_by_gpa(gpa);
-
- found = g_list_find_custom(head, &gpa, (GCompareFunc) slot_covers_gpa);
- if (found) {
- slot = found->data;
- trace_mshv_found_slot(slot->userspace_addr, slot->guest_phys_addr,
- slot->memory_size);
- return slot;
- }
-
- return NULL;
-}
-
-/* Needs to be called with mshv_state->msm.mutex held */
-static void set_mapped(MshvMemorySlot *slot, bool mapped)
-{
- /* prior writes to mapped field becomes visible before readers see slot */
- qatomic_store_release(&slot->mapped, mapped);
-}
-
-MshvRemapResult mshv_remap_overlap_region(int vm_fd, uint64_t gpa)
-{
- MshvMemorySlot *gpa_slot, *overlap_slot;
- GList *head;
- int ret;
- MshvMemorySlotManager *manager = &mshv_state->msm;
-
- /* fast path, called often by unmapped_gpa vm exit */
- WITH_RCU_READ_LOCK_GUARD() {
- assert(manager);
- head = qatomic_load_acquire(&manager->slots);
- /* return early if no slot is found */
- gpa_slot = find_mem_slot_by_gpa(head, gpa);
- if (gpa_slot == NULL) {
- return MshvRemapNoMapping;
- }
-
- /* return early if no overlapping slot is found */
- overlap_slot = find_overlap_mem_slot(head, gpa_slot);
- if (overlap_slot == NULL) {
- return MshvRemapNoOverlap;
- }
- }
-
- /*
- * We'll modify the mapping list, so we need to upgrade to mutex and
- * recheck.
- */
- assert(manager);
- QEMU_LOCK_GUARD(&manager->mutex);
-
- /* return early if no slot is found */
- gpa_slot = find_mem_slot_by_gpa(manager->slots, gpa);
- if (gpa_slot == NULL) {
- return MshvRemapNoMapping;
- }
-
- /* return early if no overlapping slot is found */
- overlap_slot = find_overlap_mem_slot(manager->slots, gpa_slot);
- if (overlap_slot == NULL) {
- return MshvRemapNoOverlap;
- }
-
- /* unmap overlapping slot */
- ret = map_or_unmap(vm_fd, overlap_slot, false);
- if (ret < 0) {
- error_report("failed to unmap overlap region");
- abort();
- }
- set_mapped(overlap_slot, false);
- warn_report("mapped out userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx",
- overlap_slot->userspace_addr,
- overlap_slot->guest_phys_addr,
- overlap_slot->memory_size);
-
- /* map region for gpa */
- ret = map_or_unmap(vm_fd, gpa_slot, true);
- if (ret < 0) {
- error_report("failed to map new region");
- abort();
- }
- set_mapped(gpa_slot, true);
- warn_report("mapped in userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx",
- gpa_slot->userspace_addr, gpa_slot->guest_phys_addr,
- gpa_slot->memory_size);
-
- return MshvRemapOk;
-}
-
static int handle_unmapped_mmio_region_read(uint64_t gpa, uint64_t size,
uint8_t *data)
{
@@ -388,97 +124,20 @@ int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
return -1;
}
-static int tracked_unmap(int vm_fd, uint64_t gpa, uint64_t size,
- uint64_t userspace_addr)
+static int set_memory(const MshvMemoryRegion *mshv_mr, bool add)
{
- int ret;
- MshvMemorySlot *slot;
- MshvMemorySlotManager *manager = &mshv_state->msm;
-
- assert(manager);
-
- QEMU_LOCK_GUARD(&manager->mutex);
-
- slot = find_mem_slot_by_region(gpa, size, userspace_addr);
- if (!slot) {
- trace_mshv_skip_unset_mem(userspace_addr, gpa, size);
- /* no work to do */
- return 0;
- }
-
- if (!is_mapped(slot)) {
- /* remove slot, no need to unmap */
- return remove_slot(slot);
- }
-
- ret = map_or_unmap(vm_fd, slot, false);
- if (ret < 0) {
- error_report("failed to unmap memory region");
- return ret;
- }
- return remove_slot(slot);
-}
-
-static int tracked_map(int vm_fd, uint64_t gpa, uint64_t size, bool readonly,
- uint64_t userspace_addr)
-{
- MshvMemorySlot *slot, *overlap_slot;
- int ret;
- MshvMemorySlotManager *manager = &mshv_state->msm;
-
- assert(manager);
-
- QEMU_LOCK_GUARD(&manager->mutex);
+ int ret = 0;
- slot = find_mem_slot_by_region(gpa, size, userspace_addr);
- if (slot) {
- error_report("memory region already mapped at gpa=0x%lx, "
- "userspace_addr=0x%lx, size=0x%lx",
- slot->guest_phys_addr, slot->userspace_addr,
- slot->memory_size);
+ if (!mshv_mr) {
+ error_report("Invalid mshv_mr");
return -1;
}
- slot = append_slot(gpa, userspace_addr, size, readonly);
-
- overlap_slot = find_overlap_mem_slot(manager->slots, slot);
- if (overlap_slot) {
- trace_mshv_remap_attempt(slot->userspace_addr,
- slot->guest_phys_addr,
- slot->memory_size);
- warn_report("attempt to map region [0x%lx-0x%lx], while "
- "[0x%lx-0x%lx] is already mapped in the guest",
- userspace_addr, userspace_addr + size - 1,
- overlap_slot->userspace_addr,
- overlap_slot->userspace_addr +
- overlap_slot->memory_size - 1);
-
- /* do not register mem slot in hv, but record for later swap-in */
- set_mapped(slot, false);
-
- return 0;
- }
-
- ret = map_or_unmap(vm_fd, slot, true);
- if (ret < 0) {
- error_report("failed to map memory region");
- return -1;
- }
- set_mapped(slot, true);
-
- return 0;
-}
-
-static int set_memory(uint64_t gpa, uint64_t size, bool readonly,
- uint64_t userspace_addr, bool add)
-{
- int vm_fd = mshv_state->vm;
-
- if (add) {
- return tracked_map(vm_fd, gpa, size, readonly, userspace_addr);
- }
-
- return tracked_unmap(vm_fd, gpa, size, userspace_addr);
+ trace_mshv_set_memory(add, mshv_mr->guest_phys_addr,
+ mshv_mr->memory_size,
+ mshv_mr->userspace_addr, mshv_mr->readonly,
+ ret);
+ return map_or_unmap(mshv_state->vm, mshv_mr, add);
}
/*
@@ -514,9 +173,7 @@ void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool writable = !area->readonly && !area->rom_device;
hwaddr start_addr, mr_offset, size;
void *ram;
-
- size = align_section(section, &start_addr);
- trace_mshv_set_phys_mem(add, section->mr->name, start_addr);
+ MshvMemoryRegion mshv_mr = {0};
size = align_section(section, &start_addr);
trace_mshv_set_phys_mem(add, section->mr->name, start_addr);
@@ -543,21 +200,14 @@ void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
ram = memory_region_get_ram_ptr(area) + mr_offset;
- ret = set_memory(start_addr, size, !writable, (uint64_t)ram, add);
+ mshv_mr.guest_phys_addr = start_addr;
+ mshv_mr.memory_size = size;
+ mshv_mr.readonly = !writable;
+ mshv_mr.userspace_addr = (uint64_t)ram;
+
+ ret = set_memory(&mshv_mr, add);
if (ret < 0) {
- error_report("failed to set memory region");
+ error_report("Failed to set memory region");
abort();
}
}
-
-void mshv_init_memory_slot_manager(MshvState *mshv_state)
-{
- MshvMemorySlotManager *manager;
-
- assert(mshv_state);
- manager = &mshv_state->msm;
-
- manager->n_slots = 0;
- manager->slots = NULL;
- qemu_mutex_init(&manager->mutex);
-}
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 80428d130d..88b66f2992 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -437,8 +437,6 @@ static int mshv_init(AccelState *as, MachineState *ms)
mshv_init_msicontrol();
- mshv_init_memory_slot_manager(s);
-
ret = create_vm(mshv_fd, &vm_fd);
if (ret < 0) {
close(mshv_fd);
diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events
index 36f0d59b38..a4dffeb24a 100644
--- a/accel/mshv/trace-events
+++ b/accel/mshv/trace-events
@@ -26,8 +26,3 @@ mshv_map_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%
mshv_unmap_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_set_phys_mem(bool add, const char *name, uint64_t gpa) "\tadd=%d name=%s gpa=0x%010" PRIx64
mshv_handle_mmio(uint64_t gva, uint64_t gpa, uint64_t size, uint8_t access_type) "\tgva=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%" PRIx64 " access_type=%d"
-
-mshv_found_slot(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
-mshv_skip_unset_mem(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
-mshv_remap_attempt(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
-mshv_find_slot_by_gpa(uint64_t gpa) "\tgpa=0x%010" PRIx64
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 490563c1ab..ad4d001c3c 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -16,8 +16,6 @@
#define MSHV_MSR_ENTRIES_COUNT 64
-#define MSHV_MAX_MEM_SLOTS 32
-
typedef struct hyperv_message hv_message;
typedef struct MshvHvCallArgs {
@@ -42,12 +40,6 @@ typedef struct MshvAddressSpace {
AddressSpace *as;
} MshvAddressSpace;
-typedef struct MshvMemorySlotManager {
- size_t n_slots;
- GList *slots;
- QemuMutex mutex;
-} MshvMemorySlotManager;
-
struct MshvState {
AccelState parent_obj;
int vm;
@@ -56,7 +48,6 @@ struct MshvState {
int nr_as;
MshvAddressSpace *as;
int fd;
- MshvMemorySlotManager msm;
};
typedef struct MshvMsiControl {
@@ -87,12 +78,6 @@ typedef enum MshvVmExit {
MshvVmExitSpecial = 2,
} MshvVmExit;
-typedef enum MshvRemapResult {
- MshvRemapOk = 0,
- MshvRemapNoMapping = 1,
- MshvRemapNoOverlap = 2,
-} MshvRemapResult;
-
void mshv_init_mmio_emu(void);
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
@@ -116,22 +101,19 @@ int mshv_hvcall(int fd, const struct mshv_root_hvcall *args);
#endif
/* memory */
-typedef struct MshvMemorySlot {
+typedef struct MshvMemoryRegion {
uint64_t guest_phys_addr;
uint64_t memory_size;
uint64_t userspace_addr;
bool readonly;
- bool mapped;
-} MshvMemorySlot;
+} MshvMemoryRegion;
-MshvRemapResult mshv_remap_overlap_region(int vm_fd, uint64_t gpa);
int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size,
bool is_secure_mode, bool instruction_fetch);
int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
bool is_secure_mode);
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add);
-void mshv_init_memory_slot_manager(MshvState *mshv_state);
/* msr */
typedef struct MshvMsrEntry {
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 1c3db02188..c577e284b4 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1168,43 +1168,6 @@ static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg,
return 0;
}
-static int handle_unmapped_mem(int vm_fd, CPUState *cpu,
- const struct hyperv_message *msg,
- MshvVmExit *exit_reason)
-{
- struct hv_x64_memory_intercept_message info = { 0 };
- uint64_t gpa;
- int ret;
- enum MshvRemapResult remap_result;
-
- ret = set_memory_info(msg, &info);
- if (ret < 0) {
- error_report("failed to convert message to memory info");
- return -1;
- }
-
- gpa = info.guest_physical_address;
-
- /* attempt to remap the region, in case of overlapping userspace mappings */
- remap_result = mshv_remap_overlap_region(vm_fd, gpa);
- *exit_reason = MshvVmExitIgnore;
-
- switch (remap_result) {
- case MshvRemapNoMapping:
- /* if we didn't find a mapping, it is probably mmio */
- return handle_mmio(cpu, msg, exit_reason);
- case MshvRemapOk:
- break;
- case MshvRemapNoOverlap:
- /* This should not happen, but we are forgiving it */
- warn_report("found no overlap for unmapped region");
- *exit_reason = MshvVmExitSpecial;
- break;
- }
-
- return 0;
-}
-
static int set_ioport_info(const struct hyperv_message *msg,
hv_x64_io_port_intercept_message *info)
{
@@ -1546,12 +1509,6 @@ int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
case HVMSG_UNRECOVERABLE_EXCEPTION:
return MshvVmExitShutdown;
case HVMSG_UNMAPPED_GPA:
- ret = handle_unmapped_mem(vm_fd, cpu, msg, &exit_reason);
- if (ret < 0) {
- error_report("failed to handle unmapped memory");
- return -1;
- }
- return exit_reason;
case HVMSG_GPA_INTERCEPT:
ret = handle_mmio(cpu, msg, &exit_reason);
if (ret < 0) {
--
2.52.0

View File

@ -0,0 +1,58 @@
From 4356c4191e1b7ba491a2dd6d8c6167f7eb9a3de1 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 21 Apr 2026 05:21:54 +0000
Subject: [PATCH 5/5] accel/mshv: implement cpu_thread_is_idle() hook
RH-Author: Magnus Kulke <None>
RH-MergeRequest: 491: accel/mshv: backport required patches for mshv guests
RH-Jira: RHEL-177969
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
RH-Commit: [2/2] 4880ec9edc01fda07186687f9d19159f3afd0689 (mkulke/qemu-kvm)
In MSHV the hypervisor APIC is always used, so we to implement this hook
to make sure the AP's vcpu thread is not blocked waiting for an INIT SIPI
by the BSP. Without this change soft reboots with -smp cpus>=2 will
hang.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Link: https://lore.kernel.org/r/20260421-mshv_accel_arm64_supp-v3-9-469f544778ba@linux.microsoft.com
[Make comment not x86 specific. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit dbfb680772d5184544f7c0a8bba96bec229c96e6)
---
accel/mshv/mshv-all.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 88b66f2992..d7df89dbd2 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -699,11 +699,23 @@ static const TypeInfo mshv_accel_type = {
.instance_size = sizeof(MshvState),
};
+/*
+ * MSHV manages secondary processors in the hypervisor. SIPI for x86 and
+ * PSCI for Arm are handled internally. Halted vCPUs must still enter
+ * mshv_cpu_exec() so that MSHV_RUN_VP is called and the hypervisor will
+ * wake APs.
+ */
+static bool mshv_vcpu_thread_is_idle(CPUState *cpu)
+{
+ return false;
+}
+
static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
ops->create_vcpu_thread = mshv_start_vcpu_thread;
+ ops->cpu_thread_is_idle = mshv_vcpu_thread_is_idle;
ops->synchronize_post_init = mshv_cpu_synchronize_post_init;
ops->synchronize_post_reset = mshv_cpu_synchronize_post_reset;
ops->synchronize_state = mshv_cpu_synchronize;
--
2.52.0

View File

@ -0,0 +1,49 @@
From bc38981e098c0dd2430c125ceb08188813e7ba12 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 1 Apr 2026 13:19:25 -0400
Subject: [PATCH 1/5] scsi: adjust error_prepend() formatting
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
RH-MergeRequest: 493: scsi: PR live migration PREEMPT fixes [9.9]
RH-Jira: RHEL-158243
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
RH-Commit: [1/3] 8ba6738b878a8926f8aaab487b863a42a44a736e (stefanha/centos-stream-qemu-kvm)
The error strings will be concatenated so add a separator to make the
combined error message easy to read.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Link: https://lore.kernel.org/r/20260401171927.396672-2-stefanha@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 15c57ac351ccb4af625cd7dc582831f472c1a68f)
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/scsi/scsi-generic.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index b8b3f399f0..5825a47311 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -438,7 +438,7 @@ static bool scsi_generic_pr_register(SCSIDevice *s, uint64_t key, Error **errp)
ret = scsi_SG_IO(s->conf.blk, SG_DXFER_TO_DEV, cmd, sizeof(cmd),
buf, sizeof(buf), s->io_timeout, errp);
if (ret < 0) {
- error_prepend(errp, "PERSISTENT RESERVE OUT with REGISTER");
+ error_prepend(errp, "PERSISTENT RESERVE OUT with REGISTER: ");
return false;
}
return true;
@@ -462,7 +462,7 @@ static bool scsi_generic_pr_preempt(SCSIDevice *s, uint64_t key,
ret = scsi_SG_IO(s->conf.blk, SG_DXFER_TO_DEV, cmd, sizeof(cmd),
buf, sizeof(buf), s->io_timeout, errp);
if (ret < 0) {
- error_prepend(errp, "PERSISTENT RESERVE OUT with PREEMPT");
+ error_prepend(errp, "PERSISTENT RESERVE OUT with PREEMPT: ");
return false;
}
return true;
--
2.52.0

View File

@ -0,0 +1,73 @@
From 45ca2a7d7a348dd1a48a5f218ca02331bc3d29a7 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 1 Apr 2026 13:19:26 -0400
Subject: [PATCH 2/5] scsi: always send valid PREEMPT TYPE field
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
RH-MergeRequest: 493: scsi: PR live migration PREEMPT fixes [9.9]
RH-Jira: RHEL-158243
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
RH-Commit: [2/3] c1b10616023abe2ec648c1b0c4d095a87f0ff258 (stefanha/centos-stream-qemu-kvm)
The SPC-6 specification says that the PREEMPT service action ignores the
TYPE field when there is no reservation. However, the LIO Linux iSCSI
target rejects commands with a zero TYPE field. The field never ends up
being used in this case, so replace it with a "valid" value to work
around the issue.
Reported-by: Qing Wang <qinwang@redhat.com>
Buglink: https://redhat.atlassian.net/browse/RHEL-155807
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Link: https://lore.kernel.org/r/20260401171927.396672-3-stefanha@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 15a202656cd553911272f9666aa067c706fc3dfe)
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/scsi/scsi-generic.c | 10 ++++++++++
include/scsi/constants.h | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 5825a47311..8a57473623 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -452,6 +452,16 @@ static bool scsi_generic_pr_preempt(SCSIDevice *s, uint64_t key,
uint64_t key_be = cpu_to_be64(key);
int ret;
+ /*
+ * The LIO iSCSI target in Linux up to at least version 7.0 rejects PREEMPT
+ * commands with a zero TYPE field although the SPC-6 specification says
+ * the field should be ignored when there is no persistent reservation.
+ * Work around this by choosing an arbitrary valid PR type value.
+ */
+ if (resv_type == 0) {
+ resv_type = PR_TYPE_WRITE_EXCLUSIVE;
+ }
+
cmd[0] = PERSISTENT_RESERVE_OUT;
cmd[1] = PRO_PREEMPT;
cmd[2] = resv_type & 0xf;
diff --git a/include/scsi/constants.h b/include/scsi/constants.h
index cb97bdb636..717e470a5d 100644
--- a/include/scsi/constants.h
+++ b/include/scsi/constants.h
@@ -340,4 +340,14 @@
#define PRO_REGISTER_AND_MOVE 0x07
#define PRO_REPLACE_LOST_RESERVATION 0x08
+/*
+ * Persistent reservation types
+ */
+#define PR_TYPE_WRITE_EXCLUSIVE 0x1
+#define PR_TYPE_EXCLUSIVE_ACCESS 0x3
+#define PR_TYPE_WRITE_EXCLUSIVE_REG_ONLY 0x5
+#define PR_TYPE_EXCLUSIVE_ACCESS_REG_ONLY 0x6
+#define PR_TYPE_WRITE_EXCLUSIVE_ALL_REGS 0x7
+#define PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGS 0x8
+
#endif
--
2.52.0

View File

@ -0,0 +1,54 @@
From 520f3f124b79709b8da67f1a8adb77da60fc4753 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 1 Apr 2026 13:19:27 -0400
Subject: [PATCH 3/5] scsi: register again after PREEMPT without reservation
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
RH-MergeRequest: 493: scsi: PR live migration PREEMPT fixes [9.9]
RH-Jira: RHEL-158243
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
RH-Commit: [3/3] 4ba66cf81945645015cb5df9f0b7f4d163f84808 (stefanha/centos-stream-qemu-kvm)
The SCSI specification says PREEMPT without a reservation removes all
registrations with the given key. Try to register again after PREEMPT
since our key will have been removed.
In practice some SCSI targets keep the calling I_T nexus' registration
instead of removing it. Therefore we need to handle both the
spec-compliant and the non-compliant behavior.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Link: https://lore.kernel.org/r/20260401171927.396672-4-stefanha@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit b9a3b329d09dea5a4692de483419693ef0e23052)
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/scsi/scsi-generic.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 8a57473623..9c5927b543 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -512,6 +512,16 @@ bool scsi_generic_pr_state_preempt(SCSIDevice *s, Error **errp)
if (!scsi_generic_pr_preempt(s, key, resv_type, errp)) {
return false;
}
+
+ /*
+ * Some SCSI targets, like the Linux LIO target, remove our
+ * registration when preempting without a reservation (resv_type is 0).
+ * Try to register again but ignore the error since a RESERVATION
+ * CONFLICT is expected if our registration remained in place.
+ */
+ if (resv_type == 0) {
+ scsi_generic_pr_register(s, key, NULL);
+ }
}
return true;
}
--
2.52.0

View File

@ -149,7 +149,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \
Summary: QEMU is a machine emulator and virtualizer
Name: qemu-kvm
Version: 10.1.0
Release: 20%{?rcrel}%{?dist}%{?cc_suffix}
Release: 21%{?rcrel}%{?dist}%{?cc_suffix}
# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
# Epoch 15 used for RHEL 8
# Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5)
@ -385,6 +385,16 @@ Patch119: kvm-target-arm-cpu64-Define-cpreg-migration-tolerance-fo.patch
Patch120: kvm-target-arm-helper-Define-cpreg-migration-tolerance-f.patch
# For RHEL-174859 - [rhel9] Backport qemu cross-kernel migration mitigation series
Patch121: kvm-Revert-target-arm-Reinstate-bogus-AArch32-DBGDTRTX-r.patch
# For RHEL-158243 - live migration failed the VM just register key only [rhel-9.9]
Patch122: kvm-scsi-adjust-error_prepend-formatting.patch
# For RHEL-158243 - live migration failed the VM just register key only [rhel-9.9]
Patch123: kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch
# For RHEL-158243 - live migration failed the VM just register key only [rhel-9.9]
Patch124: kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch
# For RHEL-177969 - MSHV backport onto QEMU 10.1.0 is not able to launch MSHV guests
Patch125: kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch
# For RHEL-177969 - MSHV backport onto QEMU 10.1.0 is not able to launch MSHV guests
Patch126: kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch
# For RHEL-11424 - [IBM 9.6 FEAT] KVM: Full boot order support - qemu part
@ -2099,6 +2109,17 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
%endif
%changelog
* Mon Jun 08 2026 Jon Maloy <jmaloy@redhat.com> - 10.1.0-21
- kvm-scsi-adjust-error_prepend-formatting.patch [RHEL-158243]
- kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch [RHEL-158243]
- kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch [RHEL-158243]
- kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch [RHEL-177969]
- kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch [RHEL-177969]
- Resolves: RHEL-158243
(live migration failed the VM just register key only [rhel-9.9])
- Resolves: RHEL-177969
(MSHV backport onto QEMU 10.1.0 is not able to launch MSHV guests)
* Tue May 19 2026 Jon Maloy <jmaloy@redhat.com> - 10.1.0-20
- kvm-vmstate-Introduce-VMSTATE_VARRAY_INT32_ALLOC.patch [RHEL-174859]
- kvm-target-arm-Move-compare_u64-to-helper.c.patch [RHEL-174859]