Skip to content

Commit 8dbc411

Browse files
committed
Merge branch kvm-arm64/lpi-xarray into kvmarm/next
* kvm-arm64/lpi-xarray: : xarray-based representation of vgic LPIs : : KVM's linked-list of LPI state has proven to be a bottleneck in LPI : injection paths, due to lock serialization when acquiring / releasing a : reference on an IRQ. : : Start the tedious process of reworking KVM's LPI injection by replacing : the LPI linked-list with an xarray, leveraging this to allow RCU readers : to walk it outside of the spinlock. KVM: arm64: vgic: Don't acquire the lpi_list_lock in vgic_put_irq() KVM: arm64: vgic: Ensure the irq refcount is nonzero when taking a ref KVM: arm64: vgic: Rely on RCU protection in vgic_get_lpi() KVM: arm64: vgic: Free LPI vgic_irq structs in an RCU-safe manner KVM: arm64: vgic: Use atomics to count LPIs KVM: arm64: vgic: Get rid of the LPI linked-list KVM: arm64: vgic-its: Walk the LPI xarray in vgic_copy_lpi_list() KVM: arm64: vgic-v3: Iterate the xarray to find pending LPIs KVM: arm64: vgic: Use xarray to find LPI in vgic_get_lpi() KVM: arm64: vgic: Store LPIs in an xarray Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
2 parents 0d87485 + e27f2d5 commit 8dbc411

File tree

7 files changed

+79
-69
lines changed

7 files changed

+79
-69
lines changed

arch/arm64/kvm/vgic/vgic-debug.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ static void print_dist_state(struct seq_file *s, struct vgic_dist *dist)
149149
seq_printf(s, "vgic_model:\t%s\n", v3 ? "GICv3" : "GICv2");
150150
seq_printf(s, "nr_spis:\t%d\n", dist->nr_spis);
151151
if (v3)
152-
seq_printf(s, "nr_lpis:\t%d\n", dist->lpi_list_count);
152+
seq_printf(s, "nr_lpis:\t%d\n", atomic_read(&dist->lpi_count));
153153
seq_printf(s, "enabled:\t%d\n", dist->enabled);
154154
seq_printf(s, "\n");
155155

arch/arm64/kvm/vgic/vgic-init.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ void kvm_vgic_early_init(struct kvm *kvm)
5353
{
5454
struct vgic_dist *dist = &kvm->arch.vgic;
5555

56-
INIT_LIST_HEAD(&dist->lpi_list_head);
5756
INIT_LIST_HEAD(&dist->lpi_translation_cache);
5857
raw_spin_lock_init(&dist->lpi_list_lock);
58+
xa_init_flags(&dist->lpi_xa, XA_FLAGS_LOCK_IRQ);
5959
}
6060

6161
/* CREATION */
@@ -366,6 +366,8 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
366366

367367
if (vgic_supports_direct_msis(kvm))
368368
vgic_v4_teardown(kvm);
369+
370+
xa_destroy(&dist->lpi_xa);
369371
}
370372

371373
static void __kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)

arch/arm64/kvm/vgic/vgic-its.c

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
5252
if (!irq)
5353
return ERR_PTR(-ENOMEM);
5454

55-
INIT_LIST_HEAD(&irq->lpi_list);
55+
ret = xa_reserve_irq(&dist->lpi_xa, intid, GFP_KERNEL_ACCOUNT);
56+
if (ret) {
57+
kfree(irq);
58+
return ERR_PTR(ret);
59+
}
60+
5661
INIT_LIST_HEAD(&irq->ap_list);
5762
raw_spin_lock_init(&irq->irq_lock);
5863

