Hi, Bibo, On Wed, Aug 28, 2024 at 3:06 PM Bibo Mao <maobibo@xxxxxxxxxxx> wrote: > > Eiointc interrupt controller support 256 interrupt vectors at most, > and irq handler gets interrupt status from base register group > EIOINTC_REG_ISR plus specific offset. It needs to read register group > EIOINTC_REG_ISR four times to get all 256 interrupt vectors status. > > Eiointc registers including EIOINTC_REG_ISR is software emulated for > VMs, there will be VM-exits when accessing eiointc registers. Here one > method is introduced so that eiointc interrupt controller can route > to different cpu interrupt pins for every 64 interrupt vectors. So > irq handler needs read only relative 64 interrupt vector, it reduces > VM-exits. > > Signed-off-by: Bibo Mao <maobibo@xxxxxxxxxxx> > --- > drivers/irqchip/irq-loongson-eiointc.c | 72 ++++++++++++++++++++++---- > 1 file changed, 63 insertions(+), 9 deletions(-) > > diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c > index e9ec63d85ee8..c6bcb6625e6d 100644 > --- a/drivers/irqchip/irq-loongson-eiointc.c > +++ b/drivers/irqchip/irq-loongson-eiointc.c > @@ -30,11 +30,20 @@ > #define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG) > #define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG) > #define EIOINTC_ALL_ENABLE 0xffffffff > +#define EIOINTC_ROUTE_MULTIPLE_IP BIT(0) > > #define MAX_EIO_NODES (NR_CPUS / CORES_PER_EIO_NODE) > > static int nr_pics; > > +struct eiointc_priv; > +struct eiointc_ip_route { > + struct eiointc_priv *priv; > + /* Routed destination IP offset */ > + int start; > + int end; > +}; > + > struct eiointc_priv { > u32 node; > u32 vec_count; > @@ -43,6 +52,8 @@ struct eiointc_priv { > struct fwnode_handle *domain_handle; > struct irq_domain *eiointc_domain; > int parent_hwirq; > + int flags; > + struct eiointc_ip_route route_info[4]; > }; > > static struct eiointc_priv *eiointc_priv[MAX_IO_PICS]; > @@ -145,12 +156,20 @@ static int eiointc_router_init(unsigned int cpu) > uint32_t data; > uint32_t node = cpu_to_eio_node(cpu); > int index = eiointc_index(node); > + int hwirq, mask; > > if (index < 0) { > pr_err("Error: invalid nodemap!\n"); > return -1; > } > > + /* Enable cpu interrupt pin routed from eiointc */ > + hwirq = eiointc_priv[index]->parent_hwirq; > + mask = BIT(hwirq); > + if (eiointc_priv[index]->flags & EIOINTC_ROUTE_MULTIPLE_IP) > + mask |= BIT(hwirq + 1) | BIT(hwirq + 2) | BIT(hwirq + 3); > + set_csr_ecfg(mask); > + > if ((cpu_logical_map(cpu) % CORES_PER_EIO_NODE) == 0) { > eiointc_enable(); > > @@ -161,12 +180,23 @@ static int eiointc_router_init(unsigned int cpu) > > for (i = 0; i < eiointc_priv[0]->vec_count / 32 / 4; i++) { > /* > - * Route to interrupt pin, using offset minus INT_HWI0 > - * Offset 0 means IP0 and so on > - * Every 32 vector routing to one interrupt pin > + * Route to interrupt pin, minus INT_HWI0 as offset > + * Offset 0 means IP0 and so on, every 32 vector > + * routing to one interrupt pin > + * > + * If flags is set with EIOINTC_ROUTE_MULTIPLE_IP, > + * every 64 vector routes to different consecutive > + * IPs, otherwise all vector routes to the same IP > */ > - bit = BIT(eiointc_priv[index]->parent_hwirq - INT_HWI0); > - data = bit | (bit << 8) | (bit << 16) | (bit << 24); > + if (eiointc_priv[index]->flags & EIOINTC_ROUTE_MULTIPLE_IP) { > + bit = BIT(hwirq++ - INT_HWI0); > + data = bit | (bit << 8); > + bit = BIT(hwirq++ - INT_HWI0); > + data |= (bit << 16) | (bit << 24); > + } else { > + bit = BIT(hwirq - INT_HWI0); > + data = bit | (bit << 8) | (bit << 16) | (bit << 24); > + } > iocsr_write32(data, EIOINTC_REG_IPMAP + i * 4); > } > > @@ -197,11 +227,18 @@ static void eiointc_irq_dispatch(struct irq_desc *desc) > u64 pending; > bool handled = false; > struct irq_chip *chip = irq_desc_get_chip(desc); > - struct eiointc_priv *priv = irq_desc_get_handler_data(desc); > + struct eiointc_ip_route *info = irq_desc_get_handler_data(desc); > > chained_irq_enter(chip, desc); > > - for (i = 0; i < eiointc_priv[0]->vec_count / VEC_COUNT_PER_REG; i++) { > + /* > + * If EIOINTC_ROUTE_MULTIPLE_IP is set, every 64 interrupt vectors in > + * eiointc interrupt controller routes to different cpu interrupt pins > + * > + * Every cpu interrupt pin has its own irq handler, it is ok to > + * read ISR for these 64 interrupt vectors rather than all vectors > + */ > + for (i = info->start; i < info->end; i++) { > pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3)); > > /* Skip handling if pending bitmap is zero */ > @@ -214,7 +251,7 @@ static void eiointc_irq_dispatch(struct irq_desc *desc) > int bit = __ffs(pending); > int irq = bit + VEC_COUNT_PER_REG * i; > > - generic_handle_domain_irq(priv->eiointc_domain, irq); > + generic_handle_domain_irq(info->priv->eiointc_domain, irq); > pending &= ~BIT(bit); > handled = true; > } > @@ -397,8 +434,25 @@ static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq, > } > > eiointc_priv[nr_pics++] = priv; > + if (cpu_has_hypervisor) { > + priv->parent_hwirq = INT_HWI0; > + for (i = 0; i < priv->vec_count / VEC_COUNT_PER_REG; i++) { > + priv->route_info[i].start = priv->parent_hwirq - INT_HWI0 + i; > + priv->route_info[i].end = priv->route_info[i].start + 1; > + priv->route_info[i].priv = priv; > + parent_irq = get_percpu_irq(priv->parent_hwirq + i); > + irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, > + &priv->route_info[i]); > + } > + priv->flags |= EIOINTC_ROUTE_MULTIPLE_IP; Can real machines also use EIOINTC_ROUTE_MULTIPLE_IP? Huacai > + } else { > + priv->route_info[0].start = 0; > + priv->route_info[0].end = priv->vec_count / VEC_COUNT_PER_REG; > + priv->route_info[0].priv = priv; > + irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, > + &priv->route_info[0]); > + } > eiointc_router_init(0); > - irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv); > > if (nr_pics == 1) { > register_syscore_ops(&eiointc_syscore_ops); > -- > 2.39.3 >