Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

KVM: arm64: Reimplement vgic-debug XArray iteration

The vgic-debug interface implementation uses XArray marks
(`LPI_XA_MARK_DEBUG_ITER`) to "snapshot" LPIs at the start of iteration.
This modifies global state for a read-only operation and complicates
reference counting, leading to leaks if iteration is aborted or fails.

Reimplement the iterator to use dynamic iteration logic:

- Remove `lpi_idx` from `struct vgic_state_iter`.
- Replace the XArray marking mechanism with dynamic iteration using
`xa_find_after(..., XA_PRESENT)`.
- Wrap XArray traversals in `rcu_read_lock()`/`rcu_read_unlock()` to
ensure safety against concurrent modifications (e.g., LPI unmapping).
- Handle potential races where an LPI is removed during iteration by
gracefully skipping it in `show()`, rather than warning.
- Remove the unused `LPI_XA_MARK_DEBUG_ITER` definition.

This simplifies the lifecycle management of the iterator and prevents
resource leaks associated with the marking mechanism, and paves the way
for using a standard seq_file iterator.

Signed-off-by: Fuad Tabba <tabba@google.com>
Link: https://patch.msgid.link/20260202085721.3954942-3-tabba@google.com
Signed-off-by: Marc Zyngier <maz@kernel.org>

authored by

Fuad Tabba and committed by
Marc Zyngier
5ab24969 dcd79ed4

