Skip to contentSkip to conversation

arm/arm64: KVM: dynamic VGIC sizing

PATCH In review Marc Zyngier 3 participants 17 messages Sep 11, 2014 – Sep 26, 2014

Subsystems (5)
Documentation Arm (32-bit) KVM arm64 +1 more subsystems
lore
Share & tools
lore
#0 lore raw

v4 cover arm/arm64: KVM: dynamic VGIC sizing

So far, the VGIC data structures have been statically sized, meaning that we always have to support more interrupts than we actually want, and more CPU interfaces than we should. This is a waste of resource, and is the kind of things that should be tuneable.

This series addresses that issue by changing the data structures to be dynamically allocated, and adds a new configuration attribute to allocate the number of interrupts. When the attribute is not used, we fallback to the old behaviour of allocating a fixed number of interrupts.

This series is also the base for Andre Przywara's GICv3 distributor emulation code (which can support far more than 8 vcpus and 1020 interrupts).

This has been tested on both ARM (TC2, A20) and arm64 (model and Juno).

The code is available from my kvm-arm64/kvmtool-vgic-dyn branch, together with the corresponding kvmtool code.

* From v3 [3]
- Number of comments added to the data structures, making slightly more obvious the various mappings
- Dropped the nr_irqs field from bitmap and bytemap structures, as it was a leftover from the initial revision that only had a single pointer
- Small cleanups all over the place
- Dropped the "sub-page offset" patch for now, as this need some serious reworking
- Rebased on top of Christoffer "vgic cleanup" series, with 3.17-rc4 thrown in for a good measure

* From v2 [2]
- Fixed bug that broke QEMU (register access can trigger allocation)
- irq_pending_on_cpu is now dynamic (needed for more than 32 or 64 vcpus)
- Rebased on top of Victor's BE patches

* From v1 [1]
- Rebased on top of 3.16-rc1
- Lots of cleanup

[1]: https://lists.cs.columbia.edu/pipermail/kvmarm/2013-October/005879.html
[2]: https://lists.cs.columbia.edu/pipermail/kvmarm/2014-June/010050.html
[3]: https://lists.cs.columbia.edu/pipermail/kvmarm/2014-July/010383.html

Marc Zyngier (8):
KVM: ARM: vgic: plug irq injection race
arm/arm64: KVM: vgic: switch to dynamic allocation arm/arm64: KVM: vgic: Parametrize VGIC_NR_SHARED_IRQS arm/arm64: KVM: vgic: kill VGIC_MAX_CPUS
arm/arm64: KVM: vgic: handle out-of-range MMIO accesses arm/arm64: KVM: vgic: kill VGIC_NR_IRQS
arm/arm64: KVM: vgic: delay vgic allocation until init time arm/arm64: KVM: vgic: make number of irqs a configurable attribute

 Documentation/virtual/kvm/devices/arm-vgic.txt |  10 +
 arch/arm/include/uapi/asm/kvm.h                |   1 +
 arch/arm/kvm/arm.c                             |  10 +-
 arch/arm64/include/uapi/asm/kvm.h              |   1 +
 include/kvm/arm_vgic.h                         |  88 ++++--
 virt/kvm/arm/vgic.c                            | 396 +++++++++++++++++++++----
 6 files changed, 413 insertions(+), 93 deletions(-)
Signature · 1 line
2.0.4
#1 lore raw

PATCH v4 1/8 KVM: ARM: vgic: plug irq injection race

As it stands, nothing prevents userspace from injecting an interrupt before the guest's GIC is actually initialized.

This goes unnoticed so far (as everything is pretty much statically allocated), but ends up exploding in a spectacular way once we switch to a more dynamic allocation (the GIC data structure isn't there yet).

The fix is to test for the "ready" flag in the VGIC distributor before trying to inject the interrupt. Note that in order to avoid breaking userspace, we have to ignore what is essentially an error.

---
 virt/kvm/arm/vgic.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
virt/kvm/arm/vgic.c +2 -1
index f7ab1ca..d3299d4 100644
@@ -1584,7 +1584,8 @@ out:
 int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
 			bool level)
 {
-	if (vgic_update_irq_pending(kvm, cpuid, irq_num, level))
+	if (likely(vgic_initialized(kvm)) &&
+	    vgic_update_irq_pending(kvm, cpuid, irq_num, level))
 		vgic_kick_vcpus(kvm);
 
 	return 0;
Signature · 1 line
2.0.4
#2 lore raw

PATCH v4 2/8 arm/arm64: KVM: vgic: switch to dynamic allocation

In reply to Marc Zyngier

So far, all the VGIC data structures are statically defined by the *maximum* number of vcpus and interrupts it supports. It means that we always have to oversize it to cater for the worse case.

Start by changing the data structures to be dynamically sizeable, and allocate them at runtime.

The sizes are still very static though.

---
 arch/arm/kvm/arm.c     |   3 +
 include/kvm/arm_vgic.h |  76 ++++++++++++----
 virt/kvm/arm/vgic.c    | 237 ++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 267 insertions(+), 49 deletions(-)
arch/arm/kvm/arm.c +3 -0
index a99e0cd..923a01d 100644
@@ -172,6 +172,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 			kvm->vcpus[i] = NULL;
 		}
 	}
+
+	kvm_vgic_destroy(kvm);
 }
 
 int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
@@ -253,6 +255,7 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
 {
 	kvm_mmu_free_memory_caches(vcpu);
 	kvm_timer_vcpu_terminate(vcpu);
+	kvm_vgic_vcpu_destroy(vcpu);
 	kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
include/kvm/arm_vgic.h +58 -18

116 lines not on this page · raw message

virt/kvm/arm/vgic.c +206 -31

383 lines not on this page · raw message

Signature · 1 line
2.0.4
#3 lore raw

Re: [PATCH v4 2/8] arm/arm64: KVM: vgic: switch to dynamic allocation

Quoted 55 lines · Marc Zyngier

So far, all the VGIC data structures are statically defined by the *maximum* number of vcpus and interrupts it supports. It means that we always have to oversize it to cater for the worse case.

Start by changing the data structures to be dynamically sizeable, and allocate them at runtime.

The sizes are still very static though.

---
 arch/arm/kvm/arm.c     |   3 +
 include/kvm/arm_vgic.h |  76 ++++++++++++----
 virt/kvm/arm/vgic.c    | 237 ++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 267 insertions(+), 49 deletions(-)
arch/arm/kvm/arm.c +3 -0
index a99e0cd..923a01d 100644
@@ -172,6 +172,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 			kvm->vcpus[i] = NULL;
 		}
 	}
+
+	kvm_vgic_destroy(kvm);
 }
 
 int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
@@ -253,6 +255,7 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
 {
 	kvm_mmu_free_memory_caches(vcpu);
 	kvm_timer_vcpu_terminate(vcpu);
+	kvm_vgic_vcpu_destroy(vcpu);
 	kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
include/kvm/arm_vgic.h +2 -8
index f074539..bdaac57 100644
@@ -54,19 +54,33 @@
  * - a bunch of shared interrupts (SPI)
  */
 struct vgic_bitmap {
-	union {
-		u32 reg[VGIC_NR_PRIVATE_IRQS / 32];
-		DECLARE_BITMAP(reg_ul, VGIC_NR_PRIVATE_IRQS);
-	} percpu[VGIC_MAX_CPUS];
-	union {
-		u32 reg[VGIC_NR_SHARED_IRQS / 32];
-		DECLARE_BITMAP(reg_ul, VGIC_NR_SHARED_IRQS);
-	} shared;
+	/*
+	 * - One UL per VCPU for private interrupts (assumes UL is at

include/kvm/arm_vgic.h line 68

Quoted 10 lines
include/kvm/arm_vgic.h +10 -0
+	 *   least 32 bits)
+	 * - As many UL as necessary for shared interrupts.
+	 *
+	 * The private interrupts are accessed via the "private"
+	 * field, one UL per vcpu (the state for vcpu n is in
+	 * private[n]). The shared interrupts are accessed via the
+	 * "shared" pointer (IRQn state is at bit n-32 in the bitmap).
+	 */
+	unsigned long *private;
+	unsigned long *shared;

the comment above the define for REG_OFFSET_SWIZZLE still talks about the unions in struct vgic_bitmap, which is no longer true. Mind updating that comment?

Quoted 53 lines
include/kvm/arm_vgic.h +28 -7
 };
 
 struct vgic_bytemap {
-	u32 percpu[VGIC_MAX_CPUS][VGIC_NR_PRIVATE_IRQS / 4];
-	u32 shared[VGIC_NR_SHARED_IRQS  / 4];
+	/*
+	 * - 8 u32 per VCPU for private interrupts
+	 * - As many u32 as necessary for shared interrupts.
+	 *
+	 * The private interrupts are accessed via the "private"
+	 * field, (the state for vcpu n is in private[n*8] to
+	 * private[n*8 + 7]). The shared interrupts are accessed via
+	 * the "shared" pointer (IRQn state is at byte (n-32)%4 of the
+	 * shared[(n-32)/4] word).
+	 */
+	u32 *private;
+	u32 *shared;
 };
 
 struct kvm_vcpu;
@@ -127,6 +141,9 @@ struct vgic_dist {
 	bool			in_kernel;
 	bool			ready;
 
+	int			nr_cpus;
+	int			nr_irqs;
+
 	/* Virtual control interface mapping */
 	void __iomem		*vctrl_base;
 
@@ -166,15 +183,36 @@ struct vgic_dist {
 	/* Level/edge triggered */
 	struct vgic_bitmap	irq_cfg;
 
-	/* Source CPU per SGI and target CPU */
-	u8			irq_sgi_sources[VGIC_MAX_CPUS][VGIC_NR_SGIS];
+	/*
+	 * Source CPU per SGI and target CPU:
+	 *
+	 * Each byte represent a SGI observable on a VCPU, each bit of
+	 * this byte indicating if the corresponding VCPU has
+	 * generated this interrupt. This is a GICv2 feature only.
+	 *
+	 * For VCPUn (n < 8), irq_sgi_sources[n*16] to [n*16 + 15] are
+	 * the SGIs observable on VCPUn.
+	 */
+	u8			*irq_sgi_sources;
 
-	/* Target CPU for each IRQ */
-	u8			irq_spi_cpu[VGIC_NR_SHARED_IRQS];
-	struct vgic_bitmap	irq_spi_target[VGIC_MAX_CPUS];
+	/*
+	 * Target CPU for each SPI:

include/kvm/arm_vgic.h struct vgic_dist { · line 209

Quoted 10 lines
include/kvm/arm_vgic.h +10 -0
+	 *
+	 * Array of available SPI, each byte indicating the target
+	 * VCPU for SPI. IRQn (n >=32) is at irq_spi_cpu[n-32].
+	 */
+	u8			*irq_spi_cpu;
+
+	/*
+	 * Reverse lookup of irq_spi_cpu for faster compute pending:
+	 *
+	 * Array of bitmaps, one per VCPU, describing is IRQn is

ah, describing *if* ?

Quoted 306 lines
include/kvm/arm_vgic.h +8 -3
+	 * routed to a particular VCPU.
+	 */
+	struct vgic_bitmap	*irq_spi_target;
 
 	/* Bitmap indicating which CPU has something pending */
-	unsigned long		irq_pending_on_cpu;
+	unsigned long		*irq_pending_on_cpu;
 #endif
 };
 
@@ -204,11 +242,11 @@ struct vgic_v3_cpu_if {
 struct vgic_cpu {
 #ifdef CONFIG_KVM_ARM_VGIC
 	/* per IRQ to LR mapping */
-	u8		vgic_irq_lr_map[VGIC_NR_IRQS];
+	u8		*vgic_irq_lr_map;
 
 	/* Pending interrupts on this VCPU */
 	DECLARE_BITMAP(	pending_percpu, VGIC_NR_PRIVATE_IRQS);
-	DECLARE_BITMAP(	pending_shared, VGIC_NR_SHARED_IRQS);
+	unsigned long	*pending_shared;
 
 	/* Bitmap of used/free list registers */
 	DECLARE_BITMAP(	lr_used, VGIC_V2_MAX_LRS);
@@ -239,7 +277,9 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
 int kvm_vgic_hyp_init(void);
 int kvm_vgic_init(struct kvm *kvm);
 int kvm_vgic_create(struct kvm *kvm);
+void kvm_vgic_destroy(struct kvm *kvm);
 int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
+void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu);
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
 int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
virt/kvm/arm/vgic.c +90 -31
index d3299d4..92c086e 100644
@@ -95,6 +95,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
 static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu);
 static void vgic_update_state(struct kvm *kvm);
 static void vgic_kick_vcpus(struct kvm *kvm);
+static u8 *vgic_get_sgi_sources(struct vgic_dist *dist, int vcpu_id, int sgi);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
 static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr);
 static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc);
