From e2263aca2f04e716601ee6c8c86e199eb2dc99a8 Mon Sep 17 00:00:00 2001 From: Miroslav Rezanina Date: Mon, 8 Jun 2026 09:38:09 +0200 Subject: [PATCH] * Mon Jun 08 2026 Miroslav Rezanina - 10.1.0-20 - kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch [RHEL-178767] - kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch [RHEL-178767] - kvm-scsi-adjust-error_prepend-formatting.patch [RHEL-155807] - kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch [RHEL-155807] - kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch [RHEL-155807] - kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch [RHEL-178846] - kvm-vfio-container-Restrict-dma_map_file-to-shared-RAM-o.patch [RHEL-180750] - kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch [RHEL-153123] - kvm-scsi-handle-reservation-changes-across-migration.patch [RHEL-153123] - Resolves: RHEL-178767 (MSHV backport onto QEMU 10.1.0 is not able to launch MSHV guests) - Resolves: RHEL-155807 (live migration failed the VM just register key only [rhel-10.3]) - Resolves: RHEL-178846 ([aarch64] qemu-kvm crashes on --device tpm-tis-device,?) - Resolves: RHEL-180750 (Backport in QEMU : vfio/container: Restrict dma_map_file() to shared RAM or RAM devices) - Resolves: RHEL-153123 (live migration failed or get failed WSFC test result during WSFC testing [rhel-10.3]) --- ...move-remap-overlapping-mappings-code.patch | 690 ++++++++++++++++++ ...hv-implement-cpu_thread_is_idle-hook.patch | 58 ++ ...ysbus-defer-resource-allocation-to-r.patch | 109 +++ ...scsi-adjust-error_prepend-formatting.patch | 49 ++ ...always-send-valid-PREEMPT-TYPE-field.patch | 73 ++ ...f_size-to-unsigned-int-in-scsi_SG_IO.patch | 55 ++ ...reservation-changes-across-migration.patch | 246 +++++++ ...ain-after-PREEMPT-without-reservatio.patch | 54 ++ ...estrict-dma_map_file-to-shared-RAM-o.patch | 97 +++ qemu-kvm.spec | 41 +- rpminspect.yaml | 1 + 11 files changed, 1472 insertions(+), 1 deletion(-) create mode 100644 kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch create mode 100644 kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch create mode 100644 kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch create mode 100644 kvm-scsi-adjust-error_prepend-formatting.patch create mode 100644 kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch create mode 100644 kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch create mode 100644 kvm-scsi-handle-reservation-changes-across-migration.patch create mode 100644 kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch create mode 100644 kvm-vfio-container-Restrict-dma_map_file-to-shared-RAM-o.patch diff --git a/kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch b/kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch new file mode 100644 index 0000000..82f7d01 --- /dev/null +++ b/kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch @@ -0,0 +1,690 @@ +From 0d484fe45aee9a7481d543ac2a0453a30872ffbb Mon Sep 17 00:00:00 2001 +From: Magnus Kulke +Date: Tue, 13 Jan 2026 16:37:08 +0100 +Subject: [PATCH 1/9] accel/mshv: Remove remap overlapping mappings code + +RH-Author: Magnus Kulke +RH-MergeRequest: 490: accel/mshv: backport required patches for mshv guests +RH-Jira: RHEL-178767 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina +RH-Commit: [1/2] 959df35b4cd77cf85f7a8a67d438ad71135525e9 (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 +Acked-by: Wei Liu (Microsoft) +Tested-by: Mohamed Mediouni +Link: https://lore.kernel.org/r/20260113153708.448968-1-magnuskulke@linux.microsoft.com +Signed-off-by: Paolo Bonzini +(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 + #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, ®ion); + } + + 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, ®ion); + } + +-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 + diff --git a/kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch b/kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch new file mode 100644 index 0000000..ba3fba3 --- /dev/null +++ b/kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch @@ -0,0 +1,58 @@ +From 2a298912b20e7d47ce5ac68c8524ab286d6ecf6f Mon Sep 17 00:00:00 2001 +From: Magnus Kulke +Date: Tue, 21 Apr 2026 05:21:54 +0000 +Subject: [PATCH 2/9] accel/mshv: implement cpu_thread_is_idle() hook + +RH-Author: Magnus Kulke +RH-MergeRequest: 490: accel/mshv: backport required patches for mshv guests +RH-Jira: RHEL-178767 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina +RH-Commit: [2/2] 3be517f22c0af3dccc6bc33ab2fee541a6802b6a (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 +Reviewed-by: Mohamed Mediouni +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 +(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 + diff --git a/kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch b/kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch new file mode 100644 index 0000000..74e206c --- /dev/null +++ b/kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch @@ -0,0 +1,109 @@ +From 6526d78b916e57e08393da88124df3adfb40604d Mon Sep 17 00:00:00 2001 +From: Mohammadfaiz Bawa +Date: Wed, 3 Jun 2026 14:05:52 +0530 +Subject: [PATCH 6/9] hw/tpm/tpm_tis_sysbus: defer resource allocation to + realize +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Mohammadfaiz Bawa +RH-MergeRequest: 496: hw/tpm/tpm_tis_sysbus: defer resource allocation to realize +RH-Jira: RHEL-178846 +RH-Acked-by: Gavin Shan +RH-Acked-by: Eric Auger +RH-Commit: [1/1] 934e0f23755aa7d675486deb8e57a94ae9f77251 (faizb/qemu-kvm) + +JIRA: https://redhat.atlassian.net/browse/RHEL-178846 + +Calling memory_region_init_ram_device_ptr() and +memory_region_init_io() from tpm_tis_sysbus_initfn() crashes +when the device is introspected without being realized, because +the memory subsystem has not been initialized at that point. + +So running: + $ qemu-system-aarch64 -device tpm-tis-device,help + +triggers qdev_device_help() which creates the device object +to list its properties, calling instance_init, but never +realizefn. The memory region calls in instance_init then hit +uninitialized subsystems: + +With CONFIG_DEBUG_TCG: + Assertion 'target_page.decided' failed. (physmem.c:2524) + +Without CONFIG_DEBUG_TCG: + Assertion 'mutex->initialized' failed. (qemu-thread-posix.c:107) + +Since realizefn is only called when the device is actually +used in a running VM, moving resource allocation there avoids +the crash without breaking introspection. + +This also fixes a memory leak that is reported by the address +sanitizer during 'make check', because we currently allocate +ppi.buf during instance_init and never free it. "Allocate in +realize and never free" is less bad, because we don't currently +support "unrealize and destroy a sysbus device". + +Fixes: 46cd2c1050f ("hw/tpm: add PPI support to tpm-tis-device for ARM64 virt") +Signed-off-by: Mohammadfaiz Bawa +Reviewed-by: Philippe Mathieu-Daudé +Message-ID: <20260528093123.55403-1-mbawa@redhat.com> +Message-ID: +[PMD: Amend Peter comment from previous mail in description] +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit dc6444b9c595dc40d7ead0837b930b83ec966e1f) +Signed-off-by: Mohammadfaiz Bawa +--- + hw/tpm/tpm_tis_sysbus.c | 18 ++++++++---------- + 1 file changed, 8 insertions(+), 10 deletions(-) + +diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c +index 3bea7cd16c..65ef59edd8 100644 +--- a/hw/tpm/tpm_tis_sysbus.c ++++ b/hw/tpm/tpm_tis_sysbus.c +@@ -100,19 +100,9 @@ static void tpm_tis_sysbus_initfn(Object *obj) + { + TPMStateSysBus *sbdev = TPM_TIS_SYSBUS(obj); + TPMState *s = &sbdev->state; +- size_t host_page_size = qemu_real_host_page_size(); +- +- memory_region_init_io(&s->mmio, obj, &tpm_tis_memory_ops, +- s, "tpm-tis-mmio", +- TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); + + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); +- +- s->ppi.buf = qemu_memalign(host_page_size, +- ROUND_UP(TPM_PPI_ADDR_SIZE, host_page_size)); +- memory_region_init_ram_device_ptr(&s->ppi.ram, obj, "tpm-ppi", +- TPM_PPI_ADDR_SIZE, s->ppi.buf); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->ppi.ram); + } + +@@ -120,6 +110,7 @@ static void tpm_tis_sysbus_realizefn(DeviceState *dev, Error **errp) + { + TPMStateSysBus *sbdev = TPM_TIS_SYSBUS(dev); + TPMState *s = &sbdev->state; ++ const size_t host_page_size = qemu_real_host_page_size(); + + if (!tpm_find()) { + error_setg(errp, "at most one TPM device is permitted"); +@@ -131,6 +122,13 @@ static void tpm_tis_sysbus_realizefn(DeviceState *dev, Error **errp) + return; + } + ++ s->ppi.buf = qemu_memalign(host_page_size, ++ ROUND_UP(TPM_PPI_ADDR_SIZE, host_page_size)); ++ memory_region_init_io(&s->mmio, OBJECT(dev), &tpm_tis_memory_ops, ++ s, "tpm-tis-mmio", ++ TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); ++ memory_region_init_ram_device_ptr(&s->ppi.ram, OBJECT(dev), "tpm-ppi", ++ TPM_PPI_ADDR_SIZE, s->ppi.buf); + vmstate_register_ram(&s->ppi.ram, dev); + } + +-- +2.52.0 + diff --git a/kvm-scsi-adjust-error_prepend-formatting.patch b/kvm-scsi-adjust-error_prepend-formatting.patch new file mode 100644 index 0000000..dce5a6d --- /dev/null +++ b/kvm-scsi-adjust-error_prepend-formatting.patch @@ -0,0 +1,49 @@ +From 77d65b4a5dc0172a29dd810363d4f702242b929a Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Wed, 1 Apr 2026 13:19:25 -0400 +Subject: [PATCH 3/9] scsi: adjust error_prepend() formatting + +RH-Author: Stefan Hajnoczi +RH-MergeRequest: 492: scsi: PR live migration PREEMPT fixes +RH-Jira: RHEL-155807 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina +RH-Commit: [1/3] 8d4442c1d4ec94db0cbea00f3cd32e0f50ef13dc (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 +Link: https://lore.kernel.org/r/20260401171927.396672-2-stefanha@redhat.com +Signed-off-by: Paolo Bonzini +(cherry picked from commit 15c57ac351ccb4af625cd7dc582831f472c1a68f) +Signed-off-by: Stefan Hajnoczi +--- + 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 + diff --git a/kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch b/kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch new file mode 100644 index 0000000..85a6d9b --- /dev/null +++ b/kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch @@ -0,0 +1,73 @@ +From 406d4eeb15d45107490b444482df8fcb9945da02 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Wed, 1 Apr 2026 13:19:26 -0400 +Subject: [PATCH 4/9] scsi: always send valid PREEMPT TYPE field + +RH-Author: Stefan Hajnoczi +RH-MergeRequest: 492: scsi: PR live migration PREEMPT fixes +RH-Jira: RHEL-155807 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina +RH-Commit: [2/3] 6fc4c51732256ea5e06eb05e7666cca216f8d45f (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 +Buglink: https://redhat.atlassian.net/browse/RHEL-155807 +Signed-off-by: Stefan Hajnoczi +Link: https://lore.kernel.org/r/20260401171927.396672-3-stefanha@redhat.com +Signed-off-by: Paolo Bonzini +(cherry picked from commit 15a202656cd553911272f9666aa067c706fc3dfe) +Signed-off-by: Stefan Hajnoczi +--- + 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 + diff --git a/kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch b/kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch new file mode 100644 index 0000000..fe3af9f --- /dev/null +++ b/kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch @@ -0,0 +1,55 @@ +From 364a6ea9f63c7bfe25ced3cea0d10e3e5177677f Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Wed, 15 Apr 2026 19:29:05 -0400 +Subject: [PATCH 8/9] scsi: change buf_size to unsigned int in scsi_SG_IO() + +RH-Author: Stefan Hajnoczi +RH-MergeRequest: 498: scsi: handle reservation changes across migration +RH-Jira: RHEL-153123 +RH-Acked-by: Miroslav Rezanina +RH-Commit: [4/5] fe0a33daf96237ea2baf03afaccf9dd310e207c7 (stefanha/centos-stream-qemu-kvm) + +SG_IO supports an unsigned int dxfer_len value. Existing callers use +less than 256 bytes, so scsi_SG_IO()'s uint8_t buf_size type was +sufficient. The next patch will use a larger value, so update the type. + +Signed-off-by: Stefan Hajnoczi +Link: https://lore.kernel.org/r/20260415232906.212349-2-stefanha@redhat.com +Signed-off-by: Paolo Bonzini +(cherry picked from commit f4193228dd20d3d5b8eaaf65f46e2426181794f3) +Signed-off-by: Stefan Hajnoczi +--- + hw/scsi/scsi-generic.c | 2 +- + include/hw/scsi/scsi.h | 3 ++- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index 9c5927b543..c2f61b320f 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -793,7 +793,7 @@ static int read_naa_id(const uint8_t *p, uint64_t *p_wwn) + } + + int scsi_SG_IO(BlockBackend *blk, int direction, uint8_t *cmd, +- uint8_t cmd_size, uint8_t *buf, uint8_t buf_size, ++ uint8_t cmd_size, uint8_t *buf, unsigned int buf_size, + uint32_t timeout, Error **errp) + { + sg_io_hdr_t io_header; +diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h +index f61c63c5ea..640a11522b 100644 +--- a/include/hw/scsi/scsi.h ++++ b/include/hw/scsi/scsi.h +@@ -247,7 +247,8 @@ void scsi_device_unit_attention_reported(SCSIDevice *dev); + void scsi_generic_read_device_inquiry(SCSIDevice *dev); + int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); + int scsi_SG_IO(BlockBackend *blk, int direction, uint8_t *cmd, uint8_t cmd_size, +- uint8_t *buf, uint8_t buf_size, uint32_t timeout, Error **errp); ++ uint8_t *buf, unsigned int buf_size, uint32_t timeout, ++ Error **errp); + SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); + SCSIDevice *scsi_device_get(SCSIBus *bus, int channel, int target, int lun); + +-- +2.52.0 + diff --git a/kvm-scsi-handle-reservation-changes-across-migration.patch b/kvm-scsi-handle-reservation-changes-across-migration.patch new file mode 100644 index 0000000..d205d3d --- /dev/null +++ b/kvm-scsi-handle-reservation-changes-across-migration.patch @@ -0,0 +1,246 @@ +From 6724a87dab9516a52086427ab53453171bada849 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Wed, 15 Apr 2026 19:29:06 -0400 +Subject: [PATCH 9/9] scsi: handle reservation changes across migration + +RH-Author: Stefan Hajnoczi +RH-MergeRequest: 498: scsi: handle reservation changes across migration +RH-Jira: RHEL-153123 +RH-Acked-by: Miroslav Rezanina +RH-Commit: [5/5] 4ba82297c25e0992ec3f695f6bd3337aa44bb50b (stefanha/centos-stream-qemu-kvm) + +Other nodes in the cluster can preempt or clear SCSI Persistent +Reservations at any time. When this happens across live migration, the +reservation state transferred with the guest might be outdated. + +Attempt to handle such cases gracefully by checking the current +reservation or registered keys to detect stale state before restoring. +If the actual state of the disk has changed, do not modify it and accept +that as the most up-to-date state. + +Do this using READ RESERVATION when the guest holds a reservation or +READ KEYS when the guest has registered a key but does not hold a +reservation. + +There is still a race condition between checking and restoring state, +but it seems unavoidable and is no worse than before. + +Buglink: https://redhat.atlassian.net/browse/RHEL-153123 +Fixes: ab57b51f1375b6a6f098a74c6f79207a9630948d ("scsi: save/load SCSI reservation state") +Reported-by: Qing Wang +Signed-off-by: Stefan Hajnoczi +Link: https://lore.kernel.org/r/20260415232906.212349-3-stefanha@redhat.com +Signed-off-by: Paolo Bonzini +(cherry picked from commit 87c7b07fe4aa43dc7257b1e711faa835fac1cccb) +Signed-off-by: Stefan Hajnoczi +--- + hw/scsi/scsi-generic.c | 173 +++++++++++++++++++++++++++++++++++------ + 1 file changed, 149 insertions(+), 24 deletions(-) + +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index c2f61b320f..452908ba95 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -478,13 +478,84 @@ static bool scsi_generic_pr_preempt(SCSIDevice *s, uint64_t key, + return true; + } + ++/* ++ * Returns true if the given key is registered or false otherwise (including ++ * errors). ++ */ ++static bool scsi_generic_pr_key_registered(SCSIDevice *s, uint64_t key, ++ Error **errp) ++{ ++ const size_t key_list_offset = 8; /* in READ KEYS parameter data */ ++ uint64_t key_be = cpu_to_be64(key); ++ uint8_t cmd[10] = {}; ++ size_t buf_len; ++ g_autofree uint8_t *buf = NULL; ++ uint32_t additional_length = 16 * 8; /* initial key list size */ ++ ++ /* ++ * Loop to resize parameter data buffer when there are many keys. It would ++ * be simpler to hardcode the maximum buffer size (it's only 64 KB), but ++ * SG_IO can fail with EINVAL if the host kernel blkdev queue limits are ++ * too low. ++ */ ++ do { ++ uint16_t allocation_length_be; ++ int ret; ++ ++ buf_len = key_list_offset + additional_length; ++ buf = g_realloc(buf, buf_len); ++ memset(buf, 0, buf_len); ++ ++ cmd[0] = PERSISTENT_RESERVE_IN; ++ cmd[1] = PRI_READ_KEYS; ++ allocation_length_be = cpu_to_be16(buf_len); ++ memcpy(&cmd[7], &allocation_length_be, sizeof(allocation_length_be)); ++ ++ ret = scsi_SG_IO(s->conf.blk, SG_DXFER_FROM_DEV, cmd, sizeof(cmd), ++ buf, buf_len, s->io_timeout, errp); ++ if (ret < 0) { ++ error_prepend(errp, "PERSISTENT RESERVE IN with READ KEYS: "); ++ return false; ++ } ++ ++ memcpy(&additional_length, &buf[4], sizeof(additional_length)); ++ be32_to_cpus(&additional_length); ++ ++ /* ++ * The parameter data's ADDITIONAL LENGTH must not overflow the CDB's ++ * 16-bit ALLOCATION LENGTH field since the next loop iteration will ++ * compute ALLOCATION LENGTH based on ADDITIONAL LENGTH. ++ */ ++ if (additional_length > UINT16_MAX - key_list_offset) { ++ error_setg(errp, "got invalid ADDITIONAL LENGTH %" PRIu32 ++ " from READ KEYS", additional_length); ++ return false; ++ } ++ ++ for (size_t i = key_list_offset; i < buf_len; i += sizeof(key_be)) { ++ if (i - key_list_offset >= additional_length) { ++ break; /* end of parameter list */ ++ } ++ ++ if (memcmp(&key_be, &buf[i], sizeof(key_be)) == 0) { ++ return true; /* key found */ ++ } ++ } ++ } while (additional_length > buf_len - key_list_offset); ++ ++ return false; /* key not found */ ++} ++ + /* Register keys and preempt reservations after live migration */ + bool scsi_generic_pr_state_preempt(SCSIDevice *s, Error **errp) + { + SCSIPRState *pr_state = &s->pr_state; ++ Error *local_err = NULL; ++ bool check_stale_key = true; + uint64_t key; + uint8_t resv_type; + ++ /* Get the migrated PR state */ + WITH_QEMU_LOCK_GUARD(&pr_state->mutex) { + key = pr_state->key; + resv_type = pr_state->resv_type; +@@ -492,36 +563,90 @@ bool scsi_generic_pr_state_preempt(SCSIDevice *s, Error **errp) + + trace_scsi_generic_pr_state_preempt(key, resv_type); + +- if (key) { +- if (!scsi_generic_pr_register(s, key, errp)) { ++ /* Handle stale PR state (e.g. another node preempted) */ ++ if (resv_type) { ++ uint64_t dev_key; ++ uint8_t dev_resv_type; ++ ++ if (scsi_generic_read_reservation(s, &dev_key, &dev_resv_type, ++ errp) < 0) { + return false; + } + +- /* +- * Two cases: +- * +- * 1. There is no reservation (resv_type is 0) and the other I_T nexus +- * will be unregistered. This is important so the source host does +- * not leak registered keys across live migration. +- * +- * 2. There is a reservation (resv_type is not 0) and the other I_T +- * nexus will be unregistered and its reservation is atomically +- * taken over by us. This is the scenario where a reservation is +- * migrated along with the guest. +- */ +- if (!scsi_generic_pr_preempt(s, key, resv_type, errp)) { +- return false; ++ if (dev_resv_type != resv_type) { ++ /* vmstate had a stale reservation type */ ++ g_autofree char *name = qdev_get_human_name(&s->qdev); ++ warn_report("Expected SCSI reservation type 0x%x on device '%s', " ++ "got 0x%x, using new type", ++ resv_type, name, dev_resv_type); ++ resv_type = dev_resv_type; + } + +- /* +- * 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); ++ if (dev_key == key) { ++ /* The reservation exists, no need to check for a stale key */ ++ check_stale_key = false; ++ } else { ++ g_autofree char *name = qdev_get_human_name(&s->qdev); ++ warn_report("Expected SCSI reservation with key 0x%" PRIx64 ++ " on device '%s', got 0x%" PRIx64 ", ignoring " ++ "reservation", ++ key, name, dev_key); ++ resv_type = 0; /* vmstate had a stale reservation */ ++ } ++ } ++ ++ if (key != 0 && check_stale_key && ++ !scsi_generic_pr_key_registered(s, key, &local_err)) { ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return false; + } ++ ++ g_autofree char *name = qdev_get_human_name(&s->qdev); ++ warn_report("SCSI reservation key 0x%" PRIx64 " on device '%s' not " ++ "registered after migration, ignoring", ++ key, name); ++ key = 0; /* vmstate had a stale key */ ++ } ++ ++ /* Stale PR state may have been updated */ ++ WITH_QEMU_LOCK_GUARD(&pr_state->mutex) { ++ pr_state->key = key; ++ pr_state->resv_type = resv_type; ++ } ++ ++ if (key == 0) { ++ return true; /* no PR state, do nothing */ ++ } ++ ++ if (!scsi_generic_pr_register(s, key, errp)) { ++ return false; ++ } ++ ++ /* ++ * Two cases: ++ * ++ * 1. There is no reservation (resv_type is 0) and the other I_T nexus ++ * will be unregistered. This is important so the source host does ++ * not leak registered keys across live migration. ++ * ++ * 2. There is a reservation (resv_type is not 0) and the other I_T ++ * nexus will be unregistered and its reservation is atomically ++ * taken over by us. This is the scenario where a reservation is ++ * migrated along with the guest. ++ */ ++ 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 + diff --git a/kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch b/kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch new file mode 100644 index 0000000..6e6e88d --- /dev/null +++ b/kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch @@ -0,0 +1,54 @@ +From 5ccd7244084cc6820a0a00df01c25484181b7fca Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Wed, 1 Apr 2026 13:19:27 -0400 +Subject: [PATCH 5/9] scsi: register again after PREEMPT without reservation + +RH-Author: Stefan Hajnoczi +RH-MergeRequest: 492: scsi: PR live migration PREEMPT fixes +RH-Jira: RHEL-155807 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina +RH-Commit: [3/3] 21242c1e293a3611c75a5cdbc99e55c6ab86cc20 (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 +Reviewed-by: Paolo Bonzini +Link: https://lore.kernel.org/r/20260401171927.396672-4-stefanha@redhat.com +Signed-off-by: Paolo Bonzini +(cherry picked from commit b9a3b329d09dea5a4692de483419693ef0e23052) +Signed-off-by: Stefan Hajnoczi +--- + 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 + diff --git a/kvm-vfio-container-Restrict-dma_map_file-to-shared-RAM-o.patch b/kvm-vfio-container-Restrict-dma_map_file-to-shared-RAM-o.patch new file mode 100644 index 0000000..e4426e5 --- /dev/null +++ b/kvm-vfio-container-Restrict-dma_map_file-to-shared-RAM-o.patch @@ -0,0 +1,97 @@ +From 773e27e2cfefb0958df726b726a6de91bda8771d Mon Sep 17 00:00:00 2001 +From: Chenyi Qiang +Date: Wed, 27 May 2026 18:11:08 +0800 +Subject: [PATCH 7/9] vfio/container: Restrict dma_map_file() to shared RAM or + RAM devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Rodolfo Vick +RH-MergeRequest: 497: vfio/container: Restrict dma_map_file() to shared RAM or RAM devices +RH-Jira: RHEL-180750 +RH-Acked-by: Eric Auger +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: Cédric Le Goater +RH-Commit: [1/1] 423a647a11151907ab26eb341e73298a2880908e (rovick1/qemu-kvm) + +vfio_container_dma_map() uses dma_map_file() whenever a RAMBlock has an +fd and the VFIO IOMMU backend supports file-based DMA mapping. That is +not correct for private file-backed guest RAM. + +dma_map_file() resolves PFNs from the backing file, but private guest +RAM mappings (MAP_PRIVATE) can run on different PFNs than the file +because they are subject to copy-on-write (COW) anomalies. As a result, +using dma_map_file() on a privately mapped RAMBlock can program DMA +against pages that do not back QEMU's actual guest memory. + +Fix this by using dma_map_file() only for shared mapped RAMBlocks +MAP_SHARED) or RAM device regions. + +Fixes: fb32965b6dd8 ("vfio/iommufd: use IOMMU_IOAS_MAP_FILE") +Reported-by: Farrah Chen +Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220776 +Reviewed-by: Zhenzhong Duan +Suggested-by: Cédric Le Goater +Signed-off-by: Chenyi Qiang +Link: https://lore.kernel.org/qemu-devel/20260527101109.71781-1-chenyi.qiang@intel.com +Reviewed-by: Cédric Le Goater +Signed-off-by: Cédric Le Goater +--- + hw/vfio/container.c | 34 +++++++++++++++++++++++++++++++--- + 1 file changed, 31 insertions(+), 3 deletions(-) + +diff --git a/hw/vfio/container.c b/hw/vfio/container.c +index 1b8569d36a..f534797a98 100644 +--- a/hw/vfio/container.c ++++ b/hw/vfio/container.c +@@ -73,15 +73,43 @@ void vfio_address_space_insert(VFIOAddressSpace *space, + bcontainer->space = space; + } + ++static bool vfio_container_can_dma_map_file(VFIOContainer *bcontainer, ++ MemoryRegion *mr, int *fd) ++{ ++ VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); ++ RAMBlock *rb = mr->ram_block; ++ ++ if (!vioc->dma_map_file || !rb) { ++ return false; ++ } ++ ++ *fd = qemu_ram_get_fd(rb); ++ if (*fd < 0) { ++ return false; ++ } ++ ++ /* ++ * We can use IOMMU DMA mapping (IOMMU_IOAS_MAP_FILE) for : ++ * ++ * 1) Guest RAM blocks explicitly configured as shared (MAP_SHARED) ++ * 2) RAM device sub-regions (MMIO BARs) ++ * ++ * Private RAM mappings (MAP_PRIVATE) are strictly excluded. Because ++ * they are subject to copy-on-write (COW) anomalies, their underlying ++ * PFNs can permanently diverge from the backing file ++ */ ++ return qemu_ram_is_shared(rb) || memory_region_is_ram_device(mr); ++} ++ + int vfio_container_dma_map(VFIOContainer *bcontainer, + hwaddr iova, uint64_t size, + void *vaddr, bool readonly, MemoryRegion *mr) + { + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); +- RAMBlock *rb = mr->ram_block; +- int mfd = rb ? qemu_ram_get_fd(rb) : -1; ++ int mfd; + +- if (mfd >= 0 && vioc->dma_map_file) { ++ if (vfio_container_can_dma_map_file(bcontainer, mr, &mfd)) { ++ RAMBlock *rb = mr->ram_block; + unsigned long start = vaddr - qemu_ram_get_host_addr(rb); + unsigned long offset = qemu_ram_get_fd_offset(rb); + +-- +2.52.0 + diff --git a/qemu-kvm.spec b/qemu-kvm.spec index 3cd07c1..a6b0662 100644 --- a/qemu-kvm.spec +++ b/qemu-kvm.spec @@ -143,7 +143,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \ Summary: QEMU is a machine emulator and virtualizer Name: qemu-kvm Version: 10.1.0 -Release: 19%{?rcrel}%{?dist}%{?cc_suffix} +Release: 20%{?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) @@ -676,6 +676,24 @@ Patch259: kvm-linux-headers-Update-to-Linux-v6.19-rc1.patch Patch260: kvm-hw-vfio-Add-helper-to-retrieve-device-feature.patch # For RHEL-138494 - NVIDIA:Grace-Hopper:Backport vfio: Add DMABUF support for PCI BAR regions - RHEL 10.3 Patch261: kvm-hw-vfio-region-Create-dmabuf-for-PCI-BAR-per-region.patch +# For RHEL-178767 - MSHV backport onto QEMU 10.1.0 is not able to launch MSHV guests +Patch262: kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch +# For RHEL-178767 - MSHV backport onto QEMU 10.1.0 is not able to launch MSHV guests +Patch263: kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch +# For RHEL-155807 - live migration failed the VM just register key only [rhel-10.3] +Patch264: kvm-scsi-adjust-error_prepend-formatting.patch +# For RHEL-155807 - live migration failed the VM just register key only [rhel-10.3] +Patch265: kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch +# For RHEL-155807 - live migration failed the VM just register key only [rhel-10.3] +Patch266: kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch +# For RHEL-178846 - [aarch64] qemu-kvm crashes on --device tpm-tis-device,? +Patch267: kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch +# For RHEL-180750 - Backport in QEMU : vfio/container: Restrict dma_map_file() to shared RAM or RAM devices +Patch268: kvm-vfio-container-Restrict-dma_map_file-to-shared-RAM-o.patch +# For RHEL-153123 - live migration failed or get failed WSFC test result during WSFC testing [rhel-10.3] +Patch269: kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch +# For RHEL-153123 - live migration failed or get failed WSFC test result during WSFC testing [rhel-10.3] +Patch270: kvm-scsi-handle-reservation-changes-across-migration.patch %if %{have_clang} BuildRequires: clang @@ -1755,6 +1773,27 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %endif %changelog +* Mon Jun 08 2026 Miroslav Rezanina - 10.1.0-20 +- kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch [RHEL-178767] +- kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch [RHEL-178767] +- kvm-scsi-adjust-error_prepend-formatting.patch [RHEL-155807] +- kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch [RHEL-155807] +- kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch [RHEL-155807] +- kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch [RHEL-178846] +- kvm-vfio-container-Restrict-dma_map_file-to-shared-RAM-o.patch [RHEL-180750] +- kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch [RHEL-153123] +- kvm-scsi-handle-reservation-changes-across-migration.patch [RHEL-153123] +- Resolves: RHEL-178767 + (MSHV backport onto QEMU 10.1.0 is not able to launch MSHV guests) +- Resolves: RHEL-155807 + (live migration failed the VM just register key only [rhel-10.3]) +- Resolves: RHEL-178846 + ([aarch64] qemu-kvm crashes on --device tpm-tis-device,?) +- Resolves: RHEL-180750 + (Backport in QEMU : vfio/container: Restrict dma_map_file() to shared RAM or RAM devices) +- Resolves: RHEL-153123 + (live migration failed or get failed WSFC test result during WSFC testing [rhel-10.3]) + * Tue May 26 2026 Miroslav Rezanina - 10.1.0-19 - kvm-vmstate-Introduce-VMSTATE_VARRAY_INT32_ALLOC.patch [RHEL-174858] - kvm-target-arm-Move-compare_u64-to-helper.c.patch [RHEL-174858] diff --git a/rpminspect.yaml b/rpminspect.yaml index 30274b3..e09d8b9 100644 --- a/rpminspect.yaml +++ b/rpminspect.yaml @@ -3,6 +3,7 @@ elf: exclude_path: (.*s390-ccw.img.*)|(.*s390-netboot.img.*) inspections: badfuncs: off + lostpayload: off annocheck: - hardened: --skip-cf-protection --skip-property-note --skip-stack-clash --ignore-unknown --verbose - rhel-policy: --skip-cf-protection --skip-property-note --skip-stack-clash --ignore-unknown --verbose