linux-doc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2/2] Loongarch:Support loongarch avec
@ 2024-05-07 12:59 Tianyang Zhang
  2024-05-07 14:20 ` Xi Ruoyao
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Tianyang Zhang @ 2024-05-07 12:59 UTC (permalink / raw)
  To: chenhuacai, kernel, tglx, jiaxun.yang, gaoliang, wangliupu,
	lvjianmin, zhangtianyang, yijun, mhocko, akpm, dianders, maobibo,
	xry111, zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: loongarch, linux-doc, linux-kernel

From: zhangtianyang <zhangtianyang@loongson.cn>

Introduce the advanced extended interrupt controllers,
This feature will allow each core to have an independent
256 interrupt vectors, and MSI interrupts can be
independently routed to any vector on any CPU.

Co-developed-by: Jianmin Lv <lvjianmin@loongson.cn>
Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
---
 arch/loongarch/Kconfig                    |   1 +
 arch/loongarch/include/asm/cpu-features.h |   1 +
 arch/loongarch/include/asm/cpu.h          |   2 +
 arch/loongarch/include/asm/hw_irq.h       |  10 +
 arch/loongarch/include/asm/irq.h          |   7 +-
 arch/loongarch/include/asm/loongarch.h    |  19 +-
 arch/loongarch/include/asm/smp.h          |   1 +
 arch/loongarch/kernel/cpu-probe.c         |   3 +-
 arch/loongarch/kernel/smp.c               |   3 +
 drivers/irqchip/Makefile                  |   2 +-
 drivers/irqchip/irq-loongarch-avec.c      | 434 ++++++++++++++++++++++
 drivers/irqchip/irq-loongarch-cpu.c       |   4 +-
 drivers/irqchip/irq-loongson-eiointc.c    |   3 +
 drivers/irqchip/irq-loongson-pch-msi.c    |  45 ++-
 14 files changed, 525 insertions(+), 10 deletions(-)
 create mode 100644 drivers/irqchip/irq-loongarch-avec.c

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 10959e6c3583..71b8f19745e0 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -76,6 +76,7 @@ config LOONGARCH
 	select GENERIC_ENTRY
 	select GENERIC_GETTIMEOFDAY
 	select GENERIC_IOREMAP if !ARCH_IOREMAP
+	select GENERIC_IRQ_MATRIX_ALLOCATOR
 	select GENERIC_IRQ_MULTI_HANDLER
 	select GENERIC_IRQ_PROBE
 	select GENERIC_IRQ_SHOW
diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
index 2eafe6a6aca8..16a716f88a5c 100644
--- a/arch/loongarch/include/asm/cpu-features.h
+++ b/arch/loongarch/include/asm/cpu-features.h
@@ -65,5 +65,6 @@
 #define cpu_has_guestid		cpu_opt(LOONGARCH_CPU_GUESTID)
 #define cpu_has_hypervisor	cpu_opt(LOONGARCH_CPU_HYPERVISOR)
 #define cpu_has_ptw		cpu_opt(LOONGARCH_CPU_PTW)
+#define cpu_has_avecint		cpu_opt(LOONGARCH_CPU_AVECINT)
 
 #endif /* __ASM_CPU_FEATURES_H */
diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
index 48b9f7168bcc..843f9c4ec980 100644
--- a/arch/loongarch/include/asm/cpu.h
+++ b/arch/loongarch/include/asm/cpu.h
@@ -99,6 +99,7 @@ enum cpu_type_enum {
 #define CPU_FEATURE_GUESTID		24	/* CPU has GuestID feature */
 #define CPU_FEATURE_HYPERVISOR		25	/* CPU has hypervisor (running in VM) */
 #define CPU_FEATURE_PTW			26	/* CPU has hardware page table walker */
+#define CPU_FEATURE_AVECINT		27	/* CPU has avec interrupt */
 
 #define LOONGARCH_CPU_CPUCFG		BIT_ULL(CPU_FEATURE_CPUCFG)
 #define LOONGARCH_CPU_LAM		BIT_ULL(CPU_FEATURE_LAM)
@@ -127,5 +128,6 @@ enum cpu_type_enum {
 #define LOONGARCH_CPU_GUESTID		BIT_ULL(CPU_FEATURE_GUESTID)
 #define LOONGARCH_CPU_HYPERVISOR	BIT_ULL(CPU_FEATURE_HYPERVISOR)
 #define LOONGARCH_CPU_PTW		BIT_ULL(CPU_FEATURE_PTW)
+#define LOONGARCH_CPU_AVECINT		BIT_ULL(CPU_FEATURE_AVECINT)
 
 #endif /* _ASM_CPU_H */
diff --git a/arch/loongarch/include/asm/hw_irq.h b/arch/loongarch/include/asm/hw_irq.h
index af4f4e8fbd85..521abce32135 100644
--- a/arch/loongarch/include/asm/hw_irq.h
+++ b/arch/loongarch/include/asm/hw_irq.h
@@ -9,6 +9,16 @@
 
 extern atomic_t irq_err_count;
 
+/*
+ * 256 vectors Map:
+ *
+ * 0 - 15: mapping legacy IPs, e.g. IP0-12.
+ * 16 - 255: mapping a vector for external IRQ.
+ *
+ */
+#define NR_VECTORS	256
+#define IRQ_MATRIX_BITS	NR_VECTORS
+#define NR_LEGACY_VECTORS	16
 /*
  * interrupt-retrigger: NOP for now. This may not be appropriate for all
  * machines, we'll see ...
diff --git a/arch/loongarch/include/asm/irq.h b/arch/loongarch/include/asm/irq.h
index 218b4da0ea90..e28bd96c6611 100644
--- a/arch/loongarch/include/asm/irq.h
+++ b/arch/loongarch/include/asm/irq.h
@@ -65,7 +65,7 @@ extern struct acpi_vector_group msi_group[MAX_IO_PICS];
 #define LOONGSON_LPC_LAST_IRQ		(LOONGSON_LPC_IRQ_BASE + 15)
 
 #define LOONGSON_CPU_IRQ_BASE		16
-#define LOONGSON_CPU_LAST_IRQ		(LOONGSON_CPU_IRQ_BASE + 14)
+#define LOONGSON_CPU_LAST_IRQ		(LOONGSON_CPU_IRQ_BASE + 15)
 
 #define LOONGSON_PCH_IRQ_BASE		64
 #define LOONGSON_PCH_ACPI_IRQ		(LOONGSON_PCH_IRQ_BASE + 47)
@@ -101,6 +101,11 @@ int pch_msi_acpi_init(struct irq_domain *parent,
 					struct acpi_madt_msi_pic *acpi_pchmsi);
 int pch_pic_acpi_init(struct irq_domain *parent,
 					struct acpi_madt_bio_pic *acpi_pchpic);
+int __init pch_msi_acpi_init_v2(struct irq_domain *parent,
+		struct acpi_madt_msi_pic *pch_msi_entry);
+int __init loongarch_avec_acpi_init(struct irq_domain *parent);
+void complete_irq_moving(int *restart);
+
 int find_pch_pic(u32 gsi);
 struct fwnode_handle *get_pch_msi_handle(int pci_segment);
 
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index 46366e783c84..4d9a09861925 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -72,7 +72,6 @@
 #define  CPUCFG1_RPLV			BIT(23)
 #define  CPUCFG1_HUGEPG			BIT(24)
 #define  CPUCFG1_CRC32			BIT(25)
-#define  CPUCFG1_MSGINT			BIT(26)
 
 #define LOONGARCH_CPUCFG2		0x2
 #define  CPUCFG2_FP			BIT(0)
@@ -240,8 +239,8 @@
 #define  CSR_ESTAT_EXC_WIDTH		6
 #define  CSR_ESTAT_EXC			(_ULCAST_(0x3f) << CSR_ESTAT_EXC_SHIFT)
 #define  CSR_ESTAT_IS_SHIFT		0
-#define  CSR_ESTAT_IS_WIDTH		14
-#define  CSR_ESTAT_IS			(_ULCAST_(0x3fff) << CSR_ESTAT_IS_SHIFT)
+#define  CSR_ESTAT_IS_WIDTH		15
+#define  CSR_ESTAT_IS			(_ULCAST_(0x7fff) << CSR_ESTAT_IS_SHIFT)
 
 #define LOONGARCH_CSR_ERA		0x6	/* ERA */
 
@@ -987,10 +986,17 @@
 #define CSR_FWPC_SKIP_SHIFT		16
 #define CSR_FWPC_SKIP			(_ULCAST_(1) << CSR_FWPC_SKIP_SHIFT)
 
+#define LOONGARCH_CSR_IRR0		0xa0
+#define LOONGARCH_CSR_IRR1		0xa1
+#define LOONGARCH_CSR_IRR2		0xa2
+#define LOONGARCH_CSR_IRR3		0xa3
+
+#define	LOONGARCH_CSR_ILR		0xa4
+
 /*
  * CSR_ECFG IM
  */
-#define ECFG0_IM		0x00001fff
+#define ECFG0_IM		0x00005fff
 #define ECFGB_SIP0		0
 #define ECFGF_SIP0		(_ULCAST_(1) << ECFGB_SIP0)
 #define ECFGB_SIP1		1
@@ -1033,6 +1039,7 @@
 #define  IOCSRF_EIODECODE		BIT_ULL(9)
 #define  IOCSRF_FLATMODE		BIT_ULL(10)
 #define  IOCSRF_VM			BIT_ULL(11)
+#define  IOCSRF_AVEC			BIT_ULL(15)
 
 #define LOONGARCH_IOCSR_VENDOR		0x10
 
@@ -1043,6 +1050,7 @@
 #define LOONGARCH_IOCSR_MISC_FUNC	0x420
 #define  IOCSR_MISC_FUNC_TIMER_RESET	BIT_ULL(21)
 #define  IOCSR_MISC_FUNC_EXT_IOI_EN	BIT_ULL(48)
+#define  IOCSR_MISC_FUNC_AVEC_EN	BIT_ULL(51)
 
 #define LOONGARCH_IOCSR_CPUTEMP		0x428
 
@@ -1363,9 +1371,10 @@ __BUILD_CSR_OP(tlbidx)
 #define INT_TI		11	/* Timer */
 #define INT_IPI		12
 #define INT_NMI		13
+#define INT_AVEC	14
 
 /* ExcCodes corresponding to interrupts */
-#define EXCCODE_INT_NUM		(INT_NMI + 1)
+#define EXCCODE_INT_NUM		(INT_AVEC + 1)
 #define EXCCODE_INT_START	64
 #define EXCCODE_INT_END		(EXCCODE_INT_START + EXCCODE_INT_NUM - 1)
 
diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h
index f81e5f01d619..577f591c1c73 100644
--- a/arch/loongarch/include/asm/smp.h
+++ b/arch/loongarch/include/asm/smp.h
@@ -62,6 +62,7 @@ extern int __cpu_logical_map[NR_CPUS];
 #define SMP_BOOT_CPU		0x1
 #define SMP_RESCHEDULE		0x2
 #define SMP_CALL_FUNCTION	0x4
+#define SMP_CLEAR_VECT		0x8
 
 struct secondary_data {
 	unsigned long stack;
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index 55320813ee08..3b2e72e8f9bd 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -106,7 +106,6 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
 		elf_hwcap |= HWCAP_LOONGARCH_CRC32;
 	}
 
-
 	config = read_cpucfg(LOONGARCH_CPUCFG2);
 	if (config & CPUCFG2_LAM) {
 		c->options |= LOONGARCH_CPU_LAM;
@@ -176,6 +175,8 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
 		c->options |= LOONGARCH_CPU_EIODECODE;
 	if (config & IOCSRF_VM)
 		c->options |= LOONGARCH_CPU_HYPERVISOR;
+	if (config & IOCSRF_AVEC)
+		c->options |= LOONGARCH_CPU_AVECINT;
 
 	config = csr_read32(LOONGARCH_CSR_ASID);
 	config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT;
diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c
index a16e3dbe9f09..4fcf399a8b17 100644
--- a/arch/loongarch/kernel/smp.c
+++ b/arch/loongarch/kernel/smp.c
@@ -176,6 +176,9 @@ irqreturn_t loongson_ipi_interrupt(int irq, void *dev)
 		per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++;
 	}
 
+	if (action & SMP_CLEAR_VECT)
+		complete_irq_moving(NULL);
+
 	return IRQ_HANDLED;
 }
 
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ec4a18380998..398db4c3e264 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -105,7 +105,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
+obj-$(CONFIG_IRQ_LOONGARCH_CPU)		+= irq-loongarch-cpu.o irq-loongarch-avec.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
new file mode 100644
index 000000000000..71ce33db63db
--- /dev/null
+++ b/drivers/irqchip/irq-loongarch-avec.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Loongson Technologies, Inc.
+ */
+
+#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 <linux/irqchip/chained_irq.h>
+#include <linux/cpuhotplug.h>
+#include <linux/radix-tree.h>
+
+#include <asm/loongarch.h>
+#include <asm/setup.h>
+
+static phys_addr_t msi_base_v2;
+
+typedef struct irq_data *irq_map_t[NR_VECTORS];
+DECLARE_PER_CPU(irq_map_t, irq_map);
+DEFINE_PER_CPU(irq_map_t, irq_map) = {
+	[0 ... NR_VECTORS - 1] = NULL,
+};
+
+struct pending_list {
+	struct list_head head;
+	raw_spinlock_t	lock;
+};
+
+DEFINE_PER_CPU(struct pending_list, pending_list);
+
+struct loongarch_avec_chip {
+	struct fwnode_handle	*fwnode;
+	struct irq_domain	*domain;
+	struct irq_matrix	*vector_matrix;
+	raw_spinlock_t		lock;
+} loongarch_avec;
+
+struct loongarch_avec_data {
+	struct list_head entry;
+	unsigned int cpu;
+	unsigned int vec;
+	unsigned int prev_cpu;
+	unsigned int prev_vec;
+};
+
+static int assign_irq_vector(struct irq_data *irqd, const struct cpumask *dest,
+		unsigned int *cpu, int *vector)
+{
+	int ret;
+
+	ret = irq_matrix_alloc(loongarch_avec.vector_matrix, dest, false, cpu);
+	if (ret < 0)
+		return ret;
+	*vector = ret;
+
+	return 0;
+}
+
+static inline void loongarch_avec_ack_irq(struct irq_data *d)
+{
+}
+
+static inline void loongarch_avec_unmask_irq(struct irq_data *d)
+{
+}
+
+static inline void loongarch_avec_mask_irq(struct irq_data *d)
+{
+}
+
+static void loongarch_avec_sync(struct loongarch_avec_data *adata)
+{
+	struct loongarch_avec_data *data;
+	struct pending_list *plist;
+
+	if (cpu_online(adata->prev_cpu)) {
+		plist = per_cpu_ptr(&pending_list, adata->prev_cpu);
+
+		data = kmalloc(sizeof(struct loongarch_avec_data), GFP_KERNEL);
+		if (!data) {
+			pr_warn("NO space for clean data\n");
+			return;
+		}
+		memcpy(data, adata, sizeof(struct loongarch_avec_data));
+		INIT_LIST_HEAD(&data->entry);
+
+		list_add_tail(&data->entry, &plist->head);
+		loongson_send_ipi_single(adata->prev_cpu, SMP_CLEAR_VECT);
+	}
+	adata->prev_cpu = adata->cpu;
+	adata->prev_vec = adata->vec;
+}
+
+static int loongarch_avec_set_affinity(struct irq_data *data,
+		const struct cpumask *dest, bool force)
+{
+	struct cpumask intersect_mask;
+	struct loongarch_avec_data *adata;
+	unsigned int cpu, vector;
+	unsigned long flags;
+	int ret = 0;
+
+	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
+	adata = irq_data_get_irq_chip_data(data);
+
+	if (adata->vec && cpu_online(adata->cpu)
+			&& cpumask_test_cpu(adata->cpu, dest)) {
+		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+		return 0;
+	}
+
+	if (!cpumask_intersects(dest, cpu_online_mask)) {
+		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+		return -EINVAL;
+	}
+
+	cpumask_and(&intersect_mask, dest, cpu_online_mask);
+
+	ret = assign_irq_vector(data, &intersect_mask, &cpu, &vector);
+	if (ret) {
+		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+		return ret;
+	}
+
+	adata->cpu = cpu;
+	adata->vec = vector;
+	per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = data;
+	loongarch_avec_sync(adata);
+
+	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+	irq_data_update_effective_affinity(data, cpumask_of(cpu));
+
+	return IRQ_SET_MASK_OK;
+}
+
+static void loongarch_avec_compose_msg(struct irq_data *d,
+		struct msi_msg *msg)
+{
+	struct loongarch_avec_data *avec_data;
+
+	avec_data = irq_data_get_irq_chip_data(d);
+
+	msg->address_hi = 0x0;
+	msg->address_lo = msi_base_v2 | ((avec_data->vec & 0xff) << 4) |
+		((cpu_logical_map(avec_data->cpu & 0xffff)) << 12);
+	msg->data = 0x0;
+
+}
+
+static struct irq_chip loongarch_avec_controller = {
+	.name			= "CORE_AVEC",
+	.irq_ack		= loongarch_avec_ack_irq,
+	.irq_mask		= loongarch_avec_mask_irq,
+	.irq_unmask		= loongarch_avec_unmask_irq,
+	.irq_set_affinity	= loongarch_avec_set_affinity,
+	.irq_compose_msi_msg	= loongarch_avec_compose_msg,
+};
+
+void complete_irq_moving(int *restart)
+{
+	struct pending_list *plist = this_cpu_ptr(&pending_list);
+	struct loongarch_avec_data *adata, *tmp;
+	int cpu, vector;
+	u32 bias;
+	u64 irr;
+
+	raw_spin_lock(&loongarch_avec.lock);
+
+	list_for_each_entry_safe(adata, tmp, &plist->head, entry) {
+
+		cpu = adata->prev_cpu;
+		vector = adata->prev_vec;
+		bias = vector/64;
+		switch (bias) {
+		case 0x0:
+			irr = csr_read64(LOONGARCH_CSR_IRR0);
+			break;
+		case 0x1:
+			irr = csr_read64(LOONGARCH_CSR_IRR1);
+			break;
+		case 0x2:
+			irr = csr_read64(LOONGARCH_CSR_IRR2);
+			break;
+		case 0x3:
+			irr = csr_read64(LOONGARCH_CSR_IRR3);
+			break;
+		default:
+			return;
+		}
+
+		if (irr & (1UL << (vector % 64))) {
+			loongson_send_ipi_single(cpu, SMP_CLEAR_VECT);
+			continue;
+		}
+		list_del(&adata->entry);
+		irq_matrix_free(loongarch_avec.vector_matrix, cpu, vector, false);
+		this_cpu_ptr(irq_map)[vector] = 0;
+		kfree(adata);
+	}
+	raw_spin_unlock(&loongarch_avec.lock);
+}
+
+static void loongarch_avec_dispatch(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct irq_data *d;
+	unsigned long vector;
+
+	chained_irq_enter(chip, desc);
+	vector = csr_read64(LOONGARCH_CSR_ILR);
+	if (vector & 0x80000000)
+		return;
+
+	vector &= 0xff;
+
+	d = raw_cpu_ptr(irq_map)[vector];
+	if (d)
+		generic_handle_irq(d->irq);
+	else
+		pr_warn("IRQ ERROR:Unexpected irq  occur on cpu %d[vector %d]\n",
+				smp_processor_id(), vector);
+
+	chained_irq_exit(chip, desc);
+}
+
+static int loongarch_avec_alloc(struct irq_domain *domain, unsigned int virq,
+		unsigned int nr_irqs, void *arg)
+{
+	struct loongarch_avec_data *adata;
+	struct irq_data *irqd;
+	unsigned int cpu, vector;
+	unsigned long flags;
+	int i, err;
+
+	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
+	for (i = 0; i < nr_irqs; i++) {
+		irqd = irq_domain_get_irq_data(domain, virq + i);
+		adata = kzalloc(sizeof(*adata), GFP_KERNEL);
+		if (!adata) {
+			raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+			return -ENOMEM;
+		}
+		err = assign_irq_vector(irqd, cpu_online_mask, &cpu, &vector);
+		if (err) {
+			raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+			return err;
+		}
+		adata->prev_cpu = adata->cpu = cpu;
+		adata->prev_vec = adata->vec = vector;
+
+		per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irqd;
+		irq_domain_set_info(domain, virq + i, virq, &loongarch_avec_controller,
+				adata, handle_edge_irq, NULL, NULL);
+		irqd_set_single_target(irqd);
+		irqd_set_affinity_on_activate(irqd);
+	}
+	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+
+	return err;
+}
+
+static void loongarch_avec_free(struct irq_domain *domain, unsigned int virq,
+		unsigned int nr_irqs)
+{
+	struct loongarch_avec_data *adata;
+	struct irq_data *d;
+	unsigned long flags;
+	unsigned int i;
+
+	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
+	for (i = 0; i < nr_irqs; i++) {
+		d = irq_domain_get_irq_data(domain, virq + i);
+		adata = irq_data_get_irq_chip_data(d);
+		if (d) {
+			irq_matrix_free(loongarch_avec.vector_matrix,
+					adata->cpu,
+					adata->vec, false);
+			irq_domain_reset_irq_data(d);
+		}
+	}
+
+	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+}
+
+static const struct irq_domain_ops loongarch_avec_domain_ops = {
+	.alloc		= loongarch_avec_alloc,
+	.free		= loongarch_avec_free,
+};
+
+static int __init irq_matrix_init(void)
+{
+	int i;
+
+	loongarch_avec.vector_matrix = irq_alloc_matrix(NR_VECTORS, 0, NR_VECTORS - 1);
+	if (!loongarch_avec.vector_matrix)
+		return -ENOMEM;
+	for (i = 0; i < NR_LEGACY_VECTORS; i++)
+		irq_matrix_assign_system(loongarch_avec.vector_matrix, i, false);
+
+	irq_matrix_online(loongarch_avec.vector_matrix);
+
+	return 0;
+}
+
+static int __init loongarch_avec_init(struct irq_domain *parent)
+{
+	int ret = 0, parent_irq;
+	unsigned long tmp;
+
+	tmp = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
+	tmp |= IOCSR_MISC_FUNC_AVEC_EN;
+	iocsr_write64(tmp, LOONGARCH_IOCSR_MISC_FUNC);
+
+	raw_spin_lock_init(&loongarch_avec.lock);
+
+	loongarch_avec.fwnode = irq_domain_alloc_named_fwnode("CORE_AVEC");
+	if (!loongarch_avec.fwnode) {
+		pr_err("Unable to allocate domain handle\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	loongarch_avec.domain = irq_domain_create_tree(loongarch_avec.fwnode,
+			&loongarch_avec_domain_ops, NULL);
+	if (!loongarch_avec.domain) {
+		pr_err("core-vec: cannot create IRQ domain\n");
+		ret = -ENOMEM;
+		goto out_free_handle;
+	}
+
+	parent_irq = irq_create_mapping(parent, INT_AVEC);
+	if (!parent_irq) {
+		pr_err("Failed to mapping hwirq\n");
+		ret = -EINVAL;
+		goto out_remove_domain;
+	}
+	irq_set_chained_handler_and_data(parent_irq, loongarch_avec_dispatch, NULL);
+
+	ret = irq_matrix_init();
+	if (ret) {
+		pr_err("Failed to init irq matrix\n");
+		goto out_free_matrix;
+	}
+
+	return ret;
+
+out_free_matrix:
+	kfree(loongarch_avec.vector_matrix);
+out_remove_domain:
+	irq_domain_remove(loongarch_avec.domain);
+out_free_handle:
+	irq_domain_free_fwnode(loongarch_avec.fwnode);
+out:
+	return ret;
+}
+
+static int loongarch_avec_offline_cpu(unsigned int cpu)
+{
+	unsigned long flags;
+	struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
+
+	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
+	if (list_empty(&plist->head)) {
+		irq_matrix_offline(loongarch_avec.vector_matrix);
+	} else {
+		pr_warn("cpu %d advanced extioi is busy\n");
+		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+		return -EBUSY;
+	}
+	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+	return 0;
+}
+
+static int loongarch_avec_online_cpu(unsigned int cpu)
+{
+	struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
+
+	irq_matrix_online(loongarch_avec.vector_matrix);
+
+	INIT_LIST_HEAD(&plist->head);
+
+	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
+	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_v2 = pchmsi_entry->msg_address;
+	return pch_msi_acpi_init_v2(loongarch_avec.domain, pchmsi_entry);
+}
+
+static inline int __init acpi_cascade_irqdomain_init(void)
+{
+	return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
+}
+
+int __init loongarch_avec_acpi_init(struct irq_domain *parent)
+{
+	int ret = 0;
+
+	ret = loongarch_avec_init(parent);
+	if (ret) {
+		pr_err("Failed to init irq domain\n");
+		return ret;
+	}
+
+	ret = acpi_cascade_irqdomain_init();
+	if (ret) {
+		pr_err("Failed to cascade IRQ domain\n");
+		return ret;
+	}
+
+	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+			"loongarch_avec:online",
+			loongarch_avec_online_cpu, loongarch_avec_offline_cpu);
+	if (ret < 0) {
+		pr_err("loongarch_avec: failed to register hotplug callbacks.\n");
+		return ret;
+	}
+
+	return ret;
+}
+#endif
diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c
index 9d8f2c406043..1ecac59925c6 100644
--- a/drivers/irqchip/irq-loongarch-cpu.c
+++ b/drivers/irqchip/irq-loongarch-cpu.c
@@ -138,7 +138,9 @@ static int __init acpi_cascade_irqdomain_init(void)
 	if (r < 0)
 		return r;
 
-	return 0;
+	if (cpu_has_avecint)
+		r = loongarch_avec_acpi_init(irq_domain);
+	return r;
 }
 
 static int __init cpuintc_acpi_init(union acpi_subtable_headers *header,
diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
index 405f622a26ad..39795241304e 100644
--- a/drivers/irqchip/irq-loongson-eiointc.c
+++ b/drivers/irqchip/irq-loongson-eiointc.c
@@ -359,6 +359,9 @@ static int __init acpi_cascade_irqdomain_init(void)
 	if (r < 0)
 		return r;
 
+	if (cpu_has_avecint)
+		return 0;
+
 	r = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
 	if (r < 0)
 		return r;
diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
index 6e1e1f011bb2..d1706080b4f4 100644
--- a/drivers/irqchip/irq-loongson-pch-msi.c
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
@@ -16,7 +16,6 @@
 #include <linux/slab.h>
 
 static int nr_pics;
-
 struct pch_msi_data {
 	struct mutex	msi_map_lock;
 	phys_addr_t	doorbell;
@@ -100,6 +99,17 @@ static struct irq_chip middle_irq_chip = {
 	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
 };
 
+static struct irq_chip pch_msi_irq_chip_v2 = {
+	.name			= "MSI",
+	.irq_ack		= irq_chip_ack_parent,
+};
+
+static struct msi_domain_info pch_msi_domain_info_v2 = {
+	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+			MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
+	.chip	= &pch_msi_irq_chip_v2,
+};
+
 static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
 					unsigned int virq, int hwirq)
 {
@@ -268,6 +278,9 @@ struct fwnode_handle *get_pch_msi_handle(int pci_segment)
 {
 	int i;
 
+	if (cpu_has_avecint)
+		return pch_msi_handle[0];
+
 	for (i = 0; i < MAX_IO_PICS; i++) {
 		if (msi_group[i].pci_segment == pci_segment)
 			return pch_msi_handle[i];
@@ -289,4 +302,34 @@ int __init pch_msi_acpi_init(struct irq_domain *parent,
 
 	return ret;
 }
+
+int __init pch_msi_acpi_init_v2(struct irq_domain *parent,
+		struct acpi_madt_msi_pic *msi_entry)
+{
+	struct irq_domain *msi_domain;
+
+	if (pch_msi_handle[0])
+		return 0;
+
+	pch_msi_handle[0] = irq_domain_alloc_named_fwnode("msipic-v2");
+	if (!pch_msi_handle[0]) {
+		pr_err("Unable to allocate domain handle\n");
+		kfree(pch_msi_handle[0]);
+		return -ENOMEM;
+	}
+
+	msi_domain = pci_msi_create_irq_domain(pch_msi_handle[0],
+			&pch_msi_domain_info_v2,
+			parent);
+	if (!msi_domain) {
+		pr_err("Failed to create PCI MSI domain\n");
+		kfree(pch_msi_handle[0]);
+		return -ENOMEM;
+	}
+
+	pr_info("IRQ domain MSIPIC-V2 init done.\n");
+	return 0;
+}
+
+
 #endif
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-07 12:59 [PATCH 2/2] Loongarch:Support loongarch avec Tianyang Zhang
@ 2024-05-07 14:20 ` Xi Ruoyao
  2024-05-13  6:41   ` Tianyang Zhang
  2024-05-08  6:19 ` kernel test robot
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Xi Ruoyao @ 2024-05-07 14:20 UTC (permalink / raw)
  To: Tianyang Zhang, chenhuacai, kernel, tglx, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, yijun, mhocko, akpm, dianders, maobibo,
	zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: loongarch, linux-doc, linux-kernel

