[PATCH 18/22] x86/mm: allow modifying per-CPU entries of remote page-tables

Roger Pau Monne posted 22 patches 1 month, 3 weeks ago
[PATCH 18/22] x86/mm: allow modifying per-CPU entries of remote page-tables
Posted by Roger Pau Monne 1 month, 3 weeks ago
Add support for modifying the per-CPU page-tables entries of remote CPUs, this
will be required in order to setup the page-tables of CPUs before bringing them
up.  A restriction is added so that remote page-tables can only be modified as
long as the remote CPU is not yet online.

Non functional change, as there's no user introduced that modifies remote
page-tables.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Can be merged with previous patch?
---
 xen/arch/x86/include/asm/mm.h | 15 ++++++++++
 xen/arch/x86/mm.c             | 55 ++++++++++++++++++++++++++---------
 2 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/xen/arch/x86/include/asm/mm.h b/xen/arch/x86/include/asm/mm.h
index 34407fb0af06..f883468b1a7c 100644
--- a/xen/arch/x86/include/asm/mm.h
+++ b/xen/arch/x86/include/asm/mm.h
@@ -637,4 +637,19 @@ void setup_perdomain_slot(const struct vcpu *v, root_pgentry_t *root_pgt);
 int allocate_perdomain_local_l3(unsigned int cpu);
 void free_perdomain_local_l3(unsigned int cpu);
 
+/* Specify the CPU idle root page-table to use for modifications. */
+int map_pages_to_xen_cpu(
+    unsigned long virt,
+    mfn_t mfn,
+    unsigned long nr_mfns,
+    unsigned int flags,
+    unsigned int cpu);
+int modify_xen_mappings_cpu(unsigned long s, unsigned long e, unsigned int nf,
+                            unsigned int cpu);
+static inline int destroy_xen_mappings_cpu(unsigned long s, unsigned long e,
+                                           unsigned int cpu)
+{
+    return modify_xen_mappings_cpu(s, e, _PAGE_NONE, cpu);
+}
+
 #endif /* __ASM_X86_MM_H__ */
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index c468b46a9d1b..faf2d42745d1 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -5021,9 +5021,8 @@ static DEFINE_SPINLOCK(map_pgdir_lock);
  * For virt_to_xen_lXe() functions, they take a linear address and return a
  * pointer to Xen's LX entry. Caller needs to unmap the pointer.
  */
-static l3_pgentry_t *virt_to_xen_l3e(unsigned long v)
+static l3_pgentry_t *virt_to_xen_l3e_cpu(unsigned long v, unsigned int cpu)
 {
-    unsigned int cpu = smp_processor_id();
     /* Called before idle_vcpu is populated, fallback to idle_pg_table. */
     root_pgentry_t *root_pgt = idle_vcpu[cpu] ?
         maddr_to_virt(idle_vcpu[cpu]->arch.cr3) : idle_pg_table;
@@ -5062,11 +5061,16 @@ static l3_pgentry_t *virt_to_xen_l3e(unsigned long v)
     return map_l3t_from_l4e(*pl4e) + l3_table_offset(v);
 }
 
-static l2_pgentry_t *virt_to_xen_l2e(unsigned long v)
+static l3_pgentry_t *virt_to_xen_l3e(unsigned long v)
+{
+    return virt_to_xen_l3e_cpu(v, smp_processor_id());
+}
+
+static l2_pgentry_t *virt_to_xen_l2e_cpu(unsigned long v, unsigned int cpu)
 {
     l3_pgentry_t *pl3e, l3e;
 
-    pl3e = virt_to_xen_l3e(v);
+    pl3e = virt_to_xen_l3e_cpu(v, cpu);
     if ( !pl3e )
         return NULL;
 
@@ -5100,11 +5104,11 @@ static l2_pgentry_t *virt_to_xen_l2e(unsigned long v)
     return map_l2t_from_l3e(l3e) + l2_table_offset(v);
 }
 
-static l1_pgentry_t *virt_to_xen_l1e(unsigned long v)
+static l1_pgentry_t *virt_to_xen_l1e_cpu(unsigned long v, unsigned int cpu)
 {
     l2_pgentry_t *pl2e, l2e;
 
-    pl2e = virt_to_xen_l2e(v);
+    pl2e = virt_to_xen_l2e_cpu(v, cpu);
     if ( !pl2e )
         return NULL;
 
@@ -5220,17 +5224,18 @@ mfn_t xen_map_to_mfn(unsigned long va)
     return ret;
 }
 
