2021-03-22 06:04:22

by Shenming Lu

[permalink] [raw]
Subject: [PATCH v5 0/6] KVM: arm64: Add VLPI migration support on GICv4.1

Hi,

In GICv4.1, migration has been supported except for (directly-injected)
VLPI. And GICv4.1 Spec explicitly gives a way to get the VLPI's pending
state (which was crucially missing in GICv4.0). So we make VLPI migration
capable on GICv4.1 in this series.

In order to support VLPI migration, we need to save and restore all
required configuration information and pending states of VLPIs. But
in fact, the configuration information of VLPIs has already been saved
(or will be reallocated on the dst host...) in vgic(kvm) migration.
So we only have to migrate the pending states of VLPIs specially.

Below is the related workflow in migration.

On the save path:
In migration completion:
pause all vCPUs
|
call each VM state change handler:
pause other devices (just keep from sending interrupts, and
such as VFIO migration protocol has already realized it [1])
|
flush ITS tables into guest RAM
|
flush RDIST pending tables (also flush VLPI pending states here)
|
...
On the resume path:
load each device's state:
restore ITS tables (include pending tables) from guest RAM
|
for other (PCI) devices (paused), if configured to have VLPIs,
establish the forwarding paths of their VLPIs (and transfer
the pending states from kvm's vgic to VPT here)

We have tested this series in VFIO migration, and found some related
issues in QEMU [2].

Links:
[1] vfio: UAPI for migration interface for device state:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a8a24f3f6e38103b77cf399c38eb54e1219d00d6
[2] vfio: Some fixes and optimizations for VFIO migration:
https://patchwork.ozlabs.org/project/qemu-devel/cover/[email protected]/

History:

v4 -> v5
- Lock the whole pending state read/write sequence. (in Patch 5, from Marc)

v3 -> v4
- Nit fixes.
- Add a CPU cache invalidation right after unmapping the vPE. (Patch 1)
- Drop the setting of PTZ altogether. (Patch 2)
- Bail out if spot !vgic_initialized(). (in Patch 4)
- Communicate the state change (clear pending_latch) via
vgic_queue_irq_unlock. (in Patch 5)

Thanks a lot for the suggestions from Marc!

v2 -> v3
- Add the vgic initialized check to ensure that the allocation and enabling
of the doorbells have already been done before unmapping the vPEs.
- Check all get_vlpi_state related conditions in save_pending_tables in one place.
- Nit fixes.

v1 -> v2:
- Get the VLPI state from the KVM side.
- Nit fixes.

Thanks,
Shenming


Marc Zyngier (1):
irqchip/gic-v3-its: Add a cache invalidation right after vPE unmapping

Shenming Lu (4):
irqchip/gic-v3-its: Drop the setting of PTZ altogether
KVM: arm64: GICv4.1: Add function to get VLPI state
KVM: arm64: GICv4.1: Try to save VLPI state in save_pending_tables
KVM: arm64: GICv4.1: Give a chance to save VLPI state

Zenghui Yu (1):
KVM: arm64: GICv4.1: Restore VLPI pending state to physical side

.../virt/kvm/devices/arm-vgic-its.rst | 2 +-
arch/arm64/kvm/vgic/vgic-its.c | 6 +-
arch/arm64/kvm/vgic/vgic-v3.c | 66 +++++++++++++++++--
arch/arm64/kvm/vgic/vgic-v4.c | 38 +++++++++++
arch/arm64/kvm/vgic/vgic.h | 1 +
drivers/irqchip/irq-gic-v3-its.c | 21 +++++-
6 files changed, 122 insertions(+), 12 deletions(-)

--
2.19.1


2021-03-22 06:04:25

by Shenming Lu

[permalink] [raw]
Subject: [PATCH v5 1/6] irqchip/gic-v3-its: Add a cache invalidation right after vPE unmapping

From: Marc Zyngier <[email protected]>

Since there may be a direct read from the CPU side to the VPT after
unmapping the vPE, we add a cache coherency maintenance at the end
of its_vpe_irq_domain_deactivate() to ensure the validity of the VPT
read later.

Signed-off-by: Marc Zyngier <[email protected]>
Signed-off-by: Shenming Lu <[email protected]>
---
drivers/irqchip/irq-gic-v3-its.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index ed46e6057e33..4eb907f65bd0 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -4554,6 +4554,15 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain,

its_send_vmapp(its, vpe, false);
}
+
+ /*
+ * There may be a direct read to the VPT after unmapping the
+ * vPE, to guarantee the validity of this, we make the VPT
+ * memory coherent with the CPU caches here.
+ */
+ if (find_4_1_its() && !atomic_read(&vpe->vmapp_count))
+ gic_flush_dcache_to_poc(page_address(vpe->vpt_page),
+ LPI_PENDBASE_SZ);
}

