Re: [PATCH] riscv: Improve kasan population by using hugepages when possible

2021-02-08 Thread Alex Ghiti

Le 2/2/21 à 3:50 AM, Alex Ghiti a écrit :

Hi,

Le 2/1/21 à 3:00 AM, Alexandre Ghiti a écrit :

Kasan function that populates the shadow regions used to allocate them
page by page and did not take advantage of hugepages, so fix this by
trying to allocate hugepages of 1GB and fallback to 2MB hugepages or 4K
pages in case it fails.

This reduces the page table memory consumption and improves TLB usage,
as shown below:

Before this patch:

---[ Kasan shadow start ]---
0xffc0-0xffc4    0x818ef000    16G 
PTE . A . . . . R V
0xffc4-0xffc447fc    0x0002b7f4f000   1179392K 
PTE D A . . . W R V
0xffc48000-0xffc8    0x818ef000    14G 
PTE . A . . . . R V

---[ Kasan shadow end ]---

After this patch:

---[ Kasan shadow start ]---
0xffc0-0xffc4    0x818ef000    16G 
PTE . A . . . . R V
0xffc4-0xffc44000    0x00024000 1G 
PGD D A . . . W R V
0xffc44000-0xffc447e0    0x0002b7e0   126M 
PMD D A . . . W R V
0xffc447e0-0xffc447fc    0x0002b818f000  1792K 
PTE D A . . . W R V
0xffc48000-0xffc8    0x818ef000    14G 
PTE . A . . . . R V

---[ Kasan shadow end ]---

Signed-off-by: Alexandre Ghiti 
---
  arch/riscv/mm/kasan_init.c | 101 +++--
  1 file changed, 73 insertions(+), 28 deletions(-)

diff --git a/arch/riscv/mm/kasan_init.c b/arch/riscv/mm/kasan_init.c
index a8a2ffd9114a..8f11b73018b1 100644
--- a/arch/riscv/mm/kasan_init.c
+++ b/arch/riscv/mm/kasan_init.c
@@ -47,37 +47,82 @@ asmlinkage void __init kasan_early_init(void)
  local_flush_tlb_all();
  }
-static void __init populate(void *start, void *end)
+static void kasan_populate_pte(pmd_t *pmd, unsigned long vaddr, 
unsigned long end)

+{
+    phys_addr_t phys_addr;
+    pte_t *ptep = memblock_alloc(PTRS_PER_PTE * sizeof(pte_t), 
PAGE_SIZE);

+
+    do {
+    phys_addr = memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE);
+    set_pte(ptep, pfn_pte(PFN_DOWN(phys_addr), PAGE_KERNEL));
+    } while (ptep++, vaddr += PAGE_SIZE, vaddr != end);
+
+    set_pmd(pmd, pfn_pmd(PFN_DOWN(__pa(ptep)), PAGE_TABLE));
+}
+
+static void kasan_populate_pmd(pgd_t *pgd, unsigned long vaddr, 
unsigned long end)

