diff mbox series

[RFC,2/2] irqchip/gic-v3-its: Introduce virtual ITS

Message ID 20210622155313.3819952-3-boqun.feng@gmail.com (mailing list archive)
State New, archived
Headers show
Series irqchip/gic-v3-its: Introduce virtual ITS | expand

Commit Message

Boqun Feng June 22, 2021, 3:53 p.m. UTC
GICv3 allows supporting LPI without an ITS, and in order to support
such a platform, a virtual ITS is introduced. The virtual ITS has the
same software part as a real ITS: having an irq domain, maintaining
->collections and maintaining the list of devices. The only difference
is the virtual ITS doesn't have a backed ITS therefore it cannot issue
ITS commands nor set up device tables. The virtual ITS only manages LPIs
and the LPIs are configured via DirectLPI interfaces.

And currently the virtual ITS is initialized only if there is no ITS in
the system and yet DirectLPI is support.

The virtual ITS approach provides the support for LPI without an ITS and
reuses as much exisiting code as possible, and is the preparation for
virtual PCI support on ARM64 Hyper-V guests.

Co-developed-by: Sunil Muthuswamy <sunilmut@microsoft.com>
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
---
 drivers/irqchip/irq-gic-v3-its.c | 114 ++++++++++++++++++++++++++++---
 1 file changed, 106 insertions(+), 8 deletions(-)

Comments

Marc Zyngier June 22, 2021, 5:17 p.m. UTC | #1
On Tue, 22 Jun 2021 16:53:13 +0100,
Boqun Feng <boqun.feng@gmail.com> wrote:
> 
> GICv3 allows supporting LPI without an ITS, and in order to support
> such a platform, a virtual ITS is introduced. The virtual ITS has the
> same software part as a real ITS: having an irq domain, maintaining
> ->collections and maintaining the list of devices. The only difference
> is the virtual ITS doesn't have a backed ITS therefore it cannot issue
> ITS commands nor set up device tables. The virtual ITS only manages LPIs
> and the LPIs are configured via DirectLPI interfaces.

That's not a virtual ITS. Not at all. It isn't even the shadow of an
ITS. This is... something else.

> 
> And currently the virtual ITS is initialized only if there is no ITS in
> the system and yet DirectLPI is support.
> 
> The virtual ITS approach provides the support for LPI without an ITS and
> reuses as much exisiting code as possible, and is the preparation for
> virtual PCI support on ARM64 Hyper-V guests.

<rant>
There is no translation, no isolation. This is a yet another
sorry excuse for a hack. Why can't the Hyper-V folks implement the
architecture, only the architecture, and all of it?
</rant>

> 
> Co-developed-by: Sunil Muthuswamy <sunilmut@microsoft.com>
> Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
> ---
>  drivers/irqchip/irq-gic-v3-its.c | 114 ++++++++++++++++++++++++++++---
>  1 file changed, 106 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
> index 1916ac5d6371..4f2600377039 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -117,9 +117,16 @@ struct its_node {
>  	int			vlpi_redist_offset;
>  };
>  
> +/*
> + * LPI can be supported without ITS, in which case, a virtual its_node is
> + * initialized to allow configuring LPI with the DirectLPI approach.
> + */
> +static struct its_node *virtual_its_node;
> +
>  #define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
>  #define is_v4_1(its)		(!!((its)->typer & GITS_TYPER_VMAPP))
>  #define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
> +#define is_virtual(its)		((its) == virtual_its_node)

And you can only have one?

>  
>  #define ITS_ITT_ALIGN		SZ_256
>  
> @@ -1096,6 +1103,10 @@ void name(struct its_node *its,						\
>  	unsigned long flags;						\
>  	u64 rd_idx;							\
>  									\
> +	/* Virtual ITS doesn't support ITS commands */			\
> +	if (is_virtual(its))						\
> +		return;							\
> +									\

Oh gawd...

>  	raw_spin_lock_irqsave(&its->lock, flags);			\
>  									\
>  	cmd = its_allocate_entry(its);					\
> @@ -1464,7 +1475,8 @@ 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 &&
> -	    (is_v4_1(its_dev->its) || !irqd_is_forwarded_to_vcpu(d)))
> +	    (is_v4_1(its_dev->its) || !irqd_is_forwarded_to_vcpu(d) ||
> +	     is_virtual(its_dev->its)))
>  		direct_lpi_inv(d);
>  	else if (!irqd_is_forwarded_to_vcpu(d))
>  		its_send_inv(its_dev, its_get_event_id(d));
> @@ -1690,6 +1702,10 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
>  	u64 addr;
>  
>  	its = its_dev->its;
> +
> +	/* Virtual ITS doesn't have ->get_msi_base function, skip */
> +	if (!its->get_msi_base)
> +		return;

So how do you target a redistributor? If you are going to use
DirectLPI, this should hit the GICR_SETLPIR for the relevant
redistributor.

Actually, how do you target another redistributor? You can't send a
MOVI, and you don't change the target address. And even if you could,
how do you move the pending state from one pending table to another?

Which means you probably have some other, non architectural stuff
somewhere else.

[...]

I'll stop here. I'm not taking this hack built on top of the ITS
code. Not now, not ever. If you really want to implement something
that is outside of the scope of the architecture, do it outside of the
GICv3 code, because this is just pretending to follow the architecture.

Or even better, get the Hyper-V folks to implement a *real* virtual
ITS, in the hypervisor. Yes, this is hard.

	M.
diff mbox series

Patch

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 1916ac5d6371..4f2600377039 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -117,9 +117,16 @@  struct its_node {
 	int			vlpi_redist_offset;
 };
 
