* Mon Jun 08 2026 Miroslav Rezanina <mrezanin@redhat.com> - 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])
This commit is contained in:
parent
085da1e81c
commit
e2263aca2f
690
kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch
Normal file
690
kvm-accel-mshv-Remove-remap-overlapping-mappings-code.patch
Normal file
@ -0,0 +1,690 @@
|
||||
From 0d484fe45aee9a7481d543ac2a0453a30872ffbb 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 1/9] accel/mshv: Remove remap overlapping mappings code
|
||||
|
||||
RH-Author: Magnus Kulke <None>
|
||||
RH-MergeRequest: 490: accel/mshv: backport required patches for mshv guests
|
||||
RH-Jira: RHEL-178767
|
||||
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
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 <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, ®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
|
||||
|
||||
58
kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch
Normal file
58
kvm-accel-mshv-implement-cpu_thread_is_idle-hook.patch
Normal file
@ -0,0 +1,58 @@
|
||||
From 2a298912b20e7d47ce5ac68c8524ab286d6ecf6f 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 2/9] accel/mshv: implement cpu_thread_is_idle() hook
|
||||
|
||||
RH-Author: Magnus Kulke <None>
|
||||
RH-MergeRequest: 490: accel/mshv: backport required patches for mshv guests
|
||||
RH-Jira: RHEL-178767
|
||||
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
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 <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
|
||||
|
||||
109
kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch
Normal file
109
kvm-hw-tpm-tpm_tis_sysbus-defer-resource-allocation-to-r.patch
Normal file
@ -0,0 +1,109 @@
|
||||
From 6526d78b916e57e08393da88124df3adfb40604d Mon Sep 17 00:00:00 2001
|
||||
From: Mohammadfaiz Bawa <mbawa@redhat.com>
|
||||
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 <None>
|
||||
RH-MergeRequest: 496: hw/tpm/tpm_tis_sysbus: defer resource allocation to realize
|
||||
RH-Jira: RHEL-178846
|
||||
RH-Acked-by: Gavin Shan <gshan@redhat.com>
|
||||
RH-Acked-by: Eric Auger <eric.auger@redhat.com>
|
||||
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 <mbawa@redhat.com>
|
||||
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
Message-ID: <20260528093123.55403-1-mbawa@redhat.com>
|
||||
Message-ID: <CAFEAcA8fEYODmPhbh1W=oPGvju-P=qWvN_dyWrPqAr-E9FK7UA@mail.gmail.com>
|
||||
[PMD: Amend Peter comment from previous mail in description]
|
||||
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
(cherry picked from commit dc6444b9c595dc40d7ead0837b930b83ec966e1f)
|
||||
Signed-off-by: Mohammadfaiz Bawa <mbawa@redhat.com>
|
||||
---
|
||||
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
|
||||
|
||||
49
kvm-scsi-adjust-error_prepend-formatting.patch
Normal file
49
kvm-scsi-adjust-error_prepend-formatting.patch
Normal file
@ -0,0 +1,49 @@
|
||||
From 77d65b4a5dc0172a29dd810363d4f702242b929a Mon Sep 17 00:00:00 2001
|
||||
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Date: Wed, 1 Apr 2026 13:19:25 -0400
|
||||
Subject: [PATCH 3/9] scsi: adjust error_prepend() formatting
|
||||
|
||||
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-MergeRequest: 492: scsi: PR live migration PREEMPT fixes
|
||||
RH-Jira: RHEL-155807
|
||||
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
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 <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
|
||||
|
||||
73
kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch
Normal file
73
kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch
Normal file
@ -0,0 +1,73 @@
|
||||
From 406d4eeb15d45107490b444482df8fcb9945da02 Mon Sep 17 00:00:00 2001
|
||||
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Date: Wed, 1 Apr 2026 13:19:26 -0400
|
||||
Subject: [PATCH 4/9] scsi: always send valid PREEMPT TYPE field
|
||||
|
||||
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-MergeRequest: 492: scsi: PR live migration PREEMPT fixes
|
||||
RH-Jira: RHEL-155807
|
||||
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
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 <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
|
||||
|
||||
55
kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch
Normal file
55
kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch
Normal file
@ -0,0 +1,55 @@
|
||||
From 364a6ea9f63c7bfe25ced3cea0d10e3e5177677f Mon Sep 17 00:00:00 2001
|
||||
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
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 <stefanha@redhat.com>
|
||||
RH-MergeRequest: 498: scsi: handle reservation changes across migration
|
||||
RH-Jira: RHEL-153123
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
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 <stefanha@redhat.com>
|
||||
Link: https://lore.kernel.org/r/20260415232906.212349-2-stefanha@redhat.com
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
(cherry picked from commit f4193228dd20d3d5b8eaaf65f46e2426181794f3)
|
||||
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
---
|
||||
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
|
||||
|
||||
246
kvm-scsi-handle-reservation-changes-across-migration.patch
Normal file
246
kvm-scsi-handle-reservation-changes-across-migration.patch
Normal file
@ -0,0 +1,246 @@
|
||||
From 6724a87dab9516a52086427ab53453171bada849 Mon Sep 17 00:00:00 2001
|
||||
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Date: Wed, 15 Apr 2026 19:29:06 -0400
|
||||
Subject: [PATCH 9/9] scsi: handle reservation changes across migration
|
||||
|
||||
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-MergeRequest: 498: scsi: handle reservation changes across migration
|
||||
RH-Jira: RHEL-153123
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
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 <stefanha@redhat.com>
|
||||
Link: https://lore.kernel.org/r/20260415232906.212349-3-stefanha@redhat.com
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
(cherry picked from commit 87c7b07fe4aa43dc7257b1e711faa835fac1cccb)
|
||||
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
---
|
||||
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
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
From 5ccd7244084cc6820a0a00df01c25484181b7fca Mon Sep 17 00:00:00 2001
|
||||
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Date: Wed, 1 Apr 2026 13:19:27 -0400
|
||||
Subject: [PATCH 5/9] scsi: register again after PREEMPT without reservation
|
||||
|
||||
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-MergeRequest: 492: scsi: PR live migration PREEMPT fixes
|
||||
RH-Jira: RHEL-155807
|
||||
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
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 <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
|
||||
|
||||
@ -0,0 +1,97 @@
|
||||
From 773e27e2cfefb0958df726b726a6de91bda8771d Mon Sep 17 00:00:00 2001
|
||||
From: Chenyi Qiang <chenyi.qiang@intel.com>
|
||||
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 <None>
|
||||
RH-MergeRequest: 497: vfio/container: Restrict dma_map_file() to shared RAM or RAM devices
|
||||
RH-Jira: RHEL-180750
|
||||
RH-Acked-by: Eric Auger <eric.auger@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Acked-by: Cédric Le Goater <clg@redhat.com>
|
||||
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 <farrah.chen@intel.com>
|
||||
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220776
|
||||
Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
|
||||
Suggested-by: Cédric Le Goater <clg@redhat.com>
|
||||
Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
|
||||
Link: https://lore.kernel.org/qemu-devel/20260527101109.71781-1-chenyi.qiang@intel.com
|
||||
Reviewed-by: Cédric Le Goater <clg@redhat.com>
|
||||
Signed-off-by: Cédric Le Goater <clg@redhat.com>
|
||||
---
|
||||
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
|
||||
|
||||
@ -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 <mrezanin@redhat.com> - 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 <mrezanin@redhat.com> - 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]
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user