[PATCH v3] xen/domain: make shutdown state explicit

Mykola Kvach posted 1 patch 3 hours ago
Patches applied successfully (tree, apply log)
git fetch https://gitlab.com/xen-project/patchew/xen tags/patchew/a32975d6d5a565690ec9b9f3619a98c4362da95b.1780124807.git.mykola._5Fkvach@epam.com
xen/arch/x86/hvm/viridian/time.c |  2 +-
xen/arch/x86/mm.c                |  2 +-
xen/arch/x86/mm/hap/hap.c        |  2 +-
xen/arch/x86/mm/shadow/common.c  |  5 +++--
xen/arch/x86/mm/shadow/multi.c   | 12 +++++++-----
xen/common/domain.c              | 20 ++++++++++----------
xen/common/domctl.c              |  2 +-
xen/common/sched/core.c          |  2 +-
xen/drivers/passthrough/iommu.c  |  8 ++++----
xen/drivers/passthrough/pci.c    |  2 +-
xen/include/xen/sched.h          | 23 +++++++++++++++++++----
11 files changed, 49 insertions(+), 31 deletions(-)
[PATCH v3] xen/domain: make shutdown state explicit
Posted by Mykola Kvach 3 hours ago
From: Mykola Kvach <mykola_kvach@epam.com>

The shutdown flow currently uses is_shutting_down and is_shut_down to
represent the domain shutdown lifecycle. The two flags are not mutually
exclusive: after shutdown completion is_shutting_down remains set until
domain_resume() clears both flags.

Replace the two booleans with an enum domain_shutdown_state. Keep
domain_shutting_down() as the direct replacement for the old
is_shutting_down flag: it is true once shutdown has been initiated and
remains true after completion, until domain_resume(). Add
domain_shutdown_completed() for users that need the final completed
state.

This makes the state transition explicit while avoiding a semantic split
between "in progress" and "completed" at call sites where the old code
only cared that shutdown had started and had not yet been reset by
domain_resume().

Suggested-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Mykola Kvach <mykola_kvach@epam.com>
---
Changes in v3:
- Keep domain_shutting_down() as a direct replacement for
  is_shutting_down: true from shutdown start until domain_resume(),
  including after shutdown completion.
- Drop domain_in_shutdown_state().
- Make old is_shutting_down conversions mechanical again; use
  domain_shutdown_completed() only for old is_shut_down users.
- Keep domain_resume() returning void and leave callers unchanged.
- Drop resume-state validation and the related invalid-resume
  diagnostic, including the cached shutdown_state/shutdown_code values.
- Reword the commit message to avoid describing
  domain_shutting_down() as only the transient shutdown phase.

Changes in v2:
- Drop the shutdown reason restrictions from domain_resume(), so the
  validation remains compatible with the existing soft-reset flow.
- Use clearer helper naming for the three shutdown-state predicates.
- Document in the commit message how old is_shutting_down/is_shut_down
  users map to the new shutdown-state helpers.
- Fix label indentation noted during review.

Link to discussion:
  https://patchew.org/Xen/cover.1756392094.git.mykola._5Fkvach@epam.com/bb53d9911b00879c7b25f5258d0e3e48005671f9.1756392094.git.mykola._5Fkvach@epam.com/#a64cff9f-df5f-467b-a944-74e803c64ab9@suse.com
---
 xen/arch/x86/hvm/viridian/time.c |  2 +-
 xen/arch/x86/mm.c                |  2 +-
 xen/arch/x86/mm/hap/hap.c        |  2 +-
 xen/arch/x86/mm/shadow/common.c  |  5 +++--
 xen/arch/x86/mm/shadow/multi.c   | 12 +++++++-----
 xen/common/domain.c              | 20 ++++++++++----------
 xen/common/domctl.c              |  2 +-
 xen/common/sched/core.c          |  2 +-
 xen/drivers/passthrough/iommu.c  |  8 ++++----
 xen/drivers/passthrough/pci.c    |  2 +-
 xen/include/xen/sched.h          | 23 +++++++++++++++++++----
 11 files changed, 49 insertions(+), 31 deletions(-)

