Signed-off-by: Andi Kleen --- arch/x86/mm/pageattr-test.c | 6 +++++- arch/x86/mm/pageattr_32.c | 23 +++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) Index: linux/arch/x86/mm/pageattr-test.c =================================================================== --- linux.orig/arch/x86/mm/pageattr-test.c +++ linux/arch/x86/mm/pageattr-test.c @@ -15,7 +15,7 @@ #include enum { - NTEST = 400, + NTEST = 5, #ifdef CONFIG_X86_64 LOWEST_LEVEL = 4, LPS = (1 << PMD_SHIFT), @@ -137,8 +137,10 @@ static __init int exercise_pageattr(void unsigned long pfn = random32() % max_mapped; addr[i] = (unsigned long)__va(pfn << PAGE_SHIFT); len[i] = random32() % 100; +#if 0 len[i] = min_t(unsigned long, len[i], max_mapped - pfn - 1); if (len[i] == 0) +#endif len[i] = 1; pte = NULL; @@ -167,6 +169,8 @@ static __init int exercise_pageattr(void continue; } + printk("%lx len %d prot1 %Lx\n", addr[i], len[i], + pgprot_val(pte_pgprot(pte0))); err = change_page_attr(virt_to_page(addr[i]), len[i], pte_pgprot(pte_clrhuge(pte_clrglobal(pte0)))); if (err < 0) { Index: linux/arch/x86/mm/pageattr_32.c =================================================================== --- linux.orig/arch/x86/mm/pageattr_32.c +++ linux/arch/x86/mm/pageattr_32.c @@ -220,6 +220,11 @@ static void set_tlb_flush(unsigned long full_flush = FLUSH_TLB; } +void track(struct page *p, char *what) +{ + printk("cpa kpte %s %lx to %lu\n", what, page_to_pfn(p), page_private(p)); +} + static int __change_page_attr(struct page *page, pgprot_t prot) { @@ -237,8 +242,11 @@ __change_page_attr(struct page *page, pg if (!kpte) return -EINVAL; + oldprot = pte_pgprot(*kpte); kpte_page = virt_to_page(kpte); + + BUG_ON(PageLRU(kpte_page)); BUG_ON(PageCompound(kpte_page)); @@ -259,10 +267,17 @@ __change_page_attr(struct page *page, pg ref_prot = canon_pgprot(ref_prot); prot = canon_pgprot(prot); + printk("cpa %lx %Lx->%Lx ref %Lx level %d ref %lu\n", address, + pgprot_val(oldprot), pgprot_val(prot), + pgprot_val(ref_prot), level, page_private(kpte_page)); + if (pgprot_val(prot) != pgprot_val(ref_prot)) { if (level == 3) { - if (pgprot_val(oldprot) == pgprot_val(ref_prot)) + if (pgprot_val(oldprot) == pgprot_val(ref_prot)) { page_private(kpte_page)++; + track(kpte_page, "change from ref_prot"); + } else + track(kpte_page, "change from non-ref"); set_pte_atomic(kpte, mk_pte(page, prot)); } else { struct page *split; @@ -272,12 +287,15 @@ __change_page_attr(struct page *page, pg set_pmd_pte(kpte,address,mk_pte(split, ref_prot)); kpte_page = split; page_private(kpte_page)++; + track(kpte_page, "split"); } } else if (level == 3) { if (pgprot_val(oldprot) != pgprot_val(ref_prot)) { BUG_ON(page_private(kpte_page) <= 0); page_private(kpte_page)--; - } + track(kpte_page, "change from non ref to ref"); + } else + track(kpte_page, "change from ref to ref level 3"); set_pte_atomic(kpte, mk_pte(page, ref_prot)); } else { /* @@ -300,6 +318,7 @@ __change_page_attr(struct page *page, pg if (cpu_has_pse && (page_private(kpte_page) == 0)) { save_page(kpte_page); paravirt_release_pt(page_to_pfn(kpte_page)); + track(kpte_page, "reverting"); revert_page(kpte_page, address, ref_prot); } }