[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v12 03/13] xen/arm: gic-v3: tolerate retained redistributor LPI state across CPU_OFF


  • To: Mykola Kvach <mykola_kvach@xxxxxxxx>
  • From: Bertrand Marquis <Bertrand.Marquis@xxxxxxx>
  • Date: Wed, 23 Sep 2026 15:34:52 +0000
  • Accept-language: en-GB, en-US
  • Arc-authentication-results: i=2; mx.microsoft.com 1; spf=pass (sender ip is 4.158.2.129) smtp.rcpttodomain=epam.com smtp.mailfrom=arm.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=arm.com; dkim=pass (signature was verified) header.d=arm.com; arc=pass (0 oda=1 ltdi=1 spf=[1,1,smtp.mailfrom=arm.com] dkim=[1,1,header.d=arm.com] dmarc=[1,1,header.from=arm.com])
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=arm.com; dmarc=pass action=none header.from=arm.com; dkim=pass header.d=arm.com; arc=none
  • Arc-message-signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=gyaXTMpxlPJmtJCjn80suRr8TvenbaZ8yXgtRm2S5k8=; b=ZnbOJxNle0JMhnk0u0pivVMxzRKf74ukk9GhX+EuuUJcjLNM+JBqiY1yiDbRrCBh0c+L4yFwPnqgNfXsBq9DjHJjANUHVNRmv18VCcKbha0PCP1NoCTBqTZ3qsEfnrNgd8ObGF23XblkQ96N7avRIslCex7ZTJhBoDGGkmv/7BRvPgo37TPR6e8zmd4k8mFUGWRNYpLdOuOJ59xaXyygiXNglcvKqOMo9jWut9jwYmw1yjueOPnFnlv0q1f6UvPT2cRjpu2094gw8YuqJDG0lfyS7wbYG9QP9lWaVhRyFqQWPEjSqgNDHyf8E3CpG9DhORBrhHXsbyLI0fpVgJn7Og==
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=gyaXTMpxlPJmtJCjn80suRr8TvenbaZ8yXgtRm2S5k8=; b=wepTQW3qACki8wxIGeYryLJoB5VeX7MlhOiPtOf7MtqYUFfeQBGqMG6Qfnz2Qlo6ipN5nfsZzI1bYZKSNPf0/JPOiRdktgxXGpmYmtN7PaGjIlRsM+M8dLx2zlJskm7tvhswA8KhYBn3/GYbBuPNz+UVgX87sTP2ga0plBlfKGF/E46bnwB8RB3S2cG8ivTs56G+lEVulRfts3o1M4TedOkMAjH8t3NoFbEXzRVMqAS5r2Xq3vblckyL7zb3P65vHiiSTUhmHLCaIHgLDVl13XbgMQCpEss/RWPS/psy9Qk4m5kcb9KhpF91/l0NAz12UfscHXZem1jP9SUN8Bjm5w==
  • Arc-seal: i=2; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=pass; b=SNBFrmEeTbVn2m/sxaI0E6dPMvbNPIioFw+2PXzSwvVIbRoqh6i0Cim8C9GxXDXzbipi/zvtarhQtyRPBeQGfUtRA+m2uohlawyP53e9wpaYqSDSwWS8doQbKoPKUT1sxvkU0vDmEsKwu5rolK3SQxKLO8UwSVlchnli34k02zIcBu6XtCAibGL+E7r7h9M8e0dDKopb62p6jWyi4PQWa9JKEueenRHyYPZ4vLIl9vvVTioPP1j6rwd15T6vQExxTqEkQjw2miuBM+m5RbE2fXZlriBGGN1klXLTmD0EnYTV0LecgVpHmEaRmT67y2OeD/c3nEkTkiHXYT7qqG8hxA==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=v+SK44WlzRAwLf+H22Hd23+lDqa2S7ygRC/fJYN/tIltxCln8ps4LLA8xgcgsKU54d77uIIsb48kplp1o28y/D0A4VoKjon1wJ5B+Kd6PTSxcnF4xn2RQzXMO03QqZOnKKbMFPkEMdG9/Qleeo7OquldXEF0OBPc6UBjAioiXMh5lKtjpHhe0wRGSroqEFg54/mOtpyBD/Mo8TILtM7s+2hu/mcrT3SQUy+R8XdVHmMJwuh24VMUAEIzY8ATJBRCxDNlhUPDcND0r2YXdHAV9VNd4zN5yTIymbYIsWM7EJBXyJWi4mflv3mfEaUwmNJwFGbmfEzV6ynYsFPnW5TO0Q==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=arm.com header.i="@arm.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"; dkim=pass header.s=selector1 header.d=arm.com header.i="@arm.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results-original: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com;
  • Cc: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Luca Fancellu <Luca.Fancellu@xxxxxxx>
  • Delivery-date: Wed, 23 Sep 2026 15:35:48 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Nodisclaimer: true
  • Thread-index: AQHdNjDlOJD2OeV6HEaZdpj45JQE7rbcdbQA
  • Thread-topic: [PATCH v12 03/13] xen/arm: gic-v3: tolerate retained redistributor LPI state across CPU_OFF

