[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v1 10/14] xen/riscv: implementation of aplic and imsic operations
On 08.04.2025 17:57, Oleksii Kurochko wrote: > Introduce interrupt controller descriptor for host APLIC to describe > the low-lovel hardare. It includes implementation of the following functions: > - aplic_irq_startup() > - aplic_irq_shutdown() > - aplic_irq_enable() > - aplic_irq_disable() > - aplic_irq_ack() > - aplic_host_irq_end() > - aplic_set_irq_affinity() > > As APLIC is used in MSI mode it requires to enable/disable interrupts not > only for APLIC but also for IMSIC. Thereby for the purpose of > aplic_irq_{enable,disable}() it is introduced imsic_irq_{enable,disable)(). > > For the purpose of aplic_set_irq_affinity() aplic_get_cpu_from_mask() is > introduced to get hart id. > > Also, introduce additional interrupt controller h/w operations and > host_irq_type for APLIC: > - aplic_host_irq_type > - aplic_set_irq_priority() > - aplic_set_irq_type() Yet these two functions nor the hooks they're used to populate are entirely unused here. Since they're also outside of the common IRQ handling machinery, it's unclear how one would sanely ack such a change. > --- a/xen/arch/riscv/aplic.c > +++ b/xen/arch/riscv/aplic.c > @@ -15,6 +15,7 @@ > #include <xen/irq.h> > #include <xen/mm.h> > #include <xen/sections.h> > +#include <xen/spinlock.h> > #include <xen/types.h> > #include <xen/vmap.h> > > @@ -110,9 +111,173 @@ static int __init aplic_init(void) > return 0; > } > > -static const struct intc_hw_operations __ro_after_init aplic_ops = { > +static void aplic_set_irq_type(struct irq_desc *desc, unsigned int type) > +{ > + unsigned int irq = desc->irq - 1; Why this adjustment by 1 (and yet both items being named "irq")? > + spin_lock(&aplic.lock); > + switch(type) { > + case IRQ_TYPE_EDGE_RISING: Nit (style): Missing blanks, brace on its own line, case labels indented like their containing switch(). > + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_EDGE_RISE; > + break; > + case IRQ_TYPE_EDGE_FALLING: Blank lines please between non-fall-through case blocks. > + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_EDGE_FALL; > + break; > + case IRQ_TYPE_LEVEL_HIGH: > + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_LEVEL_HIGH; > + break; > + case IRQ_TYPE_LEVEL_LOW: > + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_LEVEL_LOW; > + break; > + default: > + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_INACTIVE; > + break; Is the default: label legitimate to be reached? > + } > + spin_unlock(&aplic.lock); > +} > + > +static void aplic_set_irq_priority(struct irq_desc *desc, > + unsigned int priority) > +{ > + /* No priority, do nothing */ > +} Since the function dopes nothing, wouldn't it be better to omit it and have the (future) caller check for a NULL pointer ahead of making the (indirect) call? Same remark for other handlers (below) which also do nothing. > +static void aplic_irq_enable(struct irq_desc *desc) > +{ > + unsigned long flags; > + > + /* > + * TODO: Currently, APLIC is supported only with MSI interrupts. > + * If APLIC without MSI interrupts is required in the future, > + * this function will need to be updated accordingly. > + */ > + ASSERT(aplic.imsic_cfg->is_used); Such an extra field, used only for assertions, is pretty odd. Can't you use any of the other fields to achieve the same effect? > + ASSERT(spin_is_locked(&desc->lock)); If this lock (which is an IRQ-safe one) is necessarily held, ... > + spin_lock_irqsave(&aplic.lock, flags); ... you can use just spin_lock() here. > + clear_bit(_IRQ_DISABLED, &desc->status); Why an atomic bitop when desc is locked? (And yes, I ought to raise the same question on Arm code also doing so.) I'm uncertain about this bit setting anyway - on x86 we would only fiddle with it for IRQs not in use, not while enabling/disabling one. In any event this can be done outside of the APLIC-locked region, I think. > + /* enable interrupt in IMSIC */ May I remind you of Xen comment style? > + imsic_irq_enable(desc->irq); > + > + /* enable interrupt in APLIC */ > + aplic.regs->setienum = desc->irq; Are you sure you want to use plain assignments for MMIO accesses? I'd have expected writel() to be used here. (And only later I realized that I didn't spot the same already higher up from here.) >From the vague understanding I've gained so far: Isn't the APLIC closer to the CPU and the IMSIC closer to the device? If so, wouldn't you want to enable at the APLIC before enabling at the IMSIC? But of course that also depends on what exactly happens in the window while one is already enabled and the other is still disabled. (Later) From the code you add to imsic.c it looks like it's the other way around, as the IMSIC is accessed through CSRs. > + spin_unlock_irqrestore(&aplic.lock, flags); > +} > + > +static void aplic_irq_disable(struct irq_desc *desc) > +{ > + unsigned long flags; > + > + /* > + * TODO: Currently, APLIC is supported only with MSI interrupts. > + * If APLIC without MSI interrupts is required in the future, > + * this function will need to be updated accordingly. > + */ > + ASSERT(aplic.imsic_cfg->is_used); > + > + ASSERT(spin_is_locked(&desc->lock)); > + > + spin_lock_irqsave(&aplic.lock, flags); > + > + set_bit(_IRQ_DISABLED, &desc->status); > + > + /* disable interrupt in APLIC */ > + aplic.regs->clrienum = desc->irq; > + > + /* disable interrupt in IMSIC */ > + imsic_irq_disable(desc->irq); > + > + spin_unlock_irqrestore(&aplic.lock, flags); > +} > + > +static unsigned int aplic_irq_startup(struct irq_desc *desc) > +{ > + aplic_irq_enable(desc); > + > + return 0; > +} > + > +static void aplic_irq_shutdown(struct irq_desc *desc) > +{ > + aplic_irq_disable(desc); > +} You don't really need a separate hook function here, do you? > +static void aplic_irq_ack(struct irq_desc *desc) > +{ > + /* nothing to do */ > +} > + > +static void aplic_host_irq_end(struct irq_desc *desc) What's the "host" in the identifier about? > +{ > + /* nothing to do */ > +} > + > +static unsigned int aplic_get_cpu_from_mask(const cpumask_t *cpumask) > +{ > + unsigned int cpu; No real need for this variable? > + cpumask_t possible_mask; > + > + cpumask_and(&possible_mask, cpumask, &cpu_possible_map); > + cpu = cpumask_any(&possible_mask); Why would you use cpu_possible_map here? That includes any offline CPUs. I think you need to use cpu_online_map here. > + return cpu; > +} > + > +static void aplic_set_irq_affinity(struct irq_desc *desc, const cpumask_t > *mask) > +{ > + unsigned int cpu; > + uint64_t group_index, base_ppn; > + uint32_t hhxw, lhxw ,hhxs, value; > + const struct imsic_config *imsic = aplic.imsic_cfg; > + > + /* > + * TODO: Currently, APLIC is supported only with MSI interrupts. > + * If APLIC without MSI interrupts is required in the future, > + * this function will need to be updated accordingly. > + */ > + ASSERT(aplic.imsic_cfg->is_used); Use the local variable you have made yourself? > + ASSERT(!cpumask_empty(mask)); > + > + spin_lock(&aplic.lock); Aiui the lock can be acquired quite a bit later. It ought to be needed only around the actual write to the hardware register. > + cpu = cpuid_to_hartid(aplic_get_cpu_from_mask(mask)); > + hhxw = imsic->group_index_bits; > + lhxw = imsic->hart_index_bits; > + hhxs = imsic->group_index_shift - IMSIC_MMIO_PAGE_SHIFT * 2; > + base_ppn = imsic->msi[cpu].base_addr >> IMSIC_MMIO_PAGE_SHIFT; > + > + /* update hart and EEID in the target register */ > + group_index = (base_ppn >> (hhxs + 12)) & (BIT(hhxw, UL) - 1); What's this magic 12 in here? Not IMSIC_MMIO_PAGE_SHIFT I suppose? > + value = desc->irq; > + value |= cpu << APLIC_TARGET_HART_IDX_SHIFT; > + value |= group_index << (lhxw + APLIC_TARGET_HART_IDX_SHIFT) ; > + aplic.regs->target[desc->irq - 1] = value; > + > + spin_unlock(&aplic.lock); > +} > + > +static hw_irq_controller aplic_host_irq_type = { const? > --- a/xen/arch/riscv/imsic.c > +++ b/xen/arch/riscv/imsic.c > @@ -14,12 +14,68 @@ > #include <xen/errno.h> > #include <xen/init.h> > #include <xen/macros.h> > +#include <xen/spinlock.h> > #include <xen/xmalloc.h> > > #include <asm/imsic.h> > > static struct imsic_config imsic_cfg; > > +#define imsic_csr_set(c, v) \ > +do { \ > + csr_write(CSR_SISELECT, c); \ > + csr_set(CSR_SIREG, v); \ > +} while (0) > + > +#define imsic_csr_clear(c, v) \ > +do { \ > + csr_write(CSR_SISELECT, c); \ > + csr_clear(CSR_SIREG, v); \ > +} while (0) > + > +static void imsic_local_eix_update(unsigned long base_id, unsigned long > num_id, > + bool pend, bool val) > +{ > + unsigned long i, isel, ireg; These can be constrained to inside the outer loop below. > + unsigned long id = base_id, last_id = base_id + num_id; > + > + while ( id < last_id ) > + { > + isel = id / __riscv_xlen; > + isel *= __riscv_xlen / IMSIC_EIPx_BITS; > + isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0; Nit: Why the parentheses? > + ireg = 0; > + for ( i = id & (__riscv_xlen - 1); > + (id < last_id) && (i < __riscv_xlen); > + i++, id++ ) > + ireg |= (1 << i); I wonder if this calculation really needs a loop. Afaict it's just a consecutive set of bits you mean to set. > + if ( val ) > + imsic_csr_set(isel, ireg); > + else > + imsic_csr_clear(isel, ireg); > + } > +} > + > +void imsic_irq_enable(unsigned int hwirq) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&imsic_cfg.lock, flags); > + imsic_local_eix_update(hwirq, 1, false, true); No subtraction of 1 here? Also, why "hwirq" and not just "irq"? > + spin_unlock_irqrestore(&imsic_cfg.lock, flags); > +} > + > +void imsic_irq_disable(unsigned int hwirq) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&imsic_cfg.lock, flags); > + imsic_local_eix_update(hwirq, 1, false, false); > + spin_unlock_irqrestore(&imsic_cfg.lock, flags); > +} > + > const struct imsic_config *imsic_get_config(void) > { > return &imsic_cfg; > @@ -277,6 +333,13 @@ int __init imsic_init(struct dt_device_node *node) > goto imsic_init_err; > } > > + spin_lock_init(&imsic_cfg.lock); > + > + /* Enable local interrupt delivery */ > + imsic_ids_local_delivery(true); What's this? I can't find the function/macro here, nor in patch 08, nor in staging. Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |