KVM: arm64: vgic: Ensure the irq refcount is nonzero when taking a ref
authorOliver Upton <oliver.upton@linux.dev>
Wed, 21 Feb 2024 05:42:52 +0000 (05:42 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Fri, 23 Feb 2024 21:46:02 +0000 (21:46 +0000)
It will soon be possible for get() and put() calls to happen in
parallel, which means in most cases we must ensure the refcount is
nonzero when taking a new reference. Switch to using
vgic_try_get_irq_kref() where necessary, and document the few conditions
where an IRQ's refcount is guaranteed to be nonzero.

Reviewed-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20240221054253.3848076-10-oliver.upton@linux.dev
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/vgic/vgic-its.c
arch/arm64/kvm/vgic/vgic.c

index 0be3c33..dad6f0e 100644 (file)
@@ -74,18 +74,11 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
         * check that we don't add a second list entry with the same LPI.
         */
        oldirq = xa_load(&dist->lpi_xa, intid);
-       if (oldirq) {
+       if (vgic_try_get_irq_kref(oldirq)) {
                /* Someone was faster with adding this LPI, lets use that. */
                kfree(irq);
                irq = oldirq;
 
-               /*
-                * This increases the refcount, the caller is expected to
-                * call vgic_put_irq() on the returned pointer once it's
-                * finished with the IRQ.
-                */
-               vgic_get_irq_kref(irq);
-
                goto out_unlock;
        }
 
@@ -611,8 +604,8 @@ static struct vgic_irq *vgic_its_check_cache(struct kvm *kvm, phys_addr_t db,
        raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
 
        irq = __vgic_its_check_cache(dist, db, devid, eventid);
-       if (irq)
-               vgic_get_irq_kref(irq);
+       if (!vgic_try_get_irq_kref(irq))
+               irq = NULL;
 
        raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
 
@@ -658,6 +651,11 @@ static void vgic_its_cache_translation(struct kvm *kvm, struct vgic_its *its,
        if (cte->irq)
                __vgic_put_lpi_locked(kvm, cte->irq);
 
+       /*
+        * The irq refcount is guaranteed to be nonzero while holding the
+        * its_lock, as the ITE (and the reference it holds) cannot be freed.
+        */
+       lockdep_assert_held(&its->its_lock);
        vgic_get_irq_kref(irq);
 
        cte->db         = db;
index 76abf3d..df9e1aa 100644 (file)
@@ -395,7 +395,8 @@ retry:
 
        /*
         * Grab a reference to the irq to reflect the fact that it is
-        * now in the ap_list.
+        * now in the ap_list. This is safe as the caller must already hold a
+        * reference on the irq.
         */
        vgic_get_irq_kref(irq);
        list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);