@@ -124,23 +125,45 @@ static const struct vgic_params *vgic;
 #define REG_OFFSET_SWIZZLE	0
 #endif
 
+static int vgic_init_bitmap(struct vgic_bitmap *b, int nr_cpus, int nr_irqs)
+{
+	int nr_longs;
+
+	nr_longs = nr_cpus + BITS_TO_LONGS(nr_irqs - VGIC_NR_PRIVATE_IRQS);
+
+	b->private = kzalloc(sizeof(unsigned long) * nr_longs, GFP_KERNEL);
+	if (!b->private)
+		return -ENOMEM;
+
+	b->shared = b->private + nr_cpus;
+
+	return 0;
+}
+
+static void vgic_free_bitmap(struct vgic_bitmap *b)
+{
+	kfree(b->private);
+	b->private = NULL;
+	b->shared = NULL;
+}
+
 static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
 				int cpuid, u32 offset)
 {
 	offset >>= 2;
 	if (!offset)
-		return x->percpu[cpuid].reg + (offset ^ REG_OFFSET_SWIZZLE);
+		return (u32 *)(x->private + cpuid) + REG_OFFSET_SWIZZLE;
 	else
-		return x->shared.reg + ((offset - 1) ^ REG_OFFSET_SWIZZLE);
+		return (u32 *)(x->shared) + ((offset - 1) ^ REG_OFFSET_SWIZZLE);
 }
 
 static int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
 				   int cpuid, int irq)
 {
 	if (irq < VGIC_NR_PRIVATE_IRQS)
-		return test_bit(irq, x->percpu[cpuid].reg_ul);
+		return test_bit(irq, x->private + cpuid);
 
-	return test_bit(irq - VGIC_NR_PRIVATE_IRQS, x->shared.reg_ul);
+	return test_bit(irq - VGIC_NR_PRIVATE_IRQS, x->shared);
 }
 
 static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
@@ -149,9 +172,9 @@ static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
 	unsigned long *reg;
 
 	if (irq < VGIC_NR_PRIVATE_IRQS) {
-		reg = x->percpu[cpuid].reg_ul;
+		reg = x->private + cpuid;
 	} else {
-		reg =  x->shared.reg_ul;
+		reg = x->shared;
 		irq -= VGIC_NR_PRIVATE_IRQS;
 	}
 
@@ -163,24 +186,49 @@ static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
 
 static unsigned long *vgic_bitmap_get_cpu_map(struct vgic_bitmap *x, int cpuid)
 {
-	if (unlikely(cpuid >= VGIC_MAX_CPUS))
-		return NULL;
-	return x->percpu[cpuid].reg_ul;
+	return x->private + cpuid;
 }
 
 static unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x)
 {
-	return x->shared.reg_ul;
+	return x->shared;
+}
+
+static int vgic_init_bytemap(struct vgic_bytemap *x, int nr_cpus, int nr_irqs)
+{
+	int size;
+
+	size  = nr_cpus * VGIC_NR_PRIVATE_IRQS;
+	size += nr_irqs - VGIC_NR_PRIVATE_IRQS;
+
+	x->private = kzalloc(size, GFP_KERNEL);
+	if (!x->private)
+		return -ENOMEM;
+
+	x->shared = x->private + nr_cpus * VGIC_NR_PRIVATE_IRQS / sizeof(u32);
+	return 0;
+}
+
+static void vgic_free_bytemap(struct vgic_bytemap *b)
+{
+	kfree(b->private);
+	b->private = NULL;
+	b->shared = NULL;
 }
 
 static u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x, int cpuid, u32 offset)
 {
-	offset >>= 2;
-	BUG_ON(offset > (VGIC_NR_IRQS / 4));
-	if (offset < 8)
-		return x->percpu[cpuid] + offset;
-	else
-		return x->shared + offset - 8;
+	u32 *reg;
+
+	if (offset < VGIC_NR_PRIVATE_IRQS) {
+		reg = x->private;
+		offset += cpuid * VGIC_NR_PRIVATE_IRQS;
+	} else {
+		reg = x->shared;
+		offset -= VGIC_NR_PRIVATE_IRQS;
+	}
+
+	return reg + (offset / sizeof(u32));
 }
 
 #define VGIC_CFG_LEVEL	0
@@ -739,7 +787,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
 		 */
 		vgic_dist_irq_set_pending(vcpu, lr.irq);
 		if (lr.irq < VGIC_NR_SGIS)
-			dist->irq_sgi_sources[vcpu_id][lr.irq] |= 1 << lr.source;
+			*vgic_get_sgi_sources(dist, vcpu_id, lr.irq) |= 1 << lr.source;
 		lr.state &= ~LR_STATE_PENDING;
 		vgic_set_lr(vcpu, i, lr);
 
@@ -773,7 +821,7 @@ static bool read_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu,
 	/* Copy source SGIs from distributor side */
 	for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
 		int shift = 8 * (sgi - min_sgi);
-		reg |= (u32)dist->irq_sgi_sources[vcpu_id][sgi] << shift;
+		reg |= ((u32)*vgic_get_sgi_sources(dist, vcpu_id, sgi)) << shift;
 	}
 
 	mmio_data_write(mmio, ~0, reg);
@@ -797,14 +845,15 @@ static bool write_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu,
 	/* Clear pending SGIs on the distributor */
 	for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
 		u8 mask = reg >> (8 * (sgi - min_sgi));
+		u8 *src = vgic_get_sgi_sources(dist, vcpu_id, sgi);
 		if (set) {
-			if ((dist->irq_sgi_sources[vcpu_id][sgi] & mask) != mask)
+			if ((*src & mask) != mask)
 				updated = true;
-			dist->irq_sgi_sources[vcpu_id][sgi] |= mask;
+			*src |= mask;
 		} else {
-			if (dist->irq_sgi_sources[vcpu_id][sgi] & mask)
+			if (*src & mask)
 				updated = true;
-			dist->irq_sgi_sources[vcpu_id][sgi] &= ~mask;
+			*src &= ~mask;
 		}
 	}
 
@@ -988,6 +1037,11 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 	return true;
 }
 
+static u8 *vgic_get_sgi_sources(struct vgic_dist *dist, int vcpu_id, int sgi)
+{
+	return dist->irq_sgi_sources + vcpu_id * VGIC_NR_SGIS + sgi;
+}
+
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
 {
 	struct kvm *kvm = vcpu->kvm;
@@ -1021,7 +1075,7 @@ static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
 		if (target_cpus & 1) {
 			/* Flag the SGI as pending */
 			vgic_dist_irq_set_pending(vcpu, sgi);
-			dist->irq_sgi_sources[c][sgi] |= 1 << vcpu_id;
+			*vgic_get_sgi_sources(dist, c, sgi) |= 1 << vcpu_id;
 			kvm_debug("SGI%d from CPU%d to CPU%d\n", sgi, vcpu_id, c);
 		}
 
@@ -1068,14 +1122,14 @@ static void vgic_update_state(struct kvm *kvm)
 	int c;
 
 	if (!dist->enabled) {
-		set_bit(0, &dist->irq_pending_on_cpu);
+		set_bit(0, dist->irq_pending_on_cpu);
 		return;
 	}
 
 	kvm_for_each_vcpu(c, vcpu, kvm) {
 		if (compute_pending_for_cpu(vcpu)) {
 			pr_debug("CPU%d has pending interrupts\n", c);
-			set_bit(c, &dist->irq_pending_on_cpu);
+			set_bit(c, dist->irq_pending_on_cpu);
 		}
 	}
 }
@@ -1232,14 +1286,14 @@ static bool vgic_queue_sgi(struct kvm_vcpu *vcpu, int irq)
 	int vcpu_id = vcpu->vcpu_id;
 	int c;
 
-	sources = dist->irq_sgi_sources[vcpu_id][irq];
+	sources = *vgic_get_sgi_sources(dist, vcpu_id, irq);
 
 	for_each_set_bit(c, &sources, VGIC_MAX_CPUS) {
 		if (vgic_queue_irq(vcpu, c, irq))
 			clear_bit(c, &sources);
 	}
 
-	dist->irq_sgi_sources[vcpu_id][irq] = sources;
+	*vgic_get_sgi_sources(dist, vcpu_id, irq) = sources;
 
 	/*
 	 * If the sources bitmap has been cleared it means that we
@@ -1327,7 +1381,7 @@ epilog:
 		 * us. Claim we don't have anything pending. We'll
 		 * adjust that if needed while exiting.
 		 */
-		clear_bit(vcpu_id, &dist->irq_pending_on_cpu);
+		clear_bit(vcpu_id, dist->irq_pending_on_cpu);
 	}
 }
 
@@ -1429,7 +1483,7 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
 	/* Check if we still have something up our sleeve... */
 	pending = find_first_zero_bit(elrsr_ptr, vgic->nr_lr);
 	if (level_pending || pending < vgic->nr_lr)
-		set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+		set_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
 }
 
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
@@ -1463,7 +1517,7 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 	if (!irqchip_in_kernel(vcpu->kvm))
 		return 0;
 
-	return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+	return test_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
 }
 
 static void vgic_kick_vcpus(struct kvm *kvm)
@@ -1558,7 +1612,7 @@ static bool vgic_update_irq_pending(struct kvm *kvm, int cpuid,
 
 	if (level) {
 		vgic_cpu_irq_set(vcpu, irq_num);
-		set_bit(cpuid, &dist->irq_pending_on_cpu);
+		set_bit(cpuid, dist->irq_pending_on_cpu);
 	}
 
 out:
@@ -1602,6 +1656,32 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+
+	kfree(vgic_cpu->pending_shared);

virt/kvm/arm/vgic.c static irqreturn_t vgic_maintenance_handler(int irq, void *data) · line 1673

Quoted 10 lines
virt/kvm/arm/vgic.c +10 -0
+	kfree(vgic_cpu->vgic_irq_lr_map);
+	vgic_cpu->pending_shared = NULL;
+	vgic_cpu->vgic_irq_lr_map = NULL;
+}
+
+static int vgic_vcpu_init_maps(struct kvm_vcpu *vcpu, int nr_irqs)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+
+	int sz = (nr_irqs - VGIC_NR_PRIVATE_IRQS) / 8;

[copying question from last review round, apologies if I'm being stupid]

are we guaranteed that the numerator is always a multiple of 8? if not, won't you end up allocating one less byte than needed?

virt/kvm/arm/vgic.c static irqreturn_t vgic_maintenance_handler(int irq, void *data) · line 1684

Quoted 11 lines
virt/kvm/arm/vgic.c +11 -0
+	vgic_cpu->pending_shared = kzalloc(sz, GFP_KERNEL);
+	vgic_cpu->vgic_irq_lr_map = kzalloc(nr_irqs, GFP_KERNEL);
+
+	if (!vgic_cpu->pending_shared || !vgic_cpu->vgic_irq_lr_map) {
+		kvm_vgic_vcpu_destroy(vcpu);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+

[...]

Thanks,
-Christoffer

#4 lore raw

Re: [PATCH v4 2/8] arm/arm64: KVM: vgic: switch to dynamic allocation

Quoted 62 lines · Christoffer Dall
Quoted 61 lines · Marc Zyngier

So far, all the VGIC data structures are statically defined by the *maximum* number of vcpus and interrupts it supports. It means that we always have to oversize it to cater for the worse case.

Start by changing the data structures to be dynamically sizeable, and allocate them at runtime.

The sizes are still very static though.

---
 arch/arm/kvm/arm.c     |   3 +
 include/kvm/arm_vgic.h |  76 ++++++++++++----
 virt/kvm/arm/vgic.c    | 237 ++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 267 insertions(+), 49 deletions(-)
arch/arm/kvm/arm.c +3 -0
index a99e0cd..923a01d 100644
@@ -172,6 +172,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
                      kvm->vcpus[i] = NULL;
              }
      }
+
+     kvm_vgic_destroy(kvm);
 }
 
 int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
@@ -253,6 +255,7 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
 {
      kvm_mmu_free_memory_caches(vcpu);
      kvm_timer_vcpu_terminate(vcpu);
+     kvm_vgic_vcpu_destroy(vcpu);
      kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
include/kvm/arm_vgic.h +8 -8
index f074539..bdaac57 100644
@@ -54,19 +54,33 @@
  * - a bunch of shared interrupts (SPI)
  */
 struct vgic_bitmap {
-     union {
-             u32 reg[VGIC_NR_PRIVATE_IRQS / 32];
-             DECLARE_BITMAP(reg_ul, VGIC_NR_PRIVATE_IRQS);
-     } percpu[VGIC_MAX_CPUS];
-     union {
-             u32 reg[VGIC_NR_SHARED_IRQS / 32];
-             DECLARE_BITMAP(reg_ul, VGIC_NR_SHARED_IRQS);
-     } shared;
+     /*
+      * - One UL per VCPU for private interrupts (assumes UL is at
+      *   least 32 bits)
+      * - As many UL as necessary for shared interrupts.
+      *
+      * The private interrupts are accessed via the "private"
+      * field, one UL per vcpu (the state for vcpu n is in
+      * private[n]). The shared interrupts are accessed via the
Quoted 8 lines

include/kvm/arm_vgic.h line 68

Quoted 4 lines
include/kvm/arm_vgic.h +4 -0
+      * "shared" pointer (IRQn state is at bit n-32 in the bitmap).
+      */
+     unsigned long *private;
+     unsigned long *shared;

the comment above the define for REG_OFFSET_SWIZZLE still talks about the unions in struct vgic_bitmap, which is no longer true. Mind updating that comment?

Damned, thought I fixed that. Will update it.

Quoted 57 lines
Quoted 57 lines
include/kvm/arm_vgic.h +32 -7
 };
 
 struct vgic_bytemap {
-     u32 percpu[VGIC_MAX_CPUS][VGIC_NR_PRIVATE_IRQS / 4];
-     u32 shared[VGIC_NR_SHARED_IRQS  / 4];
+     /*
+      * - 8 u32 per VCPU for private interrupts
+      * - As many u32 as necessary for shared interrupts.
+      *
+      * The private interrupts are accessed via the "private"
+      * field, (the state for vcpu n is in private[n*8] to
+      * private[n*8 + 7]). The shared interrupts are accessed via
+      * the "shared" pointer (IRQn state is at byte (n-32)%4 of the
+      * shared[(n-32)/4] word).
+      */
+     u32 *private;
+     u32 *shared;
 };
 
 struct kvm_vcpu;
@@ -127,6 +141,9 @@ struct vgic_dist {
      bool                    in_kernel;
      bool                    ready;
 
+     int                     nr_cpus;
+     int                     nr_irqs;
+
      /* Virtual control interface mapping */
      void __iomem            *vctrl_base;
 
@@ -166,15 +183,36 @@ struct vgic_dist {
      /* Level/edge triggered */
      struct vgic_bitmap      irq_cfg;
 
-     /* Source CPU per SGI and target CPU */
-     u8                      irq_sgi_sources[VGIC_MAX_CPUS][VGIC_NR_SGIS];
+     /*
+      * Source CPU per SGI and target CPU:
+      *
+      * Each byte represent a SGI observable on a VCPU, each bit of
+      * this byte indicating if the corresponding VCPU has
+      * generated this interrupt. This is a GICv2 feature only.
+      *
+      * For VCPUn (n < 8), irq_sgi_sources[n*16] to [n*16 + 15] are
+      * the SGIs observable on VCPUn.
+      */
+     u8                      *irq_sgi_sources;
 
-     /* Target CPU for each IRQ */
-     u8                      irq_spi_cpu[VGIC_NR_SHARED_IRQS];
-     struct vgic_bitmap      irq_spi_target[VGIC_MAX_CPUS];
+     /*
+      * Target CPU for each SPI:
+      *
+      * Array of available SPI, each byte indicating the target
+      * VCPU for SPI. IRQn (n >=32) is at irq_spi_cpu[n-32].
+      */
Quoted 8 lines

include/kvm/arm_vgic.h struct vgic_dist { · line 209

Quoted 6 lines
include/kvm/arm_vgic.h +6 -0
+     u8                      *irq_spi_cpu;
+
+     /*
+      * Reverse lookup of irq_spi_cpu for faster compute pending:
+      *
+      * Array of bitmaps, one per VCPU, describing is IRQn is

ah, describing *if* ?

Indeed. Will fix.

Quoted 319 lines
Quoted 306 lines
include/kvm/arm_vgic.h +8 -3
+      * routed to a particular VCPU.
+      */
+     struct vgic_bitmap      *irq_spi_target;
 
      /* Bitmap indicating which CPU has something pending */
-     unsigned long           irq_pending_on_cpu;
+     unsigned long           *irq_pending_on_cpu;
 #endif
 };
 
@@ -204,11 +242,11 @@ struct vgic_v3_cpu_if {
 struct vgic_cpu {
 #ifdef CONFIG_KVM_ARM_VGIC
      /* per IRQ to LR mapping */
-     u8              vgic_irq_lr_map[VGIC_NR_IRQS];
+     u8              *vgic_irq_lr_map;
 
      /* Pending interrupts on this VCPU */
      DECLARE_BITMAP( pending_percpu, VGIC_NR_PRIVATE_IRQS);
-     DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS);
+     unsigned long   *pending_shared;
 
      /* Bitmap of used/free list registers */
      DECLARE_BITMAP( lr_used, VGIC_V2_MAX_LRS);
@@ -239,7 +277,9 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
 int kvm_vgic_hyp_init(void);
 int kvm_vgic_init(struct kvm *kvm);
 int kvm_vgic_create(struct kvm *kvm);
+void kvm_vgic_destroy(struct kvm *kvm);
 int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
+void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu);
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
 int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
virt/kvm/arm/vgic.c +90 -31
index d3299d4..92c086e 100644
@@ -95,6 +95,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
 static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu);
 static void vgic_update_state(struct kvm *kvm);
 static void vgic_kick_vcpus(struct kvm *kvm);
+static u8 *vgic_get_sgi_sources(struct vgic_dist *dist, int vcpu_id, int sgi);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
 static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr);
 static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc);
@@ -124,23 +125,45 @@ static const struct vgic_params *vgic;
 #define REG_OFFSET_SWIZZLE   0
 #endif
 
+static int vgic_init_bitmap(struct vgic_bitmap *b, int nr_cpus, int nr_irqs)
+{
+     int nr_longs;
+
+     nr_longs = nr_cpus + BITS_TO_LONGS(nr_irqs - VGIC_NR_PRIVATE_IRQS);
+
+     b->private = kzalloc(sizeof(unsigned long) * nr_longs, GFP_KERNEL);
+     if (!b->private)
+             return -ENOMEM;
+
+     b->shared = b->private + nr_cpus;
+
+     return 0;
+}
+
+static void vgic_free_bitmap(struct vgic_bitmap *b)
+{
+     kfree(b->private);
+     b->private = NULL;
+     b->shared = NULL;
+}
+
 static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
                              int cpuid, u32 offset)
 {
      offset >>= 2;
      if (!offset)
-             return x->percpu[cpuid].reg + (offset ^ REG_OFFSET_SWIZZLE);
+             return (u32 *)(x->private + cpuid) + REG_OFFSET_SWIZZLE;
      else
-             return x->shared.reg + ((offset - 1) ^ REG_OFFSET_SWIZZLE);
+             return (u32 *)(x->shared) + ((offset - 1) ^ REG_OFFSET_SWIZZLE);
 }
 
 static int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
                                 int cpuid, int irq)
 {
      if (irq < VGIC_NR_PRIVATE_IRQS)
-             return test_bit(irq, x->percpu[cpuid].reg_ul);
+             return test_bit(irq, x->private + cpuid);
 
-     return test_bit(irq - VGIC_NR_PRIVATE_IRQS, x->shared.reg_ul);
+     return test_bit(irq - VGIC_NR_PRIVATE_IRQS, x->shared);
 }
 
 static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
@@ -149,9 +172,9 @@ static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
      unsigned long *reg;
 
      if (irq < VGIC_NR_PRIVATE_IRQS) {
-             reg = x->percpu[cpuid].reg_ul;
+             reg = x->private + cpuid;
      } else {
-             reg =  x->shared.reg_ul;
+             reg = x->shared;
              irq -= VGIC_NR_PRIVATE_IRQS;
      }
 
@@ -163,24 +186,49 @@ static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
 
 static unsigned long *vgic_bitmap_get_cpu_map(struct vgic_bitmap *x, int cpuid)
 {
-     if (unlikely(cpuid >= VGIC_MAX_CPUS))
-             return NULL;
-     return x->percpu[cpuid].reg_ul;
+     return x->private + cpuid;
 }
 
 static unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x)
 {
-     return x->shared.reg_ul;
+     return x->shared;
+}
+
+static int vgic_init_bytemap(struct vgic_bytemap *x, int nr_cpus, int nr_irqs)
+{
+     int size;
+
+     size  = nr_cpus * VGIC_NR_PRIVATE_IRQS;
+     size += nr_irqs - VGIC_NR_PRIVATE_IRQS;
+
+     x->private = kzalloc(size, GFP_KERNEL);
+     if (!x->private)
+             return -ENOMEM;
+
+     x->shared = x->private + nr_cpus * VGIC_NR_PRIVATE_IRQS / sizeof(u32);
+     return 0;
+}
+
+static void vgic_free_bytemap(struct vgic_bytemap *b)
+{
+     kfree(b->private);
+     b->private = NULL;
+     b->shared = NULL;
 }
 
 static u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x, int cpuid, u32 offset)
 {
-     offset >>= 2;
-     BUG_ON(offset > (VGIC_NR_IRQS / 4));
-     if (offset < 8)
-             return x->percpu[cpuid] + offset;
-     else
-             return x->shared + offset - 8;
+     u32 *reg;
+
+     if (offset < VGIC_NR_PRIVATE_IRQS) {
+             reg = x->private;
+             offset += cpuid * VGIC_NR_PRIVATE_IRQS;
+     } else {
+             reg = x->shared;
+             offset -= VGIC_NR_PRIVATE_IRQS;
+     }
+
+     return reg + (offset / sizeof(u32));
 }
 
 #define VGIC_CFG_LEVEL       0
@@ -739,7 +787,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
               */
              vgic_dist_irq_set_pending(vcpu, lr.irq);
              if (lr.irq < VGIC_NR_SGIS)
-                     dist->irq_sgi_sources[vcpu_id][lr.irq] |= 1 << lr.source;
+                     *vgic_get_sgi_sources(dist, vcpu_id, lr.irq) |= 1 << lr.source;
              lr.state &= ~LR_STATE_PENDING;
              vgic_set_lr(vcpu, i, lr);
 
@@ -773,7 +821,7 @@ static bool read_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu,
      /* Copy source SGIs from distributor side */
      for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
              int shift = 8 * (sgi - min_sgi);
-             reg |= (u32)dist->irq_sgi_sources[vcpu_id][sgi] << shift;
+             reg |= ((u32)*vgic_get_sgi_sources(dist, vcpu_id, sgi)) << shift;
      }
 
      mmio_data_write(mmio, ~0, reg);