static const struct irq_domain_ops its_vpe_domain_ops = {
--
2.19.1

2021-03-22 06:04:31

by Shenming Lu

[permalink] [raw]
Subject: [PATCH v5 4/6] KVM: arm64: GICv4.1: Try to save VLPI state in save_pending_tables

After pausing all vCPUs and devices capable of interrupting, in order
to save the states of all interrupts, besides flushing the states in
kvm’s vgic, we also try to flush the states of VLPIs in the virtual
pending tables into guest RAM, but we need to have GICv4.1 and safely
unmap the vPEs first.

As for the saving of VSGIs, which needs the vPEs to be mapped and might
conflict with the saving of VLPIs, but since we will map the vPEs back
at the end of save_pending_tables and both savings require the kvm->lock
to be held (thus only happen serially), it will work fine.

Signed-off-by: Shenming Lu <[email protected]>
---
arch/arm64/kvm/vgic/vgic-v3.c | 66 +++++++++++++++++++++++++++++++----
1 file changed, 60 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 6f530925a231..41ecf219c333 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/irqchip/arm-gic-v3.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/kvm.h>
#include <linux/kvm_host.h>
#include <kvm/arm_vgic.h>
@@ -356,6 +358,32 @@ int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq)
return 0;
}

+/*
+ * The deactivation of the doorbell interrupt will trigger the
+ * unmapping of the associated vPE.
+ */
+static void unmap_all_vpes(struct vgic_dist *dist)
+{
+ struct irq_desc *desc;
+ int i;
+
+ for (i = 0; i < dist->its_vm.nr_vpes; i++) {
+ desc = irq_to_desc(dist->its_vm.vpes[i]->irq);
+ irq_domain_deactivate_irq(irq_desc_get_irq_data(desc));
+ }
+}
+
+static void map_all_vpes(struct vgic_dist *dist)
+{
+ struct irq_desc *desc;
+ int i;
+
+ for (i = 0; i < dist->its_vm.nr_vpes; i++) {
+ desc = irq_to_desc(dist->its_vm.vpes[i]->irq);
+ irq_domain_activate_irq(irq_desc_get_irq_data(desc), false);
+ }
+}
+
/**
* vgic_v3_save_pending_tables - Save the pending tables into guest RAM
* kvm lock and all vcpu lock must be held
@@ -365,13 +393,28 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
struct vgic_dist *dist = &kvm->arch.vgic;
struct vgic_irq *irq;
gpa_t last_ptr = ~(gpa_t)0;
- int ret;
+ bool vlpi_avail = false;
+ int ret = 0;
u8 val;

+ if (unlikely(!vgic_initialized(kvm)))
+ return -ENXIO;
+
+ /*
+ * A preparation for getting any VLPI states.
+ * The above vgic initialized check also ensures that the allocation
+ * and enabling of the doorbells have already been done.
+ */
+ if (kvm_vgic_global_state.has_gicv4_1) {
+ unmap_all_vpes(dist);
+ vlpi_avail = true;
+ }
+
list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
int byte_offset, bit_nr;
struct kvm_vcpu *vcpu;
gpa_t pendbase, ptr;
+ bool is_pending;
bool stored;

vcpu = irq->target_vcpu;
@@ -387,24 +430,35 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
if (ptr != last_ptr) {
ret = kvm_read_guest_lock(kvm, ptr, &val, 1);
if (ret)
- return ret;
+ goto out;
last_ptr = ptr;
}

stored = val & (1U << bit_nr);
- if (stored == irq->pending_latch)
+
+ is_pending = irq->pending_latch;
+
+ if (irq->hw && vlpi_avail)
+ vgic_v4_get_vlpi_state(irq, &is_pending);
+
+ if (stored == is_pending)
continue;