diff --git a/xen/arch/x86/hvm/viridian/time.c b/xen/arch/x86/hvm/viridian/time.c
index 9311858d63..cf57af263a 100644
--- a/xen/arch/x86/hvm/viridian/time.c
+++ b/xen/arch/x86/hvm/viridian/time.c
@@ -102,7 +102,7 @@ static void time_ref_count_thaw(const struct domain *d)
     struct viridian_domain *vd = d->arch.hvm.viridian;
     struct viridian_time_ref_count *trc = &vd->time_ref_count;
 
-    if ( d->is_shutting_down ||
+    if ( domain_shutting_down(d) ||
          test_and_set_bit(_TRC_running, &trc->flags) )
         return;
 
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index a158379e77..c8ce166d6e 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -1219,7 +1219,7 @@ void put_page_from_l1e(l1_pgentry_t l1e, struct domain *l1e_owner)
      */
 #if _PAGE_GNTTAB
     if ( (l1e_get_flags(l1e) & _PAGE_GNTTAB) &&
-         !l1e_owner->is_shutting_down && !l1e_owner->is_dying )
+         !domain_shutting_down(l1e_owner) && !l1e_owner->is_dying )
     {
         gprintk(XENLOG_WARNING,
                 "Attempt to implicitly unmap %pd's grant PTE %" PRIpte "\n",
diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c
index 5ccb80bda5..0ede4181a0 100644
--- a/xen/arch/x86/mm/hap/hap.c
+++ b/xen/arch/x86/mm/hap/hap.c
@@ -414,7 +414,7 @@ static mfn_t hap_make_monitor_table(struct vcpu *v)
 
  oom:
     if ( !d->is_dying &&
-         (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) )
+         (!domain_shutting_down(d) || d->shutdown_code != SHUTDOWN_crash) )
     {
         printk(XENLOG_G_ERR "%pd: out of memory building monitor pagetable\n",
                d);
diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c
index ed698fa90b..c956b4840b 100644
--- a/xen/arch/x86/mm/shadow/common.c
+++ b/xen/arch/x86/mm/shadow/common.c
@@ -164,7 +164,7 @@ void shadow_promote(struct domain *d, mfn_t gmfn, unsigned int type)
     /* We should never try to promote a gmfn that has writeable mappings */
     ASSERT((page->u.inuse.type_info & PGT_type_mask) != PGT_writable_page
            || (page->u.inuse.type_info & PGT_count_mask) == 0
-           || d->is_shutting_down);
+           || domain_shutting_down(d));
 
     /* Is the page already shadowed? */
     if ( !test_and_set_bit(_PGC_shadowed_pt, &page->count_info) )
@@ -442,7 +442,8 @@ bool shadow_prealloc(struct domain *d, unsigned int type, unsigned int count)
         count += paging_logdirty_levels();
 
     ret = _shadow_prealloc(d, count);
-    if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) )
+    if ( !ret && (!domain_shutting_down(d) ||
+                  d->shutdown_code != SHUTDOWN_crash) )
         /*
          * Failing to allocate memory required for shadow usage can only result in
          * a domain crash, do it here rather that relying on every caller to do it.
diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c
index 2990cee869..632a83a78e 100644
--- a/xen/arch/x86/mm/shadow/multi.c
+++ b/xen/arch/x86/mm/shadow/multi.c
@@ -2370,7 +2370,8 @@ static int cf_check sh_page_fault(
      * already used for some special purpose (ioreq pages, or granted pages).
      * If that happens we'll have killed the guest already but it's still not
      * safe to propagate entries out of the guest PT so get out now. */