@@ -797,14 +845,15 @@ static bool write_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu,
      /* Clear pending SGIs on the distributor */
      for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
              u8 mask = reg >> (8 * (sgi - min_sgi));
+             u8 *src = vgic_get_sgi_sources(dist, vcpu_id, sgi);
              if (set) {
-                     if ((dist->irq_sgi_sources[vcpu_id][sgi] & mask) != mask)
+                     if ((*src & mask) != mask)
                              updated = true;
-                     dist->irq_sgi_sources[vcpu_id][sgi] |= mask;
+                     *src |= mask;
              } else {
-                     if (dist->irq_sgi_sources[vcpu_id][sgi] & mask)
+                     if (*src & mask)
                              updated = true;
-                     dist->irq_sgi_sources[vcpu_id][sgi] &= ~mask;
+                     *src &= ~mask;
              }
      }
 
@@ -988,6 +1037,11 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
      return true;
 }
 
+static u8 *vgic_get_sgi_sources(struct vgic_dist *dist, int vcpu_id, int sgi)
+{
+     return dist->irq_sgi_sources + vcpu_id * VGIC_NR_SGIS + sgi;
+}
+
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
 {
      struct kvm *kvm = vcpu->kvm;
@@ -1021,7 +1075,7 @@ static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
              if (target_cpus & 1) {
                      /* Flag the SGI as pending */
                      vgic_dist_irq_set_pending(vcpu, sgi);
-                     dist->irq_sgi_sources[c][sgi] |= 1 << vcpu_id;
+                     *vgic_get_sgi_sources(dist, c, sgi) |= 1 << vcpu_id;
                      kvm_debug("SGI%d from CPU%d to CPU%d\n", sgi, vcpu_id, c);
              }
 
@@ -1068,14 +1122,14 @@ static void vgic_update_state(struct kvm *kvm)
      int c;
 
      if (!dist->enabled) {
-             set_bit(0, &dist->irq_pending_on_cpu);
+             set_bit(0, dist->irq_pending_on_cpu);
              return;
      }
 
      kvm_for_each_vcpu(c, vcpu, kvm) {
              if (compute_pending_for_cpu(vcpu)) {
                      pr_debug("CPU%d has pending interrupts\n", c);
-                     set_bit(c, &dist->irq_pending_on_cpu);
+                     set_bit(c, dist->irq_pending_on_cpu);
              }
      }
 }
@@ -1232,14 +1286,14 @@ static bool vgic_queue_sgi(struct kvm_vcpu *vcpu, int irq)
      int vcpu_id = vcpu->vcpu_id;
      int c;
 
-     sources = dist->irq_sgi_sources[vcpu_id][irq];
+     sources = *vgic_get_sgi_sources(dist, vcpu_id, irq);
 
      for_each_set_bit(c, &sources, VGIC_MAX_CPUS) {
              if (vgic_queue_irq(vcpu, c, irq))
                      clear_bit(c, &sources);
      }
 
-     dist->irq_sgi_sources[vcpu_id][irq] = sources;
+     *vgic_get_sgi_sources(dist, vcpu_id, irq) = sources;
 
      /*
       * If the sources bitmap has been cleared it means that we
@@ -1327,7 +1381,7 @@ epilog:
               * us. Claim we don't have anything pending. We'll
               * adjust that if needed while exiting.
               */
-             clear_bit(vcpu_id, &dist->irq_pending_on_cpu);
+             clear_bit(vcpu_id, dist->irq_pending_on_cpu);
      }
 }
 
@@ -1429,7 +1483,7 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
      /* Check if we still have something up our sleeve... */
      pending = find_first_zero_bit(elrsr_ptr, vgic->nr_lr);
      if (level_pending || pending < vgic->nr_lr)
-             set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+             set_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
 }
 
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
@@ -1463,7 +1517,7 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
      if (!irqchip_in_kernel(vcpu->kvm))
              return 0;
 
-     return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+     return test_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
 }
 
 static void vgic_kick_vcpus(struct kvm *kvm)
@@ -1558,7 +1612,7 @@ static bool vgic_update_irq_pending(struct kvm *kvm, int cpuid,
 
      if (level) {
              vgic_cpu_irq_set(vcpu, irq_num);
-             set_bit(cpuid, &dist->irq_pending_on_cpu);
+             set_bit(cpuid, dist->irq_pending_on_cpu);
      }
 
 out:
@@ -1602,6 +1656,32 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
      return IRQ_HANDLED;
 }
 
+void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
+{
+     struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+
+     kfree(vgic_cpu->pending_shared);

virt/kvm/arm/vgic.c static irqreturn_t vgic_maintenance_handler(int irq, void *data) · line 1673

Quoted 10 lines
virt/kvm/arm/vgic.c +10 -0
+     kfree(vgic_cpu->vgic_irq_lr_map);
+     vgic_cpu->pending_shared = NULL;
+     vgic_cpu->vgic_irq_lr_map = NULL;
+}
+
+static int vgic_vcpu_init_maps(struct kvm_vcpu *vcpu, int nr_irqs)
+{
+     struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+
+     int sz = (nr_irqs - VGIC_NR_PRIVATE_IRQS) / 8;

[copying question from last review round, apologies if I'm being stupid]

Quoted 2 lines

are we guaranteed that the numerator is always a multiple of 8? if not, won't you end up allocating one less byte than needed?

We force the allocation to be a multiple of 32, as per the GIC spec (we enforce this in arm_vgic.h for the legacy behaviour, and in the last patch for the dynamic case).

Quoted 13 lines
Quoted 12 lines
virt/kvm/arm/vgic.c +11 -0
+     vgic_cpu->pending_shared = kzalloc(sz, GFP_KERNEL);
+     vgic_cpu->vgic_irq_lr_map = kzalloc(nr_irqs, GFP_KERNEL);
+
+     if (!vgic_cpu->pending_shared || !vgic_cpu->vgic_irq_lr_map) {
+             kvm_vgic_vcpu_destroy(vcpu);
+             return -ENOMEM;
+     }
+
+     return 0;
+}
+

[...]

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
Reviewed-by
#5 lore raw

Re: [PATCH v4 2/8] arm/arm64: KVM: vgic: switch to dynamic allocation

Quoted 461 lines · Marc Zyngier
Quoted 62 lines · Christoffer Dall
Quoted 61 lines · Marc Zyngier

So far, all the VGIC data structures are statically defined by the *maximum* number of vcpus and interrupts it supports. It means that we always have to oversize it to cater for the worse case.

Start by changing the data structures to be dynamically sizeable, and allocate them at runtime.

The sizes are still very static though.

---
 arch/arm/kvm/arm.c     |   3 +
 include/kvm/arm_vgic.h |  76 ++++++++++++----
 virt/kvm/arm/vgic.c    | 237 ++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 267 insertions(+), 49 deletions(-)
arch/arm/kvm/arm.c +3 -0
index a99e0cd..923a01d 100644
@@ -172,6 +172,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
                      kvm->vcpus[i] = NULL;
              }
      }
+
+     kvm_vgic_destroy(kvm);
 }
 
 int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
@@ -253,6 +255,7 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
 {
      kvm_mmu_free_memory_caches(vcpu);
      kvm_timer_vcpu_terminate(vcpu);
+     kvm_vgic_vcpu_destroy(vcpu);
      kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
include/kvm/arm_vgic.h +8 -8
index f074539..bdaac57 100644
@@ -54,19 +54,33 @@
  * - a bunch of shared interrupts (SPI)
  */
 struct vgic_bitmap {
-     union {
-             u32 reg[VGIC_NR_PRIVATE_IRQS / 32];
-             DECLARE_BITMAP(reg_ul, VGIC_NR_PRIVATE_IRQS);
-     } percpu[VGIC_MAX_CPUS];
-     union {
-             u32 reg[VGIC_NR_SHARED_IRQS / 32];
-             DECLARE_BITMAP(reg_ul, VGIC_NR_SHARED_IRQS);
-     } shared;
+     /*
+      * - One UL per VCPU for private interrupts (assumes UL is at
+      *   least 32 bits)
+      * - As many UL as necessary for shared interrupts.
+      *
+      * The private interrupts are accessed via the "private"
+      * field, one UL per vcpu (the state for vcpu n is in
+      * private[n]). The shared interrupts are accessed via the
Quoted 8 lines

include/kvm/arm_vgic.h line 68

Quoted 4 lines
include/kvm/arm_vgic.h +4 -0
+      * "shared" pointer (IRQn state is at bit n-32 in the bitmap).
+      */
+     unsigned long *private;
+     unsigned long *shared;

the comment above the define for REG_OFFSET_SWIZZLE still talks about the unions in struct vgic_bitmap, which is no longer true. Mind updating that comment?

Damned, thought I fixed that. Will update it.

Quoted 57 lines
Quoted 57 lines
include/kvm/arm_vgic.h +32 -7
 };
 
 struct vgic_bytemap {
-     u32 percpu[VGIC_MAX_CPUS][VGIC_NR_PRIVATE_IRQS / 4];
-     u32 shared[VGIC_NR_SHARED_IRQS  / 4];
+     /*
+      * - 8 u32 per VCPU for private interrupts
+      * - As many u32 as necessary for shared interrupts.
+      *
+      * The private interrupts are accessed via the "private"
+      * field, (the state for vcpu n is in private[n*8] to
+      * private[n*8 + 7]). The shared interrupts are accessed via
+      * the "shared" pointer (IRQn state is at byte (n-32)%4 of the
+      * shared[(n-32)/4] word).
+      */
+     u32 *private;
+     u32 *shared;
 };
 
 struct kvm_vcpu;
@@ -127,6 +141,9 @@ struct vgic_dist {
      bool                    in_kernel;
      bool                    ready;
 
+     int                     nr_cpus;
+     int                     nr_irqs;
+
      /* Virtual control interface mapping */
      void __iomem            *vctrl_base;
 
@@ -166,15 +183,36 @@ struct vgic_dist {
      /* Level/edge triggered */
      struct vgic_bitmap      irq_cfg;
 
-     /* Source CPU per SGI and target CPU */
-     u8                      irq_sgi_sources[VGIC_MAX_CPUS][VGIC_NR_SGIS];
+     /*
+      * Source CPU per SGI and target CPU:
+      *
+      * Each byte represent a SGI observable on a VCPU, each bit of
+      * this byte indicating if the corresponding VCPU has
+      * generated this interrupt. This is a GICv2 feature only.
+      *
+      * For VCPUn (n < 8), irq_sgi_sources[n*16] to [n*16 + 15] are
+      * the SGIs observable on VCPUn.
+      */
+     u8                      *irq_sgi_sources;
 
-     /* Target CPU for each IRQ */
-     u8                      irq_spi_cpu[VGIC_NR_SHARED_IRQS];
-     struct vgic_bitmap      irq_spi_target[VGIC_MAX_CPUS];
+     /*
+      * Target CPU for each SPI:
+      *
+      * Array of available SPI, each byte indicating the target
+      * VCPU for SPI. IRQn (n >=32) is at irq_spi_cpu[n-32].
+      */
Quoted 8 lines

include/kvm/arm_vgic.h struct vgic_dist { · line 209

Quoted 6 lines
include/kvm/arm_vgic.h +6 -0
+     u8                      *irq_spi_cpu;
+
+     /*
+      * Reverse lookup of irq_spi_cpu for faster compute pending:
+      *
+      * Array of bitmaps, one per VCPU, describing is IRQn is

ah, describing *if* ?

Indeed. Will fix.

Quoted 319 lines
Quoted 306 lines
include/kvm/arm_vgic.h +8 -3
+      * routed to a particular VCPU.
+      */
+     struct vgic_bitmap      *irq_spi_target;
 
      /* Bitmap indicating which CPU has something pending */
-     unsigned long           irq_pending_on_cpu;
+     unsigned long           *irq_pending_on_cpu;
 #endif
 };
 
@@ -204,11 +242,11 @@ struct vgic_v3_cpu_if {
 struct vgic_cpu {
 #ifdef CONFIG_KVM_ARM_VGIC
      /* per IRQ to LR mapping */
-     u8              vgic_irq_lr_map[VGIC_NR_IRQS];
+     u8              *vgic_irq_lr_map;
 
      /* Pending interrupts on this VCPU */
      DECLARE_BITMAP( pending_percpu, VGIC_NR_PRIVATE_IRQS);
-     DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS);
+     unsigned long   *pending_shared;
 
      /* Bitmap of used/free list registers */
      DECLARE_BITMAP( lr_used, VGIC_V2_MAX_LRS);
@@ -239,7 +277,9 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
 int kvm_vgic_hyp_init(void);
 int kvm_vgic_init(struct kvm *kvm);
 int kvm_vgic_create(struct kvm *kvm);
+void kvm_vgic_destroy(struct kvm *kvm);
 int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
+void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu);
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
 int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
virt/kvm/arm/vgic.c +90 -31
index d3299d4..92c086e 100644
@@ -95,6 +95,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
 static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu);
 static void vgic_update_state(struct kvm *kvm);
 static void vgic_kick_vcpus(struct kvm *kvm);
+static u8 *vgic_get_sgi_sources(struct vgic_dist *dist, int vcpu_id, int sgi);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
 static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr);
 static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc);
