mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y
@ 2017-07-24 15:25 Andrey Ryabinin
  2017-07-24 15:37 ` Kirill A. Shutemov
  2017-07-25 13:50 ` [tip:x86/mm] " tip-bot for Andrey Ryabinin
  0 siblings, 2 replies; 5+ messages in thread
From: Andrey Ryabinin @ 2017-07-24 15:25 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, H. Peter Anvin, x86
  Cc: Kirill A. Shutemov, linux-kernel, Dmitry Vyukov,
	Alexander Potapenko, Andrey Ryabinin

KASAN fills kernel page tables with repeated values to map several
TBs of the virtual memory to the single kasan_zero_page:
  kasan_zero_p4d ->
    kasan_zero_pud ->
        kasan_zero_pmd->
            kasan_zero_pte->
                kasan_zero_page

Walking the whole KASAN shadow range takes a lot of time, especially
with 5-level page tables. Since we already know that all kasan page tables
eventually point to the kasan_zero_page we could call note_page()
right and avoid walking lower levels of the page tables.
This will not affect the output of the kernel_page_tables file,
but let us avoid spending time in page table walkers:

Before:
time cat /sys/kernel/debug/kernel_page_tables > /dev/null

real    0m55.855s
user    0m0.000s
sys     0m55.840s

After:
time cat /sys/kernel/debug/kernel_page_tables > /dev/null

real    0m0.054s
user    0m0.000s
sys     0m0.054s

Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
---
 arch/x86/mm/dump_pagetables.c | 64 +++++++++++++++++++++++++++----------------
 1 file changed, 41 insertions(+), 23 deletions(-)

diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index b371ab68f2d4..5e3ac6fe6c9e 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -13,12 +13,12 @@
  */
 
 #include <linux/debugfs.h>
