Merge branch kvm-arm64/rd-assignment-fixes into kvmarm-master/next

* kvm-arm64/rd-assignment-fixes:
  : \
  : Fixes for failed GICv3 RD assignment to vcpu rollback.
  :
  : Patches by Karl Mehltretter.
  : /
  KVM: arm64: selftests: Test VGICv3 redistributor region retry
  KVM: arm64: selftests: Pass guest code to vm_gic_create_with_vcpus()
  KVM: arm64: vgic-v3: Roll back assignments from the new region
  KVM: arm64: vgic-v3: Undo assignment on iodev registration failure

Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
Marc Zyngier 2026-10-11 11:01:00 +01:00
commit 6ec57ccbc5
2 changed files with 131 additions and 18 deletions

View File

@ -818,16 +818,21 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu)
rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rd_registers);
rd_dev->redist_vcpu = vcpu;
/* Protected by slots_lock */
rdreg->free_index++;
mutex_unlock(&kvm->arch.config_lock);
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, rd_base,
2 * SZ_64K, &rd_dev->dev);
if (ret)
return ret;
if (ret) {
guard(mutex)(&vcpu->kvm->arch.config_lock);
vgic_cpu->rdreg->free_index--;
vgic_cpu->rdreg = NULL;
vgic_cpu->rd_iodev.base_addr = VGIC_ADDR_UNDEF;
}
/* Protected by slots_lock */
rdreg->free_index++;
return 0;
return ret;
out_unlock:
mutex_unlock(&kvm->arch.config_lock);
@ -841,7 +846,8 @@ void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu)
kvm_io_bus_unregister_dev(vcpu->kvm, KVM_MMIO_BUS, &rd_dev->dev);
}
static int vgic_register_all_redist_iodevs(struct kvm *kvm)
static int vgic_register_all_redist_iodevs(struct kvm *kvm,
struct vgic_redist_region *rdreg)
{
struct kvm_vcpu *vcpu;
unsigned long c;
@ -861,7 +867,14 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm)
for (i = 0; i < c; i++) {
vcpu = kvm_get_vcpu(kvm, i);
if (vcpu->arch.vgic_cpu.rdreg != rdreg)
continue;
vgic_unregister_redist_iodev(vcpu);
guard(mutex)(&vcpu->kvm->arch.config_lock);
vcpu->arch.vgic_cpu.rdreg = NULL;
vcpu->arch.vgic_cpu.rd_iodev.base_addr = VGIC_ADDR_UNDEF;
}
}
@ -970,10 +983,13 @@ void vgic_v3_free_redist_region(struct kvm *kvm, struct vgic_redist_region *rdre
int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
{
struct vgic_redist_region *rdreg;
int ret;
mutex_lock(&kvm->arch.config_lock);
ret = vgic_v3_alloc_redist_region(kvm, index, addr, count);
if (!ret)
rdreg = vgic_v3_rdist_region_from_index(kvm, index);
mutex_unlock(&kvm->arch.config_lock);
if (ret)
return ret;
@ -982,12 +998,9 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
* Register iodevs for each existing VCPU. Adding more VCPUs
* afterwards will register the iodevs when needed.
*/
ret = vgic_register_all_redist_iodevs(kvm);
ret = vgic_register_all_redist_iodevs(kvm, rdreg);
if (ret) {
struct vgic_redist_region *rdreg;
mutex_lock(&kvm->arch.config_lock);
rdreg = vgic_v3_rdist_region_from_index(kvm, index);
vgic_v3_free_redist_region(kvm, rdreg);
mutex_unlock(&kvm->arch.config_lock);
return ret;

View File

@ -5,6 +5,7 @@
* Copyright (C) 2020, Red Hat, Inc.
*/
#include <linux/kernel.h>
#include <linux/sizes.h>
#include <sys/syscall.h>
#include <asm/kvm.h>
#include <asm/kvm_para.h>
@ -13,12 +14,21 @@
#include "test_util.h"
#include "kvm_util.h"
#include "gic.h"
#include "processor.h"
#include "vgic.h"
#include "gic_v3.h"
#define NR_VCPUS 4
#define REDIST_RETRY_REGION0_BASE GICR_BASE_GPA
#define REDIST_RETRY_REGION1_BASE \
(REDIST_RETRY_REGION0_BASE + 2 * KVM_VGIC_V3_REDIST_SIZE)
#define REDIST_RETRY_DIST_BASE \
(REDIST_RETRY_REGION1_BASE + KVM_VGIC_V3_REDIST_SIZE)
#define REDIST_RETRY_REGION2_BASE \
(REDIST_RETRY_DIST_BASE + KVM_VGIC_V3_DIST_SIZE)
#define REG_OFFSET(vcpu, offset) (((u64)vcpu << 32) | offset)
#define VGIC_DEV_IS_V2(_d) ((_d) == KVM_DEV_TYPE_ARM_VGIC_V2)
@ -65,6 +75,23 @@ static void guest_code(void)
GUEST_DONE();
}
static void guest_check_redist_retry(void)
{
unsigned int i;
/* The first three redistributors span adjacent regions 0 and 1. */
for (i = 0; i < NR_VCPUS; i++) {
u64 base = i < 3 ? REDIST_RETRY_REGION0_BASE +
i * KVM_VGIC_V3_REDIST_SIZE :
REDIST_RETRY_REGION2_BASE;
u64 typer = readq((void *)(unsigned long)(base + GICR_TYPER));
GUEST_ASSERT_EQ(GICR_TYPER_CPU_NUMBER(typer), i);
}
GUEST_DONE();
}
/* we don't want to assert on run execution, hence that helper */
static int run_vcpu(struct kvm_vcpu *vcpu)
{
@ -73,6 +100,7 @@ static int run_vcpu(struct kvm_vcpu *vcpu)
static struct vm_gic vm_gic_create_with_vcpus(u32 gic_dev_type,
u32 nr_vcpus,
void *guest_code,
struct kvm_vcpu *vcpus[])
{
struct vm_gic v;
@ -338,7 +366,7 @@ static void test_vgic_then_vcpus(u32 gic_dev_type)
struct vm_gic v;
int ret, i;
v = vm_gic_create_with_vcpus(gic_dev_type, 1, vcpus);
v = vm_gic_create_with_vcpus(gic_dev_type, 1, guest_code, vcpus);
subtest_dist_rdist(&v);
@ -359,7 +387,8 @@ static void test_vcpus_then_vgic(u32 gic_dev_type)
struct vm_gic v;
int ret;
v = vm_gic_create_with_vcpus(gic_dev_type, NR_VCPUS, vcpus);
v = vm_gic_create_with_vcpus(gic_dev_type, NR_VCPUS, guest_code,
vcpus);
subtest_dist_rdist(&v);
@ -411,7 +440,8 @@ static void test_v3_new_redist_regions(void)
u64 addr;
int ret;
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
guest_code, vcpus);
subtest_v3_redist_regions(&v);
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
@ -422,7 +452,8 @@ static void test_v3_new_redist_regions(void)
/* step2 */
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
guest_code, vcpus);
subtest_v3_redist_regions(&v);
addr = REDIST_REGION_ATTR_ADDR(1, 0x280000, 0, 2);
@ -436,7 +467,8 @@ static void test_v3_new_redist_regions(void)
/* step 3 */
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
guest_code, vcpus);
subtest_v3_redist_regions(&v);
ret = __kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
@ -457,6 +489,70 @@ static void test_v3_new_redist_regions(void)
vm_gic_destroy(&v);
}
static void test_v3_redist_region_retry(void)
{
struct kvm_vcpu *vcpus[NR_VCPUS];
struct vm_gic v;
struct ucall uc;
u64 addr;
int ret;
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
guest_check_redist_retry, vcpus);
addr = REDIST_REGION_ATTR_ADDR(2, REDIST_RETRY_REGION0_BASE, 0, 0);
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
addr = REDIST_REGION_ATTR_ADDR(1, REDIST_RETRY_REGION1_BASE, 0, 1);
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
addr = REDIST_RETRY_DIST_BASE;
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_DIST, &addr);
addr = REDIST_REGION_ATTR_ADDR(1, REDIST_RETRY_DIST_BASE, 0, 2);
ret = __kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION,
&addr);
TEST_ASSERT(ret && errno == EINVAL,
"register redist region colliding with dist");
addr = REDIST_REGION_ATTR_ADDR(1, REDIST_RETRY_REGION2_BASE, 0, 2);
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
virt_map(v.vm, REDIST_RETRY_REGION0_BASE, REDIST_RETRY_REGION0_BASE,
vm_calc_num_guest_pages(v.vm->mode,
3 * KVM_VGIC_V3_REDIST_SIZE));
virt_map(v.vm, REDIST_RETRY_REGION2_BASE, REDIST_RETRY_REGION2_BASE,
vm_calc_num_guest_pages(v.vm->mode,
KVM_VGIC_V3_REDIST_SIZE));
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
vcpu_run(vcpus[0]);
switch (get_ucall(vcpus[0], &uc)) {
case UCALL_DONE:
break;
case UCALL_ABORT:
REPORT_GUEST_ASSERT(uc);
break;
case UCALL_NONE:
if (vcpus[0]->run->exit_reason == KVM_EXIT_MMIO)
TEST_FAIL("Unexpected MMIO exit at 0x%llx",
vcpus[0]->run->mmio.phys_addr);
fallthrough;
default:
TEST_FAIL("Unexpected ucall %lu, exit_reason %u",
uc.cmd, vcpus[0]->run->exit_reason);
}
vm_gic_destroy(&v);
}
static void test_v3_typer_accesses(void)
{
struct vm_gic v;
@ -608,7 +704,8 @@ static void test_v3_redist_ipa_range_check_at_vcpu_run(void)
int ret, i;
u64 addr;
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1, vcpus);
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1, guest_code,
vcpus);
/* Set space for 3 redists, we have 1 vcpu, so this succeeds. */
addr = max_phys_size - (3 * 2 * 0x10000);
@ -641,7 +738,8 @@ static void test_v3_its_region(void)
u64 addr;
int its_fd, ret;
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
guest_code, vcpus);
its_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_ITS);
addr = 0x401000;
@ -684,7 +782,8 @@ static void test_v3_nassgicap(void)
u32 typer2;
int ret;
vm = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
vm = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
guest_code, vcpus);
kvm_device_attr_get(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
GICD_TYPER2, &typer2);
has_nassgicap = typer2 & GICD_TYPER2_nASSGIcap;
@ -978,6 +1077,7 @@ void run_tests(u32 gic_dev_type)
if (VGIC_DEV_IS_V3(gic_dev_type)) {
test_v3_new_redist_regions();
test_v3_redist_region_retry();
test_v3_typer_accesses();
test_v3_last_bit_redist_regions();
test_v3_last_bit_single_rdist();