-int map_pages_to_xen(
+int map_pages_to_xen_cpu(
     unsigned long virt,
     mfn_t mfn,
     unsigned long nr_mfns,
-    unsigned int flags)
+    unsigned int flags,
+    unsigned int cpu)
 {
     bool global = virt < PERCPU_VIRT_START ||
                   virt >= PERCPU_VIRT_SLOT(PERCPU_SLOTS);
     bool locking = system_state > SYS_STATE_boot && global;
     const cpumask_t *flush_mask = global ? &cpu_online_map
-                                         : cpumask_of(smp_processor_id());
+                                         : cpumask_of(cpu);
     l3_pgentry_t *pl3e = NULL, ol3e;
     l2_pgentry_t *pl2e = NULL, ol2e;
     l1_pgentry_t *pl1e, ol1e;
@@ -5257,6 +5262,9 @@ int map_pages_to_xen(
            (virt + nr_mfns * PAGE_SIZE >= PERCPU_VIRT_START &&
             virt + nr_mfns * PAGE_SIZE <  PERCPU_VIRT_SLOT(PERCPU_SLOTS)));
 
+    /* Only allow modifying remote page-tables if the CPU is not online. */
+    ASSERT(cpu == smp_processor_id() || !cpu_online(cpu));
+
     L3T_INIT(current_l3page);
 
     while ( nr_mfns != 0 )
@@ -5266,7 +5274,7 @@ int map_pages_to_xen(
         UNMAP_DOMAIN_PAGE(pl3e);
         UNMAP_DOMAIN_PAGE(pl2e);
 
-        pl3e = virt_to_xen_l3e(virt);
+        pl3e = virt_to_xen_l3e_cpu(virt, cpu);
         if ( !pl3e )
             goto out;
 
@@ -5391,7 +5399,7 @@ int map_pages_to_xen(
             free_xen_pagetable(l2mfn);
         }
 
-        pl2e = virt_to_xen_l2e(virt);
+        pl2e = virt_to_xen_l2e_cpu(virt, cpu);
         if ( !pl2e )
             goto out;
 
@@ -5437,7 +5445,7 @@ int map_pages_to_xen(
             /* Normal page mapping. */
             if ( !(l2e_get_flags(*pl2e) & _PAGE_PRESENT) )
             {
-                pl1e = virt_to_xen_l1e(virt);
+                pl1e = virt_to_xen_l1e_cpu(virt, cpu);
                 if ( pl1e == NULL )
                     goto out;
             }
@@ -5623,6 +5631,16 @@ int map_pages_to_xen(
     return rc;
 }
 
+int map_pages_to_xen(
+    unsigned long virt,
+    mfn_t mfn,
+    unsigned long nr_mfns,
+    unsigned int flags)
+{
+    return map_pages_to_xen_cpu(virt, mfn, nr_mfns, flags, smp_processor_id());
+}
+
+
 int __init populate_pt_range(unsigned long virt, unsigned long nr_mfns)
 {
     return map_pages_to_xen(virt, INVALID_MFN, nr_mfns, MAP_SMALL_PAGES);
@@ -5640,7 +5658,8 @@ int __init populate_pt_range(unsigned long virt, unsigned long nr_mfns)
  *
  * It is an error to call with present flags over an unpopulated range.
  */
-int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf)
+int modify_xen_mappings_cpu(unsigned long s, unsigned long e, unsigned int nf,
+                            unsigned int cpu)
 {
     bool global = s < PERCPU_VIRT_START ||
                   s >= PERCPU_VIRT_SLOT(PERCPU_SLOTS);
@@ -5658,6 +5677,9 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf)
     ASSERT(global ||
            (e >= PERCPU_VIRT_START && e < PERCPU_VIRT_SLOT(PERCPU_SLOTS)));
 
+    /* Only allow modifying remote page-tables if the CPU is not online. */
+    ASSERT(cpu == smp_processor_id() || !cpu_online(cpu));
+
     /* Set of valid PTE bits which may be altered. */
 #define FLAGS_MASK (_PAGE_NX|_PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_RW|_PAGE_PRESENT)
     nf &= FLAGS_MASK;
@@ -5674,7 +5696,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf)
         UNMAP_DOMAIN_PAGE(pl2e);
         UNMAP_DOMAIN_PAGE(pl3e);
 
-        pl3e = virt_to_xen_l3e(v);
+        pl3e = virt_to_xen_l3e_cpu(v, cpu);
         if ( !pl3e )
             goto out;
 
@@ -5927,6 +5949,11 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf)
 
 #undef flush_area
 
+int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf)
+{
+    return modify_xen_mappings_cpu(s, e, nf, smp_processor_id());
+}
+
 int destroy_xen_mappings(unsigned long s, unsigned long e)
 {
     return modify_xen_mappings(s, e, _PAGE_NONE);
-- 
2.45.2