irqchip/gic-v4: Don't allow a VMOVP on a dying VPE
Subsystems (2)
Marc Zyngier
maintainerPATCH 1/1 irqchip/gic-v4: Don't allow a VMOVP on a dying VPE
Kunkun Jiang reports that there is a small window of opportunity for userspace to force a change of affinity for a VPE while the VPE has already been unmapped, but the corresponding doorbell interrupt still visible in /proc/irq/.
Plug the race by checking the value of vmapp_count, which tracks whether the VPE is mapped ot not, and returning an error in this case.
This involves making vmapp_count common to both GICv4.1 and its v4.0 ancestor.
--- drivers/irqchip/irq-gic-v3-its.c | 18 ++++++++++++------ include/linux/irqchip/arm-gic-v4.h | 4 +++- 2 files changed, 15 insertions(+), 7 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +12 -6
@@ -797,8 +797,8 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_valid (cmd, desc->its_vmapp_cmd.valid);if (!desc->its_vmapp_cmd.valid) { + alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);if (is_v4_1 (its)) { - alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);its_encode_alloc (cmd, alloc);/* * Unmapping a VPE is self-synchronizing on GICv4.1,
@@ -817,13 +817,13 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_vpt_addr (cmd, vpt_addr);its_encode_vpt_size (cmd,LPI_NRBITS -1 ); + alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); +if (!is_v4_1 (its)) goto out; vconf_addr =virt_to_phys (page_address (desc->its_vmapp_cmd.vpe->its_vm->vprop_page)); - alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); -its_encode_alloc (cmd, alloc);/*
@@ -3806,6 +3806,13 @@ static int its_vpe_set_affinity(struct irq_data *d,struct cpumask *table_mask;unsigned long flags; +/* +* Check if we're racing against a VPE being destroyed, for +* which we don't want to allow a VMOVP. +*/ +if (!atomic_read (&vpe->vmapp_count)) +return -EINVAL ; +/* * Changing affinity is mega expensive, so let's be as lazy as * we can and only do it if we really have to. Also, if mapped
@@ -4463,9 +4470,8 @@ static int its_vpe_init(struct its_vpe *vpe)raw_spin_lock_init (&vpe->vpe_lock); vpe->vpe_id = vpe_id; vpe->vpt_page = vpt_page; -if (gic_rdists->has_rvpeid) -atomic_set (&vpe->vmapp_count,0 ); -else +atomic_set (&vpe->vmapp_count,0 ); +if (!gic_rdists->has_rvpeid) vpe->vpe_proxy_event =-1 ;return 0 ;
include/linux/irqchip/arm-gic-v4.h +3 -1
@@ -66,10 +66,12 @@ struct its_vpe {bool enabled;bool group; } sgi_config[16 ]; -atomic_t vmapp_count; }; }; +/* Track the VPE being mapped */ +atomic_t vmapp_count; +/* * Ensures mutual exclusion between affinity setting of the * vPE and vLPI operations using vpe->col_idx.
Signature · 1 line
2.39.2
Quoted 8 lines · Marc Zyngier
Kunkun Jiang reports that there is a small window of opportunity for userspace to force a change of affinity for a VPE while the VPE has already been unmapped, but the corresponding doorbell interrupt still visible in
/proc/irq/.Plug the race by checking the value of
vmapp_count, which tracks whether the VPE is mapped ot not, and returning an error in this case.
Quoted 6 lines
This involves making
vmapp_countcommon to both GICv4.1 and its v4.0 ancestor.Reported-by Kunkun Jiang <[email protected]>Signed-off-by Marc Zyngier <[email protected]>
I assume this wants a Fixes: tag and a cc: stable, no?
Thanks,
tglx
Marc Zyngier
maintainerQuoted 10 lines · Thomas Gleixner
Quoted 8 lines · Marc Zyngier
Kunkun Jiang reports that there is a small window of opportunity for userspace to force a change of affinity for a VPE while the VPE has already been unmapped, but the corresponding doorbell interrupt still visible in
/proc/irq/.Plug the race by checking the value of
vmapp_count, which tracks whether the VPE is mapped ot not, and returning an error in this case.
Quoted 8 lines
Quoted 6 lines
This involves making
vmapp_countcommon to both GICv4.1 and its v4.0 ancestor.Reported-by Kunkun Jiang <[email protected]>Signed-off-by Marc Zyngier <[email protected]>I assume this wants a Fixes: tag and a cc: stable, no?
Unclear.
While this is clearly a bug, the architectural effects are not fatal, and nothing goes really wrong. However, some implementations are reporting this as a RAS error. That's a bit silly, because this isn't indicative of HW rotting away, and only a sure way to shoot yourself in the foot. That's the real bug IMO.
So if these people are really hung up on having this addressed in prehistoric kernels, we can always add:
which points to the commit that implements the infrastructure we're relying on. GICv4.0, which predates the above by at least a couple of years is also affected, but nobody really cares about that.
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Hi Marc,
Quoted 50 lines · Marc Zyngier
Kunkun Jiang reports that there is a small window of opportunity for userspace to force a change of affinity for a VPE while the VPE has already been unmapped, but the corresponding doorbell interrupt still visible in
/proc/irq/.Plug the race by checking the value of
vmapp_count, which tracks whether the VPE is mapped ot not, and returning an error in this case.This involves making
vmapp_countcommon to both GICv4.1 and its v4.0 ancestor.Reported-by Kunkun Jiang <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- drivers/irqchip/irq-gic-v3-its.c | 18 ++++++++++++------ include/linux/irqchip/arm-gic-v4.h | 4 +++- 2 files changed, 15 insertions(+), 7 deletions(-)drivers/irqchip/irq-gic-v3-its.c +3
-3@@ -797,8 +797,8 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_valid (cmd, desc->its_vmapp_cmd.valid);if (!desc->its_vmapp_cmd.valid) { + alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);if (is_v4_1 (its)) { - alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);its_encode_alloc (cmd, alloc);/* * Unmapping a VPE is self-synchronizing on GICv4.1, @@ -817,13 +817,13 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_vpt_addr (cmd, vpt_addr);its_encode_vpt_size (cmd,LPI_NRBITS -1 ); + alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); +if (!is_v4_1 (its)) goto out; vconf_addr =virt_to_phys (page_address (desc->its_vmapp_cmd.vpe->its_vm->vprop_page)); - alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); -its_encode_alloc (cmd, alloc);/*
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3814
Quoted 9 lines
drivers/irqchip/irq-gic-v3-its.c +6
-0@@ -3806,6 +3806,13 @@ static int its_vpe_set_affinity(struct irq_data *d,struct cpumask *table_mask;unsigned long flags; +/* +* Check if we're racing against a VPE being destroyed, for +* which we don't want to allow a VMOVP. +*/ +if (!atomic_read (&vpe->vmapp_count)) +return -EINVAL ;
We lazily map the vPE so that vmapp_count is likely to be 0 on GICv4.0 implementations with the ITSList feature. Seems that that implementation is not affected by the reported race and we don't need to check vmapp_count for that.
Testing rc4 on my 920 server triggers the WARN_ON() in vgic_v3_load().
void vgic_v3_load(struct kvm_vcpu *vcpu)
{
WARN_ON(vgic_v4_load(vcpu));Thanks,
Zenghui
Marc Zyngier
maintainerHi Zenghui,
Quoted 57 lines · Zenghui Yu
Hi Marc,
Quoted 53 lines · Marc Zyngier
Kunkun Jiang reports that there is a small window of opportunity for userspace to force a change of affinity for a VPE while the VPE has already been unmapped, but the corresponding doorbell interrupt still visible in
/proc/irq/.Plug the race by checking the value of
vmapp_count, which tracks whether the VPE is mapped ot not, and returning an error in this case.This involves making
vmapp_countcommon to both GICv4.1 and its v4.0 ancestor.Reported-by Kunkun Jiang <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- drivers/irqchip/irq-gic-v3-its.c | 18 ++++++++++++------ include/linux/irqchip/arm-gic-v4.h | 4 +++- 2 files changed, 15 insertions(+), 7 deletions(-)drivers/irqchip/irq-gic-v3-its.c +3
-3@@ -797,8 +797,8 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_valid (cmd, desc->its_vmapp_cmd.valid);if (!desc->its_vmapp_cmd.valid) { + alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);if (is_v4_1 (its)) { - alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);its_encode_alloc (cmd, alloc);/* * Unmapping a VPE is self-synchronizing on GICv4.1, @@ -817,13 +817,13 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_vpt_addr (cmd, vpt_addr);its_encode_vpt_size (cmd,LPI_NRBITS -1 ); + alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); +if (!is_v4_1 (its)) goto out; vconf_addr =virt_to_phys (page_address (desc->its_vmapp_cmd.vpe->its_vm->vprop_page)); - alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); -its_encode_alloc (cmd, alloc);/* @@ -3806,6 +3806,13 @@ static int its_vpe_set_affinity(struct irq_data *d,struct cpumask *table_mask;unsigned long flags;
Quoted 11 lines
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3814
Quoted 6 lines
drivers/irqchip/irq-gic-v3-its.c +6
-0+/* +* Check if we're racing against a VPE being destroyed, for +* which we don't want to allow a VMOVP. +*/ +if (!atomic_read (&vpe->vmapp_count)) +return -EINVAL ;We lazily map the vPE so that
vmapp_countis likely to be 0 on GICv4.0 implementations with the ITSList feature. Seems that that implementation is not affected by the reported race and we don't need to checkvmapp_countfor that.
Indeed, the ITSList guards the sending of VMOVP in that case, and we avoid the original issue in that case. However, this still translates in the doorbell being moved for no reason (see its_vpe_db_proxy_move).
How about something like this?
Thanks,
M.
drivers/irqchip/irq-gic-v3-its.c +11 -2
@@ -3810,8 +3810,17 @@ static int its_vpe_set_affinity(struct irq_data *d,* Check if we're racing against a VPE being destroyed, for * which we don't want to allow a VMOVP. */ -if (!atomic_read (&vpe->vmapp_count)) -return -EINVAL ; +if (!atomic_read (&vpe->vmapp_count)) { +if (gic_requires_eager_mapping ()) +return -EINVAL ; + +/* +* If we lazily map the VPEs, this isn't an error, and +* we exit cleanly. +*/ +irq_data_update_effective_affinity (d,cpumask_of (cpu)); +return IRQ_SET_MASK_OK_DONE ; + }/* * Changing affinity is mega expensive, so let's be as lazy as
Signature · 1 line
Without deviation from the norm, progress is not possible.
Quoted 73 lines · Marc Zyngier
Hi Zenghui,
Quoted 57 lines · Zenghui Yu
Hi Marc,
Quoted 53 lines · Marc Zyngier
Kunkun Jiang reports that there is a small window of opportunity for userspace to force a change of affinity for a VPE while the VPE has already been unmapped, but the corresponding doorbell interrupt still visible in
/proc/irq/.Plug the race by checking the value of
vmapp_count, which tracks whether the VPE is mapped ot not, and returning an error in this case.This involves making
vmapp_countcommon to both GICv4.1 and its v4.0 ancestor.Reported-by Kunkun Jiang <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- drivers/irqchip/irq-gic-v3-its.c | 18 ++++++++++++------ include/linux/irqchip/arm-gic-v4.h | 4 +++- 2 files changed, 15 insertions(+), 7 deletions(-)drivers/irqchip/irq-gic-v3-its.c +3
-3@@ -797,8 +797,8 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_valid (cmd, desc->its_vmapp_cmd.valid);if (!desc->its_vmapp_cmd.valid) { + alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);if (is_v4_1 (its)) { - alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);its_encode_alloc (cmd, alloc);/* * Unmapping a VPE is self-synchronizing on GICv4.1, @@ -817,13 +817,13 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_vpt_addr (cmd, vpt_addr);its_encode_vpt_size (cmd,LPI_NRBITS -1 ); + alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); +if (!is_v4_1 (its)) goto out; vconf_addr =virt_to_phys (page_address (desc->its_vmapp_cmd.vpe->its_vm->vprop_page)); - alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); -its_encode_alloc (cmd, alloc);/* @@ -3806,6 +3806,13 @@ static int its_vpe_set_affinity(struct irq_data *d,struct cpumask *table_mask;unsigned long flags;Quoted 11 lines
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3814
Quoted 6 lines
drivers/irqchip/irq-gic-v3-its.c +6
-0+/* +* Check if we're racing against a VPE being destroyed, for +* which we don't want to allow a VMOVP. +*/ +if (!atomic_read (&vpe->vmapp_count)) +return -EINVAL ;We lazily map the vPE so that
vmapp_countis likely to be 0 on GICv4.0 implementations with the ITSList feature. Seems that that implementation is not affected by the reported race and we don't need to checkvmapp_countfor that.
Quoted 3 lines
Indeed, the ITSList guards the sending of VMOVP in that case, and we avoid the original issue in that case. However, this still translates in the doorbell being moved for no reason (see
its_vpe_db_proxy_move).
Yup.
Quoted 1 line
How about something like this?
I'm pretty sure that the splat will disappear with that.
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3815
Quoted 13 lines
drivers/irqchip/irq-gic-v3-its.c +3
-2@@ -3810,8 +3810,17 @@ static int its_vpe_set_affinity(struct irq_data *d,* Check if we're racing against a VPE being destroyed, for * which we don't want to allow a VMOVP. */ -if (!atomic_read (&vpe->vmapp_count)) -return -EINVAL ; +if (!atomic_read (&vpe->vmapp_count)) { +if (gic_requires_eager_mapping ()) +return -EINVAL ;
Nitpick: why do we treat this as an error?
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3821
Quoted 6 lines
drivers/irqchip/irq-gic-v3-its.c +6
-0+ +/* +* If we lazily map the VPEs, this isn't an error, and +* we exit cleanly. +*/ +irq_data_update_effective_affinity (d,cpumask_of (cpu));
@cpu is uninitialized to a sensible value at this point?
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3826
Quoted 5 lines
drivers/irqchip/irq-gic-v3-its.c +2
-0+return IRQ_SET_MASK_OK_DONE ; + }/* * Changing affinity is mega expensive, so let's be as lazy as
Thanks,
Zenghui
Marc Zyngier
maintainerQuoted 85 lines · Zenghui Yu
Quoted 73 lines · Marc Zyngier
Hi Zenghui,
Quoted 57 lines · Zenghui Yu
Hi Marc,
Quoted 53 lines · Marc Zyngier
Kunkun Jiang reports that there is a small window of opportunity for userspace to force a change of affinity for a VPE while the VPE has already been unmapped, but the corresponding doorbell interrupt still visible in
/proc/irq/.Plug the race by checking the value of
vmapp_count, which tracks whether the VPE is mapped ot not, and returning an error in this case.This involves making
vmapp_countcommon to both GICv4.1 and its v4.0 ancestor.Reported-by Kunkun Jiang <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- drivers/irqchip/irq-gic-v3-its.c | 18 ++++++++++++------ include/linux/irqchip/arm-gic-v4.h | 4 +++- 2 files changed, 15 insertions(+), 7 deletions(-)drivers/irqchip/irq-gic-v3-its.c +3
-3@@ -797,8 +797,8 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_valid (cmd, desc->its_vmapp_cmd.valid);if (!desc->its_vmapp_cmd.valid) { + alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);if (is_v4_1 (its)) { - alloc = !atomic_dec_return (&desc->its_vmapp_cmd.vpe->vmapp_count);its_encode_alloc (cmd, alloc);/* * Unmapping a VPE is self-synchronizing on GICv4.1, @@ -817,13 +817,13 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,its_encode_vpt_addr (cmd, vpt_addr);its_encode_vpt_size (cmd,LPI_NRBITS -1 ); + alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); +if (!is_v4_1 (its)) goto out; vconf_addr =virt_to_phys (page_address (desc->its_vmapp_cmd.vpe->its_vm->vprop_page)); - alloc = !atomic_fetch_inc (&desc->its_vmapp_cmd.vpe->vmapp_count); -its_encode_alloc (cmd, alloc);/* @@ -3806,6 +3806,13 @@ static int its_vpe_set_affinity(struct irq_data *d,struct cpumask *table_mask;unsigned long flags;Quoted 11 lines
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3814
Quoted 6 lines
drivers/irqchip/irq-gic-v3-its.c +6
-0+/* +* Check if we're racing against a VPE being destroyed, for +* which we don't want to allow a VMOVP. +*/ +if (!atomic_read (&vpe->vmapp_count)) +return -EINVAL ;We lazily map the vPE so that
vmapp_countis likely to be 0 on GICv4.0 implementations with the ITSList feature. Seems that that implementation is not affected by the reported race and we don't need to checkvmapp_countfor that.Quoted 3 lines
Indeed, the ITSList guards the sending of VMOVP in that case, and we avoid the original issue in that case. However, this still translates in the doorbell being moved for no reason (see
its_vpe_db_proxy_move).Yup.
Quoted 1 line
How about something like this?
I'm pretty sure that the splat will disappear with that.
Quoted 15 lines
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3815
Quoted 13 lines
drivers/irqchip/irq-gic-v3-its.c +3
-2@@ -3810,8 +3810,17 @@ static int its_vpe_set_affinity(struct irq_data *d,* Check if we're racing against a VPE being destroyed, for * which we don't want to allow a VMOVP. */ -if (!atomic_read (&vpe->vmapp_count)) -return -EINVAL ; +if (!atomic_read (&vpe->vmapp_count)) { +if (gic_requires_eager_mapping ()) +return -EINVAL ;Nitpick: why do we treat this as an error?
Because at this stage we can't update the affinity anymore, and I see it as basic courtesy to let the caller know.
Quoted 9 lines
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3821
Quoted 7 lines
drivers/irqchip/irq-gic-v3-its.c +6
-0+ +/* +* If we lazily map the VPEs, this isn't an error, and +* we exit cleanly. +*/ +irq_data_update_effective_affinity (d,cpumask_of (cpu));@cpu is uninitialized to a sensible value at this point?
Ah! As usual, I wrote this on the train this morning, before having had much coffee, and didn't even compile-test it. Here's an amended patch, similarly untested.
If that works for you, I'll put that in a proper patch for Thomas to merge.
Thanks,
M.
drivers/irqchip/irq-gic-v3-its.c +12 -2
@@ -3810,8 +3810,18 @@ static int its_vpe_set_affinity(struct irq_data *d,* Check if we're racing against a VPE being destroyed, for * which we don't want to allow a VMOVP. */ -if (!atomic_read (&vpe->vmapp_count)) -return -EINVAL ; +if (!atomic_read (&vpe->vmapp_count)) { +if (gic_requires_eager_mapping ()) +return -EINVAL ; + +/* +* If we lazily map the VPEs, this isn't an error and +* we can exit cleanly. +*/ + cpu =cpumask_first (mask_val); +irq_data_update_effective_affinity (d,cpumask_of (cpu)); +return IRQ_SET_MASK_OK_DONE ; + }/* * Changing affinity is mega expensive, so let's be as lazy as
Signature · 1 line
Without deviation from the norm, progress is not possible.
Zenghui Yu
Reviewed-byQuoted 15 lines · Marc Zyngier
drivers/irqchip/irq-gic-v3-its.c +5
-2@@ -3810,8 +3810,18 @@ static int its_vpe_set_affinity(struct irq_data *d,* Check if we're racing against a VPE being destroyed, for * which we don't want to allow a VMOVP. */ -if (!atomic_read (&vpe->vmapp_count)) -return -EINVAL ; +if (!atomic_read (&vpe->vmapp_count)) { +if (gic_requires_eager_mapping ()) +return -EINVAL ; + +/*
drivers/irqchip/irq-gic-v3-its.c static int its_vpe_set_affinity(struct irq_data *d, · line 3827
Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +7
-0+* If we lazily map the VPEs, this isn't an error and +* we can exit cleanly. +*/ + cpu =cpumask_first (mask_val); +irq_data_update_effective_affinity (d,cpumask_of (cpu)); +return IRQ_SET_MASK_OK_DONE ; + }/* * Changing affinity is mega expensive, so let's be as lazy as
Looks good, thanks!