|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v4 35/42] xen/riscv: move a vCPU's guest interrupt file on its way to the guest
When migrating a vCPU between pCPUs the hypervisor must also migrate
the associated virtual interrupt state. arch_move_irqs() is the
per-arch hook called by generic code to trigger that.
Replace the static inline BUG_ON placeholder in asm/irq.h with a real
implementation in intc.c dispatching through a new move_irqs vintc_ops
callback, which a vINTC may leave unset.
vAPLIC does leave it unset. arch_move_irqs() is called once the
scheduler has moved a vCPU to another pCPU, but not necessarily from
either of the two pCPUs, and in sched_unit_migrate_finish() only after
the scheduler locks have been dropped: by then any vcpu_wake() can have
the vCPU picked up by its new pCPU and switched in. Moving the vCPU's
guest interrupt file from there would race with the vCPU running, which
could then enter the guest with hstatus.VGEIN still naming the old
file, i.e. a file of the new pCPU's IMSIC which may belong to another
vCPU. Worse, the hstatus.VGEIN written at the end of the move would go
into the saved context of a running vCPU, which the next trap from the
guest overwrites.
With a and b being the old and the new interrupt file of v:
pCPU migrating v new pCPU of v
---------------- -------------
sched_unit_migrate_finish():
move v to the new pCPU,
drop the scheduler locks
vcpu_wake(v)
arch_move_irqs(v): v switched in, enters the guest
take, clear and map file b with hstatus.VGEIN still naming
retarget the APLIC to file b file a, i.e. a file of this
sync, save and clear file a pCPU's IMSIC
fill file b
hstatus.VGEIN = b ----------> lost: the next trap from the
guest overwrites the frame
The file is thus moved by the vCPU itself, on its way back to the
guest: as long as a vCPU runs on a pCPU, it can neither run on nor be
moved to any other one. For that, add a vintc_ops.enter_guest callback,
which a vINTC may leave unset too, run by the new vintc_enter_guest()
from check_for_pcpu_work() for the vCPU about to enter the guest. Wire
it up in vAPLIC with imsic_migrate_vcpu(). A move then goes as follows:
old pCPU of v new pCPU of v
------------- -------------
v switched out: HGEIE[a] = 1
v moved to the new pCPU,
arch_move_irqs(v): no .move_irqs
v switched in, on its way to the
guest in check_for_pcpu_work():
vintc_enter_guest(v):
take, clear and map file b
retarget the APLIC to file b
sync, save and clear file a <-IPI-
HGEIE[a] = 0
fill file b
hstatus.VGEIN = b
sret: the guest uses file b
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
---
Changes in v4:
- Don't implement vintc_ops.move_irqs for vAPLIC: arch_move_irqs() can
be called with the vCPU free to run on its new pCPU, so moving the
vCPU's guest interrupt file from there would race with the vCPU
running. Add vintc_ops.enter_guest instead, run by
vintc_enter_guest() from check_for_pcpu_work(), and implement it for
vAPLIC with imsic_migrate_vcpu().
- Let a vINTC leave vintc_ops.move_irqs unset.
- Make imsic_migrate_vcpu() cf_check, as it is an indirect call target.
- Move right after "xen/riscv: restore register state in the new IMSIC
VS-file", so that the hooks are wired up to a complete move of the
vCPU's guest interrupt file.
- Drop the Acked-by and the Reviewed-by, as the patch changed.
- Rename from "xen/riscv: introduce arch_move_irqs()", as the
vCPU's guest interrupt file is moved on its way to the guest
rather than by arch_move_irqs().
- Update the commit message accordingly.
---
Changes in v3:
- Update the commit message: drop the two last paragraphs as they are
stale.
---
Changes in v2:
- New patch.
---
---
xen/arch/riscv/imsic.c | 2 +-
xen/arch/riscv/include/asm/imsic.h | 2 +-
xen/arch/riscv/include/asm/intc.h | 11 +++++++++++
xen/arch/riscv/include/asm/irq.h | 5 +----
xen/arch/riscv/intc.c | 20 ++++++++++++++++++++
xen/arch/riscv/traps.c | 2 ++
xen/arch/riscv/vaplic.c | 7 +++++++
7 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c
index adacac5c7268..49b2d8cbb004 100644
--- a/xen/arch/riscv/imsic.c
+++ b/xen/arch/riscv/imsic.c
@@ -1335,7 +1335,7 @@ static void imsic_vsfile_move(struct vcpu *v)
* nor be moved to another one, so its file can't be in use elsewhere while
* it is moved.
*/
-void imsic_migrate_vcpu(struct vcpu *v)
+void cf_check imsic_migrate_vcpu(struct vcpu *v)
{
ASSERT(v == current);
ASSERT(!local_irq_is_enabled());
diff --git a/xen/arch/riscv/include/asm/imsic.h
b/xen/arch/riscv/include/asm/imsic.h
index f7e537be29b4..488ce652f0b6 100644
--- a/xen/arch/riscv/include/asm/imsic.h
+++ b/xen/arch/riscv/include/asm/imsic.h
@@ -133,6 +133,6 @@ int imsic_map_guest_file(struct vcpu *v, unsigned int
vsfile_id);
void imsic_vsfile_attach(struct vcpu *v);
-void imsic_migrate_vcpu(struct vcpu *v);
+void cf_check imsic_migrate_vcpu(struct vcpu *v);
#endif /* RISCV_IMSIC_H */
diff --git a/xen/arch/riscv/include/asm/intc.h
b/xen/arch/riscv/include/asm/intc.h
index 58196927aab2..d188a45b3fe1 100644
--- a/xen/arch/riscv/include/asm/intc.h
+++ b/xen/arch/riscv/include/asm/intc.h
@@ -70,6 +70,15 @@ struct vintc_ops {
/* Restore vINTC state of the vCPU being switched in */
void (*ctxt_switch_to)(struct vcpu *n);
+
+ /* Move interrupts of vCPU to a different pCPU */
+ void (*move_irqs)(struct vcpu *v);
+
+ /*
+ * Update vINTC state of the current vCPU, which is about to enter the
+ * guest.
+ */
+ void (*enter_guest)(struct vcpu *v);
};
struct vintc {
@@ -103,4 +112,6 @@ int vintc_reserve_virq(const struct domain *d, unsigned int
virq);
void vintc_ctxt_switch_from(struct vcpu *p);
void vintc_ctxt_switch_to(struct vcpu *n);
+void vintc_enter_guest(struct vcpu *v);
+
#endif /* RISCV_INTC_H */
diff --git a/xen/arch/riscv/include/asm/irq.h b/xen/arch/riscv/include/asm/irq.h
index 2eb68a5baf04..38277d94c7cf 100644
--- a/xen/arch/riscv/include/asm/irq.h
+++ b/xen/arch/riscv/include/asm/irq.h
@@ -41,10 +41,7 @@ struct irq_desc *irq_to_desc(unsigned int irq);
struct cpu_user_regs;
struct dt_device_node;
-static inline void arch_move_irqs(struct vcpu *v)
-{
- BUG_ON("unimplemented");
-}
+void arch_move_irqs(struct vcpu *v);
int platform_get_irq(const struct dt_device_node *device, int index);
diff --git a/xen/arch/riscv/intc.c b/xen/arch/riscv/intc.c
index 70dfe6cd6f34..449f6b252414 100644
--- a/xen/arch/riscv/intc.c
+++ b/xen/arch/riscv/intc.c
@@ -204,3 +204,23 @@ void vintc_ctxt_switch_to(struct vcpu *n)
ops->ctxt_switch_to(n);
}
+
+/* Move vCPU's IRQs from one pCPU to another */
+void arch_move_irqs(struct vcpu *v)
+{
+ const struct vintc_ops *ops = v->domain->arch.vintc->ops;
+
+ if ( ops->move_irqs )
+ ops->move_irqs(v);
+}
+
+/* Update vINTC state of @v, the vCPU about to enter the guest. */
+void vintc_enter_guest(struct vcpu *v)
+{
+ const struct vintc_ops *ops = v->domain->arch.vintc->ops;
+
+ ASSERT(v == current);
+
+ if ( ops->enter_guest )
+ ops->enter_guest(v);
+}
diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c
index 93996580cccf..aea57595c1ab 100644
--- a/xen/arch/riscv/traps.c
+++ b/xen/arch/riscv/traps.c
@@ -177,6 +177,8 @@ static void check_for_pcpu_work(void)
{
struct vcpu *curr = current;
+ vintc_enter_guest(curr);
+
vcpu_sync_interrupts(curr);
vcpu_flush_interrupts(curr);
diff --git a/xen/arch/riscv/vaplic.c b/xen/arch/riscv/vaplic.c
index eb2a263ceecb..b849ea1c97e8 100644
--- a/xen/arch/riscv/vaplic.c
+++ b/xen/arch/riscv/vaplic.c
@@ -623,6 +623,13 @@ static const struct vintc_ops vintc_ops = {
*/
.ctxt_switch_from = imsic_ctxt_switch_from,
.ctxt_switch_to = imsic_ctxt_switch_to,
+ /*
+ * No .move_irqs: the scheduler calls arch_move_irqs() from any pCPU, and
+ * in sched_unit_migrate_finish() with the vCPU free to run on its new
+ * pCPU meanwhile, while a guest interrupt file can only be moved safely
+ * by the vCPU itself. It moves it on its way to the guest instead.
+ */
+ .enter_guest = imsic_migrate_vcpu,
};
int domain_vaplic_init(struct domain *d)
--
2.56.1
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |