Hello Volodymyr, Thank you for your close review and suggestions.
On 29.08.25 23:45, Volodymyr Babchuk wrote: > > Hi Leonid, > > Leonid Komarianskyi <leonid_komarians...@epam.com> writes: > >> This change introduces resource management in the VGIC to handle >> extended SPIs introduced in GICv3.1. The pending_irqs and >> allocated_irqs arrays are resized to support the required >> number of eSPIs, based on what is supported by the hardware and >> requested by the guest. A new field, ext_shared_irqs, is added >> to the VGIC structure to store information about eSPIs, similar >> to how shared_irqs is used for regular SPIs. >> >> Since the eSPI range starts at INTID 4096 and INTIDs between 1025 >> and 4095 are reserved, helper macros are introduced to simplify the >> transformation of indices and to enable easier access to eSPI-specific >> resources. These changes prepare the VGIC for processing eSPIs as >> required by future functionality. >> >> The initialization and deinitialization paths for vgic have been updated >> to allocate and free these resources appropriately. Additionally, >> updated handling of INTIDs greater than 1024, passed from the toolstack >> during domain creation, and verification logic ensures only valid SPI or >> eSPI INTIDs are used. >> >> The existing SPI behavior remains unaffected when guests do not request >> eSPIs, GIC hardware does not support them, or the CONFIG_GICV3_ESPI >> option is disabled. >> >> Signed-off-by: Leonid Komarianskyi <leonid_komarians...@epam.com> >> >> --- >> Changes in V5: >> - removed the has_espi field because it can be determined by checking >> whether domain->arch.vgic.nr_espis is zero or not >> - since vgic_ext_rank_offset is not used in this patch, it has been >> moved to the appropriate patch in the patch series, which implements >> vgic eSPI registers emulation and requires this function >> - removed ifdefs for eSPI-specific macros to reduce the number of ifdefs >> and code duplication in further changes >> - fixed minor nit: used %pd for printing domain with its ID >> >> Changes in V4: >> - added has_espi field to simplify determining whether a domain is able >> to operate with eSPI >> - fixed formatting issues and misspellings >> >> Changes in V3: >> - fixed formatting for lines with more than 80 symbols >> - introduced helper functions to be able to use stubs in case of >> CONFIG_GICV3_ESPI disabled, and as a result, reduce the number of >> #ifdefs >> - fixed checks for nr_spis in domain_vgic_init >> - updated comment about nr_spis adjustments with dom0less mention >> - moved comment with additional explanations before checks >> - used unsigned int for indexes since they cannot be negative >> - removed unnecessary parentheses >> - move vgic_ext_rank_offset to the below ifdef guard, to reduce the >> number of ifdefs >> >> Changes in V2: >> - change is_espi_rank to is_valid_espi_rank to verify whether the array >> element ext_shared_irqs exists. The previous version, is_espi_rank, >> only checked if the rank index was less than the maximum possible eSPI >> rank index, but this could potentially result in accessing a >> non-existing array element. To address this, is_valid_espi_rank was >> introduced, which ensures that the required eSPI rank exists >> - move gic_number_espis to >> xen/arm: gicv3: implement handling of GICv3.1 eSPI >> - update vgic_is_valid_irq checks to allow operating with eSPIs >> - remove redundant newline in vgic_allocate_virq >> --- >> xen/arch/arm/include/asm/vgic.h | 12 ++ >> xen/arch/arm/vgic.c | 199 +++++++++++++++++++++++++++++++- >> 2 files changed, 208 insertions(+), 3 deletions(-) >> >> diff --git a/xen/arch/arm/include/asm/vgic.h >> b/xen/arch/arm/include/asm/vgic.h >> index 3e7cbbb196..912d5b7694 100644 >> --- a/xen/arch/arm/include/asm/vgic.h >> +++ b/xen/arch/arm/include/asm/vgic.h >> @@ -146,6 +146,10 @@ struct vgic_dist { >> int nr_spis; /* Number of SPIs */ >> unsigned long *allocated_irqs; /* bitmap of IRQs allocated */ >> struct vgic_irq_rank *shared_irqs; >> +#ifdef CONFIG_GICV3_ESPI >> + struct vgic_irq_rank *ext_shared_irqs; >> + int nr_espis; /* Number of extended SPIs */ >> +#endif >> /* >> * SPIs are domain global, SGIs and PPIs are per-VCPU and stored in >> * struct arch_vcpu. >> @@ -243,6 +247,14 @@ struct vgic_ops { >> /* Number of ranks of interrupt registers for a domain */ >> #define DOMAIN_NR_RANKS(d) (((d)->arch.vgic.nr_spis+31)/32) >> >> +#ifdef CONFIG_GICV3_ESPI >> +#define DOMAIN_NR_EXT_RANKS(d) (((d)->arch.vgic.nr_espis+31)/32) >> +#endif >> +#define EXT_RANK_MIN (ESPI_BASE_INTID/32) >> +#define EXT_RANK_MAX ((ESPI_MAX_INTID+31)/32) >> +#define EXT_RANK_NUM2IDX(num) ((num)-EXT_RANK_MIN) >> +#define EXT_RANK_IDX2NUM(idx) ((idx)+EXT_RANK_MIN) >> + >> #define vgic_lock(v) spin_lock_irq(&(v)->domain->arch.vgic.lock) >> #define vgic_unlock(v) spin_unlock_irq(&(v)->domain->arch.vgic.lock) >> >> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c >> index 2bbf4d99aa..c9b9528c66 100644 >> --- a/xen/arch/arm/vgic.c >> +++ b/xen/arch/arm/vgic.c >> @@ -27,9 +27,82 @@ >> >> bool vgic_is_valid_line(struct domain *d, unsigned int virq) >> { >> +#ifdef CONFIG_GICV3_ESPI >> + if ( virq >= ESPI_BASE_INTID && >> + virq < ESPI_IDX2INTID(d->arch.vgic.nr_espis) ) >> + return true; >> +#endif >> + >> return virq < vgic_num_irqs(d); >> } >> >> +#ifdef CONFIG_GICV3_ESPI >> +/* >> + * Since eSPI indexes start from 4096 and numbers from 1024 to >> + * 4095 are forbidden, we need to check both lower and upper >> + * limits for ranks. >> + */ >> +static inline bool is_valid_espi_rank(struct domain *d, unsigned int rank) >> +{ >> + return rank >= EXT_RANK_MIN && >> + EXT_RANK_NUM2IDX(rank) < DOMAIN_NR_EXT_RANKS(d); >> +} >> + >> +static inline struct vgic_irq_rank *vgic_get_espi_rank(struct vcpu *v, >> + unsigned int rank) >> +{ >> + return &v->domain->arch.vgic.ext_shared_irqs[EXT_RANK_NUM2IDX(rank)]; >> +} >> + >> +static inline bool vgic_reserve_espi_virq(struct domain *d, unsigned int >> virq) >> +{ >> + return !test_and_set_bit(ESPI_INTID2IDX(virq) + vgic_num_irqs(d), >> + d->arch.vgic.allocated_irqs); >> +} >> + >> +static void arch_move_espis(struct vcpu *v) > > I don't need you need a copy of arch_move_irqs(). Se below for more info. > >> +{ >> + const cpumask_t *cpu_mask = cpumask_of(v->processor); >> + struct domain *d = v->domain; >> + struct pending_irq *p; >> + struct vcpu *v_target; >> + unsigned int i; >> + >> + for ( i = ESPI_BASE_INTID; >> + i < EXT_RANK_IDX2NUM(d->arch.vgic.nr_espis); i++ ) >> + { >> + v_target = vgic_get_target_vcpu(v, i); >> + p = irq_to_pending(v_target, i); >> + >> + if ( v_target == v && !test_bit(GIC_IRQ_GUEST_MIGRATING, >> &p->status) ) >> + irq_set_affinity(p->desc, cpu_mask); >> + } >> +} >> +#else >> +static inline bool is_valid_espi_rank(struct domain *d, unsigned int rank) >> +{ >> + return false; >> +} >> + >> +/* >> + * This function is stub and will not be called if CONFIG_GICV3_ESPI=n, >> + * because in this case, is_valid_espi_rank will always return false. >> + */ >> +static inline struct vgic_irq_rank *vgic_get_espi_rank(struct vcpu *v, >> + unsigned int rank) >> +{ >> + ASSERT_UNREACHABLE(); >> + return NULL; >> +} >> + >> +static inline bool vgic_reserve_espi_virq(struct domain *d, unsigned int >> virq) >> +{ >> + return false; >> +} >> + >> +static void arch_move_espis(struct vcpu *v) { } >> +#endif >> + >> static inline struct vgic_irq_rank *vgic_get_rank(struct vcpu *v, >> unsigned int rank) >> { >> @@ -37,6 +110,8 @@ static inline struct vgic_irq_rank *vgic_get_rank(struct >> vcpu *v, >> return v->arch.vgic.private_irqs; >> else if ( rank <= DOMAIN_NR_RANKS(v->domain) ) >> return &v->domain->arch.vgic.shared_irqs[rank - 1]; >> + else if ( is_valid_espi_rank(v->domain, rank) ) >> + return vgic_get_espi_rank(v, rank); >> else >> return NULL; >> } >> @@ -117,6 +192,62 @@ int domain_vgic_register(struct domain *d, unsigned int >> *mmio_count) >> return 0; >> } >> >> +#ifdef CONFIG_GICV3_ESPI >> +static unsigned int vgic_num_spi_lines(struct domain *d) >> +{ >> + return d->arch.vgic.nr_spis + d->arch.vgic.nr_espis; >> +} >> + >> +static int init_vgic_espi(struct domain *d) >> +{ >> + unsigned int i, idx; >> + >> + if ( d->arch.vgic.nr_espis == 0 ) >> + return 0; >> + >> + d->arch.vgic.ext_shared_irqs = >> + xzalloc_array(struct vgic_irq_rank, DOMAIN_NR_EXT_RANKS(d)); >> + if ( d->arch.vgic.ext_shared_irqs == NULL ) >> + return -ENOMEM; >> + >> + for ( i = d->arch.vgic.nr_spis, idx = 0; >> + i < vgic_num_spi_lines(d); i++, idx++ ) >> + vgic_init_pending_irq(&d->arch.vgic.pending_irqs[i], >> + ESPI_IDX2INTID(idx)); >> + >> + for ( i = 0; i < DOMAIN_NR_EXT_RANKS(d); i++ ) >> + vgic_rank_init(&d->arch.vgic.ext_shared_irqs[i], i, 0); >> + >> + return 0; >> +} >> + >> +struct pending_irq *espi_to_pending(struct domain *d, unsigned int irq) > > I know that I should made this observation in previous version, but I > didn't, sorry for that. Anyways, I don't think that this is a good idea > to introduce this function and vgic_reserve_espi_virq(), as well as > arch_move_espis(), actually, because in each case this is a code > duplication, which is not good. > > I think that instead you need to introduce a pair of helpers that will > map any (e)SPI number to pending_irq[]/allocate_irqs index and back. > > somethink like > > static inline unsigned virq_to_index(int virq) > { > if (is_espi(virq)) > return ESPI_INTID2IDX(irq) + d->arch.vgic.nr_spis; > return virq; > } > > See below for examples. > You do not need to say sorry for that :) You provided a very good solution with this helper function. So thank you again for that - I will add the helper function in V6 to consolidate similar code into it and, as a result, reuse existing code without duplication. >> +{ >> + irq = ESPI_INTID2IDX(irq) + d->arch.vgic.nr_spis; >> + return &d->arch.vgic.pending_irqs[irq]; >> +} >> +#else >> +static unsigned int init_vgic_espi(struct domain *d) >> +{ >> + return 0; >> +} >> + >> +static unsigned int vgic_num_spi_lines(struct domain *d) >> +{ >> + return d->arch.vgic.nr_spis; >> +} >> + >> +struct pending_irq *espi_to_pending(struct domain *d, unsigned int irq) >> +{ >> + return NULL; >> +} >> +#endif >> + >> +static unsigned int vgic_num_alloc_irqs(struct domain *d) >> +{ >> + return vgic_num_spi_lines(d) + NR_LOCAL_IRQS; >> +} >> + >> int domain_vgic_init(struct domain *d, unsigned int nr_spis) >> { >> int i; >> @@ -131,6 +262,36 @@ int domain_vgic_init(struct domain *d, unsigned int >> nr_spis) >> */ >> nr_spis = ROUNDUP(nr_spis, 32); >> >> +#ifdef CONFIG_GICV3_ESPI >> + /* >> + * During domain creation, the dom0less DomUs code or toolstack >> specifies >> + * the maximum INTID, which is defined in the domain config subtracted >> by >> + * 32 to cover the local IRQs (please see the comment to >> VGIC_DEF_NR_SPIS). >> + * To compute the actual number of eSPI that will be usable for, >> + * add back 32. >> + */ >> + if ( nr_spis + 32 > ESPI_IDX2INTID(NR_ESPI_IRQS) ) >> + return -EINVAL; >> + >> + if ( nr_spis + 32 >= ESPI_BASE_INTID ) >> + { >> + d->arch.vgic.nr_espis = min(nr_spis - ESPI_BASE_INTID + 32, 1024U); >> + /* Verify if GIC HW can handle provided INTID */ >> + if ( d->arch.vgic.nr_espis > gic_number_espis() ) >> + return -EINVAL; >> + /* >> + * Set the maximum available number for regular >> + * SPI to pass the next check >> + */ >> + nr_spis = VGIC_DEF_NR_SPIS; >> + } >> + else >> + { >> + /* Domain will use the regular SPI range */ >> + d->arch.vgic.nr_espis = 0; >> + } >> +#endif >> + >> /* Limit the number of virtual SPIs supported to (1020 - 32) = 988 */ >> if ( nr_spis > (1020 - NR_LOCAL_IRQS) ) >> return -EINVAL; >> @@ -145,7 +306,7 @@ int domain_vgic_init(struct domain *d, unsigned int >> nr_spis) >> return -ENOMEM; >> >> d->arch.vgic.pending_irqs = >> - xzalloc_array(struct pending_irq, d->arch.vgic.nr_spis); >> + xzalloc_array(struct pending_irq, vgic_num_spi_lines(d)); >> if ( d->arch.vgic.pending_irqs == NULL ) >> return -ENOMEM; >> >> @@ -156,12 +317,16 @@ int domain_vgic_init(struct domain *d, unsigned int >> nr_spis) >> for ( i = 0; i < DOMAIN_NR_RANKS(d); i++ ) >> vgic_rank_init(&d->arch.vgic.shared_irqs[i], i + 1, 0); >> >> + ret = init_vgic_espi(d); >> + if ( ret ) >> + return ret; >> + >> ret = d->arch.vgic.handler->domain_init(d); >> if ( ret ) >> return ret; >> >> d->arch.vgic.allocated_irqs = >> - xzalloc_array(unsigned long, BITS_TO_LONGS(vgic_num_irqs(d))); >> + xzalloc_array(unsigned long, BITS_TO_LONGS(vgic_num_alloc_irqs(d))); >> if ( !d->arch.vgic.allocated_irqs ) >> return -ENOMEM; >> >> @@ -195,9 +360,27 @@ void domain_vgic_free(struct domain *d) >> } >> } >> >> +#ifdef CONFIG_GICV3_ESPI >> + for ( i = 0; i < d->arch.vgic.nr_espis; i++ ) >> + { >> + struct pending_irq *p = spi_to_pending(d, ESPI_IDX2INTID(i)); >> + >> + if ( p->desc ) >> + { >> + ret = release_guest_irq(d, p->irq); >> + if ( ret ) >> + dprintk(XENLOG_G_WARNING, "%pd: Failed to release virq %u >> ret = %d\n", >> + d, p->irq, ret); >> + } >> + } >> +#endif >> + >> if ( d->arch.vgic.handler ) >> d->arch.vgic.handler->domain_free(d); >> xfree(d->arch.vgic.shared_irqs); >> +#ifdef CONFIG_GICV3_ESPI >> + xfree(d->arch.vgic.ext_shared_irqs); >> +#endif >> xfree(d->arch.vgic.pending_irqs); >> xfree(d->arch.vgic.allocated_irqs); >> } >> @@ -331,6 +514,8 @@ void arch_move_irqs(struct vcpu *v) >> if ( v_target == v && !test_bit(GIC_IRQ_GUEST_MIGRATING, >> &p->status) ) >> irq_set_affinity(p->desc, cpu_mask); >> } >> + >> + arch_move_espis(v); >> } >> >> void vgic_disable_irqs(struct vcpu *v, uint32_t r, unsigned int n) >> @@ -538,6 +723,8 @@ struct pending_irq *irq_to_pending(struct vcpu *v, >> unsigned int irq) >> n = &v->arch.vgic.pending_irqs[irq]; >> else if ( is_lpi(irq) ) >> n = v->domain->arch.vgic.handler->lpi_to_pending(v->domain, irq); >> + else if ( is_espi(irq) ) >> + n = espi_to_pending(v->domain, irq); >> else >> n = &v->domain->arch.vgic.pending_irqs[irq - 32]; >> return n; >> @@ -547,6 +734,9 @@ struct pending_irq *spi_to_pending(struct domain *d, >> unsigned int irq) >> { >> ASSERT(irq >= NR_LOCAL_IRQS); >> >> + if ( is_espi(irq) ) >> + return espi_to_pending(d, irq); >> + > > here you can just do > > idx = virq_to_idx(virq); > >> return &d->arch.vgic.pending_irqs[irq - 32]; > > and > > return &d->arch.vgic.pending_irqs[idx]; > > instead > >> } >> >> @@ -668,6 +858,9 @@ bool vgic_reserve_virq(struct domain *d, unsigned int >> virq) >> if ( !vgic_is_valid_line(d, virq) ) >> return false; >> >> + if ( is_espi(virq) ) >> + return vgic_reserve_espi_virq(d, virq); >> + > > here you can just do > > idx = virq_to_idx(virq) > >> return !test_and_set_bit(virq, d->arch.vgic.allocated_irqs); > > and then just > > return !test_and_set_bit(idx, d->arch.vgic.allocated_irqs); > > >> } >> >> @@ -685,7 +878,7 @@ int vgic_allocate_virq(struct domain *d, bool spi) >> else >> { >> first = 32; >> - end = vgic_num_irqs(d); >> + end = vgic_num_alloc_irqs(d); >> } > > I thinj you need to recalculate "virq" value at the end of this > function. You'll return index in bitfield, but this is not the same is > IRQ number in case of eSPIs. The helpers I mentioned before can help here. > Oh, I missed this. It definitely needs to be recalculated for eSPIs. I will add a fix for this in V6. >> >> /* > > Lastly, I think that it is very wasteful to allocate pending_irqs as > continuous array, because it will consist mostly of unused entries, > especially with eSPIs enable. Probably, better approach will be to use radix > tree. As a bonus, you can use IRQ line number as a key, and get rid of > all these is_espi() checks and mappings between IRQ number and index in > the array. But this is a much more drastic change, and I don't think that it > should be done in this patch series... > Yes, I agree with that. It can be improved, but first, I need to prepare the solution with dynamic allocation for IRQ descriptors, as I promised previously. Of course, after merging the current eSPI patch series. Best regards, Leonid