- if (irq->pending_latch)
+ if (is_pending)
val |= 1 << bit_nr;
else
val &= ~(1 << bit_nr);

ret = kvm_write_guest_lock(kvm, ptr, &val, 1);
if (ret)
- return ret;
+ goto out;
}
- return 0;
+
+out:
+ if (vlpi_avail)
+ map_all_vpes(dist);
+
+ return ret;
}

/**
--
2.19.1

2021-03-22 06:04:37

by Shenming Lu

[permalink] [raw]
Subject: [PATCH v5 5/6] KVM: arm64: GICv4.1: Restore VLPI pending state to physical side

From: Zenghui Yu <[email protected]>

When setting the forwarding path of a VLPI (switch to the HW mode),
we can also transfer the pending state from irq->pending_latch to
VPT (especially in migration, the pending states of VLPIs are restored
into kvm’s vgic first). And we currently send "INT+VSYNC" to trigger
a VLPI to pending.

Signed-off-by: Zenghui Yu <[email protected]>
Signed-off-by: Shenming Lu <[email protected]>
---
arch/arm64/kvm/vgic/vgic-v4.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c
index ac029ba3d337..c1845d8f5f7e 100644
--- a/arch/arm64/kvm/vgic/vgic-v4.c
+++ b/arch/arm64/kvm/vgic/vgic-v4.c
@@ -404,6 +404,7 @@ int kvm_vgic_v4_set_forwarding(struct kvm *kvm, int virq,
struct vgic_its *its;
struct vgic_irq *irq;
struct its_vlpi_map map;
+ unsigned long flags;
int ret;

if (!vgic_supports_direct_msis(kvm))
@@ -449,6 +450,24 @@ int kvm_vgic_v4_set_forwarding(struct kvm *kvm, int virq,
irq->host_irq = virq;
atomic_inc(&map.vpe->vlpi_count);

+ /* Transfer pending state */
+ raw_spin_lock_irqsave(&irq->irq_lock, flags);
+ if (irq->pending_latch) {
+ ret = irq_set_irqchip_state(irq->host_irq,
+ IRQCHIP_STATE_PENDING,
+ irq->pending_latch);
+ WARN_RATELIMIT(ret, "IRQ %d", irq->host_irq);
+
+ /*
+ * Clear pending_latch and communicate this state
+ * change via vgic_queue_irq_unlock.
+ */
+ irq->pending_latch = false;
+ vgic_queue_irq_unlock(kvm, irq, flags);
+ } else {
+ raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
+ }
+
out:
mutex_unlock(&its->its_lock);
return ret;
--
2.19.1

2021-03-22 06:04:39

by Shenming Lu

[permalink] [raw]
Subject: [PATCH v5 3/6] KVM: arm64: GICv4.1: Add function to get VLPI state

With GICv4.1 and the vPE unmapped, which indicates the invalidation
of any VPT caches associated with the vPE, we can get the VLPI state
by peeking at the VPT. So we add a function for this.

Signed-off-by: Shenming Lu <[email protected]>
---
arch/arm64/kvm/vgic/vgic-v4.c | 19 +++++++++++++++++++
arch/arm64/kvm/vgic/vgic.h | 1 +
2 files changed, 20 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c
index 66508b03094f..ac029ba3d337 100644
--- a/arch/arm64/kvm/vgic/vgic-v4.c
+++ b/arch/arm64/kvm/vgic/vgic-v4.c
@@ -203,6 +203,25 @@ void vgic_v4_configure_vsgis(struct kvm *kvm)
kvm_arm_resume_guest(kvm);
}