@@ -68,30 +73,30 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
6873
* There could be a race with another vgic_add_lpi(), so we need to
6974
* check that we don't add a second list entry with the same LPI.
7075
*/
71-
list_for_each_entry(oldirq, &dist->lpi_list_head, lpi_list) {
72-
if (oldirq->intid != intid)
73-
continue;
74-
76+
oldirq = xa_load(&dist->lpi_xa, intid);
77+
if (vgic_try_get_irq_kref(oldirq)) {
7578
/* Someone was faster with adding this LPI, lets use that. */
7679
kfree(irq);
7780
irq = oldirq;
7881

79-
/*
80-
* This increases the refcount, the caller is expected to
81-
* call vgic_put_irq() on the returned pointer once it's
82-
* finished with the IRQ.
83-
*/
84-
vgic_get_irq_kref(irq);
82+
goto out_unlock;
83+
}
8584

85+
ret = xa_err(xa_store(&dist->lpi_xa, intid, irq, 0));
86+
if (ret) {
87+
xa_release(&dist->lpi_xa, intid);
88+
kfree(irq);
8689
goto out_unlock;
8790
}
8891

89-
list_add_tail(&irq->lpi_list, &dist->lpi_list_head);
90-
dist->lpi_list_count++;
92+
atomic_inc(&dist->lpi_count);
9193

9294
out_unlock:
9395
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
9496

97+
if (ret)
98+
return ERR_PTR(ret);
99+
95100
/*
96101
* We "cache" the configuration table entries in our struct vgic_irq's.
97102
* However we only have those structs for mapped IRQs, so we read in
@@ -311,6 +316,8 @@ static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
311316
return 0;
312317
}
313318

319+
#define GIC_LPI_MAX_INTID ((1 << INTERRUPT_ID_BITS_ITS) - 1)
320+
314321
/*
315322
* Create a snapshot of the current LPIs targeting @vcpu, so that we can
316323
* enumerate those LPIs without holding any lock.
@@ -319,6 +326,7 @@ static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
319326
int vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr)
320327
{
321328
struct vgic_dist *dist = &kvm->arch.vgic;
329+
XA_STATE(xas, &dist->lpi_xa, GIC_LPI_OFFSET);
322330
struct vgic_irq *irq;
323331
unsigned long flags;
324332
u32 *intids;
@@ -331,20 +339,24 @@ int vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr)
331339
* command). If coming from another path (such as enabling LPIs),
332340
* we must be careful not to overrun the array.
333341
*/
334-
irq_count = READ_ONCE(dist->lpi_list_count);
342+
irq_count = atomic_read(&dist->lpi_count);
335343
intids = kmalloc_array(irq_count, sizeof(intids[0]), GFP_KERNEL_ACCOUNT);
336344
if (!intids)
337345
return -ENOMEM;
338346

339347
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
340-
list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
348+
rcu_read_lock();
349+
350+
xas_for_each(&xas, irq, GIC_LPI_MAX_INTID) {
341351
if (i == irq_count)
342352
break;
343353
/* We don't need to "get" the IRQ, as we hold the list lock. */
344354
if (vcpu && irq->target_vcpu != vcpu)
345355
continue;
346356
intids[i++] = irq->intid;
347357
}
358+
359+
rcu_read_unlock();
348360
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
349361

350362
*intid_ptr = intids;
@@ -592,8 +604,8 @@ static struct vgic_irq *vgic_its_check_cache(struct kvm *kvm, phys_addr_t db,
592604
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
593605

594606
irq = __vgic_its_check_cache(dist, db, devid, eventid);
595-
if (irq)
596-
vgic_get_irq_kref(irq);
607+
if (!vgic_try_get_irq_kref(irq))
608+
irq = NULL;
597609

598610
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
599611

@@ -637,8 +649,13 @@ static void vgic_its_cache_translation(struct kvm *kvm, struct vgic_its *its,
637649
* was in the cache, and increment it on the new interrupt.
638650
*/
639651
if (cte->irq)
640-
__vgic_put_lpi_locked(kvm, cte->irq);
652+
vgic_put_irq(kvm, cte->irq);
641653

654+
/*
655+
* The irq refcount is guaranteed to be nonzero while holding the
656+
* its_lock, as the ITE (and the reference it holds) cannot be freed.
657+
*/
658+
lockdep_assert_held(&its->its_lock);
642659
vgic_get_irq_kref(irq);
643660

644661
cte->db = db;
@@ -669,7 +686,7 @@ void vgic_its_invalidate_cache(struct kvm *kvm)
669686
if (!cte->irq)
670687
break;
671688

672-
__vgic_put_lpi_locked(kvm, cte->irq);
689+
vgic_put_irq(kvm, cte->irq);
673690
cte->irq = NULL;
674691
}
675692

arch/arm64/kvm/vgic/vgic-v3.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
380380
struct vgic_irq *irq;
381381
gpa_t last_ptr = ~(gpa_t)0;
382382
bool vlpi_avail = false;
383+
unsigned long index;
383384
int ret = 0;
384385
u8 val;
385386

@@ -396,7 +397,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
396397
vlpi_avail = true;
397398
}
398399

399-
list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
400+
xa_for_each(&dist->lpi_xa, index, irq) {
400401
int byte_offset, bit_nr;
401402
struct kvm_vcpu *vcpu;
402403
gpa_t pendbase, ptr;

arch/arm64/kvm/vgic/vgic.c

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ struct vgic_global kvm_vgic_global_state __ro_after_init = {
3030
* its->its_lock (mutex)
3131
* vgic_cpu->ap_list_lock must be taken with IRQs disabled
3232
* kvm->lpi_list_lock must be taken with IRQs disabled
33-
* vgic_irq->irq_lock must be taken with IRQs disabled
33+
* vgic_dist->lpi_xa.xa_lock must be taken with IRQs disabled
34+
* vgic_irq->irq_lock must be taken with IRQs disabled
3435
*
3536
* As the ap_list_lock might be taken from the timer interrupt handler,
3637
* we have to disable IRQs before taking this lock and everything lower
@@ -54,32 +55,22 @@ struct vgic_global kvm_vgic_global_state __ro_after_init = {
5455
*/
5556

5657
/*
57-
* Iterate over the VM's list of mapped LPIs to find the one with a
58-
* matching interrupt ID and return a reference to the IRQ structure.
58+
* Index the VM's xarray of mapped LPIs and return a reference to the IRQ
59+
* structure. The caller is expected to call vgic_put_irq() later once it's
60+
* finished with the IRQ.
5961
*/
6062
static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid)
6163
{
6264
struct vgic_dist *dist = &kvm->arch.vgic;
6365
struct vgic_irq *irq = NULL;
64-
unsigned long flags;
65-
66-
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
6766

68-
list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
69-
if (irq->intid != intid)
70-
continue;
67+
rcu_read_lock();
7168

72-
/*
73-
* This increases the refcount, the caller is expected to
74-
* call vgic_put_irq() later once it's finished with the IRQ.
75-
*/
76-
vgic_get_irq_kref(irq);
77-
goto out_unlock;
78-
}
79-
irq = NULL;
69+
irq = xa_load(&dist->lpi_xa, intid);
70+
if (!vgic_try_get_irq_kref(irq))
71+
irq = NULL;
8072

81-
out_unlock:
82-
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
73+
rcu_read_unlock();
8374

8475
return irq;
8576
}
@@ -120,22 +111,6 @@ static void vgic_irq_release(struct kref *ref)
120111
{
121112
}
122113

123-
/*
124-
* Drop the refcount on the LPI. Must be called with lpi_list_lock held.
125-
*/
126-
void __vgic_put_lpi_locked(struct kvm *kvm, struct vgic_irq *irq)
127-
{
128-
struct vgic_dist *dist = &kvm->arch.vgic;
129-
130-
if (!kref_put(&irq->refcount, vgic_irq_release))
131-
return;
132-
133-
list_del(&irq->lpi_list);
134-
dist->lpi_list_count--;
135-
136-
kfree(irq);
137-
}
138-
139114
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
140115
{
141116
struct vgic_dist *dist = &kvm->arch.vgic;
@@ -144,9 +119,15 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
144119
if (irq->intid < VGIC_MIN_LPI)
145120
return;
146121

147-
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
148-
__vgic_put_lpi_locked(kvm, irq);
149-
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
122+
if (!kref_put(&irq->refcount, vgic_irq_release))
123+
return;
124+
125+
xa_lock_irqsave(&dist->lpi_xa, flags);
126+
__xa_erase(&dist->lpi_xa, irq->intid);
127+
xa_unlock_irqrestore(&dist->lpi_xa, flags);
128+
129+
atomic_dec(&dist->lpi_count);
130+
kfree_rcu(irq, rcu);
150131
}
151132

152133
void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu)
@@ -404,7 +385,8 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq,
404385

405386
/*
406387
* Grab a reference to the irq to reflect the fact that it is
407-
* now in the ap_list.
388+
* now in the ap_list. This is safe as the caller must already hold a
389+
* reference on the irq.
408390
*/
409391
vgic_get_irq_kref(irq);
410392
list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);