@@ -124,23 +125,45 @@ static const struct vgic_params *vgic;
 #define REG_OFFSET_SWIZZLE   0
 #endif
 
+static int vgic_init_bitmap(struct vgic_bitmap *b, int nr_cpus, int nr_irqs)
+{
+     int nr_longs;
+
+     nr_longs = nr_cpus + BITS_TO_LONGS(nr_irqs - VGIC_NR_PRIVATE_IRQS);
+
+     b->private = kzalloc(sizeof(unsigned long) * nr_longs, GFP_KERNEL);
+     if (!b->private)
+             return -ENOMEM;
+
+     b->shared = b->private + nr_cpus;
+
+     return 0;
+}
+
+static void vgic_free_bitmap(struct vgic_bitmap *b)
+{
+     kfree(b->private);
+     b->private = NULL;
+     b->shared = NULL;
+}
+
 static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
                              int cpuid, u32 offset)
 {
      offset >>= 2;
      if (!offset)
-             return x->percpu[cpuid].reg + (offset ^ REG_OFFSET_SWIZZLE);
+             return (u32 *)(x->private + cpuid) + REG_OFFSET_SWIZZLE;
      else
-             return x->shared.reg + ((offset - 1) ^ REG_OFFSET_SWIZZLE);
+             return (u32 *)(x->shared) + ((offset - 1) ^ REG_OFFSET_SWIZZLE);
 }
 
 static int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
                                 int cpuid, int irq)
 {
      if (irq < VGIC_NR_PRIVATE_IRQS)
-             return test_bit(irq, x->percpu[cpuid].reg_ul);
+             return test_bit(irq, x->private + cpuid);
 
-     return test_bit(irq - VGIC_NR_PRIVATE_IRQS, x->shared.reg_ul);
+     return test_bit(irq - VGIC_NR_PRIVATE_IRQS, x->shared);
 }
 
 static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
@@ -149,9 +172,9 @@ static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
      unsigned long *reg;
 
      if (irq < VGIC_NR_PRIVATE_IRQS) {
-             reg = x->percpu[cpuid].reg_ul;
+             reg = x->private + cpuid;
      } else {
-             reg =  x->shared.reg_ul;
+             reg = x->shared;
              irq -= VGIC_NR_PRIVATE_IRQS;
      }
 
@@ -163,24 +186,49 @@ static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
 
 static unsigned long *vgic_bitmap_get_cpu_map(struct vgic_bitmap *x, int cpuid)
 {
-     if (unlikely(cpuid >= VGIC_MAX_CPUS))
-             return NULL;
-     return x->percpu[cpuid].reg_ul;
+     return x->private + cpuid;
 }
 
 static unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x)
 {
-     return x->shared.reg_ul;
+     return x->shared;
+}
+
+static int vgic_init_bytemap(struct vgic_bytemap *x, int nr_cpus, int nr_irqs)
+{
+     int size;
+
+     size  = nr_cpus * VGIC_NR_PRIVATE_IRQS;
+     size += nr_irqs - VGIC_NR_PRIVATE_IRQS;
+
+     x->private = kzalloc(size, GFP_KERNEL);
+     if (!x->private)
+             return -ENOMEM;
+
+     x->shared = x->private + nr_cpus * VGIC_NR_PRIVATE_IRQS / sizeof(u32);
+     return 0;
+}
+
+static void vgic_free_bytemap(struct vgic_bytemap *b)
+{
+     kfree(b->private);
+     b->private = NULL;
+     b->shared = NULL;
 }
 
 static u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x, int cpuid, u32 offset)
 {
-     offset >>= 2;
-     BUG_ON(offset > (VGIC_NR_IRQS / 4));
-     if (offset < 8)
-             return x->percpu[cpuid] + offset;
-     else
-             return x->shared + offset - 8;
+     u32 *reg;
+
+     if (offset < VGIC_NR_PRIVATE_IRQS) {
+             reg = x->private;
+             offset += cpuid * VGIC_NR_PRIVATE_IRQS;
+     } else {
+             reg = x->shared;
+             offset -= VGIC_NR_PRIVATE_IRQS;
+     }
+
+     return reg + (offset / sizeof(u32));
 }
 
 #define VGIC_CFG_LEVEL       0
@@ -739,7 +787,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
               */
              vgic_dist_irq_set_pending(vcpu, lr.irq);
              if (lr.irq < VGIC_NR_SGIS)
-                     dist->irq_sgi_sources[vcpu_id][lr.irq] |= 1 << lr.source;
+                     *vgic_get_sgi_sources(dist, vcpu_id, lr.irq) |= 1 << lr.source;
              lr.state &= ~LR_STATE_PENDING;
              vgic_set_lr(vcpu, i, lr);
 
@@ -773,7 +821,7 @@ static bool read_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu,
      /* Copy source SGIs from distributor side */
      for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
              int shift = 8 * (sgi - min_sgi);
-             reg |= (u32)dist->irq_sgi_sources[vcpu_id][sgi] << shift;
+             reg |= ((u32)*vgic_get_sgi_sources(dist, vcpu_id, sgi)) << shift;
      }
 
      mmio_data_write(mmio, ~0, reg);
@@ -797,14 +845,15 @@ static bool write_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu,
      /* Clear pending SGIs on the distributor */
      for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
              u8 mask = reg >> (8 * (sgi - min_sgi));
+             u8 *src = vgic_get_sgi_sources(dist, vcpu_id, sgi);
              if (set) {
-                     if ((dist->irq_sgi_sources[vcpu_id][sgi] & mask) != mask)
+                     if ((*src & mask) != mask)
                              updated = true;
-                     dist->irq_sgi_sources[vcpu_id][sgi] |= mask;
+                     *src |= mask;
              } else {
-                     if (dist->irq_sgi_sources[vcpu_id][sgi] & mask)
+                     if (*src & mask)
                              updated = true;
-                     dist->irq_sgi_sources[vcpu_id][sgi] &= ~mask;
+                     *src &= ~mask;
              }
      }
 
@@ -988,6 +1037,11 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
      return true;
 }
 
+static u8 *vgic_get_sgi_sources(struct vgic_dist *dist, int vcpu_id, int sgi)
+{
+     return dist->irq_sgi_sources + vcpu_id * VGIC_NR_SGIS + sgi;
+}
+
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
 {
      struct kvm *kvm = vcpu->kvm;
@@ -1021,7 +1075,7 @@ static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
              if (target_cpus & 1) {
                      /* Flag the SGI as pending */
                      vgic_dist_irq_set_pending(vcpu, sgi);
-                     dist->irq_sgi_sources[c][sgi] |= 1 << vcpu_id;
+                     *vgic_get_sgi_sources(dist, c, sgi) |= 1 << vcpu_id;
                      kvm_debug("SGI%d from CPU%d to CPU%d\n", sgi, vcpu_id, c);
              }
 
@@ -1068,14 +1122,14 @@ static void vgic_update_state(struct kvm *kvm)
      int c;
 
      if (!dist->enabled) {
-             set_bit(0, &dist->irq_pending_on_cpu);
+             set_bit(0, dist->irq_pending_on_cpu);
              return;
      }
 
      kvm_for_each_vcpu(c, vcpu, kvm) {
              if (compute_pending_for_cpu(vcpu)) {
                      pr_debug("CPU%d has pending interrupts\n", c);
-                     set_bit(c, &dist->irq_pending_on_cpu);
+                     set_bit(c, dist->irq_pending_on_cpu);
              }
      }
 }
@@ -1232,14 +1286,14 @@ static bool vgic_queue_sgi(struct kvm_vcpu *vcpu, int irq)
      int vcpu_id = vcpu->vcpu_id;
      int c;
 
-     sources = dist->irq_sgi_sources[vcpu_id][irq];
+     sources = *vgic_get_sgi_sources(dist, vcpu_id, irq);
 
      for_each_set_bit(c, &sources, VGIC_MAX_CPUS) {
              if (vgic_queue_irq(vcpu, c, irq))
                      clear_bit(c, &sources);
      }
 
-     dist->irq_sgi_sources[vcpu_id][irq] = sources;
+     *vgic_get_sgi_sources(dist, vcpu_id, irq) = sources;
 
      /*
       * If the sources bitmap has been cleared it means that we
@@ -1327,7 +1381,7 @@ epilog:
               * us. Claim we don't have anything pending. We'll
               * adjust that if needed while exiting.
               */
-             clear_bit(vcpu_id, &dist->irq_pending_on_cpu);
+             clear_bit(vcpu_id, dist->irq_pending_on_cpu);
      }
 }
 
@@ -1429,7 +1483,7 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
      /* Check if we still have something up our sleeve... */
      pending = find_first_zero_bit(elrsr_ptr, vgic->nr_lr);
      if (level_pending || pending < vgic->nr_lr)
-             set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+             set_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
 }
 
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
@@ -1463,7 +1517,7 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
      if (!irqchip_in_kernel(vcpu->kvm))
              return 0;
 
-     return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+     return test_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
 }
 
 static void vgic_kick_vcpus(struct kvm *kvm)
@@ -1558,7 +1612,7 @@ static bool vgic_update_irq_pending(struct kvm *kvm, int cpuid,
 
      if (level) {
              vgic_cpu_irq_set(vcpu, irq_num);
-             set_bit(cpuid, &dist->irq_pending_on_cpu);
+             set_bit(cpuid, dist->irq_pending_on_cpu);
      }
 
 out:
@@ -1602,6 +1656,32 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
      return IRQ_HANDLED;
 }
 
+void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
+{
+     struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+
+     kfree(vgic_cpu->pending_shared);

virt/kvm/arm/vgic.c static irqreturn_t vgic_maintenance_handler(int irq, void *data) · line 1673

Quoted 10 lines
virt/kvm/arm/vgic.c +10 -0
+     kfree(vgic_cpu->vgic_irq_lr_map);
+     vgic_cpu->pending_shared = NULL;
+     vgic_cpu->vgic_irq_lr_map = NULL;
+}
+
+static int vgic_vcpu_init_maps(struct kvm_vcpu *vcpu, int nr_irqs)
+{
+     struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+
+     int sz = (nr_irqs - VGIC_NR_PRIVATE_IRQS) / 8;

[copying question from last review round, apologies if I'm being stupid]

Quoted 7 lines
Quoted 2 lines

are we guaranteed that the numerator is always a multiple of 8? if not, won't you end up allocating one less byte than needed?

We force the allocation to be a multiple of 32, as per the GIC spec (we enforce this in arm_vgic.h for the legacy behaviour, and in the last patch for the dynamic case).

right, in that case, with the tiny doc fixes:

#6 lore raw

PATCH v4 3/8 arm/arm64: KVM: vgic: Parametrize VGIC_NR_SHARED_IRQS

In reply to Marc Zyngier

Having a dynamic number of supported interrupts means that we cannot relly on VGIC_NR_SHARED_IRQS being fixed anymore.

Instead, make it take the distributor structure as a parameter, so it can return the right value.

---
 include/kvm/arm_vgic.h |  1 -
 virt/kvm/arm/vgic.c    | 16 +++++++++++-----
 2 files changed, 11 insertions(+), 6 deletions(-)
include/kvm/arm_vgic.h +0 -1
index bdaac57..bdeb451 100644
@@ -29,7 +29,6 @@
 #define VGIC_NR_SGIS		16
 #define VGIC_NR_PPIS		16
 #define VGIC_NR_PRIVATE_IRQS	(VGIC_NR_SGIS + VGIC_NR_PPIS)
-#define VGIC_NR_SHARED_IRQS	(VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS)
 #define VGIC_MAX_CPUS		KVM_MAX_VCPUS
 
 #define VGIC_V2_MAX_LRS		(1 << 6)
virt/kvm/arm/vgic.c +11 -5
index 92c086e..93fe73b 100644
@@ -1083,11 +1083,17 @@ static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
 	}
 }
 