+/*
+ * Must be called with GICv4.1 and the vPE unmapped, which
+ * indicates the invalidation of any VPT caches associated
+ * with the vPE, thus we can get the VLPI state by peeking
+ * at the VPT.
+ */
+void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val)
+{
+ struct its_vpe *vpe = &irq->target_vcpu->arch.vgic_cpu.vgic_v3.its_vpe;
+ int mask = BIT(irq->intid % BITS_PER_BYTE);
+ void *va;
+ u8 *ptr;
+
+ va = page_address(vpe->vpt_page);
+ ptr = va + irq->intid / BITS_PER_BYTE;
+
+ *val = !!(*ptr & mask);
+}
+
/**
* vgic_v4_init - Initialize the GICv4 data structures
* @kvm: Pointer to the VM being initialized
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index 64fcd7511110..d8cfd360838c 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -317,5 +317,6 @@ bool vgic_supports_direct_msis(struct kvm *kvm);
int vgic_v4_init(struct kvm *kvm);
void vgic_v4_teardown(struct kvm *kvm);
void vgic_v4_configure_vsgis(struct kvm *kvm);
+void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val);

#endif
--
2.19.1

2021-03-22 06:04:50

by Shenming Lu

[permalink] [raw]
Subject: [PATCH v5 2/6] irqchip/gic-v3-its: Drop the setting of PTZ altogether

GICv4.1 gives a way to get the VLPI state, which needs to map the
vPE first, and after the state read, we may remap the vPE back while
the VPT is not empty. So we can't assume that the VPT is empty at
the first map. Besides, the optimization of PTZ is probably limited
since the HW should be fairly efficient to parse the empty VPT. Let's
drop the setting of PTZ altogether.

Signed-off-by: Shenming Lu <[email protected]>
---
drivers/irqchip/irq-gic-v3-its.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 4eb907f65bd0..c8b5a88ac31c 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -794,8 +794,16 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,

its_encode_alloc(cmd, alloc);

- /* We can only signal PTZ when alloc==1. Why do we have two bits? */
- its_encode_ptz(cmd, alloc);
+ /*
+ * We can only signal PTZ when alloc==1. Why do we have two bits?
+ * GICv4.1 gives a way to get the VLPI state, which needs the vPE
+ * to be unmapped first, and in this case, we may remap the vPE
+ * back while the VPT is not empty. So we can't assume that the
+ * VPT is empty at the first map. Besides, the optimization of PTZ
+ * is probably limited since the HW should be fairly efficient to
+ * parse the empty VPT. Let's drop the setting of PTZ altogether.
+ */
+ its_encode_ptz(cmd, false);
its_encode_vconf_addr(cmd, vconf_addr);
its_encode_vmapp_default_db(cmd, desc->its_vmapp_cmd.vpe->vpe_db_lpi);

--
2.19.1

2021-03-22 06:04:54

by Shenming Lu

[permalink] [raw]
Subject: [PATCH v5 6/6] KVM: arm64: GICv4.1: Give a chance to save VLPI state

Before GICv4.1, we don't have direct access to the VLPI state. So
we simply let it fail early when encountering any VLPI in saving.

But now we don't have to return -EACCES directly if on GICv4.1. Let’s
change the hard code and give a chance to save the VLPI state (and
preserve the UAPI).

Signed-off-by: Shenming Lu <[email protected]>
---
Documentation/virt/kvm/devices/arm-vgic-its.rst | 2 +-
arch/arm64/kvm/vgic/vgic-its.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/virt/kvm/devices/arm-vgic-its.rst b/Documentation/virt/kvm/devices/arm-vgic-its.rst
index 6c304fd2b1b4..d257eddbae29 100644
--- a/Documentation/virt/kvm/devices/arm-vgic-its.rst
+++ b/Documentation/virt/kvm/devices/arm-vgic-its.rst
@@ -80,7 +80,7 @@ KVM_DEV_ARM_VGIC_GRP_CTRL
-EFAULT Invalid guest ram access
-EBUSY One or more VCPUS are running
-EACCES The virtual ITS is backed by a physical GICv4 ITS, and the
- state is not available
+ state is not available without GICv4.1
======= ==========================================================

KVM_DEV_ARM_VGIC_GRP_ITS_REGS
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index 40cbaca81333..ec7543a9617c 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -2218,10 +2218,10 @@ static int vgic_its_save_itt(struct vgic_its *its, struct its_device *device)
/*
* If an LPI carries the HW bit, this means that this
* interrupt is controlled by GICv4, and we do not
- * have direct access to that state. Let's simply fail
- * the save operation...
+ * have direct access to that state without GICv4.1.
+ * Let's simply fail the save operation...
*/
- if (ite->irq->hw)
+ if (ite->irq->hw && !kvm_vgic_global_state.has_gicv4_1)
return -EACCES;