+#include <linux/kasan.h>
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 
-#include <asm/kasan.h>
 #include <asm/pgtable.h>
 
 /*
@@ -302,23 +302,53 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
 		start++;
 	}
 }
+#ifdef CONFIG_KASAN
+
+/*
+ * This is an optimization for KASAN=y case. Since all kasan page tables
+ * eventually point to the kasan_zero_page we could call note_page()
+ * right away without walking through lower level page tables. This saves
+ * us dozens of seconds (minutes for 5-level config) while checking for
+ * W+X mapping or reading kernel_page_tables debugfs file.
+ */
+static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
+				void *pt)
+{
+	if (__pa(pt) == __pa(kasan_zero_pmd) ||
+#ifdef CONFIG_X86_5LEVEL
+	    __pa(pt) == __pa(kasan_zero_p4d) ||
+#endif
+	    __pa(pt) == __pa(kasan_zero_pud)) {
+		pgprotval_t prot = pte_flags(kasan_zero_pte[0]);
+		note_page(m, st, __pgprot(prot), 5);
+		return true;
+	}
+	return false;
+}
+#else
+static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
+				void *pt)
+{
+	return false;
+}
+#endif
 
 #if PTRS_PER_PMD > 1
 
 static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr, unsigned long P)
 {
 	int i;
-	pmd_t *start;
+	pmd_t *start, *pmd_start;
 	pgprotval_t prot;
 
-	start = (pmd_t *)pud_page_vaddr(addr);
+	pmd_start = start = (pmd_t *)pud_page_vaddr(addr);
 	for (i = 0; i < PTRS_PER_PMD; i++) {
 		st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT);
 		if (!pmd_none(*start)) {
 			if (pmd_large(*start) || !pmd_present(*start)) {
 				prot = pmd_flags(*start);
 				note_page(m, st, __pgprot(prot), 4);
-			} else {
+			} else if (!kasan_page_table(m, st, pmd_start)) {
 				walk_pte_level(m, st, *start,
 					       P + i * PMD_LEVEL_MULT);
 			}
@@ -336,34 +366,22 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr,
 
 #if PTRS_PER_PUD > 1
 
-/*
- * This is an optimization for CONFIG_DEBUG_WX=y + CONFIG_KASAN=y
- * KASAN fills page tables with the same values. Since there is no
- * point in checking page table more than once we just skip repeated
- * entries. This saves us dozens of seconds during boot.
- */
-static bool pud_already_checked(pud_t *prev_pud, pud_t *pud, bool checkwx)
-{
-	return checkwx && prev_pud && (pud_val(*prev_pud) == pud_val(*pud));
-}
-
 static void walk_pud_level(struct seq_file *m, struct pg_state *st, p4d_t addr, unsigned long P)
 {
 	int i;
-	pud_t *start;
+	pud_t *start, *pud_start;
 	pgprotval_t prot;
 	pud_t *prev_pud = NULL;
 
-	start = (pud_t *)p4d_page_vaddr(addr);
+	pud_start = start = (pud_t *)p4d_page_vaddr(addr);
 
 	for (i = 0; i < PTRS_PER_PUD; i++) {
 		st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT);
-		if (!pud_none(*start) &&
-		    !pud_already_checked(prev_pud, start, st->check_wx)) {
+		if (!pud_none(*start)) {
 			if (pud_large(*start) || !pud_present(*start)) {
 				prot = pud_flags(*start);
 				note_page(m, st, __pgprot(prot), 3);
-			} else {
+			} else if (!kasan_page_table(m, st, pud_start)) {
 				walk_pmd_level(m, st, *start,
 					       P + i * PUD_LEVEL_MULT);
 			}
@@ -386,10 +404,10 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, p4d_t addr,
 static void walk_p4d_level(struct seq_file *m, struct pg_state *st, pgd_t addr, unsigned long P)
 {
 	int i;
-	p4d_t *start;
+	p4d_t *start, *p4d_start;
 	pgprotval_t prot;
 
-	start = (p4d_t *)pgd_page_vaddr(addr);
+	p4d_start = start = (p4d_t *)pgd_page_vaddr(addr);
 
 	for (i = 0; i < PTRS_PER_P4D; i++) {
 		st->current_address = normalize_addr(P + i * P4D_LEVEL_MULT);
@@ -397,7 +415,7 @@ static void walk_p4d_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
 			if (p4d_large(*start) || !p4d_present(*start)) {
 				prot = p4d_flags(*start);
 				note_page(m, st, __pgprot(prot), 2);
-			} else {
+			} else if (!kasan_page_table(m, st, p4d_start)) {
 				walk_pud_level(m, st, *start,
 					       P + i * P4D_LEVEL_MULT);
 			}
-- 
2.13.0

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y
  2017-07-24 15:25 [PATCH] x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y Andrey Ryabinin
@ 2017-07-24 15:37 ` Kirill A. Shutemov
  2017-07-24 15:57   ` Andrey Ryabinin
  2017-07-25 13:50 ` [tip:x86/mm] " tip-bot for Andrey Ryabinin
  1 sibling, 1 reply; 5+ messages in thread
From: Kirill A. Shutemov @ 2017-07-24 15:37 UTC (permalink / raw)
  To: Andrey Ryabinin
  Cc: Thomas Gleixner, Ingo Molnar, H. Peter Anvin, x86,
	Kirill A. Shutemov, linux-kernel, Dmitry Vyukov,
	Alexander Potapenko

On Mon, Jul 24, 2017 at 06:25:58PM +0300, Andrey Ryabinin wrote:
> KASAN fills kernel page tables with repeated values to map several
> TBs of the virtual memory to the single kasan_zero_page:
>   kasan_zero_p4d ->
>     kasan_zero_pud ->
>         kasan_zero_pmd->
>             kasan_zero_pte->
>                 kasan_zero_page
> 
> Walking the whole KASAN shadow range takes a lot of time, especially
> with 5-level page tables. Since we already know that all kasan page tables
> eventually point to the kasan_zero_page we could call note_page()
> right and avoid walking lower levels of the page tables.
> This will not affect the output of the kernel_page_tables file,
> but let us avoid spending time in page table walkers:
> 
> Before:
> time cat /sys/kernel/debug/kernel_page_tables > /dev/null
> 
> real    0m55.855s
> user    0m0.000s
> sys     0m55.840s
> 
> After:
> time cat /sys/kernel/debug/kernel_page_tables > /dev/null
> 
> real    0m0.054s
> user    0m0.000s
> sys     0m0.054s
> 
> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
> ---
>  arch/x86/mm/dump_pagetables.c | 64 +++++++++++++++++++++++++++----------------
>  1 file changed, 41 insertions(+), 23 deletions(-)
> 
> diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
> index b371ab68f2d4..5e3ac6fe6c9e 100644
> --- a/arch/x86/mm/dump_pagetables.c
> +++ b/arch/x86/mm/dump_pagetables.c
> @@ -13,12 +13,12 @@
>   */
>  
>  #include <linux/debugfs.h>
> +#include <linux/kasan.h>
>  #include <linux/mm.h>
>  #include <linux/init.h>
>  #include <linux/sched.h>
>  #include <linux/seq_file.h>
>  
> -#include <asm/kasan.h>
>  #include <asm/pgtable.h>
>  
>  /*
> @@ -302,23 +302,53 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
>  		start++;
>  	}
>  }
> +#ifdef CONFIG_KASAN
> +
> +/*
> + * This is an optimization for KASAN=y case. Since all kasan page tables
> + * eventually point to the kasan_zero_page we could call note_page()
> + * right away without walking through lower level page tables. This saves
> + * us dozens of seconds (minutes for 5-level config) while checking for
> + * W+X mapping or reading kernel_page_tables debugfs file.
> + */
> +static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
> +				void *pt)
> +{
> +	if (__pa(pt) == __pa(kasan_zero_pmd) ||
> +#ifdef CONFIG_X86_5LEVEL
> +	    __pa(pt) == __pa(kasan_zero_p4d) ||
> +#endif
> +	    __pa(pt) == __pa(kasan_zero_pud)) {
> +		pgprotval_t prot = pte_flags(kasan_zero_pte[0]);
> +		note_page(m, st, __pgprot(prot), 5);

Hm. I don't think '5' is correct here. Shouldn't it be dependent on what
page table we detected? Or just take it from caller?


-- 
 Kirill A. Shutemov

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y
  2017-07-24 15:37 ` Kirill A. Shutemov
@ 2017-07-24 15:57   ` Andrey Ryabinin
  2017-07-24 17:54     ` Kirill A. Shutemov
  0 siblings, 1 reply; 5+ messages in thread
From: Andrey Ryabinin @ 2017-07-24 15:57 UTC (permalink / raw)
  To: Kirill A. Shutemov
  Cc: Thomas Gleixner, Ingo Molnar, H. Peter Anvin, x86,
	Kirill A. Shutemov, linux-kernel, Dmitry Vyukov,
	Alexander Potapenko



On 07/24/2017 06:37 PM, Kirill A. Shutemov wrote:
> On Mon, Jul 24, 2017 at 06:25:58PM +0300, Andrey Ryabinin wrote:
>> KASAN fills kernel page tables with repeated values to map several
>> TBs of the virtual memory to the single kasan_zero_page:
>>   kasan_zero_p4d ->
>>     kasan_zero_pud ->
>>         kasan_zero_pmd->
>>             kasan_zero_pte->
>>                 kasan_zero_page
>>
>> Walking the whole KASAN shadow range takes a lot of time, especially
>> with 5-level page tables. Since we already know that all kasan page tables
>> eventually point to the kasan_zero_page we could call note_page()
>> right and avoid walking lower levels of the page tables.
>> This will not affect the output of the kernel_page_tables file,
>> but let us avoid spending time in page table walkers:
>>
>> Before:
>> time cat /sys/kernel/debug/kernel_page_tables > /dev/null
>>
>> real    0m55.855s
>> user    0m0.000s
>> sys     0m55.840s
>>
>> After:
>> time cat /sys/kernel/debug/kernel_page_tables > /dev/null
>>
>> real    0m0.054s
>> user    0m0.000s
>> sys     0m0.054s
>>
>> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
>> ---
>>  arch/x86/mm/dump_pagetables.c | 64 +++++++++++++++++++++++++++----------------
>>  1 file changed, 41 insertions(+), 23 deletions(-)
>>
>> diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
>> index b371ab68f2d4..5e3ac6fe6c9e 100644
>> --- a/arch/x86/mm/dump_pagetables.c
>> +++ b/arch/x86/mm/dump_pagetables.c
>> @@ -13,12 +13,12 @@
>>   */
>>  
>>  #include <linux/debugfs.h>
>> +#include <linux/kasan.h>
>>  #include <linux/mm.h>
>>  #include <linux/init.h>
>>  #include <linux/sched.h>
>>  #include <linux/seq_file.h>
>>  
>> -#include <asm/kasan.h>
>>  #include <asm/pgtable.h>
>>  
>>  /*
>> @@ -302,23 +302,53 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
>>  		start++;
>>  	}
>>  }
>> +#ifdef CONFIG_KASAN
>> +
>> +/*
>> + * This is an optimization for KASAN=y case. Since all kasan page tables
>> + * eventually point to the kasan_zero_page we could call note_page()
>> + * right away without walking through lower level page tables. This saves
>> + * us dozens of seconds (minutes for 5-level config) while checking for
>> + * W+X mapping or reading kernel_page_tables debugfs file.
>> + */
>> +static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
>> +				void *pt)
>> +{
>> +	if (__pa(pt) == __pa(kasan_zero_pmd) ||
>> +#ifdef CONFIG_X86_5LEVEL
>> +	    __pa(pt) == __pa(kasan_zero_p4d) ||
>> +#endif
>> +	    __pa(pt) == __pa(kasan_zero_pud)) {
>> +		pgprotval_t prot = pte_flags(kasan_zero_pte[0]);
>> +		note_page(m, st, __pgprot(prot), 5);
> 
> Hm. I don't think '5' is correct here. Shouldn't it be dependent on what
> page table we detected? Or just take it from caller?
> 

No, 5 is correct, because all kasan page tables endup as a pte mapping of kasan_zero_page.
And pte is level 5.

Anything but 5 would give us incorrect output in the last column in the last column of the dump,
pmd,pud or p4d instead of correct pte.

Without the patch, if we stump on kasan page table we always endup with a note_page(..., 5) call.
With this patch we just avoid useless walks on lower levels of page tables, because we already know the end result,
so we just call note_page(.., 5) right away.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y
  2017-07-24 15:57   ` Andrey Ryabinin
@ 2017-07-24 17:54     ` Kirill A. Shutemov
  0 siblings, 0 replies; 5+ messages in thread
From: Kirill A. Shutemov @ 2017-07-24 17:54 UTC (permalink / raw)
  To: Andrey Ryabinin
  Cc: Thomas Gleixner, Ingo Molnar, H. Peter Anvin, x86,
	Kirill A. Shutemov, linux-kernel, Dmitry Vyukov,
	Alexander Potapenko

On Mon, Jul 24, 2017 at 06:57:53PM +0300, Andrey Ryabinin wrote:
> 
> 
> On 07/24/2017 06:37 PM, Kirill A. Shutemov wrote:
> > On Mon, Jul 24, 2017 at 06:25:58PM +0300, Andrey Ryabinin wrote:
> >> KASAN fills kernel page tables with repeated values to map several
> >> TBs of the virtual memory to the single kasan_zero_page:
> >>   kasan_zero_p4d ->
> >>     kasan_zero_pud ->
> >>         kasan_zero_pmd->
> >>             kasan_zero_pte->
> >>                 kasan_zero_page
> >>
> >> Walking the whole KASAN shadow range takes a lot of time, especially
> >> with 5-level page tables. Since we already know that all kasan page tables
> >> eventually point to the kasan_zero_page we could call note_page()
> >> right and avoid walking lower levels of the page tables.
> >> This will not affect the output of the kernel_page_tables file,
> >> but let us avoid spending time in page table walkers:
> >>
> >> Before:
> >> time cat /sys/kernel/debug/kernel_page_tables > /dev/null
> >>
> >> real    0m55.855s
> >> user    0m0.000s
> >> sys     0m55.840s
> >>
> >> After:
> >> time cat /sys/kernel/debug/kernel_page_tables > /dev/null
> >>
> >> real    0m0.054s
> >> user    0m0.000s
> >> sys     0m0.054s
> >>
> >> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
> >> ---
> >>  arch/x86/mm/dump_pagetables.c | 64 +++++++++++++++++++++++++++----------------
> >>  1 file changed, 41 insertions(+), 23 deletions(-)
> >>
> >> diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
> >> index b371ab68f2d4..5e3ac6fe6c9e 100644
> >> --- a/arch/x86/mm/dump_pagetables.c
> >> +++ b/arch/x86/mm/dump_pagetables.c
> >> @@ -13,12 +13,12 @@
> >>   */
> >>  
> >>  #include <linux/debugfs.h>
> >> +#include <linux/kasan.h>
> >>  #include <linux/mm.h>
> >>  #include <linux/init.h>
> >>  #include <linux/sched.h>
> >>  #include <linux/seq_file.h>
> >>  
> >> -#include <asm/kasan.h>
> >>  #include <asm/pgtable.h>
> >>  
> >>  /*
> >> @@ -302,23 +302,53 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
> >>  		start++;
> >>  	}
> >>  }
> >> +#ifdef CONFIG_KASAN
> >> +
> >> +/*
> >> + * This is an optimization for KASAN=y case. Since all kasan page tables
> >> + * eventually point to the kasan_zero_page we could call note_page()
> >> + * right away without walking through lower level page tables. This saves
> >> + * us dozens of seconds (minutes for 5-level config) while checking for
> >> + * W+X mapping or reading kernel_page_tables debugfs file.
> >> + */
> >> +static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
> >> +				void *pt)
> >> +{
> >> +	if (__pa(pt) == __pa(kasan_zero_pmd) ||
> >> +#ifdef CONFIG_X86_5LEVEL
> >> +	    __pa(pt) == __pa(kasan_zero_p4d) ||
> >> +#endif
> >> +	    __pa(pt) == __pa(kasan_zero_pud)) {
> >> +		pgprotval_t prot = pte_flags(kasan_zero_pte[0]);
> >> +		note_page(m, st, __pgprot(prot), 5);
> > 
> > Hm. I don't think '5' is correct here. Shouldn't it be dependent on what
> > page table we detected? Or just take it from caller?
> > 
> 
> No, 5 is correct, because all kasan page tables endup as a pte mapping of kasan_zero_page.
> And pte is level 5.
> 
> Anything but 5 would give us incorrect output in the last column in the last column of the dump,
> pmd,pud or p4d instead of correct pte.
> 
> Without the patch, if we stump on kasan page table we always endup with a note_page(..., 5) call.
> With this patch we just avoid useless walks on lower levels of page tables, because we already know the end result,
> so we just call note_page(.., 5) right away.

You're right. Thanks for explanation.

Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>

-- 
 Kirill A. Shutemov

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [tip:x86/mm] x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y
  2017-07-24 15:25 [PATCH] x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y Andrey Ryabinin
  2017-07-24 15:37 ` Kirill A. Shutemov
@ 2017-07-25 13:50 ` tip-bot for Andrey Ryabinin
  1 sibling, 0 replies; 5+ messages in thread
From: tip-bot for Andrey Ryabinin @ 2017-07-25 13:50 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: dvyukov, torvalds, kirill.shutemov, peterz, tglx, hpa, mingo,
	glider, linux-kernel, aryabinin

Commit-ID:  04b67022fb6d5b13025591f61a487a6ef7f4f05c
Gitweb:     http://git.kernel.org/tip/04b67022fb6d5b13025591f61a487a6ef7f4f05c
Author:     Andrey Ryabinin <aryabinin@virtuozzo.com>
AuthorDate: Mon, 24 Jul 2017 18:25:58 +0300
Committer:  Ingo Molnar <mingo@kernel.org>
CommitDate: Tue, 25 Jul 2017 11:22:09 +0200

x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y

KASAN fills kernel page tables with repeated values to map several
TBs of the virtual memory to the single kasan_zero_page:
  kasan_zero_p4d ->
    kasan_zero_pud ->
        kasan_zero_pmd->
            kasan_zero_pte->
                kasan_zero_page

Walking the whole KASAN shadow range takes a lot of time, especially
with 5-level page tables. Since we already know that all kasan page tables
eventually point to the kasan_zero_page we could call note_page()
right and avoid walking lower levels of the page tables.
This will not affect the output of the kernel_page_tables file,
but let us avoid spending time in page table walkers:

Before:

  $ time cat /sys/kernel/debug/kernel_page_tables > /dev/null

  real    0m55.855s
  user    0m0.000s
  sys     0m55.840s

After:

  $ time cat /sys/kernel/debug/kernel_page_tables > /dev/null

  real    0m0.054s
  user    0m0.000s
  sys     0m0.054s

Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/20170724152558.24689-1-aryabinin@virtuozzo.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 arch/x86/mm/dump_pagetables.c | 64 +++++++++++++++++++++++++++----------------
 1 file changed, 41 insertions(+), 23 deletions(-)

diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index b371ab6..5e3ac6f 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -13,12 +13,12 @@
  */
 
 #include <linux/debugfs.h>
+#include <linux/kasan.h>
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 
-#include <asm/kasan.h>
 #include <asm/pgtable.h>
 
 /*
@@ -302,23 +302,53 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
 		start++;
 	}
 }
+#ifdef CONFIG_KASAN
+
+/*
+ * This is an optimization for KASAN=y case. Since all kasan page tables
+ * eventually point to the kasan_zero_page we could call note_page()
+ * right away without walking through lower level page tables. This saves
+ * us dozens of seconds (minutes for 5-level config) while checking for
+ * W+X mapping or reading kernel_page_tables debugfs file.
+ */
+static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
+				void *pt)
+{
+	if (__pa(pt) == __pa(kasan_zero_pmd) ||
+#ifdef CONFIG_X86_5LEVEL
+	    __pa(pt) == __pa(kasan_zero_p4d) ||
+#endif
+	    __pa(pt) == __pa(kasan_zero_pud)) {
+		pgprotval_t prot = pte_flags(kasan_zero_pte[0]);
+		note_page(m, st, __pgprot(prot), 5);
+		return true;
+	}
+	return false;
+}
+#else
+static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
+				void *pt)
+{
+	return false;
+}
+#endif
 
 #if PTRS_PER_PMD > 1
 
 static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr, unsigned long P)
 {
 	int i;
-	pmd_t *start;
+	pmd_t *start, *pmd_start;
 	pgprotval_t prot;
 
-	start = (pmd_t *)pud_page_vaddr(addr);
+	pmd_start = start = (pmd_t *)pud_page_vaddr(addr);
 	for (i = 0; i < PTRS_PER_PMD; i++) {
 		st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT);
 		if (!pmd_none(*start)) {
 			if (pmd_large(*start) || !pmd_present(*start)) {
 				prot = pmd_flags(*start);
 				note_page(m, st, __pgprot(prot), 4);
-			} else {
+			} else if (!kasan_page_table(m, st, pmd_start)) {
 				walk_pte_level(m, st, *start,
 					       P + i * PMD_LEVEL_MULT);
 			}
@@ -336,34 +366,22 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr,
 
 #if PTRS_PER_PUD > 1
 
-/*
- * This is an optimization for CONFIG_DEBUG_WX=y + CONFIG_KASAN=y
- * KASAN fills page tables with the same values. Since there is no
- * point in checking page table more than once we just skip repeated
- * entries. This saves us dozens of seconds during boot.
- */
-static bool pud_already_checked(pud_t *prev_pud, pud_t *pud, bool checkwx)
-{
-	return checkwx && prev_pud && (pud_val(*prev_pud) == pud_val(*pud));
-}
-
 static void walk_pud_level(struct seq_file *m, struct pg_state *st, p4d_t addr, unsigned long P)
 {
 	int i;
-	pud_t *start;
+	pud_t *start, *pud_start;
 	pgprotval_t prot;
 	pud_t *prev_pud = NULL;
 
-	start = (pud_t *)p4d_page_vaddr(addr);
+	pud_start = start = (pud_t *)p4d_page_vaddr(addr);
 
 	for (i = 0; i < PTRS_PER_PUD; i++) {
 		st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT);
-		if (!pud_none(*start) &&
-		    !pud_already_checked(prev_pud, start, st->check_wx)) {
+		if (!pud_none(*start)) {
 			if (pud_large(*start) || !pud_present(*start)) {
 				prot = pud_flags(*start);
 				note_page(m, st, __pgprot(prot), 3);
-			} else {
+			} else if (!kasan_page_table(m, st, pud_start)) {
 				walk_pmd_level(m, st, *start,
 					       P + i * PUD_LEVEL_MULT);
 			}
@@ -386,10 +404,10 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, p4d_t addr,
 static void walk_p4d_level(struct seq_file *m, struct pg_state *st, pgd_t addr, unsigned long P)
 {
 	int i;
-	p4d_t *start;
+	p4d_t *start, *p4d_start;
 	pgprotval_t prot;
 
-	start = (p4d_t *)pgd_page_vaddr(addr);
+	p4d_start = start = (p4d_t *)pgd_page_vaddr(addr);
 
 	for (i = 0; i < PTRS_PER_P4D; i++) {
 		st->current_address = normalize_addr(P + i * P4D_LEVEL_MULT);
@@ -397,7 +415,7 @@ static void walk_p4d_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
 			if (p4d_large(*start) || !p4d_present(*start)) {
 				prot = p4d_flags(*start);
 				note_page(m, st, __pgprot(prot), 2);
-			} else {
+			} else if (!kasan_page_table(m, st, p4d_start)) {
 				walk_pud_level(m, st, *start,
 					       P + i * P4D_LEVEL_MULT);
 			}

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-07-25 13:54 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-24 15:25 [PATCH] x86/mm/dump_pagetables: Speed up page tables dump for CONFIG_KASAN=y Andrey Ryabinin
2017-07-24 15:37 ` Kirill A. Shutemov
2017-07-24 15:57   ` Andrey Ryabinin
2017-07-24 17:54     ` Kirill A. Shutemov
2017-07-25 13:50 ` [tip:x86/mm] " tip-bot for Andrey Ryabinin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome