* [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