Skip to contentSkip to conversation

irqchip/gic-v4: GICv4.1 architecture support

PATCH Applied Marc Zyngier 3 participants 79 messages Oct 27, 2019 – Mar 15, 2020

Subsystems (6)

v2 of 6 Applied Latest is v6 →

lore
Share & tools
lore

Marc Zyngier

author maintainer
#0 lore raw

v2 cover irqchip/gic-v4: GICv4.1 architecture support

This rather long series expands the existing GICv4 support to deal with the new GICv4.1 architecture, which comes with a set of major improvements compared to v4.0:

- One architectural doorbell per vcpu, instead of one doorbell per VLPI

- Doorbell entirely managed by the HW, with an "at most once" delivery guarantee per non-residency phase and only when requested by the hypervisor

- A shared memory scheme between ITSs and redistributors, allowing for an optimised residency sequence (the use of VMOVP becomes less frequent)

- Support for direct virtual SGI delivery (the injection path still involves the hypervisor), at the cost of losing the active state on SGIs. It shouldn't be a big deal, but some guest operating systems might notice (Linux definitely won't care)

On the other hand, public documentation is not available yet, so that's a bit annoying...

The series is roughly organised in 5 parts:

(1) A bunch of reworks to make the checking of some features more straightforward,
(2) VPE table allocation, new flavours of VMAPP/VMOVP commands
(3) v4.1 doorbell management
(4) Virtual SGI support
(5) Plumbing of virtual SGIs in KVM

Notes:

- This series has uncovered a behaviour that looks like a HW bug on the Cavium ThunderX (aka TX1) platform (see patch #10). I'd very much welcome some clarification from the Marvell/Cavium folks on Cc, as well as an official erratum number if this happens to be an actual bug

- I plan to take the first seven patches into 5.5 in order to reduce the size of the backlog:

* patch #1 and #3 are good performance optimisations
* patches #4 through to #7 are nice cleanups

Do shout if you see any issue with this.

* From v1:
- A bunch of minor reworks after Zenghui Yu's review
- A workaround for what looks like a new and unexpected TX1 bug
- A subtle reorder of the series so that some patches can go in early

Marc Zyngier (36):
KVM: arm64: vgic-v4: Move the GICv4 residency flow to be driven by
vcpu_load/put
irqchip/gic-v3-its: Factor out wait_for_syncr primitive irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface irqchip/gic-v3-its: Make is_v4 use a TYPER copy irqchip/gic-v3-its: Kill its->ite_size and use TYPER copy instead irqchip/gic-v3-its: Kill its->device_ids and use TYPER copy instead irqchip/gic-v3-its: Add get_vlpi_map() helper irqchip/gic-v3: Detect GICv4.1 supporting RVPEID irqchip/gic-v3: Add GICv4.1 VPEID size discovery irqchip/gic-v3: Workaround Cavium TX1 erratum when reading GICD_TYPER2 irqchip/gic-v4.1: VPE table (aka GICR_VPROPBASER) allocation irqchip/gic-v4.1: Implement the v4.1 flavour of VMAPP irqchip/gic-v4.1: Don't use the VPE proxy if RVPEID is set irqchip/gic-v4.1: Implement the v4.1 flavour of VMOVP irqchip/gic-v4.1: Plumb skeletal VPE irqchip irqchip/gic-v4.1: Add mask/unmask doorbell callbacks irqchip/gic-v4.1: Add VPE residency callback irqchip/gic-v4.1: Add VPE eviction callback irqchip/gic-v4.1: Add VPE INVALL callback irqchip/gic-v4.1: Suppress per-VLPI doorbell irqchip/gic-v4.1: Allow direct invalidation of VLPIs irqchip/gic-v4.1: Advertise support v4.1 to KVM irqchip/gic-v4.1: Map the ITS SGIR register page irqchip/gic-v4.1: Plumb skeletal VSGI irqchip irqchip/gic-v4.1: Add initial SGI configuration irqchip/gic-v4.1: Plumb mask/unmask SGI callbacks irqchip/gic-v4.1: Plumb get/set_irqchip_state SGI callbacks irqchip/gic-v4.1: Plumb set_vcpu_affinity SGI callbacks irqchip/gic-v4.1: Move doorbell management to the GICv4 abstraction
layer
irqchip/gic-v4.1: Add VSGI allocation/teardown irqchip/gic-v4.1: Add VSGI property setup irqchip/gic-v4.1: Eagerly vmap vPEs
KVM: arm64: GICv4.1: Let doorbells be auto-enabled
KVM: arm64: GICv4.1: Add direct injection capability to SGI registers
KVM: arm64: GICv4.1: Configure SGIs as HW interrupts
KVM: arm64: GICv4.1: Expose HW-based SGIs in debugfs

 arch/arm/include/asm/arch_gicv3.h      |    2 +
 arch/arm64/include/asm/arch_gicv3.h    |    1 +
 drivers/irqchip/irq-gic-v3-its.c       | 1102 +++++++++++++++++++++---
 drivers/irqchip/irq-gic-v3.c           |   46 +-
 drivers/irqchip/irq-gic-v4.c           |  143 ++-
 include/kvm/arm_vgic.h                 |    5 +-
 include/linux/irqchip/arm-gic-common.h |    2 +
 include/linux/irqchip/arm-gic-v3.h     |   75 +-
 include/linux/irqchip/arm-gic-v4.h     |   45 +-
 virt/kvm/arm/arm.c                     |   12 +-
 virt/kvm/arm/vgic/vgic-debug.c         |   14 +-
 virt/kvm/arm/vgic/vgic-mmio-v3.c       |   15 +-
 virt/kvm/arm/vgic/vgic-mmio.c          |   88 +-
 virt/kvm/arm/vgic/vgic-v3.c            |    5 +
 virt/kvm/arm/vgic/vgic-v4.c            |  106 ++-
 virt/kvm/arm/vgic/vgic.c               |    4 -
 virt/kvm/arm/vgic/vgic.h               |    2 -
 17 files changed, 1492 insertions(+), 175 deletions(-)
Signature · 6 lines
2.20.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
author maintainer
#1 lore raw

PATCH v2 1/36 KVM: arm64: vgic-v4: Move the GICv4 residency flow to be driven by vcpu_load/put

When the VHE code was reworked, a lot of the vgic stuff was moved around, but the GICv4 residency code did stay untouched, meaning that we come in and out of residency on each flush/sync, which is obviously suboptimal.

To address this, let's move things around a bit:

- Residency entry (flush) moves to vcpu_load
- Residency exit (sync) moves to vcpu_put
- On blocking (entry to WFI), we "put"
- On unblocking (exit from WFI, we "load"

Because these can nest (load/block/put/load/unblock/put, for example), we now have per-VPE tracking of the residency state.

Additionally, vgic_v4_put gains a "need doorbell" parameter, which only gets set to true when blocking because of a WFI. This allows a finer control of the doorbell, which now also gets disabled as soon as it gets signaled.

---
 drivers/irqchip/irq-gic-v4.c       |  7 +++-
 include/kvm/arm_vgic.h             |  4 +--
 include/linux/irqchip/arm-gic-v4.h |  2 ++
 virt/kvm/arm/arm.c                 | 12 ++++---
 virt/kvm/arm/vgic/vgic-v3.c        |  4 +++
 virt/kvm/arm/vgic/vgic-v4.c        | 55 ++++++++++++++----------------
 virt/kvm/arm/vgic/vgic.c           |  4 ---
 virt/kvm/arm/vgic/vgic.h           |  2 --
 8 files changed, 48 insertions(+), 42 deletions(-)
drivers/irqchip/irq-gic-v4.c +6 -1
index 563e87ed0766..45969927cc81 100644
@@ -141,12 +141,17 @@ static int its_send_vpe_cmd(struct its_vpe *vpe, struct its_cmd_info *info)
 int its_schedule_vpe(struct its_vpe *vpe, bool on)
 {
 	struct its_cmd_info info;
+	int ret;
 
 	WARN_ON(preemptible());
 
 	info.cmd_type = on ? SCHEDULE_VPE : DESCHEDULE_VPE;
 
-	return its_send_vpe_cmd(vpe, &info);
+	ret = its_send_vpe_cmd(vpe, &info);
+	if (!ret)
+		vpe->resident = on;
+
+	return ret;
 }
 
 int its_invall_vpe(struct its_vpe *vpe)
include/kvm/arm_vgic.h +2 -2
index af4f09c02bf1..4dc58d7a0010 100644
@@ -396,7 +396,7 @@ int kvm_vgic_v4_set_forwarding(struct kvm *kvm, int irq,
 int kvm_vgic_v4_unset_forwarding(struct kvm *kvm, int irq,
 				 struct kvm_kernel_irq_routing_entry *irq_entry);
 
-void kvm_vgic_v4_enable_doorbell(struct kvm_vcpu *vcpu);
-void kvm_vgic_v4_disable_doorbell(struct kvm_vcpu *vcpu);
+int vgic_v4_load(struct kvm_vcpu *vcpu);
+int vgic_v4_put(struct kvm_vcpu *vcpu, bool need_db);
 
 #endif /* __KVM_ARM_VGIC_H */
include/linux/irqchip/arm-gic-v4.h +2 -0
index e6b155713b47..ab1396afe08a 100644
@@ -35,6 +35,8 @@ struct its_vpe {
 	/* Doorbell interrupt */
 	int			irq;
 	irq_hw_number_t		vpe_db_lpi;
+	/* VPE resident */
+	bool			resident;
 	/* VPE proxy mapping */
 	int			vpe_proxy_event;
 	/*
virt/kvm/arm/arm.c +8 -4
index 86c6aa1cb58e..bd2afcf9a13f 100644
@@ -322,20 +322,24 @@ void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu)
 	/*
 	 * If we're about to block (most likely because we've just hit a
 	 * WFI), we need to sync back the state of the GIC CPU interface
-	 * so that we have the lastest PMR and group enables. This ensures
+	 * so that we have the latest PMR and group enables. This ensures
 	 * that kvm_arch_vcpu_runnable has up-to-date data to decide
 	 * whether we have pending interrupts.
+	 *
+	 * For the same reason, we want to tell GICv4 that we need
+	 * doorbells to be signalled, should an interrupt become pending.
 	 */
 	preempt_disable();
 	kvm_vgic_vmcr_sync(vcpu);
+	vgic_v4_put(vcpu, true);
 	preempt_enable();
-
-	kvm_vgic_v4_enable_doorbell(vcpu);
 }
 
 void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu)
 {
-	kvm_vgic_v4_disable_doorbell(vcpu);
+	preempt_disable();
+	vgic_v4_load(vcpu);
+	preempt_enable();
 }
 
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
virt/kvm/arm/vgic/vgic-v3.c +4 -0
index 8d69f007dd0c..48307a9eb1d8 100644
@@ -664,6 +664,8 @@ void vgic_v3_load(struct kvm_vcpu *vcpu)
 
 	if (has_vhe())
 		__vgic_v3_activate_traps(vcpu);
+
+	WARN_ON(vgic_v4_load(vcpu));
 }
 
 void vgic_v3_vmcr_sync(struct kvm_vcpu *vcpu)
@@ -676,6 +678,8 @@ void vgic_v3_vmcr_sync(struct kvm_vcpu *vcpu)
 
 void vgic_v3_put(struct kvm_vcpu *vcpu)
 {
+	WARN_ON(vgic_v4_put(vcpu, false));
+
 	vgic_v3_vmcr_sync(vcpu);
 
 	kvm_call_hyp(__vgic_v3_save_aprs, vcpu);
virt/kvm/arm/vgic/vgic-v4.c +26 -29

93 lines not on this page · raw message

virt/kvm/arm/vgic/vgic.c +0 -4
index 45a870cb63f5..99b02ca730a8 100644
@@ -857,8 +857,6 @@ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
 
-	WARN_ON(vgic_v4_sync_hwstate(vcpu));
-
 	/* An empty ap_list_head implies used_lrs == 0 */
 	if (list_empty(&vcpu->arch.vgic_cpu.ap_list_head))
 		return;
@@ -882,8 +880,6 @@ static inline void vgic_restore_state(struct kvm_vcpu *vcpu)
 /* Flush our emulation state into the GIC hardware before entering the guest. */
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
 {
-	WARN_ON(vgic_v4_flush_hwstate(vcpu));
-
 	/*
 	 * If there are no virtual interrupts active or pending for this
 	 * VCPU, then there is no work to do and we can bail out without
virt/kvm/arm/vgic/vgic.h +0 -2
index 83066a81b16a..c7fefd6b1c80 100644
@@ -316,7 +316,5 @@ void vgic_its_invalidate_cache(struct kvm *kvm);
 bool vgic_supports_direct_msis(struct kvm *kvm);
 int vgic_v4_init(struct kvm *kvm);
 void vgic_v4_teardown(struct kvm *kvm);
-int vgic_v4_sync_hwstate(struct kvm_vcpu *vcpu);
-int vgic_v4_flush_hwstate(struct kvm_vcpu *vcpu);
 
 #endif
Signature · 6 lines
2.20.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
author maintainer
#2 lore raw

PATCH v2 2/36 irqchip/gic-v3-its: Factor out wait_for_syncr primitive

In reply to Marc Zyngier

Waiting for a redistributor to have performed an operation is a common thing to do, and the idiom is already spread around. As we're going to make even more use of this, let's have a primitive that does just that.

---
 drivers/irqchip/irq-gic-v3-its.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +9 -6
index 62e54f1a248b..58cb233cf138 100644
@@ -1075,6 +1075,12 @@ static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 		dsb(ishst);
 }
 
+static void wait_for_syncr(void __iomem *rdbase)
+{
+	while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
+		cpu_relax();
+}
+
 static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
@@ -2757,8 +2763,7 @@ static void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to)
 
 		rdbase = per_cpu_ptr(gic_rdists->rdist, from)->rd_base;
 		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
-		while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
-			cpu_relax();
+		wait_for_syncr(rdbase);
 
 		return;
 	}
@@ -2914,8 +2919,7 @@ static void its_vpe_send_inv(struct irq_data *d)
 
 		rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
 		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_INVLPIR);
-		while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
-			cpu_relax();
+		wait_for_syncr(rdbase);
 	} else {
 		its_vpe_send_cmd(vpe, its_send_inv);
 	}
@@ -2957,8 +2961,7 @@ static int its_vpe_set_irqchip_state(struct irq_data *d,
 			gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_SETLPIR);
 		} else {
 			gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
-			while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
-				cpu_relax();
+			wait_for_syncr(rdbase);
 		}
 	} else {
 		if (state)
Signature · 6 lines
2.20.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
reviewer Reviewed-by on v2 2/36
#3 lore raw

Re: [PATCH v2 02/36] irqchip/gic-v3-its: Factor out wait_for_syncr primitive

Quoted 6 lines · Marc Zyngier

Waiting for a redistributor to have performed an operation is a common thing to do, and the idiom is already spread around. As we're going to make even more use of this, let's have a primitive that does just that.

merge bot Applied
#4 lore raw

[tip: irq/core] irqchip/gic-v3-its: Factor out wait_for_syncr primitive

In reply to Marc Zyngier
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     2f4f064b31315c7c8986522cf38ef6d11fb77986
Gitweb:        https://git.kernel.org/tip/2f4f064b31315c7c8986522cf38ef6d11fb77986
Author:        Marc Zyngier <[email protected]>
AuthorDate:    Fri, 08 Nov 2019 16:57:56 
Committer:     Marc Zyngier <[email protected]>
CommitterDate: Sun, 10 Nov 2019 18:47:50 

irqchip/gic-v3-its: Factor out wait_for_syncr primitive

Waiting for a redistributor to have performed an operation is a common thing to do, and the idiom is already spread around. As we're going to make even more use of this, let's have a primitive that does just that.

---
 drivers/irqchip/irq-gic-v3-its.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +9 -6
index d5d8f8f..78d3e73 100644
@@ -1093,6 +1093,12 @@ static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 		dsb(ishst);
 }
 
+static void wait_for_syncr(void __iomem *rdbase)
+{
+	while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
+		cpu_relax();
+}
+
 static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
@@ -2775,8 +2781,7 @@ static void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to)
 
 		rdbase = per_cpu_ptr(gic_rdists->rdist, from)->rd_base;
 		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
-		while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
-			cpu_relax();
+		wait_for_syncr(rdbase);
 
 		return;
 	}
@@ -2932,8 +2937,7 @@ static void its_vpe_send_inv(struct irq_data *d)
 
 		rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
 		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_INVLPIR);
-		while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
-			cpu_relax();
+		wait_for_syncr(rdbase);
 	} else {
 		its_vpe_send_cmd(vpe, its_send_inv);
 	}
@@ -2975,8 +2979,7 @@ static int its_vpe_set_irqchip_state(struct irq_data *d,
 			gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_SETLPIR);
 		} else {
 			gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
-			while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
-				cpu_relax();
+			wait_for_syncr(rdbase);
 		}
 	} else {
 		if (state)
author maintainer
#5 lore raw

PATCH v2 3/36 irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface

In reply to Marc Zyngier

We currently don't make much use of the DirectLPI feature, and it would be beneficial to do this more, if only because it becomes a mandatory feature for GICv4.1.

---
 drivers/irqchip/irq-gic-v3-its.c | 40 +++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 8 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +32 -8
index 58cb233cf138..3e33cdc08200 100644
@@ -175,6 +175,12 @@ static DEFINE_IDA(its_vpeid_ida);
 #define gic_data_rdist_rd_base()	(gic_data_rdist()->rd_base)
 #define gic_data_rdist_vlpi_base()	(gic_data_rdist_rd_base() + SZ_128K)
 
+static inline u32 its_get_event_id(struct irq_data *d)
+{
+	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+	return d->hwirq - its_dev->event_map.lpi_base;
+}
+
 static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 					       u32 event)
 {
@@ -183,6 +189,13 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 	return its->collections + its_dev->event_map.col_map[event];
 }
 
+static struct its_collection *irq_to_col(struct irq_data *d)
+{
+	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+
+	return dev_event_to_col(its_dev, its_get_event_id(d));
+}
+
 static struct its_collection *valid_col(struct its_collection *col)
 {
 	if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(15, 0)))
@@ -1031,12 +1044,6 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
  * irqchip functions - assumes MSI, mostly.
  */
 
-static inline u32 its_get_event_id(struct irq_data *d)
-{
-	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
-	return d->hwirq - its_dev->event_map.lpi_base;
-}
-
 static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 {
 	irq_hw_number_t hwirq;
@@ -1081,12 +1088,28 @@ static void wait_for_syncr(void __iomem *rdbase)
 		cpu_relax();
 }
 
+static void direct_lpi_inv(struct irq_data *d)
+{
+	struct its_collection *col;
+	void __iomem *rdbase;
+
+	/* Target the redistributor this LPI is currently routed to */
+	col = irq_to_col(d);
+	rdbase = per_cpu_ptr(gic_rdists->rdist, col->col_id)->rd_base;
+	gic_write_lpir(d->hwirq, rdbase + GICR_INVLPIR);
+
+	wait_for_syncr(rdbase);
+}
+
 static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 
 	lpi_write_config(d, clr, set);
-	its_send_inv(its_dev, its_get_event_id(d));
+	if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d))
+		direct_lpi_inv(d);
+	else
+		its_send_inv(its_dev, its_get_event_id(d));
 }
 
 static void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
@@ -2917,8 +2940,9 @@ static void its_vpe_send_inv(struct irq_data *d)
 	if (gic_rdists->has_direct_lpi) {
 		void __iomem *rdbase;
 
+		/* Target the redistributor this VPE is currently known on */
 		rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
-		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_INVLPIR);
+		gic_write_lpir(d->parent_data->hwirq, rdbase + GICR_INVLPIR);
 		wait_for_syncr(rdbase);
 	} else {
 		its_vpe_send_cmd(vpe, its_send_inv);
Signature · 6 lines
2.20.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
reviewer Reviewed-by on v2 3/36
#6 lore raw

Re: [PATCH v2 03/36] irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface

Hi Marc,

Quoted 5 lines · Marc Zyngier

We currently don't make much use of the DirectLPI feature, and it would be beneficial to do this more, if only because it becomes a mandatory feature for GICv4.1.

I have no objection to this patch, which says:

But this patch really drives me to look through all callsites of dev_event_to_col(), the abstraction which can be used _only_ with physical LPI mappings.

I find that when building the INV command, we use dev_event_to_col() to find the "sync_obj" and then pass it to the following SYNC command. But the "INV+SYNC" will be performed both on physical LPI and *VLPI* (lpi_update_config/its_send_inv).
So I have two questions about the way we sending INV on VLPI:

1) Which "sync" command should be followed? SYNC or VSYNC? (we currently use SYNC, while the spec says, SYNC "ensures all outstanding ITS operations associated with *physical* interrupts for the Redistributor are globally observed ...")

2) The "sync_obj" we are currently using seems to be wrong.

Thanks,
Zenghui

author maintainer
#7 lore raw

Re: [PATCH v2 03/36] irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface

Quoted 25 lines · Zenghui Yu

Hi Marc,

Quoted 5 lines · Marc Zyngier

We currently don't make much use of the DirectLPI feature, and it would be beneficial to do this more, if only because it becomes a mandatory feature for GICv4.1.

I have no objection to this patch, which says:

But this patch really drives me to look through all callsites of dev_event_to_col(), the abstraction which can be used _only_ with physical LPI mappings.

I find that when building the INV command, we use dev_event_to_col() to find the "sync_obj" and then pass it to the following SYNC command. But the "INV+SYNC" will be performed both on physical LPI and *VLPI* (lpi_update_config/its_send_inv).
So I have two questions about the way we sending INV on VLPI:

Quoted 6 lines

1) Which "sync" command should be followed? SYNC or VSYNC? (we currently use SYNC, while the spec says, SYNC "ensures all outstanding ITS operations associated with *physical* interrupts for the Redistributor are globally observed ...")

2) The "sync_obj" we are currently using seems to be wrong.

I think you're right on both counts (where were you when I wrote the initial GICv4 support? ;-). I think the confusion stems from the fact that there is no 'VINV' command, and we simply overlooked the sync object issue. It is quite likely that existing implementations don't care much about the difference (otherwise we'd have seen the problem before), but it doesn't hurt to do the right thing.

I have the following patch as part of a set of fixes that I'm about to post (once I get a chance to test them), let me know what you think.

	M.
drivers/irqchip/irq-gic-v3-its.c +35 -1
index a47ed2ba2907..75ab3716a870 100644
@@ -702,6 +702,24 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its,
 	return valid_vpe(its, desc->its_vmovp_cmd.vpe);
 }
 
+static struct its_vpe *its_build_vinv_cmd(struct its_node *its,
+					  struct its_cmd_block *cmd,
+					  struct its_cmd_desc *desc)
+{
+	struct its_vlpi_map *map;
+
+	map = dev_event_to_vlpi_map(desc->its_inv_cmd.dev,
+				    desc->its_inv_cmd.event_id);
+
+	its_encode_cmd(cmd, GITS_CMD_INV);
+	its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id);
+	its_encode_event_id(cmd, desc->its_inv_cmd.event_id);
+
+	its_fixup_cmd(cmd);
+
+	return valid_vpe(its, map->vpe);
+}
+
 static u64 its_cmd_ptr_to_offset(struct its_node *its,
 				 struct its_cmd_block *ptr)
 {
@@ -1068,6 +1086,20 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
 	its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
 }
 
+static void its_send_vinv(struct its_device *dev, u32 event_id)
+{
+	struct its_cmd_desc desc;
+
+	/*
+	 * There is no real VINV command. This is just a normal INV,
+	 * with a VSYNC instead of a SYNC.
+	 */
+	desc.its_inv_cmd.dev = dev;
+	desc.its_inv_cmd.event_id = event_id;
+
+	its_send_single_vcommand(dev->its, its_build_vinv_cmd, &desc);
+}
+
 /*
  * irqchip functions - assumes MSI, mostly.
  */
@@ -1142,8 +1174,10 @@ static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
 	lpi_write_config(d, clr, set);
 	if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d))
 		direct_lpi_inv(d);
-	else
+	else if (!irqd_is_forwarded_to_vcpu(d))
 		its_send_inv(its_dev, its_get_event_id(d));
+	else
+		its_send_vinv(its_dev, its_get_event_id(d));
 }
 
 static void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
Signature · 1 line
Jazz is not dead, it just smells funny.
reviewer
#8 lore raw

Re: [PATCH v2 03/36] irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface

Hi Marc,

Quoted 18 lines · Marc Zyngier
Quoted 16 lines · Zenghui Yu

But this patch really drives me to look through all callsites of dev_event_to_col(), the abstraction which can be used _only_ with physical LPI mappings.

I find that when building the INV command, we use dev_event_to_col() to find the "sync_obj" and then pass it to the following SYNC command. But the "INV+SYNC" will be performed both on physical LPI and *VLPI* (lpi_update_config/its_send_inv).
So I have two questions about the way we sending INV on VLPI:

1) Which "sync" command should be followed? SYNC or VSYNC? (we currently use SYNC, while the spec says, SYNC "ensures all outstanding ITS operations associated with *physical* interrupts for the Redistributor are globally observed ...")

Quoted 4 lines
Quoted 1 line

2) The "sync_obj" we are currently using seems to be wrong.

I think you're right on both counts (where were you when I wrote the initial GICv4 support? ;-). I think the confusion stems from the fact

(I'm a bit late here :-).

Quoted 16 lines

that there is no 'VINV' command, and we simply overlooked the sync object issue. It is quite likely that existing implementations don't care much about the difference (otherwise we'd have seen the problem before), but it doesn't hurt to do the right thing.

I have the following patch as part of a set of fixes that I'm about to post (once I get a chance to test them), let me know what you think.

	M.
drivers/irqchip/irq-gic-v3-its.c +0 -0
index a47ed2ba2907..75ab3716a870 100644
@@ -702,6 +702,24 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its,
  	return valid_vpe(its, desc->its_vmovp_cmd.vpe);

drivers/irqchip/irq-gic-v3-its.c static struct its_vpe *its_build_vmovp_cmd(struct its_node *its, · line 712

Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +8 -0
  }
 
+static struct its_vpe *its_build_vinv_cmd(struct its_node *its,
+					  struct its_cmd_block *cmd,
+					  struct its_cmd_desc *desc)
+{
+	struct its_vlpi_map *map;
+
+	map = dev_event_to_vlpi_map(desc->its_inv_cmd.dev,
+				    desc->its_inv_cmd.event_id);

Indeed! I think we need this kind of abstraction for VLPI.

Quoted 35 lines
drivers/irqchip/irq-gic-v3-its.c +24 -0
+
+	its_encode_cmd(cmd, GITS_CMD_INV);
+	its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id);
+	its_encode_event_id(cmd, desc->its_inv_cmd.event_id);
+
+	its_fixup_cmd(cmd);
+
+	return valid_vpe(its, map->vpe);
+}
+
  static u64 its_cmd_ptr_to_offset(struct its_node *its,
  				 struct its_cmd_block *ptr)
  {
@@ -1068,6 +1086,20 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
  	its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
  }
 
+static void its_send_vinv(struct its_device *dev, u32 event_id)
+{
+	struct its_cmd_desc desc;
+
+	/*
+	 * There is no real VINV command. This is just a normal INV,
+	 * with a VSYNC instead of a SYNC.
+	 */
+	desc.its_inv_cmd.dev = dev;
+	desc.its_inv_cmd.event_id = event_id;
+
+	its_send_single_vcommand(dev->its, its_build_vinv_cmd, &desc);
+}
+
  /*
   * irqchip functions - assumes MSI, mostly.
   */

drivers/irqchip/irq-gic-v3-its.c static void lpi_update_config(struct irq_data *d, u8 clr, u8 set) · line 1180

Quoted 8 lines
drivers/irqchip/irq-gic-v3-its.c +3 -1
@@ -1142,8 +1174,10 @@ static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
  	lpi_write_config(d, clr, set);
  	if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d))
  		direct_lpi_inv(d);
-	else
+	else if (!irqd_is_forwarded_to_vcpu(d))
  		its_send_inv(its_dev, its_get_event_id(d));
+	else
+		its_send_vinv(its_dev, its_get_event_id(d));

Yeah, this is exactly what I was having in the mind when reporting this problem - "maybe we should have a SW emulated VINV+VSYNC for VLPI". So I think this patch has done the right thing.

And what about the INT and CLEAR? In response to guest's INT/CLEAR commands, hypervisor (I mean KVM) will bother the ITS driver to send INT/CLEAR for VLPIs. They're both followed by SYNC and might need the same fixes?

Thanks,
Zenghui

Quoted 4 lines
drivers/irqchip/irq-gic-v3-its.c +0 -0
  }
 
  static void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
author maintainer
#9 lore raw

Re: [PATCH v2 03/36] irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface

Quoted 45 lines · Zenghui Yu

Hi Marc,

Quoted 13 lines · Marc Zyngier
Quoted 11 lines · Zenghui Yu

But this patch really drives me to look through all callsites of dev_event_to_col(), the abstraction which can be used _only_ with physical LPI mappings.

I find that when building the INV command, we use dev_event_to_col() to find the "sync_obj" and then pass it to the following SYNC command. But the "INV+SYNC" will be performed both on physical LPI and *VLPI* (lpi_update_config/its_send_inv).
So I have two questions about the way we sending INV on VLPI:

Quoted 8 lines
Quoted 6 lines

1) Which "sync" command should be followed? SYNC or VSYNC? (we currently use SYNC, while the spec says, SYNC "ensures all outstanding ITS operations associated with *physical* interrupts for the Redistributor are globally observed ...")

2) The "sync_obj" we are currently using seems to be wrong.

I think you're right on both counts (where were you when I wrote the initial GICv4 support? ;-). I think the confusion stems from the fact

(I'm a bit late here :-).

Quoted 18 lines

that there is no 'VINV' command, and we simply overlooked the sync object issue. It is quite likely that existing implementations don't care much about the difference (otherwise we'd have seen the problem before), but it doesn't hurt to do the right thing.
I have the following patch as part of a set of fixes that I'm about to post (once I get a chance to test them), let me know what you think.

	M.