-    if ( unlikely(d->is_shutting_down && d->shutdown_code == SHUTDOWN_crash) )
+    if ( unlikely(domain_shutting_down(d) &&
+                  d->shutdown_code == SHUTDOWN_crash) )
     {
         SHADOW_PRINTK("guest is shutting down\n");
         goto propagate;
@@ -2480,7 +2481,7 @@ static int cf_check sh_page_fault(
 #if GUEST_PAGING_LEVELS == 3
         sh_update_cr3(v, false);
 #else
-        ASSERT(d->is_shutting_down);
+        ASSERT(domain_shutting_down(d));
         sh_trace_va(TRC_SHADOW_DOMF_DYING, va);
 #endif
         paging_unlock(d);
@@ -2494,7 +2495,8 @@ static int cf_check sh_page_fault(
          && ft == ft_demand_write )
         sh_unsync(v, gmfn);
 
-    if ( unlikely(d->is_shutting_down && d->shutdown_code == SHUTDOWN_crash) )
+    if ( unlikely(domain_shutting_down(d) &&
+                  d->shutdown_code == SHUTDOWN_crash) )
     {
         /* We might end up with a crashed domain here if
          * sh_remove_shadows() in a previous sh_resync() call has
@@ -3265,7 +3267,7 @@ static pagetable_t cf_check sh_update_cr3(struct vcpu *v, bool noflush)
                                        sh_make_shadow);
     if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) )
     {
-        ASSERT(d->is_dying || d->is_shutting_down);
+        ASSERT(d->is_dying || domain_shutting_down(d));
         return old_entry;
     }
     if ( !paging_mode_external(d) && !is_pv_32bit_domain(d) )
@@ -3332,7 +3334,7 @@ static pagetable_t cf_check sh_update_cr3(struct vcpu *v, bool noflush)
     ASSERT(pagetable_is_null(old_entry));
     if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) )
     {
-        ASSERT(d->is_dying || d->is_shutting_down);
+        ASSERT(d->is_dying || domain_shutting_down(d));
         return old_entry;
     }
 #else
diff --git a/xen/common/domain.c b/xen/common/domain.c
index bb9e210c28..473b7f8e3f 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -191,7 +191,7 @@ static void set_domain_state_info(struct xen_domctl_get_domain_state *info,
                                   const struct domain *d)
 {
     info->state = XEN_DOMCTL_GETDOMSTATE_STATE_EXIST;
-    if ( d->is_shut_down )
+    if ( domain_shutdown_completed(d) )
         info->state |= XEN_DOMCTL_GETDOMSTATE_STATE_SHUTDOWN;
     if ( d->is_dying == DOMDYING_dying )
         info->state |= XEN_DOMCTL_GETDOMSTATE_STATE_DYING;
@@ -282,14 +282,14 @@ static void __domain_finalise_shutdown(struct domain *d)
 
     BUG_ON(!spin_is_locked(&d->shutdown_lock));
 
-    if ( d->is_shut_down )
+    if ( domain_shutdown_completed(d) )
         return;
 
     for_each_vcpu ( d, v )
         if ( !v->paused_for_shutdown )
             return;
 
-    d->is_shut_down = 1;
+    d->shutdown_state = DOMSHUTDOWN_complete;
     domain_changed_state(d);
     if ( (d->shutdown_code == SHUTDOWN_suspend) && d->suspend_evtchn )
         evtchn_send(d, d->suspend_evtchn);
@@ -303,7 +303,7 @@ static void vcpu_check_shutdown(struct vcpu *v)
 
     spin_lock(&d->shutdown_lock);
 
-    if ( d->is_shutting_down )
+    if ( domain_shutting_down(d) )
     {
         if ( !v->paused_for_shutdown )
             vcpu_pause_nosync(v);
@@ -1356,7 +1356,7 @@ int domain_kill(struct domain *d)
 
 void __domain_crash(struct domain *d)
 {
-    if ( d->is_shutting_down )
+    if ( domain_shutting_down(d) )
     {
         /* Print nothing: the domain is already shutting down. */
     }
@@ -1394,13 +1394,13 @@ int domain_shutdown(struct domain *d, u8 reason)
     if ( is_hardware_domain(d) )
         hwdom_shutdown(reason);
 
-    if ( d->is_shutting_down )
+    if ( domain_shutting_down(d) )
     {
         spin_unlock(&d->shutdown_lock);
         return 0;
     }
 
-    d->is_shutting_down = 1;
+    d->shutdown_state = DOMSHUTDOWN_in_progress;
 
     smp_mb(); /* set shutdown status /then/ check for per-cpu deferrals */
 
@@ -1435,7 +1435,7 @@ void domain_resume(struct domain *d)
 
     spin_lock(&d->shutdown_lock);
 
-    d->is_shutting_down = d->is_shut_down = 0;
+    d->shutdown_state = DOMSHUTDOWN_none;
 
     arch_domain_resume(d);
 
@@ -1460,7 +1460,7 @@ int vcpu_start_shutdown_deferral(struct vcpu *v)
 
     v->defer_shutdown = 1;
     smp_mb(); /* set deferral status /then/ check for shutdown */
-    if ( unlikely(v->domain->is_shutting_down) )
+    if ( unlikely(domain_shutting_down(v->domain)) )
         vcpu_check_shutdown(v);
 
     return v->defer_shutdown;
@@ -1470,7 +1470,7 @@ void vcpu_end_shutdown_deferral(struct vcpu *v)
 {
     v->defer_shutdown = 0;
     smp_mb(); /* clear deferral status /then/ check for shutdown */
-    if ( unlikely(v->domain->is_shutting_down) )
+    if ( unlikely(domain_shutting_down(v->domain)) )
         vcpu_check_shutdown(v);
 }
 
diff --git a/xen/common/domctl.c b/xen/common/domctl.c
index b969f5ada6..5594fc72d8 100644
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -86,7 +86,7 @@ void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info)
 
     info->flags = (info->nr_online_vcpus ? flags : 0) |
         ((d->is_dying == DOMDYING_dead) ? XEN_DOMINF_dying     : 0) |
-        (d->is_shut_down                ? XEN_DOMINF_shutdown  : 0) |
+        (domain_shutdown_completed(d)   ? XEN_DOMINF_shutdown  : 0) |
         (d->controller_pause_count > 0  ? XEN_DOMINF_paused    : 0) |
         (d->debugger_attached           ? XEN_DOMINF_debugged  : 0) |
         (is_xenstore_domain(d)          ? XEN_DOMINF_xs_domain : 0) |
diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c
index 8e2b75bc35..9ac00e4c0d 100644
--- a/xen/common/sched/core.c
+++ b/xen/common/sched/core.c
@@ -1539,7 +1539,7 @@ static void cf_check domain_watchdog_timeout(void *data)
 
     BUILD_BUG_ON(alignof(*d) < PAGE_SIZE);
 
-    if ( d->is_shutting_down || d->is_dying )
+    if ( domain_shutting_down(d) || d->is_dying )
         return;
 
     printk("Watchdog timer %u fired for %pd\n", id, d);
diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c
index c9425d6971..4b22ecfe65 100644
--- a/xen/drivers/passthrough/iommu.c
+++ b/xen/drivers/passthrough/iommu.c
@@ -355,7 +355,7 @@ long iommu_map(struct domain *d, dfn_t dfn0, mfn_t mfn0,
         if ( likely(!rc) )
             continue;
 
-        if ( !d->is_shutting_down && printk_ratelimit() )
+        if ( !domain_shutting_down(d) && printk_ratelimit() )
             printk(XENLOG_ERR
                    "d%d: IOMMU mapping dfn %"PRI_dfn" to mfn %"PRI_mfn" failed: %d\n",
                    d->domain_id, dfn_x(dfn), mfn_x(mfn), rc);
@@ -427,7 +427,7 @@ long iommu_unmap(struct domain *d, dfn_t dfn0, unsigned long page_count,
         if ( likely(!err) )
             continue;
 
-        if ( !d->is_shutting_down && printk_ratelimit() )
+        if ( !domain_shutting_down(d) && printk_ratelimit() )
             printk(XENLOG_ERR
                    "d%d: IOMMU unmapping dfn %"PRI_dfn" failed: %d\n",
                    d->domain_id, dfn_x(dfn), err);
@@ -492,7 +492,7 @@ int iommu_iotlb_flush(struct domain *d, dfn_t dfn, unsigned long page_count,
                     flush_flags);
     if ( unlikely(rc) )
     {
-        if ( !d->is_shutting_down && printk_ratelimit() )
+        if ( !domain_shutting_down(d) && printk_ratelimit() )
             printk(XENLOG_ERR
                    "d%d: IOMMU IOTLB flush failed: %d, dfn %"PRI_dfn", page count %lu flags %x\n",
                    d->domain_id, rc, dfn_x(dfn), page_count, flush_flags);
@@ -517,7 +517,7 @@ int iommu_iotlb_flush_all(struct domain *d, unsigned int flush_flags)
                     flush_flags | IOMMU_FLUSHF_all);
     if ( unlikely(rc) )
     {
-        if ( !d->is_shutting_down && printk_ratelimit() )
+        if ( !domain_shutting_down(d) && printk_ratelimit() )
             printk(XENLOG_ERR
                    "d%d: IOMMU IOTLB flush all failed: %d\n",
                    d->domain_id, rc);
diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
index 464bb0fee4..5dbee61900 100644
--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -1746,7 +1746,7 @@ void iommu_dev_iotlb_flush_timeout(struct domain *d, struct pci_dev *pdev)
 
     pdev->broken = true;
 
-    if ( !d->is_shutting_down && printk_ratelimit() )
+    if ( !domain_shutting_down(d) && printk_ratelimit() )
         printk(XENLOG_ERR "dom%d: ATS device %pp flush failed\n",
                d->domain_id, &pdev->sbdf);
     if ( !is_hardware_domain(d) )
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index 00db1da12f..22a0deb31f 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -222,7 +222,7 @@ struct vcpu
     bool             force_context_switch;
     /* Require shutdown to be deferred for some asynchronous operation? */
     bool             defer_shutdown;
-    /* VCPU is paused following shutdown request (d->is_shutting_down)? */
+    /* VCPU is paused following a domain shutdown request? */
     bool             paused_for_shutdown;
     /* VCPU need affinity restored */
     uint8_t          affinity_broken;
@@ -382,6 +382,12 @@ struct domain_console {
     char buf[256];
 };
 
+enum domain_shutdown_state {
+    DOMSHUTDOWN_none,
+    DOMSHUTDOWN_in_progress,
+    DOMSHUTDOWN_complete,
+};
+
 struct domain
 {
     domid_t          domain_id;
@@ -552,10 +558,9 @@ struct domain
     struct rangeset *iomem_caps;
     struct rangeset *irq_caps;
 
-    /* Guest has shut down (inc. reason code)? */
+    /* Guest shutdown state and associated reason code. */
     spinlock_t       shutdown_lock;
-    bool             is_shutting_down; /* in process of shutting down? */
-    bool             is_shut_down;     /* fully shut down? */
+    enum domain_shutdown_state shutdown_state;
 #define SHUTDOWN_CODE_INVALID ~0u
     unsigned int     shutdown_code;
 
@@ -674,6 +679,16 @@ struct domain
     unsigned int pending_scrub_index;
 } __aligned(PAGE_SIZE);
 
+static inline bool domain_shutting_down(const struct domain *d)
+{
+    return d->shutdown_state != DOMSHUTDOWN_none;
+}
+
+static inline bool domain_shutdown_completed(const struct domain *d)
+{
+    return d->shutdown_state == DOMSHUTDOWN_complete;
+}
+
 static inline struct page_list_head *page_to_list(
     struct domain *d, const struct page_info *pg)
 {
-- 
2.43.0