+/*
+ * LPI can be supported without ITS, in which case, a virtual its_node is
+ * initialized to allow configuring LPI with the DirectLPI approach.
+ */
+static struct its_node *virtual_its_node;
+
 #define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
 #define is_v4_1(its)		(!!((its)->typer & GITS_TYPER_VMAPP))
 #define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
+#define is_virtual(its)		((its) == virtual_its_node)
 
 #define ITS_ITT_ALIGN		SZ_256
 
@@ -1096,6 +1103,10 @@  void name(struct its_node *its,						\
 	unsigned long flags;						\
 	u64 rd_idx;							\
 									\
+	/* Virtual ITS doesn't support ITS commands */			\
+	if (is_virtual(its))						\
+		return;							\
+									\
 	raw_spin_lock_irqsave(&its->lock, flags);			\
 									\
 	cmd = its_allocate_entry(its);					\
@@ -1464,7 +1475,8 @@  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 &&
-	    (is_v4_1(its_dev->its) || !irqd_is_forwarded_to_vcpu(d)))
+	    (is_v4_1(its_dev->its) || !irqd_is_forwarded_to_vcpu(d) ||
+	     is_virtual(its_dev->its)))
 		direct_lpi_inv(d);
 	else if (!irqd_is_forwarded_to_vcpu(d))
 		its_send_inv(its_dev, its_get_event_id(d));
@@ -1690,6 +1702,10 @@  static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
 	u64 addr;
 
 	its = its_dev->its;
+
+	/* Virtual ITS doesn't have ->get_msi_base function, skip */
+	if (!its->get_msi_base)
+		return;
 	addr = its->get_msi_base(its_dev);
 
 	msg->address_lo		= lower_32_bits(addr);
@@ -3217,7 +3233,17 @@  static void its_cpu_init_collections(void)
 	raw_spin_lock(&its_lock);
 
 	list_for_each_entry(its, &its_nodes, entry)
-		its_cpu_init_collection(its);
+		/*
+		 * Only initializes the software part of collections for virtual
+		 * ITS.
+		 */
+		if (is_virtual(its)) {
+			int cpu = smp_processor_id();
+
+			its->collections[cpu].col_id = cpu;
+		} else {
+			its_cpu_init_collection(its);
+		}
 
 	raw_spin_unlock(&its_lock);
 }
@@ -3364,7 +3390,8 @@  static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
 	int nr_ites;
 	int sz;
 
-	if (!its_alloc_device_table(its, dev_id))
+	/* No need to allocate device table for virtual ITS */
+	if (!is_virtual(its) && !its_alloc_device_table(its, dev_id))
 		return NULL;
 
 	if (WARN_ON(!is_power_of_2(nvecs)))
@@ -3551,9 +3578,12 @@  static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 	if (err)
 		return err;
 
-	err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev));
-	if (err)
-		return err;
+	/* Virtual ITS doesn't have ->get_msi_base function, skip */
+	if (its->get_msi_base) {
+		err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev));
+		if (err)
+			return err;
+	}
 
 	for (i = 0; i < nr_irqs; i++) {
 		err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
@@ -5121,6 +5151,63 @@  static int __init its_probe_one(struct resource *res,
 	return err;
 }
 
+static int __init virtual_its_init(void)
+{
+	struct its_node *its;
+	struct fwnode_handle *fwnode;
+	int err;
+
+	fwnode = irq_domain_alloc_named_fwnode("Virtual ITS");
+	if (!fwnode)
+		return -ENOMEM;
+
+	/*
+	 * Use 0 as the ID for virtual ITS, since we only init virtual ITS if
+	 * there is no real ITS in the system, so it's fine.
+	 */
+	err = iort_register_domain_token(0, 0, fwnode);
+	if (err)
+		goto out_free_fwnode;
+
+	its = kzalloc(sizeof(*its), GFP_KERNEL);
+	if (!its) {
+		err = -ENOMEM;
+		goto out_unregister_fwnode;
+	}
+
+	raw_spin_lock_init(&its->lock);
+	mutex_init(&its->dev_alloc_lock);
+	INIT_LIST_HEAD(&its->entry);
+	INIT_LIST_HEAD(&its->its_device_list);
+	its->numa_node = NUMA_NO_NODE;
+	its->msi_domain_flags = IRQ_DOMAIN_FLAG_MSI_REMAP;
+	err = its_alloc_collections(its);
+	if (err)
+		goto out_free_its;
+
+	err = its_init_domain(fwnode, its);
+	if (err)
+		goto out_free_collections;
+
+	raw_spin_lock(&its_lock);
+	virtual_its_node = its;
+	list_add(&its->entry, &its_nodes);
+	raw_spin_unlock(&its_lock);
+
+	return 0;
+
+out_free_collections:
+	its_free_collections(its);
+out_free_its:
+	kvfree(its);
+out_unregister_fwnode:
+	iort_deregister_domain_token(0);
+out_free_fwnode:
+	irq_domain_free_fwnode(fwnode);
+	return err;
+
+}
+
 static bool gic_rdists_supports_plpis(void)
 {
 	return !!(gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS);
@@ -5414,8 +5501,19 @@  int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 		its_acpi_probe();
 
 	if (list_empty(&its_nodes)) {
-		pr_warn("ITS: No ITS available, not enabling LPIs\n");
-		return -ENXIO;
+		/* Initialize virtual ITS only if DirectLPI is set. */
+		if (gic_rdists->has_direct_lpi) {
+			pr_info("ITS: No ITS available, using virtual ITS\n");
+			err = virtual_its_init();
+			if (err) {
+				pr_info("ITS: Virtual ITS creation fails\n");
+				return err;
+			}
+			pr_info("ITS: Virtual ITS created\n");
+		} else {
+			pr_warn("ITS: No ITS available, not enabling LPIs\n");
+			return -ENXIO;
+		}
 	}
 
 	err = allocate_lpi_tables();