drivers/irqchip/irq-gic-v3-its.c +1 -0
index a47ed2ba2907..75ab3716a870 100644
@@ -702,6 +702,24 @@ static struct its_vpe 
 *its_build_vmovp_cmd(struct its_node *its,
  	return valid_vpe(its, desc->its_vmovp_cmd.vpe);
  }
  +static struct its_vpe *its_build_vinv_cmd(struct its_node *its,
+					  struct its_cmd_block *cmd,
Quoted 8 lines

drivers/irqchip/irq-gic-v3-its.c static struct its_vpe *its_build_vmovp_cmd(struct its_node *its, · line 712

Quoted 6 lines
drivers/irqchip/irq-gic-v3-its.c +6 -0
+					  struct its_cmd_desc *desc)
+{
+	struct its_vlpi_map *map;
+
+	map = dev_event_to_vlpi_map(desc->its_inv_cmd.dev,
+				    desc->its_inv_cmd.event_id);

Indeed! I think we need this kind of abstraction for VLPI.

Yeah, I finally realised we'd needed something like that, and made it part of the get_vlpi_map() patch.

Quoted 50 lines
Quoted 36 lines
drivers/irqchip/irq-gic-v3-its.c +23 -0
+
+	its_encode_cmd(cmd, GITS_CMD_INV);
+	its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id);
+	its_encode_event_id(cmd, desc->its_inv_cmd.event_id);
+
+	its_fixup_cmd(cmd);
+
+	return valid_vpe(its, map->vpe);
+}
+
  static u64 its_cmd_ptr_to_offset(struct its_node *its,
  				 struct its_cmd_block *ptr)
  {
@@ -1068,6 +1086,20 @@ static void its_send_vinvall(struct its_node 
 *its, struct its_vpe *vpe)
  	its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
  }
  +static void its_send_vinv(struct its_device *dev, u32 event_id)
+{
+	struct its_cmd_desc desc;
+
+	/*
+	 * There is no real VINV command. This is just a normal INV,
+	 * with a VSYNC instead of a SYNC.
+	 */
+	desc.its_inv_cmd.dev = dev;
+	desc.its_inv_cmd.event_id = event_id;
+
+	its_send_single_vcommand(dev->its, its_build_vinv_cmd, &desc);
+}
+
  /*
   * irqchip functions - assumes MSI, mostly.
   */

drivers/irqchip/irq-gic-v3-its.c static void lpi_update_config(struct irq_data · line 1181

Quoted 9 lines
drivers/irqchip/irq-gic-v3-its.c +3 -1
@@ -1142,8 +1174,10 @@ static void lpi_update_config(struct irq_data 
 *d, u8 clr, u8 set)
  	lpi_write_config(d, clr, set);
  	if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d))
  		direct_lpi_inv(d);
-	else
+	else if (!irqd_is_forwarded_to_vcpu(d))
  		its_send_inv(its_dev, its_get_event_id(d));
+	else
+		its_send_vinv(its_dev, its_get_event_id(d));

Yeah, this is exactly what I was having in the mind when reporting this problem - "maybe we should have a SW emulated VINV+VSYNC for VLPI". So I think this patch has done the right thing.

Quoted 4 lines

And what about the INT and CLEAR? In response to guest's INT/CLEAR commands, hypervisor (I mean KVM) will bother the ITS driver to send INT/CLEAR for VLPIs. They're both followed by SYNC and might need the same fixes?

Yup. Please see this series[1], which has grown quite a few fixups, including some pretty old ones (I've just pushed an update, and should post it shortly).

Thanks,

        M.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git/log/?h=irq/gic-5.5-wip

Signature · 1 line
Jazz is not dead. It just smells funny...
merge bot Applied
#10 lore raw

[tip: irq/core] irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface

In reply to Marc Zyngier
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     425c09be0f091a3b5940261d9a5d8467d62987e7
Gitweb:        https://git.kernel.org/tip/425c09be0f091a3b5940261d9a5d8467d62987e7
Author:        Marc Zyngier <[email protected]>
AuthorDate:    Fri, 08 Nov 2019 16:57:57 
Committer:     Marc Zyngier <[email protected]>
CommitterDate: Sun, 10 Nov 2019 18:47:51 

irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface

We currently don't make much use of the DirectLPI feature, and it would be beneficial to do this more, if only because it becomes a mandatory feature for GICv4.1.

---
 drivers/irqchip/irq-gic-v3-its.c | 40 ++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 8 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +32 -8
index 78d3e73..6065b09 100644
@@ -191,6 +191,12 @@ static u16 get_its_list(struct its_vm *vm)
 	return (u16)its_list;
 }
 
+static inline u32 its_get_event_id(struct irq_data *d)
+{
+	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+	return d->hwirq - its_dev->event_map.lpi_base;
+}
+
 static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 					       u32 event)
 {
@@ -199,6 +205,13 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 	return its->collections + its_dev->event_map.col_map[event];
 }
 
+static struct its_collection *irq_to_col(struct irq_data *d)
+{
+	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+
+	return dev_event_to_col(its_dev, its_get_event_id(d));
+}
+
 static struct its_collection *valid_col(struct its_collection *col)
 {
 	if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(15, 0)))
@@ -1049,12 +1062,6 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
  * irqchip functions - assumes MSI, mostly.
  */
 
-static inline u32 its_get_event_id(struct irq_data *d)
-{
-	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
-	return d->hwirq - its_dev->event_map.lpi_base;
-}
-
 static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 {
 	irq_hw_number_t hwirq;
@@ -1099,12 +1106,28 @@ static void wait_for_syncr(void __iomem *rdbase)
 		cpu_relax();
 }
 
+static void direct_lpi_inv(struct irq_data *d)
+{
+	struct its_collection *col;
+	void __iomem *rdbase;
+
+	/* Target the redistributor this LPI is currently routed to */
+	col = irq_to_col(d);
+	rdbase = per_cpu_ptr(gic_rdists->rdist, col->col_id)->rd_base;
+	gic_write_lpir(d->hwirq, rdbase + GICR_INVLPIR);
+
+	wait_for_syncr(rdbase);
+}
+
 static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 
 	lpi_write_config(d, clr, set);
-	its_send_inv(its_dev, its_get_event_id(d));
+	if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d))
+		direct_lpi_inv(d);
+	else
+		its_send_inv(its_dev, its_get_event_id(d));
 }
 
 static void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