+static int vgic_nr_shared_irqs(struct vgic_dist *dist)
+{
+	return dist->nr_irqs - VGIC_NR_PRIVATE_IRQS;
+}
+
 static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
 {
 	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
 	unsigned long *pending, *enabled, *pend_percpu, *pend_shared;
 	unsigned long pending_private, pending_shared;
+	int nr_shared = vgic_nr_shared_irqs(dist);
 	int vcpu_id;
 
 	vcpu_id = vcpu->vcpu_id;
@@ -1100,15 +1106,15 @@ static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
 
 	pending = vgic_bitmap_get_shared_map(&dist->irq_pending);
 	enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled);
-	bitmap_and(pend_shared, pending, enabled, VGIC_NR_SHARED_IRQS);
+	bitmap_and(pend_shared, pending, enabled, nr_shared);
 	bitmap_and(pend_shared, pend_shared,
 		   vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]),
-		   VGIC_NR_SHARED_IRQS);
+		   nr_shared);
 
 	pending_private = find_first_bit(pend_percpu, VGIC_NR_PRIVATE_IRQS);
-	pending_shared = find_first_bit(pend_shared, VGIC_NR_SHARED_IRQS);
+	pending_shared = find_first_bit(pend_shared, nr_shared);
 	return (pending_private < VGIC_NR_PRIVATE_IRQS ||
-		pending_shared < VGIC_NR_SHARED_IRQS);
+		pending_shared < vgic_nr_shared_irqs(dist));
 }
 
 /*
@@ -1365,7 +1371,7 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
 	}
 
 	/* SPIs */
-	for_each_set_bit(i, vgic_cpu->pending_shared, VGIC_NR_SHARED_IRQS) {
+	for_each_set_bit(i, vgic_cpu->pending_shared, vgic_nr_shared_irqs(dist)) {
 		if (!vgic_queue_hwirq(vcpu, i + VGIC_NR_PRIVATE_IRQS))
 			overflow = 1;
 	}
Signature · 1 line
2.0.4
#7 lore raw

PATCH v4 4/8 arm/arm64: KVM: vgic: kill VGIC_MAX_CPUS

In reply to Marc Zyngier

We now have the information about the number of CPU interfaces in the distributor itself. Let's get rid of VGIC_MAX_CPUS, and just rely on KVM_MAX_VCPUS where we don't have the choice. Yet.

---
 include/kvm/arm_vgic.h | 3 +--
 virt/kvm/arm/vgic.c    | 6 +++---
 2 files changed, 4 insertions(+), 5 deletions(-)
include/kvm/arm_vgic.h +1 -2
index bdeb451..3900e31 100644
@@ -29,13 +29,12 @@
 #define VGIC_NR_SGIS		16
 #define VGIC_NR_PPIS		16
 #define VGIC_NR_PRIVATE_IRQS	(VGIC_NR_SGIS + VGIC_NR_PPIS)
-#define VGIC_MAX_CPUS		KVM_MAX_VCPUS
 
 #define VGIC_V2_MAX_LRS		(1 << 6)
 #define VGIC_V3_MAX_LRS		16
 
 /* Sanity checks... */
-#if (VGIC_MAX_CPUS > 8)
+#if (KVM_MAX_VCPUS > 8)
 #error	Invalid number of CPU interfaces
 #endif
 
virt/kvm/arm/vgic.c +3 -3
index 93fe73b..7e6e64d 100644
@@ -1294,7 +1294,7 @@ static bool vgic_queue_sgi(struct kvm_vcpu *vcpu, int irq)
 
 	sources = *vgic_get_sgi_sources(dist, vcpu_id, irq);
 
-	for_each_set_bit(c, &sources, VGIC_MAX_CPUS) {
+	for_each_set_bit(c, &sources, dist->nr_cpus) {
 		if (vgic_queue_irq(vcpu, c, irq))
 			clear_bit(c, &sources);
 	}
@@ -1701,7 +1701,7 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
 	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
 	int i;
 
-	if (vcpu->vcpu_id >= VGIC_MAX_CPUS)
+	if (vcpu->vcpu_id >= dist->nr_cpus)
 		return -EBUSY;
 
 	for (i = 0; i < VGIC_NR_IRQS; i++) {
@@ -1845,7 +1845,7 @@ static int vgic_init_maps(struct kvm *kvm)
 	int nr_cpus, nr_irqs;
 	int ret, i;
 
-	nr_cpus = dist->nr_cpus = VGIC_MAX_CPUS;
+	nr_cpus = dist->nr_cpus = KVM_MAX_VCPUS;
 	nr_irqs = dist->nr_irqs = VGIC_NR_IRQS;
 
 	ret  = vgic_init_bitmap(&dist->irq_enabled, nr_cpus, nr_irqs);
Signature · 1 line
2.0.4
#8 lore raw

PATCH v4 5/8 arm/arm64: KVM: vgic: handle out-of-range MMIO accesses

In reply to Marc Zyngier

Now that we can (almost) dynamically size the number of interrupts, we're facing an interesting issue:

We have to evaluate at runtime whether or not an access hits a valid register, based on the sizing of this particular instance of the distributor. Furthermore, the GIC spec says that accessing a reserved register is RAZ/WI.

For this, add a new field to our range structure, indicating the number of bits a single interrupts uses. That allows us to find out whether or not the access is in range.

---
 include/kvm/arm_vgic.h |  3 ++-
 virt/kvm/arm/vgic.c    | 56 ++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 47 insertions(+), 12 deletions(-)
include/kvm/arm_vgic.h +2 -1
index 3900e31..97f5f57 100644
@@ -32,6 +32,7 @@
 
 #define VGIC_V2_MAX_LRS		(1 << 6)
 #define VGIC_V3_MAX_LRS		16
+#define VGIC_MAX_IRQS		1024
 
 /* Sanity checks... */
 #if (KVM_MAX_VCPUS > 8)
@@ -42,7 +43,7 @@
 #error "VGIC_NR_IRQS must be a multiple of 32"
 #endif
 
-#if (VGIC_NR_IRQS > 1024)
+#if (VGIC_NR_IRQS > VGIC_MAX_IRQS)
 #error "VGIC_NR_IRQS must be <= 1024"
 #endif
 
virt/kvm/arm/vgic.c +45 -11

120 lines not on this page · raw message

Signature · 1 line
2.0.4
#9 lore raw

PATCH v4 6/8 arm/arm64: KVM: vgic: kill VGIC_NR_IRQS

In reply to Marc Zyngier

Nuke VGIC_NR_IRQS entierly, now that the distributor instance contains the number of IRQ allocated to this GIC.

Also add VGIC_NR_IRQS_LEGACY to preserve the current API.

---
 include/kvm/arm_vgic.h |  6 +++---
 virt/kvm/arm/vgic.c    | 17 +++++++++++------
 2 files changed, 14 insertions(+), 9 deletions(-)
include/kvm/arm_vgic.h +3 -3
index 97f5f57..0a27564 100644
@@ -25,7 +25,7 @@
 #include <linux/spinlock.h>
 #include <linux/types.h>
 
-#define VGIC_NR_IRQS		256
+#define VGIC_NR_IRQS_LEGACY	256
 #define VGIC_NR_SGIS		16
 #define VGIC_NR_PPIS		16
 #define VGIC_NR_PRIVATE_IRQS	(VGIC_NR_SGIS + VGIC_NR_PPIS)
@@ -39,11 +39,11 @@
 #error	Invalid number of CPU interfaces
 #endif
 
-#if (VGIC_NR_IRQS & 31)
+#if (VGIC_NR_IRQS_LEGACY & 31)
 #error "VGIC_NR_IRQS must be a multiple of 32"
 #endif
 
-#if (VGIC_NR_IRQS > VGIC_MAX_IRQS)
+#if (VGIC_NR_IRQS_LEGACY > VGIC_MAX_IRQS)
 #error "VGIC_NR_IRQS must be <= 1024"
 #endif
 
virt/kvm/arm/vgic.c +11 -6
index ab01cab..dfa6430 100644
@@ -436,7 +436,7 @@ static bool handle_mmio_misc(struct kvm_vcpu *vcpu,
 
 	case 4:			/* GICD_TYPER */
 		reg  = (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5;
-		reg |= (VGIC_NR_IRQS >> 5) - 1;
+		reg |= (vcpu->kvm->arch.vgic.nr_irqs >> 5) - 1;
 		vgic_reg_access(mmio, &reg, word_offset,
 				ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED);
 		break;
@@ -1274,13 +1274,14 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
 static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
 	struct vgic_lr vlr;
 	int lr;
 
 	/* Sanitize the input... */
 	BUG_ON(sgi_source_id & ~7);
 	BUG_ON(sgi_source_id && irq >= VGIC_NR_SGIS);
-	BUG_ON(irq >= VGIC_NR_IRQS);
+	BUG_ON(irq >= dist->nr_irqs);
 
 	kvm_debug("Queue IRQ%d\n", irq);
 
@@ -1516,7 +1517,7 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
 
 		vlr = vgic_get_lr(vcpu, lr);
 
-		BUG_ON(vlr.irq >= VGIC_NR_IRQS);
+		BUG_ON(vlr.irq >= dist->nr_irqs);
 		vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY;
 	}
 
@@ -1738,7 +1739,7 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
 	if (vcpu->vcpu_id >= dist->nr_cpus)
 		return -EBUSY;
 
-	for (i = 0; i < VGIC_NR_IRQS; i++) {
+	for (i = 0; i < dist->nr_irqs; i++) {
 		if (i < VGIC_NR_PPIS)
 			vgic_bitmap_set_irq_val(&dist->irq_enabled,
 						vcpu->vcpu_id, i, 1);
@@ -1880,7 +1881,11 @@ static int vgic_init_maps(struct kvm *kvm)
 	int ret, i;
 
 	nr_cpus = dist->nr_cpus = KVM_MAX_VCPUS;
-	nr_irqs = dist->nr_irqs = VGIC_NR_IRQS;
+
+	if (!dist->nr_irqs)
+		dist->nr_irqs = VGIC_NR_IRQS_LEGACY;
+
+	nr_irqs = dist->nr_irqs;
 
 	ret  = vgic_init_bitmap(&dist->irq_enabled, nr_cpus, nr_irqs);
 	ret |= vgic_init_bitmap(&dist->irq_level, nr_cpus, nr_irqs);
@@ -1964,7 +1969,7 @@ int kvm_vgic_init(struct kvm *kvm)
 		goto out;
 	}
 
-	for (i = VGIC_NR_PRIVATE_IRQS; i < VGIC_NR_IRQS; i += 4)
+	for (i = VGIC_NR_PRIVATE_IRQS; i < kvm->arch.vgic.nr_irqs; i += 4)
 		vgic_set_target_reg(kvm, 0, i);
 
 	kvm->arch.vgic.ready = true;
Signature · 1 line
2.0.4
Reviewed-by on v4 6/8
#10 lore raw

Re: [PATCH v4 6/8] arm/arm64: KVM: vgic: kill VGIC_NR_IRQS

Quoted 6 lines · Marc Zyngier

Nuke VGIC_NR_IRQS entierly, now that the distributor instance contains the number of IRQ allocated to this GIC.

Also add VGIC_NR_IRQS_LEGACY to preserve the current API.

Did anything dramtically change in this patch since last time around?

If not, I'll re-affirm my tag:

Thanks,
-Christoffer

#11 lore raw

PATCH v4 7/8 arm/arm64: KVM: vgic: delay vgic allocation until init time

In reply to Marc Zyngier

It is now quite easy to delay the allocation of the vgic tables until we actually require it to be up and running (when the first vcpu is kicking around, or someones tries to access the GIC registers).

This allow us to allocate memory for the exact number of CPUs we have. As nobody configures the number of interrupts just yet, use a fallback to VGIC_NR_IRQS_LEGACY.

---
 arch/arm/kvm/arm.c     |  7 -------
 include/kvm/arm_vgic.h |  1 -
 virt/kvm/arm/vgic.c    | 42 +++++++++++++++++++++++++++++-------------
 3 files changed, 29 insertions(+), 21 deletions(-)
arch/arm/kvm/arm.c +0 -7
index 923a01d..7d5065e 100644
@@ -271,16 +271,9 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
 
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
-	int ret;
-
 	/* Force users to call KVM_ARM_VCPU_INIT */
 	vcpu->arch.target = -1;
 
-	/* Set up VGIC */
-	ret = kvm_vgic_vcpu_init(vcpu);
-	if (ret)
-		return ret;
-
 	/* Set up the timer */
 	kvm_timer_vcpu_init(vcpu);
 
include/kvm/arm_vgic.h +0 -1
index 0a27564..73cbb61 100644
@@ -277,7 +277,6 @@ int kvm_vgic_hyp_init(void);
 int kvm_vgic_init(struct kvm *kvm);
 int kvm_vgic_create(struct kvm *kvm);
 void kvm_vgic_destroy(struct kvm *kvm);
-int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
 void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu);
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
virt/kvm/arm/vgic.c +29 -13

105 lines not on this page · raw message

Signature · 1 line
2.0.4
#12 lore raw

PATCH v4 8/8 arm/arm64: KVM: vgic: make number of irqs a configurable attribute

In reply to Marc Zyngier

In order to make the number of interrupts configurable, use the new fancy device management API to add KVM_DEV_ARM_VGIC_GRP_NR_IRQS as a VGIC configurable attribute.

Userspace can now specify the exact size of the GIC (by increments of 32 interrupts).

---
 Documentation/virtual/kvm/devices/arm-vgic.txt | 10 +++++++
 arch/arm/include/uapi/asm/kvm.h                |  1 +
 arch/arm64/include/uapi/asm/kvm.h              |  1 +
 virt/kvm/arm/vgic.c                            | 37 ++++++++++++++++++++++++++
 4 files changed, 49 insertions(+)
Documentation/virtual/kvm/devices/arm-vgic.txt +10 -0
index 7f4e91b..df8b0c7 100644
@@ -71,3 +71,13 @@ Groups:
   Errors:
     -ENODEV: Getting or setting this register is not yet supported
     -EBUSY: One or more VCPUs are running
+
+  KVM_DEV_ARM_VGIC_GRP_NR_IRQS
+  Attributes:
+    A value describing the number of interrupts (SGI, PPI and SPI) for
+    this GIC instance, ranging from 64 to 1024, in increments of 32.
+
+  Errors:
+    -EINVAL: Value set is out of the expected range
+    -EBUSY: Value has already be set, or GIC has already been initialized
+            with default values.
arch/arm/include/uapi/asm/kvm.h +1 -0
index e6ebdd3..8b51c1a 100644
@@ -173,6 +173,7 @@ struct kvm_arch_memory_slot {
 #define   KVM_DEV_ARM_VGIC_CPUID_MASK	(0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
 #define   KVM_DEV_ARM_VGIC_OFFSET_SHIFT	0
 #define   KVM_DEV_ARM_VGIC_OFFSET_MASK	(0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
+#define KVM_DEV_ARM_VGIC_GRP_NR_IRQS	3
 
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT		24
arch/arm64/include/uapi/asm/kvm.h +1 -0
index e633ff8..b5cd6ed 100644
@@ -159,6 +159,7 @@ struct kvm_arch_memory_slot {
 #define   KVM_DEV_ARM_VGIC_CPUID_MASK	(0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
 #define   KVM_DEV_ARM_VGIC_OFFSET_SHIFT	0
 #define   KVM_DEV_ARM_VGIC_OFFSET_MASK	(0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
+#define KVM_DEV_ARM_VGIC_GRP_NR_IRQS	3
 
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT		24
virt/kvm/arm/vgic.c +37 -0
index 9180823..744388d 100644
@@ -2331,6 +2331,36 @@ static int vgic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
 
 		return vgic_attr_regs_access(dev, attr, &reg, true);
 	}
+	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: {
+		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
+		u32 val;
+		int ret = 0;
+
+		if (get_user(val, uaddr))
+			return -EFAULT;
+
+		/*
+		 * We require:
+		 * - at least 32 SPIs on top of the 16 SGIs and 16 PPIs
+		 * - at most 1024 interrupts
+		 * - a multiple of 32 interrupts
+		 */
+		if (val < (VGIC_NR_PRIVATE_IRQS + 32) ||
+		    val > VGIC_MAX_IRQS ||
+		    (val & 31))
+			return -EINVAL;
+
+		mutex_lock(&dev->kvm->lock);
+
+		if (vgic_initialized(dev->kvm) || dev->kvm->arch.vgic.nr_irqs)
+			ret = -EBUSY;
+		else
+			dev->kvm->arch.vgic.nr_irqs = val;
+
+		mutex_unlock(&dev->kvm->lock);
+
+		return ret;
+	}
 
 	}
 
@@ -2367,6 +2397,11 @@ static int vgic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
 		r = put_user(reg, uaddr);
 		break;
 	}
+	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: {
+		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
+		r = put_user(dev->kvm->arch.vgic.nr_irqs, uaddr);
+		break;
+	}
 
 	}
 
@@ -2403,6 +2438,8 @@ static int vgic_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
 	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
 		offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
 		return vgic_has_attr_regs(vgic_cpu_ranges, offset);
+	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS:
+		return 0;
 	}
 	return -ENXIO;
 }
