lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <59c6a714-e862-7d04-3843-7e2ad349764a@loongson.cn>
Date: Wed, 19 Mar 2025 13:55:09 +0800
From: Tianyang Zhang <zhangtianyang@...ngson.cn>
To: chenhuacai@...nel.org, kernel@...0n.name, corbet@....net,
 alexs@...nel.org, si.yanteng@...ux.dev, tglx@...utronix.de,
 jiaxun.yang@...goat.com, peterz@...radead.org, wangliupu@...ngson.cn,
 lvjianmin@...ngson.cn, maobibo@...ngson.cn, siyanteng@...oftware.com.cn,
 gaosong@...ngson.cn, yangtiezhu@...ngson.cn
Cc: loongarch@...ts.linux.dev, linux-doc@...r.kernel.org,
 linux-kernel@...r.kernel.org
Subject: Re: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support

Hi, Everyone

We've got feedback on issues from this patch. Please disregard v1. I'll 
test and push v2 soon.

在 2025/3/17 下午4:22, Tianyang Zhang 写道:
> The main function of the Redirected interrupt controller is to manage the
> redirected-interrupt table, which consists of many redirected entries.
> When MSI interrupts are requested, the driver creates a corresponding
> redirected entry that describes the target CPU/vector number and the
> operating mode of the interrupt. The redirected interrupt module has an
> independent cache, and during the interrupt routing process, it will
> prioritize the redirected entries that hit the cache. The driver
> invalidates certain entry caches via a command queue.
>
> Co-developed-by: Liupu Wang <wangliupu@...ngson.cn>
> Signed-off-by: Liupu Wang <wangliupu@...ngson.cn>
> Signed-off-by: Tianyang Zhang <zhangtianyang@...ngson.cn>
> ---
>   arch/loongarch/include/asm/cpu-features.h |   1 +
>   arch/loongarch/include/asm/cpu.h          |   2 +
>   arch/loongarch/include/asm/loongarch.h    |   6 +
>   arch/loongarch/kernel/cpu-probe.c         |   3 +
>   drivers/irqchip/Makefile                  |   2 +-
>   drivers/irqchip/irq-loongarch-avec.c      |  21 +-
>   drivers/irqchip/irq-loongarch-ir.c        | 561 ++++++++++++++++++++++
>   drivers/irqchip/irq-loongson.h            |  12 +
>   include/linux/cpuhotplug.h                |   1 +
>   9 files changed, 595 insertions(+), 14 deletions(-)
>   create mode 100644 drivers/irqchip/irq-loongarch-ir.c
>
> diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
> index fc83bb32f9f0..03f7e93e81e0 100644
> --- a/arch/loongarch/include/asm/cpu-features.h
> +++ b/arch/loongarch/include/asm/cpu-features.h
> @@ -68,5 +68,6 @@
>   #define cpu_has_ptw		cpu_opt(LOONGARCH_CPU_PTW)
>   #define cpu_has_lspw		cpu_opt(LOONGARCH_CPU_LSPW)
>   #define cpu_has_avecint		cpu_opt(LOONGARCH_CPU_AVECINT)
> +#define cpu_has_redirectint	cpu_opt(LOONGARCH_CPU_REDIRECTINT)
>   
>   #endif /* __ASM_CPU_FEATURES_H */
> diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
> index 98cf4d7b4b0a..33cd96e569d8 100644
> --- a/arch/loongarch/include/asm/cpu.h
> +++ b/arch/loongarch/include/asm/cpu.h
> @@ -102,6 +102,7 @@ enum cpu_type_enum {
>   #define CPU_FEATURE_PTW			27	/* CPU has hardware page table walker */
>   #define CPU_FEATURE_LSPW		28	/* CPU has LSPW (lddir/ldpte instructions) */
>   #define CPU_FEATURE_AVECINT		29	/* CPU has AVEC interrupt */
> +#define CPU_FEATURE_REDIRECTINT		30      /* CPU has interrupt remmap */
>   
>   #define LOONGARCH_CPU_CPUCFG		BIT_ULL(CPU_FEATURE_CPUCFG)
>   #define LOONGARCH_CPU_LAM		BIT_ULL(CPU_FEATURE_LAM)
> @@ -133,5 +134,6 @@ enum cpu_type_enum {
>   #define LOONGARCH_CPU_PTW		BIT_ULL(CPU_FEATURE_PTW)
>   #define LOONGARCH_CPU_LSPW		BIT_ULL(CPU_FEATURE_LSPW)
>   #define LOONGARCH_CPU_AVECINT		BIT_ULL(CPU_FEATURE_AVECINT)
> +#define LOONGARCH_CPU_REDIRECTINT	BIT_ULL(CPU_FEATURE_REDIRECTINT)
>   
>   #endif /* _ASM_CPU_H */
> diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
> index 52651aa0e583..95e06cb6831e 100644
> --- a/arch/loongarch/include/asm/loongarch.h
> +++ b/arch/loongarch/include/asm/loongarch.h
> @@ -1130,6 +1130,7 @@
>   #define  IOCSRF_FLATMODE		BIT_ULL(10)
>   #define  IOCSRF_VM			BIT_ULL(11)
>   #define  IOCSRF_AVEC			BIT_ULL(15)
> +#define  IOCSRF_REDIRECTINT		BIT_ULL(16)
>   
>   #define LOONGARCH_IOCSR_VENDOR		0x10
>   
> @@ -1189,6 +1190,11 @@
>   
>   #define LOONGARCH_IOCSR_EXTIOI_NODEMAP_BASE	0x14a0
>   #define LOONGARCH_IOCSR_EXTIOI_IPMAP_BASE	0x14c0
> +#define LOONGARCH_IOCSR_REDIRECT_CFG		0x15e0
> +#define LOONGARCH_IOCSR_REDIRECT_TBR		0x15e8  /* IRT BASE REG*/
> +#define LOONGARCH_IOCSR_REDIRECT_CQB		0x15f0  /* IRT CACHE QUEUE BASE */
> +#define LOONGARCH_IOCSR_REDIRECT_CQH		0x15f8  /* IRT CACHE QUEUE HEAD, 32bit */
> +#define LOONGARCH_IOCSR_REDIRECT_CQT		0x15fc  /* IRT CACHE QUEUE TAIL, 32bit */
>   #define LOONGARCH_IOCSR_EXTIOI_EN_BASE		0x1600
>   #define LOONGARCH_IOCSR_EXTIOI_BOUNCE_BASE	0x1680
>   #define LOONGARCH_IOCSR_EXTIOI_ISR_BASE		0x1800
> diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
> index fedaa67cde41..89a41c9ba0c0 100644
> --- a/arch/loongarch/kernel/cpu-probe.c
> +++ b/arch/loongarch/kernel/cpu-probe.c
> @@ -289,6 +289,9 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
>   		c->options |= LOONGARCH_CPU_EIODECODE;
>   	if (config & IOCSRF_AVEC)
>   		c->options |= LOONGARCH_CPU_AVECINT;
> +	if (config & IOCSRF_REDIRECTINT)
> +		c->options |= LOONGARCH_CPU_REDIRECTINT;
> +	c->options |= LOONGARCH_CPU_REDIRECTINT;
>   	if (config & IOCSRF_VM)
>   		c->options |= LOONGARCH_CPU_HYPERVISOR;
>   }
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 25e9ad29b8c4..5dd7d6b151d9 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -113,7 +113,7 @@ obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
>   obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
>   obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.o
>   obj-$(CONFIG_TI_PRUSS_INTC)		+= irq-pruss-intc.o
> -obj-$(CONFIG_IRQ_LOONGARCH_CPU)		+= irq-loongarch-cpu.o irq-loongarch-avec.o
> +obj-$(CONFIG_IRQ_LOONGARCH_CPU)		+= irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o
>   obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.o
>   obj-$(CONFIG_LOONGSON_EIOINTC)		+= irq-loongson-eiointc.o
>   obj-$(CONFIG_LOONGSON_HTPIC)		+= irq-loongson-htpic.o
> diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c
> index 80e55955a29f..a2a8c819cfb1 100644
> --- a/drivers/irqchip/irq-loongarch-avec.c
> +++ b/drivers/irqchip/irq-loongarch-avec.c
> @@ -24,7 +24,6 @@
>   #define VECTORS_PER_REG		64
>   #define IRR_VECTOR_MASK		0xffUL
>   #define IRR_INVALID_MASK	0x80000000UL
> -#define AVEC_MSG_OFFSET		0x100000
>   
>   #ifdef CONFIG_SMP
>   struct pending_list {
> @@ -47,15 +46,6 @@ struct avecintc_chip {
>   
>   static struct avecintc_chip loongarch_avec;
>   
> -struct avecintc_data {
> -	struct list_head	entry;
> -	unsigned int		cpu;
> -	unsigned int		vec;
> -	unsigned int		prev_cpu;
> -	unsigned int		prev_vec;
> -	unsigned int		moving;
> -};
> -
>   static inline void avecintc_enable(void)
>   {
>   	u64 value;
> @@ -85,7 +75,7 @@ static inline void pending_list_init(int cpu)
>   	INIT_LIST_HEAD(&plist->head);
>   }
>   
> -static void avecintc_sync(struct avecintc_data *adata)
> +void avecintc_sync(struct avecintc_data *adata)
>   {
>   	struct pending_list *plist;
>   
> @@ -109,7 +99,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
>   			return -EBUSY;
>   
>   		if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest))
> -			return 0;
> +			return IRQ_SET_MASK_OK_DONE;
>   
>   		cpumask_and(&intersect_mask, dest, cpu_online_mask);
>   
> @@ -121,7 +111,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
>   		adata->cpu = cpu;
>   		adata->vec = vector;
>   		per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data);
> -		avecintc_sync(adata);
> +		if (!cpu_has_redirectint)
> +			avecintc_sync(adata);
>   	}
>   
>   	irq_data_update_effective_affinity(data, cpumask_of(cpu));
> @@ -242,6 +233,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
>   		d = this_cpu_read(irq_map[vector]);
>   		if (d) {
>   			generic_handle_irq_desc(d);
> +
>   		} else {
>   			spurious_interrupt();
>   			pr_warn("Unexpected IRQ occurs on CPU#%d [vector %ld]\n", smp_processor_id(), vector);
> @@ -412,6 +404,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
>   
>   static inline int __init acpi_cascade_irqdomain_init(void)
>   {
> +	if (cpu_has_redirectint)
> +		return redirect_acpi_init(loongarch_avec.domain);
> +
>   	return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
>   }
>   
> diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
> new file mode 100644
> index 000000000000..9e1cbbb72e29
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongarch-ir.c
> @@ -0,0 +1,561 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Loongson Technologies, Inc.
> + */
> +
> +#include <linux/cpuhotplug.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +#include <linux/spinlock.h>
> +#include <linux/msi.h>
> +
> +#include <asm/irq.h>
> +#include <asm/loongarch.h>
> +#include <asm/setup.h>
> +#include <larchintrin.h>
> +
> +#include "irq-loongson.h"
> +#include "irq-msi-lib.h"
> +
> +#define IRD_ENTRIES			65536
> +
> +/* redirect entry size 128bits */
> +#define IRD_PAGE_ORDER			(20 - PAGE_SHIFT)
> +
> +/* irt cache invalid queue */
> +#define	INVALID_QUEUE_SIZE		4096
> +
> +#define INVALID_QUEUE_PAGE_ORDER	(16 - PAGE_SHIFT)
> +
> +#define GPID_ADDR_MASK			0x3ffffffffffULL
> +#define GPID_ADDR_SHIFT			6
> +
> +#define CQB_SIZE_SHIFT			0
> +#define CQB_SIZE_MASK			0xf
> +#define CQB_ADDR_SHIFT			12
> +#define CQB_ADDR_MASK			(0xfffffffffULL)
> +
> +#define CFG_DISABLE_IDLE		2
> +#define INVALID_INDEX			0
> +
> +#define MAX_IR_ENGINES			16
> +
> +struct irq_domain *redirect_domain;
> +
> +struct redirect_entry {
> +	struct  {
> +		__u64	valid	: 1,
> +			res1	: 5,
> +			gpid	: 42,
> +			res2	: 8,
> +			vector	: 8;
> +	}	lo;
> +	__u64	hi;
> +};
> +
> +struct redirect_gpid {
> +	u64	pir[4];      /* Pending interrupt requested */
> +	u8	en	: 1, /* doorbell */
> +		res0	: 7;
> +	u8	irqnum;
> +	u16	res1;
> +	u32	dst;
> +	u32	rsvd[6];
> +} __aligned(64);
> +
> +struct redirect_table {
> +	int			node;
> +	struct redirect_entry	*table;
> +	unsigned long		*bitmap;
> +	unsigned int		nr_ird;
> +	struct page		*page;
> +	raw_spinlock_t		lock;
> +};
> +
> +struct redirect_item {
> +	int			index;
> +	struct redirect_entry	*entry;
> +	struct redirect_gpid	*gpid;
> +	struct redirect_table	*table;
> +};
> +
> +struct redirect_queue {
> +	int		node;
> +	u64		base;
> +	u32		max_size;
> +	int		head;
> +	int		tail;
> +	struct page	*page;
> +	raw_spinlock_t	lock;
> +};
> +
> +
> +struct irde_desc {
> +	struct redirect_table	ird_table;
> +	struct redirect_queue	inv_queue;
> +};
> +
> +struct irde_inv_cmd {
> +	union {
> +		__u64	cmd_info;
> +		struct {
> +			__u64	res1		: 4,
> +				type		: 1,
> +				need_notice	: 1,
> +				pad		: 2,
> +				index		: 16,
> +				pad2		: 40;
> +		}	index;
> +	};
> +	__u64		notice_addr;
> +};
> +
> +struct smp_invalid_arg {
> +	struct redirect_queue	*queue;
> +	struct irde_inv_cmd	*cmd;
> +};
> +
> +static struct irde_desc irde_descs[MAX_IR_ENGINES];
> +static phys_addr_t msi_base_addr;
> +
> +static inline bool invalid_queue_is_full(int node_id, u32 *tail)
> +{
> +	u32 head;
> +
> +	head = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQH);
> +	*tail = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQT);
> +
> +	return !!(head == ((*tail + 1) % INVALID_QUEUE_SIZE));
> +}
> +
> +static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
> +{
> +	struct irde_inv_cmd *inv_addr;
> +	u32 tail;
> +
> +	guard(raw_spinlock_irqsave)(&rqueue->lock);
> +
> +	while (invalid_queue_is_full(rqueue->node, &tail))
> +		cpu_relax();
> +
> +	inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
> +	memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
> +	tail = (tail + 1) % INVALID_QUEUE_SIZE;
> +
> +	wmb();
> +
> +	iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
> +}
> +
> +static void smp_call_invalid_enqueue(void *arg)
> +{
> +	struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
> +
> +	invalid_enqueue(s_arg->queue, s_arg->cmd);
> +}
> +
> +static void irde_invlid_entry_node(struct redirect_item *item)
> +{
> +	struct redirect_queue *rqueue;
> +	struct smp_invalid_arg arg;
> +	struct irde_inv_cmd cmd;
> +	volatile u64 raddr = 0;
> +	int node = item->table->node, cpu;
> +
> +	rqueue = &(irde_descs[node].inv_queue);
> +	cmd.cmd_info = 0;
> +	cmd.index.type = INVALID_INDEX;
> +	cmd.index.need_notice = 1;
> +	cmd.index.index = item->index;
> +	cmd.notice_addr = (u64)(__pa(&raddr));
> +
> +	if (cpu_to_node(smp_processor_id()) == node)
> +		invalid_enqueue(rqueue, &cmd);
> +	else {
> +		for_each_cpu(cpu, cpumask_of_node(node)) {
> +			if (cpu_online(cpu))
> +				break;
> +		}
> +		arg.queue = rqueue;
> +		arg.cmd = &cmd;
> +		smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
> +	}
> +
> +	while (!raddr)
> +		cpu_relax();
> +
> +}
> +
> +static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
> +{
> +	return data->parent_data->chip_data;
> +}
> +
> +static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
> +{
> +	int index;
> +
> +	guard(raw_spinlock_irqsave)(&ird_table->lock);
> +
> +	index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
> +	if (index > IRD_ENTRIES) {
> +		pr_err("No redirect entry to use\n");
> +		return -ENOMEM;
> +	}
> +
> +	__set_bit(index, ird_table->bitmap);
> +
> +	item->index = index;
> +	item->entry = &ird_table->table[index];
> +	item->table = ird_table;
> +
> +	return 0;
> +}
> +
> +static int redirect_table_free(struct redirect_item *item)
> +{
> +	struct redirect_table *ird_table;
> +	struct redirect_entry *entry;
> +	unsigned long flags;
> +
> +	ird_table = item->table;
> +
> +	entry = item->entry;
> +	memset(entry, 0, sizeof(struct redirect_entry));
> +
> +	raw_spin_lock_irqsave(&ird_table->lock, flags);
> +	bitmap_release_region(ird_table->bitmap, item->index, 0);
> +	raw_spin_unlock_irqrestore(&ird_table->lock, flags);
> +
> +	kfree(item->gpid);
> +
> +	irde_invlid_entry_node(item);
> +
> +	return 0;
> +}
> +
> +static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
> +{
> +	struct redirect_entry *entry = item->entry;
> +
> +	item->gpid->en = 1;
> +	item->gpid->irqnum = adata->vec;
> +	item->gpid->dst = adata->cpu;
> +
> +	entry->lo.valid = 1;
> +	entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
> +	entry->lo.vector = 0xff;
> +	wmb();
> +}
> +
> +static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
> +{
> +	struct redirect_item *item = data->chip_data;
> +	struct avecintc_data *adata;
> +	int ret;
> +
> +	ret = irq_chip_set_affinity_parent(data, dest, force);
> +	if (ret == IRQ_SET_MASK_OK_DONE)
> +		return IRQ_SET_MASK_OK;
> +	else if (ret) {
> +		pr_err("IRDE:set_affinity error %d\n", ret);
> +		return ret;
> +	}
> +
> +	adata = irq_data_get_avec_data(data);
> +
> +	redirect_domain_prepare_entry(item, adata);
> +
> +	irde_invlid_entry_node(item);
> +
> +	avecintc_sync(adata);
> +	return IRQ_SET_MASK_OK;
> +}
> +
> +static void redirect_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
> +{
> +	struct redirect_item *item;
> +
> +	item = irq_data_get_irq_chip_data(d);
> +	msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
> +	msg->address_hi = 0x0;
> +	msg->data = 0x0;
> +}
> +
> +static inline void redirect_ack_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void redirect_unmask_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void redirect_mask_irq(struct irq_data *d)
> +{
> +}
> +
> +static struct irq_chip loongarch_redirect_chip = {
> +	.name			= "REDIRECT",
> +	.irq_ack		= redirect_ack_irq,
> +	.irq_mask		= redirect_mask_irq,
> +	.irq_unmask		= redirect_unmask_irq,
> +	.irq_set_affinity	= redirect_set_affinity,
> +	.irq_compose_msi_msg	= redirect_compose_msi_msg,
> +};
> +
> +static void redirect_free_resources(struct irq_domain *domain,
> +						unsigned int virq, unsigned int nr_irqs)
> +{
> +	struct irq_data *irq_data;
> +	struct redirect_item *item;
> +
> +	for (int i = 0; i < nr_irqs; i++) {
> +		irq_data = irq_domain_get_irq_data(domain, virq  + i);
> +		if (irq_data && irq_data->chip_data) {
> +			item = irq_data->chip_data;
> +			redirect_table_free(item);
> +			kfree(item);
> +		}
> +	}
> +}
> +
> +static int redirect_alloc(struct irq_domain *domain,
> +					unsigned int virq, unsigned int nr_irqs,
> +					 void *arg)
> +{
> +	struct redirect_table *ird_table;
> +	struct avecintc_data *avec_data;
> +	struct irq_data *irq_data;
> +	int ret, i, node;
> +
> +#ifdef CONFIG_NUMA
> +	node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
> +#else
> +	node = 0;
> +#endif
> +	ird_table = &irde_descs[node].ird_table;
> +	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		struct redirect_item *item;
> +
> +		item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
> +		if (!item) {
> +			pr_err("Alloc redirect descriptor failed\n");
> +			goto out_free_resources;
> +		}
> +
> +		irq_data = irq_domain_get_irq_data(domain, virq + i);
> +
> +		avec_data = irq_data_get_avec_data(irq_data);
> +		ret = redirect_table_alloc(item, ird_table);
> +		if (ret) {
> +			pr_err("Alloc redirect table entry failed\n");
> +			goto out_free_resources;
> +		}
> +
> +		item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
> +		if (!item->gpid) {
> +			pr_err("Alloc redirect GPID failed\n");
> +			goto out_free_resources;
> +		}
> +
> +		irq_data->chip_data = item;
> +		irq_data->chip = &loongarch_redirect_chip;
> +		redirect_domain_prepare_entry(item, avec_data);
> +	}
> +	return 0;
> +
> +out_free_resources:
> +	redirect_free_resources(domain, virq, nr_irqs);
> +	irq_domain_free_irqs_common(domain, virq, nr_irqs);
> +
> +	return -EINVAL;
> +}
> +
> +static void redirect_free(struct irq_domain *domain,
> +				     unsigned int virq, unsigned int nr_irqs)
> +{
> +	redirect_free_resources(domain, virq, nr_irqs);
> +	return irq_domain_free_irqs_common(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops redirect_domain_ops = {
> +	.alloc		= redirect_alloc,
> +	.free		= redirect_free,
> +	.select		= msi_lib_irq_domain_select,
> +};
> +
> +static int redirect_queue_init(int node)
> +{
> +	struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
> +	struct page *pages;
> +
> +	pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
> +	if (!pages) {
> +		pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
> +		return -ENOMEM;
> +	}
> +
> +	rqueue->page = pages;
> +	rqueue->base = (u64)page_address(pages);
> +	rqueue->max_size = INVALID_QUEUE_SIZE;
> +	rqueue->head = 0;
> +	rqueue->tail = 0;
> +	rqueue->node = node;
> +	raw_spin_lock_init(&rqueue->lock);
> +
> +	iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
> +	iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
> +	iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
> +				(CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
> +	return 0;
> +}
> +
> +static int redirect_table_init(int node)
> +{
> +	struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> +	struct page *pages;
> +	unsigned long *bitmap;
> +
> +	pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
> +	if (!pages) {
> +		pr_err("Node [%d] redirect table alloc pages failed!\n", node);
> +		return -ENOMEM;
> +	}
> +	ird_table->page = pages;
> +	ird_table->table = page_address(pages);
> +
> +	bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
> +	if (!bitmap) {
> +		pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
> +		return -ENOMEM;
> +	}
> +
> +	ird_table->bitmap = bitmap;
> +	ird_table->nr_ird = IRD_ENTRIES;
> +	ird_table->node = node;
> +
> +	raw_spin_lock_init(&ird_table->lock);
> +
> +	if (redirect_queue_init(node))
> +		return -EINVAL;
> +
> +	iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
> +	iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
> +
> +	return 0;
> +}
> +
> +static void redirect_table_fini(int node)
> +{
> +	struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> +	struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
> +
> +	if (ird_table->page) {
> +		__free_pages(ird_table->page, IRD_PAGE_ORDER);
> +		ird_table->table = NULL;
> +		ird_table->page = NULL;
> +	}
> +
> +	if (ird_table->page) {
> +		bitmap_free(ird_table->bitmap);
> +		ird_table->bitmap = NULL;
> +	}
> +
> +	if (rqueue->page) {
> +		__free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
> +		rqueue->page = NULL;
> +		rqueue->base = 0;
> +	}
> +
> +	iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_CQB);
> +	iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_TBR);
> +}
> +
> +static int redirect_cpu_online(unsigned int cpu)
> +{
> +	int ret, node = cpu_to_node(cpu);
> +
> +	if (cpu != cpumask_first(cpumask_of_node(node)))
> +		return 0;
> +
> +	ret = redirect_table_init(node);
> +	if (ret) {
> +		redirect_table_fini(node);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +#if defined(CONFIG_ACPI)
> +static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
> +		const unsigned long end)
> +{
> +	struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
> +
> +	msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET;
> +
> +	return pch_msi_acpi_init_avec(redirect_domain);
> +}
> +
> +static int __init acpi_cascade_irqdomain_init(void)
> +{
> +	return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> +}
> +
> +int __init redirect_acpi_init(struct irq_domain *parent)
> +{
> +	struct fwnode_handle *fwnode;
> +	struct irq_domain *domain;
> +	int ret;
> +
> +	fwnode = irq_domain_alloc_named_fwnode("redirect");
> +	if (!fwnode) {
> +		pr_err("Unable to alloc redirect domain handle\n");
> +		goto fail;
> +	}
> +
> +	domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode,
> +			&redirect_domain_ops, irde_descs);
> +	if (!domain) {
> +		pr_err("Unable to alloc redirect domain\n");
> +		goto out_free_fwnode;
> +	}
> +
> +	redirect_domain = domain;
> +
> +	ret = redirect_table_init(0);
> +	if (ret)
> +		goto out_free_table;
> +
> +	ret = acpi_cascade_irqdomain_init();
> +	if (ret < 0) {
> +		pr_err("Failed to cascade IRQ domain, ret=%d\n", ret);
> +		goto out_free_table;
> +	}
> +
> +	cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_REDIRECT_STARTING,
> +				  "irqchip/loongarch/redirect:starting",
> +				  redirect_cpu_online, NULL);
> +
> +	pr_info("loongarch irq redirect modules init succeeded\n");
> +	return 0;
> +
> +out_free_table:
> +	redirect_table_fini(0);
> +	irq_domain_remove(redirect_domain);
> +	redirect_domain = NULL;
> +out_free_fwnode:
> +	irq_domain_free_fwnode(fwnode);
> +fail:
> +	return -EINVAL;
> +}
> +#endif
> diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h
> index 11fa138d1f44..05ad40ffb62b 100644
> --- a/drivers/irqchip/irq-loongson.h
> +++ b/drivers/irqchip/irq-loongson.h
> @@ -5,6 +5,15 @@
>   
>   #ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
>   #define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
> +#define AVEC_MSG_OFFSET		0x100000
> +struct avecintc_data {
> +	struct list_head        entry;
> +	unsigned int            cpu;
> +	unsigned int            vec;
> +	unsigned int            prev_cpu;
> +	unsigned int            prev_vec;
> +	unsigned int            moving;
> +};
>   
>   int find_pch_pic(u32 gsi);
>   
> @@ -24,4 +33,7 @@ int pch_msi_acpi_init(struct irq_domain *parent,
>   					struct acpi_madt_msi_pic *acpi_pchmsi);
>   int pch_msi_acpi_init_avec(struct irq_domain *parent);
>   
> +int redirect_acpi_init(struct irq_domain *parent);
> +
> +void avecintc_sync(struct avecintc_data *adata);
>   #endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */
> diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
> index 6cc5e484547c..2fd5531fa378 100644
> --- a/include/linux/cpuhotplug.h
> +++ b/include/linux/cpuhotplug.h
> @@ -146,6 +146,7 @@ enum cpuhp_state {
>   	CPUHP_AP_IRQ_MIPS_GIC_STARTING,
>   	CPUHP_AP_IRQ_EIOINTC_STARTING,
>   	CPUHP_AP_IRQ_AVECINTC_STARTING,
> +	CPUHP_AP_IRQ_REDIRECT_STARTING,
>   	CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
>   	CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
>   	CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