arch/arm64/kvm/vgic/vgic.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ vgic_get_mmio_region(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev,
180180
gpa_t addr, int len);
181181
struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
182182
u32 intid);
183-
void __vgic_put_lpi_locked(struct kvm *kvm, struct vgic_irq *irq);
184183
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
185184
bool vgic_get_phys_line_level(struct vgic_irq *irq);
186185
void vgic_irq_set_phys_pending(struct vgic_irq *irq, bool pending);
@@ -220,12 +219,20 @@ void vgic_v2_vmcr_sync(struct kvm_vcpu *vcpu);
220219
void vgic_v2_save_state(struct kvm_vcpu *vcpu);
221220
void vgic_v2_restore_state(struct kvm_vcpu *vcpu);
222221

223-
static inline void vgic_get_irq_kref(struct vgic_irq *irq)
222+
static inline bool vgic_try_get_irq_kref(struct vgic_irq *irq)
224223
{
224+
if (!irq)
225+
return false;
226+
225227
if (irq->intid < VGIC_MIN_LPI)
226-
return;
228+
return true;
227229

228-
kref_get(&irq->refcount);
230+
return kref_get_unless_zero(&irq->refcount);
231+
}
232+
233+
static inline void vgic_get_irq_kref(struct vgic_irq *irq)
234+
{
235+
WARN_ON_ONCE(!vgic_try_get_irq_kref(irq));
229236
}
230237

