Commit 728fcc46 authored by Ricardo Koller's avatar Ricardo Koller Committed by Marc Zyngier

KVM: selftests: aarch64: Add test for restoring active IRQs

Add a test that restores multiple IRQs in active state, it does it by
writing into ISACTIVER from the guest and using KVM ioctls. This test
tries to emulate what would happen during a live migration: restore
active IRQs.
Signed-off-by: default avatarRicardo Koller <ricarkol@google.com>
Acked-by: default avatarAndrew Jones <drjones@redhat.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20211109023906.1091208-18-ricarkol@google.com
parent bebd8f3f
...@@ -66,6 +66,7 @@ typedef enum { ...@@ -66,6 +66,7 @@ typedef enum {
KVM_SET_LEVEL_INFO_HIGH, KVM_SET_LEVEL_INFO_HIGH,
KVM_INJECT_IRQFD, KVM_INJECT_IRQFD,
KVM_WRITE_ISPENDR, KVM_WRITE_ISPENDR,
KVM_WRITE_ISACTIVER,
} kvm_inject_cmd; } kvm_inject_cmd;
struct kvm_inject_args { struct kvm_inject_args {
...@@ -96,6 +97,9 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, ...@@ -96,6 +97,9 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc,
#define KVM_INJECT(cmd, intid) \ #define KVM_INJECT(cmd, intid) \
_KVM_INJECT_MULTI(cmd, intid, 1, false) _KVM_INJECT_MULTI(cmd, intid, 1, false)
#define KVM_ACTIVATE(cmd, intid) \
kvm_inject_call(cmd, intid, 1, 1, false);
struct kvm_inject_desc { struct kvm_inject_desc {
kvm_inject_cmd cmd; kvm_inject_cmd cmd;
/* can inject PPIs, PPIs, and/or SPIs. */ /* can inject PPIs, PPIs, and/or SPIs. */
...@@ -119,6 +123,12 @@ static struct kvm_inject_desc inject_level_fns[] = { ...@@ -119,6 +123,12 @@ static struct kvm_inject_desc inject_level_fns[] = {
{ 0, }, { 0, },
}; };
static struct kvm_inject_desc set_active_fns[] = {
/* sgi ppi spi */
{ KVM_WRITE_ISACTIVER, true, true, true },
{ 0, },
};
#define for_each_inject_fn(t, f) \ #define for_each_inject_fn(t, f) \
for ((f) = (t); (f)->cmd; (f)++) for ((f) = (t); (f)->cmd; (f)++)
...@@ -126,6 +136,9 @@ static struct kvm_inject_desc inject_level_fns[] = { ...@@ -126,6 +136,9 @@ static struct kvm_inject_desc inject_level_fns[] = {
for_each_inject_fn(t, f) \ for_each_inject_fn(t, f) \
if ((args)->kvm_supports_irqfd || (f)->cmd != KVM_INJECT_IRQFD) if ((args)->kvm_supports_irqfd || (f)->cmd != KVM_INJECT_IRQFD)
#define for_each_supported_activate_fn(args, t, f) \
for_each_supported_inject_fn((args), (t), (f))
/* Shared between the guest main thread and the IRQ handlers. */ /* Shared between the guest main thread and the IRQ handlers. */
volatile uint64_t irq_handled; volatile uint64_t irq_handled;
volatile uint32_t irqnr_received[MAX_SPI + 1]; volatile uint32_t irqnr_received[MAX_SPI + 1];
...@@ -147,6 +160,12 @@ static uint64_t gic_read_ap1r0(void) ...@@ -147,6 +160,12 @@ static uint64_t gic_read_ap1r0(void)
return reg; return reg;
} }
static void gic_write_ap1r0(uint64_t val)
{
write_sysreg_s(val, SYS_ICV_AP1R0_EL1);
isb();
}
static void guest_set_irq_line(uint32_t intid, uint32_t level); static void guest_set_irq_line(uint32_t intid, uint32_t level);
static void guest_irq_generic_handler(bool eoi_split, bool level_sensitive) static void guest_irq_generic_handler(bool eoi_split, bool level_sensitive)
...@@ -274,6 +293,55 @@ static void guest_inject(struct test_args *args, ...@@ -274,6 +293,55 @@ static void guest_inject(struct test_args *args,
reset_priorities(args); reset_priorities(args);
} }
/*
* Restore the active state of multiple concurrent IRQs (given by
* concurrent_irqs). This does what a live-migration would do on the
* destination side assuming there are some active IRQs that were not
* deactivated yet.
*/
static void guest_restore_active(struct test_args *args,
uint32_t first_intid, uint32_t num,
kvm_inject_cmd cmd)
{
uint32_t prio, intid, ap1r;
int i;
/* Set the priorities of the first (KVM_NUM_PRIOS - 1) IRQs
* in descending order, so intid+1 can preempt intid.
*/
for (i = 0, prio = (num - 1) * 8; i < num; i++, prio -= 8) {
GUEST_ASSERT(prio >= 0);
intid = i + first_intid;
gic_set_priority(intid, prio);
}
/* In a real migration, KVM would restore all GIC state before running
* guest code.
*/
for (i = 0; i < num; i++) {
intid = i + first_intid;
KVM_ACTIVATE(cmd, intid);
ap1r = gic_read_ap1r0();
ap1r |= 1U << i;
gic_write_ap1r0(ap1r);
}
/* This is where the "migration" would occur. */
/* finish handling the IRQs starting with the highest priority one. */
for (i = 0; i < num; i++) {
intid = num - i - 1 + first_intid;
gic_set_eoi(intid);
if (args->eoi_split)
gic_set_dir(intid);
}
for (i = 0; i < num; i++)
GUEST_ASSERT(!gic_irq_get_active(i + first_intid));
GUEST_ASSERT_EQ(gic_read_ap1r0(), 0);
GUEST_ASSERT_IAR_EMPTY();
}
/* /*
* Polls the IAR until it's not a spurious interrupt. * Polls the IAR until it's not a spurious interrupt.
* *
...@@ -391,6 +459,19 @@ static void test_preemption(struct test_args *args, struct kvm_inject_desc *f) ...@@ -391,6 +459,19 @@ static void test_preemption(struct test_args *args, struct kvm_inject_desc *f)
test_inject_preemption(args, MIN_SPI, 4, f->cmd); test_inject_preemption(args, MIN_SPI, 4, f->cmd);
} }
static void test_restore_active(struct test_args *args, struct kvm_inject_desc *f)
{
/* Test up to 4 active IRQs. Same reason as in test_preemption. */
if (f->sgi)
guest_restore_active(args, MIN_SGI, 4, f->cmd);
if (f->ppi)
guest_restore_active(args, MIN_PPI, 4, f->cmd);
if (f->spi)
guest_restore_active(args, MIN_SPI, 4, f->cmd);
}
static void guest_code(struct test_args args) static void guest_code(struct test_args args)
{ {
uint32_t i, nr_irqs = args.nr_irqs; uint32_t i, nr_irqs = args.nr_irqs;
...@@ -422,6 +503,12 @@ static void guest_code(struct test_args args) ...@@ -422,6 +503,12 @@ static void guest_code(struct test_args args)
test_injection_failure(&args, f); test_injection_failure(&args, f);
} }
/* Restore the active state of IRQs. This would happen when live
* migrating IRQs in the middle of being handled.
*/
for_each_supported_activate_fn(&args, set_active_fns, f)
test_restore_active(&args, f);
GUEST_DONE(); GUEST_DONE();
} }
...@@ -619,6 +706,10 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, ...@@ -619,6 +706,10 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd,
kvm_irq_write_ispendr_check(gic_fd, i, kvm_irq_write_ispendr_check(gic_fd, i,
VCPU_ID, expect_failure); VCPU_ID, expect_failure);
break; break;
case KVM_WRITE_ISACTIVER:
for (i = intid; i < intid + num; i++)
kvm_irq_write_isactiver(gic_fd, i, VCPU_ID);
break;
default: default:
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment