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

[Xen-devel] [PATCH RFC 17/44] x86/smp: Infrastructure for allocating and freeing percpu pagetables



Pagetables are allocated and freed along with the other smp datastructures,
and the root of the pagetables is stored in the percpu_mappings variable.

Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
 xen/arch/x86/smpboot.c     | 91 ++++++++++++++++++++++++++++++++++++++++++++++
 xen/include/asm-x86/page.h |  1 +
 xen/include/asm-x86/smp.h  |  1 +
 3 files changed, 93 insertions(+)

diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c
index a855301..1f92831 100644
--- a/xen/arch/x86/smpboot.c
+++ b/xen/arch/x86/smpboot.c
@@ -58,6 +58,7 @@
 unsigned long __read_mostly trampoline_phys;
 
 DEFINE_PER_CPU_READ_MOSTLY(paddr_t, percpu_idle_pt);
+DEFINE_PER_CPU_READ_MOSTLY(l4_pgentry_t, percpu_mappings);
 
 /* representing HT siblings of each logical CPU */
 DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_mask);
@@ -644,6 +645,7 @@ static int cpu_smpboot_alloc_common(unsigned int cpu)
     unsigned int memflags = 0;
     nodeid_t node = cpu_to_node(cpu);
     l4_pgentry_t *l4t = NULL;
+    l3_pgentry_t *l3t = NULL;
     struct page_info *pg;
     int rc = -ENOMEM;
 
@@ -663,15 +665,103 @@ static int cpu_smpboot_alloc_common(unsigned int cpu)
     if ( rc )
         goto out;
 
+    rc = -ENOMEM;
+
+    /* Percpu L3 table, containing the percpu mappings. */
+    pg = alloc_domheap_page(NULL, memflags);
+    if ( !pg )
+        goto out;
+    l3t = __map_domain_page(pg);
+    clear_page(l3t);
+    per_cpu(percpu_mappings, cpu) = l4t[l4_table_offset(PERCPU_LINEAR_START)] =
+        l4e_from_page(pg, __PAGE_HYPERVISOR);
+
     rc = 0; /* Success */
 
  out:
+    if ( l3t )
+        unmap_domain_page(l3t);
     if ( l4t )
         unmap_domain_page(l4t);
 
     return rc;
 }
 
+/*
+ * Dismantles the pagetable structure under per_cpu(percpu_mappings, cpu),
+ * freeing all pagetable frames, and any RAM frames which are mapped with
+ * MAP_PERCPU_AUTOFREE.
+ */
+static void free_perpcpu_pagetables(unsigned int cpu)
+{
+    l4_pgentry_t *percpu_mappings = &per_cpu(percpu_mappings, cpu);
+    unsigned int l3i;
+    l3_pgentry_t *l3t = NULL;
+
+    if ( !l4e_get_intpte(*percpu_mappings) )
+        return;
+
+    l3t = map_domain_page(l4e_get_mfn(*percpu_mappings));
+
+    for ( l3i = 0; l3i < L3_PAGETABLE_ENTRIES; ++l3i )
+    {
+        l3_pgentry_t l3e = l3t[l3i];
+
+        if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) )
+            continue;
+
+        if ( !(l3e_get_flags(l3e) & _PAGE_PSE) )
+        {
+            unsigned int l2i;
+            l2_pgentry_t *l2t = __map_domain_page(l3e_get_page(l3e));
+
+            for ( l2i = 0; l2i < L2_PAGETABLE_ENTRIES; ++l2i )
+            {
+                l2_pgentry_t l2e = l2t[l2i];
+
+                if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) )
+                    continue;
+
+                if ( !(l2e_get_flags(l2e) & _PAGE_PSE) )
+                {
+                    unsigned int l1i;
+                    l1_pgentry_t *l1t = __map_domain_page(l2e_get_page(l2e));
+
+                    for ( l1i = 0; l1i < L1_PAGETABLE_ENTRIES; ++l1i )
+                    {
+                        l1_pgentry_t l1e = l1t[l1i];
+
+                        if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) )
+                            continue;
+
+                        if ( l1e_get_flags(l1e) & MAP_PERCPU_AUTOFREE )
+                        {
+                            struct page_info *pg = l1e_get_page(l1e);
+
+                            if ( is_xen_heap_page(pg) )
+                                free_xenheap_page(page_to_virt(pg));
+                            else
+                                free_domheap_page(pg);
+                        }
+                    }
+
+                    unmap_domain_page(l1t);
+                }
+
+                free_domheap_page(l2e_get_page(l2e));
+            }
+
+            unmap_domain_page(l2t);
+        }
+
+        free_domheap_page(l3e_get_page(l3e));
+    }
+
+    unmap_domain_page(l3t);
+    free_domheap_page(l4e_get_page(*percpu_mappings));
+    *percpu_mappings = l4e_empty();
+}
+
 static void cpu_smpboot_free(unsigned int cpu)
 {
     unsigned int order, socket = cpu_to_socket(cpu);
@@ -733,6 +823,7 @@ static void cpu_smpboot_free(unsigned int cpu)
     }
 
     pt_shadow_free(cpu);
+    free_perpcpu_pagetables(cpu);
 }
 
 static int cpu_smpboot_alloc(unsigned int cpu)
diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h
index 45ca742..f330c75 100644
--- a/xen/include/asm-x86/page.h
+++ b/xen/include/asm-x86/page.h
@@ -344,6 +344,7 @@ void efi_update_l4_pgtable(unsigned int l4idx, 
l4_pgentry_t);
 #define __PAGE_HYPERVISOR_UC      (__PAGE_HYPERVISOR | _PAGE_PCD | _PAGE_PWT)
 
 #define MAP_SMALL_PAGES _PAGE_AVAIL0 /* don't use superpages mappings */
+#define MAP_PERCPU_AUTOFREE _PAGE_AVAIL1
 
 #ifndef __ASSEMBLY__
 
diff --git a/xen/include/asm-x86/smp.h b/xen/include/asm-x86/smp.h
index 5fea27d..46bbf0d 100644
--- a/xen/include/asm-x86/smp.h
+++ b/xen/include/asm-x86/smp.h
@@ -20,6 +20,7 @@
 #ifndef __ASSEMBLY__
 
 DECLARE_PER_CPU(paddr_t, percpu_idle_pt);
+DECLARE_PER_CPU(l4_pgentry_t, percpu_mappings);
 
 /*
  * Private routines/data
-- 
2.1.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

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