231238
void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu);

include/kvm/arm_vgic.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/spinlock.h>
1414
#include <linux/static_key.h>
1515
#include <linux/types.h>
16+
#include <linux/xarray.h>
1617
#include <kvm/iodev.h>
1718
#include <linux/list.h>
1819
#include <linux/jump_label.h>
@@ -116,7 +117,7 @@ struct irq_ops {
116117

117118
struct vgic_irq {
118119
raw_spinlock_t irq_lock; /* Protects the content of the struct */
119-
struct list_head lpi_list; /* Used to link all LPIs together */
120+
struct rcu_head rcu;
120121
struct list_head ap_list;
121122

122123
struct kvm_vcpu *vcpu; /* SGIs and PPIs: The VCPU
@@ -273,10 +274,10 @@ struct vgic_dist {
273274
*/
274275
u64 propbaser;
275276

276-
/* Protects the lpi_list and the count value below. */
277+
/* Protects the lpi_list. */
277278
raw_spinlock_t lpi_list_lock;
278-
struct list_head lpi_list_head;
279-
int lpi_list_count;
279+
struct xarray lpi_xa;
280+
atomic_t lpi_count;
280281

281282
/* LPI translation cache */
282283
struct list_head lpi_translation_cache;

0 commit comments

Comments
 (0)