On Tue, 2024-05-07 at 20:59 +0800, Tianyang Zhang wrote:
> +static inline void loongarch_avec_ack_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void loongarch_avec_unmask_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void loongarch_avec_mask_irq(struct irq_data *d)
> +{
> +}

"inline" has no use here because these functions are only called via
function pointers, thus such calls cannot be inline-able.  I'd suggest
to remove "inline" for them.

-- 
Xi Ruoyao <xry111@xry111.site>
School of Aerospace Science and Technology, Xidian University

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-07 12:59 [PATCH 2/2] Loongarch:Support loongarch avec Tianyang Zhang
  2024-05-07 14:20 ` Xi Ruoyao
@ 2024-05-08  6:19 ` kernel test robot
  2024-05-08  7:00 ` maobibo
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: kernel test robot @ 2024-05-08  6:19 UTC (permalink / raw)
  To: Tianyang Zhang, chenhuacai, kernel, tglx, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, yijun, mhocko, akpm, dianders, maobibo,
	xry111, zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: oe-kbuild-all, loongarch, linux-doc, linux-kernel

Hi Tianyang,

kernel test robot noticed the following build warnings:

[auto build test WARNING on tip/irq/core]
[also build test WARNING on linus/master v6.9-rc7]
[cannot apply to next-20240507]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Tianyang-Zhang/Loongarch-Support-loongarch-avec/20240507-210314
base:   tip/irq/core
patch link:    https://lore.kernel.org/r/20240507125953.9117-1-zhangtianyang%40loongson.cn
patch subject: [PATCH 2/2] Loongarch:Support loongarch avec
config: mips-loongson3_defconfig (https://download.01.org/0day-ci/archive/20240508/202405081417.CPM5mm4Q-lkp@intel.com/config)
compiler: mips64el-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240508/202405081417.CPM5mm4Q-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202405081417.CPM5mm4Q-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/irqchip/irq-loongson-pch-msi.c:107:31: warning: 'pch_msi_domain_info_v2' defined but not used [-Wunused-variable]
     107 | static struct msi_domain_info pch_msi_domain_info_v2 = {
         |                               ^~~~~~~~~~~~~~~~~~~~~~


vim +/pch_msi_domain_info_v2 +107 drivers/irqchip/irq-loongson-pch-msi.c

   106	
 > 107	static struct msi_domain_info pch_msi_domain_info_v2 = {
   108		.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
   109				MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
   110		.chip	= &pch_msi_irq_chip_v2,
   111	};
   112	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-07 12:59 [PATCH 2/2] Loongarch:Support loongarch avec Tianyang Zhang
  2024-05-07 14:20 ` Xi Ruoyao
  2024-05-08  6:19 ` kernel test robot
@ 2024-05-08  7:00 ` maobibo
  2024-05-13  6:53   ` Tianyang Zhang
  2024-05-08  9:43 ` Thomas Gleixner
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: maobibo @ 2024-05-08  7:00 UTC (permalink / raw)
  To: Tianyang Zhang, chenhuacai, kernel, tglx, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, yijun, mhocko, akpm, dianders, xry111,
	zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: loongarch, linux-doc, linux-kernel



On 2024/5/7 下午8:59, Tianyang Zhang wrote:
> From: zhangtianyang <zhangtianyang@loongson.cn>
> 
> Introduce the advanced extended interrupt controllers,
> This feature will allow each core to have an independent
> 256 interrupt vectors, and MSI interrupts can be
> independently routed to any vector on any CPU.

Hi tianyang,

Thanks for provide the patch.

One simple question, can old kernel without this driver such as kernel 
6.8 run in the new hardware with advanced extended interrupt controllers?

