82709f7
From: Jan Beulich <jbeulich@suse.com>
82709f7
Subject: x86: don't wrongly trigger linear page table assertion
82709f7
82709f7
_put_page_type() may do multiple iterations until its cmpxchg()
82709f7
succeeds. It invokes set_tlbflush_timestamp() on the first
82709f7
iteration, however. Code inside the function takes care of this, but
82709f7
- the assertion in _put_final_page_type() would trigger on the second
82709f7
  iteration if time stamps in a debug build are permitted to be
82709f7
  sufficiently much wider than the default 6 bits (see WRAP_MASK in
82709f7
  flushtlb.c),
82709f7
- it returning -EINTR (for a continuation to be scheduled) would leave
82709f7
  the page inconsistent state (until the re-invocation completes).
82709f7
Make the set_tlbflush_timestamp() invocation conditional, bypassing it
82709f7
(for now) only in the case we really can't tolerate the stamp to be
82709f7
stored.
82709f7
82709f7
This is part of XSA-240.
82709f7
82709f7
Signed-off-by: Jan Beulich <jbeulich@suse.com>
82709f7
Reviewed-by: George Dunlap <george.dunlap@citrix.com>
82709f7
82709f7
--- a/xen/arch/x86/mm.c
82709f7
+++ b/xen/arch/x86/mm.c
82709f7
@@ -2613,30 +2613,22 @@ static int _put_page_type(struct page_in
82709f7
                 break;
82709f7
             }
82709f7
 
82709f7
-            if ( ptpg && PGT_type_equal(x, ptpg->u.inuse.type_info) )
82709f7
-            {
82709f7
-                /*
82709f7
-                 * set_tlbflush_timestamp() accesses the same union
82709f7
-                 * linear_pt_count lives in. Unvalidated page table pages,
82709f7
-                 * however, should occur during domain destruction only
82709f7
-                 * anyway.  Updating of linear_pt_count luckily is not
82709f7
-                 * necessary anymore for a dying domain.
82709f7
-                 */
82709f7
-                ASSERT(page_get_owner(page)->is_dying);
82709f7
-                ASSERT(page->linear_pt_count < 0);
82709f7
-                ASSERT(ptpg->linear_pt_count > 0);
82709f7
-                ptpg = NULL;
82709f7
-            }
82709f7
-
82709f7
             /*
82709f7
              * Record TLB information for flush later. We do not stamp page
82709f7
              * tables when running in shadow mode:
82709f7
              *  1. Pointless, since it's the shadow pt's which must be tracked.
82709f7
              *  2. Shadow mode reuses this field for shadowed page tables to
82709f7
              *     store flags info -- we don't want to conflict with that.
82709f7
+             * Also page_set_tlbflush_timestamp() accesses the same union
82709f7
+             * linear_pt_count lives in. Pages (including page table ones),
82709f7
+             * however, don't need their flush time stamp set except when
82709f7
+             * the last reference is being dropped. For page table pages
82709f7
+             * this happens in _put_final_page_type().
82709f7
              */
82709f7
-            if ( !(shadow_mode_enabled(page_get_owner(page)) &&
82709f7
-                   (page->count_info & PGC_page_table)) )
82709f7
+            if ( ptpg && PGT_type_equal(x, ptpg->u.inuse.type_info) )
82709f7
+                BUG_ON(!IS_ENABLED(CONFIG_PV_LINEAR_PT));
82709f7
+            else if ( !(shadow_mode_enabled(page_get_owner(page)) &&
82709f7
+                        (page->count_info & PGC_page_table)) )
82709f7
                 page_set_tlbflush_timestamp(page);
82709f7
         }
82709f7
         else if ( unlikely((nx & (PGT_locked | PGT_count_mask)) ==