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

Re: [PATCH v8 13/13] xen/arm: Add support for system suspend triggered by hardware domain


  • To: Mykola Kvach <xakep.amatop@xxxxxxxxx>
  • From: Luca Fancellu <Luca.Fancellu@xxxxxxx>
  • Date: Wed, 29 Apr 2026 08:05:34 +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=gmail.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=Pr8Pm8/NKa+24lezy+EKDImorLwg1JXEhQkUdafYVug=; b=QSxYhG8+zt3YisB7Q7DFycVQRgfaI2S2Hjszah7J7S+1+7kmmCoMWqcD0PnlHCkJU/u90qBq3kNK7SX+UxFeJPjsIzGS55mWlbg8y44YtqWL5xY4ky0cjnWFQYl7anH34k2VfS3M2aZSoiqQDAFdU8EwrBEpanYAIJ3FWLca02hhUQioQrYc3X6vGDgTd3uxiMM2dft1lEi5TT+1+vZ041+DLusWqGo1rwR/d0TxAI9VLem9gsb5jRDr8cISWtPqVfiXaWE8matFfCuXkuZWYGmbyUfmUCslEa/Q90rrAaix0aQafYT//C7GmyhBk5DcZIo/mb8iQV2O3TwAsLOqbA==
  • 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=Pr8Pm8/NKa+24lezy+EKDImorLwg1JXEhQkUdafYVug=; b=fJRddfdfWXEy3OU+CKmkDGTVWhQlmiVQGaBmpGNk1y21dtG0K/VeUFo3sfnvCHxlbzkndcX4bQ0HJBNSYDMWqWxYWWhr6xU9NCqLeuoKYAuTGPNfebZpkcg5ZPPxFbQn33iMPUQ+aMMq/tnacm2oBP3vI2fcr+iteW0qRJUueZSY0doHKWtgt9A1mGplgYx4YjllZcjvAN8zKI/H7WJIDdI7Qsw3gvcSejtnlh3dhmLScRFaCeTqs5mXRCZH/i/lkq/TNe3Dfrdkmv3MTn1y9DK3x3x53UkgaM2NDVQ7ybc9IQ8rrGLZUTU65se32iJD8viRsy7cPwl3qkmroW0C6g==
  • Arc-seal: i=2; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=pass; b=LxBaWzzLsJ5grleqCDinsOiBxEBa6QXR6HZAUTbwdFJ2liFeGXQ+XpRm73gJmGIW1NPNOgB2yHFTKT/HIRxpmy5bwNM2R8f3SblbkmTJhcC3sI3djHgr8lKUeBpR9k7eqW9D5vNh8ngf2KV1jvdN+bYXpzv9aLPyrYofI9bpiJFjlOGQpAC7spXfNAuYuCamCndw1teuzWk9x03R5TS1tBytrCwk5iqkSjQ1QaTvSFPV+ks57uLqrCSlzDU+K2mr2aJv623lWwXp3WNFwQseY9y3w8VRt5qfFKpFWwkzBfrxpzWjloS7JUakudG5ygRoSu9xbCHgUVUHsZ+Mvz8cAQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=aa6qgi0p86R74p/+Kc6an/NB46yEiZ2gR9OSOTFcIaw8l4uOJUPT7chB9nELfmORrckEgRbpQYlIvD7XP/90VhQFALdBTTPfuOqUmFaLfPTnHgtMDFDI1qUDje7jSXs3X1WKiI+CshIXss8FdV+8Tmq9/KXTkgae5JL6dhXAOEfAXU12/4VmbZ2qcEvj2KOINLYbrJYnthiR9Rw7UiKM1Jwrg/mk9lCzBBQQI7uqt0evoych+YZwUJGzFqzu1uP9UOFLHv0aYFv+fe3/Mo/BORzXIP5+vl2PN29dtrMBGRHv0hVqRIwQEXIJpB41wvdKaXdjjCCQNJA8UuBCh4ErXQ==
  • 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: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com;
  • Cc: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>, Mykola Kvach <mykola_kvach@xxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Bertrand Marquis <Bertrand.Marquis@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Anthony PERARD <anthony.perard@xxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Roger Pau Monné <roger.pau@xxxxxxxxxx>, Rahul Singh <Rahul.Singh@xxxxxxx>
  • Delivery-date: Wed, 29 Apr 2026 08:07:01 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Nodisclaimer: true
  • Thread-index: AQHc167zncnUxfR+NEuj/SeXUCIftA==
  • Thread-topic: [PATCH v8 13/13] xen/arm: Add support for system suspend triggered by hardware domain