> 
> Co-developed-by: Jianmin Lv <lvjianmin@loongson.cn>
> Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
> Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
> ---
>   arch/loongarch/Kconfig                    |   1 +
>   arch/loongarch/include/asm/cpu-features.h |   1 +
>   arch/loongarch/include/asm/cpu.h          |   2 +
>   arch/loongarch/include/asm/hw_irq.h       |  10 +
>   arch/loongarch/include/asm/irq.h          |   7 +-
>   arch/loongarch/include/asm/loongarch.h    |  19 +-
>   arch/loongarch/include/asm/smp.h          |   1 +
>   arch/loongarch/kernel/cpu-probe.c         |   3 +-
>   arch/loongarch/kernel/smp.c               |   3 +
>   drivers/irqchip/Makefile                  |   2 +-
>   drivers/irqchip/irq-loongarch-avec.c      | 434 ++++++++++++++++++++++
>   drivers/irqchip/irq-loongarch-cpu.c       |   4 +-
>   drivers/irqchip/irq-loongson-eiointc.c    |   3 +
>   drivers/irqchip/irq-loongson-pch-msi.c    |  45 ++-
>   14 files changed, 525 insertions(+), 10 deletions(-)
>   create mode 100644 drivers/irqchip/irq-loongarch-avec.c
> 
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 10959e6c3583..71b8f19745e0 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -76,6 +76,7 @@ config LOONGARCH
>   	select GENERIC_ENTRY
>   	select GENERIC_GETTIMEOFDAY
>   	select GENERIC_IOREMAP if !ARCH_IOREMAP
> +	select GENERIC_IRQ_MATRIX_ALLOCATOR
>   	select GENERIC_IRQ_MULTI_HANDLER
>   	select GENERIC_IRQ_PROBE
>   	select GENERIC_IRQ_SHOW
> diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
> index 2eafe6a6aca8..16a716f88a5c 100644
> --- a/arch/loongarch/include/asm/cpu-features.h
> +++ b/arch/loongarch/include/asm/cpu-features.h
> @@ -65,5 +65,6 @@
>   #define cpu_has_guestid		cpu_opt(LOONGARCH_CPU_GUESTID)
>   #define cpu_has_hypervisor	cpu_opt(LOONGARCH_CPU_HYPERVISOR)
>   #define cpu_has_ptw		cpu_opt(LOONGARCH_CPU_PTW)
> +#define cpu_has_avecint		cpu_opt(LOONGARCH_CPU_AVECINT)
>   
>   #endif /* __ASM_CPU_FEATURES_H */
> diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
> index 48b9f7168bcc..843f9c4ec980 100644
> --- a/arch/loongarch/include/asm/cpu.h
> +++ b/arch/loongarch/include/asm/cpu.h
> @@ -99,6 +99,7 @@ enum cpu_type_enum {
>   #define CPU_FEATURE_GUESTID		24	/* CPU has GuestID feature */
>   #define CPU_FEATURE_HYPERVISOR		25	/* CPU has hypervisor (running in VM) */
>   #define CPU_FEATURE_PTW			26	/* CPU has hardware page table walker */
> +#define CPU_FEATURE_AVECINT		27	/* CPU has avec interrupt */
>   
>   #define LOONGARCH_CPU_CPUCFG		BIT_ULL(CPU_FEATURE_CPUCFG)
>   #define LOONGARCH_CPU_LAM		BIT_ULL(CPU_FEATURE_LAM)
> @@ -127,5 +128,6 @@ enum cpu_type_enum {
>   #define LOONGARCH_CPU_GUESTID		BIT_ULL(CPU_FEATURE_GUESTID)
>   #define LOONGARCH_CPU_HYPERVISOR	BIT_ULL(CPU_FEATURE_HYPERVISOR)
>   #define LOONGARCH_CPU_PTW		BIT_ULL(CPU_FEATURE_PTW)
> +#define LOONGARCH_CPU_AVECINT		BIT_ULL(CPU_FEATURE_AVECINT)
>   
>   #endif /* _ASM_CPU_H */
> diff --git a/arch/loongarch/include/asm/hw_irq.h b/arch/loongarch/include/asm/hw_irq.h
> index af4f4e8fbd85..521abce32135 100644
> --- a/arch/loongarch/include/asm/hw_irq.h
> +++ b/arch/loongarch/include/asm/hw_irq.h
> @@ -9,6 +9,16 @@
>   
>   extern atomic_t irq_err_count;
>   
> +/*
> + * 256 vectors Map:
> + *
> + * 0 - 15: mapping legacy IPs, e.g. IP0-12.
> + * 16 - 255: mapping a vector for external IRQ.
> + *
> + */
> +#define NR_VECTORS	256
> +#define IRQ_MATRIX_BITS	NR_VECTORS
> +#define NR_LEGACY_VECTORS	16
>   /*
>    * interrupt-retrigger: NOP for now. This may not be appropriate for all
>    * machines, we'll see ...
> diff --git a/arch/loongarch/include/asm/irq.h b/arch/loongarch/include/asm/irq.h
> index 218b4da0ea90..e28bd96c6611 100644
> --- a/arch/loongarch/include/asm/irq.h
> +++ b/arch/loongarch/include/asm/irq.h
> @@ -65,7 +65,7 @@ extern struct acpi_vector_group msi_group[MAX_IO_PICS];
>   #define LOONGSON_LPC_LAST_IRQ		(LOONGSON_LPC_IRQ_BASE + 15)
>   
>   #define LOONGSON_CPU_IRQ_BASE		16
> -#define LOONGSON_CPU_LAST_IRQ		(LOONGSON_CPU_IRQ_BASE + 14)
> +#define LOONGSON_CPU_LAST_IRQ		(LOONGSON_CPU_IRQ_BASE + 15)
>   
>   #define LOONGSON_PCH_IRQ_BASE		64
>   #define LOONGSON_PCH_ACPI_IRQ		(LOONGSON_PCH_IRQ_BASE + 47)
> @@ -101,6 +101,11 @@ int pch_msi_acpi_init(struct irq_domain *parent,
>   					struct acpi_madt_msi_pic *acpi_pchmsi);
>   int pch_pic_acpi_init(struct irq_domain *parent,
>   					struct acpi_madt_bio_pic *acpi_pchpic);
> +int __init pch_msi_acpi_init_v2(struct irq_domain *parent,
> +		struct acpi_madt_msi_pic *pch_msi_entry);
> +int __init loongarch_avec_acpi_init(struct irq_domain *parent);
> +void complete_irq_moving(int *restart);
> +
>   int find_pch_pic(u32 gsi);
>   struct fwnode_handle *get_pch_msi_handle(int pci_segment);
>   
> diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
> index 46366e783c84..4d9a09861925 100644
> --- a/arch/loongarch/include/asm/loongarch.h
> +++ b/arch/loongarch/include/asm/loongarch.h
> @@ -72,7 +72,6 @@
>   #define  CPUCFG1_RPLV			BIT(23)
>   #define  CPUCFG1_HUGEPG			BIT(24)
>   #define  CPUCFG1_CRC32			BIT(25)
> -#define  CPUCFG1_MSGINT			BIT(26)
>   
>   #define LOONGARCH_CPUCFG2		0x2
>   #define  CPUCFG2_FP			BIT(0)
> @@ -240,8 +239,8 @@
>   #define  CSR_ESTAT_EXC_WIDTH		6
>   #define  CSR_ESTAT_EXC			(_ULCAST_(0x3f) << CSR_ESTAT_EXC_SHIFT)
>   #define  CSR_ESTAT_IS_SHIFT		0
> -#define  CSR_ESTAT_IS_WIDTH		14
> -#define  CSR_ESTAT_IS			(_ULCAST_(0x3fff) << CSR_ESTAT_IS_SHIFT)
> +#define  CSR_ESTAT_IS_WIDTH		15
> +#define  CSR_ESTAT_IS			(_ULCAST_(0x7fff) << CSR_ESTAT_IS_SHIFT)
>   
>   #define LOONGARCH_CSR_ERA		0x6	/* ERA */
>   
> @@ -987,10 +986,17 @@
>   #define CSR_FWPC_SKIP_SHIFT		16
>   #define CSR_FWPC_SKIP			(_ULCAST_(1) << CSR_FWPC_SKIP_SHIFT)
>   
> +#define LOONGARCH_CSR_IRR0		0xa0
> +#define LOONGARCH_CSR_IRR1		0xa1
> +#define LOONGARCH_CSR_IRR2		0xa2
> +#define LOONGARCH_CSR_IRR3		0xa3
> +
> +#define	LOONGARCH_CSR_ILR		0xa4
> +
>   /*
>    * CSR_ECFG IM
>    */
> -#define ECFG0_IM		0x00001fff
> +#define ECFG0_IM		0x00005fff
>   #define ECFGB_SIP0		0
>   #define ECFGF_SIP0		(_ULCAST_(1) << ECFGB_SIP0)
>   #define ECFGB_SIP1		1
> @@ -1033,6 +1039,7 @@
>   #define  IOCSRF_EIODECODE		BIT_ULL(9)
>   #define  IOCSRF_FLATMODE		BIT_ULL(10)
>   #define  IOCSRF_VM			BIT_ULL(11)
> +#define  IOCSRF_AVEC			BIT_ULL(15)
>   
>   #define LOONGARCH_IOCSR_VENDOR		0x10
>   
> @@ -1043,6 +1050,7 @@
>   #define LOONGARCH_IOCSR_MISC_FUNC	0x420
>   #define  IOCSR_MISC_FUNC_TIMER_RESET	BIT_ULL(21)
>   #define  IOCSR_MISC_FUNC_EXT_IOI_EN	BIT_ULL(48)
> +#define  IOCSR_MISC_FUNC_AVEC_EN	BIT_ULL(51)
>   
>   #define LOONGARCH_IOCSR_CPUTEMP		0x428
>   
> @@ -1363,9 +1371,10 @@ __BUILD_CSR_OP(tlbidx)
>   #define INT_TI		11	/* Timer */
>   #define INT_IPI		12
>   #define INT_NMI		13
> +#define INT_AVEC	14
>   
>   /* ExcCodes corresponding to interrupts */
> -#define EXCCODE_INT_NUM		(INT_NMI + 1)
> +#define EXCCODE_INT_NUM		(INT_AVEC + 1)
>   #define EXCCODE_INT_START	64
>   #define EXCCODE_INT_END		(EXCCODE_INT_START + EXCCODE_INT_NUM - 1)
>   
> diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h
> index f81e5f01d619..577f591c1c73 100644
> --- a/arch/loongarch/include/asm/smp.h
> +++ b/arch/loongarch/include/asm/smp.h
> @@ -62,6 +62,7 @@ extern int __cpu_logical_map[NR_CPUS];
>   #define SMP_BOOT_CPU		0x1
>   #define SMP_RESCHEDULE		0x2
>   #define SMP_CALL_FUNCTION	0x4
> +#define SMP_CLEAR_VECT		0x8
>   
>   struct secondary_data {
>   	unsigned long stack;
> diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
> index 55320813ee08..3b2e72e8f9bd 100644
> --- a/arch/loongarch/kernel/cpu-probe.c
> +++ b/arch/loongarch/kernel/cpu-probe.c
> @@ -106,7 +106,6 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
>   		elf_hwcap |= HWCAP_LOONGARCH_CRC32;
>   	}
>   
> -
>   	config = read_cpucfg(LOONGARCH_CPUCFG2);
>   	if (config & CPUCFG2_LAM) {
>   		c->options |= LOONGARCH_CPU_LAM;
> @@ -176,6 +175,8 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
>   		c->options |= LOONGARCH_CPU_EIODECODE;
>   	if (config & IOCSRF_VM)
>   		c->options |= LOONGARCH_CPU_HYPERVISOR;
> +	if (config & IOCSRF_AVEC)
> +		c->options |= LOONGARCH_CPU_AVECINT;
>   
>   	config = csr_read32(LOONGARCH_CSR_ASID);
>   	config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT;
> diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c
> index a16e3dbe9f09..4fcf399a8b17 100644
> --- a/arch/loongarch/kernel/smp.c
> +++ b/arch/loongarch/kernel/smp.c
> @@ -176,6 +176,9 @@ irqreturn_t loongson_ipi_interrupt(int irq, void *dev)
>   		per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++;
>   	}
>   
> +	if (action & SMP_CLEAR_VECT)
> +		complete_irq_moving(NULL);
> +
>   	return IRQ_HANDLED;
>   }
>   
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index ec4a18380998..398db4c3e264 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -105,7 +105,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
> +obj-$(CONFIG_IRQ_LOONGARCH_CPU)		+= irq-loongarch-cpu.o irq-loongarch-avec.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
> new file mode 100644
> index 000000000000..71ce33db63db
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongarch-avec.c
> @@ -0,0 +1,434 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Loongson Technologies, Inc.
> + */
> +
> +#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 <linux/irqchip/chained_irq.h>
> +#include <linux/cpuhotplug.h>
> +#include <linux/radix-tree.h>
> +
> +#include <asm/loongarch.h>
> +#include <asm/setup.h>
> +
> +static phys_addr_t msi_base_v2;
> +
> +typedef struct irq_data *irq_map_t[NR_VECTORS];
> +DECLARE_PER_CPU(irq_map_t, irq_map);
> +DEFINE_PER_CPU(irq_map_t, irq_map) = {
> +	[0 ... NR_VECTORS - 1] = NULL,
> +};
> +
> +struct pending_list {
> +	struct list_head head;
> +	raw_spinlock_t	lock;
> +};
> +
> +DEFINE_PER_CPU(struct pending_list, pending_list);
> +
> +struct loongarch_avec_chip {
> +	struct fwnode_handle	*fwnode;
> +	struct irq_domain	*domain;
> +	struct irq_matrix	*vector_matrix;
> +	raw_spinlock_t		lock;
> +} loongarch_avec;
> +
> +struct loongarch_avec_data {
> +	struct list_head entry;
> +	unsigned int cpu;
> +	unsigned int vec;
> +	unsigned int prev_cpu;
> +	unsigned int prev_vec;
> +};
> +
> +static int assign_irq_vector(struct irq_data *irqd, const struct cpumask *dest,
> +		unsigned int *cpu, int *vector)
> +{
> +	int ret;
> +
> +	ret = irq_matrix_alloc(loongarch_avec.vector_matrix, dest, false, cpu);
> +	if (ret < 0)
> +		return ret;
> +	*vector = ret;
> +
> +	return 0;
> +}
> +
> +static inline void loongarch_avec_ack_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void loongarch_avec_unmask_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void loongarch_avec_mask_irq(struct irq_data *d)
> +{
> +}
> +
> +static void loongarch_avec_sync(struct loongarch_avec_data *adata)
> +{
> +	struct loongarch_avec_data *data;
> +	struct pending_list *plist;
> +
> +	if (cpu_online(adata->prev_cpu)) {
> +		plist = per_cpu_ptr(&pending_list, adata->prev_cpu);
> +
> +		data = kmalloc(sizeof(struct loongarch_avec_data), GFP_KERNEL);
> +		if (!data) {
> +			pr_warn("NO space for clean data\n");
> +			return;
> +		}
> +		memcpy(data, adata, sizeof(struct loongarch_avec_data));
> +		INIT_LIST_HEAD(&data->entry);
> +
> +		list_add_tail(&data->entry, &plist->head);
> +		loongson_send_ipi_single(adata->prev_cpu, SMP_CLEAR_VECT);
> +	}
> +	adata->prev_cpu = adata->cpu;
> +	adata->prev_vec = adata->vec;
> +}
> +
> +static int loongarch_avec_set_affinity(struct irq_data *data,
> +		const struct cpumask *dest, bool force)
> +{
> +	struct cpumask intersect_mask;
> +	struct loongarch_avec_data *adata;
> +	unsigned int cpu, vector;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +	adata = irq_data_get_irq_chip_data(data);
> +
> +	if (adata->vec && cpu_online(adata->cpu)
> +			&& cpumask_test_cpu(adata->cpu, dest)) {
> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +		return 0;
> +	}
> +
> +	if (!cpumask_intersects(dest, cpu_online_mask)) {
> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +		return -EINVAL;
> +	}
> +
> +	cpumask_and(&intersect_mask, dest, cpu_online_mask);
> +
> +	ret = assign_irq_vector(data, &intersect_mask, &cpu, &vector);
> +	if (ret) {
> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +		return ret;
> +	}
> +
> +	adata->cpu = cpu;
> +	adata->vec = vector;
> +	per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = data;
> +	loongarch_avec_sync(adata);
> +
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +	irq_data_update_effective_affinity(data, cpumask_of(cpu));
> +
> +	return IRQ_SET_MASK_OK;
> +}
> +
> +static void loongarch_avec_compose_msg(struct irq_data *d,
> +		struct msi_msg *msg)
> +{
> +	struct loongarch_avec_data *avec_data;
> +
> +	avec_data = irq_data_get_irq_chip_data(d);
> +
> +	msg->address_hi = 0x0;
> +	msg->address_lo = msi_base_v2 | ((avec_data->vec & 0xff) << 4) |
> +		((cpu_logical_map(avec_data->cpu & 0xffff)) << 12);
There is requirement for variable msi_base_v2 aligned with 1<< 28.

Is that right? However there is no such checking in function 
pch_msi_parse_madt().

Also it will be better if there is some notations.

> +	msg->data = 0x0;
> +
> +}
> +
> +static struct irq_chip loongarch_avec_controller = {
> +	.name			= "CORE_AVEC",
> +	.irq_ack		= loongarch_avec_ack_irq,
> +	.irq_mask		= loongarch_avec_mask_irq,
> +	.irq_unmask		= loongarch_avec_unmask_irq,
> +	.irq_set_affinity	= loongarch_avec_set_affinity,
> +	.irq_compose_msi_msg	= loongarch_avec_compose_msg,
> +};
> +
> +void complete_irq_moving(int *restart)
> +{
> +	struct pending_list *plist = this_cpu_ptr(&pending_list);
> +	struct loongarch_avec_data *adata, *tmp;
> +	int cpu, vector;
> +	u32 bias;
> +	u64 irr;
> +
> +	raw_spin_lock(&loongarch_avec.lock);
> +
> +	list_for_each_entry_safe(adata, tmp, &plist->head, entry) {
> +
> +		cpu = adata->prev_cpu;
> +		vector = adata->prev_vec;
> +		bias = vector/64;
> +		switch (bias) {
> +		case 0x0:
> +			irr = csr_read64(LOONGARCH_CSR_IRR0);
> +			break;
> +		case 0x1:
> +			irr = csr_read64(LOONGARCH_CSR_IRR1);
> +			break;
> +		case 0x2:
> +			irr = csr_read64(LOONGARCH_CSR_IRR2);
> +			break;
> +		case 0x3:
> +			irr = csr_read64(LOONGARCH_CSR_IRR3);
> +			break;
> +		default:
> +			return;
> +		}
> +
> +		if (irr & (1UL << (vector % 64))) {
> +			loongson_send_ipi_single(cpu, SMP_CLEAR_VECT);

It is strange that sending ipi to itself to clear MSI interrupt.
It requires that there cannot be loop check in ipi interrupt handler, 
else ipi hanlder loop checking will find new IPI message again.

Is it possible to do_irq() for MSI interrupt directly in ipi handler?
	d = raw_cpu_ptr(irq_map)[vector];
	if (d)
		generic_handle_irq(d->irq);

> +			continue;
> +		}
> +		list_del(&adata->entry);
> +		irq_matrix_free(loongarch_avec.vector_matrix, cpu, vector, false);
> +		this_cpu_ptr(irq_map)[vector] = 0;
> +		kfree(adata);
> +	}
> +	raw_spin_unlock(&loongarch_avec.lock);
> +}
> +
> +static void loongarch_avec_dispatch(struct irq_desc *desc)
> +{
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct irq_data *d;
> +	unsigned long vector;
> +
> +	chained_irq_enter(chip, desc);
> +	vector = csr_read64(LOONGARCH_CSR_ILR);
> +	if (vector & 0x80000000)
> +		return;
what is the meaning of 0x80000000? It had better be defined as macro.

Regards
Bibo Mao
> +
> +	vector &= 0xff;
> +
> +	d = raw_cpu_ptr(irq_map)[vector];
> +	if (d)
> +		generic_handle_irq(d->irq);
> +	else
> +		pr_warn("IRQ ERROR:Unexpected irq  occur on cpu %d[vector %d]\n",
> +				smp_processor_id(), vector);
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static int loongarch_avec_alloc(struct irq_domain *domain, unsigned int virq,
> +		unsigned int nr_irqs, void *arg)
> +{
> +	struct loongarch_avec_data *adata;
> +	struct irq_data *irqd;
> +	unsigned int cpu, vector;
> +	unsigned long flags;
> +	int i, err;
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +	for (i = 0; i < nr_irqs; i++) {
> +		irqd = irq_domain_get_irq_data(domain, virq + i);
> +		adata = kzalloc(sizeof(*adata), GFP_KERNEL);
> +		if (!adata) {
> +			raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +			return -ENOMEM;
> +		}
> +		err = assign_irq_vector(irqd, cpu_online_mask, &cpu, &vector);
> +		if (err) {
> +			raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +			return err;
> +		}
> +		adata->prev_cpu = adata->cpu = cpu;
> +		adata->prev_vec = adata->vec = vector;
> +
> +		per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irqd;
> +		irq_domain_set_info(domain, virq + i, virq, &loongarch_avec_controller,
> +				adata, handle_edge_irq, NULL, NULL);
> +		irqd_set_single_target(irqd);
> +		irqd_set_affinity_on_activate(irqd);
> +	}
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +
> +	return err;
> +}
> +
> +static void loongarch_avec_free(struct irq_domain *domain, unsigned int virq,
> +		unsigned int nr_irqs)
> +{
> +	struct loongarch_avec_data *adata;
> +	struct irq_data *d;
> +	unsigned long flags;
> +	unsigned int i;
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +	for (i = 0; i < nr_irqs; i++) {
> +		d = irq_domain_get_irq_data(domain, virq + i);
> +		adata = irq_data_get_irq_chip_data(d);
> +		if (d) {
> +			irq_matrix_free(loongarch_avec.vector_matrix,
> +					adata->cpu,
> +					adata->vec, false);
> +			irq_domain_reset_irq_data(d);
> +		}
> +	}
> +
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +}
> +
> +static const struct irq_domain_ops loongarch_avec_domain_ops = {
> +	.alloc		= loongarch_avec_alloc,
> +	.free		= loongarch_avec_free,
> +};
> +
> +static int __init irq_matrix_init(void)
> +{
> +	int i;
> +
> +	loongarch_avec.vector_matrix = irq_alloc_matrix(NR_VECTORS, 0, NR_VECTORS - 1);
> +	if (!loongarch_avec.vector_matrix)
> +		return -ENOMEM;
> +	for (i = 0; i < NR_LEGACY_VECTORS; i++)
> +		irq_matrix_assign_system(loongarch_avec.vector_matrix, i, false);
> +
> +	irq_matrix_online(loongarch_avec.vector_matrix);
> +
> +	return 0;
> +}
> +
> +static int __init loongarch_avec_init(struct irq_domain *parent)
> +{
> +	int ret = 0, parent_irq;
> +	unsigned long tmp;
> +
> +	tmp = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
> +	tmp |= IOCSR_MISC_FUNC_AVEC_EN;
> +	iocsr_write64(tmp, LOONGARCH_IOCSR_MISC_FUNC);
> +
> +	raw_spin_lock_init(&loongarch_avec.lock);
> +
> +	loongarch_avec.fwnode = irq_domain_alloc_named_fwnode("CORE_AVEC");
> +	if (!loongarch_avec.fwnode) {
> +		pr_err("Unable to allocate domain handle\n");
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	loongarch_avec.domain = irq_domain_create_tree(loongarch_avec.fwnode,
> +			&loongarch_avec_domain_ops, NULL);
> +	if (!loongarch_avec.domain) {
> +		pr_err("core-vec: cannot create IRQ domain\n");
> +		ret = -ENOMEM;
> +		goto out_free_handle;
> +	}
> +
> +	parent_irq = irq_create_mapping(parent, INT_AVEC);
> +	if (!parent_irq) {
> +		pr_err("Failed to mapping hwirq\n");
> +		ret = -EINVAL;
> +		goto out_remove_domain;
> +	}
> +	irq_set_chained_handler_and_data(parent_irq, loongarch_avec_dispatch, NULL);
> +
> +	ret = irq_matrix_init();
> +	if (ret) {
> +		pr_err("Failed to init irq matrix\n");
> +		goto out_free_matrix;
> +	}
> +
> +	return ret;
> +
> +out_free_matrix:
> +	kfree(loongarch_avec.vector_matrix);
> +out_remove_domain:
> +	irq_domain_remove(loongarch_avec.domain);
> +out_free_handle:
> +	irq_domain_free_fwnode(loongarch_avec.fwnode);
> +out:
> +	return ret;
> +}
> +
> +static int loongarch_avec_offline_cpu(unsigned int cpu)
> +{
> +	unsigned long flags;
> +	struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +	if (list_empty(&plist->head)) {
> +		irq_matrix_offline(loongarch_avec.vector_matrix);
> +	} else {
> +		pr_warn("cpu %d advanced extioi is busy\n");
> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +		return -EBUSY;
> +	}
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +	return 0;
> +}
> +
> +static int loongarch_avec_online_cpu(unsigned int cpu)
> +{
> +	struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +
> +	irq_matrix_online(loongarch_avec.vector_matrix);
> +
> +	INIT_LIST_HEAD(&plist->head);
> +
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +	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_v2 = pchmsi_entry->msg_address;
> +	return pch_msi_acpi_init_v2(loongarch_avec.domain, pchmsi_entry);
> +}
> +
> +static inline int __init acpi_cascade_irqdomain_init(void)
> +{
> +	return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> +}
> +
> +int __init loongarch_avec_acpi_init(struct irq_domain *parent)
> +{
> +	int ret = 0;
> +
> +	ret = loongarch_avec_init(parent);
> +	if (ret) {
> +		pr_err("Failed to init irq domain\n");
> +		return ret;
> +	}
> +
> +	ret = acpi_cascade_irqdomain_init();
> +	if (ret) {
> +		pr_err("Failed to cascade IRQ domain\n");
> +		return ret;
> +	}
> +
> +	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
> +			"loongarch_avec:online",
> +			loongarch_avec_online_cpu, loongarch_avec_offline_cpu);
> +	if (ret < 0) {
> +		pr_err("loongarch_avec: failed to register hotplug callbacks.\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +#endif
> diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c
> index 9d8f2c406043..1ecac59925c6 100644
> --- a/drivers/irqchip/irq-loongarch-cpu.c
> +++ b/drivers/irqchip/irq-loongarch-cpu.c
> @@ -138,7 +138,9 @@ static int __init acpi_cascade_irqdomain_init(void)
>   	if (r < 0)
>   		return r;
>   
> -	return 0;
> +	if (cpu_has_avecint)
> +		r = loongarch_avec_acpi_init(irq_domain);
> +	return r;
>   }
>   
>   static int __init cpuintc_acpi_init(union acpi_subtable_headers *header,
> diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
> index 405f622a26ad..39795241304e 100644
> --- a/drivers/irqchip/irq-loongson-eiointc.c
> +++ b/drivers/irqchip/irq-loongson-eiointc.c
> @@ -359,6 +359,9 @@ static int __init acpi_cascade_irqdomain_init(void)
>   	if (r < 0)
>   		return r;
>   
> +	if (cpu_has_avecint)
> +		return 0;
> +
>   	r = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
>   	if (r < 0)
>   		return r;
> diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
> index 6e1e1f011bb2..d1706080b4f4 100644
> --- a/drivers/irqchip/irq-loongson-pch-msi.c
> +++ b/drivers/irqchip/irq-loongson-pch-msi.c
> @@ -16,7 +16,6 @@
>   #include <linux/slab.h>
>   
>   static int nr_pics;
> -
>   struct pch_msi_data {
>   	struct mutex	msi_map_lock;
>   	phys_addr_t	doorbell;
> @@ -100,6 +99,17 @@ static struct irq_chip middle_irq_chip = {
>   	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
>   };
>   
> +static struct irq_chip pch_msi_irq_chip_v2 = {
> +	.name			= "MSI",
> +	.irq_ack		= irq_chip_ack_parent,
> +};
> +
> +static struct msi_domain_info pch_msi_domain_info_v2 = {
> +	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +			MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
> +	.chip	= &pch_msi_irq_chip_v2,
> +};
> +
>   static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
>   					unsigned int virq, int hwirq)
>   {
> @@ -268,6 +278,9 @@ struct fwnode_handle *get_pch_msi_handle(int pci_segment)
>   {
>   	int i;
>   
> +	if (cpu_has_avecint)
> +		return pch_msi_handle[0];
> +
>   	for (i = 0; i < MAX_IO_PICS; i++) {
>   		if (msi_group[i].pci_segment == pci_segment)
>   			return pch_msi_handle[i];
> @@ -289,4 +302,34 @@ int __init pch_msi_acpi_init(struct irq_domain *parent,
>   
>   	return ret;
>   }
> +
> +int __init pch_msi_acpi_init_v2(struct irq_domain *parent,
> +		struct acpi_madt_msi_pic *msi_entry)
> +{
> +	struct irq_domain *msi_domain;
> +
> +	if (pch_msi_handle[0])
> +		return 0;
> +
> +	pch_msi_handle[0] = irq_domain_alloc_named_fwnode("msipic-v2");
> +	if (!pch_msi_handle[0]) {
> +		pr_err("Unable to allocate domain handle\n");
> +		kfree(pch_msi_handle[0]);
> +		return -ENOMEM;
> +	}
> +
> +	msi_domain = pci_msi_create_irq_domain(pch_msi_handle[0],
> +			&pch_msi_domain_info_v2,
> +			parent);
> +	if (!msi_domain) {
> +		pr_err("Failed to create PCI MSI domain\n");
> +		kfree(pch_msi_handle[0]);
> +		return -ENOMEM;
> +	}
> +
> +	pr_info("IRQ domain MSIPIC-V2 init done.\n");
> +	return 0;
> +}
> +
> +
>   #endif
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-07 12:59 [PATCH 2/2] Loongarch:Support loongarch avec Tianyang Zhang
                   ` (2 preceding siblings ...)
  2024-05-08  7:00 ` maobibo
@ 2024-05-08  9:43 ` Thomas Gleixner
  2024-05-14  2:39   ` Tianyang Zhang
  2024-05-08 21:18 ` kernel test robot
  2024-05-08 21:28 ` kernel test robot
  5 siblings, 1 reply; 10+ messages in thread
From: Thomas Gleixner @ 2024-05-08  9:43 UTC (permalink / raw)
  To: Tianyang Zhang, chenhuacai, kernel, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, zhangtianyang, yijun, mhocko, akpm,
	dianders, maobibo, xry111, zhaotianrui, nathan, yangtiezhu,
	zhoubinbin
  Cc: loongarch, linux-doc, linux-kernel

On Tue, May 07 2024 at 20:59, Tianyang Zhang wrote:
> From: zhangtianyang <zhangtianyang@loongson.cn>

Please use your real name for the From line.

> +
> +#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 <linux/irqchip/chained_irq.h>
> +#include <linux/cpuhotplug.h>
> +#include <linux/radix-tree.h>

Please put the includes in alphabethical order.

> +#include <asm/loongarch.h>
> +#include <asm/setup.h>
> +
> +static phys_addr_t msi_base_v2;
> +
> +typedef struct irq_data *irq_map_t[NR_VECTORS];

No new pointless typedefs please.

struct irq_map {
	struct irq_data irqd[NR_VECTORS];
};

> +DECLARE_PER_CPU(irq_map_t, irq_map);

What's this declaration for?

> +DEFINE_PER_CPU(irq_map_t, irq_map) = {

Why is this global and not static? 

> +	[0 ... NR_VECTORS - 1] = NULL,

No need to initialize to NULL. It's zeroed by default.

> +};
> +
> +struct pending_list {
> +	struct list_head head;
> +	raw_spinlock_t	lock;
> +};

https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#struct-declarations-and-initializers

> +DEFINE_PER_CPU(struct pending_list, pending_list);

Why is this global?

> +struct loongarch_avec_chip {
> +	struct fwnode_handle	*fwnode;
> +	struct irq_domain	*domain;
> +	struct irq_matrix	*vector_matrix;
> +	raw_spinlock_t		lock;
> +} loongarch_avec;
> +
> +struct loongarch_avec_data {
> +	struct list_head entry;
> +	unsigned int cpu;
> +	unsigned int vec;
> +	unsigned int prev_cpu;
> +	unsigned int prev_vec;
> +};

See link above.

> +static int assign_irq_vector(struct irq_data *irqd, const struct cpumask *dest,
> +		unsigned int *cpu, int *vector)

Please read the line break section in the tip docomentation

> +{
> +	int ret;
> +
> +	ret = irq_matrix_alloc(loongarch_avec.vector_matrix, dest, false, cpu);
> +	if (ret < 0)
> +		return ret;
> +	*vector = ret;
> +
> +	return 0;

Why not simply returning the result of irq_matrix_alloc() and checking
it for negative value at the call site. If not negative then use it as
vector. That spares the whole indirection and makes the code readable.

> +static void loongarch_avec_sync(struct loongarch_avec_data *adata)
> +{
> +	struct loongarch_avec_data *data;
> +	struct pending_list *plist;
> +
> +	if (cpu_online(adata->prev_cpu)) {
> +		plist = per_cpu_ptr(&pending_list, adata->prev_cpu);
> +
> +		data = kmalloc(sizeof(struct loongarch_avec_data), GFP_KERNEL);

This is called from loongarch_avec_set_affinity() with raw spinlocks
held and interrupts disabled. So GFP_KERNEL cannot work. You clearly did
not test that code with proper debug options enabled.

But even GFP_ATOMIC won't work when you want to support PREEMPT_RT as
that does not allow allocations in such contexts.

Look how x86 solves exactly this problem without allocations required.

> +		if (!data) {
> +			pr_warn("NO space for clean data\n");
> +			return;
> +		}
> +		memcpy(data, adata, sizeof(struct loongarch_avec_data));
> +		INIT_LIST_HEAD(&data->entry);
> +
> +		list_add_tail(&data->entry, &plist->head);
> +		loongson_send_ipi_single(adata->prev_cpu, SMP_CLEAR_VECT);
> +	}
> +	adata->prev_cpu = adata->cpu;
> +	adata->prev_vec = adata->vec;
> +}
> +
> +static int loongarch_avec_set_affinity(struct irq_data *data,
> +		const struct cpumask *dest, bool force)
> +{
> +	struct cpumask intersect_mask;

No cpumasks on stack please. You can make that static as usage is always
serialized via loongarch_avec.lock

> +	struct loongarch_avec_data *adata;
> +	unsigned int cpu, vector;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +	adata = irq_data_get_irq_chip_data(data);
> +
> +	if (adata->vec && cpu_online(adata->cpu)
> +			&& cpumask_test_cpu(adata->cpu, dest)) {

Please align the condition proper when you need a line break:

	if (adata->vec && cpu_online(adata->cpu) &&
	    cpumask_test_cpu(adata->cpu, dest)) {

But you don't need a line break here because 

	if (adata->vec && cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest)) {

fits into the 100 character line width limit.

> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +		return 0;
> +	}
> +
> +	if (!cpumask_intersects(dest, cpu_online_mask)) {
> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +		return -EINVAL;
> +	}
> +
> +	cpumask_and(&intersect_mask, dest, cpu_online_mask);

The above intersect check is pointless as the matrix allocator already
checks the cpumask and returns -EINVAL if empty.

> +
> +	ret = assign_irq_vector(data, &intersect_mask, &cpu, &vector);
> +	if (ret) {
> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +		return ret;
> +	}

> +void complete_irq_moving(int *restart)

What is the 'restart' argument for?

> +{
> +	struct pending_list *plist = this_cpu_ptr(&pending_list);
> +	struct loongarch_avec_data *adata, *tmp;
> +	int cpu, vector;
> +	u32 bias;
> +	u64 irr;
> +
> +	raw_spin_lock(&loongarch_avec.lock);
> +
> +	list_for_each_entry_safe(adata, tmp, &plist->head, entry) {
> +
> +		cpu = adata->prev_cpu;
> +		vector = adata->prev_vec;
> +		bias = vector/64;
> +
> +		switch (bias) {
> +		case 0x0:
> +			irr = csr_read64(LOONGARCH_CSR_IRR0);
> +			break;
> +		case 0x1:
> +			irr = csr_read64(LOONGARCH_CSR_IRR1);
> +			break;
> +		case 0x2:
> +			irr = csr_read64(LOONGARCH_CSR_IRR2);
> +			break;
> +		case 0x3:
> +			irr = csr_read64(LOONGARCH_CSR_IRR3);
> +			break;
> +		default:
> +			return;

How can that happen ?

> +		}

                irr = csr_read64(LOONGARCH_CSR_IRR0 + vector / 64);

should be good enough, no?

Also please use a proper constant instead of '64', e.g. VECTORS_PER_IRR

> +
> +		if (irr & (1UL << (vector % 64))) {
> +			loongson_send_ipi_single(cpu, SMP_CLEAR_VECT);

So this sends an IPI to the current CPU. What guarantees that the
pending interrupt is handled _before_ the IPI is handled again?

> +			continue;
> +		}
> +		list_del(&adata->entry);
> +		irq_matrix_free(loongarch_avec.vector_matrix, cpu, vector, false);
> +		this_cpu_ptr(irq_map)[vector] = 0;

s/0/NULL/ as this writes a pointer.

                this_cpu_write(irq_map.irqd[vector], NULL);

avoids the whole pointer indirection.

> +		kfree(adata);

Again this won't work with PREEMPT_RT.

> +	}
> +	raw_spin_unlock(&loongarch_avec.lock);
> +}
> +
> +static void loongarch_avec_dispatch(struct irq_desc *desc)
> +{
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct irq_data *d;
> +	unsigned long vector;
> +
> +	chained_irq_enter(chip, desc);
> +	vector = csr_read64(LOONGARCH_CSR_ILR);
> +	if (vector & 0x80000000)

No magic numbers. Please use proper constant defines.

> +		return;
> +
> +	vector &= 0xff;

Ditto.

> +
> +	d = raw_cpu_ptr(irq_map)[vector];

Why raw?

        d = __this_cpu_read(...);

Also, what is the point of storing irqdata in the irq_map if the only
thing you use is d->irq. You can simply store the interrupt number, no?

If you want to spare cycles for the lookup, then you want to store the
interrupt descriptor like x86 does.

> +	if (d)
> +		generic_handle_irq(d->irq);
> +	else
> +		pr_warn("IRQ ERROR:Unexpected irq  occur on cpu %d[vector %d]\n",
> +				smp_processor_id(), vector);

See bracket rules in the tip documentation.

> +	chained_irq_exit(chip, desc);
> +}
> +
> +static int loongarch_avec_alloc(struct irq_domain *domain, unsigned int virq,
> +		unsigned int nr_irqs, void *arg)
> +{
> +	struct loongarch_avec_data *adata;
> +	struct irq_data *irqd;
> +	unsigned int cpu, vector;
> +	unsigned long flags;
> +	int i, err;

See variable declaration rules in the tip documentation

> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +	for (i = 0; i < nr_irqs; i++) {
> +		irqd = irq_domain_get_irq_data(domain, virq + i);
> +		adata = kzalloc(sizeof(*adata), GFP_KERNEL);
> +		if (!adata) {
> +			raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +			return -ENOMEM;
> +		}
> +		err = assign_irq_vector(irqd, cpu_online_mask, &cpu, &vector);
> +		if (err) {
> +			raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +			return err;
> +		}
> +		adata->prev_cpu = adata->cpu = cpu;
> +		adata->prev_vec = adata->vec = vector;
> +
> +		per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irqd;

This needs to be set last, no?

> +		irq_domain_set_info(domain, virq + i, virq, &loongarch_avec_controller,
> +				adata, handle_edge_irq, NULL, NULL);
> +		irqd_set_single_target(irqd);
> +		irqd_set_affinity_on_activate(irqd);
> +	}
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +
> +	return err;
> +}
> +
> +static void loongarch_avec_free(struct irq_domain *domain, unsigned int virq,
> +		unsigned int nr_irqs)
> +{
> +	struct loongarch_avec_data *adata;
> +	struct irq_data *d;
> +	unsigned long flags;
> +	unsigned int i;
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +	for (i = 0; i < nr_irqs; i++) {
> +		d = irq_domain_get_irq_data(domain, virq + i);
> +		adata = irq_data_get_irq_chip_data(d);
> +		if (d) {
> +			irq_matrix_free(loongarch_avec.vector_matrix,
> +					adata->cpu,
> +					adata->vec, false);
> +			irq_domain_reset_irq_data(d);
> +		}

What cleans up the irq_map and pending cleanups? There is a UAF waiting
around the corner.

> +	}
> +
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +}

> +static int __init loongarch_avec_init(struct irq_domain *parent)
> +{
> +	int ret = 0, parent_irq;
> +	unsigned long tmp;
> +
> +	tmp = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
> +	tmp |= IOCSR_MISC_FUNC_AVEC_EN;
> +	iocsr_write64(tmp, LOONGARCH_IOCSR_MISC_FUNC);

Enabling AVEC _before_ everything is set up is a patently bad idea.

> +	raw_spin_lock_init(&loongarch_avec.lock);
> +
> +	loongarch_avec.fwnode = irq_domain_alloc_named_fwnode("CORE_AVEC");
> +	if (!loongarch_avec.fwnode) {
> +		pr_err("Unable to allocate domain handle\n");
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	loongarch_avec.domain = irq_domain_create_tree(loongarch_avec.fwnode,
> +			&loongarch_avec_domain_ops, NULL);
> +	if (!loongarch_avec.domain) {
> +		pr_err("core-vec: cannot create IRQ domain\n");
> +		ret = -ENOMEM;
> +		goto out_free_handle;
> +	}
> +
> +	parent_irq = irq_create_mapping(parent, INT_AVEC);
> +	if (!parent_irq) {
> +		pr_err("Failed to mapping hwirq\n");
> +		ret = -EINVAL;
> +		goto out_remove_domain;
> +	}
> +	irq_set_chained_handler_and_data(parent_irq, loongarch_avec_dispatch, NULL);
> +
> +	ret = irq_matrix_init();
> +	if (ret) {
> +		pr_err("Failed to init irq matrix\n");
> +		goto out_free_matrix;
> +	}
> +
> +	return ret;
> +
> +out_free_matrix:
> +	kfree(loongarch_avec.vector_matrix);
> +out_remove_domain:
> +	irq_domain_remove(loongarch_avec.domain);
> +out_free_handle:
> +	irq_domain_free_fwnode(loongarch_avec.fwnode);
> +out:
> +	return ret;
> +}
> +
> +static int loongarch_avec_offline_cpu(unsigned int cpu)
> +{
> +	unsigned long flags;
> +	struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +	if (list_empty(&plist->head)) {
> +		irq_matrix_offline(loongarch_avec.vector_matrix);
> +	} else {
> +		pr_warn("cpu %d advanced extioi is busy\n");
> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +		return -EBUSY;
> +	}
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +	return 0;
> +}
> +
> +static int loongarch_avec_online_cpu(unsigned int cpu)
> +{
> +	struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
> +
> +	irq_matrix_online(loongarch_avec.vector_matrix);
> +
> +	INIT_LIST_HEAD(&plist->head);
> +
> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
> +	return 0;
> +}
> +#if defined(CONFIG_ACPI)

Missing newline before #if and also please use #ifdef 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_v2 = pchmsi_entry->msg_address;
> +	return pch_msi_acpi_init_v2(loongarch_avec.domain, pchmsi_entry);
> +}
> +
> +static inline int __init acpi_cascade_irqdomain_init(void)
> +{
> +	return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> +}
> +
> +int __init loongarch_avec_acpi_init(struct irq_domain *parent)
> +{
> +	int ret = 0;
> +
> +	ret = loongarch_avec_init(parent);
> +	if (ret) {
> +		pr_err("Failed to init irq domain\n");
> +		return ret;
> +	}
> +
> +	ret = acpi_cascade_irqdomain_init();
> +	if (ret) {
> +		pr_err("Failed to cascade IRQ domain\n");
> +		return ret;
> +	}
> +
> +	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
> +			"loongarch_avec:online",
> +			loongarch_avec_online_cpu, loongarch_avec_offline_cpu);

You cannot online/offline the matrix and handle eventually pending
cleanups from a CPUHP_AP_ONLINE_DYN state. That needs to happen in the
STARTING section between CPUHP_AP_OFFLINE and CPUHP_AP_ONLINE

> +	if (ret < 0) {
> +		pr_err("loongarch_avec: failed to register hotplug callbacks.\n");
> +		return ret;
> +	}
> +
> +	return ret;

So if CONFIG_ACPI is disabled then loongarch_avec_init() is unused and
results in a defined but not used build warning...

> diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
> index 6e1e1f011bb2..d1706080b4f4 100644
> --- a/drivers/irqchip/irq-loongson-pch-msi.c
> +++ b/drivers/irqchip/irq-loongson-pch-msi.c
> @@ -16,7 +16,6 @@
>  #include <linux/slab.h>
>  
>  static int nr_pics;
> -
>  struct pch_msi_data {
>  	struct mutex	msi_map_lock;
>  	phys_addr_t	doorbell;
> @@ -100,6 +99,17 @@ static struct irq_chip middle_irq_chip = {
>  	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
>  };
>  
> +static struct irq_chip pch_msi_irq_chip_v2 = {
> +	.name			= "MSI",
> +	.irq_ack		= irq_chip_ack_parent,
> +};
> +
> +static struct msi_domain_info pch_msi_domain_info_v2 = {
> +	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +			MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
> +	.chip	= &pch_msi_irq_chip_v2,
> +};
> +
>  static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
>  					unsigned int virq, int hwirq)
>  {
> @@ -268,6 +278,9 @@ struct fwnode_handle *get_pch_msi_handle(int pci_segment)
>  {
>  	int i;
>  
> +	if (cpu_has_avecint)
> +		return pch_msi_handle[0];
> +
>  	for (i = 0; i < MAX_IO_PICS; i++) {
>  		if (msi_group[i].pci_segment == pci_segment)
>  			return pch_msi_handle[i];
> @@ -289,4 +302,34 @@ int __init pch_msi_acpi_init(struct irq_domain *parent,
>  
>  	return ret;
>  }
> +
> +int __init pch_msi_acpi_init_v2(struct irq_domain *parent,
> +		struct acpi_madt_msi_pic *msi_entry)
> +{
> +	struct irq_domain *msi_domain;
> +
> +	if (pch_msi_handle[0])
> +		return 0;
> +
> +	pch_msi_handle[0] = irq_domain_alloc_named_fwnode("msipic-v2");
> +	if (!pch_msi_handle[0]) {
> +		pr_err("Unable to allocate domain handle\n");
> +		kfree(pch_msi_handle[0]);
> +		return -ENOMEM;
> +	}
> +
> +	msi_domain = pci_msi_create_irq_domain(pch_msi_handle[0],
> +			&pch_msi_domain_info_v2,
> +			parent);
> +	if (!msi_domain) {
> +		pr_err("Failed to create PCI MSI domain\n");
> +		kfree(pch_msi_handle[0]);
> +		return -ENOMEM;
> +	}
> +
> +	pr_info("IRQ domain MSIPIC-V2 init done.\n");
> +	return 0;
> +}
> +
> +

Stray newlines. But as with the other CONFIG_ACPI part above a build
with CONFIG_ACPI=n will result in defined but not used warnings ....

Thanks,

        tglx

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-07 12:59 [PATCH 2/2] Loongarch:Support loongarch avec Tianyang Zhang
                   ` (3 preceding siblings ...)
  2024-05-08  9:43 ` Thomas Gleixner
@ 2024-05-08 21:18 ` kernel test robot
  2024-05-08 21:28 ` kernel test robot
  5 siblings, 0 replies; 10+ messages in thread
From: kernel test robot @ 2024-05-08 21:18 UTC (permalink / raw)
  To: Tianyang Zhang, chenhuacai, kernel, tglx, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, yijun, mhocko, akpm, dianders, maobibo,
	xry111, zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: oe-kbuild-all, loongarch, linux-doc, linux-kernel

Hi Tianyang,

kernel test robot noticed the following build warnings:

[auto build test WARNING on tip/irq/core]
[also build test WARNING on linus/master v6.9-rc7]
[cannot apply to next-20240508]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Tianyang-Zhang/Loongarch-Support-loongarch-avec/20240507-210314
base:   tip/irq/core
patch link:    https://lore.kernel.org/r/20240507125953.9117-1-zhangtianyang%40loongson.cn
patch subject: [PATCH 2/2] Loongarch:Support loongarch avec
config: loongarch-randconfig-r133-20240508 (https://download.01.org/0day-ci/archive/20240509/202405090438.c1xvjipc-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 13.2.0
reproduce: (https://download.01.org/0day-ci/archive/20240509/202405090438.c1xvjipc-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202405090438.c1xvjipc-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/irqchip/irq-loongarch-avec.c:41:3: sparse: sparse: symbol 'loongarch_avec' was not declared. Should it be static?
   drivers/irqchip/irq-loongarch-avec.c:181:31: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:181:31: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c:184:31: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:184:31: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c:187:31: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:187:31: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c:190:31: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:190:31: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c:215:18: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:215:18: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c: note: in included file (through include/linux/smp.h, include/linux/percpu.h, include/linux/context_tracking_state.h, ...):
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
   drivers/irqchip/irq-loongarch-avec.c:164:6: sparse: sparse: context imbalance in 'complete_irq_moving' - different lock contexts for basic block
   drivers/irqchip/irq-loongarch-avec.c: note: in included file (through arch/loongarch/include/asm/loongarch.h, arch/loongarch/include/asm/cpu-info.h, ...):
   ../lib/gcc/loongarch64-linux/13.2.0/include/larchintrin.h:294:30: sparse: sparse: undefined identifier '__builtin_loongarch_iocsrrd_d'
   ../lib/gcc/loongarch64-linux/13.2.0/include/larchintrin.h:294:11: sparse: sparse: cast from unknown type
   ../lib/gcc/loongarch64-linux/13.2.0/include/larchintrin.h:332:3: sparse: sparse: undefined identifier '__builtin_loongarch_iocsrwr_d'

vim +/loongarch_avec +41 drivers/irqchip/irq-loongarch-avec.c

    35	
    36	struct loongarch_avec_chip {
    37		struct fwnode_handle	*fwnode;
    38		struct irq_domain	*domain;
    39		struct irq_matrix	*vector_matrix;
    40		raw_spinlock_t		lock;
  > 41	} loongarch_avec;
    42	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-07 12:59 [PATCH 2/2] Loongarch:Support loongarch avec Tianyang Zhang
                   ` (4 preceding siblings ...)
  2024-05-08 21:18 ` kernel test robot
@ 2024-05-08 21:28 ` kernel test robot
  5 siblings, 0 replies; 10+ messages in thread
From: kernel test robot @ 2024-05-08 21:28 UTC (permalink / raw)
  To: Tianyang Zhang, chenhuacai, kernel, tglx, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, yijun, mhocko, akpm, dianders, maobibo,
	xry111, zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: oe-kbuild-all, loongarch, linux-doc, linux-kernel

Hi Tianyang,

kernel test robot noticed the following build warnings:

[auto build test WARNING on tip/irq/core]
[also build test WARNING on linus/master v6.9-rc7]
[cannot apply to next-20240508]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Tianyang-Zhang/Loongarch-Support-loongarch-avec/20240507-210314
base:   tip/irq/core
patch link:    https://lore.kernel.org/r/20240507125953.9117-1-zhangtianyang%40loongson.cn
patch subject: [PATCH 2/2] Loongarch:Support loongarch avec
config: loongarch-randconfig-r113-20240508 (https://download.01.org/0day-ci/archive/20240509/202405090419.xGNdK28X-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 13.2.0
reproduce: (https://download.01.org/0day-ci/archive/20240509/202405090419.xGNdK28X-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202405090419.xGNdK28X-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/irqchip/irq-loongarch-avec.c:34:1: sparse: sparse: symbol '__pcpu_scope_pending_list' was not declared. Should it be static?
   drivers/irqchip/irq-loongarch-avec.c:41:3: sparse: sparse: symbol 'loongarch_avec' was not declared. Should it be static?
   drivers/irqchip/irq-loongarch-avec.c:181:31: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:181:31: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c:184:31: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:184:31: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c:187:31: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:187:31: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c:190:31: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:190:31: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c:215:18: sparse: sparse: undefined identifier '__builtin_loongarch_csrrd_d'
   drivers/irqchip/irq-loongarch-avec.c:215:18: sparse: sparse: cast from unknown type
   drivers/irqchip/irq-loongarch-avec.c: note: in included file (through include/linux/mmzone.h, include/linux/topology.h, include/linux/irq.h, ...):
   include/linux/page-flags.h:242:46: sparse: sparse: self-comparison always evaluates to false
   include/linux/page-flags.h:242:46: sparse: sparse: self-comparison always evaluates to false
   drivers/irqchip/irq-loongarch-avec.c:164:6: sparse: sparse: context imbalance in 'complete_irq_moving' - different lock contexts for basic block
   drivers/irqchip/irq-loongarch-avec.c: note: in included file (through arch/loongarch/include/asm/loongarch.h, arch/loongarch/include/asm/cpu-info.h, ...):
   ../lib/gcc/loongarch64-linux/13.2.0/include/larchintrin.h:294:30: sparse: sparse: undefined identifier '__builtin_loongarch_iocsrrd_d'
   ../lib/gcc/loongarch64-linux/13.2.0/include/larchintrin.h:294:11: sparse: sparse: cast from unknown type
   ../lib/gcc/loongarch64-linux/13.2.0/include/larchintrin.h:332:3: sparse: sparse: undefined identifier '__builtin_loongarch_iocsrwr_d'

vim +/__pcpu_scope_pending_list +34 drivers/irqchip/irq-loongarch-avec.c

    33	
  > 34	DEFINE_PER_CPU(struct pending_list, pending_list);
    35	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-07 14:20 ` Xi Ruoyao
@ 2024-05-13  6:41   ` Tianyang Zhang
  0 siblings, 0 replies; 10+ messages in thread
From: Tianyang Zhang @ 2024-05-13  6:41 UTC (permalink / raw)
  To: Xi Ruoyao, chenhuacai, kernel, tglx, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, yijun, mhocko, akpm, dianders, maobibo,
	zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: loongarch, linux-doc, linux-kernel


在 2024/5/7 下午10:20, Xi Ruoyao 写道:
> On Tue, 2024-05-07 at 20:59 +0800, Tianyang Zhang wrote:
>> +static inline void loongarch_avec_ack_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static inline void loongarch_avec_unmask_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static inline void loongarch_avec_mask_irq(struct irq_data *d)
>> +{
>> +}
> "inline" has no use here because these functions are only called via
> function pointers, thus such calls cannot be inline-able.  I'd suggest
> to remove "inline" for them.
>
Thank you for your feedback. I will make the necessary changes here


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-08  7:00 ` maobibo
@ 2024-05-13  6:53   ` Tianyang Zhang
  0 siblings, 0 replies; 10+ messages in thread
From: Tianyang Zhang @ 2024-05-13  6:53 UTC (permalink / raw)
  To: maobibo, chenhuacai, kernel, tglx, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, yijun, mhocko, akpm, dianders, xry111,
	zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: loongarch, linux-doc, linux-kernel


在 2024/5/8 下午3:00, maobibo 写道:
>
>
> On 2024/5/7 下午8:59, Tianyang Zhang wrote:
>> From: zhangtianyang <zhangtianyang@loongson.cn>
>>
>> Introduce the advanced extended interrupt controllers,
>> This feature will allow each core to have an independent
>> 256 interrupt vectors, and MSI interrupts can be
>> independently routed to any vector on any CPU.
>
> Hi tianyang,
>
> Thanks for provide the patch.
>
> One simple question, can old kernel without this driver such as kernel 
> 6.8 run in the new hardware with advanced extended interrupt controllers?

Hi Bibo,


The new hardware is fully compatible with the old interrupt model, and 
the old kernel will route interrupts in the original way

>
>>
>> Co-developed-by: Jianmin Lv <lvjianmin@loongson.cn>
>> Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
>> Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
>> Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
>> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
>> ---
>>   arch/loongarch/Kconfig                    |   1 +
>>   arch/loongarch/include/asm/cpu-features.h |   1 +
>>   arch/loongarch/include/asm/cpu.h          |   2 +
>>   arch/loongarch/include/asm/hw_irq.h       |  10 +
>>   arch/loongarch/include/asm/irq.h          |   7 +-
>>   arch/loongarch/include/asm/loongarch.h    |  19 +-
>>   arch/loongarch/include/asm/smp.h          |   1 +
>>   arch/loongarch/kernel/cpu-probe.c         |   3 +-
>>   arch/loongarch/kernel/smp.c               |   3 +
>>   drivers/irqchip/Makefile                  |   2 +-
>>   drivers/irqchip/irq-loongarch-avec.c      | 434 ++++++++++++++++++++++
>>   drivers/irqchip/irq-loongarch-cpu.c       |   4 +-
>>   drivers/irqchip/irq-loongson-eiointc.c    |   3 +
>>   drivers/irqchip/irq-loongson-pch-msi.c    |  45 ++-
>>   14 files changed, 525 insertions(+), 10 deletions(-)
>>   create mode 100644 drivers/irqchip/irq-loongarch-avec.c
>>
>> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
>> index 10959e6c3583..71b8f19745e0 100644
>> --- a/arch/loongarch/Kconfig
>> +++ b/arch/loongarch/Kconfig
>> @@ -76,6 +76,7 @@ config LOONGARCH
>>       select GENERIC_ENTRY
>>       select GENERIC_GETTIMEOFDAY
>>       select GENERIC_IOREMAP if !ARCH_IOREMAP
>> +    select GENERIC_IRQ_MATRIX_ALLOCATOR
>>       select GENERIC_IRQ_MULTI_HANDLER
>>       select GENERIC_IRQ_PROBE
>>       select GENERIC_IRQ_SHOW
>> diff --git a/arch/loongarch/include/asm/cpu-features.h 
>> b/arch/loongarch/include/asm/cpu-features.h
>> index 2eafe6a6aca8..16a716f88a5c 100644
>> --- a/arch/loongarch/include/asm/cpu-features.h
>> +++ b/arch/loongarch/include/asm/cpu-features.h
>> @@ -65,5 +65,6 @@
>>   #define cpu_has_guestid        cpu_opt(LOONGARCH_CPU_GUESTID)
>>   #define cpu_has_hypervisor cpu_opt(LOONGARCH_CPU_HYPERVISOR)
>>   #define cpu_has_ptw        cpu_opt(LOONGARCH_CPU_PTW)
>> +#define cpu_has_avecint        cpu_opt(LOONGARCH_CPU_AVECINT)
>>     #endif /* __ASM_CPU_FEATURES_H */
>> diff --git a/arch/loongarch/include/asm/cpu.h 
>> b/arch/loongarch/include/asm/cpu.h
>> index 48b9f7168bcc..843f9c4ec980 100644
>> --- a/arch/loongarch/include/asm/cpu.h
>> +++ b/arch/loongarch/include/asm/cpu.h
>> @@ -99,6 +99,7 @@ enum cpu_type_enum {
>>   #define CPU_FEATURE_GUESTID        24    /* CPU has GuestID feature */
>>   #define CPU_FEATURE_HYPERVISOR        25    /* CPU has hypervisor 
>> (running in VM) */
>>   #define CPU_FEATURE_PTW            26    /* CPU has hardware page 
>> table walker */
>> +#define CPU_FEATURE_AVECINT        27    /* CPU has avec interrupt */
>>     #define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
>>   #define LOONGARCH_CPU_LAM        BIT_ULL(CPU_FEATURE_LAM)
>> @@ -127,5 +128,6 @@ enum cpu_type_enum {
>>   #define LOONGARCH_CPU_GUESTID BIT_ULL(CPU_FEATURE_GUESTID)
>>   #define LOONGARCH_CPU_HYPERVISOR BIT_ULL(CPU_FEATURE_HYPERVISOR)
>>   #define LOONGARCH_CPU_PTW        BIT_ULL(CPU_FEATURE_PTW)
>> +#define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
>>     #endif /* _ASM_CPU_H */
>> diff --git a/arch/loongarch/include/asm/hw_irq.h 
>> b/arch/loongarch/include/asm/hw_irq.h
>> index af4f4e8fbd85..521abce32135 100644
>> --- a/arch/loongarch/include/asm/hw_irq.h
>> +++ b/arch/loongarch/include/asm/hw_irq.h
>> @@ -9,6 +9,16 @@
>>     extern atomic_t irq_err_count;
>>   +/*
>> + * 256 vectors Map:
>> + *
>> + * 0 - 15: mapping legacy IPs, e.g. IP0-12.
>> + * 16 - 255: mapping a vector for external IRQ.
>> + *
>> + */
>> +#define NR_VECTORS    256
>> +#define IRQ_MATRIX_BITS    NR_VECTORS
>> +#define NR_LEGACY_VECTORS    16
>>   /*
>>    * interrupt-retrigger: NOP for now. This may not be appropriate 
>> for all
>>    * machines, we'll see ...
>> diff --git a/arch/loongarch/include/asm/irq.h 
>> b/arch/loongarch/include/asm/irq.h
>> index 218b4da0ea90..e28bd96c6611 100644
>> --- a/arch/loongarch/include/asm/irq.h
>> +++ b/arch/loongarch/include/asm/irq.h
>> @@ -65,7 +65,7 @@ extern struct acpi_vector_group 
>> msi_group[MAX_IO_PICS];
>>   #define LOONGSON_LPC_LAST_IRQ        (LOONGSON_LPC_IRQ_BASE + 15)
>>     #define LOONGSON_CPU_IRQ_BASE        16
>> -#define LOONGSON_CPU_LAST_IRQ        (LOONGSON_CPU_IRQ_BASE + 14)
>> +#define LOONGSON_CPU_LAST_IRQ        (LOONGSON_CPU_IRQ_BASE + 15)
>>     #define LOONGSON_PCH_IRQ_BASE        64
>>   #define LOONGSON_PCH_ACPI_IRQ        (LOONGSON_PCH_IRQ_BASE + 47)
>> @@ -101,6 +101,11 @@ int pch_msi_acpi_init(struct irq_domain *parent,
>>                       struct acpi_madt_msi_pic *acpi_pchmsi);
>>   int pch_pic_acpi_init(struct irq_domain *parent,
>>                       struct acpi_madt_bio_pic *acpi_pchpic);
>> +int __init pch_msi_acpi_init_v2(struct irq_domain *parent,
>> +        struct acpi_madt_msi_pic *pch_msi_entry);
>> +int __init loongarch_avec_acpi_init(struct irq_domain *parent);
>> +void complete_irq_moving(int *restart);
>> +
>>   int find_pch_pic(u32 gsi);
>>   struct fwnode_handle *get_pch_msi_handle(int pci_segment);
>>   diff --git a/arch/loongarch/include/asm/loongarch.h 
>> b/arch/loongarch/include/asm/loongarch.h
>> index 46366e783c84..4d9a09861925 100644
>> --- a/arch/loongarch/include/asm/loongarch.h
>> +++ b/arch/loongarch/include/asm/loongarch.h
>> @@ -72,7 +72,6 @@
>>   #define  CPUCFG1_RPLV            BIT(23)
>>   #define  CPUCFG1_HUGEPG            BIT(24)
>>   #define  CPUCFG1_CRC32            BIT(25)
>> -#define  CPUCFG1_MSGINT            BIT(26)
>>     #define LOONGARCH_CPUCFG2        0x2
>>   #define  CPUCFG2_FP            BIT(0)
>> @@ -240,8 +239,8 @@
>>   #define  CSR_ESTAT_EXC_WIDTH        6
>>   #define  CSR_ESTAT_EXC            (_ULCAST_(0x3f) << 
>> CSR_ESTAT_EXC_SHIFT)
>>   #define  CSR_ESTAT_IS_SHIFT        0
>> -#define  CSR_ESTAT_IS_WIDTH        14
>> -#define  CSR_ESTAT_IS            (_ULCAST_(0x3fff) << 
>> CSR_ESTAT_IS_SHIFT)
>> +#define  CSR_ESTAT_IS_WIDTH        15
>> +#define  CSR_ESTAT_IS            (_ULCAST_(0x7fff) << 
>> CSR_ESTAT_IS_SHIFT)
>>     #define LOONGARCH_CSR_ERA        0x6    /* ERA */
>>   @@ -987,10 +986,17 @@
>>   #define CSR_FWPC_SKIP_SHIFT        16
>>   #define CSR_FWPC_SKIP            (_ULCAST_(1) << CSR_FWPC_SKIP_SHIFT)
>>   +#define LOONGARCH_CSR_IRR0        0xa0
>> +#define LOONGARCH_CSR_IRR1        0xa1
>> +#define LOONGARCH_CSR_IRR2        0xa2
>> +#define LOONGARCH_CSR_IRR3        0xa3
>> +
>> +#define    LOONGARCH_CSR_ILR        0xa4
>> +
>>   /*
>>    * CSR_ECFG IM
>>    */
>> -#define ECFG0_IM        0x00001fff
>> +#define ECFG0_IM        0x00005fff
>>   #define ECFGB_SIP0        0
>>   #define ECFGF_SIP0        (_ULCAST_(1) << ECFGB_SIP0)
>>   #define ECFGB_SIP1        1
>> @@ -1033,6 +1039,7 @@
>>   #define  IOCSRF_EIODECODE        BIT_ULL(9)
>>   #define  IOCSRF_FLATMODE        BIT_ULL(10)
>>   #define  IOCSRF_VM            BIT_ULL(11)
>> +#define  IOCSRF_AVEC            BIT_ULL(15)
>>     #define LOONGARCH_IOCSR_VENDOR        0x10
>>   @@ -1043,6 +1050,7 @@
>>   #define LOONGARCH_IOCSR_MISC_FUNC    0x420
>>   #define  IOCSR_MISC_FUNC_TIMER_RESET    BIT_ULL(21)
>>   #define  IOCSR_MISC_FUNC_EXT_IOI_EN    BIT_ULL(48)
>> +#define  IOCSR_MISC_FUNC_AVEC_EN    BIT_ULL(51)
>>     #define LOONGARCH_IOCSR_CPUTEMP        0x428
>>   @@ -1363,9 +1371,10 @@ __BUILD_CSR_OP(tlbidx)
>>   #define INT_TI        11    /* Timer */
>>   #define INT_IPI        12
>>   #define INT_NMI        13
>> +#define INT_AVEC    14
>>     /* ExcCodes corresponding to interrupts */
>> -#define EXCCODE_INT_NUM        (INT_NMI + 1)
>> +#define EXCCODE_INT_NUM        (INT_AVEC + 1)
>>   #define EXCCODE_INT_START    64
>>   #define EXCCODE_INT_END        (EXCCODE_INT_START + EXCCODE_INT_NUM 
>> - 1)
>>   diff --git a/arch/loongarch/include/asm/smp.h 
>> b/arch/loongarch/include/asm/smp.h
>> index f81e5f01d619..577f591c1c73 100644
>> --- a/arch/loongarch/include/asm/smp.h
>> +++ b/arch/loongarch/include/asm/smp.h
>> @@ -62,6 +62,7 @@ extern int __cpu_logical_map[NR_CPUS];
>>   #define SMP_BOOT_CPU        0x1
>>   #define SMP_RESCHEDULE        0x2
>>   #define SMP_CALL_FUNCTION    0x4
>> +#define SMP_CLEAR_VECT        0x8
>>     struct secondary_data {
>>       unsigned long stack;
>> diff --git a/arch/loongarch/kernel/cpu-probe.c 
>> b/arch/loongarch/kernel/cpu-probe.c
>> index 55320813ee08..3b2e72e8f9bd 100644
>> --- a/arch/loongarch/kernel/cpu-probe.c
>> +++ b/arch/loongarch/kernel/cpu-probe.c
>> @@ -106,7 +106,6 @@ static void cpu_probe_common(struct 
>> cpuinfo_loongarch *c)
>>           elf_hwcap |= HWCAP_LOONGARCH_CRC32;
>>       }
>>   -
>>       config = read_cpucfg(LOONGARCH_CPUCFG2);
>>       if (config & CPUCFG2_LAM) {
>>           c->options |= LOONGARCH_CPU_LAM;
>> @@ -176,6 +175,8 @@ static void cpu_probe_common(struct 
>> cpuinfo_loongarch *c)
>>           c->options |= LOONGARCH_CPU_EIODECODE;
>>       if (config & IOCSRF_VM)
>>           c->options |= LOONGARCH_CPU_HYPERVISOR;
>> +    if (config & IOCSRF_AVEC)
>> +        c->options |= LOONGARCH_CPU_AVECINT;
>>         config = csr_read32(LOONGARCH_CSR_ASID);
>>       config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT;
>> diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c
>> index a16e3dbe9f09..4fcf399a8b17 100644
>> --- a/arch/loongarch/kernel/smp.c
>> +++ b/arch/loongarch/kernel/smp.c
>> @@ -176,6 +176,9 @@ irqreturn_t loongson_ipi_interrupt(int irq, void 
>> *dev)
>>           per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++;
>>       }
>>   +    if (action & SMP_CLEAR_VECT)
>> +        complete_irq_moving(NULL);
>> +
>>       return IRQ_HANDLED;
>>   }
>>   diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>> index ec4a18380998..398db4c3e264 100644
>> --- a/drivers/irqchip/Makefile
>> +++ b/drivers/irqchip/Makefile
>> @@ -105,7 +105,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
>> +obj-$(CONFIG_IRQ_LOONGARCH_CPU)        += irq-loongarch-cpu.o 
>> irq-loongarch-avec.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
>> new file mode 100644
>> index 000000000000..71ce33db63db
>> --- /dev/null
>> +++ b/drivers/irqchip/irq-loongarch-avec.c
>> @@ -0,0 +1,434 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020 Loongson Technologies, Inc.
>> + */
>> +
>> +#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 <linux/irqchip/chained_irq.h>
>> +#include <linux/cpuhotplug.h>
>> +#include <linux/radix-tree.h>
>> +
>> +#include <asm/loongarch.h>
>> +#include <asm/setup.h>
>> +
>> +static phys_addr_t msi_base_v2;
>> +
>> +typedef struct irq_data *irq_map_t[NR_VECTORS];
>> +DECLARE_PER_CPU(irq_map_t, irq_map);
>> +DEFINE_PER_CPU(irq_map_t, irq_map) = {
>> +    [0 ... NR_VECTORS - 1] = NULL,
>> +};
>> +
>> +struct pending_list {
>> +    struct list_head head;
>> +    raw_spinlock_t    lock;
>> +};
>> +
>> +DEFINE_PER_CPU(struct pending_list, pending_list);
>> +
>> +struct loongarch_avec_chip {
>> +    struct fwnode_handle    *fwnode;
>> +    struct irq_domain    *domain;
>> +    struct irq_matrix    *vector_matrix;
>> +    raw_spinlock_t        lock;
>> +} loongarch_avec;
>> +
>> +struct loongarch_avec_data {
>> +    struct list_head entry;
>> +    unsigned int cpu;
>> +    unsigned int vec;
>> +    unsigned int prev_cpu;
>> +    unsigned int prev_vec;
>> +};
>> +
>> +static int assign_irq_vector(struct irq_data *irqd, const struct 
>> cpumask *dest,
>> +        unsigned int *cpu, int *vector)
>> +{
>> +    int ret;
>> +
>> +    ret = irq_matrix_alloc(loongarch_avec.vector_matrix, dest, 
>> false, cpu);
>> +    if (ret < 0)
>> +        return ret;
>> +    *vector = ret;
>> +
>> +    return 0;
>> +}
>> +
>> +static inline void loongarch_avec_ack_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static inline void loongarch_avec_unmask_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static inline void loongarch_avec_mask_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static void loongarch_avec_sync(struct loongarch_avec_data *adata)
>> +{
>> +    struct loongarch_avec_data *data;
>> +    struct pending_list *plist;
>> +
>> +    if (cpu_online(adata->prev_cpu)) {
>> +        plist = per_cpu_ptr(&pending_list, adata->prev_cpu);
>> +
>> +        data = kmalloc(sizeof(struct loongarch_avec_data), GFP_KERNEL);
>> +        if (!data) {
>> +            pr_warn("NO space for clean data\n");
>> +            return;
>> +        }
>> +        memcpy(data, adata, sizeof(struct loongarch_avec_data));
>> +        INIT_LIST_HEAD(&data->entry);
>> +
>> +        list_add_tail(&data->entry, &plist->head);
>> +        loongson_send_ipi_single(adata->prev_cpu, SMP_CLEAR_VECT);
>> +    }
>> +    adata->prev_cpu = adata->cpu;
>> +    adata->prev_vec = adata->vec;
>> +}
>> +
>> +static int loongarch_avec_set_affinity(struct irq_data *data,
>> +        const struct cpumask *dest, bool force)
>> +{
>> +    struct cpumask intersect_mask;
>> +    struct loongarch_avec_data *adata;
>> +    unsigned int cpu, vector;
>> +    unsigned long flags;
>> +    int ret = 0;
>> +
>> +    raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +    adata = irq_data_get_irq_chip_data(data);
>> +
>> +    if (adata->vec && cpu_online(adata->cpu)
>> +            && cpumask_test_cpu(adata->cpu, dest)) {
>> +        raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +        return 0;
>> +    }
>> +
>> +    if (!cpumask_intersects(dest, cpu_online_mask)) {
>> +        raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +        return -EINVAL;
>> +    }
>> +
>> +    cpumask_and(&intersect_mask, dest, cpu_online_mask);
>> +
>> +    ret = assign_irq_vector(data, &intersect_mask, &cpu, &vector);
>> +    if (ret) {
>> +        raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +        return ret;
>> +    }
>> +
>> +    adata->cpu = cpu;
>> +    adata->vec = vector;
>> +    per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = data;
>> +    loongarch_avec_sync(adata);
>> +
>> +    raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +    irq_data_update_effective_affinity(data, cpumask_of(cpu));
>> +
>> +    return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static void loongarch_avec_compose_msg(struct irq_data *d,
>> +        struct msi_msg *msg)
>> +{
>> +    struct loongarch_avec_data *avec_data;
>> +
>> +    avec_data = irq_data_get_irq_chip_data(d);
>> +
>> +    msg->address_hi = 0x0;
>> +    msg->address_lo = msi_base_v2 | ((avec_data->vec & 0xff) << 4) |
>> +        ((cpu_logical_map(avec_data->cpu & 0xffff)) << 12);
> There is requirement for variable msi_base_v2 aligned with 1<< 28.
>
> Is that right? However there is no such checking in function 
> pch_msi_parse_madt().
>
> Also it will be better if there is some notations.
>
>> +    msg->data = 0x0;
>> +
>> +}
>> +
>> +static struct irq_chip loongarch_avec_controller = {
>> +    .name            = "CORE_AVEC",
>> +    .irq_ack        = loongarch_avec_ack_irq,
>> +    .irq_mask        = loongarch_avec_mask_irq,
>> +    .irq_unmask        = loongarch_avec_unmask_irq,
>> +    .irq_set_affinity    = loongarch_avec_set_affinity,
>> +    .irq_compose_msi_msg    = loongarch_avec_compose_msg,
>> +};
>> +
>> +void complete_irq_moving(int *restart)
>> +{
>> +    struct pending_list *plist = this_cpu_ptr(&pending_list);
>> +    struct loongarch_avec_data *adata, *tmp;
>> +    int cpu, vector;
>> +    u32 bias;
>> +    u64 irr;
>> +
>> +    raw_spin_lock(&loongarch_avec.lock);
>> +
>> +    list_for_each_entry_safe(adata, tmp, &plist->head, entry) {
>> +
>> +        cpu = adata->prev_cpu;
>> +        vector = adata->prev_vec;
>> +        bias = vector/64;
>> +        switch (bias) {
>> +        case 0x0:
>> +            irr = csr_read64(LOONGARCH_CSR_IRR0);
>> +            break;
>> +        case 0x1:
>> +            irr = csr_read64(LOONGARCH_CSR_IRR1);
>> +            break;
>> +        case 0x2:
>> +            irr = csr_read64(LOONGARCH_CSR_IRR2);
>> +            break;
>> +        case 0x3:
>> +            irr = csr_read64(LOONGARCH_CSR_IRR3);
>> +            break;
>> +        default:
>> +            return;
>> +        }
>> +
>> +        if (irr & (1UL << (vector % 64))) {
>> +            loongson_send_ipi_single(cpu, SMP_CLEAR_VECT);
>
> It is strange that sending ipi to itself to clear MSI interrupt.
> It requires that there cannot be loop check in ipi interrupt handler, 
> else ipi hanlder loop checking will find new IPI message again.
>
> Is it possible to do_irq() for MSI interrupt directly in ipi handler?
>     d = raw_cpu_ptr(irq_map)[vector];
>     if (d)
>         generic_handle_irq(d->irq);
>
>> +            continue;
>> +        }
>> +        list_del(&adata->entry);
>> +        irq_matrix_free(loongarch_avec.vector_matrix, cpu, vector, 
>> false);
>> +        this_cpu_ptr(irq_map)[vector] = 0;
>> +        kfree(adata);
>> +    }
>> +    raw_spin_unlock(&loongarch_avec.lock);
>> +}
>> +
>> +static void loongarch_avec_dispatch(struct irq_desc *desc)
>> +{
>> +    struct irq_chip *chip = irq_desc_get_chip(desc);
>> +    struct irq_data *d;
>> +    unsigned long vector;
>> +
>> +    chained_irq_enter(chip, desc);
>> +    vector = csr_read64(LOONGARCH_CSR_ILR);
>> +    if (vector & 0x80000000)
>> +        return;
> what is the meaning of 0x80000000? It had better be defined as macro.
>
> Regards
> Bibo Mao
>> +
>> +    vector &= 0xff;
>> +
>> +    d = raw_cpu_ptr(irq_map)[vector];
>> +    if (d)
>> +        generic_handle_irq(d->irq);
>> +    else
>> +        pr_warn("IRQ ERROR:Unexpected irq  occur on cpu %d[vector 
>> %d]\n",
>> +                smp_processor_id(), vector);
>> +
>> +    chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int loongarch_avec_alloc(struct irq_domain *domain, unsigned 
>> int virq,
>> +        unsigned int nr_irqs, void *arg)
>> +{
>> +    struct loongarch_avec_data *adata;
>> +    struct irq_data *irqd;
>> +    unsigned int cpu, vector;
>> +    unsigned long flags;
>> +    int i, err;
>> +
>> +    raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +    for (i = 0; i < nr_irqs; i++) {
>> +        irqd = irq_domain_get_irq_data(domain, virq + i);
>> +        adata = kzalloc(sizeof(*adata), GFP_KERNEL);
>> +        if (!adata) {
>> + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +            return -ENOMEM;
>> +        }
>> +        err = assign_irq_vector(irqd, cpu_online_mask, &cpu, &vector);
>> +        if (err) {
>> + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +            return err;
>> +        }
>> +        adata->prev_cpu = adata->cpu = cpu;
>> +        adata->prev_vec = adata->vec = vector;
>> +
>> +        per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irqd;
>> +        irq_domain_set_info(domain, virq + i, virq, 
>> &loongarch_avec_controller,
>> +                adata, handle_edge_irq, NULL, NULL);
>> +        irqd_set_single_target(irqd);
>> +        irqd_set_affinity_on_activate(irqd);
>> +    }
>> +    raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +
>> +    return err;
>> +}
>> +
>> +static void loongarch_avec_free(struct irq_domain *domain, unsigned 
>> int virq,
>> +        unsigned int nr_irqs)
>> +{
>> +    struct loongarch_avec_data *adata;
>> +    struct irq_data *d;
>> +    unsigned long flags;
>> +    unsigned int i;
>> +
>> +    raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +    for (i = 0; i < nr_irqs; i++) {
>> +        d = irq_domain_get_irq_data(domain, virq + i);
>> +        adata = irq_data_get_irq_chip_data(d);
>> +        if (d) {
>> +            irq_matrix_free(loongarch_avec.vector_matrix,
>> +                    adata->cpu,
>> +                    adata->vec, false);
>> +            irq_domain_reset_irq_data(d);
>> +        }
>> +    }
>> +
>> +    raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +}
>> +
>> +static const struct irq_domain_ops loongarch_avec_domain_ops = {
>> +    .alloc        = loongarch_avec_alloc,
>> +    .free        = loongarch_avec_free,
>> +};
>> +
>> +static int __init irq_matrix_init(void)
>> +{
>> +    int i;
>> +
>> +    loongarch_avec.vector_matrix = irq_alloc_matrix(NR_VECTORS, 0, 
>> NR_VECTORS - 1);
>> +    if (!loongarch_avec.vector_matrix)
>> +        return -ENOMEM;
>> +    for (i = 0; i < NR_LEGACY_VECTORS; i++)
>> +        irq_matrix_assign_system(loongarch_avec.vector_matrix, i, 
>> false);
>> +
>> +    irq_matrix_online(loongarch_avec.vector_matrix);
>> +
>> +    return 0;
>> +}
>> +
>> +static int __init loongarch_avec_init(struct irq_domain *parent)
>> +{
>> +    int ret = 0, parent_irq;
>> +    unsigned long tmp;
>> +
>> +    tmp = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
>> +    tmp |= IOCSR_MISC_FUNC_AVEC_EN;
>> +    iocsr_write64(tmp, LOONGARCH_IOCSR_MISC_FUNC);
>> +
>> +    raw_spin_lock_init(&loongarch_avec.lock);
>> +
>> +    loongarch_avec.fwnode = irq_domain_alloc_named_fwnode("CORE_AVEC");
>> +    if (!loongarch_avec.fwnode) {
>> +        pr_err("Unable to allocate domain handle\n");
>> +        ret = -ENOMEM;
>> +        goto out;
>> +    }
>> +
>> +    loongarch_avec.domain = 
>> irq_domain_create_tree(loongarch_avec.fwnode,
>> +            &loongarch_avec_domain_ops, NULL);
>> +    if (!loongarch_avec.domain) {
>> +        pr_err("core-vec: cannot create IRQ domain\n");
>> +        ret = -ENOMEM;
>> +        goto out_free_handle;
>> +    }
>> +
>> +    parent_irq = irq_create_mapping(parent, INT_AVEC);
>> +    if (!parent_irq) {
>> +        pr_err("Failed to mapping hwirq\n");
>> +        ret = -EINVAL;
>> +        goto out_remove_domain;
>> +    }
>> +    irq_set_chained_handler_and_data(parent_irq, 
>> loongarch_avec_dispatch, NULL);
>> +
>> +    ret = irq_matrix_init();
>> +    if (ret) {
>> +        pr_err("Failed to init irq matrix\n");
>> +        goto out_free_matrix;
>> +    }
>> +
>> +    return ret;
>> +
>> +out_free_matrix:
>> +    kfree(loongarch_avec.vector_matrix);
>> +out_remove_domain:
>> +    irq_domain_remove(loongarch_avec.domain);
>> +out_free_handle:
>> +    irq_domain_free_fwnode(loongarch_avec.fwnode);
>> +out:
>> +    return ret;
>> +}
>> +
>> +static int loongarch_avec_offline_cpu(unsigned int cpu)
>> +{
>> +    unsigned long flags;
>> +    struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
>> +
>> +    raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +    if (list_empty(&plist->head)) {
>> +        irq_matrix_offline(loongarch_avec.vector_matrix);
>> +    } else {
>> +        pr_warn("cpu %d advanced extioi is busy\n");
>> +        raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +        return -EBUSY;
>> +    }
>> +    raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +    return 0;
>> +}
>> +
>> +static int loongarch_avec_online_cpu(unsigned int cpu)
>> +{
>> +    struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
>> +    unsigned long flags;
>> +
>> +    raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +
>> +    irq_matrix_online(loongarch_avec.vector_matrix);
>> +
>> +    INIT_LIST_HEAD(&plist->head);
>> +
>> +    raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +    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_v2 = pchmsi_entry->msg_address;
>> +    return pch_msi_acpi_init_v2(loongarch_avec.domain, pchmsi_entry);
>> +}
>> +
>> +static inline int __init acpi_cascade_irqdomain_init(void)
>> +{
>> +    return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, 
>> pch_msi_parse_madt, 1);
>> +}
>> +
>> +int __init loongarch_avec_acpi_init(struct irq_domain *parent)
>> +{
>> +    int ret = 0;
>> +
>> +    ret = loongarch_avec_init(parent);
>> +    if (ret) {
>> +        pr_err("Failed to init irq domain\n");
>> +        return ret;
>> +    }
>> +
>> +    ret = acpi_cascade_irqdomain_init();
>> +    if (ret) {
>> +        pr_err("Failed to cascade IRQ domain\n");
>> +        return ret;
>> +    }
>> +
>> +    ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
>> +            "loongarch_avec:online",
>> +            loongarch_avec_online_cpu, loongarch_avec_offline_cpu);
>> +    if (ret < 0) {
>> +        pr_err("loongarch_avec: failed to register hotplug 
>> callbacks.\n");
>> +        return ret;
>> +    }
>> +
>> +    return ret;
>> +}
>> +#endif
>> diff --git a/drivers/irqchip/irq-loongarch-cpu.c 
>> b/drivers/irqchip/irq-loongarch-cpu.c
>> index 9d8f2c406043..1ecac59925c6 100644
>> --- a/drivers/irqchip/irq-loongarch-cpu.c
>> +++ b/drivers/irqchip/irq-loongarch-cpu.c
>> @@ -138,7 +138,9 @@ static int __init acpi_cascade_irqdomain_init(void)
>>       if (r < 0)
>>           return r;
>>   -    return 0;
>> +    if (cpu_has_avecint)
>> +        r = loongarch_avec_acpi_init(irq_domain);
>> +    return r;
>>   }
>>     static int __init cpuintc_acpi_init(union acpi_subtable_headers 
>> *header,
>> diff --git a/drivers/irqchip/irq-loongson-eiointc.c 
>> b/drivers/irqchip/irq-loongson-eiointc.c
>> index 405f622a26ad..39795241304e 100644
>> --- a/drivers/irqchip/irq-loongson-eiointc.c
>> +++ b/drivers/irqchip/irq-loongson-eiointc.c
>> @@ -359,6 +359,9 @@ static int __init acpi_cascade_irqdomain_init(void)
>>       if (r < 0)
>>           return r;
>>   +    if (cpu_has_avecint)
>> +        return 0;
>> +
>>       r = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, 
>> pch_msi_parse_madt, 1);
>>       if (r < 0)
>>           return r;
>> diff --git a/drivers/irqchip/irq-loongson-pch-msi.c 
>> b/drivers/irqchip/irq-loongson-pch-msi.c
>> index 6e1e1f011bb2..d1706080b4f4 100644
>> --- a/drivers/irqchip/irq-loongson-pch-msi.c
>> +++ b/drivers/irqchip/irq-loongson-pch-msi.c
>> @@ -16,7 +16,6 @@
>>   #include <linux/slab.h>
>>     static int nr_pics;
>> -
>>   struct pch_msi_data {
>>       struct mutex    msi_map_lock;
>>       phys_addr_t    doorbell;
>> @@ -100,6 +99,17 @@ static struct irq_chip middle_irq_chip = {
>>       .irq_compose_msi_msg    = pch_msi_compose_msi_msg,
>>   };
>>   +static struct irq_chip pch_msi_irq_chip_v2 = {
>> +    .name            = "MSI",
>> +    .irq_ack        = irq_chip_ack_parent,
>> +};
>> +
>> +static struct msi_domain_info pch_msi_domain_info_v2 = {
>> +    .flags        = MSI_FLAG_USE_DEF_DOM_OPS | 
>> MSI_FLAG_USE_DEF_CHIP_OPS |
>> +            MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
>> +    .chip    = &pch_msi_irq_chip_v2,
>> +};
>> +
>>   static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
>>                       unsigned int virq, int hwirq)
>>   {
>> @@ -268,6 +278,9 @@ struct fwnode_handle *get_pch_msi_handle(int 
>> pci_segment)
>>   {
>>       int i;
>>   +    if (cpu_has_avecint)
>> +        return pch_msi_handle[0];
>> +
>>       for (i = 0; i < MAX_IO_PICS; i++) {
>>           if (msi_group[i].pci_segment == pci_segment)
>>               return pch_msi_handle[i];
>> @@ -289,4 +302,34 @@ int __init pch_msi_acpi_init(struct irq_domain 
>> *parent,
>>         return ret;
>>   }
>> +
>> +int __init pch_msi_acpi_init_v2(struct irq_domain *parent,
>> +        struct acpi_madt_msi_pic *msi_entry)
>> +{
>> +    struct irq_domain *msi_domain;
>> +
>> +    if (pch_msi_handle[0])
>> +        return 0;
>> +
>> +    pch_msi_handle[0] = irq_domain_alloc_named_fwnode("msipic-v2");
>> +    if (!pch_msi_handle[0]) {
>> +        pr_err("Unable to allocate domain handle\n");
>> +        kfree(pch_msi_handle[0]);
>> +        return -ENOMEM;
>> +    }
>> +
>> +    msi_domain = pci_msi_create_irq_domain(pch_msi_handle[0],
>> +            &pch_msi_domain_info_v2,
>> +            parent);
>> +    if (!msi_domain) {
>> +        pr_err("Failed to create PCI MSI domain\n");
>> +        kfree(pch_msi_handle[0]);
>> +        return -ENOMEM;
>> +    }
>> +
>> +    pr_info("IRQ domain MSIPIC-V2 init done.\n");
>> +    return 0;
>> +}
>> +
>> +
>>   #endif
>>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] Loongarch:Support loongarch avec
  2024-05-08  9:43 ` Thomas Gleixner
@ 2024-05-14  2:39   ` Tianyang Zhang
  0 siblings, 0 replies; 10+ messages in thread
From: Tianyang Zhang @ 2024-05-14  2:39 UTC (permalink / raw)
  To: Thomas Gleixner, chenhuacai, kernel, jiaxun.yang, gaoliang,
	wangliupu, lvjianmin, yijun, mhocko, akpm, dianders, maobibo,
	xry111, zhaotianrui, nathan, yangtiezhu, zhoubinbin
  Cc: loongarch, linux-doc, linux-kernel

Hi ,Thomas

Thank you for your patient reply,I will make unified corrections to some 
format and

specification issues in subsequent patches.

在 2024/5/8 下午5:43, Thomas Gleixner 写道:
> On Tue, May 07 2024 at 20:59, Tianyang Zhang wrote:
>> From: zhangtianyang <zhangtianyang@loongson.cn>
> Please use your real name for the From line.
>
>> +
>> +#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 <linux/irqchip/chained_irq.h>
>> +#include <linux/cpuhotplug.h>
>> +#include <linux/radix-tree.h>
> Please put the includes in alphabethical order.
>
>> +#include <asm/loongarch.h>
>> +#include <asm/setup.h>
>> +
>> +static phys_addr_t msi_base_v2;
>> +
>> +typedef struct irq_data *irq_map_t[NR_VECTORS];
> No new pointless typedefs please.
>
> struct irq_map {
> 	struct irq_data irqd[NR_VECTORS];
> };
>
>> +DECLARE_PER_CPU(irq_map_t, irq_map);
> What's this declaration for?
>
>> +DEFINE_PER_CPU(irq_map_t, irq_map) = {
> Why is this global and not static?
>
>> +	[0 ... NR_VECTORS - 1] = NULL,
> No need to initialize to NULL. It's zeroed by default.
>
>> +};
>> +
>> +struct pending_list {
>> +	struct list_head head;
>> +	raw_spinlock_t	lock;
>> +};
> https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#struct-declarations-and-initializers
>
>> +DEFINE_PER_CPU(struct pending_list, pending_list);
> Why is this global?
>
>> +struct loongarch_avec_chip {
>> +	struct fwnode_handle	*fwnode;
>> +	struct irq_domain	*domain;
>> +	struct irq_matrix	*vector_matrix;
>> +	raw_spinlock_t		lock;
>> +} loongarch_avec;
>> +
>> +struct loongarch_avec_data {
>> +	struct list_head entry;
>> +	unsigned int cpu;
>> +	unsigned int vec;
>> +	unsigned int prev_cpu;
>> +	unsigned int prev_vec;
>> +};
> See link above.
>
>> +static int assign_irq_vector(struct irq_data *irqd, const struct cpumask *dest,
>> +		unsigned int *cpu, int *vector)
> Please read the line break section in the tip docomentation
>
>> +{
>> +	int ret;
>> +
>> +	ret = irq_matrix_alloc(loongarch_avec.vector_matrix, dest, false, cpu);
>> +	if (ret < 0)
>> +		return ret;
>> +	*vector = ret;
>> +
>> +	return 0;
> Why not simply returning the result of irq_matrix_alloc() and checking
> it for negative value at the call site. If not negative then use it as
> vector. That spares the whole indirection and makes the code readable.
ok, I will simplify this part of the code
>
>> +static void loongarch_avec_sync(struct loongarch_avec_data *adata)
>> +{
>> +	struct loongarch_avec_data *data;
>> +	struct pending_list *plist;
>> +
>> +	if (cpu_online(adata->prev_cpu)) {
>> +		plist = per_cpu_ptr(&pending_list, adata->prev_cpu);
>> +
>> +		data = kmalloc(sizeof(struct loongarch_avec_data), GFP_KERNEL);
> This is called from loongarch_avec_set_affinity() with raw spinlocks
> held and interrupts disabled. So GFP_KERNEL cannot work. You clearly did
> not test that code with proper debug options enabled.
>
> But even GFP_ATOMIC won't work when you want to support PREEMPT_RT as
> that does not allow allocations in such contexts.
>
> Look how x86 solves exactly this problem without allocations required.

I did not consider it thoroughly here. I have read the documentation and 
code of

kernel memory management and will make corrections here in future versions

>
>> +		if (!data) {
>> +			pr_warn("NO space for clean data\n");
>> +			return;
>> +		}
>> +		memcpy(data, adata, sizeof(struct loongarch_avec_data));
>> +		INIT_LIST_HEAD(&data->entry);
>> +
>> +		list_add_tail(&data->entry, &plist->head);
>> +		loongson_send_ipi_single(adata->prev_cpu, SMP_CLEAR_VECT);
>> +	}
>> +	adata->prev_cpu = adata->cpu;
>> +	adata->prev_vec = adata->vec;
>> +}
>> +
>> +static int loongarch_avec_set_affinity(struct irq_data *data,
>> +		const struct cpumask *dest, bool force)
>> +{
>> +	struct cpumask intersect_mask;
> No cpumasks on stack please. You can make that static as usage is always
> serialized via loongarch_avec.lock
Ok, I will correct this
>
>> +	struct loongarch_avec_data *adata;
>> +	unsigned int cpu, vector;
>> +	unsigned long flags;
>> +	int ret = 0;
>> +
>> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +	adata = irq_data_get_irq_chip_data(data);
>> +
>> +	if (adata->vec && cpu_online(adata->cpu)
>> +			&& cpumask_test_cpu(adata->cpu, dest)) {
> Please align the condition proper when you need a line break:
>
> 	if (adata->vec && cpu_online(adata->cpu) &&
> 	    cpumask_test_cpu(adata->cpu, dest)) {
>
> But you don't need a line break here because
>
> 	if (adata->vec && cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest)) {
>
> fits into the 100 character line width limit.
>
>> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +		return 0;
>> +	}
>> +
>> +	if (!cpumask_intersects(dest, cpu_online_mask)) {
>> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +		return -EINVAL;
>> +	}
>> +
>> +	cpumask_and(&intersect_mask, dest, cpu_online_mask);
> The above intersect check is pointless as the matrix allocator already
> checks the cpumask and returns -EINVAL if empty.
>
>> +
>> +	ret = assign_irq_vector(data, &intersect_mask, &cpu, &vector);
>> +	if (ret) {
>> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +		return ret;
>> +	}
>> +void complete_irq_moving(int *restart)
> What is the 'restart' argument for?
Sorry, This is the remaining part of the code evolution, which will be 
removed in the future
>
>> +{
>> +	struct pending_list *plist = this_cpu_ptr(&pending_list);
>> +	struct loongarch_avec_data *adata, *tmp;
>> +	int cpu, vector;
>> +	u32 bias;
>> +	u64 irr;
>> +
>> +	raw_spin_lock(&loongarch_avec.lock);
>> +
>> +	list_for_each_entry_safe(adata, tmp, &plist->head, entry) {
>> +
>> +		cpu = adata->prev_cpu;
>> +		vector = adata->prev_vec;
>> +		bias = vector/64;
>> +
>> +		switch (bias) {
>> +		case 0x0:
>> +			irr = csr_read64(LOONGARCH_CSR_IRR0);
>> +			break;
>> +		case 0x1:
>> +			irr = csr_read64(LOONGARCH_CSR_IRR1);
>> +			break;
>> +		case 0x2:
>> +			irr = csr_read64(LOONGARCH_CSR_IRR2);
>> +			break;
>> +		case 0x3:
>> +			irr = csr_read64(LOONGARCH_CSR_IRR3);
>> +			break;
>> +		default:
>> +			return;
> How can that happen ?
>
>> +		}
>                  irr = csr_read64(LOONGARCH_CSR_IRR0 + vector / 64);
>
> should be good enough, no?

Our initial idea was to increase the readability of the code,

but it does seem more cumbersome. Perhaps we should define macros as

LOONGARCH_CSR_IRR_BASE

and adopt your approach, thanks

>
> Also please use a proper constant instead of '64', e.g. VECTORS_PER_IRR
>
>> +
>> +		if (irr & (1UL << (vector % 64))) {
>> +			loongson_send_ipi_single(cpu, SMP_CLEAR_VECT);
> So this sends an IPI to the current CPU. What guarantees that the
> pending interrupt is handled _before_ the IPI is handled again?

In fact, LA adopts a software query interrupt method at the core 
interrupt domain,

while IPI is routed to ip-12 and AVEC is routed to ip-14, ensuring that 
the AVEC interrupt can receive a response after IPI

driver/irqchip/irq-loongson-cpu.c:

static void handle_cpu_irq(struct pt_regs *regs)
{
         int hwirq;
         unsigned int estat = read_csr_estat() & CSR_ESTAT_IS;

         while ((hwirq = ffs(estat))) {
                 estat &= ~BIT(hwirq - 1);
                 generic_handle_domain_irq(irq_domain, hwirq - 1);
         }
}

>
>> +			continue;
>> +		}
>> +		list_del(&adata->entry);
>> +		irq_matrix_free(loongarch_avec.vector_matrix, cpu, vector, false);
>> +		this_cpu_ptr(irq_map)[vector] = 0;
> s/0/NULL/ as this writes a pointer.
>
>                  this_cpu_write(irq_map.irqd[vector], NULL);
>
> avoids the whole pointer indirection.
>
>> +		kfree(adata);
> Again this won't work with PREEMPT_RT.
>
>> +	}
>> +	raw_spin_unlock(&loongarch_avec.lock);
>> +}
>> +
>> +static void loongarch_avec_dispatch(struct irq_desc *desc)
>> +{
>> +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> +	struct irq_data *d;
>> +	unsigned long vector;
>> +
>> +	chained_irq_enter(chip, desc);
>> +	vector = csr_read64(LOONGARCH_CSR_ILR);
>> +	if (vector & 0x80000000)
> No magic numbers. Please use proper constant defines.
>
>> +		return;
>> +
>> +	vector &= 0xff;
> Ditto.
>
>> +
>> +	d = raw_cpu_ptr(irq_map)[vector];
> Why raw?
>
>          d = __this_cpu_read(...);
>
> Also, what is the point of storing irqdata in the irq_map if the only
> thing you use is d->irq. You can simply store the interrupt number, no?
>
> If you want to spare cycles for the lookup, then you want to store the
> interrupt descriptor like x86 does.
We will reconsider the implementation details of this section, thanks
>
>> +	if (d)
>> +		generic_handle_irq(d->irq);
>> +	else
>> +		pr_warn("IRQ ERROR:Unexpected irq  occur on cpu %d[vector %d]\n",
>> +				smp_processor_id(), vector);
> See bracket rules in the tip documentation.
>
>> +	chained_irq_exit(chip, desc);
>> +}
>> +
>> +static int loongarch_avec_alloc(struct irq_domain *domain, unsigned int virq,
>> +		unsigned int nr_irqs, void *arg)
>> +{
>> +	struct loongarch_avec_data *adata;
>> +	struct irq_data *irqd;
>> +	unsigned int cpu, vector;
>> +	unsigned long flags;
>> +	int i, err;
> See variable declaration rules in the tip documentation
>
>> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +	for (i = 0; i < nr_irqs; i++) {
>> +		irqd = irq_domain_get_irq_data(domain, virq + i);
>> +		adata = kzalloc(sizeof(*adata), GFP_KERNEL);
>> +		if (!adata) {
>> +			raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +			return -ENOMEM;
>> +		}
>> +		err = assign_irq_vector(irqd, cpu_online_mask, &cpu, &vector);
>> +		if (err) {
>> +			raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +			return err;
>> +		}
>> +		adata->prev_cpu = adata->cpu = cpu;
>> +		adata->prev_vec = adata->vec = vector;
>> +
>> +		per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irqd;
> This needs to be set last, no?
>
>> +		irq_domain_set_info(domain, virq + i, virq, &loongarch_avec_controller,
>> +				adata, handle_edge_irq, NULL, NULL);
>> +		irqd_set_single_target(irqd);
>> +		irqd_set_affinity_on_activate(irqd);
>> +	}
>> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +
>> +	return err;
>> +}
>> +
>> +static void loongarch_avec_free(struct irq_domain *domain, unsigned int virq,
>> +		unsigned int nr_irqs)
>> +{
>> +	struct loongarch_avec_data *adata;
>> +	struct irq_data *d;
>> +	unsigned long flags;
>> +	unsigned int i;
>> +
>> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +	for (i = 0; i < nr_irqs; i++) {
>> +		d = irq_domain_get_irq_data(domain, virq + i);
>> +		adata = irq_data_get_irq_chip_data(d);
>> +		if (d) {
>> +			irq_matrix_free(loongarch_avec.vector_matrix,
>> +					adata->cpu,
>> +					adata->vec, false);
>> +			irq_domain_reset_irq_data(d);
>> +		}
> What cleans up the irq_map and pending cleanups? There is a UAF waiting
> around the corner.
>
>> +	}
>> +
>> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +}
>> +static int __init loongarch_avec_init(struct irq_domain *parent)
>> +{
>> +	int ret = 0, parent_irq;
>> +	unsigned long tmp;
>> +
>> +	tmp = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
>> +	tmp |= IOCSR_MISC_FUNC_AVEC_EN;
>> +	iocsr_write64(tmp, LOONGARCH_IOCSR_MISC_FUNC);
> Enabling AVEC _before_ everything is set up is a patently bad idea.
>
>> +	raw_spin_lock_init(&loongarch_avec.lock);
>> +
>> +	loongarch_avec.fwnode = irq_domain_alloc_named_fwnode("CORE_AVEC");
>> +	if (!loongarch_avec.fwnode) {
>> +		pr_err("Unable to allocate domain handle\n");
>> +		ret = -ENOMEM;
>> +		goto out;
>> +	}
>> +
>> +	loongarch_avec.domain = irq_domain_create_tree(loongarch_avec.fwnode,
>> +			&loongarch_avec_domain_ops, NULL);
>> +	if (!loongarch_avec.domain) {
>> +		pr_err("core-vec: cannot create IRQ domain\n");
>> +		ret = -ENOMEM;
>> +		goto out_free_handle;
>> +	}
>> +
>> +	parent_irq = irq_create_mapping(parent, INT_AVEC);
>> +	if (!parent_irq) {
>> +		pr_err("Failed to mapping hwirq\n");
>> +		ret = -EINVAL;
>> +		goto out_remove_domain;
>> +	}
>> +	irq_set_chained_handler_and_data(parent_irq, loongarch_avec_dispatch, NULL);
>> +
>> +	ret = irq_matrix_init();
>> +	if (ret) {
>> +		pr_err("Failed to init irq matrix\n");
>> +		goto out_free_matrix;
>> +	}
>> +
>> +	return ret;
>> +
>> +out_free_matrix:
>> +	kfree(loongarch_avec.vector_matrix);
>> +out_remove_domain:
>> +	irq_domain_remove(loongarch_avec.domain);
>> +out_free_handle:
>> +	irq_domain_free_fwnode(loongarch_avec.fwnode);
>> +out:
>> +	return ret;
>> +}
>> +
>> +static int loongarch_avec_offline_cpu(unsigned int cpu)
>> +{
>> +	unsigned long flags;
>> +	struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
>> +
>> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +	if (list_empty(&plist->head)) {
>> +		irq_matrix_offline(loongarch_avec.vector_matrix);
>> +	} else {
>> +		pr_warn("cpu %d advanced extioi is busy\n");
>> +		raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +		return -EBUSY;
>> +	}
>> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +	return 0;
>> +}
>> +
>> +static int loongarch_avec_online_cpu(unsigned int cpu)
>> +{
>> +	struct pending_list *plist = per_cpu_ptr(&pending_list, cpu);
>> +	unsigned long flags;
>> +
>> +	raw_spin_lock_irqsave(&loongarch_avec.lock, flags);
>> +
>> +	irq_matrix_online(loongarch_avec.vector_matrix);
>> +
>> +	INIT_LIST_HEAD(&plist->head);
>> +
>> +	raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags);
>> +	return 0;
>> +}
>> +#if defined(CONFIG_ACPI)
> Missing newline before #if and also please use #ifdef 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_v2 = pchmsi_entry->msg_address;
>> +	return pch_msi_acpi_init_v2(loongarch_avec.domain, pchmsi_entry);
>> +}
>> +
>> +static inline int __init acpi_cascade_irqdomain_init(void)
>> +{
>> +	return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
>> +}
>> +
>> +int __init loongarch_avec_acpi_init(struct irq_domain *parent)
>> +{
>> +	int ret = 0;
>> +
>> +	ret = loongarch_avec_init(parent);
>> +	if (ret) {
>> +		pr_err("Failed to init irq domain\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = acpi_cascade_irqdomain_init();
>> +	if (ret) {
>> +		pr_err("Failed to cascade IRQ domain\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
>> +			"loongarch_avec:online",
>> +			loongarch_avec_online_cpu, loongarch_avec_offline_cpu);
> You cannot online/offline the matrix and handle eventually pending
> cleanups from a CPUHP_AP_ONLINE_DYN state. That needs to happen in the
> STARTING section between CPUHP_AP_OFFLINE and CPUHP_AP_ONLINE
We will readjust the code for the CPU on/offline section. Thank you for 
your correction
>
>> +	if (ret < 0) {
>> +		pr_err("loongarch_avec: failed to register hotplug callbacks.\n");
>> +		return ret;
>> +	}
>> +
>> +	return ret;
> So if CONFIG_ACPI is disabled then loongarch_avec_init() is unused and
> results in a defined but not used build warning...
>
>> diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
>> index 6e1e1f011bb2..d1706080b4f4 100644
>> --- a/drivers/irqchip/irq-loongson-pch-msi.c
>> +++ b/drivers/irqchip/irq-loongson-pch-msi.c
>> @@ -16,7 +16,6 @@
>>   #include <linux/slab.h>
>>   
>>   static int nr_pics;
>> -
>>   struct pch_msi_data {
>>   	struct mutex	msi_map_lock;
>>   	phys_addr_t	doorbell;
>> @@ -100,6 +99,17 @@ static struct irq_chip middle_irq_chip = {
>>   	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
>>   };
>>   
>> +static struct irq_chip pch_msi_irq_chip_v2 = {
>> +	.name			= "MSI",
>> +	.irq_ack		= irq_chip_ack_parent,
>> +};
>> +
>> +static struct msi_domain_info pch_msi_domain_info_v2 = {
>> +	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +			MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
>> +	.chip	= &pch_msi_irq_chip_v2,
>> +};
>> +
>>   static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
>>   					unsigned int virq, int hwirq)
>>   {
>> @@ -268,6 +278,9 @@ struct fwnode_handle *get_pch_msi_handle(int pci_segment)
>>   {
>>   	int i;
>>   
>> +	if (cpu_has_avecint)
>> +		return pch_msi_handle[0];
>> +
>>   	for (i = 0; i < MAX_IO_PICS; i++) {
>>   		if (msi_group[i].pci_segment == pci_segment)
>>   			return pch_msi_handle[i];
>> @@ -289,4 +302,34 @@ int __init pch_msi_acpi_init(struct irq_domain *parent,
>>   
>>   	return ret;
>>   }
>> +
>> +int __init pch_msi_acpi_init_v2(struct irq_domain *parent,
>> +		struct acpi_madt_msi_pic *msi_entry)
>> +{
>> +	struct irq_domain *msi_domain;
>> +
>> +	if (pch_msi_handle[0])
>> +		return 0;
>> +
>> +	pch_msi_handle[0] = irq_domain_alloc_named_fwnode("msipic-v2");
>> +	if (!pch_msi_handle[0]) {
>> +		pr_err("Unable to allocate domain handle\n");
>> +		kfree(pch_msi_handle[0]);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	msi_domain = pci_msi_create_irq_domain(pch_msi_handle[0],
>> +			&pch_msi_domain_info_v2,
>> +			parent);
>> +	if (!msi_domain) {
>> +		pr_err("Failed to create PCI MSI domain\n");
>> +		kfree(pch_msi_handle[0]);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	pr_info("IRQ domain MSIPIC-V2 init done.\n");
>> +	return 0;
>> +}
>> +
>> +
> Stray newlines. But as with the other CONFIG_ACPI part above a build
> with CONFIG_ACPI=n will result in defined but not used warnings ....
We will readjust the code for the CPU on/offline section. Thank you for 
your correction
>
> Thanks,
>
>          tglx

Thanks

          Tianyang


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-05-14  2:40 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-07 12:59 [PATCH 2/2] Loongarch:Support loongarch avec Tianyang Zhang
2024-05-07 14:20 ` Xi Ruoyao
2024-05-13  6:41   ` Tianyang Zhang
2024-05-08  6:19 ` kernel test robot
2024-05-08  7:00 ` maobibo
2024-05-13  6:53   ` Tianyang Zhang
2024-05-08  9:43 ` Thomas Gleixner
2024-05-14  2:39   ` Tianyang Zhang
2024-05-08 21:18 ` kernel test robot
2024-05-08 21:28 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).