ret = vgic_its_save_ite(its, device, ite, gpa, ite_esz);
--
2.19.1

2021-03-25 03:23:46

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v5 0/6] KVM: arm64: Add VLPI migration support on GICv4.1

On Mon, 22 Mar 2021 14:01:52 +0800, Shenming Lu wrote:
> In GICv4.1, migration has been supported except for (directly-injected)
> VLPI. And GICv4.1 Spec explicitly gives a way to get the VLPI's pending
> state (which was crucially missing in GICv4.0). So we make VLPI migration
> capable on GICv4.1 in this series.
>
> In order to support VLPI migration, we need to save and restore all
> required configuration information and pending states of VLPIs. But
> in fact, the configuration information of VLPIs has already been saved
> (or will be reallocated on the dst host...) in vgic(kvm) migration.
> So we only have to migrate the pending states of VLPIs specially.
>
> [...]

Applied to next, thanks!

[1/6] irqchip/gic-v3-its: Add a cache invalidation right after vPE unmapping
commit: 301beaf19739cb6e640ed44e630e7da993f0ecc8
[2/6] irqchip/gic-v3-its: Drop the setting of PTZ altogether
commit: c21bc068cdbe5613d3319ae171c3f2eb9f321352
[3/6] KVM: arm64: GICv4.1: Add function to get VLPI state
commit: 80317fe4a65375fae668672a1398a0fb73eb9023
[4/6] KVM: arm64: GICv4.1: Try to save VLPI state in save_pending_tables
commit: f66b7b151e00427168409f8c1857970e926b1e27
[5/6] KVM: arm64: GICv4.1: Restore VLPI pending state to physical side
commit: 12df7429213abbfa9632ab7db94f629ec309a58b
[6/6] KVM: arm64: GICv4.1: Give a chance to save VLPI state
commit: 8082d50f4817ff6a7e08f4b7e9b18e5f8bfa290d

Cheers,

M.
--
Without deviation from the norm, progress is not possible.


2021-03-25 05:59:52

by Shenming Lu

[permalink] [raw]
Subject: Re: [PATCH v5 0/6] KVM: arm64: Add VLPI migration support on GICv4.1

On 2021/3/25 2:19, Marc Zyngier wrote:
> On Mon, 22 Mar 2021 14:01:52 +0800, Shenming Lu wrote:
>> In GICv4.1, migration has been supported except for (directly-injected)
>> VLPI. And GICv4.1 Spec explicitly gives a way to get the VLPI's pending
>> state (which was crucially missing in GICv4.0). So we make VLPI migration
>> capable on GICv4.1 in this series.
>>
>> In order to support VLPI migration, we need to save and restore all
>> required configuration information and pending states of VLPIs. But
>> in fact, the configuration information of VLPIs has already been saved
>> (or will be reallocated on the dst host...) in vgic(kvm) migration.
>> So we only have to migrate the pending states of VLPIs specially.
>>
>> [...]
>
> Applied to next, thanks!

Thanks a lot again for all the comments and suggestions. :-)

Shenming

>
> [1/6] irqchip/gic-v3-its: Add a cache invalidation right after vPE unmapping
> commit: 301beaf19739cb6e640ed44e630e7da993f0ecc8
> [2/6] irqchip/gic-v3-its: Drop the setting of PTZ altogether
> commit: c21bc068cdbe5613d3319ae171c3f2eb9f321352
> [3/6] KVM: arm64: GICv4.1: Add function to get VLPI state
> commit: 80317fe4a65375fae668672a1398a0fb73eb9023
> [4/6] KVM: arm64: GICv4.1: Try to save VLPI state in save_pending_tables
> commit: f66b7b151e00427168409f8c1857970e926b1e27
> [5/6] KVM: arm64: GICv4.1: Restore VLPI pending state to physical side
> commit: 12df7429213abbfa9632ab7db94f629ec309a58b
> [6/6] KVM: arm64: GICv4.1: Give a chance to save VLPI state
> commit: 8082d50f4817ff6a7e08f4b7e9b18e5f8bfa290d
>
> Cheers,
>
> M.
>