@@ -2935,8 +2958,9 @@ static void its_vpe_send_inv(struct irq_data *d)
 	if (gic_rdists->has_direct_lpi) {
 		void __iomem *rdbase;
 
+		/* Target the redistributor this VPE is currently known on */
 		rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
-		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_INVLPIR);
+		gic_write_lpir(d->parent_data->hwirq, rdbase + GICR_INVLPIR);
 		wait_for_syncr(rdbase);
 	} else {
 		its_vpe_send_cmd(vpe, its_send_inv);
author maintainer
#11 lore raw

PATCH v2 4/36 irqchip/gic-v3-its: Make is_v4 use a TYPER copy

In reply to Marc Zyngier

Instead of caching the GICv4 compatibility in a discrete way, cache the TYPER register instead, which can then be used to implement the same functionnality. This will get used more extensively in subsequent patches.

---
 drivers/irqchip/irq-gic-v3-its.c | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +13 -11
index 3e33cdc08200..b3c9bc0c3ecd 100644
@@ -102,6 +102,7 @@ struct its_node {
 	struct its_collection	*collections;
 	struct fwnode_handle	*fwnode_handle;
 	u64			(*get_msi_base)(struct its_device *its_dev);
+	u64			typer;
 	u64			cbaser_save;
 	u32			ctlr_save;
 	struct list_head	its_device_list;
@@ -112,10 +113,11 @@ struct its_node {
 	int			numa_node;
 	unsigned int		msi_domain_flags;
 	u32			pre_its_base; /* for Socionext Synquacer */
-	bool			is_v4;
 	int			vlpi_redist_offset;
 };
 
+#define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
+
 #define ITS_ITT_ALIGN		SZ_256
 
 /* The maximum number of VPEID bits supported by VLPI commands */
@@ -1019,7 +1021,7 @@ static void its_send_vmovp(struct its_vpe *vpe)
 
 	/* Emit VMOVPs */
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		if (!vpe->its_vm->vlpi_count[its->list_nr])
@@ -1430,7 +1432,7 @@ static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 	struct its_cmd_info *info = vcpu_info;
 
 	/* Need a v4 ITS */
-	if (!its_dev->its->is_v4)
+	if (!is_v4(its_dev->its))
 		return -EINVAL;
 
 	/* Unmap request? */
@@ -2394,7 +2396,7 @@ static bool its_alloc_vpe_table(u32 vpe_id)
 	list_for_each_entry(its, &its_nodes, entry) {
 		struct its_baser *baser;
 
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		baser = its_get_baser(its, GITS_BASER_TYPE_VCPU);
@@ -2882,7 +2884,7 @@ static void its_vpe_invall(struct its_vpe *vpe)
 	struct its_node *its;
 
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr])
@@ -3150,7 +3152,7 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain,
 	vpe->col_idx = cpumask_first(cpu_online_mask);
 
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		its_send_vmapp(its, vpe, true);
@@ -3176,7 +3178,7 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain,
 		return;
 
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		its_send_vmapp(its, vpe, false);
@@ -3614,12 +3616,12 @@ static int __init its_probe_one(struct resource *res,
 	INIT_LIST_HEAD(&its->entry);
 	INIT_LIST_HEAD(&its->its_device_list);
 	typer = gic_read_typer(its_base + GITS_TYPER);
+	its->typer = typer;
 	its->base = its_base;
 	its->phys_base = res->start;
 	its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer);
 	its->device_ids = GITS_TYPER_DEVBITS(typer);
-	its->is_v4 = !!(typer & GITS_TYPER_VLPIS);
-	if (its->is_v4) {
+	if (is_v4(its)) {
 		if (!(typer & GITS_TYPER_VMOVP)) {
 			err = its_compute_its_list_map(res, its_base);
 			if (err < 0)
@@ -3686,7 +3688,7 @@ static int __init its_probe_one(struct resource *res,
 	gits_write_cwriter(0, its->base + GITS_CWRITER);
 	ctlr = readl_relaxed(its->base + GITS_CTLR);
 	ctlr |= GITS_CTLR_ENABLE;
-	if (its->is_v4)
+	if (is_v4(its))
 		ctlr |= GITS_CTLR_ImDe;
 	writel_relaxed(ctlr, its->base + GITS_CTLR);
 
@@ -4011,7 +4013,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 		return err;
 
 	list_for_each_entry(its, &its_nodes, entry)
-		has_v4 |= its->is_v4;
+		has_v4 |= is_v4(its);
 
 	if (has_v4 & rdists->has_vlpis) {
 		if (its_init_vpe_domain() ||
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 4/36
#12 lore raw

Re: [PATCH v2 04/36] irqchip/gic-v3-its: Make is_v4 use a TYPER copy

Hi Marc,

Quoted 5 lines · Marc Zyngier

Instead of caching the GICv4 compatibility in a discrete way, cache the TYPER register instead, which can then be used to implement the same functionnality. This will get used more extensively in subsequent patches.

nit: You may need to rebase some patches on top of mainline when you finally decide to take them in, i.e., as we currently have get_its_list(), which will use the legacy its->is_v4.

Besides,

author maintainer
#13 lore raw

Re: [PATCH v2 04/36] irqchip/gic-v3-its: Make is_v4 use a TYPER copy

Quoted 8 lines · Zenghui Yu

Hi Marc,

Quoted 4 lines · Marc Zyngier

Instead of caching the GICv4 compatibility in a discrete way, cache the TYPER register instead, which can then be used to implement the same functionnality. This will get used more extensively in subsequent patches.

Quoted 5 lines
Quoted 1 line

nit: You may need to rebase some patches on top of mainline when you finally decide to take them in, i.e., as we currently have get_its_list(), which will use the legacy its->is_v4.

Yeah, I was planning on rebasing the whole irqchip-next branch on top of -rc5 to avoid this kind of problems.

Quoted 1 line

Thanks for that.

	M.
Signature · 5 lines
Jazz is not dead, it just smells funny.
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
merge bot Applied
#14 lore raw

[tip: irq/core] irqchip/gic-v3-its: Make is_v4 use a TYPER copy

In reply to Marc Zyngier
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     0dd57fed6b46659b2db1156cb9100fbcfef6fe5d
Gitweb:        https://git.kernel.org/tip/0dd57fed6b46659b2db1156cb9100fbcfef6fe5d
Author:        Marc Zyngier <[email protected]>
AuthorDate:    Fri, 08 Nov 2019 16:57:58 
Committer:     Marc Zyngier <[email protected]>
CommitterDate: Sun, 10 Nov 2019 18:47:51 

irqchip/gic-v3-its: Make is_v4 use a TYPER copy

Instead of caching the GICv4 compatibility in a discrete way, cache the TYPER register instead, which can then be used to implement the same functionnality. This will get used more extensively in subsequent patches.

---
 drivers/irqchip/irq-gic-v3-its.c | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +14 -12
index 6065b09..5bb3eac 100644
@@ -102,6 +102,7 @@ struct its_node {
 	struct its_collection	*collections;
 	struct fwnode_handle	*fwnode_handle;
 	u64			(*get_msi_base)(struct its_device *its_dev);
+	u64			typer;
 	u64			cbaser_save;
 	u32			ctlr_save;
 	struct list_head	its_device_list;
@@ -112,10 +113,11 @@ struct its_node {
 	int			numa_node;
 	unsigned int		msi_domain_flags;
 	u32			pre_its_base; /* for Socionext Synquacer */
-	bool			is_v4;
 	int			vlpi_redist_offset;
 };
 
+#define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
+
 #define ITS_ITT_ALIGN		SZ_256
 
 /* The maximum number of VPEID bits supported by VLPI commands */
@@ -181,7 +183,7 @@ static u16 get_its_list(struct its_vm *vm)
 	unsigned long its_list = 0;
 
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		if (vm->vlpi_count[its->list_nr])
@@ -1037,7 +1039,7 @@ static void its_send_vmovp(struct its_vpe *vpe)
 
 	/* Emit VMOVPs */
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		if (!vpe->its_vm->vlpi_count[its->list_nr])
@@ -1448,7 +1450,7 @@ static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 	struct its_cmd_info *info = vcpu_info;
 
 	/* Need a v4 ITS */
-	if (!its_dev->its->is_v4)
+	if (!is_v4(its_dev->its))
 		return -EINVAL;
 
 	/* Unmap request? */
@@ -2412,7 +2414,7 @@ static bool its_alloc_vpe_table(u32 vpe_id)
 	list_for_each_entry(its, &its_nodes, entry) {
 		struct its_baser *baser;
 
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		baser = its_get_baser(its, GITS_BASER_TYPE_VCPU);
@@ -2900,7 +2902,7 @@ static void its_vpe_invall(struct its_vpe *vpe)
 	struct its_node *its;
 
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr])
@@ -3168,7 +3170,7 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain,
 	vpe->col_idx = cpumask_first(cpu_online_mask);
 
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		its_send_vmapp(its, vpe, true);
@@ -3194,7 +3196,7 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain,
 		return;
 
 	list_for_each_entry(its, &its_nodes, entry) {
-		if (!its->is_v4)
+		if (!is_v4(its))
 			continue;
 
 		its_send_vmapp(its, vpe, false);
@@ -3632,12 +3634,12 @@ static int __init its_probe_one(struct resource *res,
 	INIT_LIST_HEAD(&its->entry);
 	INIT_LIST_HEAD(&its->its_device_list);
 	typer = gic_read_typer(its_base + GITS_TYPER);
+	its->typer = typer;
 	its->base = its_base;
 	its->phys_base = res->start;
 	its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer);
 	its->device_ids = GITS_TYPER_DEVBITS(typer);
-	its->is_v4 = !!(typer & GITS_TYPER_VLPIS);
-	if (its->is_v4) {
+	if (is_v4(its)) {
 		if (!(typer & GITS_TYPER_VMOVP)) {
 			err = its_compute_its_list_map(res, its_base);
 			if (err < 0)
@@ -3704,7 +3706,7 @@ static int __init its_probe_one(struct resource *res,
 	gits_write_cwriter(0, its->base + GITS_CWRITER);
 	ctlr = readl_relaxed(its->base + GITS_CTLR);
 	ctlr |= GITS_CTLR_ENABLE;
-	if (its->is_v4)
+	if (is_v4(its))
 		ctlr |= GITS_CTLR_ImDe;
 	writel_relaxed(ctlr, its->base + GITS_CTLR);
 
@@ -4029,7 +4031,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 		return err;
 
 	list_for_each_entry(its, &its_nodes, entry)
-		has_v4 |= its->is_v4;
+		has_v4 |= is_v4(its);
 
 	if (has_v4 & rdists->has_vlpis) {
 		if (its_init_vpe_domain() ||
author maintainer
#15 lore raw

PATCH v2 5/36 irqchip/gic-v3-its: Kill its->ite_size and use TYPER copy instead

In reply to Marc Zyngier

Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->ite_size, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure.

---
 drivers/irqchip/irq-gic-v3-its.c   | 8 ++++----
 include/linux/irqchip/arm-gic-v3.h | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +4 -4
index b3c9bc0c3ecd..3b046181ddfc 100644
@@ -6,6 +6,7 @@
 
 #include <linux/acpi.h>
 #include <linux/acpi_iort.h>
+#include <linux/bitfield.h>
 #include <linux/bitmap.h>
 #include <linux/cpu.h>
 #include <linux/crash_dump.h>
@@ -108,7 +109,6 @@ struct its_node {
 	struct list_head	its_device_list;
 	u64			flags;
 	unsigned long		list_nr;
-	u32			ite_size;
 	u32			device_ids;
 	int			numa_node;
 	unsigned int		msi_domain_flags;
@@ -2435,7 +2435,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
 	 * sized as a power of two (and you need at least one bit...).
 	 */
 	nr_ites = max(2, nvecs);
-	sz = nr_ites * its->ite_size;
+	sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1);
 	sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
 	itt = kzalloc_node(sz, GFP_KERNEL, its->numa_node);
 	if (alloc_lpis) {
@@ -3250,7 +3250,8 @@ static bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data)
 	struct its_node *its = data;
 
 	/* On QDF2400, the size of the ITE is 16Bytes */
-	its->ite_size = 16;
+	its->typer &= ~GITS_TYPER_ITT_ENTRY_SIZE;
+	its->typer |= FIELD_PREP(GITS_TYPER_ITT_ENTRY_SIZE, 16 - 1);
 
 	return true;
 }
@@ -3619,7 +3620,6 @@ static int __init its_probe_one(struct resource *res,
 	its->typer = typer;
 	its->base = its_base;
 	its->phys_base = res->start;
-	its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer);
 	its->device_ids = GITS_TYPER_DEVBITS(typer);
 	if (is_v4(its)) {
 		if (!(typer & GITS_TYPER_VMOVP)) {
include/linux/irqchip/arm-gic-v3.h +1 -1
index 5cc10cf7cb3e..4bce7a904075 100644
@@ -334,7 +334,7 @@
 #define GITS_TYPER_PLPIS		(1UL << 0)
 #define GITS_TYPER_VLPIS		(1UL << 1)
 #define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT	4
-#define GITS_TYPER_ITT_ENTRY_SIZE(r)	((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0xf) + 1)
+#define GITS_TYPER_ITT_ENTRY_SIZE	GENMASK_ULL(7, 4)
 #define GITS_TYPER_IDBITS_SHIFT		8
 #define GITS_TYPER_DEVBITS_SHIFT	13
 #define GITS_TYPER_DEVBITS(r)		((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 5/36
#16 lore raw

Re: [PATCH v2 05/36] irqchip/gic-v3-its: Kill its->ite_size and use TYPER copy instead

Quoted 6 lines · Marc Zyngier

Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->ite_size, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure.

merge bot Applied
#17 lore raw

[tip: irq/core] irqchip/gic-v3-its: Kill its->ite_size and use TYPER copy instead

In reply to Marc Zyngier
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     ffedbf0cba153c91a0da5d1280a5e639664c5ab3
Gitweb:        https://git.kernel.org/tip/ffedbf0cba153c91a0da5d1280a5e639664c5ab3
Author:        Marc Zyngier <[email protected]>
AuthorDate:    Fri, 08 Nov 2019 16:57:59 
Committer:     Marc Zyngier <[email protected]>
CommitterDate: Sun, 10 Nov 2019 18:47:52 

irqchip/gic-v3-its: Kill its->ite_size and use TYPER copy instead

Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->ite_size, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure.

---
 drivers/irqchip/irq-gic-v3-its.c   | 8 ++++----
 include/linux/irqchip/arm-gic-v3.h | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +4 -4
index 5bb3eac..c3a1e47 100644
@@ -6,6 +6,7 @@
 
 #include <linux/acpi.h>
 #include <linux/acpi_iort.h>
+#include <linux/bitfield.h>
 #include <linux/bitmap.h>
 #include <linux/cpu.h>
 #include <linux/crash_dump.h>
@@ -108,7 +109,6 @@ struct its_node {
 	struct list_head	its_device_list;
 	u64			flags;
 	unsigned long		list_nr;
-	u32			ite_size;
 	u32			device_ids;
 	int			numa_node;
 	unsigned int		msi_domain_flags;
@@ -2453,7 +2453,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
 	 * sized as a power of two (and you need at least one bit...).
 	 */
 	nr_ites = max(2, nvecs);
-	sz = nr_ites * its->ite_size;
+	sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1);
 	sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
 	itt = kzalloc_node(sz, GFP_KERNEL, its->numa_node);
 	if (alloc_lpis) {
@@ -3268,7 +3268,8 @@ static bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data)
 	struct its_node *its = data;
 
 	/* On QDF2400, the size of the ITE is 16Bytes */
-	its->ite_size = 16;
+	its->typer &= ~GITS_TYPER_ITT_ENTRY_SIZE;
+	its->typer |= FIELD_PREP(GITS_TYPER_ITT_ENTRY_SIZE, 16 - 1);
 
 	return true;
 }
@@ -3637,7 +3638,6 @@ static int __init its_probe_one(struct resource *res,
 	its->typer = typer;
 	its->base = its_base;
 	its->phys_base = res->start;
-	its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer);
 	its->device_ids = GITS_TYPER_DEVBITS(typer);
 	if (is_v4(its)) {
 		if (!(typer & GITS_TYPER_VMOVP)) {
include/linux/irqchip/arm-gic-v3.h +1 -1
index 5cc10cf..4bce7a9 100644
@@ -334,7 +334,7 @@
 #define GITS_TYPER_PLPIS		(1UL << 0)
 #define GITS_TYPER_VLPIS		(1UL << 1)
 #define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT	4
-#define GITS_TYPER_ITT_ENTRY_SIZE(r)	((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0xf) + 1)
+#define GITS_TYPER_ITT_ENTRY_SIZE	GENMASK_ULL(7, 4)
 #define GITS_TYPER_IDBITS_SHIFT		8
 #define GITS_TYPER_DEVBITS_SHIFT	13
 #define GITS_TYPER_DEVBITS(r)		((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
author maintainer
#18 lore raw

PATCH v2 6/36 irqchip/gic-v3-its: Kill its->device_ids and use TYPER copy instead

In reply to Marc Zyngier

Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->device_ids, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure.

---
 drivers/irqchip/irq-gic-v3-its.c   | 24 +++++++++++++-----------
 include/linux/irqchip/arm-gic-v3.h |  2 +-
 2 files changed, 14 insertions(+), 12 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +13 -11
index 3b046181ddfc..6c91c7feadf3 100644
@@ -109,7 +109,6 @@ struct its_node {
 	struct list_head	its_device_list;
 	u64			flags;
 	unsigned long		list_nr;
-	u32			device_ids;
 	int			numa_node;
 	unsigned int		msi_domain_flags;
 	u32			pre_its_base; /* for Socionext Synquacer */
@@ -117,6 +116,7 @@ struct its_node {
 };
 
 #define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
+#define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
 
 #define ITS_ITT_ALIGN		SZ_256
 
@@ -1938,9 +1938,9 @@ static bool its_parse_indirect_baser(struct its_node *its,
 	if (new_order >= MAX_ORDER) {
 		new_order = MAX_ORDER - 1;
 		ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz);
-		pr_warn("ITS@%pa: %s Table too large, reduce ids %u->%u\n",
+		pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n",
 			&its->phys_base, its_base_type_string[type],
-			its->device_ids, ids);
+			device_ids(its), ids);
 	}
 
 	*order = new_order;
@@ -1986,7 +1986,7 @@ static int its_alloc_tables(struct its_node *its)
 		case GITS_BASER_TYPE_DEVICE:
 			indirect = its_parse_indirect_baser(its, baser,
 							    psz, &order,
-							    its->device_ids);
+							    device_ids(its));
 			break;
 
 		case GITS_BASER_TYPE_VCPU:
@@ -2377,7 +2377,7 @@ static bool its_alloc_device_table(struct its_node *its, u32 dev_id)
 
 	/* Don't allow device id that exceeds ITS hardware limit */
 	if (!baser)
-		return (ilog2(dev_id) < its->device_ids);
+		return (ilog2(dev_id) < device_ids(its));
 
 	return its_alloc_table_entry(its, baser, dev_id);
 }
@@ -3229,8 +3229,9 @@ static bool __maybe_unused its_enable_quirk_cavium_22375(void *data)
 {
 	struct its_node *its = data;
 
-	/* erratum 22375: only alloc 8MB table size */
-	its->device_ids = 0x14;		/* 20 bits, 8MB */
+	/* erratum 22375: only alloc 8MB table size (20 bits) */
+	its->typer &= ~GITS_TYPER_DEVBITS;
+	its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, 20 - 1);
 	its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
 
 	return true;
@@ -3285,8 +3286,10 @@ static bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data)
 		its->get_msi_base = its_irq_get_msi_base_pre_its;
 
 		ids = ilog2(pre_its_window[1]) - 2;
-		if (its->device_ids > ids)
-			its->device_ids = ids;
+		if (device_ids(its) > ids) {
+			its->typer &= ~GITS_TYPER_DEVBITS;
+			its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, ids - 1);
+		}
 
 		/* the pre-ITS breaks isolation, so disable MSI remapping */
 		its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP;
@@ -3519,7 +3522,7 @@ static int its_init_vpe_domain(void)
 	}
 
 	/* Use the last possible DevID */
-	devid = GENMASK(its->device_ids - 1, 0);
+	devid = GENMASK(device_ids(its) - 1, 0);
 	vpe_proxy.dev = its_create_device(its, devid, entries, false);
 	if (!vpe_proxy.dev) {
 		kfree(vpe_proxy.vpes);
@@ -3620,7 +3623,6 @@ static int __init its_probe_one(struct resource *res,
 	its->typer = typer;
 	its->base = its_base;
 	its->phys_base = res->start;
-	its->device_ids = GITS_TYPER_DEVBITS(typer);
 	if (is_v4(its)) {
 		if (!(typer & GITS_TYPER_VMOVP)) {
 			err = its_compute_its_list_map(res, its_base);
include/linux/irqchip/arm-gic-v3.h +1 -1
index 4bce7a904075..b6514e8893bf 100644
@@ -337,7 +337,7 @@
 #define GITS_TYPER_ITT_ENTRY_SIZE	GENMASK_ULL(7, 4)
 #define GITS_TYPER_IDBITS_SHIFT		8
 #define GITS_TYPER_DEVBITS_SHIFT	13
-#define GITS_TYPER_DEVBITS(r)		((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
+#define GITS_TYPER_DEVBITS		GENMASK_ULL(17, 13)
 #define GITS_TYPER_PTA			(1UL << 19)
 #define GITS_TYPER_HCC_SHIFT		24
 #define GITS_TYPER_HCC(r)		(((r) >> GITS_TYPER_HCC_SHIFT) & 0xff)
Signature · 6 lines
2.20.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
reviewer Reviewed-by on v2 6/36
#19 lore raw

Re: [PATCH v2 06/36] irqchip/gic-v3-its: Kill its->device_ids and use TYPER copy instead

Hi Marc,

Quoted 6 lines · Marc Zyngier

Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->device_ids, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure.

Quoted 26 lines
---
  drivers/irqchip/irq-gic-v3-its.c   | 24 +++++++++++++-----------
  include/linux/irqchip/arm-gic-v3.h |  2 +-
  2 files changed, 14 insertions(+), 12 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +1 -1
index 3b046181ddfc..6c91c7feadf3 100644
@@ -109,7 +109,6 @@ struct its_node {
  	struct list_head	its_device_list;
  	u64			flags;
  	unsigned long		list_nr;
-	u32			device_ids;
  	int			numa_node;
  	unsigned int		msi_domain_flags;
  	u32			pre_its_base; /* for Socionext Synquacer */
@@ -117,6 +116,7 @@ struct its_node {
  };
 
  #define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
+#define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
 
  #define ITS_ITT_ALIGN		SZ_256
 

drivers/irqchip/irq-gic-v3-its.c static bool its_parse_indirect_baser(struct its_node *its, · line 1943

Quoted 8 lines
drivers/irqchip/irq-gic-v3-its.c +2 -2
@@ -1938,9 +1938,9 @@ static bool its_parse_indirect_baser(struct its_node *its,
  	if (new_order >= MAX_ORDER) {
  		new_order = MAX_ORDER - 1;
  		ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz);
-		pr_warn("ITS@%pa: %s Table too large, reduce ids %u->%u\n",
+		pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n",
  			&its->phys_base, its_base_type_string[type],
-			its->device_ids, ids);
+			device_ids(its), ids);

But this pr_warn() looks a bit odd. The table type is chosen from its_base_type_string[], but ids is always Devbits (+1)?

Thanks,
Zenghui

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

author maintainer
#20 lore raw

Re: [PATCH v2 06/36] irqchip/gic-v3-its: Kill its->device_ids and use TYPER copy instead

Hi Zenghui,

Quoted 4 lines · Zenghui Yu

Hi Marc,

On 2019/10/27 22:42, Marc Zyngier wrote:

Quoted 8 lines
Quoted 6 lines

Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->device_ids, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure.

Thanks for that.

Quoted 29 lines
Quoted 29 lines
---
  drivers/irqchip/irq-gic-v3-its.c   | 24 +++++++++++++-----------
  include/linux/irqchip/arm-gic-v3.h |  2 +-
  2 files changed, 14 insertions(+), 12 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +1 -1
index 3b046181ddfc..6c91c7feadf3 100644
@@ -109,7 +109,6 @@ struct its_node {
  	struct list_head	its_device_list;
  	u64			flags;
  	unsigned long		list_nr;
-	u32			device_ids;
  	int			numa_node;
  	unsigned int		msi_domain_flags;
  	u32			pre_its_base; /* for Socionext Synquacer */
@@ -117,6 +116,7 @@ struct its_node {
  };
    #define is_v4(its)		(!!((its)->typer &
 GITS_TYPER_VLPIS))
+#define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
    #define ITS_ITT_ALIGN		SZ_256
  @@ -1938,9 +1938,9 @@ static bool its_parse_indirect_baser(struct
 its_node *its,
  	if (new_order >= MAX_ORDER) {
  		new_order = MAX_ORDER - 1;
  		ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz);
Quoted 8 lines

drivers/irqchip/irq-gic-v3-its.c static bool its_parse_indirect_baser(struct its_node *its, · line 1943

Quoted 5 lines
drivers/irqchip/irq-gic-v3-its.c +2 -2
-		pr_warn("ITS@%pa: %s Table too large, reduce ids %u->%u\n",
+		pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n",
  			&its->phys_base, its_base_type_string[type],
-			its->device_ids, ids);
+			device_ids(its), ids);

But this pr_warn() looks a bit odd. The table type is chosen from its_base_type_string[], but ids is always Devbits (+1)?

This is a bit of a shortcut, I agree. But the device table practically is the only one where we can run out of space if the ITS doesn't support two level tables. All the other tables are very small, being limited by the number of CPUs (collections) or a small ID space (vPEs).

So while this is a bit ugly, I don't thing it is not too concerning.

Thanks,

	M.
Signature · 5 lines
Jazz is not dead, it just smells funny.
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
reviewer
#21 lore raw

Re: [PATCH v2 06/36] irqchip/gic-v3-its: Kill its->device_ids and use TYPER copy instead

Quoted 57 lines · Marc Zyngier

Hi Zenghui,

Quoted 4 lines · Zenghui Yu

Hi Marc,

On 2019/10/27 22:42, Marc Zyngier wrote:

Quoted 8 lines
Quoted 6 lines

Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->device_ids, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure.

Thanks for that.

Quoted 29 lines
Quoted 29 lines
---
   drivers/irqchip/irq-gic-v3-its.c   | 24 +++++++++++++-----------
   include/linux/irqchip/arm-gic-v3.h |  2 +-
   2 files changed, 14 insertions(+), 12 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +1 -1
index 3b046181ddfc..6c91c7feadf3 100644
@@ -109,7 +109,6 @@ struct its_node {
   	struct list_head	its_device_list;
   	u64			flags;
   	unsigned long		list_nr;
-	u32			device_ids;
   	int			numa_node;
   	unsigned int		msi_domain_flags;
   	u32			pre_its_base; /* for Socionext Synquacer */
@@ -117,6 +116,7 @@ struct its_node {
   };
     #define is_v4(its)		(!!((its)->typer &
 GITS_TYPER_VLPIS))
+#define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
     #define ITS_ITT_ALIGN		SZ_256
   @@ -1938,9 +1938,9 @@ static bool its_parse_indirect_baser(struct
 its_node *its,
   	if (new_order >= MAX_ORDER) {
   		new_order = MAX_ORDER - 1;
   		ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz);
Quoted 8 lines

drivers/irqchip/irq-gic-v3-its.c static bool its_parse_indirect_baser(struct its_node *its, · line 1943

Quoted 5 lines
drivers/irqchip/irq-gic-v3-its.c +2 -2
-		pr_warn("ITS@%pa: %s Table too large, reduce ids %u->%u\n",
+		pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n",
   			&its->phys_base, its_base_type_string[type],
-			its->device_ids, ids);
+			device_ids(its), ids);

But this pr_warn() looks a bit odd. The table type is chosen from its_base_type_string[], but ids is always Devbits (+1)?

Quoted 5 lines

This is a bit of a shortcut, I agree. But the device table practically is the only one where we can run out of space if the ITS doesn't support two level tables. All the other tables are very small, being limited by the number of CPUs (collections) or a small ID space (vPEs).

Make sense. Thanks for the clarification.

Zenghui

Quoted 7 lines

So while this is a bit ugly, I don't thing it is not too concerning.

Thanks,

	M.
merge bot Applied
#22 lore raw

[tip: irq/core] irqchip/gic-v3-its: Kill its->device_ids and use TYPER copy instead

In reply to Marc Zyngier
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     576a83429757999f220f36f206044af2b9026672
Gitweb:        https://git.kernel.org/tip/576a83429757999f220f36f206044af2b9026672
Author:        Marc Zyngier <[email protected]>
AuthorDate:    Fri, 08 Nov 2019 16:58:00 
Committer:     Marc Zyngier <[email protected]>
CommitterDate: Sun, 10 Nov 2019 18:47:52 

irqchip/gic-v3-its: Kill its->device_ids and use TYPER copy instead

Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->device_ids, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure.

---
 drivers/irqchip/irq-gic-v3-its.c   | 24 +++++++++++++-----------
 include/linux/irqchip/arm-gic-v3.h |  2 +-
 2 files changed, 14 insertions(+), 12 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +13 -11
index c3a1e47..e8aeb07 100644
@@ -109,7 +109,6 @@ struct its_node {
 	struct list_head	its_device_list;
 	u64			flags;
 	unsigned long		list_nr;
-	u32			device_ids;
 	int			numa_node;
 	unsigned int		msi_domain_flags;
 	u32			pre_its_base; /* for Socionext Synquacer */
@@ -117,6 +116,7 @@ struct its_node {
 };
 
 #define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
+#define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
 
 #define ITS_ITT_ALIGN		SZ_256
 
@@ -1956,9 +1956,9 @@ static bool its_parse_indirect_baser(struct its_node *its,
 	if (new_order >= MAX_ORDER) {
 		new_order = MAX_ORDER - 1;
 		ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz);
-		pr_warn("ITS@%pa: %s Table too large, reduce ids %u->%u\n",
+		pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n",
 			&its->phys_base, its_base_type_string[type],
-			its->device_ids, ids);
+			device_ids(its), ids);
 	}
 
 	*order = new_order;
@@ -2004,7 +2004,7 @@ static int its_alloc_tables(struct its_node *its)
 		case GITS_BASER_TYPE_DEVICE:
 			indirect = its_parse_indirect_baser(its, baser,
 							    psz, &order,
-							    its->device_ids);
+							    device_ids(its));
 			break;
 
 		case GITS_BASER_TYPE_VCPU:
@@ -2395,7 +2395,7 @@ static bool its_alloc_device_table(struct its_node *its, u32 dev_id)
 
 	/* Don't allow device id that exceeds ITS hardware limit */
 	if (!baser)
-		return (ilog2(dev_id) < its->device_ids);
+		return (ilog2(dev_id) < device_ids(its));
 
 	return its_alloc_table_entry(its, baser, dev_id);
 }
@@ -3247,8 +3247,9 @@ static bool __maybe_unused its_enable_quirk_cavium_22375(void *data)
 {
 	struct its_node *its = data;
 
-	/* erratum 22375: only alloc 8MB table size */
-	its->device_ids = 0x14;		/* 20 bits, 8MB */
+	/* erratum 22375: only alloc 8MB table size (20 bits) */
+	its->typer &= ~GITS_TYPER_DEVBITS;
+	its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, 20 - 1);
 	its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
 
 	return true;
@@ -3303,8 +3304,10 @@ static bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data)
 		its->get_msi_base = its_irq_get_msi_base_pre_its;
 
 		ids = ilog2(pre_its_window[1]) - 2;
-		if (its->device_ids > ids)
-			its->device_ids = ids;
+		if (device_ids(its) > ids) {
+			its->typer &= ~GITS_TYPER_DEVBITS;
+			its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, ids - 1);
+		}
 
 		/* the pre-ITS breaks isolation, so disable MSI remapping */
 		its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP;
@@ -3537,7 +3540,7 @@ static int its_init_vpe_domain(void)
 	}
 
 	/* Use the last possible DevID */
-	devid = GENMASK(its->device_ids - 1, 0);
+	devid = GENMASK(device_ids(its) - 1, 0);
 	vpe_proxy.dev = its_create_device(its, devid, entries, false);
 	if (!vpe_proxy.dev) {
 		kfree(vpe_proxy.vpes);
@@ -3638,7 +3641,6 @@ static int __init its_probe_one(struct resource *res,
 	its->typer = typer;
 	its->base = its_base;
 	its->phys_base = res->start;
-	its->device_ids = GITS_TYPER_DEVBITS(typer);
 	if (is_v4(its)) {
 		if (!(typer & GITS_TYPER_VMOVP)) {
 			err = its_compute_its_list_map(res, its_base);
include/linux/irqchip/arm-gic-v3.h +1 -1
index 4bce7a9..b6514e8 100644
@@ -337,7 +337,7 @@
 #define GITS_TYPER_ITT_ENTRY_SIZE	GENMASK_ULL(7, 4)
 #define GITS_TYPER_IDBITS_SHIFT		8
 #define GITS_TYPER_DEVBITS_SHIFT	13
-#define GITS_TYPER_DEVBITS(r)		((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
+#define GITS_TYPER_DEVBITS		GENMASK_ULL(17, 13)
 #define GITS_TYPER_PTA			(1UL << 19)
 #define GITS_TYPER_HCC_SHIFT		24
 #define GITS_TYPER_HCC(r)		(((r) >> GITS_TYPER_HCC_SHIFT) & 0xff)
author maintainer
#23 lore raw

PATCH v2 7/36 irqchip/gic-v3-its: Add get_vlpi_map() helper

In reply to Marc Zyngier

Obtaining the mapping information for a VLPI is something quite common, and the GICv4.1 code is going to make even more use of it. Expose it as a separate helper.

---
 drivers/irqchip/irq-gic-v3-its.c | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +16 -11
index 6c91c7feadf3..94c9c2e9f917 100644
@@ -1045,20 +1045,26 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
 /*
  * irqchip functions - assumes MSI, mostly.
  */
+static struct its_vlpi_map *get_vlpi_map(struct irq_data *d)
+{
+	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+	u32 event = its_get_event_id(d);
+
+	if (!irqd_is_forwarded_to_vcpu(d))
+		return NULL;
+
+	return &its_dev->event_map.vlpi_maps[event];
+}
 
 static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 {
+	struct its_vlpi_map *map = get_vlpi_map(d);
 	irq_hw_number_t hwirq;
 	void *va;
 	u8 *cfg;
 
-	if (irqd_is_forwarded_to_vcpu(d)) {
-		struct its_device *its_dev = irq_data_get_irq_chip_data(d);
-		u32 event = its_get_event_id(d);
-		struct its_vlpi_map *map;
-
-		va = page_address(its_dev->event_map.vm->vprop_page);
-		map = &its_dev->event_map.vlpi_maps[event];
+	if (map) {
+		va = page_address(map->vm->vprop_page);
 		hwirq = map->vintid;
 
 		/* Remember the updated property */
@@ -1351,19 +1357,18 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info)
 static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
-	u32 event = its_get_event_id(d);
+	struct its_vlpi_map *map = get_vlpi_map(d);
 	int ret = 0;
 
 	mutex_lock(&its_dev->event_map.vlpi_lock);
 
-	if (!its_dev->event_map.vm ||
-	    !its_dev->event_map.vlpi_maps[event].vm) {
+	if (!its_dev->event_map.vm || !map->vm) {
 		ret = -EINVAL;
 		goto out;
 	}
 
 	/* Copy our mapping information to the incoming request */
-	*info->map = its_dev->event_map.vlpi_maps[event];
+	*info->map = *map;
 
 out:
 	mutex_unlock(&its_dev->event_map.vlpi_lock);
Signature · 6 lines
2.20.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
reviewer Reviewed-by on v2 7/36
#24 lore raw

Re: [PATCH v2 07/36] irqchip/gic-v3-its: Add get_vlpi_map() helper

Quoted 5 lines · Marc Zyngier

Obtaining the mapping information for a VLPI is something quite common, and the GICv4.1 code is going to make even more use of it. Expose it as a separate helper.

merge bot Applied
#25 lore raw

[tip: irq/core] irqchip/gic-v3-its: Add its_vlpi_map helpers

In reply to Marc Zyngier
The following commit has been merged into the irq/core branch of tip:

The following commit has been merged into the irq/core branch of tip:

Commit-ID:     c1d4d5cd203cc8ec83d67d4e2af51f1a9f01ba34
Gitweb:        https://git.kernel.org/tip/c1d4d5cd203cc8ec83d67d4e2af51f1a9f01ba34
Author:        Marc Zyngier <[email protected]>
AuthorDate:    Fri, 08 Nov 2019 16:58:01 
Committer:     Marc Zyngier <[email protected]>
CommitterDate: Sun, 10 Nov 2019 18:47:52 

irqchip/gic-v3-its: Add its_vlpi_map helpers

Obtaining the mapping information for a VLPI is something quite common, and the GICv4.1 code is going to make even more use of it. Expose it as a separate set of helpers.

---
 drivers/irqchip/irq-gic-v3-its.c | 47 +++++++++++++++++++++----------
 1 file changed, 32 insertions(+), 15 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +32 -15
index e8aeb07..e8d088c 100644
@@ -207,6 +207,15 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 	return its->collections + its_dev->event_map.col_map[event];
 }
 
+static struct its_vlpi_map *dev_event_to_vlpi_map(struct its_device *its_dev,
+					       u32 event)
+{
+	if (WARN_ON_ONCE(event >= its_dev->event_map.nr_lpis))
+		return NULL;
+
+	return &its_dev->event_map.vlpi_maps[event];
+}
+
 static struct its_collection *irq_to_col(struct irq_data *d)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
@@ -971,7 +980,7 @@ static void its_send_invall(struct its_node *its, struct its_collection *col)
 
 static void its_send_vmapti(struct its_device *dev, u32 id)
 {
-	struct its_vlpi_map *map = &dev->event_map.vlpi_maps[id];
+	struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id);
 	struct its_cmd_desc desc;
 
 	desc.its_vmapti_cmd.vpe = map->vpe;
@@ -985,7 +994,7 @@ static void its_send_vmapti(struct its_device *dev, u32 id)
 
 static void its_send_vmovi(struct its_device *dev, u32 id)
 {
-	struct its_vlpi_map *map = &dev->event_map.vlpi_maps[id];
+	struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id);
 	struct its_cmd_desc desc;
 
 	desc.its_vmovi_cmd.vpe = map->vpe;
@@ -1063,20 +1072,26 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
 /*
  * irqchip functions - assumes MSI, mostly.
  */
+static struct its_vlpi_map *get_vlpi_map(struct irq_data *d)
+{
+	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+	u32 event = its_get_event_id(d);
+
+	if (!irqd_is_forwarded_to_vcpu(d))
+		return NULL;
+
+	return dev_event_to_vlpi_map(its_dev, event);
+}
 
 static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 {
+	struct its_vlpi_map *map = get_vlpi_map(d);
 	irq_hw_number_t hwirq;
 	void *va;
 	u8 *cfg;
 
-	if (irqd_is_forwarded_to_vcpu(d)) {
-		struct its_device *its_dev = irq_data_get_irq_chip_data(d);
-		u32 event = its_get_event_id(d);
-		struct its_vlpi_map *map;
-
-		va = page_address(its_dev->event_map.vm->vprop_page);
-		map = &its_dev->event_map.vlpi_maps[event];
+	if (map) {
+		va = page_address(map->vm->vprop_page);
 		hwirq = map->vintid;
 
 		/* Remember the updated property */
@@ -1136,11 +1151,14 @@ static void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 	u32 event = its_get_event_id(d);
+	struct its_vlpi_map *map;
 
-	if (its_dev->event_map.vlpi_maps[event].db_enabled == enable)
+	map = dev_event_to_vlpi_map(its_dev, event);
+
+	if (map->db_enabled == enable)
 		return;
 
-	its_dev->event_map.vlpi_maps[event].db_enabled = enable;
+	map->db_enabled = enable;
 
 	/*
 	 * More fun with the architecture:
@@ -1369,19 +1387,18 @@ out:
 static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
-	u32 event = its_get_event_id(d);
+	struct its_vlpi_map *map = get_vlpi_map(d);
 	int ret = 0;
 
 	mutex_lock(&its_dev->event_map.vlpi_lock);
 
-	if (!its_dev->event_map.vm ||
-	    !its_dev->event_map.vlpi_maps[event].vm) {
+	if (!its_dev->event_map.vm || !map->vm) {
 		ret = -EINVAL;
 		goto out;
 	}
 
 	/* Copy our mapping information to the incoming request */
-	*info->map = its_dev->event_map.vlpi_maps[event];
+	*info->map = *map;
 
 out:
 	mutex_unlock(&its_dev->event_map.vlpi_lock);
author maintainer
#26 lore raw

PATCH v2 8/36 irqchip/gic-v3: Detect GICv4.1 supporting RVPEID

In reply to Marc Zyngier

GICv4.1 supports the RVPEID ("Residency per vPE ID"), which allows for a much efficient way of making virtual CPUs resident (to allow direct injection of interrupts).

The functionnality needs to be discovered on each and every redistributor in the system, and disabled if the settings are inconsistent.

---
 drivers/irqchip/irq-gic-v3.c       | 21 ++++++++++++++++++---
 include/linux/irqchip/arm-gic-v3.h |  2 ++
 2 files changed, 20 insertions(+), 3 deletions(-)
drivers/irqchip/irq-gic-v3.c +18 -3
index 1edc99335a94..4f20caf9bc88 100644
@@ -849,8 +849,21 @@ static int __gic_update_rdist_properties(struct redist_region *region,
 					 void __iomem *ptr)
 {
 	u64 typer = gic_read_typer(ptr + GICR_TYPER);
+
 	gic_data.rdists.has_vlpis &= !!(typer & GICR_TYPER_VLPIS);
-	gic_data.rdists.has_direct_lpi &= !!(typer & GICR_TYPER_DirectLPIS);
+
+	/* RVPEID implies some form of DirectLPI, no matter what the doc says... :-/ */
+	gic_data.rdists.has_rvpeid &= !!(typer & GICR_TYPER_RVPEID);
+	gic_data.rdists.has_direct_lpi &= (!!(typer & GICR_TYPER_DirectLPIS) |
+					   gic_data.rdists.has_rvpeid);
+
+	/* Detect non-sensical configurations */
+	if (WARN_ON_ONCE(gic_data.rdists.has_rvpeid && !gic_data.rdists.has_vlpis)) {
+		gic_data.rdists.has_direct_lpi = false;
+		gic_data.rdists.has_vlpis = false;
+		gic_data.rdists.has_rvpeid = false;
+	}
+
 	gic_data.ppi_nr = min(GICR_TYPER_NR_PPIS(typer), gic_data.ppi_nr);
 
 	return 1;
@@ -863,9 +876,10 @@ static void gic_update_rdist_properties(void)
 	if (WARN_ON(gic_data.ppi_nr == UINT_MAX))
 		gic_data.ppi_nr = 0;
 	pr_info("%d PPIs implemented\n", gic_data.ppi_nr);
-	pr_info("%sVLPI support, %sdirect LPI support\n",
+	pr_info("%sVLPI support, %sdirect LPI support, %sRVPEID support\n",
 		!gic_data.rdists.has_vlpis ? "no " : "",
-		!gic_data.rdists.has_direct_lpi ? "no " : "");
+		!gic_data.rdists.has_direct_lpi ? "no " : "",
+		!gic_data.rdists.has_rvpeid ? "no " : "");
 }
 
 /* Check whether it's single security state view */
@@ -1546,6 +1560,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
 						 &gic_data);
 	irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);
 	gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
+	gic_data.rdists.has_rvpeid = true;
 	gic_data.rdists.has_vlpis = true;
 	gic_data.rdists.has_direct_lpi = true;
 
include/linux/irqchip/arm-gic-v3.h +2 -0
index b6514e8893bf..c98f34296599 100644
@@ -234,6 +234,7 @@
 #define GICR_TYPER_VLPIS		(1U << 1)
 #define GICR_TYPER_DirectLPIS		(1U << 3)
 #define GICR_TYPER_LAST			(1U << 4)
+#define GICR_TYPER_RVPEID		(1U << 7)
 
 #define GIC_V3_REDIST_SIZE		0x20000
 
@@ -613,6 +614,7 @@ struct rdists {
 	u64			flags;
 	u32			gicd_typer;
 	bool			has_vlpis;
+	bool			has_rvpeid;
 	bool			has_direct_lpi;
 };
 
Signature · 6 lines
2.20.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
reviewer Reviewed-by on v2 8/36
#27 lore raw

Re: [PATCH v2 08/36] irqchip/gic-v3: Detect GICv4.1 supporting RVPEID

Hi Marc,

Quoted 8 lines · Marc Zyngier

GICv4.1 supports the RVPEID ("Residency per vPE ID"), which allows for a much efficient way of making virtual CPUs resident (to allow direct injection of interrupts).

The functionnality needs to be discovered on each and every redistributor in the system, and disabled if the settings are inconsistent.

author maintainer
#28 lore raw

PATCH v2 9/36 irqchip/gic-v3: Add GICv4.1 VPEID size discovery

In reply to Marc Zyngier

While GICv4.0 mandates 16 bit worth of VPEIDs, GICv4.1 allows smaller implementations to be built. Add the required glue to dynamically compute the limit.

---
 drivers/irqchip/irq-gic-v3-its.c   | 11 ++++++++++-
 drivers/irqchip/irq-gic-v3.c       |  3 +++
 include/linux/irqchip/arm-gic-v3.h |  5 +++++
 3 files changed, 18 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +10 -1
index 94c9c2e9f917..40912b3fb0e1 100644
@@ -121,7 +121,16 @@ struct its_node {
 #define ITS_ITT_ALIGN		SZ_256
 
 /* The maximum number of VPEID bits supported by VLPI commands */
-#define ITS_MAX_VPEID_BITS	(16)
+#define ITS_MAX_VPEID_BITS						\
+	({								\
+		int nvpeid = 16;					\
+		if (gic_rdists->has_rvpeid &&				\
+		    gic_rdists->gicd_typer2 & GICD_TYPER2_VIL)		\
+			nvpeid = 1 + (gic_rdists->gicd_typer2 &		\
+				      GICD_TYPER2_VID);			\
+									\
+		nvpeid;							\
+	})
 #define ITS_MAX_VPEID		(1 << (ITS_MAX_VPEID_BITS))
 
 /* Convert page order to size in bytes */
drivers/irqchip/irq-gic-v3.c +3 -0
index 4f20caf9bc88..50538709bd49 100644
@@ -1556,6 +1556,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
 
 	pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);
 	pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);
+
+	gic_data.rdists.gicd_typer2 = readl_relaxed(gic_data.dist_base + GICD_TYPER2);
+
 	gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
 						 &gic_data);
 	irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);
include/linux/irqchip/arm-gic-v3.h +5 -0
index c98f34296599..8c6be56da7e9 100644
@@ -13,6 +13,7 @@
 #define GICD_CTLR			0x0000
 #define GICD_TYPER			0x0004
 #define GICD_IIDR			0x0008
+#define GICD_TYPER2			0x000C
 #define GICD_STATUSR			0x0010
 #define GICD_SETSPI_NSR			0x0040
 #define GICD_CLRSPI_NSR			0x0048
@@ -89,6 +90,9 @@
 #define GICD_TYPER_ESPIS(typer)						\
 	(((typer) & GICD_TYPER_ESPI) ? GICD_TYPER_SPIS((typer) >> 27) : 0)
 
+#define GICD_TYPER2_VIL			(1U << 7)
+#define GICD_TYPER2_VID			GENMASK(4, 0)
+
 #define GICD_IROUTER_SPI_MODE_ONE	(0U << 31)
 #define GICD_IROUTER_SPI_MODE_ANY	(1U << 31)
 
@@ -613,6 +617,7 @@ struct rdists {
 	void			*prop_table_va;
 	u64			flags;
 	u32			gicd_typer;
+	u32			gicd_typer2;
 	bool			has_vlpis;
 	bool			has_rvpeid;
 	bool			has_direct_lpi;
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 9/36
#29 lore raw

Re: [PATCH v2 09/36] irqchip/gic-v3: Add GICv4.1 VPEID size discovery

Hi Marc,

Quoted 17 lines · Marc Zyngier

While GICv4.0 mandates 16 bit worth of VPEIDs, GICv4.1 allows smaller implementations to be built. Add the required glue to dynamically compute the limit.

---
  drivers/irqchip/irq-gic-v3-its.c   | 11 ++++++++++-
  drivers/irqchip/irq-gic-v3.c       |  3 +++
  include/linux/irqchip/arm-gic-v3.h |  5 +++++
  3 files changed, 18 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +0 -0
index 94c9c2e9f917..40912b3fb0e1 100644
@@ -121,7 +121,16 @@ struct its_node {
  #define ITS_ITT_ALIGN		SZ_256

drivers/irqchip/irq-gic-v3-its.c struct its_node { · line 130

Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +7 -1
 
  /* The maximum number of VPEID bits supported by VLPI commands */
-#define ITS_MAX_VPEID_BITS	(16)
+#define ITS_MAX_VPEID_BITS						\
+	({								\
+		int nvpeid = 16;					\
+		if (gic_rdists->has_rvpeid &&				\
+		    gic_rdists->gicd_typer2 & GICD_TYPER2_VIL)		\
+			nvpeid = 1 + (gic_rdists->gicd_typer2 &		\
+				      GICD_TYPER2_VID);			\

Does it make sense to let nvpeid not more than 16 here? As the spec says "Values above 0x0F are RESERVED". But I don't know why should we have this restriction ;-)

Either way,

Thanks

Quoted 51 lines
drivers/irqchip/irq-gic-v3-its.c +3 -0
+									\
+		nvpeid;							\
+	})
  #define ITS_MAX_VPEID		(1 << (ITS_MAX_VPEID_BITS))
 
  /* Convert page order to size in bytes */
drivers/irqchip/irq-gic-v3.c +3 -0
index 4f20caf9bc88..50538709bd49 100644
@@ -1556,6 +1556,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
 
  	pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);
  	pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);
+
+	gic_data.rdists.gicd_typer2 = readl_relaxed(gic_data.dist_base + GICD_TYPER2);
+
  	gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
  						 &gic_data);
  	irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);
include/linux/irqchip/arm-gic-v3.h +5 -0
index c98f34296599..8c6be56da7e9 100644
@@ -13,6 +13,7 @@
  #define GICD_CTLR			0x0000
  #define GICD_TYPER			0x0004
  #define GICD_IIDR			0x0008
+#define GICD_TYPER2			0x000C
  #define GICD_STATUSR			0x0010
  #define GICD_SETSPI_NSR			0x0040
  #define GICD_CLRSPI_NSR			0x0048
@@ -89,6 +90,9 @@
  #define GICD_TYPER_ESPIS(typer)						\
  	(((typer) & GICD_TYPER_ESPI) ? GICD_TYPER_SPIS((typer) >> 27) : 0)
 
+#define GICD_TYPER2_VIL			(1U << 7)
+#define GICD_TYPER2_VID			GENMASK(4, 0)
+
  #define GICD_IROUTER_SPI_MODE_ONE	(0U << 31)
  #define GICD_IROUTER_SPI_MODE_ANY	(1U << 31)
 
@@ -613,6 +617,7 @@ struct rdists {
  	void			*prop_table_va;
  	u64			flags;
  	u32			gicd_typer;
+	u32			gicd_typer2;
  	bool			has_vlpis;
  	bool			has_rvpeid;
  	bool			has_direct_lpi;
author maintainer
#30 lore raw

Re: [PATCH v2 09/36] irqchip/gic-v3: Add GICv4.1 VPEID size discovery

Quoted 27 lines · Zenghui Yu

Hi Marc,

Quoted 23 lines · Marc Zyngier

While GICv4.0 mandates 16 bit worth of VPEIDs, GICv4.1 allows smaller implementations to be built. Add the required glue to dynamically compute the limit.

---
  drivers/irqchip/irq-gic-v3-its.c   | 11 ++++++++++-
  drivers/irqchip/irq-gic-v3.c       |  3 +++
  include/linux/irqchip/arm-gic-v3.h |  5 +++++
  3 files changed, 18 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +3 -1
index 94c9c2e9f917..40912b3fb0e1 100644
@@ -121,7 +121,16 @@ struct its_node {
  #define ITS_ITT_ALIGN		SZ_256
    /* The maximum number of VPEID bits supported by VLPI commands
 */
-#define ITS_MAX_VPEID_BITS	(16)
+#define ITS_MAX_VPEID_BITS						\
+	({								\
+		int nvpeid = 16;					\
Quoted 8 lines

drivers/irqchip/irq-gic-v3-its.c struct its_node { · line 130

Quoted 4 lines
drivers/irqchip/irq-gic-v3-its.c +4 -0
+		if (gic_rdists->has_rvpeid &&				\
+		    gic_rdists->gicd_typer2 & GICD_TYPER2_VIL)		\
+			nvpeid = 1 + (gic_rdists->gicd_typer2 &		\
+				      GICD_TYPER2_VID);			\

Does it make sense to let nvpeid not more than 16 here? As the spec says "Values above 0x0F are RESERVED". But I don't know why should we have this restriction ;-)

This is something that Andrew raised in the previous version. My take on this is that properly implemented HW should give us something that matches the spec (in the 0-15 range), and I don't think that guarding against that is useful at this stage.

My understanding is that once the architecture allows using the 16-31 range, we'll either use it directly because it is just a natural extension to the existing range, or they mean something different altogether and we'll get a new discovery bit somewhere.

Quoted 3 lines

Either way,

Thanks,

	M.
Signature · 1 line
Jazz is not dead, it just smells funny.
author maintainer
#31 lore raw

PATCH v2 10/36 irqchip/gic-v3: Workaround Cavium TX1 erratum when reading GICD_TYPER2

In reply to Marc Zyngier

Despite the architecture spec being extremely clear about the fact that reserved registers in the GIC distributor memory map are RES0 (and thus are not allowed to generate an exception), the Cavium ThunderX (aka TX1) SoC explodes as such:

[    0.000000] GICv3: GIC: Using split EOI/Deactivate mode
[    0.000000] GICv3: 128 SPIs implemented
[    0.000000] GICv3: 0 Extended SPIs implemented
[    0.000000] Internal error: synchronous external abort: 96000210 [#1] SMP
[    0.000000] Modules linked in:
[    0.000000] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.4.0-rc4-00035-g3cf6a3d5725f #7956
[    0.000000] Hardware name: cavium,thunder-88xx (DT)
[    0.000000] pstate: 60000085 (nZCv daIf -PAN -UAO)
[    0.000000] pc : __raw_readl+0x0/0x8
[    0.000000] lr : gic_init_bases+0x110/0x560
[    0.000000] sp : ffff800011243d90
[    0.000000] x29: ffff800011243d90 x28: 0000000000000000
[    0.000000] x27: 0000000000000018 x26: 0000000000000002
[    0.000000] x25: ffff8000116f0000 x24: ffff000fbe6a2c80
[    0.000000] x23: 0000000000000000 x22: ffff010fdc322b68
[    0.000000] x21: ffff800010a7a208 x20: 00000000009b0404
[    0.000000] x19: ffff80001124dad0 x18: 0000000000000010
[    0.000000] x17: 000000004d8d492b x16: 00000000f67eb9af
[    0.000000] x15: ffffffffffffffff x14: ffff800011249908
[    0.000000] x13: ffff800091243ae7 x12: ffff800011243af4
[    0.000000] x11: ffff80001126e000 x10: ffff800011243a70
[    0.000000] x9 : 00000000ffffffd0 x8 : ffff80001069c828
[    0.000000] x7 : 0000000000000059 x6 : ffff8000113fb4d1
[    0.000000] x5 : 0000000000000001 x4 : 0000000000000000
[    0.000000] x3 : 0000000000000000 x2 : 0000000000000000
[    0.000000] x1 : 0000000000000000 x0 : ffff8000116f000c
[    0.000000] Call trace:
[    0.000000]  __raw_readl+0x0/0x8
[    0.000000]  gic_of_init+0x188/0x224
[    0.000000]  of_irq_init+0x200/0x3cc
[    0.000000]  irqchip_init+0x1c/0x40
[    0.000000]  init_IRQ+0x160/0x1d0
[    0.000000]  start_kernel+0x2ec/0x4b8
[    0.000000] Code: a8c47bfd d65f03c0 d538d080 d65f03c0 (b9400000)

when reading the GICv4.1 GICD_TYPER2 register, which is unexpected...

Work around it by adding a new quirk flagging all the A1 revisions of the distributor, but it remains unknown whether this could affect other revisions of this SoC (or even other SoCs from the same silicon vendor).

---
 drivers/irqchip/irq-gic-v3.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3.c +22 -1
index 50538709bd49..f0d33ac64a99 100644
@@ -34,6 +34,7 @@
 #define GICD_INT_NMI_PRI	(GICD_INT_DEF_PRI & ~0x80)
 
 #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996	(1ULL << 0)
+#define FLAGS_WORKAROUND_GICD_TYPER2_TX1	(1ULL << 1)
 
 struct redist_region {
 	void __iomem		*redist_base;
@@ -1455,6 +1456,15 @@ static bool gic_enable_quirk_msm8996(void *data)
 	return true;
 }
 
+static bool gic_enable_quirk_tx1(void *data)
+{
+	struct gic_chip_data *d = data;
+
+	d->flags |= FLAGS_WORKAROUND_GICD_TYPER2_TX1;
+
+	return true;
+}
+
 static bool gic_enable_quirk_hip06_07(void *data)
 {
 	struct gic_chip_data *d = data;
@@ -1493,6 +1503,12 @@ static const struct gic_quirk gic_quirks[] = {
 		.mask	= 0xffffffff,
 		.init	= gic_enable_quirk_hip06_07,
 	},
+	{
+		.desc	= "GICv3: Cavium TX1 GICD_TYPER2 erratum",
+		.iidr	= 0xa100034c,
+		.mask	= 0xfff00fff,
+		.init	= gic_enable_quirk_tx1,
+	},
 	{
 	}
 };
@@ -1557,7 +1573,12 @@ static int __init gic_init_bases(void __iomem *dist_base,
 	pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);
 	pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);
 
-	gic_data.rdists.gicd_typer2 = readl_relaxed(gic_data.dist_base + GICD_TYPER2);
+	/*
+	 * ThunderX1 explodes on reading GICD_TYPER2, in total violation
+	 * of the spec (which says that reserved addresses are RES0).
+	 */
+	if (!(gic_data.flags & FLAGS_WORKAROUND_GICD_TYPER2_TX1))
+		gic_data.rdists.gicd_typer2 = readl_relaxed(gic_data.dist_base + GICD_TYPER2);
 
 	gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
 						 &gic_data);
Signature · 1 line
2.20.1
merge bot Applied
#32 lore raw

[tip: irq/urgent] irqchip/gic-v3: Workaround Cavium erratum 38539 when reading GICD_TYPER2

The following commit has been merged into the irq/urgent branch of tip:

The following commit has been merged into the irq/urgent branch of tip:

Commit-ID:     d01fd161e85904064290435f67f4ed59af5daf74
Gitweb:        https://git.kernel.org/tip/d01fd161e85904064290435f67f4ed59af5daf74
Author:        Marc Zyngier <[email protected]>
AuthorDate:    Wed, 11 Mar 2020 11:56:49 
Committer:     Marc Zyngier <[email protected]>
CommitterDate: Sat, 14 Mar 2020 10:15:19 

irqchip/gic-v3: Workaround Cavium erratum 38539 when reading GICD_TYPER2

Despite the architecture spec requiring that reserved registers in the GIC distributor memory map are RES0 (and thus are not allowed to generate an exception), the Cavium ThunderX (aka TX1) SoC explodes as such:

[    0.000000] GICv3: GIC: Using split EOI/Deactivate mode
[    0.000000] GICv3: 128 SPIs implemented
[    0.000000] GICv3: 0 Extended SPIs implemented
[    0.000000] Internal error: synchronous external abort: 96000210 [#1] SMP
[    0.000000] Modules linked in:
[    0.000000] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.4.0-rc4-00035-g3cf6a3d5725f #7956
[    0.000000] Hardware name: cavium,thunder-88xx (DT)
[    0.000000] pstate: 60000085 (nZCv daIf -PAN -UAO)
[    0.000000] pc : __raw_readl+0x0/0x8
[    0.000000] lr : gic_init_bases+0x110/0x560
[    0.000000] sp : ffff800011243d90
[    0.000000] x29: ffff800011243d90 x28: 0000000000000000
[    0.000000] x27: 0000000000000018 x26: 0000000000000002
[    0.000000] x25: ffff8000116f0000 x24: ffff000fbe6a2c80
[    0.000000] x23: 0000000000000000 x22: ffff010fdc322b68
[    0.000000] x21: ffff800010a7a208 x20: 00000000009b0404
[    0.000000] x19: ffff80001124dad0 x18: 0000000000000010
[    0.000000] x17: 000000004d8d492b x16: 00000000f67eb9af
[    0.000000] x15: ffffffffffffffff x14: ffff800011249908
[    0.000000] x13: ffff800091243ae7 x12: ffff800011243af4
[    0.000000] x11: ffff80001126e000 x10: ffff800011243a70
[    0.000000] x9 : 00000000ffffffd0 x8 : ffff80001069c828
[    0.000000] x7 : 0000000000000059 x6 : ffff8000113fb4d1
[    0.000000] x5 : 0000000000000001 x4 : 0000000000000000
[    0.000000] x3 : 0000000000000000 x2 : 0000000000000000
[    0.000000] x1 : 0000000000000000 x0 : ffff8000116f000c
[    0.000000] Call trace:
[    0.000000]  __raw_readl+0x0/0x8
[    0.000000]  gic_of_init+0x188/0x224
[    0.000000]  of_irq_init+0x200/0x3cc
[    0.000000]  irqchip_init+0x1c/0x40
[    0.000000]  init_IRQ+0x160/0x1d0
[    0.000000]  start_kernel+0x2ec/0x4b8
[    0.000000] Code: a8c47bfd d65f03c0 d538d080 d65f03c0 (b9400000)

when reading the GICv4.1 GICD_TYPER2 register, which is unexpected...

Work around it by adding a new quirk for the following variants:

ThunderX: CN88xx
OCTEON TX: CN83xx, CN81xx
OCTEON TX2: CN93xx, CN96xx, CN98xx, CNF95xx*

and use this flag to avoid accessing GICD_TYPER2. Note that all reserved registers (including redistributors and ITS) are impacted by this erratum, but that only GICD_TYPER2 has to be worked around so far.

---
 Documentation/arm64/silicon-errata.rst |  2 ++-
 drivers/irqchip/irq-gic-v3.c           | 30 ++++++++++++++++++++++++-
 2 files changed, 31 insertions(+), 1 deletion(-)
Documentation/arm64/silicon-errata.rst +2 -0
index 99b2545..a1563da 100644
@@ -108,6 +108,8 @@ stable kernels.
 +----------------+-----------------+-----------------+-----------------------------+
 | Cavium         | ThunderX GICv3  | #23154          | CAVIUM_ERRATUM_23154        |
 +----------------+-----------------+-----------------+-----------------------------+
+| Cavium         | ThunderX GICv3  | #38539          | N/A                         |
++----------------+-----------------+-----------------+-----------------------------+
 | Cavium         | ThunderX Core   | #27456          | CAVIUM_ERRATUM_27456        |
 +----------------+-----------------+-----------------+-----------------------------+
 | Cavium         | ThunderX Core   | #30115          | CAVIUM_ERRATUM_30115        |
drivers/irqchip/irq-gic-v3.c +29 -1
index c1f7af9..1eec9d4 100644
@@ -34,6 +34,7 @@
 #define GICD_INT_NMI_PRI	(GICD_INT_DEF_PRI & ~0x80)
 
 #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996	(1ULL << 0)
+#define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539	(1ULL << 1)
 
 struct redist_region {
 	void __iomem		*redist_base;
@@ -1464,6 +1465,15 @@ static bool gic_enable_quirk_msm8996(void *data)
 	return true;
 }
 
+static bool gic_enable_quirk_cavium_38539(void *data)
+{
+	struct gic_chip_data *d = data;
+
+	d->flags |= FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539;
+
+	return true;
+}
+
 static bool gic_enable_quirk_hip06_07(void *data)
 {
 	struct gic_chip_data *d = data;
@@ -1503,6 +1513,19 @@ static const struct gic_quirk gic_quirks[] = {
 		.init	= gic_enable_quirk_hip06_07,
 	},
 	{
+		/*
+		 * Reserved register accesses generate a Synchronous
+		 * External Abort. This erratum applies to:
+		 * - ThunderX: CN88xx
+		 * - OCTEON TX: CN83xx, CN81xx
+		 * - OCTEON TX2: CN93xx, CN96xx, CN98xx, CNF95xx*
+		 */
+		.desc	= "GICv3: Cavium erratum 38539",
+		.iidr	= 0xa000034c,
+		.mask	= 0xe8f00fff,
+		.init	= gic_enable_quirk_cavium_38539,
+	},
+	{
 	}
 };
 
@@ -1577,7 +1600,12 @@ static int __init gic_init_bases(void __iomem *dist_base,
 	pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);
 	pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);
 
-	gic_data.rdists.gicd_typer2 = readl_relaxed(gic_data.dist_base + GICD_TYPER2);
+	/*
+	 * ThunderX1 explodes on reading GICD_TYPER2, in violation of the
+	 * architecture spec (which says that reserved registers are RES0).
+	 */
+	if (!(gic_data.flags & FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539))
+		gic_data.rdists.gicd_typer2 = readl_relaxed(gic_data.dist_base + GICD_TYPER2);
 
 	gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
 						 &gic_data);
author maintainer
#33 lore raw

PATCH v2 11/36 irqchip/gic-v4.1: VPE table (aka GICR_VPROPBASER) allocation

In reply to Marc Zyngier

GICv4.1 defines a new VPE table that is potentially shared between both the ITSs and the redistributors, following complicated affinity rules.

To make things more confusing, the programming of this table at the redistributor level is reusing the GICv4.0 GICR_VPROPBASER register for something completely different.

The code flow is somewhat complexified by the need to respect the affinities required by the HW, meaning that tables can either be inherited from a previously discovered ITS or redistributor.

---
 arch/arm/include/asm/arch_gicv3.h   |   2 +
 arch/arm64/include/asm/arch_gicv3.h |   1 +
 drivers/irqchip/irq-gic-v3-its.c    | 305 +++++++++++++++++++++++++++-
 include/linux/irqchip/arm-gic-v3.h  |  33 ++-
 4 files changed, 334 insertions(+), 7 deletions(-)
arch/arm/include/asm/arch_gicv3.h +2 -0
index 0555f14cc8be..20bacab1d791 100644
@@ -10,6 +10,7 @@
 #ifndef __ASSEMBLY__
 
 #include <linux/io.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
 #include <asm/barrier.h>
 #include <asm/cacheflush.h>
 #include <asm/cp15.h>
@@ -327,6 +328,7 @@ static inline u64 __gic_readq_nonatomic(const volatile void __iomem *addr)
 /*
  * GITS_VPROPBASER - hi and lo bits may be accessed independently.
  */
+#define gits_read_vpropbaser(c)		__gic_readq_nonatomic(c)
 #define gits_write_vpropbaser(v, c)	__gic_writeq_nonatomic(v, c)
 
 /*
arch/arm64/include/asm/arch_gicv3.h +1 -0
index 89e4c8b79349..4750fc8030c3 100644
@@ -141,6 +141,7 @@ static inline u32 gic_read_rpr(void)
 #define gicr_read_pendbaser(c)		readq_relaxed(c)
 
 #define gits_write_vpropbaser(v, c)	writeq_relaxed(v, c)
+#define gits_read_vpropbaser(c)		readq_relaxed(c)
 
 #define gits_write_vpendbaser(v, c)	writeq_relaxed(v, c)
 #define gits_read_vpendbaser(c)		readq_relaxed(c)
drivers/irqchip/irq-gic-v3-its.c +302 -3

371 lines not on this page · raw message

include/linux/irqchip/arm-gic-v3.h +29 -4
index 8c6be56da7e9..f1d6de53e09b 100644
@@ -102,6 +102,11 @@
 
 #define GIC_V3_DIST_SIZE		0x10000
 
+#define GIC_PAGE_SIZE_4K		0ULL
+#define GIC_PAGE_SIZE_16K		1ULL
+#define GIC_PAGE_SIZE_64K		2ULL
+#define GIC_PAGE_SIZE_MASK		3ULL
+
 /*
  * Re-Distributor registers, offsets from RD_base
  */
@@ -239,6 +244,8 @@
 #define GICR_TYPER_DirectLPIS		(1U << 3)
 #define GICR_TYPER_LAST			(1U << 4)
 #define GICR_TYPER_RVPEID		(1U << 7)
+#define GICR_TYPER_COMMON_LPI_AFF	GENMASK_ULL(25, 24)
+#define GICR_TYPER_AFFINITY		GENMASK_ULL(63, 32)
 
 #define GIC_V3_REDIST_SIZE		0x20000
 
@@ -277,6 +284,18 @@
 #define GICR_VPROPBASER_RaWaWt	GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWt)
 #define GICR_VPROPBASER_RaWaWb	GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWb)
 
+/*
+ * GICv4.1 VPROPBASER reinvention. A subtle mix between the old
+ * VPROPBASER and ITS_BASER. Just not quite any of the two.
+ */
+#define GICR_VPROPBASER_4_1_VALID	(1ULL << 63)
+#define GICR_VPROPBASER_4_1_ENTRY_SIZE	GENMASK_ULL(61, 59)
+#define GICR_VPROPBASER_4_1_INDIRECT	(1ULL << 55)
+#define GICR_VPROPBASER_4_1_PAGE_SIZE	GENMASK_ULL(54, 53)
+#define GICR_VPROPBASER_4_1_Z		(1ULL << 52)
+#define GICR_VPROPBASER_4_1_ADDR	GENMASK_ULL(51, 12)
+#define GICR_VPROPBASER_4_1_SIZE	GENMASK_ULL(6, 0)
+
 #define GICR_VPENDBASER			0x0078
 
 #define GICR_VPENDBASER_SHAREABILITY_SHIFT		(10)
@@ -314,6 +333,7 @@
 #define GITS_CTLR			0x0000
 #define GITS_IIDR			0x0004
 #define GITS_TYPER			0x0008
+#define GITS_MPIDR			0x0018
 #define GITS_CBASER			0x0080
 #define GITS_CWRITER			0x0088
 #define GITS_CREADR			0x0090
@@ -347,6 +367,8 @@
 #define GITS_TYPER_HCC_SHIFT		24
 #define GITS_TYPER_HCC(r)		(((r) >> GITS_TYPER_HCC_SHIFT) & 0xff)
 #define GITS_TYPER_VMOVP		(1ULL << 37)
+#define GITS_TYPER_VMAPP		(1ULL << 40)
+#define GITS_TYPER_SVPET		GENMASK_ULL(42, 41)
 
 #define GITS_IIDR_REV_SHIFT		12
 #define GITS_IIDR_REV_MASK		(0xf << GITS_IIDR_REV_SHIFT)
@@ -417,10 +439,11 @@
 #define GITS_BASER_InnerShareable					\
 	GIC_BASER_SHAREABILITY(GITS_BASER, InnerShareable)
 #define GITS_BASER_PAGE_SIZE_SHIFT	(8)
-#define GITS_BASER_PAGE_SIZE_4K		(0ULL << GITS_BASER_PAGE_SIZE_SHIFT)
-#define GITS_BASER_PAGE_SIZE_16K	(1ULL << GITS_BASER_PAGE_SIZE_SHIFT)
-#define GITS_BASER_PAGE_SIZE_64K	(2ULL << GITS_BASER_PAGE_SIZE_SHIFT)
-#define GITS_BASER_PAGE_SIZE_MASK	(3ULL << GITS_BASER_PAGE_SIZE_SHIFT)
+#define __GITS_BASER_PSZ(sz)		(GIC_PAGE_SIZE_ ## sz << GITS_BASER_PAGE_SIZE_SHIFT)
+#define GITS_BASER_PAGE_SIZE_4K		__GITS_BASER_PSZ(4K)
+#define GITS_BASER_PAGE_SIZE_16K	__GITS_BASER_PSZ(16K)
+#define GITS_BASER_PAGE_SIZE_64K	__GITS_BASER_PSZ(64K)
+#define GITS_BASER_PAGE_SIZE_MASK	__GITS_BASER_PSZ(MASK)
 #define GITS_BASER_PAGES_MAX		256
 #define GITS_BASER_PAGES_SHIFT		(0)
 #define GITS_BASER_NR_PAGES(r)		(((r) & 0xff) + 1)
@@ -610,8 +633,10 @@ struct rdists {
 	struct {
 		void __iomem	*rd_base;
 		struct page	*pend_page;
+		struct page	*vpe_l1_page;
 		phys_addr_t	phys_base;
 		bool		lpi_enabled;
+		cpumask_t	*vpe_table_mask;
 	} __percpu		*rdist;
 	phys_addr_t		prop_table_pa;
 	void			*prop_table_va;
Signature · 1 line
2.20.1
reviewer
#34 lore raw

Re: [PATCH v2 11/36] irqchip/gic-v4.1: VPE table (aka GICR_VPROPBASER) allocation

Hi Marc,

[ +Wudi and Nianyao. As they spotted the following issue but
I forgot to send it out. ]

Quoted 18 lines · Marc Zyngier

GICv4.1 defines a new VPE table that is potentially shared between both the ITSs and the redistributors, following complicated affinity rules.

To make things more confusing, the programming of this table at the redistributor level is reusing the GICv4.0 GICR_VPROPBASER register for something completely different.

The code flow is somewhat complexified by the need to respect the affinities required by the HW, meaning that tables can either be inherited from a previously discovered ITS or redistributor.

---[...]

drivers/irqchip/irq-gic-v3-its.c +0 -0
index 40912b3fb0e1..478d3678850c 100644

[...]

Quoted 28 lines
drivers/irqchip/irq-gic-v3-its.c +24 -0
@@ -2025,6 +2098,214 @@ static int its_alloc_tables(struct its_node *its)
  	return 0;
  }
 
+static u64 inherit_vpe_l1_table_from_its(void)
+{
+	struct its_node *its;
+	u64 val;
+	u32 aff;
+
+	val = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER);
+	aff = compute_common_aff(val);
+
+	list_for_each_entry(its, &its_nodes, entry) {
+		u64 baser;
+
+		if (!is_v4_1(its))
+			continue;
+
+		if (!FIELD_GET(GITS_TYPER_SVPET, its->typer))
+			continue;
+
+		if (aff != compute_its_aff(its))
+			continue;
+
+		/* GICv4.1 guarantees that the vPE table is GITS_BASER2 */
+		baser = its->tables[2].val;
+		if (!(baser & GITS_BASER_VALID))

drivers/irqchip/irq-gic-v3-its.c static int its_alloc_tables(struct its_node *its) · line 2134

Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +10 -0
+			continue;
+
+		/* We have a winner! */
+		val  = GICR_VPROPBASER_4_1_VALID;
+		if (baser & GITS_BASER_INDIRECT)
+			val |= GICR_VPROPBASER_4_1_INDIRECT;
+		val |= FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE,
+				  FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser));
+		val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR,
+				  GITS_BASER_ADDR_48_to_52(baser) >> 12);

We've used GITS_BASER_ADDR_48_to_52() only in the KVM code where the pagesize of ITS table is fixed to 64K.
It may not work when the pagesize is 4K or 16K?

Thanks,
Zenghui

Quoted 11 lines
drivers/irqchip/irq-gic-v3-its.c +11 -0
+		val |= FIELD_PREP(GICR_VPROPBASER_SHAREABILITY_MASK,
+				  FIELD_GET(GITS_BASER_SHAREABILITY_MASK, baser));
+		val |= FIELD_PREP(GICR_VPROPBASER_INNER_CACHEABILITY_MASK,
+				  FIELD_GET(GITS_BASER_INNER_CACHEABILITY_MASK, baser));
+		val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, GITS_BASER_NR_PAGES(baser) - 1);
+
+		return val;
+	}
+
+	return 0;
+}
author maintainer
#35 lore raw

Re: [PATCH v2 11/36] irqchip/gic-v4.1: VPE table (aka GICR_VPROPBASER) allocation

Hi Zenghui,

Quoted 56 lines · Zenghui Yu

Hi Marc,

[ +Wudi and Nianyao. As they spotted the following issue but
I forgot to send it out. ]

Quoted 15 lines · Marc Zyngier

GICv4.1 defines a new VPE table that is potentially shared between both the ITSs and the redistributors, following complicated affinity rules.
To make things more confusing, the programming of this table at the redistributor level is reusing the GICv4.0 GICR_VPROPBASER register for something completely different.
The code flow is somewhat complexified by the need to respect the affinities required by the HW, meaning that tables can either be inherited from a previously discovered ITS or redistributor.

---[...]

drivers/irqchip/irq-gic-v3-its.c +0 -0
index 40912b3fb0e1..478d3678850c 100644

[...]

Quoted 34 lines
drivers/irqchip/irq-gic-v3-its.c +29 -0
@@ -2025,6 +2098,214 @@ static int its_alloc_tables(struct its_node 
 *its)
  	return 0;
  }
  +static u64 inherit_vpe_l1_table_from_its(void)
+{
+	struct its_node *its;
+	u64 val;
+	u32 aff;
+
+	val = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER);
+	aff = compute_common_aff(val);
+
+	list_for_each_entry(its, &its_nodes, entry) {
+		u64 baser;
+
+		if (!is_v4_1(its))
+			continue;
+
+		if (!FIELD_GET(GITS_TYPER_SVPET, its->typer))
+			continue;
+
+		if (aff != compute_its_aff(its))
+			continue;
+
+		/* GICv4.1 guarantees that the vPE table is GITS_BASER2 */
+		baser = its->tables[2].val;
+		if (!(baser & GITS_BASER_VALID))
+			continue;
+
+		/* We have a winner! */
+		val  = GICR_VPROPBASER_4_1_VALID;
+		if (baser & GITS_BASER_INDIRECT)
+			val |= GICR_VPROPBASER_4_1_INDIRECT;
Quoted 8 lines

drivers/irqchip/irq-gic-v3-its.c static int its_alloc_tables(struct its_node · line 2134

Quoted 4 lines
drivers/irqchip/irq-gic-v3-its.c +4 -0
+		val |= FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE,
+				  FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser));
+		val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR,
+				  GITS_BASER_ADDR_48_to_52(baser) >> 12);

We've used GITS_BASER_ADDR_48_to_52() only in the KVM code where the pagesize of ITS table is fixed to 64K.
It may not work when the pagesize is 4K or 16K?

You're absolutely right, we shouldn't mess with the 52bit macros when PZ isn't set to 64k. I'm adding the following fix to this patch:

drivers/irqchip/irq-gic-v3-its.c +10 -3
index e1f8d5f9a0e3..3234bb9fbdbe 100644
@@ -2433,7 +2433,7 @@ static u64 inherit_vpe_l1_table_from_its(void)
 	aff = compute_common_aff(val);
 
 	list_for_each_entry(its, &its_nodes, entry) {
-		u64 baser;
+		u64 baser, addr;
 
 		if (!is_v4_1(its))
 			continue;
@@ -2455,8 +2455,15 @@ static u64 inherit_vpe_l1_table_from_its(void)
 			val |= GICR_VPROPBASER_4_1_INDIRECT;
 		val |= FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE,
 				  FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser));
-		val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR,
-				  GITS_BASER_ADDR_48_to_52(baser) >> 12);
+		switch (FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser)) {
+		case GIC_PAGE_SIZE_64K:
+			addr = GITS_BASER_ADDR_48_to_52(baser);
+			break;
+		default:
+			addr = baser & GENMASK_ULL(47, 12);
+			break;
+		}
+		val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR, addr >> 12);
 		val |= FIELD_PREP(GICR_VPROPBASER_SHAREABILITY_MASK,
 				  FIELD_GET(GITS_BASER_SHAREABILITY_MASK, baser));
 		val |= FIELD_PREP(GICR_VPROPBASER_INNER_CACHEABILITY_MASK,

Thanks again,

        M.
Signature · 1 line
Jazz is not dead. It just smells funny...
author maintainer
#36 lore raw

PATCH v2 12/36 irqchip/gic-v4.1: Implement the v4.1 flavour of VMAPP

In reply to Marc Zyngier

The ITS VMAPP command gains some new fields with GICv4.1:
- a default doorbell, which allows a single doorbell to be used for all the VLPIs routed to a given VPE
- a pointer to the configuration table (instead of having it in a register that gets context switched)
- a flag indicating whether this is the first map or the last unmap for this particulat VPE
- a flag indicating whether the pending table is known to be zeroed, or not

Plumb in the new fields in the VMAPP builder, and add the map/unmap refcounting so that the ITS can do the right thing.

---
 drivers/irqchip/irq-gic-v3-its.c   | 60 +++++++++++++++++++++++++++---
 include/linux/irqchip/arm-gic-v4.h | 18 +++++++--
 2 files changed, 69 insertions(+), 9 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +55 -5
index 478d3678850c..220d490d516e 100644
@@ -422,6 +422,27 @@ static void its_encode_vpt_size(struct its_cmd_block *cmd, u8 vpt_size)
 	its_mask_encode(&cmd->raw_cmd[3], vpt_size, 4, 0);
 }
 
+static void its_encode_vconf_addr(struct its_cmd_block *cmd, u64 vconf_pa)
+{
+	its_mask_encode(&cmd->raw_cmd[0], vconf_pa >> 16, 51, 16);
+}
+
+static void its_encode_alloc(struct its_cmd_block *cmd, bool alloc)
+{
+	its_mask_encode(&cmd->raw_cmd[0], alloc, 8, 8);
+}
+
+static void its_encode_ptz(struct its_cmd_block *cmd, bool ptz)
+{
+	its_mask_encode(&cmd->raw_cmd[0], ptz, 9, 9);
+}
+
+static void its_encode_vmapp_default_db(struct its_cmd_block *cmd,
+					u32 vpe_db_lpi)
+{
+	its_mask_encode(&cmd->raw_cmd[1], vpe_db_lpi, 31, 0);
+}
+
 static inline void its_fixup_cmd(struct its_cmd_block *cmd)
 {
 	/* Let's fixup BE commands */
@@ -605,19 +626,45 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
 					   struct its_cmd_block *cmd,
 					   struct its_cmd_desc *desc)
 {
-	unsigned long vpt_addr;
+	unsigned long vpt_addr, vconf_addr;
 	u64 target;
-
-	vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
-	target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset;
+	bool alloc;
 
 	its_encode_cmd(cmd, GITS_CMD_VMAPP);
 	its_encode_vpeid(cmd, desc->its_vmapp_cmd.vpe->vpe_id);
 	its_encode_valid(cmd, desc->its_vmapp_cmd.valid);
+
+	if (!desc->its_vmapp_cmd.valid) {
+		if (is_v4_1(its)) {
+			alloc = !atomic_dec_return(&desc->its_vmapp_cmd.vpe->vmapp_count);
+			its_encode_alloc(cmd, alloc);
+		}
+
+		goto out;
+	}
+
+	vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
+	target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset;
+
 	its_encode_target(cmd, target);
 	its_encode_vpt_addr(cmd, vpt_addr);
 	its_encode_vpt_size(cmd, LPI_NRBITS - 1);
 
+	if (!is_v4_1(its))
+		goto out;
+
+	vconf_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->its_vm->vprop_page));
+
+	alloc = atomic_inc_and_test(&desc->its_vmapp_cmd.vpe->vmapp_count);
+
+	its_encode_alloc(cmd, alloc);
+
+	/* We can only signal PTZ when alloc==1. Why do we have two bits? */
+	its_encode_ptz(cmd, alloc);
+	its_encode_vconf_addr(cmd, vconf_addr);
+	its_encode_vmapp_default_db(cmd, desc->its_vmapp_cmd.vpe->vpe_db_lpi);
+
+out:
 	its_fixup_cmd(cmd);
 
 	return valid_vpe(its, desc->its_vmapp_cmd.vpe);
@@ -3349,7 +3396,10 @@ static int its_vpe_init(struct its_vpe *vpe)
 
 	vpe->vpe_id = vpe_id;
 	vpe->vpt_page = vpt_page;
-	vpe->vpe_proxy_event = -1;
+	if (gic_rdists->has_rvpeid)
+		atomic_set(&vpe->vmapp_count, 0);
+	else
+		vpe->vpe_proxy_event = -1;
 
 	return 0;
 }
include/linux/irqchip/arm-gic-v4.h +14 -4
index ab1396afe08a..6213ced6f199 100644
@@ -37,8 +37,20 @@ struct its_vpe {
 	irq_hw_number_t		vpe_db_lpi;
 	/* VPE resident */
 	bool			resident;
-	/* VPE proxy mapping */
-	int			vpe_proxy_event;
+	union {
+		/* GICv4.0 implementations */
+		struct {
+			/* VPE proxy mapping */
+			int	vpe_proxy_event;
+			/* Implementation Defined Area Invalid */
+			bool	idai;
+		};
+		/* GICv4.1 implementations */
+		struct {
+			atomic_t vmapp_count;
+		};
+	};
+
 	/*
 	 * This collection ID is used to indirect the target
 	 * redistributor for this VPE. The ID itself isn't involved in
@@ -47,8 +59,6 @@ struct its_vpe {
 	u16			col_idx;
 	/* Unique (system-wide) VPE identifier */
 	u16			vpe_id;
-	/* Implementation Defined Area Invalid */
-	bool			idai;
 	/* Pending VLPIs on schedule out? */
 	bool			pending_last;
 };
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 12/36
#37 lore raw

Re: [PATCH v2 12/36] irqchip/gic-v4.1: Implement the v4.1 flavour of VMAPP

Hi Marc,

Quoted 7 lines · Marc Zyngier

The ITS VMAPP command gains some new fields with GICv4.1:
- a default doorbell, which allows a single doorbell to be used for all the VLPIs routed to a given VPE
- a pointer to the configuration table (instead of having it in a register that gets context switched)
- a flag indicating whether this is the first map or the last unmap for this particulat VPE

particular

Quoted 6 lines

- a flag indicating whether the pending table is known to be zeroed, or not

Plumb in the new fields in the VMAPP builder, and add the map/unmap refcounting so that the ITS can do the right thing.

Quoted 50 lines
---
  drivers/irqchip/irq-gic-v3-its.c   | 60 +++++++++++++++++++++++++++---
  include/linux/irqchip/arm-gic-v4.h | 18 +++++++--
  2 files changed, 69 insertions(+), 9 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +23 -4
index 478d3678850c..220d490d516e 100644
@@ -422,6 +422,27 @@ static void its_encode_vpt_size(struct its_cmd_block *cmd, u8 vpt_size)
  	its_mask_encode(&cmd->raw_cmd[3], vpt_size, 4, 0);
  }
 
+static void its_encode_vconf_addr(struct its_cmd_block *cmd, u64 vconf_pa)
+{
+	its_mask_encode(&cmd->raw_cmd[0], vconf_pa >> 16, 51, 16);
+}
+
+static void its_encode_alloc(struct its_cmd_block *cmd, bool alloc)
+{
+	its_mask_encode(&cmd->raw_cmd[0], alloc, 8, 8);
+}
+
+static void its_encode_ptz(struct its_cmd_block *cmd, bool ptz)
+{
+	its_mask_encode(&cmd->raw_cmd[0], ptz, 9, 9);
+}
+
+static void its_encode_vmapp_default_db(struct its_cmd_block *cmd,
+					u32 vpe_db_lpi)
+{
+	its_mask_encode(&cmd->raw_cmd[1], vpe_db_lpi, 31, 0);
+}
+
  static inline void its_fixup_cmd(struct its_cmd_block *cmd)
  {
  	/* Let's fixup BE commands */
@@ -605,19 +626,45 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
  					   struct its_cmd_block *cmd,
  					   struct its_cmd_desc *desc)
  {
-	unsigned long vpt_addr;
+	unsigned long vpt_addr, vconf_addr;
  	u64 target;
-
-	vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
-	target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset;
+	bool alloc;
 
  	its_encode_cmd(cmd, GITS_CMD_VMAPP);

drivers/irqchip/irq-gic-v3-its.c static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, · line 643

Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +8 -0
  	its_encode_vpeid(cmd, desc->its_vmapp_cmd.vpe->vpe_id);
  	its_encode_valid(cmd, desc->its_vmapp_cmd.valid);
+
+	if (!desc->its_vmapp_cmd.valid) {
+		if (is_v4_1(its)) {
+			alloc = !atomic_dec_return(&desc->its_vmapp_cmd.vpe->vmapp_count);
+			its_encode_alloc(cmd, alloc);
+		}
+
+		goto out;

(note to myself, the reason for "goto out" here is that in GICv4.1, the remaining field are RES0 when V==0.)

Thanks,
Zenghui

Quoted 76 lines
drivers/irqchip/irq-gic-v3-its.c +24 -1
+	}
+
+	vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
+	target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset;
+
  	its_encode_target(cmd, target);
  	its_encode_vpt_addr(cmd, vpt_addr);
  	its_encode_vpt_size(cmd, LPI_NRBITS - 1);
 
+	if (!is_v4_1(its))
+		goto out;
+
+	vconf_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->its_vm->vprop_page));
+
+	alloc = atomic_inc_and_test(&desc->its_vmapp_cmd.vpe->vmapp_count);
+
+	its_encode_alloc(cmd, alloc);
+
+	/* We can only signal PTZ when alloc==1. Why do we have two bits? */
+	its_encode_ptz(cmd, alloc);
+	its_encode_vconf_addr(cmd, vconf_addr);
+	its_encode_vmapp_default_db(cmd, desc->its_vmapp_cmd.vpe->vpe_db_lpi);
+
+out:
  	its_fixup_cmd(cmd);
 
  	return valid_vpe(its, desc->its_vmapp_cmd.vpe);
@@ -3349,7 +3396,10 @@ static int its_vpe_init(struct its_vpe *vpe)
 
  	vpe->vpe_id = vpe_id;
  	vpe->vpt_page = vpt_page;
-	vpe->vpe_proxy_event = -1;
+	if (gic_rdists->has_rvpeid)
+		atomic_set(&vpe->vmapp_count, 0);
+	else
+		vpe->vpe_proxy_event = -1;
 
  	return 0;
  }
include/linux/irqchip/arm-gic-v4.h +14 -4
index ab1396afe08a..6213ced6f199 100644
@@ -37,8 +37,20 @@ struct its_vpe {
  	irq_hw_number_t		vpe_db_lpi;
  	/* VPE resident */
  	bool			resident;
-	/* VPE proxy mapping */
-	int			vpe_proxy_event;
+	union {
+		/* GICv4.0 implementations */
+		struct {
+			/* VPE proxy mapping */
+			int	vpe_proxy_event;
+			/* Implementation Defined Area Invalid */
+			bool	idai;
+		};
+		/* GICv4.1 implementations */
+		struct {
+			atomic_t vmapp_count;
+		};
+	};
+
  	/*
  	 * This collection ID is used to indirect the target
  	 * redistributor for this VPE. The ID itself isn't involved in
@@ -47,8 +59,6 @@ struct its_vpe {
  	u16			col_idx;
  	/* Unique (system-wide) VPE identifier */
  	u16			vpe_id;
-	/* Implementation Defined Area Invalid */
-	bool			idai;
  	/* Pending VLPIs on schedule out? */
  	bool			pending_last;
  };
reviewer
#38 lore raw

Re: [PATCH v2 12/36] irqchip/gic-v4.1: Implement the v4.1 flavour of VMAPP

In reply to Marc Zyngier

Hi Marc,

Quoted 13 lines · Marc Zyngier

The ITS VMAPP command gains some new fields with GICv4.1:
- a default doorbell, which allows a single doorbell to be used for all the VLPIs routed to a given VPE
- a pointer to the configuration table (instead of having it in a register that gets context switched)
- a flag indicating whether this is the first map or the last unmap for this particulat VPE
- a flag indicating whether the pending table is known to be zeroed, or not

Plumb in the new fields in the VMAPP builder, and add the map/unmap refcounting so that the ITS can do the right thing.

[...]

Quoted 28 lines
drivers/irqchip/irq-gic-v3-its.c +15 -4
@@ -605,19 +626,45 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
  					   struct its_cmd_block *cmd,
  					   struct its_cmd_desc *desc)
  {
-	unsigned long vpt_addr;
+	unsigned long vpt_addr, vconf_addr;
  	u64 target;
-
-	vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
-	target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset;
+	bool alloc;
 
  	its_encode_cmd(cmd, GITS_CMD_VMAPP);
  	its_encode_vpeid(cmd, desc->its_vmapp_cmd.vpe->vpe_id);
  	its_encode_valid(cmd, desc->its_vmapp_cmd.valid);
+
+	if (!desc->its_vmapp_cmd.valid) {
+		if (is_v4_1(its)) {
+			alloc = !atomic_dec_return(&desc->its_vmapp_cmd.vpe->vmapp_count);
+			its_encode_alloc(cmd, alloc);
+		}
+
+		goto out;
+	}
+
+	vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
+	target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset;
+

drivers/irqchip/irq-gic-v3-its.c static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, · line 658

Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +6 -0
  	its_encode_target(cmd, target);
  	its_encode_vpt_addr(cmd, vpt_addr);
  	its_encode_vpt_size(cmd, LPI_NRBITS - 1);
 
+	if (!is_v4_1(its))
+		goto out;
+
+	vconf_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->its_vm->vprop_page));
+
+	alloc = atomic_inc_and_test(&desc->its_vmapp_cmd.vpe->vmapp_count);