+20 -49
+20 -48
arch/arm64/kvm/vgic/vgic-debug.c
··· 25 25 struct vgic_state_iter { 26 26 int nr_cpus; 27 27 int nr_spis; 28 - int nr_lpis; 29 28 int dist_id; 30 29 int vcpu_id; 31 30 unsigned long intid; 32 - int lpi_idx; 33 31 }; 34 32 35 33 static void iter_next(struct kvm *kvm, struct vgic_state_iter *iter) ··· 43 45 * Let the xarray drive the iterator after the last SPI, as the iterator 44 46 * has exhausted the sequentially-allocated INTID space. 45 47 */ 46 - if (iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS - 1) && 47 - iter->nr_lpis) { 48 - if (iter->lpi_idx < iter->nr_lpis) 49 - xa_find_after(&dist->lpi_xa, &iter->intid, 50 - VGIC_LPI_MAX_INTID, 51 - LPI_XA_MARK_DEBUG_ITER); 52 - iter->lpi_idx++; 48 + if (iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS - 1)) { 49 + if (iter->intid == VGIC_LPI_MAX_INTID + 1) 50 + return; 51 + 52 + rcu_read_lock(); 53 + if (!xa_find_after(&dist->lpi_xa, &iter->intid, 54 + VGIC_LPI_MAX_INTID, XA_PRESENT)) 55 + iter->intid = VGIC_LPI_MAX_INTID + 1; 56 + rcu_read_unlock(); 53 57 return; 54 58 } 55 59 ··· 61 61 iter->intid = 0; 62 62 } 63 63 64 - static int iter_mark_lpis(struct kvm *kvm) 64 + static int vgic_count_lpis(struct kvm *kvm) 65 65 { 66 66 struct vgic_dist *dist = &kvm->arch.vgic; 67 - unsigned long intid, flags; 68 67 struct vgic_irq *irq; 68 + unsigned long intid; 69 69 int nr_lpis = 0; 70 70 71 - xa_lock_irqsave(&dist->lpi_xa, flags); 72 - 73 - xa_for_each(&dist->lpi_xa, intid, irq) { 74 - if (!vgic_try_get_irq_ref(irq)) 75 - continue; 76 - 77 - __xa_set_mark(&dist->lpi_xa, intid, LPI_XA_MARK_DEBUG_ITER); 71 + rcu_read_lock(); 72 + xa_for_each(&dist->lpi_xa, intid, irq) 78 73 nr_lpis++; 79 - } 80 - 81 - xa_unlock_irqrestore(&dist->lpi_xa, flags); 74 + rcu_read_unlock(); 82 75 83 76 return nr_lpis; 84 - } 85 - 86 - static void iter_unmark_lpis(struct kvm *kvm) 87 - { 88 - struct vgic_dist *dist = &kvm->arch.vgic; 89 - unsigned long intid, flags; 90 - struct vgic_irq *irq; 91 - 92 - xa_for_each_marked(&dist->lpi_xa, intid, irq, LPI_XA_MARK_DEBUG_ITER) { 93 - xa_lock_irqsave(&dist->lpi_xa, flags); 94 - __xa_clear_mark(&dist->lpi_xa, intid, LPI_XA_MARK_DEBUG_ITER); 95 - xa_unlock_irqrestore(&dist->lpi_xa, flags); 96 - 97 - /* vgic_put_irq() expects to be called outside of the xa_lock */ 98 - vgic_put_irq(kvm, irq); 99 - } 100 77 } 101 78 102 79 static void iter_init(struct kvm *kvm, struct vgic_state_iter *iter, ··· 85 108 86 109 iter->nr_cpus = nr_cpus; 87 110 iter->nr_spis = kvm->arch.vgic.nr_spis; 88 - if (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) 89 - iter->nr_lpis = iter_mark_lpis(kvm); 90 111 91 112 /* Fast forward to the right position if needed */ 92 113 while (pos--) ··· 96 121 return iter->dist_id > 0 && 97 122 iter->vcpu_id == iter->nr_cpus && 98 123 iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS) && 99 - (!iter->nr_lpis || iter->lpi_idx > iter->nr_lpis); 124 + iter->intid > VGIC_LPI_MAX_INTID; 100 125 } 101 126 102 127 static void *vgic_debug_start(struct seq_file *s, loff_t *pos) ··· 153 178 154 179 mutex_lock(&kvm->arch.config_lock); 155 180 iter = kvm->arch.vgic.iter; 156 - iter_unmark_lpis(kvm); 157 181 kfree(iter); 158 182 kvm->arch.vgic.iter = NULL; 159 183 mutex_unlock(&kvm->arch.config_lock); ··· 162 188 struct vgic_state_iter *iter) 163 189 { 164 190 bool v3 = dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3; 191 + struct kvm *kvm = s->private; 165 192 166 193 seq_printf(s, "Distributor\n"); 167 194 seq_printf(s, "===========\n"); 168 195 seq_printf(s, "vgic_model:\t%s\n", v3 ? "GICv3" : "GICv2"); 169 196 seq_printf(s, "nr_spis:\t%d\n", dist->nr_spis); 170 197 if (v3) 171 - seq_printf(s, "nr_lpis:\t%d\n", iter->nr_lpis); 198 + seq_printf(s, "nr_lpis:\t%d\n", vgic_count_lpis(kvm)); 172 199 seq_printf(s, "enabled:\t%d\n", dist->enabled); 173 200 seq_printf(s, "\n"); 174 201 ··· 266 291 if (iter->vcpu_id < iter->nr_cpus) 267 292 vcpu = kvm_get_vcpu(kvm, iter->vcpu_id); 268 293 269 - /* 270 - * Expect this to succeed, as iter_mark_lpis() takes a reference on 271 - * every LPI to be visited. 272 - */ 273 294 if (iter->intid < VGIC_NR_PRIVATE_IRQS) 274 295 irq = vgic_get_vcpu_irq(vcpu, iter->intid); 275 296 else 276 297 irq = vgic_get_irq(kvm, iter->intid); 277 - if (WARN_ON_ONCE(!irq)) 278 - return -EINVAL; 298 + 299 + if (!irq) 300 + return 0; 279 301 280 302 raw_spin_lock_irqsave(&irq->irq_lock, flags); 281 303 print_irq_state(s, irq, vcpu);
-1
include/kvm/arm_vgic.h
··· 300 300 */ 301 301 u64 propbaser; 302 302 303 - #define LPI_XA_MARK_DEBUG_ITER XA_MARK_0 304 303 struct xarray lpi_xa; 305 304 306 305 /* used by vgic-debug */