Hi Mykola,

> diff --git a/xen/arch/arm/suspend.c b/xen/arch/arm/suspend.c
> index e38566b0b7..4d1289776b 100644
> --- a/xen/arch/arm/suspend.c
> +++ b/xen/arch/arm/suspend.c
> @@ -1,9 +1,190 @@
> /* SPDX-License-Identifier: GPL-2.0-only */
> 
> +#include <asm/psci.h>
> #include <asm/suspend.h>
> 
> +#include <public/sched.h>
> +#include <xen/console.h>
> +#include <xen/cpu.h>
> +#include <xen/errno.h>
> +#include <xen/iommu.h>
> +#include <xen/sched.h>
> +#include <xen/tasklet.h>
> +
> struct cpu_context cpu_context = {};
> 
> +static int can_system_suspend(void)
> +{
> +    int ret = 0;
> +    struct domain *d;
> +
> +    rcu_read_lock(&domlist_read_lock);
> +
> +    for_each_domain ( d )
> +    {
> +        bool domain_suspended;
> +
> +        spin_lock(&d->shutdown_lock);
> +        domain_suspended = d->is_shut_down &&
> +                           d->shutdown_code == SHUTDOWN_suspend;
> +        spin_unlock(&d->shutdown_lock);
> +
> +        if ( domain_suspended )
> +            continue;
> +
> +        printk(XENLOG_ERR
> +               "System suspend requires all domains to be shut down for 
> suspend (dom%d: isn't in suspend state)\n",

d->domain_id is unsigned if I’m not mistaken, it wants %u (typedef uint16_t 
domid_t;)

> +               d->domain_id);
> +
> +        ret = -EBUSY;
> +        break;
> +    }
> +
> +    rcu_read_unlock(&domlist_read_lock);
> +
> +    return ret;
> +}
> +
> +/* Xen suspend. data identifies the domain that initiated suspend. */
> +static void system_suspend(void *data)
> +{
> +    int status;
> +    unsigned long flags;
> +    struct domain *d = (struct domain *)data;
> +
> +    BUG_ON(system_state != SYS_STATE_active);
> +
> +    system_state = SYS_STATE_suspend;
> +
> +    printk("Xen suspending...\n");
> +
> +    freeze_domains();
> +    scheduler_disable();
> +
> +    status = can_system_suspend();
> +    if ( status )
> +    {
> +        system_state = SYS_STATE_resume;
> +        goto resume_scheduler;

When we have an error and we get the resume_scheduler path, we apply back the
context of the guest saved previously in do_psci_1_0_system_suspend(), so am I
correct saying the guest won’t get any PSCI error back and we resume the guest
from the guest resume entrypoint?

In case, should we have a different path that returns a PSCI error (PSCI_*) 
into the guest
x0, and skips the context restore?

> +    }
> +
> +    /*
> +     * Non-boot CPUs have to be disabled on suspend and enabled on resume
> +     * (hotplug-based mechanism). Disabling non-boot CPUs will lead to PSCI
> +     * CPU_OFF to be called by each non-boot CPU. Depending on the underlying
> +     * platform capabilities, this may lead to the physical powering down of
> +     * CPUs.
> +     */
> +    status = disable_nonboot_cpus();
> +    if ( status )
> +    {
> +        system_state = SYS_STATE_resume;
> +        goto resume_nonboot_cpus;
> +    }
> +
> +    time_suspend();
> +
> +    status = iommu_suspend();
> +    if ( status )
> +    {
> +        system_state = SYS_STATE_resume;
> +        goto resume_time;
> +    }
> +
> +    console_start_sync();
> +    status = console_suspend();
> +    if ( status )
> +    {
> +        dprintk(XENLOG_ERR, "Failed to suspend the console, err=%d\n", 
> status);
> +        system_state = SYS_STATE_resume;
> +        goto resume_end_sync;
> +    }
> +
> +    local_irq_save(flags);
> +    status = gic_suspend();
> +    if ( status )
> +    {
> +        system_state = SYS_STATE_resume;
> +        goto resume_irqs;
> +    }
> +
> +    set_init_ttbr(xen_pgtable);
> +
> +    /*
> +     * Enable identity mapping before entering suspend to simplify
> +     * the resume path
> +     */
> +    update_boot_mapping(true);
> +
> +    if ( prepare_resume_ctx(&cpu_context) )
> +    {
> +        status = call_psci_system_suspend();
> +        /*
> +         * If suspend is finalized properly by above system suspend PSCI 
> call,
> +         * the code below in this 'if' branch will never execute. Execution
> +         * will continue from hyp_resume which is the hypervisor's resume 
> point.
> +         * In hyp_resume CPU context will be restored and since 
> link-register is
> +         * restored as well, it will appear to return from 
> prepare_resume_ctx.
> +         * The difference in returning from prepare_resume_ctx on system 
> suspend
> +         * versus resume is in function's return value: on suspend, the 
> return
> +         * value is a non-zero value, on resume it is zero. That is why the
> +         * control flow will not re-enter this 'if' branch on resume.
> +         */
> +        if ( status )
> +            dprintk(XENLOG_WARNING, "PSCI system suspend failed, err=%d\n",
> +                    status);
> +    }
> +
> +    system_state = SYS_STATE_resume;
> +    update_boot_mapping(false);
> +
> +    gic_resume();
> +
> + resume_irqs:
> +    local_irq_restore(flags);
> +
> +    console_resume();
> + resume_end_sync:
> +    console_end_sync();
> +
> +    iommu_resume();
> +
> + resume_time:
> +    time_resume();
> +
> + resume_nonboot_cpus:
> +    /*
> +     * The rcu_barrier() has to be added to ensure that the per cpu area is
> +     * freed before a non-boot CPU tries to initialize it 
> (_free_percpu_area()
> +     * has to be called before the init_percpu_area()). This scenario occurs
> +     * when non-boot CPUs are hot-unplugged on suspend and hotplugged on 
> resume.
> +     */
> +    rcu_barrier();
> +    enable_nonboot_cpus();
> +
> + resume_scheduler:
> +    scheduler_enable();
> +    thaw_domains();
> +
> +    system_state = SYS_STATE_active;
> +
> +    printk("Resume (status %d)\n", status);
> +
> +    domain_resume(d);
> +}
> +
> +static DECLARE_TASKLET(system_suspend_tasklet, system_suspend, NULL);
> +
> +void host_system_suspend(struct domain *d)
> +{
> +    system_suspend_tasklet.data = (void *)d;
> +    /*
> +     * The suspend procedure has to be finalized by the pCPU#0 (non-boot 
> pCPUs
> +     * will be disabled during the suspend).
> +     */
> +    tasklet_schedule_on_cpu(&system_suspend_tasklet, 0);
> +}
> +
> /*
>  * Local variables:
>  * mode: C
> diff --git a/xen/arch/arm/vpsci.c b/xen/arch/arm/vpsci.c
> index bd87ec430d..8fb9172186 100644
> --- a/xen/arch/arm/vpsci.c
> +++ b/xen/arch/arm/vpsci.c
> @@ -5,6 +5,7 @@
> 
> #include <asm/current.h>
> #include <asm/domain.h>
> +#include <asm/suspend.h>
> #include <asm/vgic.h>
> #include <asm/vpsci.h>
> #include <asm/event.h>
> @@ -232,8 +233,7 @@ static int32_t do_psci_1_0_system_suspend(register_t 
> epoint, register_t cid)
>     if ( is_64bit_domain(d) && is_thumb )
>         return PSCI_INVALID_ADDRESS;
> 
> -    /* SYSTEM_SUSPEND is not supported for the hardware domain yet */
> -    if ( is_hardware_domain(d) )
> +    if ( !IS_ENABLED(CONFIG_SYSTEM_SUSPEND) && is_hardware_domain(d) )
>         return PSCI_NOT_SUPPORTED;
> 
>     /* Ensure that all CPUs other than the calling one are offline */
> @@ -266,6 +266,9 @@ static int32_t do_psci_1_0_system_suspend(register_t 
> epoint, register_t cid)
>             "SYSTEM_SUSPEND requested, epoint=%#"PRIregister", 
> cid=%#"PRIregister"\n",
>             epoint, cid);
> 
> +    if ( is_control_domain(d) )