As the comment block on top of atomic_inc_and_test(atomic *v) says,

* Atomically increments @v by 1
* and returns true if the result is zero, or false for all
* other cases.
*/

We will always get the 'alloc' as false here, even if this is the first mapping of this vPE. This is not as expected, I think.

And on the other hand, I wonder what is the reason for 'vmapp_count' to be atomic_t?

Thanks,
Zenghui

Quoted 61 lines
drivers/irqchip/irq-gic-v3-its.c +13 -1
+
+	its_encode_alloc(cmd, alloc);
+
+	/* We can only signal PTZ when alloc==1. Why do we have two bits? */
+	its_encode_ptz(cmd, alloc);
+	its_encode_vconf_addr(cmd, vconf_addr);
+	its_encode_vmapp_default_db(cmd, desc->its_vmapp_cmd.vpe->vpe_db_lpi);
+
+out:
  	its_fixup_cmd(cmd);
 
  	return valid_vpe(its, desc->its_vmapp_cmd.vpe);
@@ -3349,7 +3396,10 @@ static int its_vpe_init(struct its_vpe *vpe)
 
  	vpe->vpe_id = vpe_id;
  	vpe->vpt_page = vpt_page;
-	vpe->vpe_proxy_event = -1;
+	if (gic_rdists->has_rvpeid)
+		atomic_set(&vpe->vmapp_count, 0);
+	else
+		vpe->vpe_proxy_event = -1;
 
  	return 0;
  }