Signature · 1 line
2.0.4
Reviewed-by on v4 8/8
#13 lore raw

Re: [PATCH v4 8/8] arm/arm64: KVM: vgic: make number of irqs a configurable attribute

Quoted 122 lines · Marc Zyngier

In order to make the number of interrupts configurable, use the new fancy device management API to add KVM_DEV_ARM_VGIC_GRP_NR_IRQS as a VGIC configurable attribute.

Userspace can now specify the exact size of the GIC (by increments of 32 interrupts).

---
 Documentation/virtual/kvm/devices/arm-vgic.txt | 10 +++++++
 arch/arm/include/uapi/asm/kvm.h                |  1 +
 arch/arm64/include/uapi/asm/kvm.h              |  1 +
 virt/kvm/arm/vgic.c                            | 37 ++++++++++++++++++++++++++
 4 files changed, 49 insertions(+)
Documentation/virtual/kvm/devices/arm-vgic.txt +10 -0
index 7f4e91b..df8b0c7 100644
@@ -71,3 +71,13 @@ Groups:
   Errors:
     -ENODEV: Getting or setting this register is not yet supported
     -EBUSY: One or more VCPUs are running
+
+  KVM_DEV_ARM_VGIC_GRP_NR_IRQS
+  Attributes:
+    A value describing the number of interrupts (SGI, PPI and SPI) for
+    this GIC instance, ranging from 64 to 1024, in increments of 32.
+
+  Errors:
+    -EINVAL: Value set is out of the expected range
+    -EBUSY: Value has already be set, or GIC has already been initialized
+            with default values.
arch/arm/include/uapi/asm/kvm.h +1 -0
index e6ebdd3..8b51c1a 100644
@@ -173,6 +173,7 @@ struct kvm_arch_memory_slot {
 #define   KVM_DEV_ARM_VGIC_CPUID_MASK	(0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
 #define   KVM_DEV_ARM_VGIC_OFFSET_SHIFT	0
 #define   KVM_DEV_ARM_VGIC_OFFSET_MASK	(0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
+#define KVM_DEV_ARM_VGIC_GRP_NR_IRQS	3
 
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT		24
arch/arm64/include/uapi/asm/kvm.h +1 -0
index e633ff8..b5cd6ed 100644
@@ -159,6 +159,7 @@ struct kvm_arch_memory_slot {
 #define   KVM_DEV_ARM_VGIC_CPUID_MASK	(0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
 #define   KVM_DEV_ARM_VGIC_OFFSET_SHIFT	0
 #define   KVM_DEV_ARM_VGIC_OFFSET_MASK	(0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
+#define KVM_DEV_ARM_VGIC_GRP_NR_IRQS	3
 
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT		24
virt/kvm/arm/vgic.c +37 -0
index 9180823..744388d 100644
@@ -2331,6 +2331,36 @@ static int vgic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
 
 		return vgic_attr_regs_access(dev, attr, &reg, true);
 	}
+	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: {
+		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
+		u32 val;
+		int ret = 0;
+
+		if (get_user(val, uaddr))
+			return -EFAULT;
+
+		/*
+		 * We require:
+		 * - at least 32 SPIs on top of the 16 SGIs and 16 PPIs
+		 * - at most 1024 interrupts
+		 * - a multiple of 32 interrupts
+		 */
+		if (val < (VGIC_NR_PRIVATE_IRQS + 32) ||
+		    val > VGIC_MAX_IRQS ||
+		    (val & 31))
+			return -EINVAL;
+
+		mutex_lock(&dev->kvm->lock);
+
+		if (vgic_initialized(dev->kvm) || dev->kvm->arch.vgic.nr_irqs)
+			ret = -EBUSY;
+		else
+			dev->kvm->arch.vgic.nr_irqs = val;
+
+		mutex_unlock(&dev->kvm->lock);
+
+		return ret;
+	}
 
 	}
 
@@ -2367,6 +2397,11 @@ static int vgic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
 		r = put_user(reg, uaddr);
 		break;
 	}
+	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: {
+		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
+		r = put_user(dev->kvm->arch.vgic.nr_irqs, uaddr);
+		break;
+	}
 
 	}
 
@@ -2403,6 +2438,8 @@ static int vgic_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
 	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
 		offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
 		return vgic_has_attr_regs(vgic_cpu_ranges, offset);
+	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS:
+		return 0;
 	}
 	return -ENXIO;
 }
Signature · 1 line
2.0.4
#14 lore raw
In reply to Marc Zyngier

Hi all,

I have a problem that I want to ask for your advice.
Before I send this mail to Marc and Christoffer. But it seems that they are busy or not online recently.

I git clone Marc's "kvmtool-vgic-dyn" branch and run it on Fastmodel Cortex-A57*4 with qemu 2.1.0.
https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git

I want to do repeated lifecycle test. The test is that start 2 VMs, sleep 10 and do pkill qemu. Test script is following:
#!/bin/sh
while true
do

	qemu-system-aarch64 \
    		-enable-kvm -smp 4 \
    		-kernel Image \
    		-m 512 -machine virt,kernel_irqchip=on \
    		-initrd guestfs.cpio.gz \
    		-cpu host \
    		-chardev pty,id=pty0,mux=on -monitor chardev:pty0 \
    		-serial chardev:pty0 -daemonize \
    		-vnc 0.0.0.0:0 \
    		-append "rdinit=/sbin/init console=ttyAMA0 mem=512M root=/dev/ram earlyprintk=pl011,0x9000000 rw" &

	qemu-system-aarch64 \
    		-enable-kvm -smp 4 \
    		-kernel Image \
    		-m 512 -machine virt,kernel_irqchip=on \
    		-initrd guestfs.cpio.gz \
    		-cpu host \
    		-chardev pty,id=pty0,mux=on -monitor chardev:pty0 \
   		-serial chardev:pty0 -daemonize \
    		-vnc 0.0.0.0:1 \
    		-append "rdinit=/sbin/init console=ttyAMA0 mem=512M root=/dev/ram earlyprintk=pl011,0x9000000 rw" &
    	sleep 10
	pkill qemu

done

After repeating sometimes there is something wrong happened as followed. Look forward for your reply.
Thanks,
Shannon

BUG: failure at mm/slub.c:3346/kfree()!
Kernel panic - not syncing: BUG!
CPU: 0 PID: 874 Comm: qemu-system-aar Not tainted 3.17.0-rc4+ #1
Call trace:
[<ffffffc000087f50>] dump_backtrace+0x0/0x130
[<ffffffc000088090>] show_stack+0x10/0x1c
[<ffffffc000499074>] dump_stack+0x74/0x94
[<ffffffc0004956c4>] panic+0xe0/0x218
[<ffffffc000152fb4>] kfree+0x168/0x16c
[<ffffffc0000a260c>] kvm_vgic_destroy+0x104/0x164
[<ffffffc000099f54>] kvm_arch_destroy_vm+0x68/0x7c
[<ffffffc000097a04>] kvm_put_kvm+0x158/0x244
[<ffffffc000097b28>] kvm_device_release+0x38/0x4c
[<ffffffc000159710>] __fput+0x98/0x1d8
[<ffffffc0001598a4>] ____fput+0x8/0x14
[<ffffffc0000beb04>] task_work_run+0xac/0xec
[<ffffffc0000a8314>] do_exit+0x280/0x92c
[<ffffffc0000a9788>] do_group_exit+0x40/0xd4
[<ffffffc0000b3214>] get_signal+0x2b4/0x4fc
[<ffffffc000087574>] do_signal+0x170/0x4fc
[<ffffffc000087b18>] do_notify_resume+0x58/0x64
CPU3: stopping
CPU: 3 PID: 0 Comm: swapper/3 Not tainted 3.17.0-rc4+ #1
Call trace:
[<ffffffc000087f50>] dump_backtrace+0x0/0x130
[<ffffffc000088090>] show_stack+0x10/0x1c
[<ffffffc000499074>] dump_stack+0x74/0x94
[<ffffffc00008fd74>] handle_IPI+0x180/0x198
[<ffffffc0000812d0>] gic_handle_irq+0x78/0x80
Exception stack(0xffffffc87b8dbe30 to 0xffffffc87b8dbf50)

be20: 7b8d8000 ffffffc8 006a00d0 ffffffc0
be40: 7b8dbf70 ffffffc8 000851a0 ffffffc0 00000000 00000000 00000000 00000000
be60: 7ffc49ec ffffffc8 00000000 01000000 00672700 ffffffc0 00000003 00000000
be80: 00000000 00098968 000002a8 00000000 7b8975b0 ffffffc8 7b8dbd80 ffffffc8
bea0: 00000400 00000000 00000400 00000000 00000018 00000000 e8000000 00000003
bec0: 00000000 00000000 80000000 0008bab2 0015879c ffffffc0 b2f741d0 0000007f
bee0: c4ae28c0 0000007f 7b8d8000 ffffffc8 006a00d0 ffffffc0 006a1bc0 ffffffc0
bf00: 004aa258 ffffffc0 0069cdc7 ffffffc0 005b3300 ffffffc0 00000001 00000000
bf20: 8070c000 00000000 00080330 ffffffc0 80000000 00000040 7b8dbf70 ffffffc8
bf40: 0008519c ffffffc0 7b8dbf70 ffffffc8

[<ffffffc000083da0>] el1_irq+0x60/0xc0
[<ffffffc0000d68cc>] cpu_startup_entry+0xe8/0x134
[<ffffffc00008f848>] secondary_start_kernel+0x108/0x118
CPU1: stopping
CPU: 1 PID: 0 Comm: swapper/1 Not tainted 3.17.0-rc4+ #1
Call trace:
[<ffffffc000087f50>] dump_backtrace+0x0/0x130
[<ffffffc000088090>] show_stack+0x10/0x1c
[<ffffffc000499074>] dump_stack+0x74/0x94
[<ffffffc00008fd74>] handle_IPI+0x180/0x198
[<ffffffc0000812d0>] gic_handle_irq+0x78/0x80
Exception stack(0xffffffc87b8d3e30 to 0xffffffc87b8d3f50)

3e20: 7b8d0000 ffffffc8 006a00d0 ffffffc0
3e40: 7b8d3f70 ffffffc8 000851a0 ffffffc0 00000000 00000000 00000000 00000000 3e60: 7ffae9ec ffffffc8 00000000 01000000 00672700 ffffffc0 00000001 00000000 3e80: 00000000 0008583b 000003fe 00000000 7b896130 ffffffc8 7b8d3d80 ffffffc8 3ea0: 00000400 00000000 00000400 00000000 004e9000 00000000 00000001 00000000 3ec0: 00000001 00000000 ffffffff ffffffff 000a9a54 ffffffc0 b16ac310 0000007f 3ee0: e8a17570 0000007f 7b8d0000 ffffffc8 006a00d0 ffffffc0 006a1bc0 ffffffc0 3f00: 004aa258 ffffffc0 0069cdc7 ffffffc0 005b3300 ffffffc0 00000001 00000000 3f20: 8070c000 00000000 00080330 ffffffc0 80000000 00000040 7b8d3f70 ffffffc8 3f40: 0008519c ffffffc0 7b8d3f70 ffffffc8

[<ffffffc000083da0>] el1_irq+0x60/0xc0
[<ffffffc0000d68cc>] cpu_startup_entry+0xe8/0x134
[<ffffffc00008f848>] secondary_start_kernel+0x108/0x118
CPU2: stopping
CPU: 2 PID: 0 Comm: swapper/2 Not tainted 3.17.0-rc4+ #1
Call trace:
[<ffffffc000087f50>] dump_backtrace+0x0/0x130
[<ffffffc000088090>] show_stack+0x10/0x1c
[<ffffffc000499074>] dump_stack+0x74/0x94
[<ffffffc00008fd74>] handle_IPI+0x180/0x198
[<ffffffc0000812d0>] gic_handle_irq+0x78/0x80
Exception stack(0xffffffc87b8d7e30 to 0xffffffc87b8d7f50)

7e20: 7b8d4000 ffffffc8 006a00d0 ffffffc0
7e40: 7b8d7f70 ffffffc8 000851a0 ffffffc0 00000000 00000000 00000000 00000000 7e60: 7ffb99ec ffffffc8 00000000 01000000 00672700 ffffffc0 00000002 00000000 7e80: 80000000 0007bfa4 000003fe 00000000 7b896b70 ffffffc8 7b8d7d80 ffffffc8 7ea0: 00000400 00000000 00000400 00000000 0064dd20 00000000 0064dd18 00000000 7ec0: 0064dd10 00000000 ffffffff ffffffff 00168e84 ffffffc0 93c15150 0000007f 7ee0: eed0dfc0 0000007f 7b8d4000 ffffffc8 006a00d0 ffffffc0 006a1bc0 ffffffc0 7f00: 004aa258 ffffffc0 0069cdc7 ffffffc0 005b3300 ffffffc0 00000001 00000000 7f20: 8070c000 00000000 00080330 ffffffc0 80000000 00000040 7b8d7f70 ffffffc8 7f40: 0008519c ffffffc0 7b8d7f70 ffffffc8

[<ffffffc000083da0>] el1_irq+0x60/0xc0
[<ffffffc0000d68cc>] cpu_startup_entry+0xe8/0x134
[<ffffffc00008f848>] secondary_start_kernel+0x108/0x118
---[ end Kernel panic - not syncing: BUG!
Quoted 5 lines · Marc Zyngier

So far, the VGIC data structures have been statically sized, meaning that we always have to support more interrupts than we actually want, and more CPU interfaces than we should. This is a waste of resource, and is the kind of things that should be tuneable.

Signature · 1 line
Shannon
#15 lore raw
Quoted 36 lines · Shannon Zhao

Hi all,

I have a problem that I want to ask for your advice.
Before I send this mail to Marc and Christoffer. But it seems that they are busy or not online recently.

I git clone Marc's "kvmtool-vgic-dyn" branch and run it on Fastmodel Cortex-A57*4 with qemu 2.1.0.
https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git

I want to do repeated lifecycle test. The test is that start 2 VMs, sleep 10 and do pkill qemu. Test script is following:
#!/bin/sh
while true
do

	qemu-system-aarch64 \
    		-enable-kvm -smp 4 \
    		-kernel Image \
    		-m 512 -machine virt,kernel_irqchip=on \
    		-initrd guestfs.cpio.gz \
    		-cpu host \
    		-chardev pty,id=pty0,mux=on -monitor chardev:pty0 \
    		-serial chardev:pty0 -daemonize \
    		-vnc 0.0.0.0:0 \
    		-append "rdinit=/sbin/init console=ttyAMA0 mem=512M root=/dev/ram earlyprintk=pl011,0x9000000 rw" &

	qemu-system-aarch64 \
    		-enable-kvm -smp 4 \
    		-kernel Image \
    		-m 512 -machine virt,kernel_irqchip=on \
    		-initrd guestfs.cpio.gz \
    		-cpu host \
    		-chardev pty,id=pty0,mux=on -monitor chardev:pty0 \
   		-serial chardev:pty0 -daemonize \
    		-vnc 0.0.0.0:1 \
    		-append "rdinit=/sbin/init console=ttyAMA0 mem=512M root=/dev/ram earlyprintk=pl011,0x9000000 rw" &
    	sleep 10
	pkill qemu
Quoted 7 lines

done

After repeating sometimes there is something wrong happened as followed. Look forward for your reply.
Thanks,
Shannon

Hi Shannon,

This appears to be related to a bug in vgic_set_attr() which messes up the bitmap initialization, I'm working on a patch.

-Christoffer

#16 lore raw
Quoted 44 lines · Christoffer Dall
Quoted 36 lines · Shannon Zhao

Hi all,

I have a problem that I want to ask for your advice.
Before I send this mail to Marc and Christoffer. But it seems that they are busy or not online recently.

I git clone Marc's "kvmtool-vgic-dyn" branch and run it on Fastmodel Cortex-A57*4 with qemu 2.1.0.
https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git

I want to do repeated lifecycle test. The test is that start 2 VMs, sleep 10 and do pkill qemu. Test script is following:
#!/bin/sh
while true
do

	qemu-system-aarch64 \
    		-enable-kvm -smp 4 \
    		-kernel Image \
    		-m 512 -machine virt,kernel_irqchip=on \
    		-initrd guestfs.cpio.gz \
    		-cpu host \
    		-chardev pty,id=pty0,mux=on -monitor chardev:pty0 \
    		-serial chardev:pty0 -daemonize \
    		-vnc 0.0.0.0:0 \
    		-append "rdinit=/sbin/init console=ttyAMA0 mem=512M root=/dev/ram earlyprintk=pl011,0x9000000 rw" &

	qemu-system-aarch64 \
    		-enable-kvm -smp 4 \
    		-kernel Image \
    		-m 512 -machine virt,kernel_irqchip=on \
    		-initrd guestfs.cpio.gz \
    		-cpu host \
    		-chardev pty,id=pty0,mux=on -monitor chardev:pty0 \
   		-serial chardev:pty0 -daemonize \
    		-vnc 0.0.0.0:1 \
    		-append "rdinit=/sbin/init console=ttyAMA0 mem=512M root=/dev/ram earlyprintk=pl011,0x9000000 rw" &
    	sleep 10
	pkill qemu
Quoted 7 lines

done

After repeating sometimes there is something wrong happened as followed. Look forward for your reply.
Thanks,
Shannon

Quoted 9 lines

Hi Shannon,

This appears to be related to a bug in vgic_set_attr() which messes up the bitmap initialization, I'm working on a patch.

-Christoffer

.

Hi Christoffer,

Thanks for you reply.
I will test your patch to check whether the bug disappears. Thanks,

Signature · 1 line
Shannon