+{
+    phys_addr_t phys_addr;
+    pmd_t *pmdp = memblock_alloc(PTRS_PER_PMD * sizeof(pmd_t), 
PAGE_SIZE);

+    unsigned long next;
+
+    do {
+    next = pmd_addr_end(vaddr, end);
+
+    if (IS_ALIGNED(vaddr, PMD_SIZE) && (next - vaddr) >= PMD_SIZE) {
+    phys_addr = memblock_phys_alloc(PMD_SIZE, PMD_SIZE);
+    if (phys_addr) {
+    set_pmd(pmdp, pfn_pmd(PFN_DOWN(phys_addr), 
PAGE_KERNEL));

+    continue;
+    }
+    }
+
+    kasan_populate_pte(pmdp, vaddr, end);
+    } while (pmdp++, vaddr = next, vaddr != end);
+
+    /*
+ * Wait for the whole PGD to be populated before setting the PGD in
+ * the page table, otherwise, if we did set the PGD before 
populating

+ * it entirely, memblock could allocate a page at a physical address
+ * where KASAN is not populated yet and then we'd get a page fault.
+ */
+    set_pgd(pgd, pfn_pgd(PFN_DOWN(__pa(pmdp)), PAGE_TABLE));


In case the PMD was filled entirely, PFN_DOWN(__pa(pmdp)) will point to 
the next physical page, which is wrong. The same problem happens on the 
other levels too.


I'll fix that in a v2 later today.

Alex


+}
+
+static void kasan_populate_pgd(unsigned long vaddr, unsigned long end)
+{
+    phys_addr_t phys_addr;
+    pgd_t *pgdp = pgd_offset_k(vaddr);
+    unsigned long next;
+
+    do {
+    next = pgd_addr_end(vaddr, end);
+
+    if (IS_ALIGNED(vaddr, PGDIR_SIZE) && (next - vaddr) >= 
PGDIR_SIZE) {

+    phys_addr = memblock_phys_alloc(PGDIR_SIZE, PGDIR_SIZE);
+    if (phys_addr) {
+    set_pgd(pgdp, pfn_pgd(PFN_DOWN(phys_addr), 
PAGE_KERNEL));

+    continue;
+    }
+    }
+
+    kasan_populate_pmd(pgdp, vaddr, end);
+    } while (pgdp++, vaddr = next, vaddr != end);
+}
+
+/*
+ * This function populates KASAN shadow region focusing on hugepages in
+ * order to minimize the page table cost and TLB usage too.
+ * Note that start must be PGDIR_SIZE-aligned in SV39 which amounts 
to be
+ * 1G aligned (that represents a 8G alignment constraint on virtual 
address

+ * ranges because of KASAN_SHADOW_SCALE_SHIFT).
+ */
+static void __init kasan_populate(void *start, void *end)
  {
-    unsigned long i, offset;
  unsigned long vaddr = (unsigned long)start & PAGE_MASK;
  unsigned long vend = PAGE_ALIGN((unsigned long)end);
-    unsigned long n_pages = (vend - vaddr) / PAGE_SIZE;
-    unsigned long n_ptes =
-    ((n_pages + PTRS_PER_PTE) & -PTRS_PER_PTE) / PTRS_PER_PTE;
-    unsigned long n_pmds =
-    ((n_ptes + PTRS_PER_PMD) & 

Re: [PATCH] riscv: Improve kasan population by using hugepages when possible

2021-02-02 Thread Alex Ghiti

Hi,

Le 2/1/21 à 3:00 AM, Alexandre Ghiti a écrit :

Kasan function that populates the shadow regions used to allocate them
page by page and did not take advantage of hugepages, so fix this by
trying to allocate hugepages of 1GB and fallback to 2MB hugepages or 4K
pages in case it fails.

This reduces the page table memory consumption and improves TLB usage,
as shown below:

Before this patch:

---[ Kasan shadow start ]---
0xffc0-0xffc40x818ef00016G PTE 
. A . . . . R V
0xffc4-0xffc447fc0x0002b7f4f000   1179392K PTE 
D A . . . W R V
0xffc48000-0xffc80x818ef00014G PTE 
. A . . . . R V
---[ Kasan shadow end ]---

After this patch:

---[ Kasan shadow start ]---
0xffc0-0xffc40x818ef00016G PTE 
. A . . . . R V
0xffc4-0xffc440000x00024000 1G PGD 
D A . . . W R V
0xffc44000-0xffc447e00x0002b7e0   126M PMD 
D A . . . W R V
0xffc447e0-0xffc447fc0x0002b818f000  1792K PTE 
D A . . . W R V
0xffc48000-0xffc80x818ef00014G PTE 
. A . . . . R V
---[ Kasan shadow end ]---

Signed-off-by: Alexandre Ghiti 
---
  arch/riscv/mm/kasan_init.c | 101 +++--
  1 file changed, 73 insertions(+), 28 deletions(-)

diff --git a/arch/riscv/mm/kasan_init.c b/arch/riscv/mm/kasan_init.c
index a8a2ffd9114a..8f11b73018b1 100644
--- a/arch/riscv/mm/kasan_init.c
+++ b/arch/riscv/mm/kasan_init.c
@@ -47,37 +47,82 @@ asmlinkage void __init kasan_early_init(void)
local_flush_tlb_all();
  }
  
-static void __init populate(void *start, void *end)

+static void kasan_populate_pte(pmd_t *pmd, unsigned long vaddr, unsigned long 
end)
+{
+   phys_addr_t phys_addr;
+   pte_t *ptep = memblock_alloc(PTRS_PER_PTE * sizeof(pte_t), PAGE_SIZE);
+
+   do {
+   phys_addr = memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE);
+   set_pte(ptep, pfn_pte(PFN_DOWN(phys_addr), PAGE_KERNEL));
+   } while (ptep++, vaddr += PAGE_SIZE, vaddr != end);
+
+   set_pmd(pmd, pfn_pmd(PFN_DOWN(__pa(ptep)), PAGE_TABLE));
+}
+
+static void kasan_populate_pmd(pgd_t *pgd, unsigned long vaddr, unsigned long 
end)
+{
+   phys_addr_t phys_addr;
+   pmd_t *pmdp = memblock_alloc(PTRS_PER_PMD * sizeof(pmd_t), PAGE_SIZE);
+   unsigned long next;
+
+   do {
+   next = pmd_addr_end(vaddr, end);
+
+   if (IS_ALIGNED(vaddr, PMD_SIZE) && (next - vaddr) >= PMD_SIZE) {
+   phys_addr = memblock_phys_alloc(PMD_SIZE, PMD_SIZE);
+   if (phys_addr) {
+   set_pmd(pmdp, pfn_pmd(PFN_DOWN(phys_addr), 
PAGE_KERNEL));
+   continue;
+   }
+   }
+
+   kasan_populate_pte(pmdp, vaddr, end);
+   } while (pmdp++, vaddr = next, vaddr != end);
+
+   /*
+* Wait for the whole PGD to be populated before setting the PGD in
+* the page table, otherwise, if we did set the PGD before populating
+* it entirely, memblock could allocate a page at a physical address
+* where KASAN is not populated yet and then we'd get a page fault.
+*/
+   set_pgd(pgd, pfn_pgd(PFN_DOWN(__pa(pmdp)), PAGE_TABLE));


In case the PMD was filled entirely, PFN_DOWN(__pa(pmdp)) will point to 
the next physical page, which is wrong. The same problem happens on the 
other levels too.


I'll fix that in a v2 later today.

Alex


+}
+
+static void kasan_populate_pgd(unsigned long vaddr, unsigned long end)
+{
+   phys_addr_t phys_addr;
+   pgd_t *pgdp = pgd_offset_k(vaddr);
+   unsigned long next;
+
+   do {
+   next = pgd_addr_end(vaddr, end);
+
+   if (IS_ALIGNED(vaddr, PGDIR_SIZE) && (next - vaddr) >= 
PGDIR_SIZE) {
+   phys_addr = memblock_phys_alloc(PGDIR_SIZE, PGDIR_SIZE);
+   if (phys_addr) {
+   set_pgd(pgdp, pfn_pgd(PFN_DOWN(phys_addr), 
PAGE_KERNEL));
+   continue;
+   }
+   }
+
+   kasan_populate_pmd(pgdp, vaddr, end);
+   } while (pgdp++, vaddr = next, vaddr != end);
+}
+
+/*
+ * This function populates KASAN shadow region focusing on hugepages in
+ * order to minimize the page table cost and TLB usage too.
+ * Note that start must be PGDIR_SIZE-aligned in SV39 which amounts to be
+ * 1G aligned (that represents a 8G alignment constraint on virtual address
+ * ranges because of KASAN_SHADOW_SCALE_SHIFT).
+ */
+static void __init kasan_populate(void *start, void *end)
  {
-   unsigned long i, offset;
unsigned long vaddr = (unsigned long)start & PAGE_MASK;
unsigned long vend = PAGE_ALIGN((unsigned long)end);
-   

[PATCH] riscv: Improve kasan population by using hugepages when possible

2021-02-01 Thread Alexandre Ghiti
Kasan function that populates the shadow regions used to allocate them
page by page and did not take advantage of hugepages, so fix this by
trying to allocate hugepages of 1GB and fallback to 2MB hugepages or 4K
pages in case it fails.

This reduces the page table memory consumption and improves TLB usage,
as shown below:

Before this patch:

---[ Kasan shadow start ]---
0xffc0-0xffc40x818ef00016G PTE 
. A . . . . R V
0xffc4-0xffc447fc0x0002b7f4f000   1179392K PTE 
D A . . . W R V
0xffc48000-0xffc80x818ef00014G PTE 
. A . . . . R V
---[ Kasan shadow end ]---

After this patch:

---[ Kasan shadow start ]---
0xffc0-0xffc40x818ef00016G PTE 
. A . . . . R V
0xffc4-0xffc440000x00024000 1G PGD 
D A . . . W R V
0xffc44000-0xffc447e00x0002b7e0   126M PMD 
D A . . . W R V
0xffc447e0-0xffc447fc0x0002b818f000  1792K PTE 
D A . . . W R V
0xffc48000-0xffc80x818ef00014G PTE 
. A . . . . R V
---[ Kasan shadow end ]---

Signed-off-by: Alexandre Ghiti 
---
 arch/riscv/mm/kasan_init.c | 101 +++--
 1 file changed, 73 insertions(+), 28 deletions(-)

diff --git a/arch/riscv/mm/kasan_init.c b/arch/riscv/mm/kasan_init.c
index a8a2ffd9114a..8f11b73018b1 100644
--- a/arch/riscv/mm/kasan_init.c
+++ b/arch/riscv/mm/kasan_init.c
@@ -47,37 +47,82 @@ asmlinkage void __init kasan_early_init(void)
local_flush_tlb_all();
 }
 
-static void __init populate(void *start, void *end)
+static void kasan_populate_pte(pmd_t *pmd, unsigned long vaddr, unsigned long 
end)
+{
+   phys_addr_t phys_addr;
+   pte_t *ptep = memblock_alloc(PTRS_PER_PTE * sizeof(pte_t), PAGE_SIZE);
+
+   do {
+   phys_addr = memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE);
+   set_pte(ptep, pfn_pte(PFN_DOWN(phys_addr), PAGE_KERNEL));
+   } while (ptep++, vaddr += PAGE_SIZE, vaddr != end);
+
+   set_pmd(pmd, pfn_pmd(PFN_DOWN(__pa(ptep)), PAGE_TABLE));
+}
+
+static void kasan_populate_pmd(pgd_t *pgd, unsigned long vaddr, unsigned long 
end)
+{
+   phys_addr_t phys_addr;
+   pmd_t *pmdp = memblock_alloc(PTRS_PER_PMD * sizeof(pmd_t), PAGE_SIZE);
+   unsigned long next;
+
+   do {
+   next = pmd_addr_end(vaddr, end);
+
+   if (IS_ALIGNED(vaddr, PMD_SIZE) && (next - vaddr) >= PMD_SIZE) {
+   phys_addr = memblock_phys_alloc(PMD_SIZE, PMD_SIZE);
+   if (phys_addr) {
+   set_pmd(pmdp, pfn_pmd(PFN_DOWN(phys_addr), 
PAGE_KERNEL));
+   continue;
+   }
+   }
+
+   kasan_populate_pte(pmdp, vaddr, end);
+   } while (pmdp++, vaddr = next, vaddr != end);
+
+   /*
+* Wait for the whole PGD to be populated before setting the PGD in
+* the page table, otherwise, if we did set the PGD before populating
+* it entirely, memblock could allocate a page at a physical address
+* where KASAN is not populated yet and then we'd get a page fault.
+*/
+   set_pgd(pgd, pfn_pgd(PFN_DOWN(__pa(pmdp)), PAGE_TABLE));
+}
+
+static void kasan_populate_pgd(unsigned long vaddr, unsigned long end)
+{
+   phys_addr_t phys_addr;
+   pgd_t *pgdp = pgd_offset_k(vaddr);
+   unsigned long next;
+
+   do {
+   next = pgd_addr_end(vaddr, end);
+
+   if (IS_ALIGNED(vaddr, PGDIR_SIZE) && (next - vaddr) >= 
PGDIR_SIZE) {
+   phys_addr = memblock_phys_alloc(PGDIR_SIZE, PGDIR_SIZE);
+   if (phys_addr) {
+   set_pgd(pgdp, pfn_pgd(PFN_DOWN(phys_addr), 
PAGE_KERNEL));
+   continue;
+   }
+   }
+
+   kasan_populate_pmd(pgdp, vaddr, end);
+   } while (pgdp++, vaddr = next, vaddr != end);
+}
+
+/*
+ * This function populates KASAN shadow region focusing on hugepages in
+ * order to minimize the page table cost and TLB usage too.
+ * Note that start must be PGDIR_SIZE-aligned in SV39 which amounts to be
+ * 1G aligned (that represents a 8G alignment constraint on virtual address
+ * ranges because of KASAN_SHADOW_SCALE_SHIFT).
+ */
+static void __init kasan_populate(void *start, void *end)
 {
-   unsigned long i, offset;
unsigned long vaddr = (unsigned long)start & PAGE_MASK;
unsigned long vend = PAGE_ALIGN((unsigned long)end);
-   unsigned long n_pages = (vend - vaddr) / PAGE_SIZE;
-   unsigned long n_ptes =
-   ((n_pages + PTRS_PER_PTE) & -PTRS_PER_PTE) / PTRS_PER_PTE;
-   unsigned long n_pmds =
-   ((n_ptes + PTRS_PER_PMD) & -PTRS_PER_PMD) / PTRS_PER_PMD;
-
-   pte_t *pte =