include/linux/irqchip/arm-gic-v4.h +14 -4
index ab1396afe08a..6213ced6f199 100644
@@ -37,8 +37,20 @@ struct its_vpe {
  	irq_hw_number_t		vpe_db_lpi;
  	/* VPE resident */
  	bool			resident;
-	/* VPE proxy mapping */
-	int			vpe_proxy_event;
+	union {
+		/* GICv4.0 implementations */
+		struct {
+			/* VPE proxy mapping */
+			int	vpe_proxy_event;
+			/* Implementation Defined Area Invalid */
+			bool	idai;
+		};
+		/* GICv4.1 implementations */
+		struct {
+			atomic_t vmapp_count;
+		};
+	};
+
  	/*
  	 * This collection ID is used to indirect the target
  	 * redistributor for this VPE. The ID itself isn't involved in
@@ -47,8 +59,6 @@ struct its_vpe {
  	u16			col_idx;
  	/* Unique (system-wide) VPE identifier */
  	u16			vpe_id;
-	/* Implementation Defined Area Invalid */
-	bool			idai;
  	/* Pending VLPIs on schedule out? */
  	bool			pending_last;
  };
author maintainer
#39 lore raw

Re: [PATCH v2 12/36] irqchip/gic-v4.1: Implement the v4.1 flavour of VMAPP

Hi Zenghui,

Quoted 68 lines · Zenghui Yu

Hi Marc,

Quoted 14 lines · Marc Zyngier

The ITS VMAPP command gains some new fields with GICv4.1:
- a default doorbell, which allows a single doorbell to be used for all the VLPIs routed to a given VPE
- a pointer to the configuration table (instead of having it in a register
that gets context switched)
- a flag indicating whether this is the first map or the last unmap for
this particulat VPE
- a flag indicating whether the pending table is known to be zeroed, or not
Plumb in the new fields in the VMAPP builder, and add the map/unmap refcounting so that the ITS can do the right thing.

[...]

Quoted 44 lines
drivers/irqchip/irq-gic-v3-its.c +18 -4
@@ -605,19 +626,45 @@ static struct its_vpe 
 *its_build_vmapp_cmd(struct its_node *its,
  					   struct its_cmd_block *cmd,
  					   struct its_cmd_desc *desc)
  {
-	unsigned long vpt_addr;
+	unsigned long vpt_addr, vconf_addr;
  	u64 target;
-
-	vpt_addr = 
 virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
-	target = desc->its_vmapp_cmd.col->target_address + 
 its->vlpi_redist_offset;
+	bool alloc;
 
  	its_encode_cmd(cmd, GITS_CMD_VMAPP);
  	its_encode_vpeid(cmd, desc->its_vmapp_cmd.vpe->vpe_id);
  	its_encode_valid(cmd, desc->its_vmapp_cmd.valid);
+
+	if (!desc->its_vmapp_cmd.valid) {
+		if (is_v4_1(its)) {
+			alloc = 
 !atomic_dec_return(&desc->its_vmapp_cmd.vpe->vmapp_count);
+			its_encode_alloc(cmd, alloc);
+		}
+
+		goto out;
+	}
+
+	vpt_addr = 
 virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
+	target = desc->its_vmapp_cmd.col->target_address + 
 its->vlpi_redist_offset;
+
  	its_encode_target(cmd, target);
  	its_encode_vpt_addr(cmd, vpt_addr);
  	its_encode_vpt_size(cmd, LPI_NRBITS - 1);
  +	if (!is_v4_1(its))
+		goto out;
+
+	vconf_addr = 

virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->its_vm->vprop_page));

(quoted diff) +2 -0
+
+	alloc = 
Quoted 1 line

atomic_inc_and_test(&desc->its_vmapp_cmd.vpe->vmapp_count);

As the comment block on top of atomic_inc_and_test(atomic *v) says,

Quoted 7 lines

* Atomically increments @v by 1
* and returns true if the result is zero, or false for all
* other cases.
*/

We will always get the 'alloc' as false here, even if this is the first mapping of this vPE. This is not as expected, I think.

As usual, a very good observation!

Indeed, I cocked up the logic here, as we need to test the value before the increment (and not after). What we want is probably something like:

  alloc = !atomic_fetch_inc(&desc->its_vmapp_cmd.vpe->vmapp_count);
Quoted 2 lines

And on the other hand, I wonder what is the reason for 'vmapp_count' to be atomic_t?

The rational is that we could end-up with multiple VMAPP commands emitted in parallel, for example. That's probably not strictly necessary right now, but I'm trying to be cautious.

Thanks,

        M.
Signature · 1 line
Jazz is not dead. It just smells funny...
author maintainer
#40 lore raw

PATCH v2 13/36 irqchip/gic-v4.1: Don't use the VPE proxy if RVPEID is set

In reply to Marc Zyngier

The infamous VPE proxy device isn't used with GICv4.1 because:
- we can invalidate any LPI from the DirectLPI MMIO interface
- the ITS and redistributors understand the life cycle of the doorbell, so we don't need to enable/disable it all the time

So let's escape early from the proxy related functions.

---
 drivers/irqchip/irq-gic-v3-its.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +22 -1