Hi Mykola,

> On 27 Aug 2026, at 16:31, Mykola Kvach <mykola_kvach@xxxxxxxx> wrote:
> 
> PSCI does not guarantee that a GICv3 redistributor is powered down across
> CPU_OFF -> CPU_ON.
> 
> DEN0022F.b says CPU_OFF powers down the calling core (5.5) and CPU_ON
> brings the core back with a defined initial CPU state (5.6, 6.4).
> However, PSCI leaves interrupt migration and GIC re-initialization to the
> supervisory software/firmware stack: the caller must migrate interrupts
> away before CPU_OFF (5.5.2), and the execution context that is lost in a
> powerdown state must be saved and restored by software (6.8). PSCI also
> calls out GIC management explicitly in 6.8, including retargeting SPIs,
> preventing PPIs/SGIs from targeting a powered down CPU, and reinitializing
> the CPU interface after CPU_ON.
> 
> This matches the GIC architecture. IHI0069H.b Chapter 11.1 requires the PE
> and CPU interface to share a power domain, but explicitly allows the
> associated redistributor, distributor, and ITS to remain powered while the
> PE and CPU interface are off. All other GIC power-management behavior is
> IMPLEMENTATION DEFINED. DEN0050D Chapter 4.2, "Generic Interrupt
> Controller (GIC)", says the GICv3 redistributor may live either in the AP
> core power domain or in a relatively always-on parent domain. So after
> CPU_OFF -> CPU_ON a secondary CPU can legitimately come back to a live
> redistributor with GICR_CTLR.EnableLPIs still set.
> 
> Handle that case in the LPI setup path instead of assuming a fully reset
> redistributor.
> 
> The LPI path needs special care because the GIC spec makes redistributor
> LPI state sticky and partially implementation defined. IHI0069H.b 5.1.1
> and 5.1.2 say that changing GICR_PROPBASER or GICR_PENDBASER while
> GICR_CTLR.EnableLPIs == 1 is UNPREDICTABLE. After clearing EnableLPIs,
> software must wait for GICR_CTLR.RWP == 0 before touching the pending
> table. The architecture also permits implementations where, once
> EnableLPIs has been set, clearing it again is not guaranteed to work.
> Where an ITS is present, the spec strongly recommends moving LPIs to
> another redistributor before clearing EnableLPIs.
> 
> Because of that, treat a retained EnableLPIs state as valid when the
> redistributor still points at Xen's expected PROPBASER/PENDBASER tables.
> Only try to clear EnableLPIs when the retained configuration does not
> match Xen's state, and wait for RWP before reprogramming the tables.
> 
> This is also consistent with platform firmware reality: PSCI and the GIC
> architecture allow platform-specific redistributor power handling, and not
> all platform firmware implementations force a full redistributor power-off
> through implementation-defined controls during CPU_OFF. Xen therefore needs
> to tolerate retained redistributor state on secondary CPU bring-up.
> 
> Keep gicv3_populate_rdist() resident as well, because gicv3_cpu_init()
> reuses it on secondary CPU bring-up after init.
> 
> Tested using Xen's non-boot CPU disable/enable path on Arm
> FVP_Base_RevC-2xAEMvA, both with and without:
> -C gic_distributor.allow-LPIEN-clear=1
> -C gic_distributor.GICR-clear-enable-supported=1
> and on Orange Pi 5.
> 
> Signed-off-by: Mykola Kvach <mykola_kvach@xxxxxxxx>
> Reviewed-by: Luca Fancellu <luca.fancellu@xxxxxxx>
> ---
> Changes in v10:
> - Drop unrelated gicv3_populate_rdist() printk() format cleanups to keep
>  the patch focused on retained redistributor LPI state.
> 
> Changes in v9:
> - move gicv3_do_wait_for_rwp prototype from its related header to gic.h
> - drop __init from gicv3_populate_rdist(), which is reused on secondary
>  CPU bring-up after boot
> - changed print format for smp_processor_id in gicv3_populate_rdist func
> - cosmetic changes
> ---
> xen/arch/arm/gic-v3-lpi.c      | 77 +++++++++++++++++++++++++++++++++-
> xen/arch/arm/gic-v3.c          | 15 ++++---
> xen/arch/arm/include/asm/gic.h |  4 ++
> 3 files changed, 90 insertions(+), 6 deletions(-)
> 
> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index 9ee338edc2..847da26ff7 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -81,6 +81,13 @@ static DEFINE_PER_CPU(struct lpi_redist_data, lpi_redist);
> #define MAX_NR_HOST_LPIS   (lpi_data.max_host_lpi_ids - LPI_OFFSET)
> #define HOST_LPIS_PER_PAGE      (PAGE_SIZE / sizeof(union host_lpi))
> 
> +#define GICR_PROPBASER_XEN_MASK  GENMASK_ULL(51, 12)
> +/*
> + * For retained redistributor state, match the pending table by address only.
> + * Attribute bits such as PTZ may not read back with the programmed value.
> + */
> +#define GICR_PENDBASER_XEN_MASK  GENMASK_ULL(51, 16)
> +
> static union host_lpi *gic_get_host_lpi(uint32_t plpi)
> {
>     union host_lpi *block;
> @@ -296,6 +303,60 @@ static int gicv3_lpi_set_pendtable(void __iomem 
> *rdist_base)
>     return 0;
> }
> 
> +static uint64_t gicv3_lpi_expected_proptable(void)
> +{
> +    return virt_to_maddr(lpi_data.lpi_property);
> +}
> +
> +static uint64_t gicv3_lpi_expected_pendtable(void)
> +{
> +    return virt_to_maddr(this_cpu(lpi_redist).pending_table);
> +}
> +
> +static bool gicv3_lpi_tables_match(void __iomem *rdist_base)
> +{
> +    uint64_t propbase, pendbase;
> +
> +    if ( !lpi_data.lpi_property || !this_cpu(lpi_redist).pending_table )
> +        return false;
> +
> +    propbase = readq_relaxed(rdist_base + GICR_PROPBASER);
> +    pendbase = readq_relaxed(rdist_base + GICR_PENDBASER);
> +
> +    return ((propbase & GICR_PROPBASER_XEN_MASK) ==
> +            (gicv3_lpi_expected_proptable() & GICR_PROPBASER_XEN_MASK)) &&
> +           ((pendbase & GICR_PENDBASER_XEN_MASK) ==
> +            (gicv3_lpi_expected_pendtable() & GICR_PENDBASER_XEN_MASK));
> +}
> +
> +static int gicv3_lpi_disable_lpis(void __iomem *rdist_base)
> +{
> +    uint32_t reg = readl_relaxed(rdist_base + GICR_CTLR);
> +    int ret;
> +
> +    if ( !(reg & GICR_CTLR_ENABLE_LPIS) )
> +        return 0;
> +
> +    writel_relaxed(reg & ~GICR_CTLR_ENABLE_LPIS, rdist_base + GICR_CTLR);
> +
> +    /*
> +     * The spec only guarantees programmability when we have observed the bit
> +     * cleared. Where clearing is supported, RWP must reach 0 before touching
> +     * PROPBASER/PENDBASER again.
> +     */
> +    wmb();
> +
> +    ret = gicv3_do_wait_for_rwp(rdist_base, GICR_CTLR_RWP);
> +    if ( ret )
> +        return ret;
> +
> +    reg = readl_relaxed(rdist_base + GICR_CTLR);
> +    if ( reg & GICR_CTLR_ENABLE_LPIS )
> +        return -EBUSY;
> +
> +    return 0;
> +}
> +
> /*
>  * Tell a redistributor about the (shared) property table, allocating one
>  * if not already done.
> @@ -374,7 +435,21 @@ int gicv3_lpi_init_rdist(void __iomem * rdist_base)
>     /* Make sure LPIs are disabled before setting up the tables. */
>     reg = readl_relaxed(rdist_base + GICR_CTLR);
>     if ( reg & GICR_CTLR_ENABLE_LPIS )
> -        return -EBUSY;
> +    {
> +        if ( gicv3_lpi_tables_match(rdist_base) )
> +            return -EBUSY;

I am wondering if there is a corner case when a CPU is unplugged and then
plugged back in. free_percpu_area() eventually frees the per-CPU area
containing lpi_redist.pending_table, but not the table itself. On the next
cpu_up(), gicv3_lpi_allocate_pendtable() allocates a new table, and I cannot
find where the old one is freed.

If the redistributor kept EnableLPIs=1 and GICR_PENDBASER pointing to the
old table, wouldn't gicv3_lpi_tables_match() fail? 

What will happen if EnableLPIs cannot be cleared ? (i think this is something
possible in the hardware). 

Cheers
Bertrand

> +
> +        ret = gicv3_lpi_disable_lpis(rdist_base);
> +        if ( ret == -EBUSY )
> +        {
> +            printk(XENLOG_ERR
> +                   "GICv3: CPU%u: LPIs still enabled with unexpected 
> redistributor tables\n",
> +                   smp_processor_id());
> +            return -EINVAL;
> +        }
> +        if ( ret )
> +            return ret;
> +    }
> 
>     ret = gicv3_lpi_set_pendtable(rdist_base);
>     if ( ret )
> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
> index acdac22953..b16888ad84 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -275,7 +275,7 @@ static void gicv3_enable_sre(void)
> }
> 
> /* Wait for completion of a distributor/redistributor change */
> -static void gicv3_do_wait_for_rwp(void __iomem *base, uint32_t rwp_bit)
> +int gicv3_do_wait_for_rwp(void __iomem *base, uint32_t rwp_bit)
> {
>     uint32_t val;
>     bool timeout = false;
> @@ -299,17 +299,22 @@ static void gicv3_do_wait_for_rwp(void __iomem *base, 
> uint32_t rwp_bit)
>     } while ( 1 );
> 
>     if ( timeout )
> +    {
>         dprintk(XENLOG_ERR, "RWP timeout\n");
> +        return -ETIMEDOUT;
> +    }
> +
> +    return 0;
> }
> 
> static void gicv3_dist_wait_for_rwp(void)
> {
> -    gicv3_do_wait_for_rwp(GICD, GICD_CTLR_RWP);
> +    (void)gicv3_do_wait_for_rwp(GICD, GICD_CTLR_RWP);
> }
> 
> static void gicv3_redist_wait_for_rwp(void)
> {
> -    gicv3_do_wait_for_rwp(GICD_RDIST_BASE, GICR_CTLR_RWP);
> +    (void)gicv3_do_wait_for_rwp(GICD_RDIST_BASE, GICR_CTLR_RWP);
> }
> 
> static void gicv3_wait_for_rwp(int irq)
> @@ -863,7 +868,7 @@ static bool gicv3_enable_lpis(void)
>     return true;
> }
> 
> -static int __init gicv3_populate_rdist(void)
> +static int gicv3_populate_rdist(void)
> {
>     int i;
>     uint32_t aff;
> @@ -931,7 +936,7 @@ static int __init gicv3_populate_rdist(void)
>                     gicv3_set_redist_address(rdist_addr, procnum);
> 
>                     ret = gicv3_lpi_init_rdist(ptr);
> -                    if ( ret && ret != -ENODEV )
> +                    if ( ret && ret != -ENODEV && ret != -EBUSY )
>                     {
>                         printk("GICv3: CPU%d: Cannot initialize LPIs: %u\n",
>                                smp_processor_id(), ret);
> diff --git a/xen/arch/arm/include/asm/gic.h b/xen/arch/arm/include/asm/gic.h
> index 29bb9a89a4..68003ab116 100644
> --- a/xen/arch/arm/include/asm/gic.h
> +++ b/xen/arch/arm/include/asm/gic.h
> @@ -301,6 +301,10 @@ extern int gicv_setup(struct domain *d);
> extern void gic_save_state(struct vcpu *v);
> extern void gic_restore_state(struct vcpu *v);
> 
> +#ifdef CONFIG_GICV3
> +int gicv3_do_wait_for_rwp(void __iomem *base, uint32_t rwp_bit);
> +#endif
> +
> #ifdef CONFIG_SYSTEM_SUSPEND
> /* Suspend/resume */
> extern int gic_suspend(void);
> -- 
> 2.43.0
> 




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.