After the page ranges in the pagetable have been reserved the pagetable
is no longer needed.  Rather than free it during early boot by unreserving
page-sized blocks which can be inefficient when dealing with a large number
of blocks, wait until the page structs have been initialized and free them
as pages.

Signed-off-by: Anthony Yznaga <[email protected]>
---
 arch/x86/mm/init_64.c |  1 +
 include/linux/pkram.h |  3 ++
 mm/pkram.c            | 11 +++++++
 mm/pkram_pagetable.c  | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 97 insertions(+)

diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index ae569ef6bd7d..72662615977b 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -1245,6 +1245,7 @@ void __init mem_init(void)
        after_bootmem = 1;
        x86_init.hyper.init_after_bootmem();
 
+       pkram_free_pgt();
        totalram_pages_add(pkram_reserved_pages);
        /*
         * Must be done after boot memory is put on freelist, because here we
diff --git a/include/linux/pkram.h b/include/linux/pkram.h
index 1b475f6e1598..edc5d8bef9d3 100644
--- a/include/linux/pkram.h
+++ b/include/linux/pkram.h
@@ -39,6 +39,7 @@ struct pkram_pg_state {
 };
 
 void pkram_walk_pgt_rev(struct pkram_pg_state *st, pgd_t *pgd);
+void pkram_free_pgt_walk_pgd(pgd_t *pgd);
 
 int pkram_prepare_save(struct pkram_stream *ps, const char *name,
                       gfp_t gfp_mask);
@@ -64,9 +65,11 @@ size_t pkram_read(struct pkram_stream *ps, void *buf, size_t 
count);
 #ifdef CONFIG_PKRAM
 extern unsigned long pkram_reserved_pages;
 void pkram_reserve(void);
+void pkram_free_pgt(void);
 #else
 #define pkram_reserved_pages 0UL
 static inline void pkram_reserve(void) { }
+static inline void pkram_free_pgt(void) { }
 #endif
 
 #endif /* _LINUX_PKRAM_H */
diff --git a/mm/pkram.c b/mm/pkram.c
index 2c323154df76..dd3c89614010 100644
--- a/mm/pkram.c
+++ b/mm/pkram.c
@@ -1227,3 +1227,14 @@ static int __init pkram_reserve_page_ranges(pgd_t *pgd)
 
        return err;
 }
+
+void pkram_free_pgt(void)
+{
+       if (!pkram_pgd)
+               return;
+
+       pkram_free_pgt_walk_pgd(pkram_pgd);
+
+       __free_pages_core(virt_to_page(pkram_pgd), 0);
+       pkram_pgd = NULL;
+}
diff --git a/mm/pkram_pagetable.c b/mm/pkram_pagetable.c
index d31aa36207ba..7033e9b1c47f 100644
--- a/mm/pkram_pagetable.c
+++ b/mm/pkram_pagetable.c
@@ -3,6 +3,8 @@
 #include <asm/pgtable.h>
 #include <linux/pkram.h>
 
+#include "internal.h"
+
 #define pgd_none(a)  (pgtable_l5_enabled() ? pgd_none(a) : 
p4d_none(__p4d(pgd_val(a))))
 
 static int note_page_rev(struct pkram_pg_state *st, unsigned long curr_size, 
bool present)
@@ -167,3 +169,83 @@ void pkram_walk_pgt_rev(struct pkram_pg_state *st, pgd_t 
*pgd)
                        break;
        }
 }
+
+static void pkram_free_pgt_walk_pmd(pud_t addr)
+{
+       unsigned long bitmap_pa;
+       struct page *page;
+       pmd_t *start;
+       int i;
+
+       start = (pmd_t *)pud_page_vaddr(addr);
+       for (i = 0; i < PTRS_PER_PMD; i++, start++) {
+               if (!pmd_none(*start)) {
+                       bitmap_pa = pte_val(pte_clrhuge(*(pte_t *)start));
+                       if (pmd_large(*start) && !bitmap_pa)
+                               continue;
+                       page = virt_to_page(__va(bitmap_pa));
+                       __free_pages_core(page, 0);
+               }
+       }
+}
+
+static void pkram_free_pgt_walk_pud(p4d_t addr)
+{
+       struct page *page;
+       pud_t *start;
+       int i;
+
+       start = (pud_t *)p4d_page_vaddr(addr);
+       for (i = 0; i < PTRS_PER_PUD; i++, start++) {
+               if (!pud_none(*start)) {
+                       if (pud_large(*start)) {
+                               WARN_ONCE(1, "PKRAM: unexpected pud 
hugepage\n");
+                               continue;
+                       }
+                       pkram_free_pgt_walk_pmd(*start);
+                       page = virt_to_page(__va(pud_val(*start)));
+                       __free_pages_core(page, 0);
+               }
+       }
+}
+
+static void pkram_free_pgt_walk_p4d(pgd_t addr)
+{
+       struct page *page;
+       p4d_t *start;
+       int i;
+
+       if (PTRS_PER_P4D == 1)
+               return pkram_free_pgt_walk_pud(__p4d(pgd_val(addr)));
+
+       start = (p4d_t *)pgd_page_vaddr(addr);
+       for (i = 0; i < PTRS_PER_P4D; i++, start++) {
+               if (!p4d_none(*start)) {
+                       if (p4d_large(*start)) {
+                               WARN_ONCE(1, "PKRAM: unexpected p4d 
hugepage\n");
+                               continue;
+                       }
+                       pkram_free_pgt_walk_pud(*start);
+                       page = virt_to_page(__va(p4d_val(*start)));
+                       __free_pages_core(page, 0);
+               }
+       }
+}
+
+/*
+ * Free the pagetable passed from the previous boot.
+ */
+void pkram_free_pgt_walk_pgd(pgd_t *pgd)
+{
+       pgd_t *start = pgd;
+       struct page *page;
+       int i;
+
+       for (i = 0; i < PTRS_PER_PGD; i++, start++) {
+               if (!pgd_none(*start)) {
+                       pkram_free_pgt_walk_p4d(*start);
+                       page = virt_to_page(__va(pgd_val(*start)));
+                       __free_pages_core(page, 0);
+               }
+       }
+}
-- 
2.13.3

Reply via email to