index 220d490d516e..999e61a9b2c3 100644
@@ -3069,7 +3069,7 @@ static const struct irq_domain_ops its_domain_ops = {
 /*
  * This is insane.
  *
- * If a GICv4 doesn't implement Direct LPIs (which is extremely
+ * If a GICv4.0 doesn't implement Direct LPIs (which is extremely
  * likely), the only way to perform an invalidate is to use a fake
  * device to issue an INV command, implying that the LPI has first
  * been mapped to some event on that device. Since this is not exactly
@@ -3077,9 +3077,18 @@ static const struct irq_domain_ops its_domain_ops = {
  * only issue an UNMAP if we're short on available slots.
  *
  * Broken by design(tm).
+ *
+ * GICv4.1 actually mandates that we're able to invalidate by writing to a
+ * MMIO register. It doesn't implement the whole of DirectLPI, but that's
+ * good enough. And most of the time, we don't even have to invalidate
+ * anything, so that's actually pretty good!
  */
 static void its_vpe_db_proxy_unmap_locked(struct its_vpe *vpe)
 {
+	/* GICv4.1 doesn't use a proxy, so nothing to do here */
+	if (gic_rdists->has_rvpeid)
+		return;
+
 	/* Already unmapped? */
 	if (vpe->vpe_proxy_event == -1)
 		return;
@@ -3102,6 +3111,10 @@ static void its_vpe_db_proxy_unmap_locked(struct its_vpe *vpe)
 
 static void its_vpe_db_proxy_unmap(struct its_vpe *vpe)
 {
+	/* GICv4.1 doesn't use a proxy, so nothing to do here */
+	if (gic_rdists->has_rvpeid)
+		return;
+
 	if (!gic_rdists->has_direct_lpi) {
 		unsigned long flags;
 
@@ -3113,6 +3126,10 @@ static void its_vpe_db_proxy_unmap(struct its_vpe *vpe)
 
 static void its_vpe_db_proxy_map_locked(struct its_vpe *vpe)
 {
+	/* GICv4.1 doesn't use a proxy, so nothing to do here */
+	if (gic_rdists->has_rvpeid)
+		return;
+
 	/* Already mapped? */
 	if (vpe->vpe_proxy_event != -1)
 		return;
@@ -3135,6 +3152,10 @@ static void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to)
 	unsigned long flags;
 	struct its_collection *target_col;
 
+	/* GICv4.1 doesn't use a proxy, so nothing to do here */
+	if (gic_rdists->has_rvpeid)
+		return;
+
 	if (gic_rdists->has_direct_lpi) {
 		void __iomem *rdbase;
 
Signature · 6 lines
2.20.1

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
reviewer Reviewed-by on v2 13/36
#41 lore raw

Re: [PATCH v2 13/36] irqchip/gic-v4.1: Don't use the VPE proxy if RVPEID is set

Hi Marc,

Quoted 9 lines · Marc Zyngier

The infamous VPE proxy device isn't used with GICv4.1 because:
- we can invalidate any LPI from the DirectLPI MMIO interface
- the ITS and redistributors understand the life cycle of the doorbell, so we don't need to enable/disable it all the time

So let's escape early from the proxy related functions.

Quoted 18 lines
---
  drivers/irqchip/irq-gic-v3-its.c | 23 ++++++++++++++++++++++-
  1 file changed, 22 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +1 -1
index 220d490d516e..999e61a9b2c3 100644
@@ -3069,7 +3069,7 @@ static const struct irq_domain_ops its_domain_ops = {
  /*
   * This is insane.
   *
- * If a GICv4 doesn't implement Direct LPIs (which is extremely
+ * If a GICv4.0 doesn't implement Direct LPIs (which is extremely
   * likely), the only way to perform an invalidate is to use a fake
   * device to issue an INV command, implying that the LPI has first
   * been mapped to some event on that device. Since this is not exactly

drivers/irqchip/irq-gic-v3-its.c static const struct irq_domain_ops its_domain_ops = { · line 3084

Quoted 8 lines
drivers/irqchip/irq-gic-v3-its.c +5 -0
@@ -3077,9 +3077,18 @@ static const struct irq_domain_ops its_domain_ops = {
   * only issue an UNMAP if we're short on available slots.
   *
   * Broken by design(tm).
+ *
+ * GICv4.1 actually mandates that we're able to invalidate by writing to a
+ * MMIO register. It doesn't implement the whole of DirectLPI, but that's
+ * good enough. And most of the time, we don't even have to invalidate
+ * anything, so that's actually pretty good!

I can't understand the meaning of this last sentence. May I ask for an explanation? :)

Thanks,
Zenghui

Quoted 44 lines
drivers/irqchip/irq-gic-v3-its.c +16 -0
   */
  static void its_vpe_db_proxy_unmap_locked(struct its_vpe *vpe)
  {
+	/* GICv4.1 doesn't use a proxy, so nothing to do here */
+	if (gic_rdists->has_rvpeid)
+		return;
+
  	/* Already unmapped? */
  	if (vpe->vpe_proxy_event == -1)
  		return;
@@ -3102,6 +3111,10 @@ static void its_vpe_db_proxy_unmap_locked(struct its_vpe *vpe)
 
  static void its_vpe_db_proxy_unmap(struct its_vpe *vpe)
  {
+	/* GICv4.1 doesn't use a proxy, so nothing to do here */
+	if (gic_rdists->has_rvpeid)
+		return;
+
  	if (!gic_rdists->has_direct_lpi) {
  		unsigned long flags;
 
@@ -3113,6 +3126,10 @@ static void its_vpe_db_proxy_unmap(struct its_vpe *vpe)
 
  static void its_vpe_db_proxy_map_locked(struct its_vpe *vpe)
  {
+	/* GICv4.1 doesn't use a proxy, so nothing to do here */
+	if (gic_rdists->has_rvpeid)
+		return;
+
  	/* Already mapped? */
  	if (vpe->vpe_proxy_event != -1)
  		return;
@@ -3135,6 +3152,10 @@ static void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to)
  	unsigned long flags;
  	struct its_collection *target_col;
 
+	/* GICv4.1 doesn't use a proxy, so nothing to do here */
+	if (gic_rdists->has_rvpeid)
+		return;
+
  	if (gic_rdists->has_direct_lpi) {
  		void __iomem *rdbase;
 
author maintainer
#42 lore raw

Re: [PATCH v2 13/36] irqchip/gic-v4.1: Don't use the VPE proxy if RVPEID is set

Quoted 39 lines · Zenghui Yu

Hi Marc,

Quoted 7 lines · Marc Zyngier

The infamous VPE proxy device isn't used with GICv4.1 because:
- we can invalidate any LPI from the DirectLPI MMIO interface
- the ITS and redistributors understand the life cycle of the doorbell, so we don't need to enable/disable it all the time
So let's escape early from the proxy related functions.

Quoted 26 lines
---
  drivers/irqchip/irq-gic-v3-its.c | 23 ++++++++++++++++++++++-
  1 file changed, 22 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +1 -1
index 220d490d516e..999e61a9b2c3 100644
@@ -3069,7 +3069,7 @@ static const struct irq_domain_ops 
 its_domain_ops = {
  /*
   * This is insane.
   *
- * If a GICv4 doesn't implement Direct LPIs (which is extremely
+ * If a GICv4.0 doesn't implement Direct LPIs (which is extremely
   * likely), the only way to perform an invalidate is to use a fake
   * device to issue an INV command, implying that the LPI has first

* been mapped to some event on that device. Since this is not
exactly

drivers/irqchip/irq-gic-v3-its.c +2 -0
@@ -3077,9 +3077,18 @@ static const struct irq_domain_ops 
 its_domain_ops = {
   * only issue an UNMAP if we're short on available slots.
   *
   * Broken by design(tm).
+ *
+ * GICv4.1 actually mandates that we're able to invalidate by 

writing to a

Quoted 8 lines
Quoted 5 lines

+ * MMIO register. It doesn't implement the whole of DirectLPI, but that's
+ * good enough. And most of the time, we don't even have to invalidate
+ * anything, so that's actually pretty good!

I can't understand the meaning of this last sentence. May I ask for an explanation? :)

Yeah, reading this now, it feels pretty clumsy, and only remotely connected to the patch.

What I'm trying to say here is that, although GICv4.1 doesn't have the full spectrum of v4.0 DirectLPI (it only allows a subset of it), this subset is more then enough for us. Here's the rational:

When a vPE exits from the hypervisor, we know whether we need to request a doorbell or not (depending on whether we're blocking on WFI or not). On GICv4.0, this translates into enabling the doorbell interrupt, which generates an invalidation (costly). And whenever we've taken a doorbell, or are scheduled again, we need to turn the doorbell off (invalidation again).

With v4.1, we can just say *at exit time* whether we want doorbells to be subsequently generated (see its_vpe_4_1_deschedule() and the req_db parameter in the info structure). This is part of making the vPE non-resident, so we have 0 overhead at this stage.

I'll try and update the comment here.

Thanks,

        M.
Signature · 5 lines
Jazz is not dead. It just smells funny...
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
reviewer
#43 lore raw

Re: [PATCH v2 13/36] irqchip/gic-v4.1: Don't use the VPE proxy if RVPEID is set

Hi Marc,

Quoted 61 lines · Marc Zyngier
Quoted 37 lines · Zenghui Yu

Hi Marc,

Quoted 7 lines · Marc Zyngier

The infamous VPE proxy device isn't used with GICv4.1 because:
- we can invalidate any LPI from the DirectLPI MMIO interface
- the ITS and redistributors understand the life cycle of the doorbell, so we don't need to enable/disable it all the time
So let's escape early from the proxy related functions.

Quoted 24 lines

---
  drivers/irqchip/irq-gic-v3-its.c | 23 ++++++++++++++++++++++-

  1 file changed, 22 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +1 -1
index 220d490d516e..999e61a9b2c3 100644
@@ -3069,7 +3069,7 @@ static const struct irq_domain_ops 
 its_domain_ops = {
   /*
    * This is insane.
    *
- * If a GICv4 doesn't implement Direct LPIs (which is extremely
+ * If a GICv4.0 doesn't implement Direct LPIs (which is extremely
    * likely), the only way to perform an invalidate is to use a fake
    * device to issue an INV command, implying that the LPI has first

   * been mapped to some event on that device. Since this is not exactly

drivers/irqchip/irq-gic-v3-its.c +2 -0
@@ -3077,9 +3077,18 @@ static const struct irq_domain_ops 
 its_domain_ops = {
    * only issue an UNMAP if we're short on available slots.
    *
    * Broken by design(tm).
+ *
+ * GICv4.1 actually mandates that we're able to invalidate by 
Quoted 8 lines
Quoted 5 lines

writing to a
+ * MMIO register. It doesn't implement the whole of DirectLPI, but that's
+ * good enough. And most of the time, we don't even have to invalidate
+ * anything, so that's actually pretty good!

I can't understand the meaning of this last sentence. May I ask for an explanation? :)

Yeah, reading this now, it feels pretty clumsy, and only remotely connected to the patch.

What I'm trying to say here is that, although GICv4.1 doesn't have the full spectrum of v4.0 DirectLPI (it only allows a subset of it), this subset is more then enough for us. Here's the rational:

When a vPE exits from the hypervisor, we know whether we need to request a doorbell or not (depending on whether we're blocking on WFI or not). On GICv4.0, this translates into enabling the doorbell interrupt, which generates an invalidation (costly). And whenever we've taken a doorbell, or are scheduled again, we need to turn the doorbell off (invalidation again).

Quoted 4 lines

With v4.1, we can just say *at exit time* whether we want doorbells to be subsequently generated (see its_vpe_4_1_deschedule() and the req_db parameter in the info structure). This is part of making the vPE non-resident, so we have 0 overhead at this stage.

Great, and get it. Thanks for this clear explanation!

Zenghui

author maintainer
#44 lore raw

PATCH v2 14/36 irqchip/gic-v4.1: Implement the v4.1 flavour of VMOVP

In reply to Marc Zyngier

With GICv4.1, VMOVP is extended to allow a default doorbell to be specified, as well as a validity bit for this doorbell. As an added bonus, VMOPVP isn't required anymore of moving a VPE between redistributors that share the same affinity.

Let's add this support to the VMOVP builder, and make sure we don't issuer the command if we don't really need to.

---
 drivers/irqchip/irq-gic-v3-its.c | 40 ++++++++++++++++++++++++++------
 1 file changed, 33 insertions(+), 7 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +33 -7
index 999e61a9b2c3..b5a122093a46 100644
@@ -443,6 +443,17 @@ static void its_encode_vmapp_default_db(struct its_cmd_block *cmd,
 	its_mask_encode(&cmd->raw_cmd[1], vpe_db_lpi, 31, 0);
 }
 
+static void its_encode_vmovp_default_db(struct its_cmd_block *cmd,
+					u32 vpe_db_lpi)
+{
+	its_mask_encode(&cmd->raw_cmd[3], vpe_db_lpi, 31, 0);
+}
+
+static void its_encode_db(struct its_cmd_block *cmd, bool db)
+{
+	its_mask_encode(&cmd->raw_cmd[2], db, 63, 63);
+}
+
 static inline void its_fixup_cmd(struct its_cmd_block *cmd)
 {
 	/* Let's fixup BE commands */
@@ -729,6 +740,11 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its,
 	its_encode_vpeid(cmd, desc->its_vmovp_cmd.vpe->vpe_id);
 	its_encode_target(cmd, target);
 
+	if (is_v4_1(its)) {
+		its_encode_db(cmd, true);
+		its_encode_vmovp_default_db(cmd, desc->its_vmovp_cmd.vpe->vpe_db_lpi);
+	}
+
 	its_fixup_cmd(cmd);
 
 	return valid_vpe(its, desc->its_vmovp_cmd.vpe);
@@ -3182,7 +3198,7 @@ static int its_vpe_set_affinity(struct irq_data *d,
 				bool force)
 {
 	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
-	int cpu = cpumask_first(mask_val);
+	int from, cpu = cpumask_first(mask_val);
 
 	/*
 	 * Changing affinity is mega expensive, so let's be as lazy as
@@ -3190,14 +3206,24 @@ static int its_vpe_set_affinity(struct irq_data *d,
 	 * into the proxy device, we need to move the doorbell
 	 * interrupt to its new location.
 	 */
-	if (vpe->col_idx != cpu) {
-		int from = vpe->col_idx;
+	if (vpe->col_idx == cpu)
+		goto out;
 
-		vpe->col_idx = cpu;
-		its_send_vmovp(vpe);
-		its_vpe_db_proxy_move(vpe, from, cpu);
-	}
+	from = vpe->col_idx;
+	vpe->col_idx = cpu;
+
+	/*
+	 * GICv4.1 allows us to skip VMOVP if moving to a cpu whose RD
+	 * is sharing its VPE table with the current one.
+	 */
+	if (gic_data_rdist_cpu(cpu)->vpe_table_mask &&
+	    cpumask_test_cpu(from, gic_data_rdist_cpu(cpu)->vpe_table_mask))
+		goto out;
 
+	its_send_vmovp(vpe);
+	its_vpe_db_proxy_move(vpe, from, cpu);
+
+out:
 	irq_data_update_effective_affinity(d, cpumask_of(cpu));
 
 	return IRQ_SET_MASK_OK_DONE;
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 14/36
#45 lore raw

Re: [PATCH v2 14/36] irqchip/gic-v4.1: Implement the v4.1 flavour of VMOVP

Hi Marc,

Quoted 3 lines · Marc Zyngier

With GICv4.1, VMOVP is extended to allow a default doorbell to be specified, as well as a validity bit for this doorbell. As an added bonus, VMOPVP isn't required anymore of moving a VPE between

        ^^^^^^

VMOVP

Quoted 4 lines

redistributors that share the same affinity.

Let's add this support to the VMOVP builder, and make sure we don't issuer the command if we don't really need to.

issue

Quoted 2 lines
author maintainer
#46 lore raw

PATCH v2 15/36 irqchip/gic-v4.1: Plumb skeletal VPE irqchip

In reply to Marc Zyngier

Just like for GICv4.0, each VPE has its own doorbell interrupt, and thus an irqchip that manages them. Since the doorbell management is quite different on GICv4.1, let's introduce an almost empty irqchip the will get populated over the next new patches.

---
 drivers/irqchip/irq-gic-v3-its.c | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +31 -1
index b5a122093a46..dcc7227af5f1 100644
@@ -3408,6 +3408,32 @@ static struct irq_chip its_vpe_irq_chip = {
 	.irq_set_vcpu_affinity	= its_vpe_set_vcpu_affinity,
 };
 
+static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
+{
+	struct its_cmd_info *info = vcpu_info;
+
+	switch (info->cmd_type) {
+	case SCHEDULE_VPE:
+		return 0;
+
+	case DESCHEDULE_VPE:
+		return 0;
+
+	case INVALL_VPE:
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct irq_chip its_vpe_4_1_irq_chip = {
+	.name			= "GICv4.1-vpe",
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_affinity	= its_vpe_set_affinity,
+	.irq_set_vcpu_affinity	= its_vpe_4_1_set_vcpu_affinity,
+};
+
 static int its_vpe_id_alloc(void)
 {
 	return ida_simple_get(&its_vpeid_ida, 0, ITS_MAX_VPEID, GFP_KERNEL);
@@ -3488,6 +3514,7 @@ static void its_vpe_irq_domain_free(struct irq_domain *domain,
 static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 				    unsigned int nr_irqs, void *args)
 {
+	struct irq_chip *irqchip = &its_vpe_irq_chip;
 	struct its_vm *vm = args;
 	unsigned long *bitmap;
 	struct page *vprop_page;
@@ -3515,6 +3542,9 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
 	vm->nr_db_lpis = nr_ids;
 	vm->vprop_page = vprop_page;
 
+	if (gic_rdists->has_rvpeid)
+		irqchip = &its_vpe_4_1_irq_chip;
+
 	for (i = 0; i < nr_irqs; i++) {
 		vm->vpes[i]->vpe_db_lpi = base + i;
 		err = its_vpe_init(vm->vpes[i]);
@@ -3525,7 +3555,7 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
 		if (err)
 			break;
 		irq_domain_set_hwirq_and_chip(domain, virq + i, i,
-					      &its_vpe_irq_chip, vm->vpes[i]);
+					      irqchip, vm->vpes[i]);
 		set_bit(i, bitmap);
 	}
 
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 15/36
#47 lore raw

Re: [PATCH v2 15/36] irqchip/gic-v4.1: Plumb skeletal VPE irqchip

Hi Marc,

Quoted 6 lines · Marc Zyngier

Just like for GICv4.0, each VPE has its own doorbell interrupt, and thus an irqchip that manages them. Since the doorbell management is quite different on GICv4.1, let's introduce an almost empty irqchip the will get populated over the next new patches.

author maintainer
#48 lore raw

PATCH v2 16/36 irqchip/gic-v4.1: Add mask/unmask doorbell callbacks

In reply to Marc Zyngier

masking/unmasking doorbells on GICv4.1 relies on a new INVDB command, which broadcasts the invalidation to all RDs.

Implement the new command as well as the masking callbacks, and plug the whole thing into the v4.1 VPE irqchip.

---
 drivers/irqchip/irq-gic-v3-its.c   | 60 ++++++++++++++++++++++++++++++
 include/linux/irqchip/arm-gic-v3.h |  3 +-
 2 files changed, 62 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +60 -0
index dcc7227af5f1..3c34bef70bdd 100644
@@ -309,6 +309,10 @@ struct its_cmd_desc {
 			u16 seq_num;
 			u16 its_list;
 		} its_vmovp_cmd;
+
+		struct {
+			struct its_vpe *vpe;
+		} its_invdb_cmd;
 	};
 };
 
@@ -750,6 +754,21 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its,
 	return valid_vpe(its, desc->its_vmovp_cmd.vpe);
 }
 
+static struct its_vpe *its_build_invdb_cmd(struct its_node *its,
+					   struct its_cmd_block *cmd,
+					   struct its_cmd_desc *desc)
+{
+	if (WARN_ON(!is_v4_1(its)))
+		return NULL;
+
+	its_encode_cmd(cmd, GITS_CMD_INVDB);
+	its_encode_vpeid(cmd, desc->its_invdb_cmd.vpe->vpe_id);
+
+	its_fixup_cmd(cmd);
+
+	return valid_vpe(its, desc->its_invdb_cmd.vpe);
+}
+
 static u64 its_cmd_ptr_to_offset(struct its_node *its,
 				 struct its_cmd_block *ptr)
 {
@@ -1117,6 +1136,14 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
 	its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
 }
 
+static void its_send_invdb(struct its_node *its, struct its_vpe *vpe)
+{
+	struct its_cmd_desc desc;
+
+	desc.its_invdb_cmd.vpe = vpe;
+	its_send_single_vcommand(its, its_build_invdb_cmd, &desc);
+}
+
 /*
  * irqchip functions - assumes MSI, mostly.
  */
@@ -3408,6 +3435,37 @@ static struct irq_chip its_vpe_irq_chip = {
 	.irq_set_vcpu_affinity	= its_vpe_set_vcpu_affinity,
 };
 
+static void its_vpe_4_1_send_inv(struct irq_data *d)
+{
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+	struct its_node *its;
+
+	/*
+	 * GICv4.1 wants doorbells to be invalidated using the
+	 * INVDB command in order to be broadcast to all RDs. Send
+	 * it to the first valid ITS, and let the HW do its magic.
+	 */
+	list_for_each_entry(its, &its_nodes, entry) {
+		if (!is_v4_1(its))
+			continue;
+
+		its_send_invdb(its, vpe);
+		break;
+	}
+}
+
+static void its_vpe_4_1_mask_irq(struct irq_data *d)
+{
+	lpi_write_config(d->parent_data, LPI_PROP_ENABLED, 0);
+	its_vpe_4_1_send_inv(d);
+}
+
+static void its_vpe_4_1_unmask_irq(struct irq_data *d)
+{
+	lpi_write_config(d->parent_data, 0, LPI_PROP_ENABLED);
+	its_vpe_4_1_send_inv(d);
+}
+
 static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 {
 	struct its_cmd_info *info = vcpu_info;
@@ -3429,6 +3487,8 @@ static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 
 static struct irq_chip its_vpe_4_1_irq_chip = {
 	.name			= "GICv4.1-vpe",
+	.irq_mask		= its_vpe_4_1_mask_irq,
+	.irq_unmask		= its_vpe_4_1_unmask_irq,
 	.irq_eoi		= irq_chip_eoi_parent,
 	.irq_set_affinity	= its_vpe_set_affinity,
 	.irq_set_vcpu_affinity	= its_vpe_4_1_set_vcpu_affinity,
include/linux/irqchip/arm-gic-v3.h +2 -1
index f1d6de53e09b..8157737053e4 100644
@@ -484,8 +484,9 @@
 #define GITS_CMD_VMAPTI			GITS_CMD_GICv4(GITS_CMD_MAPTI)
 #define GITS_CMD_VMOVI			GITS_CMD_GICv4(GITS_CMD_MOVI)
 #define GITS_CMD_VSYNC			GITS_CMD_GICv4(GITS_CMD_SYNC)
-/* VMOVP is the odd one, as it doesn't have a physical counterpart */
+/* VMOVP and INVDB are the odd ones, as they dont have a physical counterpart */
 #define GITS_CMD_VMOVP			GITS_CMD_GICv4(2)
+#define GITS_CMD_INVDB			GITS_CMD_GICv4(0xe)
 
 /*
  * ITS error numbers
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 16/36
#49 lore raw

Re: [PATCH v2 16/36] irqchip/gic-v4.1: Add mask/unmask doorbell callbacks

Hi Marc,

Quoted 7 lines · Marc Zyngier

masking/unmasking doorbells on GICv4.1 relies on a new INVDB command, which broadcasts the invalidation to all RDs.

Implement the new command as well as the masking callbacks, and plug the whole thing into the v4.1 VPE irqchip.

Quoted 68 lines
---
  drivers/irqchip/irq-gic-v3-its.c   | 60 ++++++++++++++++++++++++++++++
  include/linux/irqchip/arm-gic-v3.h |  3 +-
  2 files changed, 62 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +34 -0
index dcc7227af5f1..3c34bef70bdd 100644
@@ -309,6 +309,10 @@ struct its_cmd_desc {
  			u16 seq_num;
  			u16 its_list;
  		} its_vmovp_cmd;
+
+		struct {
+			struct its_vpe *vpe;
+		} its_invdb_cmd;
  	};
  };
 
@@ -750,6 +754,21 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its,
  	return valid_vpe(its, desc->its_vmovp_cmd.vpe);
  }
 
+static struct its_vpe *its_build_invdb_cmd(struct its_node *its,
+					   struct its_cmd_block *cmd,
+					   struct its_cmd_desc *desc)
+{
+	if (WARN_ON(!is_v4_1(its)))
+		return NULL;
+
+	its_encode_cmd(cmd, GITS_CMD_INVDB);
+	its_encode_vpeid(cmd, desc->its_invdb_cmd.vpe->vpe_id);
+
+	its_fixup_cmd(cmd);
+
+	return valid_vpe(its, desc->its_invdb_cmd.vpe);
+}
+
  static u64 its_cmd_ptr_to_offset(struct its_node *its,
  				 struct its_cmd_block *ptr)
  {
@@ -1117,6 +1136,14 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
  	its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
  }
 
+static void its_send_invdb(struct its_node *its, struct its_vpe *vpe)
+{
+	struct its_cmd_desc desc;
+
+	desc.its_invdb_cmd.vpe = vpe;
+	its_send_single_vcommand(its, its_build_invdb_cmd, &desc);
+}
+
  /*
   * irqchip functions - assumes MSI, mostly.
   */
@@ -3408,6 +3435,37 @@ static struct irq_chip its_vpe_irq_chip = {
  	.irq_set_vcpu_affinity	= its_vpe_set_vcpu_affinity,
  };
 
+static void its_vpe_4_1_send_inv(struct irq_data *d)
+{
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+	struct its_node *its;
+
+	/*
+	 * GICv4.1 wants doorbells to be invalidated using the

drivers/irqchip/irq-gic-v3-its.c static struct irq_chip its_vpe_irq_chip = { · line 3454

Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +10 -0
+	 * INVDB command in order to be broadcast to all RDs. Send
+	 * it to the first valid ITS, and let the HW do its magic.
+	 */
+	list_for_each_entry(its, &its_nodes, entry) {
+		if (!is_v4_1(its))
+			continue;
+
+		its_send_invdb(its, vpe);
+		break;
+	}

Maybe use find_4_1_its() helper instead?

Thanks,
Zenghui

Quoted 42 lines
drivers/irqchip/irq-gic-v3-its.c +16 -0
+}
+
+static void its_vpe_4_1_mask_irq(struct irq_data *d)
+{
+	lpi_write_config(d->parent_data, LPI_PROP_ENABLED, 0);
+	its_vpe_4_1_send_inv(d);
+}
+
+static void its_vpe_4_1_unmask_irq(struct irq_data *d)
+{
+	lpi_write_config(d->parent_data, 0, LPI_PROP_ENABLED);
+	its_vpe_4_1_send_inv(d);
+}
+
  static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
  {
  	struct its_cmd_info *info = vcpu_info;
@@ -3429,6 +3487,8 @@ static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 
  static struct irq_chip its_vpe_4_1_irq_chip = {
  	.name			= "GICv4.1-vpe",
+	.irq_mask		= its_vpe_4_1_mask_irq,
+	.irq_unmask		= its_vpe_4_1_unmask_irq,
  	.irq_eoi		= irq_chip_eoi_parent,
  	.irq_set_affinity	= its_vpe_set_affinity,
  	.irq_set_vcpu_affinity	= its_vpe_4_1_set_vcpu_affinity,
include/linux/irqchip/arm-gic-v3.h +2 -1
index f1d6de53e09b..8157737053e4 100644
@@ -484,8 +484,9 @@
  #define GITS_CMD_VMAPTI			GITS_CMD_GICv4(GITS_CMD_MAPTI)
  #define GITS_CMD_VMOVI			GITS_CMD_GICv4(GITS_CMD_MOVI)
  #define GITS_CMD_VSYNC			GITS_CMD_GICv4(GITS_CMD_SYNC)
-/* VMOVP is the odd one, as it doesn't have a physical counterpart */
+/* VMOVP and INVDB are the odd ones, as they dont have a physical counterpart */
  #define GITS_CMD_VMOVP			GITS_CMD_GICv4(2)
+#define GITS_CMD_INVDB			GITS_CMD_GICv4(0xe)
 
  /*
   * ITS error numbers
author maintainer
#50 lore raw

Re: [PATCH v2 16/36] irqchip/gic-v4.1: Add mask/unmask doorbell callbacks

Quoted 82 lines · Zenghui Yu

Hi Marc,

Quoted 5 lines · Marc Zyngier

masking/unmasking doorbells on GICv4.1 relies on a new INVDB command, which broadcasts the invalidation to all RDs.
Implement the new command as well as the masking callbacks, and plug the whole thing into the v4.1 VPE irqchip.

Quoted 71 lines
---
  drivers/irqchip/irq-gic-v3-its.c   | 60 
++++++++++++++++++++++++++++++
  include/linux/irqchip/arm-gic-v3.h |  3 +-
  2 files changed, 62 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +4 -0
index dcc7227af5f1..3c34bef70bdd 100644
@@ -309,6 +309,10 @@ struct its_cmd_desc {
  			u16 seq_num;
  			u16 its_list;
  		} its_vmovp_cmd;
+
+		struct {
+			struct its_vpe *vpe;
+		} its_invdb_cmd;
  	};
  };
  @@ -750,6 +754,21 @@ static struct its_vpe 

*its_build_vmovp_cmd(struct its_node *its,
return valid_vpe(its, desc->its_vmovp_cmd.vpe);
}
+static struct its_vpe *its_build_invdb_cmd(struct its_node *its,

drivers/irqchip/irq-gic-v3-its.c +31 -0
+					   struct its_cmd_block *cmd,
+					   struct its_cmd_desc *desc)
+{
+	if (WARN_ON(!is_v4_1(its)))
+		return NULL;
+
+	its_encode_cmd(cmd, GITS_CMD_INVDB);
+	its_encode_vpeid(cmd, desc->its_invdb_cmd.vpe->vpe_id);
+
+	its_fixup_cmd(cmd);
+
+	return valid_vpe(its, desc->its_invdb_cmd.vpe);
+}
+
  static u64 its_cmd_ptr_to_offset(struct its_node *its,
  				 struct its_cmd_block *ptr)
  {
@@ -1117,6 +1136,14 @@ static void its_send_vinvall(struct its_node 
 *its, struct its_vpe *vpe)
  	its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
  }
  +static void its_send_invdb(struct its_node *its, struct its_vpe 
 *vpe)
+{
+	struct its_cmd_desc desc;
+
+	desc.its_invdb_cmd.vpe = vpe;
+	its_send_single_vcommand(its, its_build_invdb_cmd, &desc);
+}
+
  /*
   * irqchip functions - assumes MSI, mostly.
   */
@@ -3408,6 +3435,37 @@ static struct irq_chip its_vpe_irq_chip = {
  	.irq_set_vcpu_affinity	= its_vpe_set_vcpu_affinity,
  };
  +static void its_vpe_4_1_send_inv(struct irq_data *d)
+{
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+	struct its_node *its;
+
+	/*
+	 * GICv4.1 wants doorbells to be invalidated using the
+	 * INVDB command in order to be broadcast to all RDs. Send
+	 * it to the first valid ITS, and let the HW do its magic.
+	 */
+	list_for_each_entry(its, &its_nodes, entry) {
Quoted 8 lines

drivers/irqchip/irq-gic-v3-its.c static struct irq_chip its_vpe_irq_chip = { · line 3454

Quoted 6 lines
drivers/irqchip/irq-gic-v3-its.c +6 -0
+		if (!is_v4_1(its))
+			continue;
+
+		its_send_invdb(its, vpe);
+		break;
+	}

Maybe use find_4_1_its() helper instead?

Yes, good point. I've moved it up in the series, and it is now added to this patch.

Thanks,

        M.
Signature · 1 line
Jazz is not dead. It just smells funny...
author maintainer
#51 lore raw

PATCH v2 17/36 irqchip/gic-v4.1: Add VPE residency callback

In reply to Marc Zyngier

Making a VPE resident on GICv4.1 is pretty simple, as it is just a single write to the local redistributor. We just need extra information about which groups to enable, which the KVM code will have to provide.

---
 drivers/irqchip/irq-gic-v3-its.c   | 17 +++++++++++++++++
 include/linux/irqchip/arm-gic-v3.h |  9 +++++++++
 include/linux/irqchip/arm-gic-v4.h |  5 +++++
 3 files changed, 31 insertions(+)
drivers/irqchip/irq-gic-v3-its.c +17 -0
index 3c34bef70bdd..d45e9b4e5622 100644
@@ -3466,12 +3466,29 @@ static void its_vpe_4_1_unmask_irq(struct irq_data *d)
 	its_vpe_4_1_send_inv(d);
 }
 
+static void its_vpe_4_1_schedule(struct its_vpe *vpe,
+				 struct its_cmd_info *info)
+{
+	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
+	u64 val = 0;
+
+	/* Schedule the VPE */
+	val |= GICR_VPENDBASER_Valid;
+	val |= info->g0en ? GICR_VPENDBASER_4_1_VGRP0EN : 0;
+	val |= info->g1en ? GICR_VPENDBASER_4_1_VGRP1EN : 0;
+	val |= FIELD_PREP(GICR_VPENDBASER_4_1_VPEID, vpe->vpe_id);
+
+	gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
+}
+
 static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 {
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
 	struct its_cmd_info *info = vcpu_info;
 
 	switch (info->cmd_type) {
 	case SCHEDULE_VPE:
+		its_vpe_4_1_schedule(vpe, info);
 		return 0;
 
 	case DESCHEDULE_VPE:
include/linux/irqchip/arm-gic-v3.h +9 -0
index 8157737053e4..6fd89d77b2b2 100644
@@ -327,6 +327,15 @@
 #define GICR_VPENDBASER_IDAI		(1ULL << 62)
 #define GICR_VPENDBASER_Valid		(1ULL << 63)
 
+/*
+ * GICv4.1 VPENDBASER, used for VPE residency. On top of these fields,
+ * also use the above Valid, PendingLast and Dirty.
+ */
+#define GICR_VPENDBASER_4_1_DB		(1ULL << 62)
+#define GICR_VPENDBASER_4_1_VGRP0EN	(1ULL << 59)
+#define GICR_VPENDBASER_4_1_VGRP1EN	(1ULL << 58)
+#define GICR_VPENDBASER_4_1_VPEID	GENMASK_ULL(15, 0)
+
 /*
  * ITS registers, offsets from ITS_base
  */
include/linux/irqchip/arm-gic-v4.h +5 -0
index 6213ced6f199..edbaa37fd3f1 100644
@@ -98,6 +98,11 @@ struct its_cmd_info {
 	union {
 		struct its_vlpi_map	*map;
 		u8			config;
+		bool			req_db;
+		struct {
+			bool		g0en;
+			bool		g1en;
+		};
 	};
 };
 
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 17/36
#52 lore raw

Re: [PATCH v2 17/36] irqchip/gic-v4.1: Add VPE residency callback

Hi Marc,

Quoted 5 lines · Marc Zyngier

Making a VPE resident on GICv4.1 is pretty simple, as it is just a single write to the local redistributor. We just need extra information about which groups to enable, which the KVM code will have to provide.

author maintainer
#53 lore raw

PATCH v2 18/36 irqchip/gic-v4.1: Add VPE eviction callback

In reply to Marc Zyngier

When descheduling a VPE, special care must be taken to tell the GIC about whether we want to receive a doorbell or not. This is a major improvement on GICv4.0, where the doorbell had to be separately enabled/disabled.

---
 drivers/irqchip/irq-gic-v3-its.c | 53 +++++++++++++++++++++++++-------
 1 file changed, 42 insertions(+), 11 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +42 -11
index d45e9b4e5622..f7effd453729 100644
@@ -2487,7 +2487,7 @@ static int __init allocate_lpi_tables(void)
 	return 0;
 }
 
-static u64 its_clear_vpend_valid(void __iomem *vlpi_base)
+static u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set)
 {
 	u32 count = 1000000;	/* 1s! */
 	bool clean;
@@ -2495,6 +2495,8 @@ static u64 its_clear_vpend_valid(void __iomem *vlpi_base)
 
 	val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
 	val &= ~GICR_VPENDBASER_Valid;
+	val &= ~clr;
+	val |= set;
 	gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
 
 	do {
@@ -2507,6 +2509,11 @@ static u64 its_clear_vpend_valid(void __iomem *vlpi_base)
 		}
 	} while (!clean && count);
 
+	if (unlikely(val & GICR_VPENDBASER_Dirty)) {
+		pr_err_ratelimited("ITS virtual pending table not cleaning\n");
+		val |= GICR_VPENDBASER_PendingLast;
+	}
+
 	return val;
 }
 
@@ -2615,7 +2622,7 @@ static void its_cpu_init_lpis(void)
 		 * ancient programming gets left in and has possibility of
 		 * corrupting memory.
 		 */
-		val = its_clear_vpend_valid(vlpi_base);
+		val = its_clear_vpend_valid(vlpi_base, 0, 0);
 		WARN_ON(val & GICR_VPENDBASER_Dirty);
 	}
 
@@ -3293,16 +3300,10 @@ static void its_vpe_deschedule(struct its_vpe *vpe)
 	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
 	u64 val;
 
-	val = its_clear_vpend_valid(vlpi_base);
+	val = its_clear_vpend_valid(vlpi_base, 0, 0);
 
-	if (unlikely(val & GICR_VPENDBASER_Dirty)) {
-		pr_err_ratelimited("ITS virtual pending table not cleaning\n");
-		vpe->idai = false;
-		vpe->pending_last = true;
-	} else {
-		vpe->idai = !!(val & GICR_VPENDBASER_IDAI);
-		vpe->pending_last = !!(val & GICR_VPENDBASER_PendingLast);
-	}
+	vpe->idai = !!(val & GICR_VPENDBASER_IDAI);
+	vpe->pending_last = !!(val & GICR_VPENDBASER_PendingLast);
 }
 
 static void its_vpe_invall(struct its_vpe *vpe)
@@ -3481,6 +3482,35 @@ static void its_vpe_4_1_schedule(struct its_vpe *vpe,
 	gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
 }
 
+static void its_vpe_4_1_deschedule(struct its_vpe *vpe,
+				   struct its_cmd_info *info)
+{
+	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
+	u64 val;
+
+	if (info->req_db) {
+		/*
+		 * vPE is going to block: make the vPE non-resident with
+		 * PendingLast clear and DB set. The GIC guarantees that if
+		 * we read-back PendingLast clear, then a doorbell will be
+		 * delivered when an interrupt comes.
+		 */
+		val = its_clear_vpend_valid(vlpi_base,
+					    GICR_VPENDBASER_PendingLast,
+					    GICR_VPENDBASER_4_1_DB);
+		vpe->pending_last = !!(val & GICR_VPENDBASER_PendingLast);
+	} else {
+		/*
+		 * We're not blocking, so just make the vPE non-resident
+		 * with PendingLast set, indicating that we'll be back.
+		 */
+		val = its_clear_vpend_valid(vlpi_base,
+					    0,
+					    GICR_VPENDBASER_PendingLast);
+		vpe->pending_last = true;
+	}
+}
+
 static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 {
 	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
@@ -3492,6 +3522,7 @@ static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 		return 0;
 
 	case DESCHEDULE_VPE:
+		its_vpe_4_1_deschedule(vpe, info);
 		return 0;
 
 	case INVALL_VPE:
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 18/36
#54 lore raw

Re: [PATCH v2 18/36] irqchip/gic-v4.1: Add VPE eviction callback

Hi Marc,

Quoted 6 lines · Marc Zyngier

When descheduling a VPE, special care must be taken to tell the GIC about whether we want to receive a doorbell or not. This is a major improvement on GICv4.0, where the doorbell had to be separately enabled/disabled.

author maintainer
#55 lore raw

PATCH v2 19/36 irqchip/gic-v4.1: Add VPE INVALL callback

In reply to Marc Zyngier

GICv4.1 redistributors have a VPE-aware INVALL register. Progress! We can now emulate a guest-requested INVALL without emiting a VINVALL command.

---
 drivers/irqchip/irq-gic-v3-its.c   | 14 ++++++++++++++
 include/linux/irqchip/arm-gic-v3.h |  3 +++
 2 files changed, 17 insertions(+)
drivers/irqchip/irq-gic-v3-its.c +14 -0
index f7effd453729..10bd156aa042 100644
@@ -3511,6 +3511,19 @@ static void its_vpe_4_1_deschedule(struct its_vpe *vpe,
 	}
 }
 
+static void its_vpe_4_1_invall(struct its_vpe *vpe)
+{
+	void __iomem *rdbase;
+	u64 val;
+
+	val  = GICR_INVLPIR_V;
+	val |= FIELD_PREP(GICR_INVLPIR_VPEID, vpe->vpe_id);
+
+	/* Target the redistributor this vPE is currently known on */
+	rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
+	gic_write_lpir(val, rdbase + GICR_INVALLR);
+}
+
 static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 {
 	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
@@ -3526,6 +3539,7 @@ static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 		return 0;
 
 	case INVALL_VPE:
+		its_vpe_4_1_invall(vpe);
 		return 0;
 
 	default:
include/linux/irqchip/arm-gic-v3.h +3 -0
index 6fd89d77b2b2..b69f60792554 100644
@@ -247,6 +247,9 @@
 #define GICR_TYPER_COMMON_LPI_AFF	GENMASK_ULL(25, 24)
 #define GICR_TYPER_AFFINITY		GENMASK_ULL(63, 32)
 
+#define GICR_INVLPIR_VPEID		GENMASK_ULL(47, 32)
+#define GICR_INVLPIR_V			GENMASK_ULL(63, 63)
+
 #define GIC_V3_REDIST_SIZE		0x20000
 
 #define LPI_PROP_GROUP1			(1 << 1)
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 19/36
#56 lore raw

Re: [PATCH v2 19/36] irqchip/gic-v4.1: Add VPE INVALL callback

Hi Marc,

Quoted 5 lines · Marc Zyngier

GICv4.1 redistributors have a VPE-aware INVALL register. Progress! We can now emulate a guest-requested INVALL without emiting a VINVALL command.

Quoted 10 lines
---
  drivers/irqchip/irq-gic-v3-its.c   | 14 ++++++++++++++
  include/linux/irqchip/arm-gic-v3.h |  3 +++
  2 files changed, 17 insertions(+)
drivers/irqchip/irq-gic-v3-its.c +0 -0
index f7effd453729..10bd156aa042 100644

drivers/irqchip/irq-gic-v3-its.c static void its_vpe_4_1_deschedule(struct its_vpe *vpe, · line 3520

Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +7 -0
@@ -3511,6 +3511,19 @@ static void its_vpe_4_1_deschedule(struct its_vpe *vpe,
  	}
  }
 
+static void its_vpe_4_1_invall(struct its_vpe *vpe)
+{
+	void __iomem *rdbase;
+	u64 val;
+
+	val  = GICR_INVLPIR_V;
+	val |= FIELD_PREP(GICR_INVLPIR_VPEID, vpe->vpe_id);

Can we use GICR_INVALL_V/VPEID instead, and ...

Quoted 22 lines
drivers/irqchip/irq-gic-v3-its.c +7 -0
+
+	/* Target the redistributor this vPE is currently known on */
+	rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
+	gic_write_lpir(val, rdbase + GICR_INVALLR);
+}
+
  static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
  {
  	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
@@ -3526,6 +3539,7 @@ static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
  		return 0;
 
  	case INVALL_VPE:
+		its_vpe_4_1_invall(vpe);
  		return 0;
 
  	default:
include/linux/irqchip/arm-gic-v3.h +0 -0
index 6fd89d77b2b2..b69f60792554 100644

include/linux/irqchip/arm-gic-v3.h line 252

Quoted 6 lines
include/linux/irqchip/arm-gic-v3.h +3 -0
@@ -247,6 +247,9 @@
  #define GICR_TYPER_COMMON_LPI_AFF	GENMASK_ULL(25, 24)
  #define GICR_TYPER_AFFINITY		GENMASK_ULL(63, 32)
 
+#define GICR_INVLPIR_VPEID		GENMASK_ULL(47, 32)
+#define GICR_INVLPIR_V			GENMASK_ULL(63, 63)
+

... define them here:

#define GICR_INVALL_VPEID GICR_INVLPIR_VPEID
#define GICR_INVALL_V GICR_INVLPIR_V

Thanks,
Zenghui

Quoted 4 lines
include/linux/irqchip/arm-gic-v3.h +0 -0
  #define GIC_V3_REDIST_SIZE		0x20000
 
  #define LPI_PROP_GROUP1			(1 << 1)
author maintainer
#57 lore raw

Re: [PATCH v2 19/36] irqchip/gic-v4.1: Add VPE INVALL callback

[Old email, doing some v3 cleanup]

Quoted 64 lines · Zenghui Yu

Hi Marc,

Quoted 4 lines · Marc Zyngier

GICv4.1 redistributors have a VPE-aware INVALL register. Progress! We can now emulate a guest-requested INVALL without emiting a VINVALL command.

Quoted 9 lines
---
  drivers/irqchip/irq-gic-v3-its.c   | 14 ++++++++++++++
  include/linux/irqchip/arm-gic-v3.h |  3 +++
  2 files changed, 17 insertions(+)
drivers/irqchip/irq-gic-v3-its.c +0 -0
index f7effd453729..10bd156aa042 100644

drivers/irqchip/irq-gic-v3-its.c static void its_vpe_4_1_deschedule(struct · line 3520

Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +6 -0
@@ -3511,6 +3511,19 @@ static void its_vpe_4_1_deschedule(struct 
 its_vpe *vpe,
  	}
  }
  +static void its_vpe_4_1_invall(struct its_vpe *vpe)
+{
+	void __iomem *rdbase;
+	u64 val;
+
+	val  = GICR_INVLPIR_V;
+	val |= FIELD_PREP(GICR_INVLPIR_VPEID, vpe->vpe_id);

Can we use GICR_INVALL_V/VPEID instead, and ...

Quoted 24 lines
drivers/irqchip/irq-gic-v3-its.c +7 -0
+
+	/* Target the redistributor this vPE is currently known on */
+	rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
+	gic_write_lpir(val, rdbase + GICR_INVALLR);
+}
+
  static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void 
 *vcpu_info)
  {
  	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
@@ -3526,6 +3539,7 @@ static int 
 its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
  		return 0;
 
  	case INVALL_VPE:
+		its_vpe_4_1_invall(vpe);
  		return 0;
 

default:

include/linux/irqchip/arm-gic-v3.h +0 -0
index 6fd89d77b2b2..b69f60792554 100644

include/linux/irqchip/arm-gic-v3.h line 251

Quoted 5 lines
include/linux/irqchip/arm-gic-v3.h +2 -0
@@ -247,6 +247,9 @@
  #define GICR_TYPER_COMMON_LPI_AFF	GENMASK_ULL(25, 24)
  #define GICR_TYPER_AFFINITY		GENMASK_ULL(63, 32)
  +#define GICR_INVLPIR_VPEID		GENMASK_ULL(47, 32)
+#define GICR_INVLPIR_V			GENMASK_ULL(63, 63)
+

... define them here:

Quoted 2 lines

#define GICR_INVALL_VPEID GICR_INVLPIR_VPEID
#define GICR_INVALL_V GICR_INVLPIR_V

Yes, that's a sensible things to do. I'll squash that in my rebased series.

Thanks,

        M.
Signature · 1 line
Jazz is not dead. It just smells funny...
author maintainer
#58 lore raw

PATCH v2 20/36 irqchip/gic-v4.1: Suppress per-VLPI doorbell

In reply to Marc Zyngier

Since GICv4.1 gives us a per-VPE doorbell, avoid programming anything else on VMOVI/VMAPI/VMAPTI and on any other action that would have otherwise resulted in a per-VLPI doorbell to be programmed.

---
 drivers/irqchip/irq-gic-v3-its.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +9 -2
index 10bd156aa042..75c4d1a6f20d 100644
@@ -691,7 +691,7 @@ static struct its_vpe *its_build_vmapti_cmd(struct its_node *its,
 {
 	u32 db;
 
-	if (desc->its_vmapti_cmd.db_enabled)
+	if (!is_v4_1(its) && desc->its_vmapti_cmd.db_enabled)
 		db = desc->its_vmapti_cmd.vpe->vpe_db_lpi;
 	else
 		db = 1023;
@@ -714,7 +714,7 @@ static struct its_vpe *its_build_vmovi_cmd(struct its_node *its,
 {
 	u32 db;
 
-	if (desc->its_vmovi_cmd.db_enabled)
+	if (!is_v4_1(its) && desc->its_vmovi_cmd.db_enabled)
 		db = desc->its_vmovi_cmd.vpe->vpe_db_lpi;
 	else
 		db = 1023;
@@ -1227,6 +1227,13 @@ static void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 	u32 event = its_get_event_id(d);
 
+	/*
+	 * GICv4.1 does away with the per-LPI nonsense, nothing to do
+	 * here.
+	 */
+	if (is_v4_1(its_dev->its))
+		return;
+
 	if (its_dev->event_map.vlpi_maps[event].db_enabled == enable)
 		return;
 
Signature · 1 line
2.20.1
reviewer Reviewed-by on v2 20/36
#59 lore raw

Re: [PATCH v2 20/36] irqchip/gic-v4.1: Suppress per-VLPI doorbell

Hi Marc,

Quoted 5 lines · Marc Zyngier

Since GICv4.1 gives us a per-VPE doorbell, avoid programming anything else on VMOVI/VMAPI/VMAPTI and on any other action that would have otherwise resulted in a per-VLPI doorbell to be programmed.

author maintainer
#60 lore raw

PATCH v2 21/36 irqchip/gic-v4.1: Allow direct invalidation of VLPIs

In reply to Marc Zyngier

Just like for INVALL, GICv4.1 has grown a VPE-aware INVLPI register. Let's plumb it in and make use of the DirectLPI code in that case.

---
 drivers/irqchip/irq-gic-v3-its.c   | 53 ++++++++++++++++++++----------
 include/linux/irqchip/arm-gic-v3.h |  1 +
 2 files changed, 36 insertions(+), 18 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +35 -18
index 75c4d1a6f20d..df259e202482 100644
@@ -203,11 +203,26 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 	return its->collections + its_dev->event_map.col_map[event];
 }
 
-static struct its_collection *irq_to_col(struct irq_data *d)
+static struct its_vlpi_map *get_vlpi_map(struct irq_data *d)
 {
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+	u32 event = its_get_event_id(d);
+
+	if (!irqd_is_forwarded_to_vcpu(d))
+		return NULL;
 
-	return dev_event_to_col(its_dev, its_get_event_id(d));
+	return &its_dev->event_map.vlpi_maps[event];
+}
+
+static int irq_to_cpuid(struct irq_data *d)
+{
+	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+	struct its_vlpi_map *map = get_vlpi_map(d);
+
+	if (map)
+		return map->vpe->col_idx;
+
+	return its_dev->event_map.col_map[its_get_event_id(d)];
 }
 
 static struct its_collection *valid_col(struct its_collection *col)
@@ -1147,17 +1162,6 @@ static void its_send_invdb(struct its_node *its, struct its_vpe *vpe)
 /*
  * irqchip functions - assumes MSI, mostly.
  */
-static struct its_vlpi_map *get_vlpi_map(struct irq_data *d)
-{
-	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
-	u32 event = its_get_event_id(d);
-
-	if (!irqd_is_forwarded_to_vcpu(d))
-		return NULL;
-
-	return &its_dev->event_map.vlpi_maps[event];
-}
-
 static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 {
 	struct its_vlpi_map *map = get_vlpi_map(d);
@@ -1200,13 +1204,25 @@ static void wait_for_syncr(void __iomem *rdbase)
 
 static void direct_lpi_inv(struct irq_data *d)
 {
-	struct its_collection *col;
+	struct its_vlpi_map *map = get_vlpi_map(d);
 	void __iomem *rdbase;
+	u64 val;
+
+	if (map) {
+		struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+
+		WARN_ON(!is_v4_1(its_dev->its));
+
+		val  = GICR_INVLPIR_V;
+		val |= FIELD_PREP(GICR_INVLPIR_VPEID, map->vpe->vpe_id);
+		val |= FIELD_PREP(GICR_INVLPIR_INTID, map->vintid);
+	} else {
+		val = d->hwirq;
+	}
 
 	/* Target the redistributor this LPI is currently routed to */
-	col = irq_to_col(d);
-	rdbase = per_cpu_ptr(gic_rdists->rdist, col->col_id)->rd_base;
-	gic_write_lpir(d->hwirq, rdbase + GICR_INVLPIR);
+	rdbase = per_cpu_ptr(gic_rdists->rdist, irq_to_cpuid(d))->rd_base;
+	gic_write_lpir(val, rdbase + GICR_INVLPIR);
 
 	wait_for_syncr(rdbase);
 }
@@ -1216,7 +1232,8 @@ static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
 	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 
 	lpi_write_config(d, clr, set);
-	if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d))
+	if (gic_rdists->has_direct_lpi &&
+	    (is_v4_1(its_dev->its) || !irqd_is_forwarded_to_vcpu(d)))
 		direct_lpi_inv(d);
 	else
 		its_send_inv(its_dev, its_get_event_id(d));
include/linux/irqchip/arm-gic-v3.h +1 -0
index b69f60792554..5f3278cbf247 100644
@@ -247,6 +247,7 @@
 #define GICR_TYPER_COMMON_LPI_AFF	GENMASK_ULL(25, 24)
 #define GICR_TYPER_AFFINITY		GENMASK_ULL(63, 32)
 
+#define GICR_INVLPIR_INTID		GENMASK_ULL(31, 0)
 #define GICR_INVLPIR_VPEID		GENMASK_ULL(47, 32)
 #define GICR_INVLPIR_V			GENMASK_ULL(63, 63)
 
Signature · 1 line
2.20.1
author maintainer
#62 lore raw

PATCH v2 22/36 irqchip/gic-v4.1: Advertise support v4.1 to KVM

In reply to Marc Zyngier

Tell KVM that we support v4.1. Nothing uses this information so far.

---
 drivers/irqchip/irq-gic-v3-its.c       | 9 ++++++++-
 drivers/irqchip/irq-gic-v3.c           | 1 +
 include/linux/irqchip/arm-gic-common.h | 2 ++
 3 files changed, 11 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +8 -1
index df259e202482..6483f8051b3e 100644
@@ -4580,6 +4580,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 	struct device_node *of_node;
 	struct its_node *its;
 	bool has_v4 = false;
+	bool has_v4_1 = false;
 	int err;
 
 	gic_rdists = rdists;
@@ -4600,8 +4601,14 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 	if (err)
 		return err;
 
-	list_for_each_entry(its, &its_nodes, entry)
+	list_for_each_entry(its, &its_nodes, entry) {
 		has_v4 |= is_v4(its);
+		has_v4_1 |= is_v4_1(its);
+	}
+
+	/* Don't bother with inconsistent systems */
+	if (WARN_ON(!has_v4_1 && rdists->has_rvpeid))
+		rdists->has_rvpeid = false;
 
 	if (has_v4 & rdists->has_vlpis) {
 		if (its_init_vpe_domain() ||
drivers/irqchip/irq-gic-v3.c +1 -0
index f0d33ac64a99..94dddfb21076 100644
@@ -1758,6 +1758,7 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
 		gic_v3_kvm_info.vcpu = r;
 
 	gic_v3_kvm_info.has_v4 = gic_data.rdists.has_vlpis;
+	gic_v3_kvm_info.has_v4_1 = gic_data.rdists.has_rvpeid;
 	gic_set_kvm_info(&gic_v3_kvm_info);
 }
 
include/linux/irqchip/arm-gic-common.h +2 -0
index b9850f5f1906..fa8c0455c352 100644
@@ -32,6 +32,8 @@ struct gic_kvm_info {
 	struct resource vctrl;
 	/* vlpi support */
 	bool		has_v4;
+	/* rvpeid support */
+	bool		has_v4_1;
 };
 
 const struct gic_kvm_info *gic_get_kvm_info(void);
Signature · 1 line
2.20.1
reviewer
#63 lore raw

Re: [PATCH v2 22/36] irqchip/gic-v4.1: Advertise support v4.1 to KVM

Hi Marc,

Quoted 42 lines · Marc Zyngier

Tell KVM that we support v4.1. Nothing uses this information so far.

---
  drivers/irqchip/irq-gic-v3-its.c       | 9 ++++++++-
  drivers/irqchip/irq-gic-v3.c           | 1 +
  include/linux/irqchip/arm-gic-common.h | 2 ++
  3 files changed, 11 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +8 -1
index df259e202482..6483f8051b3e 100644
@@ -4580,6 +4580,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
  	struct device_node *of_node;
  	struct its_node *its;
  	bool has_v4 = false;
+	bool has_v4_1 = false;
  	int err;
 
  	gic_rdists = rdists;
@@ -4600,8 +4601,14 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
  	if (err)
  		return err;
 
-	list_for_each_entry(its, &its_nodes, entry)
+	list_for_each_entry(its, &its_nodes, entry) {
  		has_v4 |= is_v4(its);
+		has_v4_1 |= is_v4_1(its);
+	}
+
+	/* Don't bother with inconsistent systems */
+	if (WARN_ON(!has_v4_1 && rdists->has_rvpeid))
+		rdists->has_rvpeid = false;
 
  	if (has_v4 & rdists->has_vlpis) {
  		if (its_init_vpe_domain() ||
drivers/irqchip/irq-gic-v3.c +0 -0
index f0d33ac64a99..94dddfb21076 100644

drivers/irqchip/irq-gic-v3.c static void __init gic_of_setup_kvm_info(struct device_node *node) · line 1761

Quoted 4 lines
drivers/irqchip/irq-gic-v3.c +1 -0
@@ -1758,6 +1758,7 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
  		gic_v3_kvm_info.vcpu = r;
 
  	gic_v3_kvm_info.has_v4 = gic_data.rdists.has_vlpis;
+	gic_v3_kvm_info.has_v4_1 = gic_data.rdists.has_rvpeid;

Also set gic_v3_kvm_info.has_v4_1 in gic_acpi_setup_kvm_info().

Thanks,
Zenghui

Quoted 17 lines
drivers/irqchip/irq-gic-v3.c +0 -0
  	gic_set_kvm_info(&gic_v3_kvm_info);
  }
 
include/linux/irqchip/arm-gic-common.h +2 -0
index b9850f5f1906..fa8c0455c352 100644
@@ -32,6 +32,8 @@ struct gic_kvm_info {
  	struct resource vctrl;
  	/* vlpi support */
  	bool		has_v4;
+	/* rvpeid support */
+	bool		has_v4_1;
  };
 
  const struct gic_kvm_info *gic_get_kvm_info(void);
author maintainer
#64 lore raw

Re: [PATCH v2 22/36] irqchip/gic-v4.1: Advertise support v4.1 to KVM

Quoted 47 lines · Zenghui Yu

Hi Marc,

Quoted 44 lines · Marc Zyngier

Tell KVM that we support v4.1. Nothing uses this information so far.

---
  drivers/irqchip/irq-gic-v3-its.c       | 9 ++++++++-
  drivers/irqchip/irq-gic-v3.c           | 1 +
  include/linux/irqchip/arm-gic-common.h | 2 ++
  3 files changed, 11 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +1 -0
index df259e202482..6483f8051b3e 100644
@@ -4580,6 +4580,7 @@ int __init its_init(struct fwnode_handle 
 *handle, struct rdists *rdists,
  	struct device_node *of_node;
  	struct its_node *its;
  	bool has_v4 = false;
+	bool has_v4_1 = false;
  	int err;
 
  	gic_rdists = rdists;
drivers/irqchip/irq-gic-v3-its.c +7 -0
@@ -4600,8 +4601,14 @@ int __init its_init(struct fwnode_handle 
 *handle, struct rdists *rdists,
  	if (err)
  		return err;
  -	list_for_each_entry(its, &its_nodes, entry)
+	list_for_each_entry(its, &its_nodes, entry) {
  		has_v4 |= is_v4(its);
+		has_v4_1 |= is_v4_1(its);
+	}
+
+	/* Don't bother with inconsistent systems */
+	if (WARN_ON(!has_v4_1 && rdists->has_rvpeid))
+		rdists->has_rvpeid = false;
 
  	if (has_v4 & rdists->has_vlpis) {
  		if (its_init_vpe_domain() ||
drivers/irqchip/irq-gic-v3.c +0 -0
index f0d33ac64a99..94dddfb21076 100644
@@ -1758,6 +1758,7 @@ static void __init 
 gic_of_setup_kvm_info(struct device_node *node)
  		gic_v3_kvm_info.vcpu = r;
 
Quoted 4 lines

drivers/irqchip/irq-gic-v3.c static void __init gic_of_setup_kvm_info(struct device_node *node) · line 1761

Quoted 2 lines
drivers/irqchip/irq-gic-v3.c +1 -0
  	gic_v3_kvm_info.has_v4 = gic_data.rdists.has_vlpis;
+	gic_v3_kvm_info.has_v4_1 = gic_data.rdists.has_rvpeid;

Also set gic_v3_kvm_info.has_v4_1 in gic_acpi_setup_kvm_info().

Indeed. Now fixed.

Thanks,

        M.
Signature · 1 line
Jazz is not dead. It just smells funny...
author maintainer
#65 lore raw

PATCH v2 23/36 irqchip/gic-v4.1: Map the ITS SGIR register page

In reply to Marc Zyngier

One of the new features of GICv4.1 is to allow virtual SGIs to be directly signaled to a VPE. For that, the ITS has grown a new 64kB page containing only a single register that is used to signal a SGI to a given VPE.

Add a second mapping covering this new 64kB range, and take this opportunity to limit the original mapping to 64kB, which is enough to cover the span of the ITS registers.

---
 drivers/irqchip/irq-gic-v3-its.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +13 -2
index 6483f8051b3e..991a70b457b4 100644
@@ -97,6 +97,7 @@ struct its_node {
 	struct mutex		dev_alloc_lock;
 	struct list_head	entry;
 	void __iomem		*base;
+	void __iomem		*sgir_base;
 	phys_addr_t		phys_base;
 	struct its_cmd_block	*cmd_base;
 	struct its_cmd_block	*cmd_write;
@@ -4166,7 +4167,7 @@ static int __init its_probe_one(struct resource *res,
 	struct page *page;
 	int err;
 
-	its_base = ioremap(res->start, resource_size(res));
+	its_base = ioremap(res->start, SZ_64K);
 	if (!its_base) {
 		pr_warn("ITS@%pa: Unable to map ITS registers\n", &res->start);
 		return -ENOMEM;
@@ -4217,6 +4218,13 @@ static int __init its_probe_one(struct resource *res,
 
 		if (is_v4_1(its)) {
 			u32 svpet = FIELD_GET(GITS_TYPER_SVPET, typer);
+
+			its->sgir_base = ioremap(res->start + SZ_128K, SZ_64K);
+			if (!its->sgir_base) {
+				err = -ENOMEM;
+				goto out_free_its;
+			}
+
 			its->mpidr = readl_relaxed(its_base + GITS_MPIDR);
 
 			pr_info("ITS@%pa: Using GICv4.1 mode %08x %08x\n",
@@ -4230,7 +4238,7 @@ static int __init its_probe_one(struct resource *res,
 				get_order(ITS_CMD_QUEUE_SZ));
 	if (!page) {
 		err = -ENOMEM;
-		goto out_free_its;
+		goto out_unmap_sgir;
 	}
 	its->cmd_base = (void *)page_address(page);
 	its->cmd_write = its->cmd_base;
@@ -4297,6 +4305,9 @@ static int __init its_probe_one(struct resource *res,
 	its_free_tables(its);
 out_free_cmd:
 	free_pages((unsigned long)its->cmd_base, get_order(ITS_CMD_QUEUE_SZ));
+out_unmap_sgir:
+	if (its->sgir_base)
+		iounmap(its->sgir_base);
 out_free_its:
 	kfree(its);
 out_unmap:
Signature · 1 line
2.20.1
author maintainer
#66 lore raw

PATCH v2 24/36 irqchip/gic-v4.1: Plumb skeletal VSGI irqchip

In reply to Marc Zyngier

Since GICv4.1 has the capability to inject 16 SGIs into each VPE, and that I'm keen not to invent too many specific interfaces to manupulate these interrupts, let's pretend that each of these SGIs is an actual Linux interrupt.

For that matter, let's introduce a minimal irqchip and irqdomain setup that will get fleshed up in the following patches.

---
 drivers/irqchip/irq-gic-v3-its.c   | 68 +++++++++++++++++++++++++++++-
 drivers/irqchip/irq-gic-v4.c       |  8 +++-
 include/linux/irqchip/arm-gic-v4.h |  9 +++-
 3 files changed, 81 insertions(+), 4 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +67 -1
index 991a70b457b4..6a3952d3c379 100644
@@ -3581,6 +3581,67 @@ static struct irq_chip its_vpe_4_1_irq_chip = {
 	.irq_set_vcpu_affinity	= its_vpe_4_1_set_vcpu_affinity,
 };
 
+static int its_sgi_set_affinity(struct irq_data *d,
+				const struct cpumask *mask_val,
+				bool force)
+{
+	return -EINVAL;
+}
+
+static struct irq_chip its_sgi_irq_chip = {
+	.name			= "GICv4.1-sgi",
+	.irq_set_affinity	= its_sgi_set_affinity,
+};
+
+static int its_sgi_irq_domain_alloc(struct irq_domain *domain,
+				    unsigned int virq, unsigned int nr_irqs,
+				    void *args)
+{
+	struct its_vpe *vpe = args;
+	int i;
+
+	/* Yes, we do want 16 SGIs */
+	WARN_ON(nr_irqs != 16);
+
+	for (i = 0; i < 16; i++) {
+		vpe->sgi_config[i].priority = 0;
+		vpe->sgi_config[i].enabled = false;
+		vpe->sgi_config[i].group = false;
+
+		irq_domain_set_hwirq_and_chip(domain, virq + i, i,
+					      &its_sgi_irq_chip, vpe);
+		irq_set_status_flags(virq + i, IRQ_DISABLE_UNLAZY);
+	}
+
+	return 0;
+}
+
+static void its_sgi_irq_domain_free(struct irq_domain *domain,
+				    unsigned int virq,
+				    unsigned int nr_irqs)
+{
+	/* Nothing to do */
+}
+
+static int its_sgi_irq_domain_activate(struct irq_domain *domain,
+				       struct irq_data *d, bool reserve)
+{
+	return 0;
+}
+
+static void its_sgi_irq_domain_deactivate(struct irq_domain *domain,
+					  struct irq_data *d)
+{
+	/* Nothing to do */
+}
+
+static struct irq_domain_ops its_sgi_domain_ops = {
+	.alloc		= its_sgi_irq_domain_alloc,
+	.free		= its_sgi_irq_domain_free,
+	.activate	= its_sgi_irq_domain_activate,
+	.deactivate	= its_sgi_irq_domain_deactivate,
+};
+
 static int its_vpe_id_alloc(void)
 {
 	return ida_simple_get(&its_vpeid_ida, 0, ITS_MAX_VPEID, GFP_KERNEL);
@@ -4622,8 +4683,13 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 		rdists->has_rvpeid = false;
 
 	if (has_v4 & rdists->has_vlpis) {
+		struct irq_domain_ops *sgi_ops = NULL;
+
+		if (has_v4_1)
+			sgi_ops = &its_sgi_domain_ops;
+
 		if (its_init_vpe_domain() ||
-		    its_init_v4(parent_domain, &its_vpe_domain_ops)) {
+		    its_init_v4(parent_domain, &its_vpe_domain_ops, sgi_ops)) {
 			rdists->has_vlpis = false;
 			pr_err("ITS: Disabling GICv4 support\n");
 		}
drivers/irqchip/irq-gic-v4.c +6 -2
index 45969927cc81..c01910d53f9e 100644
@@ -85,6 +85,7 @@
 
 static struct irq_domain *gic_domain;
 static const struct irq_domain_ops *vpe_domain_ops;
+static const struct irq_domain_ops *sgi_domain_ops;
 
 int its_alloc_vcpu_irqs(struct its_vm *vm)
 {
@@ -216,12 +217,15 @@ int its_prop_update_vlpi(int irq, u8 config, bool inv)
 	return irq_set_vcpu_affinity(irq, &info);
 }
 
-int its_init_v4(struct irq_domain *domain, const struct irq_domain_ops *ops)
+int its_init_v4(struct irq_domain *domain,
+		const struct irq_domain_ops *vpe_ops,
+		const struct irq_domain_ops *sgi_ops)
 {
 	if (domain) {
 		pr_info("ITS: Enabling GICv4 support\n");
 		gic_domain = domain;
-		vpe_domain_ops = ops;
+		vpe_domain_ops = vpe_ops;
+		sgi_domain_ops = sgi_ops;
 		return 0;
 	}
 
include/linux/irqchip/arm-gic-v4.h +8 -1
index edbaa37fd3f1..03bbd0aed2e2 100644
@@ -47,6 +47,11 @@ struct its_vpe {
 		};
 		/* GICv4.1 implementations */
 		struct {
+			struct {
+				u8	priority;
+				bool	enabled;
+				bool	group;
+			}			sgi_config[16];
 			atomic_t vmapp_count;
 		};
 	};
@@ -116,6 +121,8 @@ int its_unmap_vlpi(int irq);
 int its_prop_update_vlpi(int irq, u8 config, bool inv);
 
 struct irq_domain_ops;
-int its_init_v4(struct irq_domain *domain, const struct irq_domain_ops *ops);
+int its_init_v4(struct irq_domain *domain,
+		const struct irq_domain_ops *vpe_ops,
+		const struct irq_domain_ops *sgi_ops);
 
 #endif
Signature · 1 line
2.20.1
author maintainer
#67 lore raw

PATCH v2 25/36 irqchip/gic-v4.1: Add initial SGI configuration

In reply to Marc Zyngier

The GICv4.1 ITS has yet another new command (VSGI) which allows a VPE-targeted SGI to be configured (or have its pending state cleared). Add support for this command and plumb it into the activate irqdomain callback so that it is ready to be used.

---
 drivers/irqchip/irq-gic-v3-its.c   | 88 ++++++++++++++++++++++++++++++
 include/linux/irqchip/arm-gic-v3.h |  3 +-
 2 files changed, 90 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v3-its.c +88 -0
index 6a3952d3c379..ef5988724aa3 100644
@@ -329,6 +329,15 @@ struct its_cmd_desc {
 		struct {
 			struct its_vpe *vpe;
 		} its_invdb_cmd;
+
+		struct {
+			struct its_vpe *vpe;
+			u8 sgi;
+			u8 priority;
+			bool enable;
+			bool group;
+			bool clear;
+		} its_vsgi_cmd;
 	};
 };
 
@@ -474,6 +483,31 @@ static void its_encode_db(struct its_cmd_block *cmd, bool db)
 	its_mask_encode(&cmd->raw_cmd[2], db, 63, 63);
 }
 
+static void its_encode_sgi_intid(struct its_cmd_block *cmd, u8 sgi)
+{
+	its_mask_encode(&cmd->raw_cmd[0], sgi, 35, 32);
+}
+
+static void its_encode_sgi_priority(struct its_cmd_block *cmd, u8 prio)
+{
+	its_mask_encode(&cmd->raw_cmd[0], prio >> 4, 23, 20);
+}
+
+static void its_encode_sgi_group(struct its_cmd_block *cmd, bool grp)
+{
+	its_mask_encode(&cmd->raw_cmd[0], grp, 10, 10);
+}
+
+static void its_encode_sgi_clear(struct its_cmd_block *cmd, bool clr)
+{
+	its_mask_encode(&cmd->raw_cmd[0], clr, 9, 9);
+}
+
+static void its_encode_sgi_enable(struct its_cmd_block *cmd, bool en)
+{
+	its_mask_encode(&cmd->raw_cmd[0], en, 8, 8);
+}
+
 static inline void its_fixup_cmd(struct its_cmd_block *cmd)
 {
 	/* Let's fixup BE commands */
@@ -785,6 +819,26 @@ static struct its_vpe *its_build_invdb_cmd(struct its_node *its,
 	return valid_vpe(its, desc->its_invdb_cmd.vpe);
 }
 
+static struct its_vpe *its_build_vsgi_cmd(struct its_node *its,
+					  struct its_cmd_block *cmd,
+					  struct its_cmd_desc *desc)
+{
+	if (WARN_ON(!is_v4_1(its)))
+		return NULL;
+
+	its_encode_cmd(cmd, GITS_CMD_VSGI);
+	its_encode_vpeid(cmd, desc->its_vsgi_cmd.vpe->vpe_id);
+	its_encode_sgi_intid(cmd, desc->its_vsgi_cmd.sgi);
+	its_encode_sgi_priority(cmd, desc->its_vsgi_cmd.priority);
+	its_encode_sgi_group(cmd, desc->its_vsgi_cmd.group);
+	its_encode_sgi_clear(cmd, desc->its_vsgi_cmd.clear);
+	its_encode_sgi_enable(cmd, desc->its_vsgi_cmd.enable);
+
+	its_fixup_cmd(cmd);
+
+	return valid_vpe(its, desc->its_vsgi_cmd.vpe);
+}
+
 static u64 its_cmd_ptr_to_offset(struct its_node *its,
 				 struct its_cmd_block *ptr)
 {
@@ -3581,6 +3635,38 @@ static struct irq_chip its_vpe_4_1_irq_chip = {
 	.irq_set_vcpu_affinity	= its_vpe_4_1_set_vcpu_affinity,
 };
 
+static struct its_node *find_4_1_its(void)
+{
+	static struct its_node *its = NULL;
+
+	if (!its) {
+		list_for_each_entry(its, &its_nodes, entry) {
+			if (is_v4_1(its))
+				return its;
+		}
+
+		/* Oops? */
+		its = NULL;
+	}
+
+	return its;
+}
+
+static void its_configure_sgi(struct irq_data *d, bool clear)
+{
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+	struct its_cmd_desc desc;
+
+	desc.its_vsgi_cmd.vpe = vpe;
+	desc.its_vsgi_cmd.sgi = d->hwirq;
+	desc.its_vsgi_cmd.priority = vpe->sgi_config[d->hwirq].priority;
+	desc.its_vsgi_cmd.enable = vpe->sgi_config[d->hwirq].enabled;
+	desc.its_vsgi_cmd.group = vpe->sgi_config[d->hwirq].group;
+	desc.its_vsgi_cmd.clear = clear;
+
+	its_send_single_vcommand(find_4_1_its(), its_build_vsgi_cmd, &desc);
+}
+
 static int its_sgi_set_affinity(struct irq_data *d,
 				const struct cpumask *mask_val,
 				bool force)
@@ -3626,6 +3712,8 @@ static void its_sgi_irq_domain_free(struct irq_domain *domain,
 static int its_sgi_irq_domain_activate(struct irq_domain *domain,
 				       struct irq_data *d, bool reserve)
 {
+	/* Write out the initial SGI configuration */
+	its_configure_sgi(d, false);
 	return 0;
 }
 
include/linux/irqchip/arm-gic-v3.h +2 -1
index 5f3278cbf247..c73176d3ab2b 100644
@@ -497,8 +497,9 @@
 #define GITS_CMD_VMAPTI			GITS_CMD_GICv4(GITS_CMD_MAPTI)
 #define GITS_CMD_VMOVI			GITS_CMD_GICv4(GITS_CMD_MOVI)
 #define GITS_CMD_VSYNC			GITS_CMD_GICv4(GITS_CMD_SYNC)
-/* VMOVP and INVDB are the odd ones, as they dont have a physical counterpart */
+/* VMOVP, VSGI and INVDB are the odd ones, as they dont have a physical counterpart */
 #define GITS_CMD_VMOVP			GITS_CMD_GICv4(2)
+#define GITS_CMD_VSGI			GITS_CMD_GICv4(3)
 #define GITS_CMD_INVDB			GITS_CMD_GICv4(0xe)
 
 /*
Signature · 1 line
2.20.1
author maintainer
#68 lore raw

PATCH v2 26/36 irqchip/gic-v4.1: Plumb mask/unmask SGI callbacks

In reply to Marc Zyngier

Implement mask/unmask for virtual SGIs by calling into the configuration helper.

---
 drivers/irqchip/irq-gic-v3-its.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
drivers/irqchip/irq-gic-v3-its.c +18 -0
index ef5988724aa3..b9cef419f006 100644
@@ -3667,6 +3667,22 @@ static void its_configure_sgi(struct irq_data *d, bool clear)
 	its_send_single_vcommand(find_4_1_its(), its_build_vsgi_cmd, &desc);
 }
 
+static void its_sgi_mask_irq(struct irq_data *d)
+{
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+
+	vpe->sgi_config[d->hwirq].enabled = false;
+	its_configure_sgi(d, false);
+}
+
+static void its_sgi_unmask_irq(struct irq_data *d)
+{
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+
+	vpe->sgi_config[d->hwirq].enabled = true;
+	its_configure_sgi(d, false);
+}
+
 static int its_sgi_set_affinity(struct irq_data *d,
 				const struct cpumask *mask_val,
 				bool force)
@@ -3676,6 +3692,8 @@ static int its_sgi_set_affinity(struct irq_data *d,
 
 static struct irq_chip its_sgi_irq_chip = {
 	.name			= "GICv4.1-sgi",
+	.irq_mask		= its_sgi_mask_irq,
+	.irq_unmask		= its_sgi_unmask_irq,
 	.irq_set_affinity	= its_sgi_set_affinity,
 };
 
Signature · 1 line
2.20.1
author maintainer
#69 lore raw

PATCH v2 27/36 irqchip/gic-v4.1: Plumb get/set_irqchip_state SGI callbacks

In reply to Marc Zyngier

To implement the get/set_irqchip_state callbacks (limited to the PENDING state), we have to use a particular set of hacks:

- Reading the pending state is done by using a pair of new redistributor registers (GICR_VSGIR, GICR_VSGIPENDR), which allow the 16 interrupts state to be retrieved.
- Setting the pending state is done by generating it as we'd otherwise do for a guest (writing to GITS_SGIR)
- Clearing the pending state is done by emiting a VSGI command with the "clear" bit set.

---
 drivers/irqchip/irq-gic-v3-its.c   | 56 ++++++++++++++++++++++++++++++
 include/linux/irqchip/arm-gic-v3.h | 14 ++++++++
 2 files changed, 70 insertions(+)
drivers/irqchip/irq-gic-v3-its.c +56 -0
index b9cef419f006..58dd497dc274 100644
@@ -3690,11 +3690,67 @@ static int its_sgi_set_affinity(struct irq_data *d,
 	return -EINVAL;
 }
 
+static int its_sgi_set_irqchip_state(struct irq_data *d,
+				     enum irqchip_irq_state which,
+				     bool state)
+{
+	if (which != IRQCHIP_STATE_PENDING)
+		return -EINVAL;
+
+	if (state) {
+		struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+		struct its_node *its = find_4_1_its();
+		u64 val;
+
+		val  = FIELD_PREP(GITS_SGIR_VPEID, vpe->vpe_id);
+		val |= FIELD_PREP(GITS_SGIR_VINTID, d->hwirq);
+		writeq_relaxed(val, its->sgir_base + GITS_SGIR - SZ_128K);
+	} else {
+		its_configure_sgi(d, true);
+	}
+
+	return 0;
+}
+
+static int its_sgi_get_irqchip_state(struct irq_data *d,
+				     enum irqchip_irq_state which, bool *val)
+{
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+	void __iomem *base = gic_data_rdist_cpu(vpe->col_idx)->rd_base + SZ_128K;
+	u32 count = 1000000;	/* 1s! */
+	u32 status;
+
+	if (which != IRQCHIP_STATE_PENDING)
+		return -EINVAL;
+
+	writel_relaxed(vpe->vpe_id, base + GICR_VSGIR);
+	do {
+		status = readl_relaxed(base + GICR_VSGIPENDR);
+		if (!(status & GICR_VSGIPENDR_BUSY))
+			goto out;
+
+		count--;
+		if (!count) {
+			pr_err_ratelimited("Unable to get SGI status\n");
+			goto out;
+		}
+		cpu_relax();
+		udelay(1);
+	} while(count);
+
+out:
+	*val = !!(status & (1 << d->hwirq));
+
+	return 0;
+}
+
 static struct irq_chip its_sgi_irq_chip = {
 	.name			= "GICv4.1-sgi",
 	.irq_mask		= its_sgi_mask_irq,
 	.irq_unmask		= its_sgi_unmask_irq,
 	.irq_set_affinity	= its_sgi_set_affinity,
+	.irq_set_irqchip_state	= its_sgi_set_irqchip_state,
+	.irq_get_irqchip_state	= its_sgi_get_irqchip_state,
 };
 
 static int its_sgi_irq_domain_alloc(struct irq_domain *domain,
include/linux/irqchip/arm-gic-v3.h +14 -0
index c73176d3ab2b..cb8563554ed2 100644
@@ -340,6 +340,15 @@
 #define GICR_VPENDBASER_4_1_VGRP1EN	(1ULL << 58)
 #define GICR_VPENDBASER_4_1_VPEID	GENMASK_ULL(15, 0)
 
+#define GICR_VSGIR			0x0080
+
+#define GICR_VSGIR_VPEID		GENMASK(15, 0)
+
+#define GICR_VSGIPENDR			0x0088
+
+#define GICR_VSGIPENDR_BUSY		(1U << 31)
+#define GICR_VSGIPENDR_PENDING		GENMASK(15, 0)
+
 /*
  * ITS registers, offsets from ITS_base
  */
@@ -363,6 +372,11 @@
 
 #define GITS_TRANSLATER			0x10040
 
+#define GITS_SGIR			0x20020
+
+#define GITS_SGIR_VPEID			GENMASK_ULL(47, 32)
+#define GITS_SGIR_VINTID		GENMASK_ULL(7, 0)
+
 #define GITS_CTLR_ENABLE		(1U << 0)
 #define GITS_CTLR_ImDe			(1U << 1)
 #define	GITS_CTLR_ITS_NUMBER_SHIFT	4
Signature · 1 line
2.20.1
author maintainer
#70 lore raw

PATCH v2 28/36 irqchip/gic-v4.1: Plumb set_vcpu_affinity SGI callbacks

In reply to Marc Zyngier

As for VLPIs, there is a number of configuration bits that cannot be directly communicated through the normal irqchip API, and we have to use our good old friend set_vcpu_affinity.

This is used to configure group and priority for a given vSGI.

---
 drivers/irqchip/irq-gic-v3-its.c   | 18 ++++++++++++++++++
 include/linux/irqchip/arm-gic-v4.h |  5 +++++
 2 files changed, 23 insertions(+)
drivers/irqchip/irq-gic-v3-its.c +18 -0
index 58dd497dc274..03ba4964a7f5 100644
@@ -3744,6 +3744,23 @@ static int its_sgi_get_irqchip_state(struct irq_data *d,
 	return 0;
 }
 
+static int its_sgi_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
+{
+	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
+	struct its_cmd_info *info = vcpu_info;
+
+	switch (info->cmd_type) {
+	case PROP_UPDATE_SGI:
+		vpe->sgi_config[d->hwirq].priority = info->priority;
+		vpe->sgi_config[d->hwirq].group = info->group;
+		its_configure_sgi(d, false);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
 static struct irq_chip its_sgi_irq_chip = {
 	.name			= "GICv4.1-sgi",
 	.irq_mask		= its_sgi_mask_irq,
@@ -3751,6 +3768,7 @@ static struct irq_chip its_sgi_irq_chip = {
 	.irq_set_affinity	= its_sgi_set_affinity,
 	.irq_set_irqchip_state	= its_sgi_set_irqchip_state,
 	.irq_get_irqchip_state	= its_sgi_get_irqchip_state,
+	.irq_set_vcpu_affinity	= its_sgi_set_vcpu_affinity,
 };
 
 static int its_sgi_irq_domain_alloc(struct irq_domain *domain,
include/linux/irqchip/arm-gic-v4.h +5 -0
index 03bbd0aed2e2..6185926e4582 100644
@@ -96,6 +96,7 @@ enum its_vcpu_info_cmd_type {
 	SCHEDULE_VPE,
 	DESCHEDULE_VPE,
 	INVALL_VPE,
+	PROP_UPDATE_SGI,
 };
 
 struct its_cmd_info {
@@ -108,6 +109,10 @@ struct its_cmd_info {
 			bool		g0en;
 			bool		g1en;
 		};
+		struct {
+			u8		priority;
+			bool		group;
+		};
 	};
 };
 
Signature · 1 line
2.20.1
author maintainer
#71 lore raw

PATCH v2 29/36 irqchip/gic-v4.1: Move doorbell management to the GICv4 abstraction layer

In reply to Marc Zyngier

In order to hide some of the differences between v4.0 and v4.1, move the doorbell management out of the KVM code, and into the GICv4-specific layer. This allows the calling code to ask for the doorbell when blocking, and otherwise to leave the doorbell permanently disabled.

This matches the v4.1 code perfectly, and only results in a minor refactoring of the v4.0 code.

---
 drivers/irqchip/irq-gic-v4.c       | 49 +++++++++++++++++++++++++++---
 include/kvm/arm_vgic.h             |  1 +
 include/linux/irqchip/arm-gic-v4.h |  3 +-
 virt/kvm/arm/vgic/vgic-v3.c        |  1 +
 virt/kvm/arm/vgic/vgic-v4.c        | 34 +++++++++------------
 5 files changed, 63 insertions(+), 25 deletions(-)
drivers/irqchip/irq-gic-v4.c +45 -4
index c01910d53f9e..60b51d6683aa 100644
@@ -87,6 +87,11 @@ static struct irq_domain *gic_domain;
 static const struct irq_domain_ops *vpe_domain_ops;
 static const struct irq_domain_ops *sgi_domain_ops;
 
+static bool has_v4_1(void)
+{
+	return !!sgi_domain_ops;
+}
+
 int its_alloc_vcpu_irqs(struct its_vm *vm)
 {
 	int vpe_base_irq, i;
@@ -139,18 +144,54 @@ static int its_send_vpe_cmd(struct its_vpe *vpe, struct its_cmd_info *info)
 	return irq_set_vcpu_affinity(vpe->irq, info);
 }
 
-int its_schedule_vpe(struct its_vpe *vpe, bool on)
+int its_make_vpe_non_resident(struct its_vpe *vpe, bool db)
 {
-	struct its_cmd_info info;
+	struct irq_desc *desc = irq_to_desc(vpe->irq);
+	struct its_cmd_info info = { };
 	int ret;
 
 	WARN_ON(preemptible());
 
-	info.cmd_type = on ? SCHEDULE_VPE : DESCHEDULE_VPE;
+	info.cmd_type = DESCHEDULE_VPE;
+	if (has_v4_1()) {
+		/* GICv4.1 can directly deal with doorbells */
+		info.req_db = db;
+	} else {
+		/* Undo the nested disable_irq() calls... */
+		while (db && irqd_irq_disabled(&desc->irq_data))
+			enable_irq(vpe->irq);
+	}
+
+	ret = its_send_vpe_cmd(vpe, &info);
+	if (!ret)
+		vpe->resident = false;
+
+	return ret;
+}
+
+int its_make_vpe_resident(struct its_vpe *vpe)
+{
+	struct its_cmd_info info = { };
+	int ret;
+
+	WARN_ON(preemptible());
+
+	info.cmd_type = SCHEDULE_VPE;
+	if (has_v4_1()) {
+		/*
+		 * FIXME: This needs to come from the distributor, and
+		 * not be blindly set out of thin air...
+		 */
+		info.g0en = true;
+		info.g1en = true;
+	} else {
+		/* Disabled the doorbell, as we're about to enter the guest */
+		disable_irq_nosync(vpe->irq);
+	}
 
 	ret = its_send_vpe_cmd(vpe, &info);
 	if (!ret)
-		vpe->resident = on;
+		vpe->resident = true;
 
 	return ret;
 }
include/kvm/arm_vgic.h +1 -0
index 4dc58d7a0010..86cb95171eec 100644
@@ -70,6 +70,7 @@ struct vgic_global {
 
 	/* Hardware has GICv4? */
 	bool			has_gicv4;
+	bool			has_gicv4_1;
 
 	/* GIC system register CPU interface */
 	struct static_key_false gicv3_cpuif;
include/linux/irqchip/arm-gic-v4.h +2 -1
index 6185926e4582..084890a5c2bf 100644
@@ -118,7 +118,8 @@ struct its_cmd_info {
 
 int its_alloc_vcpu_irqs(struct its_vm *vm);
 void its_free_vcpu_irqs(struct its_vm *vm);
-int its_schedule_vpe(struct its_vpe *vpe, bool on);
+int its_make_vpe_resident(struct its_vpe *vpe);
+int its_make_vpe_non_resident(struct its_vpe *vpe, bool db);
 int its_invall_vpe(struct its_vpe *vpe);
 int its_map_vlpi(int irq, struct its_vlpi_map *map);
 int its_get_vlpi(int irq, struct its_vlpi_map *map);
virt/kvm/arm/vgic/vgic-v3.c +1 -0
index 48307a9eb1d8..07a33b3e71c5 100644
@@ -595,6 +595,7 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
 	/* GICv4 support? */
 	if (info->has_v4) {
 		kvm_vgic_global_state.has_gicv4 = gicv4_enable;
+		kvm_vgic_global_state.has_gicv4_1 = info->has_v4_1;
 		kvm_info("GICv4 support %sabled\n",
 			 gicv4_enable ? "en" : "dis");
 	}
virt/kvm/arm/vgic/vgic-v4.c +14 -20
index 7e1f3202968a..50f84f4ce903 100644
@@ -67,10 +67,10 @@
  * it. And if we've migrated our vcpu from one CPU to another, we must
  * tell the ITS (so that the messages reach the right redistributor).
  * This is done in two steps: first issue a irq_set_affinity() on the
- * irq corresponding to the vcpu, then call its_schedule_vpe(). You
- * must be in a non-preemptible context. On exit, another call to
- * its_schedule_vpe() tells the redistributor that we're done with the
- * vcpu.
+ * irq corresponding to the vcpu, then call its_make_vpe_resident().
+ * You must be in a non-preemptible context. On exit, a call to
+ * its_make_vpe_non_resident() tells the redistributor that we're done
+ * with the vcpu.
  *
  * Finally, the doorbell handling: Each vcpu is allocated an interrupt
  * which will fire each time a VLPI is made pending whilst the vcpu is
@@ -86,7 +86,8 @@ static irqreturn_t vgic_v4_doorbell_handler(int irq, void *info)
 	struct kvm_vcpu *vcpu = info;
 
 	/* We got the message, no need to fire again */
-	if (!irqd_irq_disabled(&irq_to_desc(irq)->irq_data))
+	if (!kvm_vgic_global_state.has_gicv4_1 &&
+	    !irqd_irq_disabled(&irq_to_desc(irq)->irq_data))
 		disable_irq_nosync(irq);
 
 	vcpu->arch.vgic_cpu.vgic_v3.its_vpe.pending_last = true;
@@ -199,19 +200,11 @@ void vgic_v4_teardown(struct kvm *kvm)
 int vgic_v4_put(struct kvm_vcpu *vcpu, bool need_db)
 {
 	struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe;
-	struct irq_desc *desc = irq_to_desc(vpe->irq);
 
 	if (!vgic_supports_direct_msis(vcpu->kvm) || !vpe->resident)
 		return 0;
 
-	/*
-	 * If blocking, a doorbell is required. Undo the nested
-	 * disable_irq() calls...
-	 */
-	while (need_db && irqd_irq_disabled(&desc->irq_data))
-		enable_irq(vpe->irq);
-
-	return its_schedule_vpe(vpe, false);
+	return its_make_vpe_non_resident(vpe, need_db);
 }
 
 int vgic_v4_load(struct kvm_vcpu *vcpu)
@@ -232,18 +225,19 @@ int vgic_v4_load(struct kvm_vcpu *vcpu)
 	if (err)
 		return err;
 
-	/* Disabled the doorbell, as we're about to enter the guest */
-	disable_irq_nosync(vpe->irq);
-
-	err = its_schedule_vpe(vpe, true);
+	err = its_make_vpe_resident(vpe);
 	if (err)
 		return err;
 
 	/*
 	 * Now that the VPE is resident, let's get rid of a potential
-	 * doorbell interrupt that would still be pending.
+	 * doorbell interrupt that would still be pending. This is a
+	 * GICv4.0 only "feature"...
 	 */
-	return irq_set_irqchip_state(vpe->irq, IRQCHIP_STATE_PENDING, false);
+	if (!kvm_vgic_global_state.has_gicv4_1)
+		err = irq_set_irqchip_state(vpe->irq, IRQCHIP_STATE_PENDING, false);
+
+	return err;
 }
 
 static struct vgic_its *vgic_get_its(struct kvm *kvm,
Signature · 1 line
2.20.1
author maintainer
#72 lore raw

PATCH v2 30/36 irqchip/gic-v4.1: Add VSGI allocation/teardown

In reply to Marc Zyngier

Allocate per-VPE SGIs when initializing the GIC-specific part of the VPE data structure.

---
 drivers/irqchip/irq-gic-v4.c       | 68 +++++++++++++++++++++++++++++-
 include/linux/irqchip/arm-gic-v4.h |  2 +
 2 files changed, 69 insertions(+), 1 deletion(-)
drivers/irqchip/irq-gic-v4.c +67 -1
index 60b51d6683aa..a29a063861bc 100644
@@ -92,6 +92,47 @@ static bool has_v4_1(void)
 	return !!sgi_domain_ops;
 }
 
+static int its_alloc_vcpu_sgis(struct its_vpe *vpe, int idx)
+{
+	char *name;
+	int sgi_base;
+
+	if (!has_v4_1())
+		return 0;
+
+	name = kasprintf(GFP_KERNEL, "GICv4-sgi-%d", task_pid_nr(current));
+	if (!name)
+		goto err;
+
+	vpe->fwnode = irq_domain_alloc_named_id_fwnode(name, idx);
+	if (!vpe->fwnode)
+		goto err;
+
+	kfree(name);
+	name = NULL;
+
+	vpe->sgi_domain = irq_domain_create_linear(vpe->fwnode, 16,
+						   sgi_domain_ops, vpe);
+	if (!vpe->sgi_domain)
+		goto err;
+
+	sgi_base = __irq_domain_alloc_irqs(vpe->sgi_domain, -1, 16,
+					       NUMA_NO_NODE, vpe,
+					       false, NULL);
+	if (sgi_base <= 0)
+		goto err;
+
+	return 0;
+
+err:
+	if (vpe->sgi_domain)
+		irq_domain_remove(vpe->sgi_domain);
+	if (vpe->fwnode)
+		irq_domain_free_fwnode(vpe->fwnode);
+	kfree(name);
+	return -ENOMEM;
+}
+
 int its_alloc_vcpu_irqs(struct its_vm *vm)
 {
 	int vpe_base_irq, i;
@@ -118,8 +159,13 @@ int its_alloc_vcpu_irqs(struct its_vm *vm)
 	if (vpe_base_irq <= 0)
 		goto err;
 
-	for (i = 0; i < vm->nr_vpes; i++)
+	for (i = 0; i < vm->nr_vpes; i++) {
+		int ret;
 		vm->vpes[i]->irq = vpe_base_irq + i;
+		ret = its_alloc_vcpu_sgis(vm->vpes[i], i);
+		if (ret)
+			goto err;
+	}
 
 	return 0;
 
@@ -132,8 +178,28 @@ int its_alloc_vcpu_irqs(struct its_vm *vm)
 	return -ENOMEM;
 }
 
+static void its_free_sgi_irqs(struct its_vm *vm)
+{
+	int i;
+
+	if (!has_v4_1())
+		return;
+
+	for (i = 0; i < vm->nr_vpes; i++) {
+		unsigned int irq = irq_find_mapping(vm->vpes[i]->sgi_domain, 0);
+
+		if (WARN_ON(!irq))
+			continue;
+
+		irq_domain_free_irqs(irq, 16);
+		irq_domain_remove(vm->vpes[i]->sgi_domain);
+		irq_domain_free_fwnode(vm->vpes[i]->fwnode);
+	}
+}
+
 void its_free_vcpu_irqs(struct its_vm *vm)
 {
+	its_free_sgi_irqs(vm);
 	irq_domain_free_irqs(vm->vpes[0]->irq, vm->nr_vpes);
 	irq_domain_remove(vm->domain);
 	irq_domain_free_fwnode(vm->fwnode);
include/linux/irqchip/arm-gic-v4.h +2 -0
index 084890a5c2bf..5578cbe7430b 100644
@@ -47,6 +47,8 @@ struct its_vpe {
 		};
 		/* GICv4.1 implementations */
 		struct {
+			struct fwnode_handle	*fwnode;
+			struct irq_domain	*sgi_domain;
 			struct {
 				u8	priority;
 				bool	enabled;
Signature · 1 line
2.20.1
author maintainer
#73 lore raw

PATCH v2 31/36 irqchip/gic-v4.1: Add VSGI property setup

In reply to Marc Zyngier

Add the SGI configuration entry point for KVM to use.

---
 drivers/irqchip/irq-gic-v4.c       | 13 +++++++++++++
 include/linux/irqchip/arm-gic-v4.h |  1 +
 2 files changed, 14 insertions(+)
drivers/irqchip/irq-gic-v4.c +13 -0
index a29a063861bc..b937e51a9178 100644
@@ -324,6 +324,19 @@ int its_prop_update_vlpi(int irq, u8 config, bool inv)
 	return irq_set_vcpu_affinity(irq, &info);
 }
 
+int its_prop_update_vsgi(int irq, u8 priority, bool group)
+{
+	struct its_cmd_info info = {
+		.cmd_type = PROP_UPDATE_SGI,
+		{
+			.priority	= priority,
+			.group		= group,
+		},
+	};
+
+	return irq_set_vcpu_affinity(irq, &info);
+}
+
 int its_init_v4(struct irq_domain *domain,
 		const struct irq_domain_ops *vpe_ops,
 		const struct irq_domain_ops *sgi_ops)
include/linux/irqchip/arm-gic-v4.h +1 -0
index 5578cbe7430b..b894796df9ab 100644
@@ -127,6 +127,7 @@ int its_map_vlpi(int irq, struct its_vlpi_map *map);
 int its_get_vlpi(int irq, struct its_vlpi_map *map);
 int its_unmap_vlpi(int irq);
 int its_prop_update_vlpi(int irq, u8 config, bool inv);
+int its_prop_update_vsgi(int irq, u8 priority, bool group);
 
 struct irq_domain_ops;
 int its_init_v4(struct irq_domain *domain,
Signature · 1 line
2.20.1
author maintainer
#74 lore raw

PATCH v2 32/36 irqchip/gic-v4.1: Eagerly vmap vPEs

In reply to Marc Zyngier

Now that we have HW-accelerated SGIs being delivered to VPEs, it becomes required to map the VPEs on all ITSs instead of relying on the lazy approach that we would use when using the ITS-list mechanism.

---
 drivers/irqchip/irq-gic-v3-its.c | 39 +++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 8 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +31 -8
index 03ba4964a7f5..796c5937ec15 100644
@@ -1419,12 +1419,31 @@ static int its_irq_set_irqchip_state(struct irq_data *d,
 	return 0;
 }
 
+/*
+ * Two favourable cases:
+ *
+ * (a) Either we have a GICv4.1, and all vPEs have to be mapped at all times
+ *     for vSGI delivery
+ *
+ * (b) Or the ITSs do not use a list map, meaning that VMOVP is cheap enough
+ *     and we're better off mapping all VPEs always
+ *
+ * If neither (a) nor (b) is true, then we map vPEs on demand.
+ *
+ */
+static bool gic_requires_eager_mapping(void)
+{
+	if (!its_list_map || gic_rdists->has_rvpeid)
+		return true;
+
+	return false;
+}
+
 static void its_map_vm(struct its_node *its, struct its_vm *vm)
 {
 	unsigned long flags;
 
-	/* Not using the ITS list? Everything is always mapped. */
-	if (!its_list_map)
+	if (gic_requires_eager_mapping())
 		return;
 
 	raw_spin_lock_irqsave(&vmovp_lock, flags);
@@ -1458,7 +1477,7 @@ static void its_unmap_vm(struct its_node *its, struct its_vm *vm)
 	unsigned long flags;
 
 	/* Not using the ITS list? Everything is always mapped. */
-	if (!its_list_map)
+	if (gic_requires_eager_mapping())
 		return;
 
 	raw_spin_lock_irqsave(&vmovp_lock, flags);
@@ -3964,8 +3983,12 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain,
 	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
 	struct its_node *its;
 
-	/* If we use the list map, we issue VMAPP on demand... */
-	if (its_list_map)
+	/*
+	 * If we use the list map, we issue VMAPP on demand... Unless
+	 * we're on a GICv4.1 and we eagerly map the VPE on all ITSs
+	 * so that VSGIs can work.
+	 */
+	if (!gic_requires_eager_mapping())
 		return 0;
 
 	/* Map the VPE to the first possible CPU */
@@ -3991,10 +4014,10 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain,
 	struct its_node *its;
 
 	/*
-	 * If we use the list map, we unmap the VPE once no VLPIs are
-	 * associated with the VM.
+	 * If we use the list map on GICv4.0, we unmap the VPE once no
+	 * VLPIs are associated with the VM.
 	 */
-	if (its_list_map)
+	if (!gic_requires_eager_mapping())
 		return;
 
 	list_for_each_entry(its, &its_nodes, entry) {
Signature · 1 line
2.20.1
author maintainer
#75 lore raw

PATCH v2 33/36 KVM: arm64: GICv4.1: Let doorbells be auto-enabled

In reply to Marc Zyngier

As GICv4.1 understands the life cycle of doorbells (instead of just randomly firing them at the most inconvenient time), just enable them at irq_request time, and be done with it.

---
 virt/kvm/arm/vgic/vgic-v4.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
virt/kvm/arm/vgic/vgic-v4.c +8 -1
index 50f84f4ce903..e61c7a149515 100644
@@ -141,6 +141,7 @@ int vgic_v4_init(struct kvm *kvm)
 
 	kvm_for_each_vcpu(i, vcpu, kvm) {
 		int irq = dist->its_vm.vpes[i]->irq;
+		unsigned long irq_flags = DB_IRQ_FLAGS;
 
 		/*
 		 * Don't automatically enable the doorbell, as we're
@@ -148,8 +149,14 @@ int vgic_v4_init(struct kvm *kvm)
 		 * blocked. Also disable the lazy disabling, as the
 		 * doorbell could kick us out of the guest too
 		 * early...
+		 *
+		 * On GICv4.1, the doorbell is managed in HW and must
+		 * be left enabled.
 		 */
-		irq_set_status_flags(irq, DB_IRQ_FLAGS);
+		if (kvm_vgic_global_state.has_gicv4_1)
+			irq_flags &= ~IRQ_NOAUTOEN;
+		irq_set_status_flags(irq, irq_flags);
+
 		ret = request_irq(irq, vgic_v4_doorbell_handler,
 				  0, "vcpu", vcpu);
 		if (ret) {
Signature · 1 line
2.20.1
author maintainer
#76 lore raw

PATCH v2 34/36 KVM: arm64: GICv4.1: Add direct injection capability to SGI registers

In reply to Marc Zyngier

Most of the GICv3 emulation code that deals with SGIs now has to be aware of the v4.1 capabilities in order to benefit from it.

Add such support, keyed on the interrupt having the hw flag set and being a SGI.

---
 virt/kvm/arm/vgic/vgic-mmio-v3.c | 15 +++++-
 virt/kvm/arm/vgic/vgic-mmio.c    | 88 ++++++++++++++++++++++++++++++--
 2 files changed, 96 insertions(+), 7 deletions(-)
virt/kvm/arm/vgic/vgic-mmio-v3.c +13 -2
index 7dfd15dbb308..d73f4ffd7d36 100644
@@ -6,6 +6,7 @@
 #include <linux/irqchip/arm-gic-v3.h>
 #include <linux/kvm.h>
 #include <linux/kvm_host.h>
+#include <linux/interrupt.h>
 #include <kvm/iodev.h>
 #include <kvm/arm_vgic.h>
 
@@ -939,8 +940,18 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1)
 		 * generate interrupts of either group.
 		 */
 		if (!irq->group || allow_group1) {
-			irq->pending_latch = true;
-			vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
+			if (!irq->hw) {
+				irq->pending_latch = true;
+				vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
+			} else {
+				/* HW SGI? Ask the GIC to inject it */
+				int err;
+				err = irq_set_irqchip_state(irq->host_irq,
+							    IRQCHIP_STATE_PENDING,
+							    true);
+				WARN_RATELIMIT(err, "IRQ %d", irq->host_irq);
+				raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
+			}
 		} else {
 			raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
 		}
virt/kvm/arm/vgic/vgic-mmio.c +83 -5

151 lines not on this page · raw message

Signature · 1 line
2.20.1
author maintainer
#77 lore raw

PATCH v2 35/36 KVM: arm64: GICv4.1: Configure SGIs as HW interrupts

In reply to Marc Zyngier

To enable HW delivery of SGIs, set their hw flag to true, link them to the corresponding Linux interrupt, which is then activated.

This configures the SGIs at the ITS level, and put the show on the road!

---
 virt/kvm/arm/vgic/vgic-v4.c | 42 +++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
virt/kvm/arm/vgic/vgic-v4.c +42 -0
index e61c7a149515..0ff6dac16cba 100644
@@ -97,6 +97,46 @@ static irqreturn_t vgic_v4_doorbell_handler(int irq, void *info)
 	return IRQ_HANDLED;
 }
 
+static void vgic_v4_sync_sgi_config(struct its_vpe *vpe, struct vgic_irq *irq)
+{
+	vpe->sgi_config[irq->intid].enabled	= irq->enabled;
+	vpe->sgi_config[irq->intid].group 	= irq->group;
+	vpe->sgi_config[irq->intid].priority	= irq->priority;
+}
+
+static void vgic_v4_init_vsgis(struct kvm_vcpu *vcpu)
+{
+	struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe;
+	int i;
+
+	if (!kvm_vgic_global_state.has_gicv4_1)
+		return;
+
+	/*
+	 * With GICv4.1, every virtual SGI can be directly injected. So
+	 * let's pretend that they are HW interrupts, tied to a host
+	 * IRQ. The SGI code will do its magic.
+	 */
+	for (i = 0; i < VGIC_NR_SGIS; i++) {
+		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, i);
+		struct irq_desc *desc;
+		int ret;
+
+		irq->hw = true;
+		irq->host_irq = irq_find_mapping(vpe->sgi_domain, i);
+		vgic_v4_sync_sgi_config(vpe, irq);
+		/*
+		 * SGIs are initialised as disabled. Enable them if
+		 * required by the rest of the VGIC init code.
+		 */
+		desc = irq_to_desc(irq->host_irq);
+		ret = irq_domain_activate_irq(irq_desc_get_irq_data(desc),
+					      false);
+		WARN_ON(ret);
+		vgic_put_irq(vcpu->kvm, irq);
+	}
+}
+
 /**
  * vgic_v4_init - Initialize the GICv4 data structures
  * @kvm:	Pointer to the VM being initialized
@@ -168,6 +208,8 @@ int vgic_v4_init(struct kvm *kvm)
 			dist->its_vm.nr_vpes = i;
 			break;
 		}
+
+		vgic_v4_init_vsgis(vcpu);
 	}
 
 	if (ret)
Signature · 1 line
2.20.1
author maintainer
#78 lore raw

PATCH v2 36/36 KVM: arm64: GICv4.1: Expose HW-based SGIs in debugfs

In reply to Marc Zyngier

The vgic-state debugfs file could do with showing the pending state of the HW-backed SGIs. Plug it into the low-level code.

---
 virt/kvm/arm/vgic/vgic-debug.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)
virt/kvm/arm/vgic/vgic-debug.c +13 -1
index cc12fe9b2df3..98f3242a34a6 100644
@@ -178,6 +178,8 @@ static void print_irq_state(struct seq_file *s, struct vgic_irq *irq,
 			    struct kvm_vcpu *vcpu)
 {
 	char *type;
+	bool pending;
+
 	if (irq->intid < VGIC_NR_SGIS)
 		type = "SGI";
 	else if (irq->intid < VGIC_NR_PRIVATE_IRQS)
@@ -190,6 +192,16 @@ static void print_irq_state(struct seq_file *s, struct vgic_irq *irq,
 	if (irq->intid ==0 || irq->intid == VGIC_NR_PRIVATE_IRQS)
 		print_header(s, irq, vcpu);
 
+	pending = irq->pending_latch;
+	if (irq->hw && vgic_irq_is_sgi(irq->intid)) {
+		int err;
+
+		err = irq_get_irqchip_state(irq->host_irq,
+					    IRQCHIP_STATE_PENDING,
+					    &pending);
+		WARN_ON_ONCE(err);
+	}
+		
 	seq_printf(s, "       %s %4d "
 		      "    %2d "
 		      "%d%d%d%d%d%d%d "
@@ -201,7 +213,7 @@ static void print_irq_state(struct seq_file *s, struct vgic_irq *irq,
 		      "\n",
 			type, irq->intid,
 			(irq->target_vcpu) ? irq->target_vcpu->vcpu_id : -1,
-			irq->pending_latch,
+		   	pending,
 			irq->line_level,
 			irq->active,
 			irq->enabled,
Signature · 1 line
2.20.1