[PATCH] irqchip/gic-v4: Fix vcpus racing for vpe->col_idx in vmapp and vmovp

Nianyao Tang tangnianyao at huawei.com
Mon Jul 1 00:23:05 PDT 2024


its_map_vm may modify vpe->col_idx without holding vpe->vpe_lock.
It would result in a vpe resident on one RD after vmovp to a different RD.
Or, a vpe maybe vmovp to a RD same as it is current mapped in vpe table.

On a 2-ITS, GICv4 enabled system, 32 vcpus deployed on cpu of collection 0
and 1. Two pci devices route VLPIs, using each of the ITS.
VPE ready to reside on RD1 may have such unexpected case because another
vcpu on other cpu is doing vmapp and modify his vpe->col_idx.

Unexpected Case 1:
RD                0                              1
                                           vcpu_load
                                           lock vpe_lock
                                           vpe->col_idx = 1
            its_map_vm
            lock vmovp_lock
                                           waiting vmovp_lock
            vpe->col_idx = 0
            (cpu0 is first online cpu)
            vmapp vpe on col0
            unlock vmovp_lock
                                           lock vmovp_lock
                                           vmovp vpe to col0
                                           unlock vmovp_lock
                                           vpe resident here fail to
                                             receive VLPI!

Unexpected Case 2:
RD                0                              1
            its_map_vm                     vcpu_load
            lock vmovp_lock                lock vpe_lock
            vpe->col_idx = 0
                                           vpe->col_idx = 1
            vmapp vpe on col1              waiting vmovp_lock
            unlock vmovp_lock
                                           lock vmovp_lock
                                           vmovp vpe to col1
                                           (target RD == source RD!)
                                           unlock vmovp_lock



Signed-off-by: Nianyao Tang <tangnianyao at huawei.com>
---
 drivers/irqchip/irq-gic-v3-its.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index f99c0a86320b..adda9824e0e7 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -1794,11 +1794,15 @@ static bool gic_requires_eager_mapping(void)
 static void its_map_vm(struct its_node *its, struct its_vm *vm)
 {
 	unsigned long flags;
+	bool vm_mapped_on_any_its = false;
+	int i;
 
 	if (gic_requires_eager_mapping())
 		return;
 
-	raw_spin_lock_irqsave(&vmovp_lock, flags);
+	for (i = 0; i < GICv4_ITS_LIST_MAX; i++)
+		if (vm->vlpi_count[i] > 0)
+			vm_mapped_on_any_its = true;
 
 	/*
 	 * If the VM wasn't mapped yet, iterate over the vpes and get
@@ -1813,15 +1817,19 @@ static void its_map_vm(struct its_node *its, struct its_vm *vm)
 			struct its_vpe *vpe = vm->vpes[i];
 			struct irq_data *d = irq_get_irq_data(vpe->irq);
 
-			/* Map the VPE to the first possible CPU */
-			vpe->col_idx = cpumask_first(cpu_online_mask);
+			raw_spin_lock_irqsave(&vpe->vpe_lock, flags);
+
+			if (!vm_mapped_on_any_its) {
+				/* Map the VPE to the first possible CPU */
+				vpe->col_idx = cpumask_first(cpu_online_mask);
+			}
 			its_send_vmapp(its, vpe, true);
 			its_send_vinvall(its, vpe);
 			irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx));
+
+			raw_spin_unlock_irqrestore(&vpe->vpe_lock, flags);
 		}
 	}
-
-	raw_spin_unlock_irqrestore(&vmovp_lock, flags);
 }
 
 static void its_unmap_vm(struct its_node *its, struct its_vm *vm)
-- 
2.30.0




More information about the linux-arm-kernel mailing list