[PATCH 10/13] KVM: selftests: Add macro to atomically sync per-VM "global" pointers

From: Sean Christopherson
Date: Thu Aug 18 2022 - 18:22:55 EST


Add atomic_sync_global_pointer_to_guest() to allow sync'ing "global"
pointers that hold per-VM values, i.e. technically need to be handled in
a thread-safe manner.

Use the new macro to fix a mostly-theoretical bug where ARM's ucall MMIO
setup could result in different VMs stomping on each other.

Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
.../testing/selftests/kvm/include/kvm_util_base.h | 15 +++++++++++++++
tools/testing/selftests/kvm/lib/aarch64/ucall.c | 15 +++++++++++----
2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index 8ce9e5be70a3..f4a2622db53c 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -16,6 +16,7 @@
#include <linux/kvm.h>
#include "linux/rbtree.h"

+#include <asm/atomic.h>

#include <sys/ioctl.h>

@@ -727,6 +728,20 @@ kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start,
memcpy(&(g), _p, sizeof(g)); \
})

+/*
+ * Sync a global pointer to the guest that has a per-VM value, in which case
+ * writes to the host copy of the "global" must be serialized (in case a test
+ * is being truly crazy and spawning multiple VMs concurrently).
+ */
+#define atomic_sync_global_pointer_to_guest(vm, g, val) ({ \
+ typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g)); \
+ \
+ while (cmpxchg(&g, NULL, val)) \
+ ; \
+ memcpy(_p, &(g), sizeof(g)); \
+ WRITE_ONCE(g, NULL); \
+})
+
void assert_on_unhandled_exception(struct kvm_vcpu *vcpu);

void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu,
diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
index 132c0e98bf49..c30a6eacde34 100644
--- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
+++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
@@ -6,8 +6,17 @@
*/
#include "kvm_util.h"

+/*
+ * This "global" holds different per-VM values, it must not be accessed from
+ * host code except to sync the guest value, and that must be done atomically.
+ */
static vm_vaddr_t *ucall_exit_mmio_addr;

+static void ucall_set_mmio_addr(struct kvm_vm *vm, vm_vaddr_t *val)
+{
+ atomic_sync_global_pointer_to_guest(vm, ucall_exit_mmio_addr, val);
+}
+
static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa)
{
if (kvm_userspace_memory_region_find(vm, gpa, gpa + 1))
@@ -15,8 +24,7 @@ static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa)

virt_pg_map(vm, gpa, gpa);

- ucall_exit_mmio_addr = (vm_vaddr_t *)gpa;
- sync_global_to_guest(vm, ucall_exit_mmio_addr);
+ ucall_set_mmio_addr(vm, (vm_vaddr_t *)gpa);

return true;
}
@@ -66,8 +74,7 @@ void ucall_arch_init(struct kvm_vm *vm, void *arg)

void ucall_arch_uninit(struct kvm_vm *vm)
{
- ucall_exit_mmio_addr = 0;
- sync_global_to_guest(vm, ucall_exit_mmio_addr);
+ ucall_set_mmio_addr(vm, (vm_vaddr_t *)NULL);
}

void ucall_arch_do_ucall(vm_vaddr_t uc)
--
2.37.1.595.g718a3a8f04-goog


--iuUX1rO0XqAgLi0Z
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="0011-KVM-selftests-Add-ucall-pool-based-implementation.patch"