Why is_control_domain() here and not is_hardware_domain() ?

> +        host_system_suspend(d);
> +
>     return rc;
> }
> 
> @@ -290,7 +293,10 @@ static int32_t do_psci_1_0_features(uint32_t 
> psci_func_id)
>         return 0;
>     case PSCI_1_0_FN32_SYSTEM_SUSPEND:
>     case PSCI_1_0_FN64_SYSTEM_SUSPEND:
> -        return is_hardware_domain(current->domain) ? PSCI_NOT_SUPPORTED : 0;
> +        if ( IS_ENABLED(CONFIG_SYSTEM_SUSPEND) ||
> +             !is_hardware_domain(current->domain) )

Should this have also the condition that “is hardware domain and psci_ver >= 
PSCI_VERSION(1, 0)”?
Otherwise if the host machine doestn’t support PSCI 1.0 we would return OK here 
but the call would
fail later in call_psci_system_suspend()? 

> +            return 0;
> +        fallthrough;
>     default:
>         return PSCI_NOT_SUPPORTED;
>     }
> diff --git a/xen/common/Kconfig b/xen/common/Kconfig
> index 0a20aa0a12..feb1336f46 100644
> --- a/xen/common/Kconfig
> +++ b/xen/common/Kconfig
> @@ -137,6 +137,9 @@ config HAS_EX_TABLE
> config HAS_FAST_MULTIPLY
> bool
> 
> +config HAS_HWDOM_SYSTEM_SUSPEND
> + bool
> +
> config HAS_IOPORTS
> bool
> 
> diff --git a/xen/common/domain.c b/xen/common/domain.c
> index bb9e210c28..d3edfb2a13 100644
> --- a/xen/common/domain.c
> +++ b/xen/common/domain.c
> @@ -1375,6 +1375,11 @@ void __domain_crash(struct domain *d)
>     domain_shutdown(d, SHUTDOWN_crash);
> }
> 
> +static inline bool want_hwdom_shutdown(uint8_t reason)
> +{
> +    return !IS_ENABLED(CONFIG_HAS_HWDOM_SYSTEM_SUSPEND) ||
> +           reason != SHUTDOWN_suspend;
> +}
> 
> int domain_shutdown(struct domain *d, u8 reason)
> {
> @@ -1391,7 +1396,7 @@ int domain_shutdown(struct domain *d, u8 reason)
>         d->shutdown_code = reason;
>     reason = d->shutdown_code;
> 
> -    if ( is_hardware_domain(d) )
> +    if ( is_hardware_domain(d) && want_hwdom_shutdown(reason) )
>         hwdom_shutdown(reason);
> 
>     if ( d->is_shutting_down )
> diff --git a/xen/drivers/passthrough/arm/smmu.c 
> b/xen/drivers/passthrough/arm/smmu.c
> index 22d306d0cb..45f29ef8ec 100644
> --- a/xen/drivers/passthrough/arm/smmu.c
> +++ b/xen/drivers/passthrough/arm/smmu.c
> @@ -2947,6 +2947,13 @@ static void arm_smmu_iommu_domain_teardown(struct 
> domain *d)
> xfree(xen_domain);
> }
> 
> +#ifdef CONFIG_SYSTEM_SUSPEND
> +static int arm_smmu_suspend(void)
> +{
> + return -ENOSYS;
> +}
> +#endif

Maybe we want to gate the feature also to !CONFIG_ARM_SMMU ? I would wait for 
the maintainers
view on this.

Cheers,
Luca



 


Rackspace

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