Message ID | 20210702103225.51448-1-gshan@redhat.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | mm/debug_vm_pgtable: Fix corrupted PG_arch_1 by set_pmd_at() | expand |
Hi Gavin, Thank you for the patch! Yet something to improve: [auto build test ERROR on linus/master] [cannot apply to hnaz-linux-mm/master linux/master v5.13 next-20210701] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Gavin-Shan/mm-debug_vm_pgtable-Fix-corrupted-PG_arch_1-by-set_pmd_at/20210702-183310 base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 3dbdb38e286903ec220aaf1fb29a8d94297da246 config: x86_64-randconfig-a004-20210702 (attached as .config) compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 9eb613b2de3163686b1a4bd1160f15ac56a4b083) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install x86_64 cross compiling tool for clang build # apt-get install binutils-x86-64-linux-gnu # https://github.com/0day-ci/linux/commit/414db1c0feb54b545b3df56bc19ffff27580deb5 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Gavin-Shan/mm-debug_vm_pgtable-Fix-corrupted-PG_arch_1-by-set_pmd_at/20210702-183310 git checkout 414db1c0feb54b545b3df56bc19ffff27580deb5 # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): >> mm/debug_vm_pgtable.c:347:22: error: invalid operands to binary expression ('struct page *' and 'struct page *') struct page *page = pfn_to_page(page); ^~~~~~~~~~~~~~~~~ include/asm-generic/memory_model.h:53:21: note: expanded from macro 'pfn_to_page' #define pfn_to_page __pfn_to_page ^ include/asm-generic/memory_model.h:25:37: note: expanded from macro '__pfn_to_page' #define __pfn_to_page(pfn) (vmemmap + (pfn)) ~~~~~~~ ^ ~~~~~ 1 error generated. vim +347 mm/debug_vm_pgtable.c 341 342 static void __init pud_advanced_tests(struct mm_struct *mm, 343 struct vm_area_struct *vma, pud_t *pudp, 344 unsigned long pfn, unsigned long vaddr, 345 pgprot_t prot) 346 { > 347 struct page *page = pfn_to_page(page); 348 pud_t pud; 349 350 if (!has_transparent_hugepage()) 351 return; 352 353 pr_debug("Validating PUD advanced\n"); 354 /* Align the address wrt HPAGE_PUD_SIZE */ 355 vaddr &= HPAGE_PUD_MASK; 356 357 pud = pfn_pud(pfn, prot); 358 set_pud_at(mm, vaddr, pudp, pud); 359 flush_dcache_page(page); 360 pudp_set_wrprotect(mm, vaddr, pudp); 361 pud = READ_ONCE(*pudp); 362 WARN_ON(pud_write(pud)); 363 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
On 7/2/21 8:32 PM, Gavin Shan wrote: > There are two addresses selected: random virtual address and physical > address corresponding to kernel symbol @start_kernel. During the PMD > tests in pmd_advanced_tests(), the physical address is aligned down > to the starting address of the huge page, whose size is 512MB on ARM64 > when we have 64KB base page size. After that, set_pmd_at() is called > to populate the PMD entry. PG_arch_1, PG_dcache_clean on ARM64, is > set to the page flags. Unforunately, the page, corresponding to the > starting address of the huge page could be owned by buddy. It means > PG_arch_1 can be unconditionally set to page owned by buddy. > > Afterwards, the page with PG_arch_1 set is fetched from buddy's free > area list, but fails the checking. It leads to the following warning > on ARM64: > > BUG: Bad page state in process memhog pfn:08000 > page:0000000015c0a628 refcount:0 mapcount:0 \ > mapping:0000000000000000 index:0x1 pfn:0x8000 > flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff) > raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000 > raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000 > page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set > > This fixes the issue by calling flush_dcache_page() after each call > to set_{pud, pmd, pte}_at() because PG_arch_1 isn't needed in any case. > > Fixes: a5c3b9ffb0f4 ("mm/debug_vm_pgtable: add tests validating advanced arch page table helpers") > Cc: stable@vger.kernel.org # v5.9+ > Signed-off-by: Gavin Shan <gshan@redhat.com> > --- > mm/debug_vm_pgtable.c | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > > diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c > index 92bfc37300df..7dedf6c6dd25 100644 > --- a/mm/debug_vm_pgtable.c > +++ b/mm/debug_vm_pgtable.c > @@ -29,6 +29,8 @@ > #include <linux/start_kernel.h> > #include <linux/sched/mm.h> > #include <linux/io.h> > + > +#include <asm/cacheflush.h> > #include <asm/pgalloc.h> > #include <asm/tlbflush.h> > > @@ -91,6 +93,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, > unsigned long pfn, unsigned long vaddr, > pgprot_t prot) > { > + struct page *page = pfn_to_page(pfn); > pte_t pte = pfn_pte(pfn, prot); > > /* > @@ -102,6 +105,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, > pr_debug("Validating PTE advanced\n"); > pte = pfn_pte(pfn, prot); > set_pte_at(mm, vaddr, ptep, pte); > + flush_dcache_page(page); > ptep_set_wrprotect(mm, vaddr, ptep); > pte = ptep_get(ptep); > WARN_ON(pte_write(pte)); > @@ -113,6 +117,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, > pte = pte_wrprotect(pte); > pte = pte_mkclean(pte); > set_pte_at(mm, vaddr, ptep, pte); > + flush_dcache_page(page); > pte = pte_mkwrite(pte); > pte = pte_mkdirty(pte); > ptep_set_access_flags(vma, vaddr, ptep, pte, 1); > @@ -125,6 +130,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, > pte = pfn_pte(pfn, prot); > pte = pte_mkyoung(pte); > set_pte_at(mm, vaddr, ptep, pte); > + flush_dcache_page(page); > ptep_test_and_clear_young(vma, vaddr, ptep); > pte = ptep_get(ptep); > WARN_ON(pte_young(pte)); > @@ -186,6 +192,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, > unsigned long pfn, unsigned long vaddr, > pgprot_t prot, pgtable_t pgtable) > { > + struct page *page = pfn_to_page(pfn); > pmd_t pmd; > > if (!has_transparent_hugepage()) > @@ -199,6 +206,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, > > pmd = pfn_pmd(pfn, prot); > set_pmd_at(mm, vaddr, pmdp, pmd); > + flush_dcache_page(page); > pmdp_set_wrprotect(mm, vaddr, pmdp); > pmd = READ_ONCE(*pmdp); > WARN_ON(pmd_write(pmd)); > @@ -210,6 +218,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, > pmd = pmd_wrprotect(pmd); > pmd = pmd_mkclean(pmd); > set_pmd_at(mm, vaddr, pmdp, pmd); > + flush_dcache_page(page); > pmd = pmd_mkwrite(pmd); > pmd = pmd_mkdirty(pmd); > pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1); > @@ -222,6 +231,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, > pmd = pmd_mkhuge(pfn_pmd(pfn, prot)); > pmd = pmd_mkyoung(pmd); > set_pmd_at(mm, vaddr, pmdp, pmd); > + flush_dcache_page(page); > pmdp_test_and_clear_young(vma, vaddr, pmdp); > pmd = READ_ONCE(*pmdp); > WARN_ON(pmd_young(pmd)); > @@ -334,6 +344,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, > unsigned long pfn, unsigned long vaddr, > pgprot_t prot) > { > + struct page *page = pfn_to_page(page); > pud_t pud; > Typo here. @page should be replaced by @pfn. I'm holding to post v2 until I receive comments on v1. > if (!has_transparent_hugepage()) > @@ -345,6 +356,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, > > pud = pfn_pud(pfn, prot); > set_pud_at(mm, vaddr, pudp, pud); > + flush_dcache_page(page); > pudp_set_wrprotect(mm, vaddr, pudp); > pud = READ_ONCE(*pudp); > WARN_ON(pud_write(pud)); > @@ -358,6 +370,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, > pud = pud_wrprotect(pud); > pud = pud_mkclean(pud); > set_pud_at(mm, vaddr, pudp, pud); > + flush_dcache_page(page); > pud = pud_mkwrite(pud); > pud = pud_mkdirty(pud); > pudp_set_access_flags(vma, vaddr, pudp, pud, 1); > @@ -373,6 +386,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, > pud = pfn_pud(pfn, prot); > pud = pud_mkyoung(pud); > set_pud_at(mm, vaddr, pudp, pud); > + flush_dcache_page(page); > pudp_test_and_clear_young(vma, vaddr, pudp); > pud = READ_ONCE(*pudp); > WARN_ON(pud_young(pud)); > @@ -604,6 +618,7 @@ static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep, > unsigned long pfn, unsigned long vaddr, > pgprot_t prot) > { > + struct page *page = pfn_to_page(pfn); > pte_t pte = pfn_pte(pfn, prot); > > pr_debug("Validating PTE clear\n"); > @@ -611,6 +626,7 @@ static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep, > pte = __pte(pte_val(pte) | RANDOM_ORVALUE); > #endif > set_pte_at(mm, vaddr, ptep, pte); > + flush_dcache_page(page); > barrier(); > pte_clear(mm, vaddr, ptep); > pte = ptep_get(ptep); > Thanks, Gavin
Hello Gavin, On 7/2/21 4:02 PM, Gavin Shan wrote: > There are two addresses selected: random virtual address and physical > address corresponding to kernel symbol @start_kernel. During the PMD > tests in pmd_advanced_tests(), the physical address is aligned down > to the starting address of the huge page, whose size is 512MB on ARM64 > when we have 64KB base page size. After that, set_pmd_at() is called > to populate the PMD entry. PG_arch_1, PG_dcache_clean on ARM64, is > set to the page flags. Unforunately, the page, corresponding to the > starting address of the huge page could be owned by buddy. It means > PG_arch_1 can be unconditionally set to page owned by buddy. > > Afterwards, the page with PG_arch_1 set is fetched from buddy's free > area list, but fails the checking. It leads to the following warning > on ARM64: > > BUG: Bad page state in process memhog pfn:08000 > page:0000000015c0a628 refcount:0 mapcount:0 \ > mapping:0000000000000000 index:0x1 pfn:0x8000 > flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff) > raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000 > raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000 > page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set Does this problem happen right after the boot ? OR you ran some tests and workloads to trigger this ? IIRC never seen this before on arm64. Does this happen on other archs too ? > > This fixes the issue by calling flush_dcache_page() after each call > to set_{pud, pmd, pte}_at() because PG_arch_1 isn't needed in any case. This (arm64 specific solution) might cause some side effects on other platforms ? The solution here needs to be generic enough. I will take a look into this patch but probably later this week or next week. - Anshuman
Hi Anshuman, On 7/5/21 1:59 PM, Anshuman Khandual wrote: > On 7/2/21 4:02 PM, Gavin Shan wrote: >> There are two addresses selected: random virtual address and physical >> address corresponding to kernel symbol @start_kernel. During the PMD >> tests in pmd_advanced_tests(), the physical address is aligned down >> to the starting address of the huge page, whose size is 512MB on ARM64 >> when we have 64KB base page size. After that, set_pmd_at() is called >> to populate the PMD entry. PG_arch_1, PG_dcache_clean on ARM64, is >> set to the page flags. Unforunately, the page, corresponding to the >> starting address of the huge page could be owned by buddy. It means >> PG_arch_1 can be unconditionally set to page owned by buddy. >> >> Afterwards, the page with PG_arch_1 set is fetched from buddy's free >> area list, but fails the checking. It leads to the following warning >> on ARM64: >> >> BUG: Bad page state in process memhog pfn:08000 >> page:0000000015c0a628 refcount:0 mapcount:0 \ >> mapping:0000000000000000 index:0x1 pfn:0x8000 >> flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff) >> raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000 >> raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000 >> page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set > > Does this problem happen right after the boot ? OR you ran some tests > and workloads to trigger this ? IIRC never seen this before on arm64. > Does this happen on other archs too ? > The page flag (PG_arch_1) is corrupted during boot on ARM64 where 64KB base page size is selected, but the failing page check happens when the page is pulled from buddy's free area list by "memhog". I don't think other platform has same issue. >> >> This fixes the issue by calling flush_dcache_page() after each call >> to set_{pud, pmd, pte}_at() because PG_arch_1 isn't needed in any case. > > This (arm64 specific solution) might cause some side effects on other > platforms ? The solution here needs to be generic enough. I will take > a look into this patch but probably later this week or next week. > Apart from the overhead of flushing the dcache introduced by flush_dcache_page(). I don't think there is any side-effect. By the way, I'm working on a series to fix this issue and another issue. I will post the series for review pretty soon and it's going to fix the following issues: (1) Current code is organized in relaxed fashion. All information are maintained in variables in debug_vm_pgtable(). The variables are passed to test functions. It make the code hard to be maintained in long term. So I will introduce a dedicated data struct (struct vm_pgtable_debug), as place holder for various information. (2) With the data struct, I'm able to allocate page, to be used by set_{pud, pmd, pte}_at() because the target page is accessed on ARM64. The PG_arch_1 flag is set to the page and the corresponding iCache is flush if execution permission is given. There are two issues if the page used by set_{pud, pmd, pte}_at() wasn't allocated from buddy: (a) the PG_arch_1 flag corruption as this patch tries to fix; (b) kernel crash because of invalid page fault on accessing the target page. The page isn't mapped if CONFIG_DEBUG_PAGEALLOC is enabled. start_kernel mm_init mem_init memblock_free_all free_low_memory_core_early __free_memory_core __free_pages_memory memblock_free_pages __free_pages_core __free_pages_ok free_pages_prepare debug_pagealloc_unmap_pages # The page is unmapped here Thanks, Gavin
Hi Gavin,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master]
[cannot apply to hnaz-linux-mm/master linux/master v5.13 next-20210707]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Gavin-Shan/mm-debug_vm_pgtable-Fix-corrupted-PG_arch_1-by-set_pmd_at/20210702-183310
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 3dbdb38e286903ec220aaf1fb29a8d94297da246
config: x86_64-allyesconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
# https://github.com/0day-ci/linux/commit/414db1c0feb54b545b3df56bc19ffff27580deb5
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Gavin-Shan/mm-debug_vm_pgtable-Fix-corrupted-PG_arch_1-by-set_pmd_at/20210702-183310
git checkout 414db1c0feb54b545b3df56bc19ffff27580deb5
# save the attached .config to linux build tree
make W=1 ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
In file included from arch/x86/include/asm/page.h:76,
from arch/x86/include/asm/thread_info.h:12,
from include/linux/thread_info.h:59,
from arch/x86/include/asm/preempt.h:7,
from include/linux/preempt.h:78,
from include/linux/spinlock.h:51,
from include/linux/mmzone.h:8,
from include/linux/gfp.h:6,
from mm/debug_vm_pgtable.c:13:
mm/debug_vm_pgtable.c: In function 'pud_advanced_tests':
>> include/asm-generic/memory_model.h:25:37: error: invalid operands to binary + (have 'struct page *' and 'struct page *')
25 | #define __pfn_to_page(pfn) (vmemmap + (pfn))
| ^
include/asm-generic/memory_model.h:53:21: note: in expansion of macro '__pfn_to_page'
53 | #define pfn_to_page __pfn_to_page
| ^~~~~~~~~~~~~
mm/debug_vm_pgtable.c:347:22: note: in expansion of macro 'pfn_to_page'
347 | struct page *page = pfn_to_page(page);
| ^~~~~~~~~~~
Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for PHY_SPARX5_SERDES
Depends on (ARCH_SPARX5 || COMPILE_TEST && OF && HAS_IOMEM
Selected by
- SPARX5_SWITCH && NETDEVICES && ETHERNET && NET_VENDOR_MICROCHIP && NET_SWITCHDEV && HAS_IOMEM
vim +25 include/asm-generic/memory_model.h
8f6aac419bd590 Christoph Lameter 2007-10-16 23
af901ca181d92a André Goddard Rosa 2009-11-14 24 /* memmap is virtually contiguous. */
8f6aac419bd590 Christoph Lameter 2007-10-16 @25 #define __pfn_to_page(pfn) (vmemmap + (pfn))
32272a26974d20 Martin Schwidefsky 2008-12-25 26 #define __page_to_pfn(page) (unsigned long)((page) - vmemmap)
8f6aac419bd590 Christoph Lameter 2007-10-16 27
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
On 7/8/21 6:05 AM, kernel test robot wrote: > Hi Gavin, > > Thank you for the patch! Yet something to improve: > > [auto build test ERROR on linus/master] > [cannot apply to hnaz-linux-mm/master linux/master v5.13 next-20210707] > [If your patch is applied to the wrong git tree, kindly drop us a note. > And when submitting patch, we suggest to use '--base' as documented in > https://git-scm.com/docs/git-format-patch] > > url: https://github.com/0day-ci/linux/commits/Gavin-Shan/mm-debug_vm_pgtable-Fix-corrupted-PG_arch_1-by-set_pmd_at/20210702-183310 > base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 3dbdb38e286903ec220aaf1fb29a8d94297da246 > config: x86_64-allyesconfig (attached as .config) > compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 > reproduce (this is a W=1 build): > # https://github.com/0day-ci/linux/commit/414db1c0feb54b545b3df56bc19ffff27580deb5 > git remote add linux-review https://github.com/0day-ci/linux > git fetch --no-tags linux-review Gavin-Shan/mm-debug_vm_pgtable-Fix-corrupted-PG_arch_1-by-set_pmd_at/20210702-183310 > git checkout 414db1c0feb54b545b3df56bc19ffff27580deb5 > # save the attached .config to linux build tree > make W=1 ARCH=x86_64 > > If you fix the issue, kindly add following tag as appropriate > Reported-by: kernel test robot <lkp@intel.com> > > All errors (new ones prefixed by >>): > > In file included from arch/x86/include/asm/page.h:76, > from arch/x86/include/asm/thread_info.h:12, > from include/linux/thread_info.h:59, > from arch/x86/include/asm/preempt.h:7, > from include/linux/preempt.h:78, > from include/linux/spinlock.h:51, > from include/linux/mmzone.h:8, > from include/linux/gfp.h:6, > from mm/debug_vm_pgtable.c:13: > mm/debug_vm_pgtable.c: In function 'pud_advanced_tests': >>> include/asm-generic/memory_model.h:25:37: error: invalid operands to binary + (have 'struct page *' and 'struct page *') Hello Gavin, So this problem has been fixed in the other series (which now includes this patch) you have posted ? - Anshuman > 25 | #define __pfn_to_page(pfn) (vmemmap + (pfn)) > | ^ > include/asm-generic/memory_model.h:53:21: note: in expansion of macro '__pfn_to_page' > 53 | #define pfn_to_page __pfn_to_page > | ^~~~~~~~~~~~~ > mm/debug_vm_pgtable.c:347:22: note: in expansion of macro 'pfn_to_page' > 347 | struct page *page = pfn_to_page(page); > | ^~~~~~~~~~~ > > Kconfig warnings: (for reference only) > WARNING: unmet direct dependencies detected for PHY_SPARX5_SERDES > Depends on (ARCH_SPARX5 || COMPILE_TEST && OF && HAS_IOMEM > Selected by > - SPARX5_SWITCH && NETDEVICES && ETHERNET && NET_VENDOR_MICROCHIP && NET_SWITCHDEV && HAS_IOMEM > > > vim +25 include/asm-generic/memory_model.h > > 8f6aac419bd590 Christoph Lameter 2007-10-16 23 > af901ca181d92a André Goddard Rosa 2009-11-14 24 /* memmap is virtually contiguous. */ > 8f6aac419bd590 Christoph Lameter 2007-10-16 @25 #define __pfn_to_page(pfn) (vmemmap + (pfn)) > 32272a26974d20 Martin Schwidefsky 2008-12-25 26 #define __page_to_pfn(page) (unsigned long)((page) - vmemmap) > 8f6aac419bd590 Christoph Lameter 2007-10-16 27 > > --- > 0-DAY CI Kernel Test Service, Intel Corporation > https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org >
On 7/12/21 1:21 PM, Anshuman Khandual wrote: > > > On 7/8/21 6:05 AM, kernel test robot wrote: >> Hi Gavin, >> >> Thank you for the patch! Yet something to improve: >> >> [auto build test ERROR on linus/master] >> [cannot apply to hnaz-linux-mm/master linux/master v5.13 next-20210707] >> [If your patch is applied to the wrong git tree, kindly drop us a note. >> And when submitting patch, we suggest to use '--base' as documented in >> https://git-scm.com/docs/git-format-patch] >> >> url: https://github.com/0day-ci/linux/commits/Gavin-Shan/mm-debug_vm_pgtable-Fix-corrupted-PG_arch_1-by-set_pmd_at/20210702-183310 >> base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 3dbdb38e286903ec220aaf1fb29a8d94297da246 >> config: x86_64-allyesconfig (attached as .config) >> compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 >> reproduce (this is a W=1 build): >> # https://github.com/0day-ci/linux/commit/414db1c0feb54b545b3df56bc19ffff27580deb5 >> git remote add linux-review https://github.com/0day-ci/linux >> git fetch --no-tags linux-review Gavin-Shan/mm-debug_vm_pgtable-Fix-corrupted-PG_arch_1-by-set_pmd_at/20210702-183310 >> git checkout 414db1c0feb54b545b3df56bc19ffff27580deb5 >> # save the attached .config to linux build tree >> make W=1 ARCH=x86_64 >> >> If you fix the issue, kindly add following tag as appropriate >> Reported-by: kernel test robot <lkp@intel.com> >> >> All errors (new ones prefixed by >>): >> >> In file included from arch/x86/include/asm/page.h:76, >> from arch/x86/include/asm/thread_info.h:12, >> from include/linux/thread_info.h:59, >> from arch/x86/include/asm/preempt.h:7, >> from include/linux/preempt.h:78, >> from include/linux/spinlock.h:51, >> from include/linux/mmzone.h:8, >> from include/linux/gfp.h:6, >> from mm/debug_vm_pgtable.c:13: >> mm/debug_vm_pgtable.c: In function 'pud_advanced_tests': >>>> include/asm-generic/memory_model.h:25:37: error: invalid operands to binary + (have 'struct page *' and 'struct page *') > > > So this problem has been fixed in the other series (which now > includes this patch) you have posted ? > Yes, Anshuman. Please ignore this one. The fix has been included into the following series. The last patch [12/12] of that series fixes the issue. [PATCH 00/12] mm/debug_vm_pgtable: Enhancements Thanks, Gavin
diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c index 92bfc37300df..7dedf6c6dd25 100644 --- a/mm/debug_vm_pgtable.c +++ b/mm/debug_vm_pgtable.c @@ -29,6 +29,8 @@ #include <linux/start_kernel.h> #include <linux/sched/mm.h> #include <linux/io.h> + +#include <asm/cacheflush.h> #include <asm/pgalloc.h> #include <asm/tlbflush.h> @@ -91,6 +93,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, unsigned long pfn, unsigned long vaddr, pgprot_t prot) { + struct page *page = pfn_to_page(pfn); pte_t pte = pfn_pte(pfn, prot); /* @@ -102,6 +105,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, pr_debug("Validating PTE advanced\n"); pte = pfn_pte(pfn, prot); set_pte_at(mm, vaddr, ptep, pte); + flush_dcache_page(page); ptep_set_wrprotect(mm, vaddr, ptep); pte = ptep_get(ptep); WARN_ON(pte_write(pte)); @@ -113,6 +117,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, pte = pte_wrprotect(pte); pte = pte_mkclean(pte); set_pte_at(mm, vaddr, ptep, pte); + flush_dcache_page(page); pte = pte_mkwrite(pte); pte = pte_mkdirty(pte); ptep_set_access_flags(vma, vaddr, ptep, pte, 1); @@ -125,6 +130,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, pte = pfn_pte(pfn, prot); pte = pte_mkyoung(pte); set_pte_at(mm, vaddr, ptep, pte); + flush_dcache_page(page); ptep_test_and_clear_young(vma, vaddr, ptep); pte = ptep_get(ptep); WARN_ON(pte_young(pte)); @@ -186,6 +192,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, unsigned long pfn, unsigned long vaddr, pgprot_t prot, pgtable_t pgtable) { + struct page *page = pfn_to_page(pfn); pmd_t pmd; if (!has_transparent_hugepage()) @@ -199,6 +206,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, pmd = pfn_pmd(pfn, prot); set_pmd_at(mm, vaddr, pmdp, pmd); + flush_dcache_page(page); pmdp_set_wrprotect(mm, vaddr, pmdp); pmd = READ_ONCE(*pmdp); WARN_ON(pmd_write(pmd)); @@ -210,6 +218,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, pmd = pmd_wrprotect(pmd); pmd = pmd_mkclean(pmd); set_pmd_at(mm, vaddr, pmdp, pmd); + flush_dcache_page(page); pmd = pmd_mkwrite(pmd); pmd = pmd_mkdirty(pmd); pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1); @@ -222,6 +231,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, pmd = pmd_mkhuge(pfn_pmd(pfn, prot)); pmd = pmd_mkyoung(pmd); set_pmd_at(mm, vaddr, pmdp, pmd); + flush_dcache_page(page); pmdp_test_and_clear_young(vma, vaddr, pmdp); pmd = READ_ONCE(*pmdp); WARN_ON(pmd_young(pmd)); @@ -334,6 +344,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, unsigned long pfn, unsigned long vaddr, pgprot_t prot) { + struct page *page = pfn_to_page(page); pud_t pud; if (!has_transparent_hugepage()) @@ -345,6 +356,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, pud = pfn_pud(pfn, prot); set_pud_at(mm, vaddr, pudp, pud); + flush_dcache_page(page); pudp_set_wrprotect(mm, vaddr, pudp); pud = READ_ONCE(*pudp); WARN_ON(pud_write(pud)); @@ -358,6 +370,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, pud = pud_wrprotect(pud); pud = pud_mkclean(pud); set_pud_at(mm, vaddr, pudp, pud); + flush_dcache_page(page); pud = pud_mkwrite(pud); pud = pud_mkdirty(pud); pudp_set_access_flags(vma, vaddr, pudp, pud, 1); @@ -373,6 +386,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, pud = pfn_pud(pfn, prot); pud = pud_mkyoung(pud); set_pud_at(mm, vaddr, pudp, pud); + flush_dcache_page(page); pudp_test_and_clear_young(vma, vaddr, pudp); pud = READ_ONCE(*pudp); WARN_ON(pud_young(pud)); @@ -604,6 +618,7 @@ static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep, unsigned long pfn, unsigned long vaddr, pgprot_t prot) { + struct page *page = pfn_to_page(pfn); pte_t pte = pfn_pte(pfn, prot); pr_debug("Validating PTE clear\n"); @@ -611,6 +626,7 @@ static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep, pte = __pte(pte_val(pte) | RANDOM_ORVALUE); #endif set_pte_at(mm, vaddr, ptep, pte); + flush_dcache_page(page); barrier(); pte_clear(mm, vaddr, ptep); pte = ptep_get(ptep);
There are two addresses selected: random virtual address and physical address corresponding to kernel symbol @start_kernel. During the PMD tests in pmd_advanced_tests(), the physical address is aligned down to the starting address of the huge page, whose size is 512MB on ARM64 when we have 64KB base page size. After that, set_pmd_at() is called to populate the PMD entry. PG_arch_1, PG_dcache_clean on ARM64, is set to the page flags. Unforunately, the page, corresponding to the starting address of the huge page could be owned by buddy. It means PG_arch_1 can be unconditionally set to page owned by buddy. Afterwards, the page with PG_arch_1 set is fetched from buddy's free area list, but fails the checking. It leads to the following warning on ARM64: BUG: Bad page state in process memhog pfn:08000 page:0000000015c0a628 refcount:0 mapcount:0 \ mapping:0000000000000000 index:0x1 pfn:0x8000 flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff) raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000 raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000 page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set This fixes the issue by calling flush_dcache_page() after each call to set_{pud, pmd, pte}_at() because PG_arch_1 isn't needed in any case. Fixes: a5c3b9ffb0f4 ("mm/debug_vm_pgtable: add tests validating advanced arch page table helpers") Cc: stable@vger.kernel.org # v5.9+ Signed-off-by: Gavin Shan <gshan@redhat.com> --- mm/debug_vm_pgtable.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)