The main function of the Redirected interrupt controller is to manage the
redirected-interrupt table, which consists of many redirected entries.
When MSI interrupts are requested, the driver creates a corresponding
redirected entry that describes the target CPU/vector number and the
operating mode of the interrupt. The redirected interrupt module has an
independent cache, and during the interrupt routing process, it will
prioritize the redirected entries that hit the cache. The driver
invalidates certain entry caches via a command queue.
Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
---
arch/loongarch/include/asm/cpu-features.h | 1 +
arch/loongarch/include/asm/cpu.h | 2 +
arch/loongarch/include/asm/loongarch.h | 6 +
arch/loongarch/kernel/cpu-probe.c | 2 +
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-loongarch-avec.c | 21 +-
drivers/irqchip/irq-loongarch-ir.c | 561 ++++++++++++++++++++++
drivers/irqchip/irq-loongson.h | 12 +
include/linux/cpuhotplug.h | 1 +
9 files changed, 594 insertions(+), 14 deletions(-)
create mode 100644 drivers/irqchip/irq-loongarch-ir.c
diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
index fc83bb32f9f0..03f7e93e81e0 100644
--- a/arch/loongarch/include/asm/cpu-features.h
+++ b/arch/loongarch/include/asm/cpu-features.h
@@ -68,5 +68,6 @@
#define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
#define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
#define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
+#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
#endif /* __ASM_CPU_FEATURES_H */
diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
index 98cf4d7b4b0a..33cd96e569d8 100644
--- a/arch/loongarch/include/asm/cpu.h
+++ b/arch/loongarch/include/asm/cpu.h
@@ -102,6 +102,7 @@ enum cpu_type_enum {
#define CPU_FEATURE_PTW 27 /* CPU has hardware page table walker */
#define CPU_FEATURE_LSPW 28 /* CPU has LSPW (lddir/ldpte instructions) */
#define CPU_FEATURE_AVECINT 29 /* CPU has AVEC interrupt */
+#define CPU_FEATURE_REDIRECTINT 30 /* CPU has interrupt remmap */
#define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
#define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
@@ -133,5 +134,6 @@ enum cpu_type_enum {
#define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
#define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
#define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
+#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
#endif /* _ASM_CPU_H */
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index 52651aa0e583..95e06cb6831e 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -1130,6 +1130,7 @@
#define IOCSRF_FLATMODE BIT_ULL(10)
#define IOCSRF_VM BIT_ULL(11)
#define IOCSRF_AVEC BIT_ULL(15)
+#define IOCSRF_REDIRECTINT BIT_ULL(16)
#define LOONGARCH_IOCSR_VENDOR 0x10
@@ -1189,6 +1190,11 @@
#define LOONGARCH_IOCSR_EXTIOI_NODEMAP_BASE 0x14a0
#define LOONGARCH_IOCSR_EXTIOI_IPMAP_BASE 0x14c0
+#define LOONGARCH_IOCSR_REDIRECT_CFG 0x15e0
+#define LOONGARCH_IOCSR_REDIRECT_TBR 0x15e8 /* IRT BASE REG*/
+#define LOONGARCH_IOCSR_REDIRECT_CQB 0x15f0 /* IRT CACHE QUEUE BASE */
+#define LOONGARCH_IOCSR_REDIRECT_CQH 0x15f8 /* IRT CACHE QUEUE HEAD, 32bit */
+#define LOONGARCH_IOCSR_REDIRECT_CQT 0x15fc /* IRT CACHE QUEUE TAIL, 32bit */
#define LOONGARCH_IOCSR_EXTIOI_EN_BASE 0x1600
#define LOONGARCH_IOCSR_EXTIOI_BOUNCE_BASE 0x1680
#define LOONGARCH_IOCSR_EXTIOI_ISR_BASE 0x1800
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index fedaa67cde41..543474fd1399 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -289,6 +289,8 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
c->options |= LOONGARCH_CPU_EIODECODE;
if (config & IOCSRF_AVEC)
c->options |= LOONGARCH_CPU_AVECINT;
+ if (config & IOCSRF_REDIRECTINT)
+ c->options |= LOONGARCH_CPU_REDIRECTINT;
if (config & IOCSRF_VM)
c->options |= LOONGARCH_CPU_HYPERVISOR;
}
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 25e9ad29b8c4..5dd7d6b151d9 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -113,7 +113,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
-obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o
+obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o
obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c
index 80e55955a29f..a2a8c819cfb1 100644
--- a/drivers/irqchip/irq-loongarch-avec.c
+++ b/drivers/irqchip/irq-loongarch-avec.c
@@ -24,7 +24,6 @@
#define VECTORS_PER_REG 64
#define IRR_VECTOR_MASK 0xffUL
#define IRR_INVALID_MASK 0x80000000UL
-#define AVEC_MSG_OFFSET 0x100000
#ifdef CONFIG_SMP
struct pending_list {
@@ -47,15 +46,6 @@ struct avecintc_chip {
static struct avecintc_chip loongarch_avec;
-struct avecintc_data {
- struct list_head entry;
- unsigned int cpu;
- unsigned int vec;
- unsigned int prev_cpu;
- unsigned int prev_vec;
- unsigned int moving;
-};
-
static inline void avecintc_enable(void)
{
u64 value;
@@ -85,7 +75,7 @@ static inline void pending_list_init(int cpu)
INIT_LIST_HEAD(&plist->head);
}
-static void avecintc_sync(struct avecintc_data *adata)
+void avecintc_sync(struct avecintc_data *adata)
{
struct pending_list *plist;
@@ -109,7 +99,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
return -EBUSY;
if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest))
- return 0;
+ return IRQ_SET_MASK_OK_DONE;
cpumask_and(&intersect_mask, dest, cpu_online_mask);
@@ -121,7 +111,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
adata->cpu = cpu;
adata->vec = vector;
per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data);
- avecintc_sync(adata);
+ if (!cpu_has_redirectint)
+ avecintc_sync(adata);
}
irq_data_update_effective_affinity(data, cpumask_of(cpu));
@@ -242,6 +233,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
d = this_cpu_read(irq_map[vector]);
if (d) {
generic_handle_irq_desc(d);
+
} else {
spurious_interrupt();
pr_warn("Unexpected IRQ occurs on CPU#%d [vector %ld]\n", smp_processor_id(), vector);
@@ -412,6 +404,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
static inline int __init acpi_cascade_irqdomain_init(void)
{
+ if (cpu_has_redirectint)
+ return redirect_acpi_init(loongarch_avec.domain);
+
return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
}
diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
new file mode 100644
index 000000000000..9e1cbbb72e29
--- /dev/null
+++ b/drivers/irqchip/irq-loongarch-ir.c
@@ -0,0 +1,561 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Loongson Technologies, Inc.
+ */
+
+#include <linux/cpuhotplug.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/spinlock.h>
+#include <linux/msi.h>
+
+#include <asm/irq.h>
+#include <asm/loongarch.h>
+#include <asm/setup.h>
+#include <larchintrin.h>
+
+#include "irq-loongson.h"
+#include "irq-msi-lib.h"
+
+#define IRD_ENTRIES 65536
+
+/* redirect entry size 128bits */
+#define IRD_PAGE_ORDER (20 - PAGE_SHIFT)
+
+/* irt cache invalid queue */
+#define INVALID_QUEUE_SIZE 4096
+
+#define INVALID_QUEUE_PAGE_ORDER (16 - PAGE_SHIFT)
+
+#define GPID_ADDR_MASK 0x3ffffffffffULL
+#define GPID_ADDR_SHIFT 6
+
+#define CQB_SIZE_SHIFT 0
+#define CQB_SIZE_MASK 0xf
+#define CQB_ADDR_SHIFT 12
+#define CQB_ADDR_MASK (0xfffffffffULL)
+
+#define CFG_DISABLE_IDLE 2
+#define INVALID_INDEX 0
+
+#define MAX_IR_ENGINES 16
+
+struct irq_domain *redirect_domain;
+
+struct redirect_entry {
+ struct {
+ __u64 valid : 1,
+ res1 : 5,
+ gpid : 42,
+ res2 : 8,
+ vector : 8;
+ } lo;
+ __u64 hi;
+};
+
+struct redirect_gpid {
+ u64 pir[4]; /* Pending interrupt requested */
+ u8 en : 1, /* doorbell */
+ res0 : 7;
+ u8 irqnum;
+ u16 res1;
+ u32 dst;
+ u32 rsvd[6];
+} __aligned(64);
+
+struct redirect_table {
+ int node;
+ struct redirect_entry *table;
+ unsigned long *bitmap;
+ unsigned int nr_ird;
+ struct page *page;
+ raw_spinlock_t lock;
+};
+
+struct redirect_item {
+ int index;
+ struct redirect_entry *entry;
+ struct redirect_gpid *gpid;
+ struct redirect_table *table;
+};
+
+struct redirect_queue {
+ int node;
+ u64 base;
+ u32 max_size;
+ int head;
+ int tail;
+ struct page *page;
+ raw_spinlock_t lock;
+};
+
+
+struct irde_desc {
+ struct redirect_table ird_table;
+ struct redirect_queue inv_queue;
+};
+
+struct irde_inv_cmd {
+ union {
+ __u64 cmd_info;
+ struct {
+ __u64 res1 : 4,
+ type : 1,
+ need_notice : 1,
+ pad : 2,
+ index : 16,
+ pad2 : 40;
+ } index;
+ };
+ __u64 notice_addr;
+};
+
+struct smp_invalid_arg {
+ struct redirect_queue *queue;
+ struct irde_inv_cmd *cmd;
+};
+
+static struct irde_desc irde_descs[MAX_IR_ENGINES];
+static phys_addr_t msi_base_addr;
+
+static inline bool invalid_queue_is_full(int node_id, u32 *tail)
+{
+ u32 head;
+
+ head = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQH);
+ *tail = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQT);
+
+ return !!(head == ((*tail + 1) % INVALID_QUEUE_SIZE));
+}
+
+static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
+{
+ struct irde_inv_cmd *inv_addr;
+ u32 tail;
+
+ guard(raw_spinlock_irqsave)(&rqueue->lock);
+
+ while (invalid_queue_is_full(rqueue->node, &tail))
+ cpu_relax();
+
+ inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
+ memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
+ tail = (tail + 1) % INVALID_QUEUE_SIZE;
+
+ wmb();
+
+ iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
+}
+
+static void smp_call_invalid_enqueue(void *arg)
+{
+ struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
+
+ invalid_enqueue(s_arg->queue, s_arg->cmd);
+}
+
+static void irde_invlid_entry_node(struct redirect_item *item)
+{
+ struct redirect_queue *rqueue;
+ struct smp_invalid_arg arg;
+ struct irde_inv_cmd cmd;
+ volatile u64 raddr = 0;
+ int node = item->table->node, cpu;
+
+ rqueue = &(irde_descs[node].inv_queue);
+ cmd.cmd_info = 0;
+ cmd.index.type = INVALID_INDEX;
+ cmd.index.need_notice = 1;
+ cmd.index.index = item->index;
+ cmd.notice_addr = (u64)(__pa(&raddr));
+
+ if (cpu_to_node(smp_processor_id()) == node)
+ invalid_enqueue(rqueue, &cmd);
+ else {
+ for_each_cpu(cpu, cpumask_of_node(node)) {
+ if (cpu_online(cpu))
+ break;
+ }
+ arg.queue = rqueue;
+ arg.cmd = &cmd;
+ smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
+ }
+
+ while (!raddr)
+ cpu_relax();
+
+}
+
+static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
+{
+ return data->parent_data->chip_data;
+}
+
+static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
+{
+ int index;
+
+ guard(raw_spinlock_irqsave)(&ird_table->lock);
+
+ index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
+ if (index > IRD_ENTRIES) {
+ pr_err("No redirect entry to use\n");
+ return -ENOMEM;
+ }
+
+ __set_bit(index, ird_table->bitmap);
+
+ item->index = index;
+ item->entry = &ird_table->table[index];
+ item->table = ird_table;
+
+ return 0;
+}
+
+static int redirect_table_free(struct redirect_item *item)
+{
+ struct redirect_table *ird_table;
+ struct redirect_entry *entry;
+ unsigned long flags;
+
+ ird_table = item->table;
+
+ entry = item->entry;
+ memset(entry, 0, sizeof(struct redirect_entry));
+
+ raw_spin_lock_irqsave(&ird_table->lock, flags);
+ bitmap_release_region(ird_table->bitmap, item->index, 0);
+ raw_spin_unlock_irqrestore(&ird_table->lock, flags);
+
+ kfree(item->gpid);
+
+ irde_invlid_entry_node(item);
+
+ return 0;
+}
+
+static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
+{
+ struct redirect_entry *entry = item->entry;
+
+ item->gpid->en = 1;
+ item->gpid->irqnum = adata->vec;
+ item->gpid->dst = adata->cpu;
+
+ entry->lo.valid = 1;
+ entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
+ entry->lo.vector = 0xff;
+ wmb();
+}
+
+static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
+{
+ struct redirect_item *item = data->chip_data;
+ struct avecintc_data *adata;
+ int ret;
+
+ ret = irq_chip_set_affinity_parent(data, dest, force);
+ if (ret == IRQ_SET_MASK_OK_DONE)
+ return IRQ_SET_MASK_OK;
+ else if (ret) {
+ pr_err("IRDE:set_affinity error %d\n", ret);
+ return ret;
+ }
+
+ adata = irq_data_get_avec_data(data);
+
+ redirect_domain_prepare_entry(item, adata);
+
+ irde_invlid_entry_node(item);
+
+ avecintc_sync(adata);
+ return IRQ_SET_MASK_OK;
+}
+
+static void redirect_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
+{
+ struct redirect_item *item;
+
+ item = irq_data_get_irq_chip_data(d);
+ msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
+ msg->address_hi = 0x0;
+ msg->data = 0x0;
+}
+
+static inline void redirect_ack_irq(struct irq_data *d)
+{
+}
+
+static inline void redirect_unmask_irq(struct irq_data *d)
+{
+}
+
+static inline void redirect_mask_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip loongarch_redirect_chip = {
+ .name = "REDIRECT",
+ .irq_ack = redirect_ack_irq,
+ .irq_mask = redirect_mask_irq,
+ .irq_unmask = redirect_unmask_irq,
+ .irq_set_affinity = redirect_set_affinity,
+ .irq_compose_msi_msg = redirect_compose_msi_msg,
+};
+
+static void redirect_free_resources(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ struct irq_data *irq_data;
+ struct redirect_item *item;
+
+ for (int i = 0; i < nr_irqs; i++) {
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+ if (irq_data && irq_data->chip_data) {
+ item = irq_data->chip_data;
+ redirect_table_free(item);
+ kfree(item);
+ }
+ }
+}
+
+static int redirect_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *arg)
+{
+ struct redirect_table *ird_table;
+ struct avecintc_data *avec_data;
+ struct irq_data *irq_data;
+ int ret, i, node;
+
+#ifdef CONFIG_NUMA
+ node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
+#else
+ node = 0;
+#endif
+ ird_table = &irde_descs[node].ird_table;
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ struct redirect_item *item;
+
+ item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
+ if (!item) {
+ pr_err("Alloc redirect descriptor failed\n");
+ goto out_free_resources;
+ }
+
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+
+ avec_data = irq_data_get_avec_data(irq_data);
+ ret = redirect_table_alloc(item, ird_table);
+ if (ret) {
+ pr_err("Alloc redirect table entry failed\n");
+ goto out_free_resources;
+ }
+
+ item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
+ if (!item->gpid) {
+ pr_err("Alloc redirect GPID failed\n");
+ goto out_free_resources;
+ }
+
+ irq_data->chip_data = item;
+ irq_data->chip = &loongarch_redirect_chip;
+ redirect_domain_prepare_entry(item, avec_data);
+ }
+ return 0;
+
+out_free_resources:
+ redirect_free_resources(domain, virq, nr_irqs);
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+
+ return -EINVAL;
+}
+
+static void redirect_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ redirect_free_resources(domain, virq, nr_irqs);
+ return irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops redirect_domain_ops = {
+ .alloc = redirect_alloc,
+ .free = redirect_free,
+ .select = msi_lib_irq_domain_select,
+};
+
+static int redirect_queue_init(int node)
+{
+ struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
+ struct page *pages;
+
+ pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
+ if (!pages) {
+ pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+
+ rqueue->page = pages;
+ rqueue->base = (u64)page_address(pages);
+ rqueue->max_size = INVALID_QUEUE_SIZE;
+ rqueue->head = 0;
+ rqueue->tail = 0;
+ rqueue->node = node;
+ raw_spin_lock_init(&rqueue->lock);
+
+ iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
+ iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
+ iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
+ (CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
+ return 0;
+}
+
+static int redirect_table_init(int node)
+{
+ struct redirect_table *ird_table = &(irde_descs[node].ird_table);
+ struct page *pages;
+ unsigned long *bitmap;
+
+ pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
+ if (!pages) {
+ pr_err("Node [%d] redirect table alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+ ird_table->page = pages;
+ ird_table->table = page_address(pages);
+
+ bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
+ if (!bitmap) {
+ pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+
+ ird_table->bitmap = bitmap;
+ ird_table->nr_ird = IRD_ENTRIES;
+ ird_table->node = node;
+
+ raw_spin_lock_init(&ird_table->lock);
+
+ if (redirect_queue_init(node))
+ return -EINVAL;
+
+ iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
+ iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
+
+ return 0;
+}
+
+static void redirect_table_fini(int node)
+{
+ struct redirect_table *ird_table = &(irde_descs[node].ird_table);
+ struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
+
+ if (ird_table->page) {
+ __free_pages(ird_table->page, IRD_PAGE_ORDER);
+ ird_table->table = NULL;
+ ird_table->page = NULL;
+ }
+
+ if (ird_table->page) {
+ bitmap_free(ird_table->bitmap);
+ ird_table->bitmap = NULL;
+ }
+
+ if (rqueue->page) {
+ __free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
+ rqueue->page = NULL;
+ rqueue->base = 0;
+ }
+
+ iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_CQB);
+ iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_TBR);
+}
+
+static int redirect_cpu_online(unsigned int cpu)
+{
+ int ret, node = cpu_to_node(cpu);
+
+ if (cpu != cpumask_first(cpumask_of_node(node)))
+ return 0;
+
+ ret = redirect_table_init(node);
+ if (ret) {
+ redirect_table_fini(node);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_ACPI)
+static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
+
+ msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET;
+
+ return pch_msi_acpi_init_avec(redirect_domain);
+}
+
+static int __init acpi_cascade_irqdomain_init(void)
+{
+ return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
+}
+
+int __init redirect_acpi_init(struct irq_domain *parent)
+{
+ struct fwnode_handle *fwnode;
+ struct irq_domain *domain;
+ int ret;
+
+ fwnode = irq_domain_alloc_named_fwnode("redirect");
+ if (!fwnode) {
+ pr_err("Unable to alloc redirect domain handle\n");
+ goto fail;
+ }
+
+ domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode,
+ &redirect_domain_ops, irde_descs);
+ if (!domain) {
+ pr_err("Unable to alloc redirect domain\n");
+ goto out_free_fwnode;
+ }
+
+ redirect_domain = domain;
+
+ ret = redirect_table_init(0);
+ if (ret)
+ goto out_free_table;
+
+ ret = acpi_cascade_irqdomain_init();
+ if (ret < 0) {
+ pr_err("Failed to cascade IRQ domain, ret=%d\n", ret);
+ goto out_free_table;
+ }
+
+ cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_REDIRECT_STARTING,
+ "irqchip/loongarch/redirect:starting",
+ redirect_cpu_online, NULL);
+
+ pr_info("loongarch irq redirect modules init succeeded\n");
+ return 0;
+
+out_free_table:
+ redirect_table_fini(0);
+ irq_domain_remove(redirect_domain);
+ redirect_domain = NULL;
+out_free_fwnode:
+ irq_domain_free_fwnode(fwnode);
+fail:
+ return -EINVAL;
+}
+#endif
diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h
index 11fa138d1f44..05ad40ffb62b 100644
--- a/drivers/irqchip/irq-loongson.h
+++ b/drivers/irqchip/irq-loongson.h
@@ -5,6 +5,15 @@
#ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
#define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
+#define AVEC_MSG_OFFSET 0x100000
+struct avecintc_data {
+ struct list_head entry;
+ unsigned int cpu;
+ unsigned int vec;
+ unsigned int prev_cpu;
+ unsigned int prev_vec;
+ unsigned int moving;
+};
int find_pch_pic(u32 gsi);
@@ -24,4 +33,7 @@ int pch_msi_acpi_init(struct irq_domain *parent,
struct acpi_madt_msi_pic *acpi_pchmsi);
int pch_msi_acpi_init_avec(struct irq_domain *parent);
+int redirect_acpi_init(struct irq_domain *parent);
+
+void avecintc_sync(struct avecintc_data *adata);
#endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 6cc5e484547c..2fd5531fa378 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -146,6 +146,7 @@ enum cpuhp_state {
CPUHP_AP_IRQ_MIPS_GIC_STARTING,
CPUHP_AP_IRQ_EIOINTC_STARTING,
CPUHP_AP_IRQ_AVECINTC_STARTING,
+ CPUHP_AP_IRQ_REDIRECT_STARTING,
CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,
--
2.43.0
Hi, Tianyang,
On Mon, Mar 31, 2025 at 2:41 PM Tianyang Zhang
<zhangtianyang@loongson.cn> wrote:
>
> The main function of the Redirected interrupt controller is to manage the
> redirected-interrupt table, which consists of many redirected entries.
> When MSI interrupts are requested, the driver creates a corresponding
> redirected entry that describes the target CPU/vector number and the
> operating mode of the interrupt. The redirected interrupt module has an
> independent cache, and during the interrupt routing process, it will
> prioritize the redirected entries that hit the cache. The driver
> invalidates certain entry caches via a command queue.
>
> Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
> ---
> arch/loongarch/include/asm/cpu-features.h | 1 +
> arch/loongarch/include/asm/cpu.h | 2 +
> arch/loongarch/include/asm/loongarch.h | 6 +
> arch/loongarch/kernel/cpu-probe.c | 2 +
> drivers/irqchip/Makefile | 2 +-
> drivers/irqchip/irq-loongarch-avec.c | 21 +-
> drivers/irqchip/irq-loongarch-ir.c | 561 ++++++++++++++++++++++
> drivers/irqchip/irq-loongson.h | 12 +
> include/linux/cpuhotplug.h | 1 +
> 9 files changed, 594 insertions(+), 14 deletions(-)
> create mode 100644 drivers/irqchip/irq-loongarch-ir.c
>
> diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
> index fc83bb32f9f0..03f7e93e81e0 100644
> --- a/arch/loongarch/include/asm/cpu-features.h
> +++ b/arch/loongarch/include/asm/cpu-features.h
> @@ -68,5 +68,6 @@
> #define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
> #define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
> #define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
> +#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
>
> #endif /* __ASM_CPU_FEATURES_H */
> diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
> index 98cf4d7b4b0a..33cd96e569d8 100644
> --- a/arch/loongarch/include/asm/cpu.h
> +++ b/arch/loongarch/include/asm/cpu.h
> @@ -102,6 +102,7 @@ enum cpu_type_enum {
> #define CPU_FEATURE_PTW 27 /* CPU has hardware page table walker */
> #define CPU_FEATURE_LSPW 28 /* CPU has LSPW (lddir/ldpte instructions) */
> #define CPU_FEATURE_AVECINT 29 /* CPU has AVEC interrupt */
> +#define CPU_FEATURE_REDIRECTINT 30 /* CPU has interrupt remmap */
>
> #define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
> #define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
> @@ -133,5 +134,6 @@ enum cpu_type_enum {
> #define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
> #define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
> #define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
> +#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
>
> #endif /* _ASM_CPU_H */
> diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
> index 52651aa0e583..95e06cb6831e 100644
> --- a/arch/loongarch/include/asm/loongarch.h
> +++ b/arch/loongarch/include/asm/loongarch.h
> @@ -1130,6 +1130,7 @@
> #define IOCSRF_FLATMODE BIT_ULL(10)
> #define IOCSRF_VM BIT_ULL(11)
> #define IOCSRF_AVEC BIT_ULL(15)
> +#define IOCSRF_REDIRECTINT BIT_ULL(16)
>
> #define LOONGARCH_IOCSR_VENDOR 0x10
>
> @@ -1189,6 +1190,11 @@
>
> #define LOONGARCH_IOCSR_EXTIOI_NODEMAP_BASE 0x14a0
> #define LOONGARCH_IOCSR_EXTIOI_IPMAP_BASE 0x14c0
> +#define LOONGARCH_IOCSR_REDIRECT_CFG 0x15e0
> +#define LOONGARCH_IOCSR_REDIRECT_TBR 0x15e8 /* IRT BASE REG*/
> +#define LOONGARCH_IOCSR_REDIRECT_CQB 0x15f0 /* IRT CACHE QUEUE BASE */
> +#define LOONGARCH_IOCSR_REDIRECT_CQH 0x15f8 /* IRT CACHE QUEUE HEAD, 32bit */
> +#define LOONGARCH_IOCSR_REDIRECT_CQT 0x15fc /* IRT CACHE QUEUE TAIL, 32bit */
> #define LOONGARCH_IOCSR_EXTIOI_EN_BASE 0x1600
> #define LOONGARCH_IOCSR_EXTIOI_BOUNCE_BASE 0x1680
> #define LOONGARCH_IOCSR_EXTIOI_ISR_BASE 0x1800
> diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
> index fedaa67cde41..543474fd1399 100644
> --- a/arch/loongarch/kernel/cpu-probe.c
> +++ b/arch/loongarch/kernel/cpu-probe.c
> @@ -289,6 +289,8 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
> c->options |= LOONGARCH_CPU_EIODECODE;
> if (config & IOCSRF_AVEC)
> c->options |= LOONGARCH_CPU_AVECINT;
> + if (config & IOCSRF_REDIRECTINT)
> + c->options |= LOONGARCH_CPU_REDIRECTINT;
> if (config & IOCSRF_VM)
> c->options |= LOONGARCH_CPU_HYPERVISOR;
> }
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 25e9ad29b8c4..5dd7d6b151d9 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -113,7 +113,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
> obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
> obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
> obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
> -obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o
> +obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o
> obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
> obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
> obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
> diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c
> index 80e55955a29f..a2a8c819cfb1 100644
> --- a/drivers/irqchip/irq-loongarch-avec.c
> +++ b/drivers/irqchip/irq-loongarch-avec.c
> @@ -24,7 +24,6 @@
> #define VECTORS_PER_REG 64
> #define IRR_VECTOR_MASK 0xffUL
> #define IRR_INVALID_MASK 0x80000000UL
> -#define AVEC_MSG_OFFSET 0x100000
>
> #ifdef CONFIG_SMP
> struct pending_list {
> @@ -47,15 +46,6 @@ struct avecintc_chip {
>
> static struct avecintc_chip loongarch_avec;
>
> -struct avecintc_data {
> - struct list_head entry;
> - unsigned int cpu;
> - unsigned int vec;
> - unsigned int prev_cpu;
> - unsigned int prev_vec;
> - unsigned int moving;
> -};
> -
> static inline void avecintc_enable(void)
> {
> u64 value;
> @@ -85,7 +75,7 @@ static inline void pending_list_init(int cpu)
> INIT_LIST_HEAD(&plist->head);
> }
>
> -static void avecintc_sync(struct avecintc_data *adata)
> +void avecintc_sync(struct avecintc_data *adata)
> {
> struct pending_list *plist;
>
> @@ -109,7 +99,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
> return -EBUSY;
>
> if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest))
> - return 0;
> + return IRQ_SET_MASK_OK_DONE;
>
> cpumask_and(&intersect_mask, dest, cpu_online_mask);
>
> @@ -121,7 +111,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
> adata->cpu = cpu;
> adata->vec = vector;
> per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data);
> - avecintc_sync(adata);
> + if (!cpu_has_redirectint)
> + avecintc_sync(adata);
> }
>
> irq_data_update_effective_affinity(data, cpumask_of(cpu));
> @@ -242,6 +233,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
> d = this_cpu_read(irq_map[vector]);
> if (d) {
> generic_handle_irq_desc(d);
> +
> } else {
> spurious_interrupt();
> pr_warn("Unexpected IRQ occurs on CPU#%d [vector %ld]\n", smp_processor_id(), vector);
> @@ -412,6 +404,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
>
> static inline int __init acpi_cascade_irqdomain_init(void)
> {
> + if (cpu_has_redirectint)
> + return redirect_acpi_init(loongarch_avec.domain);
> +
> return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> }
>
> diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
> new file mode 100644
> index 000000000000..9e1cbbb72e29
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongarch-ir.c
> @@ -0,0 +1,561 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Loongson Technologies, Inc.
> + */
> +
> +#include <linux/cpuhotplug.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +#include <linux/spinlock.h>
> +#include <linux/msi.h>
> +
> +#include <asm/irq.h>
> +#include <asm/loongarch.h>
> +#include <asm/setup.h>
> +#include <larchintrin.h>
> +
> +#include "irq-loongson.h"
> +#include "irq-msi-lib.h"
> +
> +#define IRD_ENTRIES 65536
> +
> +/* redirect entry size 128bits */
> +#define IRD_PAGE_ORDER (20 - PAGE_SHIFT)
> +
> +/* irt cache invalid queue */
> +#define INVALID_QUEUE_SIZE 4096
> +
> +#define INVALID_QUEUE_PAGE_ORDER (16 - PAGE_SHIFT)
> +
> +#define GPID_ADDR_MASK 0x3ffffffffffULL
> +#define GPID_ADDR_SHIFT 6
> +
> +#define CQB_SIZE_SHIFT 0
> +#define CQB_SIZE_MASK 0xf
> +#define CQB_ADDR_SHIFT 12
> +#define CQB_ADDR_MASK (0xfffffffffULL)
> +
> +#define CFG_DISABLE_IDLE 2
> +#define INVALID_INDEX 0
> +
> +#define MAX_IR_ENGINES 16
> +
> +struct irq_domain *redirect_domain;
> +
> +struct redirect_entry {
> + struct {
> + __u64 valid : 1,
> + res1 : 5,
> + gpid : 42,
> + res2 : 8,
> + vector : 8;
> + } lo;
> + __u64 hi;
> +};
> +
> +struct redirect_gpid {
> + u64 pir[4]; /* Pending interrupt requested */
> + u8 en : 1, /* doorbell */
> + res0 : 7;
> + u8 irqnum;
> + u16 res1;
> + u32 dst;
> + u32 rsvd[6];
> +} __aligned(64);
> +
> +struct redirect_table {
> + int node;
> + struct redirect_entry *table;
> + unsigned long *bitmap;
> + unsigned int nr_ird;
> + struct page *page;
> + raw_spinlock_t lock;
> +};
> +
> +struct redirect_item {
> + int index;
> + struct redirect_entry *entry;
> + struct redirect_gpid *gpid;
> + struct redirect_table *table;
> +};
> +
> +struct redirect_queue {
> + int node;
> + u64 base;
> + u32 max_size;
> + int head;
> + int tail;
> + struct page *page;
> + raw_spinlock_t lock;
> +};
> +
> +
> +struct irde_desc {
> + struct redirect_table ird_table;
> + struct redirect_queue inv_queue;
> +};
> +
> +struct irde_inv_cmd {
> + union {
> + __u64 cmd_info;
> + struct {
> + __u64 res1 : 4,
> + type : 1,
> + need_notice : 1,
> + pad : 2,
> + index : 16,
> + pad2 : 40;
> + } index;
> + };
> + __u64 notice_addr;
> +};
> +
> +struct smp_invalid_arg {
> + struct redirect_queue *queue;
> + struct irde_inv_cmd *cmd;
> +};
> +
> +static struct irde_desc irde_descs[MAX_IR_ENGINES];
> +static phys_addr_t msi_base_addr;
> +
> +static inline bool invalid_queue_is_full(int node_id, u32 *tail)
> +{
> + u32 head;
> +
> + head = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQH);
> + *tail = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQT);
> +
> + return !!(head == ((*tail + 1) % INVALID_QUEUE_SIZE));
> +}
> +
> +static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
> +{
> + struct irde_inv_cmd *inv_addr;
> + u32 tail;
> +
> + guard(raw_spinlock_irqsave)(&rqueue->lock);
> +
> + while (invalid_queue_is_full(rqueue->node, &tail))
> + cpu_relax();
> +
> + inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
> + memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
> + tail = (tail + 1) % INVALID_QUEUE_SIZE;
> +
> + wmb();
> +
> + iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
> +}
> +
> +static void smp_call_invalid_enqueue(void *arg)
> +{
> + struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
> +
> + invalid_enqueue(s_arg->queue, s_arg->cmd);
> +}
> +
> +static void irde_invlid_entry_node(struct redirect_item *item)
> +{
> + struct redirect_queue *rqueue;
> + struct smp_invalid_arg arg;
> + struct irde_inv_cmd cmd;
> + volatile u64 raddr = 0;
> + int node = item->table->node, cpu;
> +
> + rqueue = &(irde_descs[node].inv_queue);
> + cmd.cmd_info = 0;
> + cmd.index.type = INVALID_INDEX;
> + cmd.index.need_notice = 1;
> + cmd.index.index = item->index;
> + cmd.notice_addr = (u64)(__pa(&raddr));
> +
> + if (cpu_to_node(smp_processor_id()) == node)
> + invalid_enqueue(rqueue, &cmd);
> + else {
> + for_each_cpu(cpu, cpumask_of_node(node)) {
> + if (cpu_online(cpu))
> + break;
> + }
> + arg.queue = rqueue;
> + arg.cmd = &cmd;
> + smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
> + }
> +
> + while (!raddr)
> + cpu_relax();
> +
> +}
> +
> +static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
> +{
> + return data->parent_data->chip_data;
> +}
> +
> +static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
> +{
> + int index;
> +
> + guard(raw_spinlock_irqsave)(&ird_table->lock);
> +
> + index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
> + if (index > IRD_ENTRIES) {
> + pr_err("No redirect entry to use\n");
> + return -ENOMEM;
> + }
> +
> + __set_bit(index, ird_table->bitmap);
> +
> + item->index = index;
> + item->entry = &ird_table->table[index];
> + item->table = ird_table;
> +
> + return 0;
> +}
> +
> +static int redirect_table_free(struct redirect_item *item)
> +{
> + struct redirect_table *ird_table;
> + struct redirect_entry *entry;
> + unsigned long flags;
> +
> + ird_table = item->table;
> +
> + entry = item->entry;
> + memset(entry, 0, sizeof(struct redirect_entry));
> +
> + raw_spin_lock_irqsave(&ird_table->lock, flags);
> + bitmap_release_region(ird_table->bitmap, item->index, 0);
> + raw_spin_unlock_irqrestore(&ird_table->lock, flags);
> +
> + kfree(item->gpid);
> +
> + irde_invlid_entry_node(item);
> +
> + return 0;
> +}
> +
> +static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
> +{
> + struct redirect_entry *entry = item->entry;
> +
> + item->gpid->en = 1;
> + item->gpid->irqnum = adata->vec;
> + item->gpid->dst = adata->cpu;
> +
> + entry->lo.valid = 1;
> + entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
> + entry->lo.vector = 0xff;
> + wmb();
> +}
> +
> +static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
> +{
> + struct redirect_item *item = data->chip_data;
> + struct avecintc_data *adata;
> + int ret;
> +
> + ret = irq_chip_set_affinity_parent(data, dest, force);
> + if (ret == IRQ_SET_MASK_OK_DONE)
> + return IRQ_SET_MASK_OK;
> + else if (ret) {
> + pr_err("IRDE:set_affinity error %d\n", ret);
> + return ret;
> + }
> +
> + adata = irq_data_get_avec_data(data);
> +
> + redirect_domain_prepare_entry(item, adata);
> +
> + irde_invlid_entry_node(item);
> +
> + avecintc_sync(adata);
> + return IRQ_SET_MASK_OK;
> +}
> +
> +static void redirect_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
> +{
> + struct redirect_item *item;
> +
> + item = irq_data_get_irq_chip_data(d);
> + msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
> + msg->address_hi = 0x0;
> + msg->data = 0x0;
> +}
> +
> +static inline void redirect_ack_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void redirect_unmask_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void redirect_mask_irq(struct irq_data *d)
> +{
> +}
> +
> +static struct irq_chip loongarch_redirect_chip = {
> + .name = "REDIRECT",
> + .irq_ack = redirect_ack_irq,
> + .irq_mask = redirect_mask_irq,
> + .irq_unmask = redirect_unmask_irq,
> + .irq_set_affinity = redirect_set_affinity,
> + .irq_compose_msi_msg = redirect_compose_msi_msg,
> +};
> +
> +static void redirect_free_resources(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
> +{
> + struct irq_data *irq_data;
> + struct redirect_item *item;
> +
> + for (int i = 0; i < nr_irqs; i++) {
> + irq_data = irq_domain_get_irq_data(domain, virq + i);
> + if (irq_data && irq_data->chip_data) {
> + item = irq_data->chip_data;
> + redirect_table_free(item);
> + kfree(item);
> + }
> + }
> +}
> +
> +static int redirect_alloc(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs,
> + void *arg)
> +{
> + struct redirect_table *ird_table;
> + struct avecintc_data *avec_data;
> + struct irq_data *irq_data;
> + int ret, i, node;
> +
> +#ifdef CONFIG_NUMA
> + node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
> +#else
> + node = 0;
> +#endif
> + ird_table = &irde_descs[node].ird_table;
> + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
> + if (ret < 0)
> + return ret;
> +
> + for (i = 0; i < nr_irqs; i++) {
> + struct redirect_item *item;
> +
> + item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
> + if (!item) {
> + pr_err("Alloc redirect descriptor failed\n");
> + goto out_free_resources;
> + }
> +
> + irq_data = irq_domain_get_irq_data(domain, virq + i);
> +
> + avec_data = irq_data_get_avec_data(irq_data);
> + ret = redirect_table_alloc(item, ird_table);
> + if (ret) {
> + pr_err("Alloc redirect table entry failed\n");
> + goto out_free_resources;
> + }
> +
> + item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
> + if (!item->gpid) {
> + pr_err("Alloc redirect GPID failed\n");
> + goto out_free_resources;
> + }
> +
> + irq_data->chip_data = item;
> + irq_data->chip = &loongarch_redirect_chip;
> + redirect_domain_prepare_entry(item, avec_data);
> + }
> + return 0;
> +
> +out_free_resources:
> + redirect_free_resources(domain, virq, nr_irqs);
> + irq_domain_free_irqs_common(domain, virq, nr_irqs);
> +
> + return -EINVAL;
> +}
> +
> +static void redirect_free(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
> +{
> + redirect_free_resources(domain, virq, nr_irqs);
> + return irq_domain_free_irqs_common(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops redirect_domain_ops = {
> + .alloc = redirect_alloc,
> + .free = redirect_free,
Since this patch needs a new version, you can rename
redirect_alloc/redirect_free to
redirect_domain_alloc/redirect_domain_free as other loongson specific
drivers.
Huacai
> + .select = msi_lib_irq_domain_select,
> +};
> +
> +static int redirect_queue_init(int node)
> +{
> + struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
> + struct page *pages;
> +
> + pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
> + if (!pages) {
> + pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> +
> + rqueue->page = pages;
> + rqueue->base = (u64)page_address(pages);
> + rqueue->max_size = INVALID_QUEUE_SIZE;
> + rqueue->head = 0;
> + rqueue->tail = 0;
> + rqueue->node = node;
> + raw_spin_lock_init(&rqueue->lock);
> +
> + iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
> + iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
> + iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
> + (CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
> + return 0;
> +}
> +
> +static int redirect_table_init(int node)
> +{
> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> + struct page *pages;
> + unsigned long *bitmap;
> +
> + pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
> + if (!pages) {
> + pr_err("Node [%d] redirect table alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> + ird_table->page = pages;
> + ird_table->table = page_address(pages);
> +
> + bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
> + if (!bitmap) {
> + pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> +
> + ird_table->bitmap = bitmap;
> + ird_table->nr_ird = IRD_ENTRIES;
> + ird_table->node = node;
> +
> + raw_spin_lock_init(&ird_table->lock);
> +
> + if (redirect_queue_init(node))
> + return -EINVAL;
> +
> + iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
> + iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
> +
> + return 0;
> +}
> +
> +static void redirect_table_fini(int node)
> +{
> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> + struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
> +
> + if (ird_table->page) {
> + __free_pages(ird_table->page, IRD_PAGE_ORDER);
> + ird_table->table = NULL;
> + ird_table->page = NULL;
> + }
> +
> + if (ird_table->page) {
> + bitmap_free(ird_table->bitmap);
> + ird_table->bitmap = NULL;
> + }
> +
> + if (rqueue->page) {
> + __free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
> + rqueue->page = NULL;
> + rqueue->base = 0;
> + }
> +
> + iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_CQB);
> + iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_TBR);
> +}
> +
> +static int redirect_cpu_online(unsigned int cpu)
> +{
> + int ret, node = cpu_to_node(cpu);
> +
> + if (cpu != cpumask_first(cpumask_of_node(node)))
> + return 0;
> +
> + ret = redirect_table_init(node);
> + if (ret) {
> + redirect_table_fini(node);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +#if defined(CONFIG_ACPI)
> +static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
> + const unsigned long end)
> +{
> + struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
> +
> + msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET;
> +
> + return pch_msi_acpi_init_avec(redirect_domain);
> +}
> +
> +static int __init acpi_cascade_irqdomain_init(void)
> +{
> + return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> +}
> +
> +int __init redirect_acpi_init(struct irq_domain *parent)
> +{
> + struct fwnode_handle *fwnode;
> + struct irq_domain *domain;
> + int ret;
> +
> + fwnode = irq_domain_alloc_named_fwnode("redirect");
> + if (!fwnode) {
> + pr_err("Unable to alloc redirect domain handle\n");
> + goto fail;
> + }
> +
> + domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode,
> + &redirect_domain_ops, irde_descs);
> + if (!domain) {
> + pr_err("Unable to alloc redirect domain\n");
> + goto out_free_fwnode;
> + }
> +
> + redirect_domain = domain;
> +
> + ret = redirect_table_init(0);
> + if (ret)
> + goto out_free_table;
> +
> + ret = acpi_cascade_irqdomain_init();
> + if (ret < 0) {
> + pr_err("Failed to cascade IRQ domain, ret=%d\n", ret);
> + goto out_free_table;
> + }
> +
> + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_REDIRECT_STARTING,
> + "irqchip/loongarch/redirect:starting",
> + redirect_cpu_online, NULL);
> +
> + pr_info("loongarch irq redirect modules init succeeded\n");
> + return 0;
> +
> +out_free_table:
> + redirect_table_fini(0);
> + irq_domain_remove(redirect_domain);
> + redirect_domain = NULL;
> +out_free_fwnode:
> + irq_domain_free_fwnode(fwnode);
> +fail:
> + return -EINVAL;
> +}
> +#endif
> diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h
> index 11fa138d1f44..05ad40ffb62b 100644
> --- a/drivers/irqchip/irq-loongson.h
> +++ b/drivers/irqchip/irq-loongson.h
> @@ -5,6 +5,15 @@
>
> #ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
> #define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
> +#define AVEC_MSG_OFFSET 0x100000
> +struct avecintc_data {
> + struct list_head entry;
> + unsigned int cpu;
> + unsigned int vec;
> + unsigned int prev_cpu;
> + unsigned int prev_vec;
> + unsigned int moving;
> +};
>
> int find_pch_pic(u32 gsi);
>
> @@ -24,4 +33,7 @@ int pch_msi_acpi_init(struct irq_domain *parent,
> struct acpi_madt_msi_pic *acpi_pchmsi);
> int pch_msi_acpi_init_avec(struct irq_domain *parent);
>
> +int redirect_acpi_init(struct irq_domain *parent);
> +
> +void avecintc_sync(struct avecintc_data *adata);
> #endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */
> diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
> index 6cc5e484547c..2fd5531fa378 100644
> --- a/include/linux/cpuhotplug.h
> +++ b/include/linux/cpuhotplug.h
> @@ -146,6 +146,7 @@ enum cpuhp_state {
> CPUHP_AP_IRQ_MIPS_GIC_STARTING,
> CPUHP_AP_IRQ_EIOINTC_STARTING,
> CPUHP_AP_IRQ_AVECINTC_STARTING,
> + CPUHP_AP_IRQ_REDIRECT_STARTING,
> CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
> CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
> CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,
> --
> 2.43.0
>
>
Hi, Huacai
在 2025/4/1 下午8:30, Huacai Chen 写道:
> +static const struct irq_domain_ops redirect_domain_ops = {
> + .alloc = redirect_alloc,
> + .free = redirect_free,
> Since this patch needs a new version, you can rename
> redirect_alloc/redirect_free to
> redirect_domain_alloc/redirect_domain_free as other loongson specific
> drivers.
>
> Huacai
Ok, I got it
On Mon, Mar 31 2025 at 14:41, Tianyang Zhang wrote:
> irq_data_update_effective_affinity(data, cpumask_of(cpu));
> @@ -242,6 +233,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
> d = this_cpu_read(irq_map[vector]);
> if (d) {
> generic_handle_irq_desc(d);
> +
Stray newline.
> } else {
> +
> +static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
> +{
> + struct irde_inv_cmd *inv_addr;
> + u32 tail;
> +
> + guard(raw_spinlock_irqsave)(&rqueue->lock);
> +
> + while (invalid_queue_is_full(rqueue->node, &tail))
> + cpu_relax();
> +
> + inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
> + memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
> + tail = (tail + 1) % INVALID_QUEUE_SIZE;
> +
> + wmb();
Undocumented memory barrier.
> +
> + iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
> +}
> +
> +static void smp_call_invalid_enqueue(void *arg)
> +{
> + struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
> +
> + invalid_enqueue(s_arg->queue, s_arg->cmd);
> +}
> +
> +static void irde_invlid_entry_node(struct redirect_item *item)
> +{
> + struct redirect_queue *rqueue;
> + struct smp_invalid_arg arg;
> + struct irde_inv_cmd cmd;
> + volatile u64 raddr = 0;
> + int node = item->table->node, cpu;
> +
> + rqueue = &(irde_descs[node].inv_queue);
> + cmd.cmd_info = 0;
> + cmd.index.type = INVALID_INDEX;
> + cmd.index.need_notice = 1;
> + cmd.index.index = item->index;
> + cmd.notice_addr = (u64)(__pa(&raddr));
> +
> + if (cpu_to_node(smp_processor_id()) == node)
> + invalid_enqueue(rqueue, &cmd);
> + else {
if () lacks brackets
> + for_each_cpu(cpu, cpumask_of_node(node)) {
> + if (cpu_online(cpu))
> + break;
> + }
> + arg.queue = rqueue;
> + arg.cmd = &cmd;
> + smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
> + }
> +
> + while (!raddr)
> + cpu_relax();
> +
> +}
> +static int redirect_table_free(struct redirect_item *item)
> +{
> + struct redirect_table *ird_table;
> + struct redirect_entry *entry;
> + unsigned long flags;
> +
> + ird_table = item->table;
> +
> + entry = item->entry;
> + memset(entry, 0, sizeof(struct redirect_entry));
> +
> + raw_spin_lock_irqsave(&ird_table->lock, flags);
> + bitmap_release_region(ird_table->bitmap, item->index, 0);
> + raw_spin_unlock_irqrestore(&ird_table->lock, flags);
scoped_guard(raw_spinlock_irqsave, &ird_table->lock)
bitmap_release_region(ird_table->bitmap, item->index, 0);
> +static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
> +{
> + struct redirect_item *item = data->chip_data;
> + struct avecintc_data *adata;
> + int ret;
> +
> + ret = irq_chip_set_affinity_parent(data, dest, force);
> + if (ret == IRQ_SET_MASK_OK_DONE)
> + return IRQ_SET_MASK_OK;
Again bracket rules. Also what is this return value translation about?
> + else if (ret) {
> + pr_err("IRDE:set_affinity error %d\n", ret);
> + return ret;
> + }
> +
> + adata = irq_data_get_avec_data(data);
> +
> + redirect_domain_prepare_entry(item, adata);
> +
> + irde_invlid_entry_node(item);
This cannot work when irde_invlid_entry_node() goes into the SMP
function call path, because smp_call_function_single() cannot be invoked
with interrupts disabled.
> +
> +static void redirect_free_resources(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
Please align the arguments as documented.
> +{
> + struct irq_data *irq_data;
> + struct redirect_item *item;
> +
Please move the variables into the scope where they are used
> + for (int i = 0; i < nr_irqs; i++) {
> + irq_data = irq_domain_get_irq_data(domain, virq + i);
> + if (irq_data && irq_data->chip_data) {
> + item = irq_data->chip_data;
> + redirect_table_free(item);
> + kfree(item);
> + }
> + }
> +}
> +
> +static int redirect_alloc(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs,
> + void *arg)
More random coding style
> +{
> + struct redirect_table *ird_table;
> + struct avecintc_data *avec_data;
> + struct irq_data *irq_data;
> + int ret, i, node;
> +
> +#ifdef CONFIG_NUMA
> + node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
Bah.
> +#else
> + node = 0;
> +#endif
msi_alloc_info_t *info = arg;
node = dev_to_node(info->desc->dev);
> +static int redirect_table_init(int node)
> +{
> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> + struct page *pages;
> + unsigned long *bitmap;
Use proper reverse fir tree ordering
Thanks,
tglx
Hi, Thomas
在 2025/4/1 下午3:14, Thomas Gleixner 写道:
> On Mon, Mar 31 2025 at 14:41, Tianyang Zhang wrote:
>> irq_data_update_effective_affinity(data, cpumask_of(cpu));
>> @@ -242,6 +233,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
>> d = this_cpu_read(irq_map[vector]);
>> if (d) {
>> generic_handle_irq_desc(d);
>> +
> Stray newline.
OK, I got it
>
>> } else {
>> +
>> +static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
>> +{
>> + struct irde_inv_cmd *inv_addr;
>> + u32 tail;
>> +
>> + guard(raw_spinlock_irqsave)(&rqueue->lock);
>> +
>> + while (invalid_queue_is_full(rqueue->node, &tail))
>> + cpu_relax();
>> +
>> + inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
>> + memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
>> + tail = (tail + 1) % INVALID_QUEUE_SIZE;
>> +
>> + wmb();
> Undocumented memory barrier.
OK , I got it
>
>> +
>> + iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
>> +}
>> +
>> +static void smp_call_invalid_enqueue(void *arg)
>> +{
>> + struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
>> +
>> + invalid_enqueue(s_arg->queue, s_arg->cmd);
>> +}
>> +
>> +static void irde_invlid_entry_node(struct redirect_item *item)
>> +{
>> + struct redirect_queue *rqueue;
>> + struct smp_invalid_arg arg;
>> + struct irde_inv_cmd cmd;
>> + volatile u64 raddr = 0;
>> + int node = item->table->node, cpu;
>> +
>> + rqueue = &(irde_descs[node].inv_queue);
>> + cmd.cmd_info = 0;
>> + cmd.index.type = INVALID_INDEX;
>> + cmd.index.need_notice = 1;
>> + cmd.index.index = item->index;
>> + cmd.notice_addr = (u64)(__pa(&raddr));
>> +
>> + if (cpu_to_node(smp_processor_id()) == node)
>> + invalid_enqueue(rqueue, &cmd);
>> + else {
> if () lacks brackets
OK , I got it
>
>> + for_each_cpu(cpu, cpumask_of_node(node)) {
>> + if (cpu_online(cpu))
>> + break;
>> + }
>> + arg.queue = rqueue;
>> + arg.cmd = &cmd;
>> + smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
>> + }
>> +
>> + while (!raddr)
>> + cpu_relax();
>> +
>> +}
>
>> +static int redirect_table_free(struct redirect_item *item)
>> +{
>> + struct redirect_table *ird_table;
>> + struct redirect_entry *entry;
>> + unsigned long flags;
>> +
>> + ird_table = item->table;
>> +
>> + entry = item->entry;
>> + memset(entry, 0, sizeof(struct redirect_entry));
>> +
>> + raw_spin_lock_irqsave(&ird_table->lock, flags);
>> + bitmap_release_region(ird_table->bitmap, item->index, 0);
>> + raw_spin_unlock_irqrestore(&ird_table->lock, flags);
> scoped_guard(raw_spinlock_irqsave, &ird_table->lock)
> bitmap_release_region(ird_table->bitmap, item->index, 0);
Initially, I thought there were no complex branches here, so I directly
used raw_spin_lock_irqsave.
However, considering the unified code style, scoped_guard is more
appropriate
>
>> +static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
>> +{
>> + struct redirect_item *item = data->chip_data;
>> + struct avecintc_data *adata;
>> + int ret;
>> +
>> + ret = irq_chip_set_affinity_parent(data, dest, force);
>> + if (ret == IRQ_SET_MASK_OK_DONE)
>> + return IRQ_SET_MASK_OK;
> Again bracket rules. Also what is this return value ranslation about?
My initial aim was to keep the return value consistent with the previous
one.
I'll study the differences between the two return values and make
appropriate changes. Thank you.
>
>> + else if (ret) {
>> + pr_err("IRDE:set_affinity error %d\n", ret);
>> + return ret;
>> + }
>> +
>> + adata = irq_data_get_avec_data(data);
>> +
>> + redirect_domain_prepare_entry(item, adata);
>> +
>> + irde_invlid_entry_node(item);
> This cannot work when irde_invlid_entry_node() goes into the SMP
> function call path, because smp_call_function_single() cannot be invoked
> with interrupts disabled.
My oversight. This approach is indeed risky. I'll rethink the design.
Thanks for your advice.
>> +{
>> + struct redirect_table *ird_table;
>> + struct avecintc_data *avec_data;
>> + struct irq_data *irq_data;
>> + int ret, i, node;
>> +
>> +#ifdef CONFIG_NUMA
>> + node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
> Bah.
>
>> +#else
>> + node = 0;
>> +#endif
> msi_alloc_info_t *info = arg;
>
> node = dev_to_node(info->desc->dev);
This is appropriate and safe. Thank you.
>
>> +static int redirect_table_init(int node)
>> +{
>> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
>> + struct page *pages;
>> + unsigned long *bitmap;
> Use proper reverse fir tree ordering
>
> Thanks,
>
> tglx
I'll strive to fix other code style issues.
Thank you for your reply.
Tianyang
在 3/31/25 2:41 PM, Tianyang Zhang 写道:
> The main function of the Redirected interrupt controller is to manage the
> redirected-interrupt table, which consists of many redirected entries.
> When MSI interrupts are requested, the driver creates a corresponding
> redirected entry that describes the target CPU/vector number and the
> operating mode of the interrupt. The redirected interrupt module has an
> independent cache, and during the interrupt routing process, it will
> prioritize the redirected entries that hit the cache. The driver
> invalidates certain entry caches via a command queue.
>
> Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
> ---
> arch/loongarch/include/asm/cpu-features.h | 1 +
> arch/loongarch/include/asm/cpu.h | 2 +
> arch/loongarch/include/asm/loongarch.h | 6 +
> arch/loongarch/kernel/cpu-probe.c | 2 +
> drivers/irqchip/Makefile | 2 +-
> drivers/irqchip/irq-loongarch-avec.c | 21 +-
> drivers/irqchip/irq-loongarch-ir.c | 561 ++++++++++++++++++++++
> drivers/irqchip/irq-loongson.h | 12 +
> include/linux/cpuhotplug.h | 1 +
> 9 files changed, 594 insertions(+), 14 deletions(-)
> create mode 100644 drivers/irqchip/irq-loongarch-ir.c
>
>
> +static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
> +{
> + struct irde_inv_cmd *inv_addr;
> + u32 tail;
> +
> + guard(raw_spinlock_irqsave)(&rqueue->lock);
> +
> + while (invalid_queue_is_full(rqueue->node, &tail))
> + cpu_relax();
> +
> + inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
> + memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
> + tail = (tail + 1) % INVALID_QUEUE_SIZE;
> +
> + wmb();
Add some comments? In kernel docs:
22) 所有内存屏障(例如 ``barrier()``, ``rmb()``, ``wmb()`` )都需要源代码注
释来解释它们正在执行的操作及其原因的逻辑。
> +
> + iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
> +}
> +
> +static void smp_call_invalid_enqueue(void *arg)
> +{
> + struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
> +
> + invalid_enqueue(s_arg->queue, s_arg->cmd);
> +}
> +
> +static void irde_invlid_entry_node(struct redirect_item *item)
> +{
> + struct redirect_queue *rqueue;
> + struct smp_invalid_arg arg;
> + struct irde_inv_cmd cmd;
> + volatile u64 raddr = 0;
> + int node = item->table->node, cpu;
> +
> + rqueue = &(irde_descs[node].inv_queue);
> + cmd.cmd_info = 0;
> + cmd.index.type = INVALID_INDEX;
> + cmd.index.need_notice = 1;
> + cmd.index.index = item->index;
> + cmd.notice_addr = (u64)(__pa(&raddr));
> +
> + if (cpu_to_node(smp_processor_id()) == node)
> + invalid_enqueue(rqueue, &cmd);
> + else {
> + for_each_cpu(cpu, cpumask_of_node(node)) {
> + if (cpu_online(cpu))
> + break;
> + }
> + arg.queue = rqueue;
> + arg.cmd = &cmd;
> + smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
> + }
> +
> + while (!raddr)
> + cpu_relax();
> +
> +}
> +
> +static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
> +{
> + return data->parent_data->chip_data;
> +}
> +
> +static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
> +{
> + int index;
> +
> + guard(raw_spinlock_irqsave)(&ird_table->lock);
> +
> + index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
> + if (index > IRD_ENTRIES) {
> + pr_err("No redirect entry to use\n");
> + return -ENOMEM;
> + }
> +
> + __set_bit(index, ird_table->bitmap);
> +
> + item->index = index;
> + item->entry = &ird_table->table[index];
> + item->table = ird_table;
> +
> + return 0;
> +}
> +
> +static int redirect_table_free(struct redirect_item *item)
> +{
> + struct redirect_table *ird_table;
> + struct redirect_entry *entry;
> + unsigned long flags;
> +
> + ird_table = item->table;
> +
> + entry = item->entry;
> + memset(entry, 0, sizeof(struct redirect_entry));
> +
> + raw_spin_lock_irqsave(&ird_table->lock, flags);
> + bitmap_release_region(ird_table->bitmap, item->index, 0);
> + raw_spin_unlock_irqrestore(&ird_table->lock, flags);
> +
> + kfree(item->gpid);
> +
> + irde_invlid_entry_node(item);
> +
> + return 0;
> +}
> +
> +static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
> +{
> + struct redirect_entry *entry = item->entry;
> +
> + item->gpid->en = 1;
> + item->gpid->irqnum = adata->vec;
> + item->gpid->dst = adata->cpu;
> +
> + entry->lo.valid = 1;
> + entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
> + entry->lo.vector = 0xff;
> + wmb();
> +}
> +
> +static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
> +{
> + struct redirect_item *item = data->chip_data;
> + struct avecintc_data *adata;
> + int ret;
> +
> + ret = irq_chip_set_affinity_parent(data, dest, force);
> + if (ret == IRQ_SET_MASK_OK_DONE)
> + return IRQ_SET_MASK_OK;
> + else if (ret) {
> + pr_err("IRDE:set_affinity error %d\n", ret);
> + return ret;
> + }
> +
> + adata = irq_data_get_avec_data(data);
> +
> + redirect_domain_prepare_entry(item, adata);
> +
> + irde_invlid_entry_node(item);
> +
> + avecintc_sync(adata);
> + return IRQ_SET_MASK_OK;
> +}
> +
> +static void redirect_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
> +{
> + struct redirect_item *item;
> +
> + item = irq_data_get_irq_chip_data(d);
> + msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
> + msg->address_hi = 0x0;
> + msg->data = 0x0;
> +}
> +
> +static inline void redirect_ack_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void redirect_unmask_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void redirect_mask_irq(struct irq_data *d)
> +{
> +}
> +
> +static struct irq_chip loongarch_redirect_chip = {
> + .name = "REDIRECT",
> + .irq_ack = redirect_ack_irq,
> + .irq_mask = redirect_mask_irq,
> + .irq_unmask = redirect_unmask_irq,
> + .irq_set_affinity = redirect_set_affinity,
> + .irq_compose_msi_msg = redirect_compose_msi_msg,
> +};
> +
> +static void redirect_free_resources(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
> +{
> + struct irq_data *irq_data;
> + struct redirect_item *item;
> +
> + for (int i = 0; i < nr_irqs; i++) {
> + irq_data = irq_domain_get_irq_data(domain, virq + i);
> + if (irq_data && irq_data->chip_data) {
> + item = irq_data->chip_data;
> + redirect_table_free(item);
> + kfree(item);
> + }
> + }
> +}
> +
> +static int redirect_alloc(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs,
> + void *arg)
The line break handling here is not good. Two lines would be
sufficient. Your code style is extremely terrible, and it seems
that you haven't passed the checkpatch.pl test yet.
https://docs.kernel.org/translations/zh_CN/process/coding-style.html
Maybe it would be a good idea to read the kernel
documentation before preparing for v3.
Thanks,
Yanteng
Hi, Yanteng
在 2025/4/1 上午10:15, Yanteng Si 写道:
>
> 在 3/31/25 2:41 PM, Tianyang Zhang 写道:
>> The main function of the Redirected interrupt controller is to manage
>> the
>> redirected-interrupt table, which consists of many redirected entries.
>> When MSI interrupts are requested, the driver creates a corresponding
>> redirected entry that describes the target CPU/vector number and the
>> operating mode of the interrupt. The redirected interrupt module has an
>> independent cache, and during the interrupt routing process, it will
>> prioritize the redirected entries that hit the cache. The driver
>> invalidates certain entry caches via a command queue.
>>
>> Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
>> Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
>> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
>> ---
>> arch/loongarch/include/asm/cpu-features.h | 1 +
>> arch/loongarch/include/asm/cpu.h | 2 +
>> arch/loongarch/include/asm/loongarch.h | 6 +
>> arch/loongarch/kernel/cpu-probe.c | 2 +
>> drivers/irqchip/Makefile | 2 +-
>> drivers/irqchip/irq-loongarch-avec.c | 21 +-
>> drivers/irqchip/irq-loongarch-ir.c | 561 ++++++++++++++++++++++
>> drivers/irqchip/irq-loongson.h | 12 +
>> include/linux/cpuhotplug.h | 1 +
>> 9 files changed, 594 insertions(+), 14 deletions(-)
>> create mode 100644 drivers/irqchip/irq-loongarch-ir.c
>>
>>
>> +static void invalid_enqueue(struct redirect_queue *rqueue, struct
>> irde_inv_cmd *cmd)
>> +{
>> + struct irde_inv_cmd *inv_addr;
>> + u32 tail;
>> +
>> + guard(raw_spinlock_irqsave)(&rqueue->lock);
>> +
>> + while (invalid_queue_is_full(rqueue->node, &tail))
>> + cpu_relax();
>> +
>> + inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail *
>> sizeof(struct irde_inv_cmd));
>> + memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
>> + tail = (tail + 1) % INVALID_QUEUE_SIZE;
>> +
>
>> + wmb();
>
> Add some comments? In kernel docs:
>
>
> 22) 所有内存屏障(例如 ``barrier()``, ``rmb()``, ``wmb()``
> )都需要源代码注
> 释来解释它们正在执行的操作及其原因的逻辑。
Ok, I got it ,thansk
>> +
>> + iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
>> +}
>> +
>> +static void smp_call_invalid_enqueue(void *arg)
>> +{
>> + struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
>> +
>> + invalid_enqueue(s_arg->queue, s_arg->cmd);
>> +}
>> +
>> +static void irde_invlid_entry_node(struct redirect_item *item)
>> +{
>> + struct redirect_queue *rqueue;
>> + struct smp_invalid_arg arg;
>> + struct irde_inv_cmd cmd;
>> + volatile u64 raddr = 0;
>> + int node = item->table->node, cpu;
>> +
>> + rqueue = &(irde_descs[node].inv_queue);
>> + cmd.cmd_info = 0;
>> + cmd.index.type = INVALID_INDEX;
>> + cmd.index.need_notice = 1;
>> + cmd.index.index = item->index;
>> + cmd.notice_addr = (u64)(__pa(&raddr));
>> +
>> + if (cpu_to_node(smp_processor_id()) == node)
>> + invalid_enqueue(rqueue, &cmd);
>> + else {
>> + for_each_cpu(cpu, cpumask_of_node(node)) {
>> + if (cpu_online(cpu))
>> + break;
>> + }
>> + arg.queue = rqueue;
>> + arg.cmd = &cmd;
>> + smp_call_function_single(cpu, smp_call_invalid_enqueue,
>> &arg, 0);
>> + }
>> +
>> + while (!raddr)
>> + cpu_relax();
>> +
>> +}
>> +
>> +static inline struct avecintc_data *irq_data_get_avec_data(struct
>> irq_data *data)
>> +{
>> + return data->parent_data->chip_data;
>> +}
>> +
>> +static int redirect_table_alloc(struct redirect_item *item, struct
>> redirect_table *ird_table)
>> +{
>> + int index;
>> +
>> + guard(raw_spinlock_irqsave)(&ird_table->lock);
>> +
>> + index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
>> + if (index > IRD_ENTRIES) {
>> + pr_err("No redirect entry to use\n");
>> + return -ENOMEM;
>> + }
>> +
>> + __set_bit(index, ird_table->bitmap);
>> +
>> + item->index = index;
>> + item->entry = &ird_table->table[index];
>> + item->table = ird_table;
>> +
>> + return 0;
>> +}
>> +
>> +static int redirect_table_free(struct redirect_item *item)
>> +{
>> + struct redirect_table *ird_table;
>> + struct redirect_entry *entry;
>> + unsigned long flags;
>> +
>> + ird_table = item->table;
>> +
>> + entry = item->entry;
>> + memset(entry, 0, sizeof(struct redirect_entry));
>> +
>> + raw_spin_lock_irqsave(&ird_table->lock, flags);
>> + bitmap_release_region(ird_table->bitmap, item->index, 0);
>> + raw_spin_unlock_irqrestore(&ird_table->lock, flags);
>> +
>> + kfree(item->gpid);
>> +
>> + irde_invlid_entry_node(item);
>> +
>> + return 0;
>> +}
>> +
>> +static inline void redirect_domain_prepare_entry(struct
>> redirect_item *item, struct avecintc_data *adata)
>> +{
>> + struct redirect_entry *entry = item->entry;
>> +
>> + item->gpid->en = 1;
>> + item->gpid->irqnum = adata->vec;
>> + item->gpid->dst = adata->cpu;
>> +
>> + entry->lo.valid = 1;
>> + entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) &
>> (GPID_ADDR_MASK);
>> + entry->lo.vector = 0xff;
>> + wmb();
>> +}
>> +
>> +static int redirect_set_affinity(struct irq_data *data, const struct
>> cpumask *dest, bool force)
>> +{
>> + struct redirect_item *item = data->chip_data;
>> + struct avecintc_data *adata;
>> + int ret;
>> +
>> + ret = irq_chip_set_affinity_parent(data, dest, force);
>> + if (ret == IRQ_SET_MASK_OK_DONE)
>> + return IRQ_SET_MASK_OK;
>> + else if (ret) {
>> + pr_err("IRDE:set_affinity error %d\n", ret);
>> + return ret;
>> + }
>> +
>> + adata = irq_data_get_avec_data(data);
>> +
>> + redirect_domain_prepare_entry(item, adata);
>> +
>> + irde_invlid_entry_node(item);
>> +
>> + avecintc_sync(adata);
>> + return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static void redirect_compose_msi_msg(struct irq_data *d, struct
>> msi_msg *msg)
>> +{
>> + struct redirect_item *item;
>> +
>> + item = irq_data_get_irq_chip_data(d);
>> + msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index &
>> 0xffff) << 4));
>> + msg->address_hi = 0x0;
>> + msg->data = 0x0;
>> +}
>> +
>> +static inline void redirect_ack_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static inline void redirect_unmask_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static inline void redirect_mask_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static struct irq_chip loongarch_redirect_chip = {
>> + .name = "REDIRECT",
>> + .irq_ack = redirect_ack_irq,
>> + .irq_mask = redirect_mask_irq,
>> + .irq_unmask = redirect_unmask_irq,
>> + .irq_set_affinity = redirect_set_affinity,
>> + .irq_compose_msi_msg = redirect_compose_msi_msg,
>> +};
>> +
>> +static void redirect_free_resources(struct irq_domain *domain,
>> + unsigned int virq, unsigned int nr_irqs)
>> +{
>> + struct irq_data *irq_data;
>> + struct redirect_item *item;
>> +
>> + for (int i = 0; i < nr_irqs; i++) {
>> + irq_data = irq_domain_get_irq_data(domain, virq + i);
>> + if (irq_data && irq_data->chip_data) {
>> + item = irq_data->chip_data;
>> + redirect_table_free(item);
>> + kfree(item);
>> + }
>> + }
>> +}
>> +
>
>> +static int redirect_alloc(struct irq_domain *domain,
>> + unsigned int virq, unsigned int nr_irqs,
>> + void *arg)
>
> The line break handling here is not good. Two lines would be
>
> sufficient. Your code style is extremely terrible, and it seems
>
> that you haven't passed the checkpatch.pl test yet.
>
>
> https://docs.kernel.org/translations/zh_CN/process/coding-style.html
>
>
> Maybe it would be a good idea to read the kernel
>
> documentation before preparing for v3.
Well, according to the documentation, this is indeed inappropriate. I'll
take your advice.
>
>
> Thanks,
>
> Yanteng
Thanks,
Tianyang
© 2016 - 2025 Red Hat, Inc.