Linux M68K Architecture development
 help / color / mirror / Atom feed
* Re: [PATCH v4] mm/memory: use pmdp_get_lockless() for the pmd read in do_fault_around()
       [not found] <20261104091050.117076-1-hui.zhu@linux.dev>
@ 2026-11-05 12:00 ` David Hildenbrand (Arm)
  2026-11-06  3:03   ` Hui Zhu
  0 siblings, 1 reply; 6+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-05 12:00 UTC (permalink / raw)
  To: Hui Zhu, Andrew Morton, Lorenzo Stoakes, Liam R. Howlett,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	linux-mm, linux-kernel, Geert Uytterhoeven, Sam Creasey
  Cc: Hui Zhu, stable+noautosel, linux-m68k

On 11/4/26 10:10, Hui Zhu wrote:
> From: Hui Zhu <zhuhui@kylinos.cn>
> 
> KCSAN reports a data-race in do_read_fault / pmd_install; the same code
> is in mainline:
> 
>   write (marked) to 0xffff8d52a75fb810 of 8 bytes by task 27487 on cpu 5:
>    native_set_pmd arch/x86/include/asm/pgtable_64.h:83 [inline]
>    pmd_populate arch/x86/include/asm/pgalloc.h:84 [inline]
>    pmd_install+0xe0/0x120 mm/memory.c:444
>    finish_fault+0x5bc/0xb00 mm/memory.c:5204
>    do_cow_fault mm/memory.c:5432 [inline]
>    do_fault+0x5d0/0x6e0 mm/memory.c:5526
>    handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>    __mm_populate+0x144/0x270 mm/gup.c:2207
>    __do_sys_mlockall+0x2cd/0x380 mm/mlock.c:764
> 
>   read to 0xffff8d52a75fb810 of 8 bytes by task 27527 on cpu 9:
>    do_fault_around mm/memory.c:5341 [inline]
>    do_read_fault+0x162/0x670 mm/memory.c:5381
>    do_fault+0x31a/0x6e0 mm/memory.c:5524
>    handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>    faultin_page_range+0x227/0x530 mm/gup.c:2156
>    __x64_sys_madvise+0x67/0x80 mm/madvise.c:1479
> 
>   value changed: 0x0000000000000000 -> 0x000000001386c067
> 
> Both paths only hold mmap_lock for read, so do_fault_around() tests
> vmf->pmd while pmd_install() populates it under the pmd lock.  The race is
> benign: a stale pmd_none() only wastes the prealloc_pte, which do_fault()
> frees again, and a pmd seen as populated only makes ->map_pages() find no
> PTE and do_read_fault() fall back to __do_fault().
> 
> The load still has to be lockless-safe.  The store side already is: on
> i386-PAE, pmd_install() publishes both halves of the pmd with a single
> cmpxchg8b.  The load side is not.  pmdp_get() is a plain READ_ONCE() over
> a pmd_t that is wider than a word there, so it becomes two 32-bit loads
> and can return the low half of one pmd together with the high half of
> another.  Use pmdp_get_lockless(), the accessor for pmd reads that do not
> hold the pmd lock: it re-reads until both halves come from the same pmd.
> It is also what __pte_offset_map() already uses on this very pmd a few
> instructions later.
> 
> Only X86_32+X86_PAE and SH+X2TLB compile the retry loop, these being the
> GUP_GET_PXX_LOW_HIGH selectors that have more than two page table levels.
> mips32 with 64-bit physical addresses selects GUP_GET_PXX_LOW_HIGH too,
> but is 2-level and so falls back to pmdp_get().  On every other
> configuration, x86-64 included, pmdp_get_lockless() is pmdp_get(): the
> pmd is loaded in one go, the READ_ONCE() silences the report the same
> way, and there is no functional change.
> 
> Fixes: 7267ec008b5c ("mm: postpone page table allocation until we have page to map")
> Cc: stable+noautosel@kernel.org # KCSAN annotation only, no functional change
> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
> Acked-by: Lorenzo Stoakes <ljs@kernel.org>
> ---
> Changelog:
> v4:
> Commit message only, the diff is identical to v3.  Spell out the affected
> arches and add the lore links for v1-v3 (Lorenzo Stoakes).  Reword the
> cmpxchg8b sentence: the store is already atomic, it is the load that tears.
> Add the Fixes: tag (David Hildenbrand).
> v3:
> https://lore.kernel.org/all/20261103014749.96764-1-hui.zhu@linux.dev/
> Shorten the comment to one line (David Hildenbrand).
> v2:
> https://lore.kernel.org/all/20261102093120.92654-1-hui.zhu@linux.dev/
> Use pmdp_get_lockless() instead of pmdp_get() (David Hildenbrand).
> v1:
> https://lore.kernel.org/all/20261102031308.66177-1-hui.zhu@linux.dev/
> 
>  mm/memory.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/mm/memory.c b/mm/memory.c
> index 0fb676dd2bd63..f25c2ce0ef153 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -6017,7 +6017,8 @@ static vm_fault_t do_fault_around(struct vm_fault *vmf)
>  	to_pte = min3(from_pte + nr_pages, (pgoff_t)PTRS_PER_PTE,
>  		      pte_off + vma_pages(vmf->vma) - vma_off) - 1;
>  
> -	if (pmd_none(*vmf->pmd)) {
> +	/* pmd_install() rechecks pmd_none() under the pmd lock. */
> +	if (pmd_none(pmdp_get_lockless(vmf->pmd))) {
>  		vmf->prealloc_pte = pte_alloc_one(vmf->vma->vm_mm);
>  		if (!vmf->prealloc_pte)
>  			return VM_FAULT_OOM;

Fun, this breaks some odd m68k configs because they do some legacy weird stuff with pmd_none().

I'll throw in the following cleanup. CCing m68k maintainers: I would drag
this along with this fix here unless you disagree.

From e3c739933a04743e3923f6f39a603e93f2700304 Mon Sep 17 00:00:00 2001
From: "David Hildenbrand (Arm)" <david@kernel.org>
Date: Thu, 5 Nov 2026 12:49:38 +0100
Subject: [PATCH] m68k/mm: simplify
 pmd_none()/pmd_val()/pmd_present()/pmd_bad()

pmd_t on m68k used to contain 16 words. Commit ef22d8abd876 ("m68k:
mm: Restructure Motorola MMU page-table layout") reduced Motorola PMDs
to one word; Sun3 and ColdFire use word-sized folded PMDs.

Use direct member access in pmd_val() and replace the Sun3 and ColdFire
pointer-taking predicate wrappers with inline functions taking values.

This allows us to write something like:

  if (pmd_none(pmdp_get(pmdp)))

While at it remove unused pmd_present2() and commented-out pmd_bad()
on sun3.

Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
 arch/m68k/include/asm/mcf_pgtable.h  | 20 +++++++++++++++-----
 arch/m68k/include/asm/page.h         |  2 +-
 arch/m68k/include/asm/sun3_pgtable.h | 23 +++++++++++++++--------
 3 files changed, 31 insertions(+), 14 deletions(-)

diff --git a/arch/m68k/include/asm/mcf_pgtable.h b/arch/m68k/include/asm/mcf_pgtable.h
index f45a882238dbb..4efa8e3e1e98a 100644
--- a/arch/m68k/include/asm/mcf_pgtable.h
+++ b/arch/m68k/include/asm/mcf_pgtable.h
@@ -130,11 +130,21 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr,
 
 #define pte_page(pte)	virt_to_page(__pte_page(pte))
 
-static inline int pmd_none2(pmd_t *pmd) { return !pmd_val(*pmd); }
-#define pmd_none(pmd) pmd_none2(&(pmd))
-static inline int pmd_bad2(pmd_t *pmd) { return 0; }
-#define pmd_bad(pmd) pmd_bad2(&(pmd))
-#define pmd_present(pmd) (!pmd_none2(&(pmd)))
+static inline int pmd_none(pmd_t pmd)
+{
+	return !pmd_val(pmd);
+}
+
+static inline int pmd_bad(pmd_t pmd)
+{
+	return 0;
+}
+
+static inline int pmd_present(pmd_t pmd)
+{
+	return !pmd_none(pmd);
+}
+
 static inline void pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
 
 
diff --git a/arch/m68k/include/asm/page.h b/arch/m68k/include/asm/page.h
index d30f8b2f15927..09a2c020ec992 100644
--- a/arch/m68k/include/asm/page.h
+++ b/arch/m68k/include/asm/page.h
@@ -17,7 +17,7 @@
  */
 #if !defined(CONFIG_MMU) || CONFIG_PGTABLE_LEVELS == 3
 typedef struct { unsigned long pmd; } pmd_t;
-#define pmd_val(x)	((&x)->pmd)
+#define pmd_val(x)	((x).pmd)
 #define __pmd(x)	((pmd_t) { (x) } )
 #endif
 
diff --git a/arch/m68k/include/asm/sun3_pgtable.h b/arch/m68k/include/asm/sun3_pgtable.h
index 704442a391fd7..5867d24032655 100644
--- a/arch/m68k/include/asm/sun3_pgtable.h
+++ b/arch/m68k/include/asm/sun3_pgtable.h
@@ -109,14 +109,21 @@ static inline void pte_clear (struct mm_struct *mm, unsigned long addr, pte_t *p
 #define pmd_page(pmd)		virt_to_page((void *)pmd_page_vaddr(pmd))
 
 
-static inline int pmd_none2 (pmd_t *pmd) { return !pmd_val (*pmd); }
-#define pmd_none(pmd) pmd_none2(&(pmd))
-//static inline int pmd_bad (pmd_t pmd) { return (pmd_val (pmd) & SUN3_PMD_MASK) != SUN3_PMD_MAGIC; }
-static inline int pmd_bad2 (pmd_t *pmd) { return 0; }
-#define pmd_bad(pmd) pmd_bad2(&(pmd))
-static inline int pmd_present2 (pmd_t *pmd) { return pmd_val (*pmd) & SUN3_PMD_VALID; }
-/* #define pmd_present(pmd) pmd_present2(&(pmd)) */
-#define pmd_present(pmd) (!pmd_none2(&(pmd)))
+static inline int pmd_none(pmd_t pmd)
+{
+	return !pmd_val(pmd);
+}
+
+static inline int pmd_bad(pmd_t pmd)
+{
+	return 0;
+}
+
+static inline int pmd_present(pmd_t pmd)
+{
+	return !pmd_none(pmd);
+}
+
 static inline void pmd_clear (pmd_t *pmdp) { pmd_val (*pmdp) = 0; }
 
 /*
-- 
2.43.0


-- 
Cheers,

David

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

* Re: [PATCH v4] mm/memory: use pmdp_get_lockless() for the pmd read in do_fault_around()
  2026-11-05 12:00 ` [PATCH v4] mm/memory: use pmdp_get_lockless() for the pmd read in do_fault_around() David Hildenbrand (Arm)
@ 2026-11-06  3:03   ` Hui Zhu
  2026-11-06 12:08     ` Greg Ungerer
  0 siblings, 1 reply; 6+ messages in thread
From: Hui Zhu @ 2026-11-06  3:03 UTC (permalink / raw)
  To: David Hildenbrand (Arm), Andrew Morton, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, linux-mm, linux-kernel,
	Geert Uytterhoeven, Sam Creasey
  Cc: Hui Zhu, stable+noautosel, linux-m68k


2026/11/5 20:00, David Hildenbrand (Arm) :
> On 11/4/26 10:10, Hui Zhu wrote:
>> From: Hui Zhu <zhuhui@kylinos.cn>
>>
>> KCSAN reports a data-race in do_read_fault / pmd_install; the same code
>> is in mainline:
>>
>>    write (marked) to 0xffff8d52a75fb810 of 8 bytes by task 27487 on cpu 5:
>>     native_set_pmd arch/x86/include/asm/pgtable_64.h:83 [inline]
>>     pmd_populate arch/x86/include/asm/pgalloc.h:84 [inline]
>>     pmd_install+0xe0/0x120 mm/memory.c:444
>>     finish_fault+0x5bc/0xb00 mm/memory.c:5204
>>     do_cow_fault mm/memory.c:5432 [inline]
>>     do_fault+0x5d0/0x6e0 mm/memory.c:5526
>>     handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>>     __mm_populate+0x144/0x270 mm/gup.c:2207
>>     __do_sys_mlockall+0x2cd/0x380 mm/mlock.c:764
>>
>>    read to 0xffff8d52a75fb810 of 8 bytes by task 27527 on cpu 9:
>>     do_fault_around mm/memory.c:5341 [inline]
>>     do_read_fault+0x162/0x670 mm/memory.c:5381
>>     do_fault+0x31a/0x6e0 mm/memory.c:5524
>>     handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>>     faultin_page_range+0x227/0x530 mm/gup.c:2156
>>     __x64_sys_madvise+0x67/0x80 mm/madvise.c:1479
>>
>>    value changed: 0x0000000000000000 -> 0x000000001386c067
>>
>> Both paths only hold mmap_lock for read, so do_fault_around() tests
>> vmf->pmd while pmd_install() populates it under the pmd lock.  The race is
>> benign: a stale pmd_none() only wastes the prealloc_pte, which do_fault()
>> frees again, and a pmd seen as populated only makes ->map_pages() find no
>> PTE and do_read_fault() fall back to __do_fault().
>>
>> The load still has to be lockless-safe.  The store side already is: on
>> i386-PAE, pmd_install() publishes both halves of the pmd with a single
>> cmpxchg8b.  The load side is not.  pmdp_get() is a plain READ_ONCE() over
>> a pmd_t that is wider than a word there, so it becomes two 32-bit loads
>> and can return the low half of one pmd together with the high half of
>> another.  Use pmdp_get_lockless(), the accessor for pmd reads that do not
>> hold the pmd lock: it re-reads until both halves come from the same pmd.
>> It is also what __pte_offset_map() already uses on this very pmd a few
>> instructions later.
>>
>> Only X86_32+X86_PAE and SH+X2TLB compile the retry loop, these being the
>> GUP_GET_PXX_LOW_HIGH selectors that have more than two page table levels.
>> mips32 with 64-bit physical addresses selects GUP_GET_PXX_LOW_HIGH too,
>> but is 2-level and so falls back to pmdp_get().  On every other
>> configuration, x86-64 included, pmdp_get_lockless() is pmdp_get(): the
>> pmd is loaded in one go, the READ_ONCE() silences the report the same
>> way, and there is no functional change.
>>
>> Fixes: 7267ec008b5c ("mm: postpone page table allocation until we have page to map")
>> Cc: stable+noautosel@kernel.org # KCSAN annotation only, no functional change
>> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
>> Acked-by: Lorenzo Stoakes <ljs@kernel.org>
>> ---
>> Changelog:
>> v4:
>> Commit message only, the diff is identical to v3.  Spell out the affected
>> arches and add the lore links for v1-v3 (Lorenzo Stoakes).  Reword the
>> cmpxchg8b sentence: the store is already atomic, it is the load that tears.
>> Add the Fixes: tag (David Hildenbrand).
>> v3:
>> https://lore.kernel.org/all/20261103014749.96764-1-hui.zhu@linux.dev/
>> Shorten the comment to one line (David Hildenbrand).
>> v2:
>> https://lore.kernel.org/all/20261102093120.92654-1-hui.zhu@linux.dev/
>> Use pmdp_get_lockless() instead of pmdp_get() (David Hildenbrand).
>> v1:
>> https://lore.kernel.org/all/20261102031308.66177-1-hui.zhu@linux.dev/
>>
>>   mm/memory.c | 3 ++-
>>   1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/mm/memory.c b/mm/memory.c
>> index 0fb676dd2bd63..f25c2ce0ef153 100644
>> --- a/mm/memory.c
>> +++ b/mm/memory.c
>> @@ -6017,7 +6017,8 @@ static vm_fault_t do_fault_around(struct vm_fault *vmf)
>>   	to_pte = min3(from_pte + nr_pages, (pgoff_t)PTRS_PER_PTE,
>>   		      pte_off + vma_pages(vmf->vma) - vma_off) - 1;
>>   
>> -	if (pmd_none(*vmf->pmd)) {
>> +	/* pmd_install() rechecks pmd_none() under the pmd lock. */
>> +	if (pmd_none(pmdp_get_lockless(vmf->pmd))) {
>>   		vmf->prealloc_pte = pte_alloc_one(vmf->vma->vm_mm);
>>   		if (!vmf->prealloc_pte)
>>   			return VM_FAULT_OOM;
> Fun, this breaks some odd m68k configs because they do some legacy weird stuff with pmd_none().
>
> I'll throw in the following cleanup. CCing m68k maintainers: I would drag
> this along with this fix here unless you disagree.
>
>  From e3c739933a04743e3923f6f39a603e93f2700304 Mon Sep 17 00:00:00 2001
> From: "David Hildenbrand (Arm)" <david@kernel.org>
> Date: Thu, 5 Nov 2026 12:49:38 +0100
> Subject: [PATCH] m68k/mm: simplify
>   pmd_none()/pmd_val()/pmd_present()/pmd_bad()
>
> pmd_t on m68k used to contain 16 words. Commit ef22d8abd876 ("m68k:
> mm: Restructure Motorola MMU page-table layout") reduced Motorola PMDs
> to one word; Sun3 and ColdFire use word-sized folded PMDs.
>
> Use direct member access in pmd_val() and replace the Sun3 and ColdFire
> pointer-taking predicate wrappers with inline functions taking values.
>
> This allows us to write something like:
>
>    if (pmd_none(pmdp_get(pmdp)))
>
> While at it remove unused pmd_present2() and commented-out pmd_bad()
> on sun3.
>
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
> ---
>   arch/m68k/include/asm/mcf_pgtable.h  | 20 +++++++++++++++-----
>   arch/m68k/include/asm/page.h         |  2 +-
>   arch/m68k/include/asm/sun3_pgtable.h | 23 +++++++++++++++--------
>   3 files changed, 31 insertions(+), 14 deletions(-)
>
> diff --git a/arch/m68k/include/asm/mcf_pgtable.h b/arch/m68k/include/asm/mcf_pgtable.h
> index f45a882238dbb..4efa8e3e1e98a 100644
> --- a/arch/m68k/include/asm/mcf_pgtable.h
> +++ b/arch/m68k/include/asm/mcf_pgtable.h
> @@ -130,11 +130,21 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr,
>   
>   #define pte_page(pte)	virt_to_page(__pte_page(pte))
>   
> -static inline int pmd_none2(pmd_t *pmd) { return !pmd_val(*pmd); }
> -#define pmd_none(pmd) pmd_none2(&(pmd))
> -static inline int pmd_bad2(pmd_t *pmd) { return 0; }
> -#define pmd_bad(pmd) pmd_bad2(&(pmd))
> -#define pmd_present(pmd) (!pmd_none2(&(pmd)))
> +static inline int pmd_none(pmd_t pmd)
> +{
> +	return !pmd_val(pmd);
> +}
> +
> +static inline int pmd_bad(pmd_t pmd)
> +{
> +	return 0;
> +}
> +
> +static inline int pmd_present(pmd_t pmd)
> +{
> +	return !pmd_none(pmd);
> +}
> +
>   static inline void pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
>   
>   
> diff --git a/arch/m68k/include/asm/page.h b/arch/m68k/include/asm/page.h
> index d30f8b2f15927..09a2c020ec992 100644
> --- a/arch/m68k/include/asm/page.h
> +++ b/arch/m68k/include/asm/page.h
> @@ -17,7 +17,7 @@
>    */
>   #if !defined(CONFIG_MMU) || CONFIG_PGTABLE_LEVELS == 3
>   typedef struct { unsigned long pmd; } pmd_t;
> -#define pmd_val(x)	((&x)->pmd)
> +#define pmd_val(x)	((x).pmd)
>   #define __pmd(x)	((pmd_t) { (x) } )
>   #endif
>   
> diff --git a/arch/m68k/include/asm/sun3_pgtable.h b/arch/m68k/include/asm/sun3_pgtable.h
> index 704442a391fd7..5867d24032655 100644
> --- a/arch/m68k/include/asm/sun3_pgtable.h
> +++ b/arch/m68k/include/asm/sun3_pgtable.h
> @@ -109,14 +109,21 @@ static inline void pte_clear (struct mm_struct *mm, unsigned long addr, pte_t *p
>   #define pmd_page(pmd)		virt_to_page((void *)pmd_page_vaddr(pmd))
>   
>   
> -static inline int pmd_none2 (pmd_t *pmd) { return !pmd_val (*pmd); }
> -#define pmd_none(pmd) pmd_none2(&(pmd))
> -//static inline int pmd_bad (pmd_t pmd) { return (pmd_val (pmd) & SUN3_PMD_MASK) != SUN3_PMD_MAGIC; }
> -static inline int pmd_bad2 (pmd_t *pmd) { return 0; }
> -#define pmd_bad(pmd) pmd_bad2(&(pmd))
> -static inline int pmd_present2 (pmd_t *pmd) { return pmd_val (*pmd) & SUN3_PMD_VALID; }
> -/* #define pmd_present(pmd) pmd_present2(&(pmd)) */
> -#define pmd_present(pmd) (!pmd_none2(&(pmd)))
> +static inline int pmd_none(pmd_t pmd)
> +{
> +	return !pmd_val(pmd);
> +}
> +
> +static inline int pmd_bad(pmd_t pmd)
> +{
> +	return 0;
> +}
> +
> +static inline int pmd_present(pmd_t pmd)
> +{
> +	return !pmd_none(pmd);
> +}
> +
>   static inline void pmd_clear (pmd_t *pmdp) { pmd_val (*pmdp) = 0; }
>   
>   /*

Hi David,

Thanks for picking this up, and for the m68k cleanup!

Tested-by: Hui Zhu <zhuhui@kylinos.cn>

Tested with gcc-m68k-linux-gnu 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04), on
three configs that between them cover all three m68k families:

   amiga_defconfig                    Motorola MMU, PGTABLE_LEVELS=3
   sun3_defconfig                     SUN3,  PGTABLE_LEVELS=2
   m5208evb_defconfig + CONFIG_MMU=y  ColdFire,  PGTABLE_LEVELS=2

Without your patch none of the three can build mm/memory.o, and each fails
at a different site:

   arch/m68k/include/asm/page.h:20:27: error: lvalue required as unary 
'&' operand
   arch/m68k/include/asm/sun3_pgtable.h:113:33: error: lvalue required 
as unary '&' operand
   arch/m68k/include/asm/mcf_pgtable.h:134:33: error: lvalue required as 
unary '&' operand

With it, all three build a full vmlinux with zero warnings.

Two things that might save you some time:

- m68k looks like the only arch with this pattern. Grepping arch/ and
   include/ for '(&x)->pmd' and '(&(pmd))' finds nothing outside arch/m68k/,
   and pmd_none2()/pmd_bad2()/pmd_present2() only exist in mcf_pgtable.h and
   sun3_pgtable.h. So your three hunks look complete to me.

- No m52xx/m54xx defconfig enables CONFIG_MMU, so the ColdFire path through
   mcf_pgtable.h has no defconfig coverage at all; you have to flip it on by
   hand (scripts/config -e MMU && make olddefconfig) to reach it. That is
   probably how this went unnoticed for so long - my patch has been breaking
   m68k since v1, because pmdp_get() returns by value just like
   pmdp_get_lockless() does.

I also checked whether your patch changes code generation, by building the
same tree with and without it and comparing every object with objdump -d
(plain cmp is useless here, timestamps and link layout shift everything).
Counting only objects whose disassembly actually differs:

   amiga      3733 objects, 0 changed
   sun3       3553 objects, 3 changed: mm/memory.o, mm/pgtable-generic.o,
                                       mm/vmalloc.o
   m5208evb    716 objects, 1 changed: mm/pgtable-generic.o

amiga is the interesting one. It is the only config where pmd_val() itself
changed, and it comes out bit-for-bit identical, which also says the build
is reproducible enough for this comparison to mean anything. sun3 and
ColdFire differ because pmd_none() stops taking the address of its
argument, and the direction is an improvement. In mm/vmalloc.o on sun3 one
function loses a forced spill:

   -  subql #4,%sp
   -  movel %d0,%sp@(12)     /* spill the pmd value */
   -  lea %sp@(12),%a1       /* &(pmd) */
   -  moveal %a1@,%a0        /* read it back */

and turns into a 13 instruction function with no stack slot at all.
Instruction counts are otherwise flat (sun3 mm/memory.o 7373 -> 7373,
mm/vmalloc.o 5514 -> 5515), so it is not a size regression.

Dragging the cleanup along with the fix sounds right to me. It is a hard
prerequisite, and sent on its own it would just look like a cleanup with no
urgency. I assume it goes in ahead of 6ab55cfd in core-7.5.misc, so that
linux-next never sees a broken m68k in between.

One red herring in case anyone else pokes at this: m5208evb with its stock
defconfig (CONFIG_MMU not set) fails to build mm/memory.o for reasons that
have nothing to do with any of this - implicit declarations of
pte_is_uffd_wp_marker() and pmd_pgtable(), plus a parse error in
include/linux/mm.h. Pre-existing, and it goes away once MMU is on.

Best,
Hui



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

* Re: [PATCH v4] mm/memory: use pmdp_get_lockless() for the pmd read in do_fault_around()
  2026-11-06  3:03   ` Hui Zhu
@ 2026-11-06 12:08     ` Greg Ungerer
  2026-11-09  5:50       ` Hui Zhu
  0 siblings, 1 reply; 6+ messages in thread
From: Greg Ungerer @ 2026-11-06 12:08 UTC (permalink / raw)
  To: Hui Zhu, David Hildenbrand (Arm), Andrew Morton, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, linux-mm, linux-kernel,
	Geert Uytterhoeven, Sam Creasey
  Cc: Hui Zhu, stable+noautosel, linux-m68k


Hi Hui,

On 6/11/26 13:03, Hui Zhu wrote:
> 2026/11/5 20:00, David Hildenbrand (Arm) :
>> On 11/4/26 10:10, Hui Zhu wrote:
>>> From: Hui Zhu <zhuhui@kylinos.cn>
>>>
>>> KCSAN reports a data-race in do_read_fault / pmd_install; the same code
>>> is in mainline:
>>>
>>>    write (marked) to 0xffff8d52a75fb810 of 8 bytes by task 27487 on cpu 5:
>>>     native_set_pmd arch/x86/include/asm/pgtable_64.h:83 [inline]
>>>     pmd_populate arch/x86/include/asm/pgalloc.h:84 [inline]
>>>     pmd_install+0xe0/0x120 mm/memory.c:444
>>>     finish_fault+0x5bc/0xb00 mm/memory.c:5204
>>>     do_cow_fault mm/memory.c:5432 [inline]
>>>     do_fault+0x5d0/0x6e0 mm/memory.c:5526
>>>     handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>>>     __mm_populate+0x144/0x270 mm/gup.c:2207
>>>     __do_sys_mlockall+0x2cd/0x380 mm/mlock.c:764
>>>
>>>    read to 0xffff8d52a75fb810 of 8 bytes by task 27527 on cpu 9:
>>>     do_fault_around mm/memory.c:5341 [inline]
>>>     do_read_fault+0x162/0x670 mm/memory.c:5381
>>>     do_fault+0x31a/0x6e0 mm/memory.c:5524
>>>     handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>>>     faultin_page_range+0x227/0x530 mm/gup.c:2156
>>>     __x64_sys_madvise+0x67/0x80 mm/madvise.c:1479
>>>
>>>    value changed: 0x0000000000000000 -> 0x000000001386c067
>>>
>>> Both paths only hold mmap_lock for read, so do_fault_around() tests
>>> vmf->pmd while pmd_install() populates it under the pmd lock.  The race is
>>> benign: a stale pmd_none() only wastes the prealloc_pte, which do_fault()
>>> frees again, and a pmd seen as populated only makes ->map_pages() find no
>>> PTE and do_read_fault() fall back to __do_fault().
>>>
>>> The load still has to be lockless-safe.  The store side already is: on
>>> i386-PAE, pmd_install() publishes both halves of the pmd with a single
>>> cmpxchg8b.  The load side is not.  pmdp_get() is a plain READ_ONCE() over
>>> a pmd_t that is wider than a word there, so it becomes two 32-bit loads
>>> and can return the low half of one pmd together with the high half of
>>> another.  Use pmdp_get_lockless(), the accessor for pmd reads that do not
>>> hold the pmd lock: it re-reads until both halves come from the same pmd.
>>> It is also what __pte_offset_map() already uses on this very pmd a few
>>> instructions later.
>>>
>>> Only X86_32+X86_PAE and SH+X2TLB compile the retry loop, these being the
>>> GUP_GET_PXX_LOW_HIGH selectors that have more than two page table levels.
>>> mips32 with 64-bit physical addresses selects GUP_GET_PXX_LOW_HIGH too,
>>> but is 2-level and so falls back to pmdp_get().  On every other
>>> configuration, x86-64 included, pmdp_get_lockless() is pmdp_get(): the
>>> pmd is loaded in one go, the READ_ONCE() silences the report the same
>>> way, and there is no functional change.
>>>
>>> Fixes: 7267ec008b5c ("mm: postpone page table allocation until we have page to map")
>>> Cc: stable+noautosel@kernel.org # KCSAN annotation only, no functional change
>>> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
>>> Acked-by: Lorenzo Stoakes <ljs@kernel.org>
>>> ---
>>> Changelog:
>>> v4:
>>> Commit message only, the diff is identical to v3.  Spell out the affected
>>> arches and add the lore links for v1-v3 (Lorenzo Stoakes).  Reword the
>>> cmpxchg8b sentence: the store is already atomic, it is the load that tears.
>>> Add the Fixes: tag (David Hildenbrand).
>>> v3:
>>> https://lore.kernel.org/all/20261103014749.96764-1-hui.zhu@linux.dev/
>>> Shorten the comment to one line (David Hildenbrand).
>>> v2:
>>> https://lore.kernel.org/all/20261102093120.92654-1-hui.zhu@linux.dev/
>>> Use pmdp_get_lockless() instead of pmdp_get() (David Hildenbrand).
>>> v1:
>>> https://lore.kernel.org/all/20261102031308.66177-1-hui.zhu@linux.dev/
>>>
>>>   mm/memory.c | 3 ++-
>>>   1 file changed, 2 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/mm/memory.c b/mm/memory.c
>>> index 0fb676dd2bd63..f25c2ce0ef153 100644
>>> --- a/mm/memory.c
>>> +++ b/mm/memory.c
>>> @@ -6017,7 +6017,8 @@ static vm_fault_t do_fault_around(struct vm_fault *vmf)
>>>       to_pte = min3(from_pte + nr_pages, (pgoff_t)PTRS_PER_PTE,
>>>                 pte_off + vma_pages(vmf->vma) - vma_off) - 1;
>>>   -    if (pmd_none(*vmf->pmd)) {
>>> +    /* pmd_install() rechecks pmd_none() under the pmd lock. */
>>> +    if (pmd_none(pmdp_get_lockless(vmf->pmd))) {
>>>           vmf->prealloc_pte = pte_alloc_one(vmf->vma->vm_mm);
>>>           if (!vmf->prealloc_pte)
>>>               return VM_FAULT_OOM;
>> Fun, this breaks some odd m68k configs because they do some legacy weird stuff with pmd_none().
>>
>> I'll throw in the following cleanup. CCing m68k maintainers: I would drag
>> this along with this fix here unless you disagree.
>>
>>  From e3c739933a04743e3923f6f39a603e93f2700304 Mon Sep 17 00:00:00 2001
>> From: "David Hildenbrand (Arm)" <david@kernel.org>
>> Date: Thu, 5 Nov 2026 12:49:38 +0100
>> Subject: [PATCH] m68k/mm: simplify
>>   pmd_none()/pmd_val()/pmd_present()/pmd_bad()
>>
>> pmd_t on m68k used to contain 16 words. Commit ef22d8abd876 ("m68k:
>> mm: Restructure Motorola MMU page-table layout") reduced Motorola PMDs
>> to one word; Sun3 and ColdFire use word-sized folded PMDs.
>>
>> Use direct member access in pmd_val() and replace the Sun3 and ColdFire
>> pointer-taking predicate wrappers with inline functions taking values.
>>
>> This allows us to write something like:
>>
>>    if (pmd_none(pmdp_get(pmdp)))
>>
>> While at it remove unused pmd_present2() and commented-out pmd_bad()
>> on sun3.
>>
>> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
>> ---
>>   arch/m68k/include/asm/mcf_pgtable.h  | 20 +++++++++++++++-----
>>   arch/m68k/include/asm/page.h         |  2 +-
>>   arch/m68k/include/asm/sun3_pgtable.h | 23 +++++++++++++++--------
>>   3 files changed, 31 insertions(+), 14 deletions(-)
>>
>> diff --git a/arch/m68k/include/asm/mcf_pgtable.h b/arch/m68k/include/asm/mcf_pgtable.h
>> index f45a882238dbb..4efa8e3e1e98a 100644
>> --- a/arch/m68k/include/asm/mcf_pgtable.h
>> +++ b/arch/m68k/include/asm/mcf_pgtable.h
>> @@ -130,11 +130,21 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr,
>>     #define pte_page(pte)    virt_to_page(__pte_page(pte))
>>   -static inline int pmd_none2(pmd_t *pmd) { return !pmd_val(*pmd); }
>> -#define pmd_none(pmd) pmd_none2(&(pmd))
>> -static inline int pmd_bad2(pmd_t *pmd) { return 0; }
>> -#define pmd_bad(pmd) pmd_bad2(&(pmd))
>> -#define pmd_present(pmd) (!pmd_none2(&(pmd)))
>> +static inline int pmd_none(pmd_t pmd)
>> +{
>> +    return !pmd_val(pmd);
>> +}
>> +
>> +static inline int pmd_bad(pmd_t pmd)
>> +{
>> +    return 0;
>> +}
>> +
>> +static inline int pmd_present(pmd_t pmd)
>> +{
>> +    return !pmd_none(pmd);
>> +}
>> +
>>   static inline void pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
>>     diff --git a/arch/m68k/include/asm/page.h b/arch/m68k/include/asm/page.h
>> index d30f8b2f15927..09a2c020ec992 100644
>> --- a/arch/m68k/include/asm/page.h
>> +++ b/arch/m68k/include/asm/page.h
>> @@ -17,7 +17,7 @@
>>    */
>>   #if !defined(CONFIG_MMU) || CONFIG_PGTABLE_LEVELS == 3
>>   typedef struct { unsigned long pmd; } pmd_t;
>> -#define pmd_val(x)    ((&x)->pmd)
>> +#define pmd_val(x)    ((x).pmd)
>>   #define __pmd(x)    ((pmd_t) { (x) } )
>>   #endif
>>   diff --git a/arch/m68k/include/asm/sun3_pgtable.h b/arch/m68k/include/asm/sun3_pgtable.h
>> index 704442a391fd7..5867d24032655 100644
>> --- a/arch/m68k/include/asm/sun3_pgtable.h
>> +++ b/arch/m68k/include/asm/sun3_pgtable.h
>> @@ -109,14 +109,21 @@ static inline void pte_clear (struct mm_struct *mm, unsigned long addr, pte_t *p
>>   #define pmd_page(pmd)        virt_to_page((void *)pmd_page_vaddr(pmd))
>>     -static inline int pmd_none2 (pmd_t *pmd) { return !pmd_val (*pmd); }
>> -#define pmd_none(pmd) pmd_none2(&(pmd))
>> -//static inline int pmd_bad (pmd_t pmd) { return (pmd_val (pmd) & SUN3_PMD_MASK) != SUN3_PMD_MAGIC; }
>> -static inline int pmd_bad2 (pmd_t *pmd) { return 0; }
>> -#define pmd_bad(pmd) pmd_bad2(&(pmd))
>> -static inline int pmd_present2 (pmd_t *pmd) { return pmd_val (*pmd) & SUN3_PMD_VALID; }
>> -/* #define pmd_present(pmd) pmd_present2(&(pmd)) */
>> -#define pmd_present(pmd) (!pmd_none2(&(pmd)))
>> +static inline int pmd_none(pmd_t pmd)
>> +{
>> +    return !pmd_val(pmd);
>> +}
>> +
>> +static inline int pmd_bad(pmd_t pmd)
>> +{
>> +    return 0;
>> +}
>> +
>> +static inline int pmd_present(pmd_t pmd)
>> +{
>> +    return !pmd_none(pmd);
>> +}
>> +
>>   static inline void pmd_clear (pmd_t *pmdp) { pmd_val (*pmdp) = 0; }
>>     /*
> 
> Hi David,
> 
> Thanks for picking this up, and for the m68k cleanup!
> 
> Tested-by: Hui Zhu <zhuhui@kylinos.cn>
> 
> Tested with gcc-m68k-linux-gnu 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04), on
> three configs that between them cover all three m68k families:
> 
>   amiga_defconfig                    Motorola MMU, PGTABLE_LEVELS=3
>   sun3_defconfig                     SUN3,  PGTABLE_LEVELS=2
>   m5208evb_defconfig + CONFIG_MMU=y  ColdFire,  PGTABLE_LEVELS=2
> 
> Without your patch none of the three can build mm/memory.o, and each fails
> at a different site:
> 
>   arch/m68k/include/asm/page.h:20:27: error: lvalue required as unary '&' operand
>   arch/m68k/include/asm/sun3_pgtable.h:113:33: error: lvalue required as unary '&' operand
>   arch/m68k/include/asm/mcf_pgtable.h:134:33: error: lvalue required as unary '&' operand
> 
> With it, all three build a full vmlinux with zero warnings.
> 
> Two things that might save you some time:
> 
> - m68k looks like the only arch with this pattern. Grepping arch/ and
>   include/ for '(&x)->pmd' and '(&(pmd))' finds nothing outside arch/m68k/,
>   and pmd_none2()/pmd_bad2()/pmd_present2() only exist in mcf_pgtable.h and
>   sun3_pgtable.h. So your three hunks look complete to me.
> 
> - No m52xx/m54xx defconfig enables CONFIG_MMU, so the ColdFire path through
>   mcf_pgtable.h has no defconfig coverage at all; you have to flip it on by
>   hand (scripts/config -e MMU && make olddefconfig) to reach it. That is
>   probably how this went unnoticed for so long - my patch has been breaking
>   m68k since v1, because pmdp_get() returns by value just like
>   pmdp_get_lockless() does.

The m5475evb_defconfig is supposed to have MMU enabled:

     $ ARCH=m68k CROSS_COMPILE=m68k-linux-uclibc- make m5475evb_defconfig 
       HOSTCC  scripts/basic/fixdep
       [snip]
       HOSTLD  scripts/kconfig/conf
     #
     # configuration written to .config
     #
     $
     $ grep CONFIG_MMU .config
     CONFIG_MMU=y
     CONFIG_MMU_COLDFIRE=y
     CONFIG_MMU_GATHER_NO_RANGE=y
     CONFIG_MMU_GATHER_MERGE_VMAS=y
     CONFIG_MMU_LAZY_TLB_REFCOUNT=y

But something ends up disabling it when you compile.
I'll fix this.


> I also checked whether your patch changes code generation, by building the
> same tree with and without it and comparing every object with objdump -d
> (plain cmp is useless here, timestamps and link layout shift everything).
> Counting only objects whose disassembly actually differs:
> 
>   amiga      3733 objects, 0 changed
>   sun3       3553 objects, 3 changed: mm/memory.o, mm/pgtable-generic.o,
>                                       mm/vmalloc.o
>   m5208evb    716 objects, 1 changed: mm/pgtable-generic.o
> 
> amiga is the interesting one. It is the only config where pmd_val() itself
> changed, and it comes out bit-for-bit identical, which also says the build
> is reproducible enough for this comparison to mean anything. sun3 and
> ColdFire differ because pmd_none() stops taking the address of its
> argument, and the direction is an improvement. In mm/vmalloc.o on sun3 one
> function loses a forced spill:
> 
>   -  subql #4,%sp
>   -  movel %d0,%sp@(12)     /* spill the pmd value */
>   -  lea %sp@(12),%a1       /* &(pmd) */
>   -  moveal %a1@,%a0        /* read it back */
> 
> and turns into a 13 instruction function with no stack slot at all.
> Instruction counts are otherwise flat (sun3 mm/memory.o 7373 -> 7373,
> mm/vmalloc.o 5514 -> 5515), so it is not a size regression.
> 
> Dragging the cleanup along with the fix sounds right to me. It is a hard
> prerequisite, and sent on its own it would just look like a cleanup with no
> urgency. I assume it goes in ahead of 6ab55cfd in core-7.5.misc, so that
> linux-next never sees a broken m68k in between.
> 
> One red herring in case anyone else pokes at this: m5208evb with its stock
> defconfig (CONFIG_MMU not set) fails to build mm/memory.o for reasons that
> have nothing to do with any of this - implicit declarations of
> pte_is_uffd_wp_marker() and pmd_pgtable(), plus a parse error in
> include/linux/mm.h. Pre-existing, and it goes away once MMU is on.

You should not be able to set MMU=y if configured for the M5208.
It doesn't have an MMU. You want the 5475, 5485 or 54418 to test
valid MMU=y configurations.

Regards
Greg





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

* Re: [PATCH v4] mm/memory: use pmdp_get_lockless() for the pmd read in do_fault_around()
  2026-11-06 12:08     ` Greg Ungerer
@ 2026-11-09  5:50       ` Hui Zhu
  2026-11-09 14:02         ` Greg Ungerer
  0 siblings, 1 reply; 6+ messages in thread
From: Hui Zhu @ 2026-11-09  5:50 UTC (permalink / raw)
  To: Greg Ungerer, David Hildenbrand (Arm), Andrew Morton,
	Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, linux-mm, linux-kernel,
	Geert Uytterhoeven, Sam Creasey
  Cc: Hui Zhu, stable+noautosel, linux-m68k

2026/11/6 20:08, Greg Ungerer:

Hi Greg,

> Hi Hui,
>
> On 6/11/26 13:03, Hui Zhu wrote:
>> 2026/11/5 20:00, David Hildenbrand (Arm) :
>>> On 11/4/26 10:10, Hui Zhu wrote:
>>>> From: Hui Zhu <zhuhui@kylinos.cn>
>>>>
>>>> KCSAN reports a data-race in do_read_fault / pmd_install; the same code
>>>> is in mainline:
>>>>
>>>>     write (marked) to 0xffff8d52a75fb810 of 8 bytes by task 27487 on cpu 5:
>>>>      native_set_pmd arch/x86/include/asm/pgtable_64.h:83 [inline]
>>>>      pmd_populate arch/x86/include/asm/pgalloc.h:84 [inline]
>>>>      pmd_install+0xe0/0x120 mm/memory.c:444
>>>>      finish_fault+0x5bc/0xb00 mm/memory.c:5204
>>>>      do_cow_fault mm/memory.c:5432 [inline]
>>>>      do_fault+0x5d0/0x6e0 mm/memory.c:5526
>>>>      handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>>>>      __mm_populate+0x144/0x270 mm/gup.c:2207
>>>>      __do_sys_mlockall+0x2cd/0x380 mm/mlock.c:764
>>>>
>>>>     read to 0xffff8d52a75fb810 of 8 bytes by task 27527 on cpu 9:
>>>>      do_fault_around mm/memory.c:5341 [inline]
>>>>      do_read_fault+0x162/0x670 mm/memory.c:5381
>>>>      do_fault+0x31a/0x6e0 mm/memory.c:5524
>>>>      handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>>>>      faultin_page_range+0x227/0x530 mm/gup.c:2156
>>>>      __x64_sys_madvise+0x67/0x80 mm/madvise.c:1479
>>>>
>>>>     value changed: 0x0000000000000000 -> 0x000000001386c067
>>>>
>>>> Both paths only hold mmap_lock for read, so do_fault_around() tests
>>>> vmf->pmd while pmd_install() populates it under the pmd lock.  The race is
>>>> benign: a stale pmd_none() only wastes the prealloc_pte, which do_fault()
>>>> frees again, and a pmd seen as populated only makes ->map_pages() find no
>>>> PTE and do_read_fault() fall back to __do_fault().
>>>>
>>>> The load still has to be lockless-safe.  The store side already is: on
>>>> i386-PAE, pmd_install() publishes both halves of the pmd with a single
>>>> cmpxchg8b.  The load side is not.  pmdp_get() is a plain READ_ONCE() over
>>>> a pmd_t that is wider than a word there, so it becomes two 32-bit loads
>>>> and can return the low half of one pmd together with the high half of
>>>> another.  Use pmdp_get_lockless(), the accessor for pmd reads that do not
>>>> hold the pmd lock: it re-reads until both halves come from the same pmd.
>>>> It is also what __pte_offset_map() already uses on this very pmd a few
>>>> instructions later.
>>>>
>>>> Only X86_32+X86_PAE and SH+X2TLB compile the retry loop, these being the
>>>> GUP_GET_PXX_LOW_HIGH selectors that have more than two page table levels.
>>>> mips32 with 64-bit physical addresses selects GUP_GET_PXX_LOW_HIGH too,
>>>> but is 2-level and so falls back to pmdp_get().  On every other
>>>> configuration, x86-64 included, pmdp_get_lockless() is pmdp_get(): the
>>>> pmd is loaded in one go, the READ_ONCE() silences the report the same
>>>> way, and there is no functional change.
>>>>
>>>> Fixes: 7267ec008b5c ("mm: postpone page table allocation until we have page to map")
>>>> Cc: stable+noautosel@kernel.org # KCSAN annotation only, no functional change
>>>> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
>>>> Acked-by: Lorenzo Stoakes <ljs@kernel.org>
>>>> ---
>>>> Changelog:
>>>> v4:
>>>> Commit message only, the diff is identical to v3.  Spell out the affected
>>>> arches and add the lore links for v1-v3 (Lorenzo Stoakes).  Reword the
>>>> cmpxchg8b sentence: the store is already atomic, it is the load that tears.
>>>> Add the Fixes: tag (David Hildenbrand).
>>>> v3:
>>>> https://lore.kernel.org/all/20261103014749.96764-1-hui.zhu@linux.dev/
>>>> Shorten the comment to one line (David Hildenbrand).
>>>> v2:
>>>> https://lore.kernel.org/all/20261102093120.92654-1-hui.zhu@linux.dev/
>>>> Use pmdp_get_lockless() instead of pmdp_get() (David Hildenbrand).
>>>> v1:
>>>> https://lore.kernel.org/all/20261102031308.66177-1-hui.zhu@linux.dev/
>>>>
>>>>    mm/memory.c | 3 ++-
>>>>    1 file changed, 2 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/mm/memory.c b/mm/memory.c
>>>> index 0fb676dd2bd63..f25c2ce0ef153 100644
>>>> --- a/mm/memory.c
>>>> +++ b/mm/memory.c
>>>> @@ -6017,7 +6017,8 @@ static vm_fault_t do_fault_around(struct vm_fault *vmf)
>>>>        to_pte = min3(from_pte + nr_pages, (pgoff_t)PTRS_PER_PTE,
>>>>                  pte_off + vma_pages(vmf->vma) - vma_off) - 1;
>>>>    -    if (pmd_none(*vmf->pmd)) {
>>>> +    /* pmd_install() rechecks pmd_none() under the pmd lock. */
>>>> +    if (pmd_none(pmdp_get_lockless(vmf->pmd))) {
>>>>            vmf->prealloc_pte = pte_alloc_one(vmf->vma->vm_mm);
>>>>            if (!vmf->prealloc_pte)
>>>>                return VM_FAULT_OOM;
>>> Fun, this breaks some odd m68k configs because they do some legacy weird stuff with pmd_none().
>>>
>>> I'll throw in the following cleanup. CCing m68k maintainers: I would drag
>>> this along with this fix here unless you disagree.
>>>
>>>   From e3c739933a04743e3923f6f39a603e93f2700304 Mon Sep 17 00:00:00 2001
>>> From: "David Hildenbrand (Arm)" <david@kernel.org>
>>> Date: Thu, 5 Nov 2026 12:49:38 +0100
>>> Subject: [PATCH] m68k/mm: simplify
>>>    pmd_none()/pmd_val()/pmd_present()/pmd_bad()
>>>
>>> pmd_t on m68k used to contain 16 words. Commit ef22d8abd876 ("m68k:
>>> mm: Restructure Motorola MMU page-table layout") reduced Motorola PMDs
>>> to one word; Sun3 and ColdFire use word-sized folded PMDs.
>>>
>>> Use direct member access in pmd_val() and replace the Sun3 and ColdFire
>>> pointer-taking predicate wrappers with inline functions taking values.
>>>
>>> This allows us to write something like:
>>>
>>>     if (pmd_none(pmdp_get(pmdp)))
>>>
>>> While at it remove unused pmd_present2() and commented-out pmd_bad()
>>> on sun3.
>>>
>>> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
>>> ---
>>>    arch/m68k/include/asm/mcf_pgtable.h  | 20 +++++++++++++++-----
>>>    arch/m68k/include/asm/page.h         |  2 +-
>>>    arch/m68k/include/asm/sun3_pgtable.h | 23 +++++++++++++++--------
>>>    3 files changed, 31 insertions(+), 14 deletions(-)
>>>
>>> diff --git a/arch/m68k/include/asm/mcf_pgtable.h b/arch/m68k/include/asm/mcf_pgtable.h
>>> index f45a882238dbb..4efa8e3e1e98a 100644
>>> --- a/arch/m68k/include/asm/mcf_pgtable.h
>>> +++ b/arch/m68k/include/asm/mcf_pgtable.h
>>> @@ -130,11 +130,21 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr,
>>>      #define pte_page(pte)    virt_to_page(__pte_page(pte))
>>>    -static inline int pmd_none2(pmd_t *pmd) { return !pmd_val(*pmd); }
>>> -#define pmd_none(pmd) pmd_none2(&(pmd))
>>> -static inline int pmd_bad2(pmd_t *pmd) { return 0; }
>>> -#define pmd_bad(pmd) pmd_bad2(&(pmd))
>>> -#define pmd_present(pmd) (!pmd_none2(&(pmd)))
>>> +static inline int pmd_none(pmd_t pmd)
>>> +{
>>> +    return !pmd_val(pmd);
>>> +}
>>> +
>>> +static inline int pmd_bad(pmd_t pmd)
>>> +{
>>> +    return 0;
>>> +}
>>> +
>>> +static inline int pmd_present(pmd_t pmd)
>>> +{
>>> +    return !pmd_none(pmd);
>>> +}
>>> +
>>>    static inline void pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
>>>      diff --git a/arch/m68k/include/asm/page.h b/arch/m68k/include/asm/page.h
>>> index d30f8b2f15927..09a2c020ec992 100644
>>> --- a/arch/m68k/include/asm/page.h
>>> +++ b/arch/m68k/include/asm/page.h
>>> @@ -17,7 +17,7 @@
>>>     */
>>>    #if !defined(CONFIG_MMU) || CONFIG_PGTABLE_LEVELS == 3
>>>    typedef struct { unsigned long pmd; } pmd_t;
>>> -#define pmd_val(x)    ((&x)->pmd)
>>> +#define pmd_val(x)    ((x).pmd)
>>>    #define __pmd(x)    ((pmd_t) { (x) } )
>>>    #endif
>>>    diff --git a/arch/m68k/include/asm/sun3_pgtable.h b/arch/m68k/include/asm/sun3_pgtable.h
>>> index 704442a391fd7..5867d24032655 100644
>>> --- a/arch/m68k/include/asm/sun3_pgtable.h
>>> +++ b/arch/m68k/include/asm/sun3_pgtable.h
>>> @@ -109,14 +109,21 @@ static inline void pte_clear (struct mm_struct *mm, unsigned long addr, pte_t *p
>>>    #define pmd_page(pmd)        virt_to_page((void *)pmd_page_vaddr(pmd))
>>>      -static inline int pmd_none2 (pmd_t *pmd) { return !pmd_val (*pmd); }
>>> -#define pmd_none(pmd) pmd_none2(&(pmd))
>>> -//static inline int pmd_bad (pmd_t pmd) { return (pmd_val (pmd) & SUN3_PMD_MASK) != SUN3_PMD_MAGIC; }
>>> -static inline int pmd_bad2 (pmd_t *pmd) { return 0; }
>>> -#define pmd_bad(pmd) pmd_bad2(&(pmd))
>>> -static inline int pmd_present2 (pmd_t *pmd) { return pmd_val (*pmd) & SUN3_PMD_VALID; }
>>> -/* #define pmd_present(pmd) pmd_present2(&(pmd)) */
>>> -#define pmd_present(pmd) (!pmd_none2(&(pmd)))
>>> +static inline int pmd_none(pmd_t pmd)
>>> +{
>>> +    return !pmd_val(pmd);
>>> +}
>>> +
>>> +static inline int pmd_bad(pmd_t pmd)
>>> +{
>>> +    return 0;
>>> +}
>>> +
>>> +static inline int pmd_present(pmd_t pmd)
>>> +{
>>> +    return !pmd_none(pmd);
>>> +}
>>> +
>>>    static inline void pmd_clear (pmd_t *pmdp) { pmd_val (*pmdp) = 0; }
>>>      /*
>> Hi David,
>>
>> Thanks for picking this up, and for the m68k cleanup!
>>
>> Tested-by: Hui Zhu <zhuhui@kylinos.cn>
>>
>> Tested with gcc-m68k-linux-gnu 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04), on
>> three configs that between them cover all three m68k families:
>>
>>    amiga_defconfig                    Motorola MMU, PGTABLE_LEVELS=3
>>    sun3_defconfig                     SUN3,  PGTABLE_LEVELS=2
>>    m5208evb_defconfig + CONFIG_MMU=y  ColdFire,  PGTABLE_LEVELS=2
>>
>> Without your patch none of the three can build mm/memory.o, and each fails
>> at a different site:
>>
>>    arch/m68k/include/asm/page.h:20:27: error: lvalue required as unary '&' operand
>>    arch/m68k/include/asm/sun3_pgtable.h:113:33: error: lvalue required as unary '&' operand
>>    arch/m68k/include/asm/mcf_pgtable.h:134:33: error: lvalue required as unary '&' operand
>>
>> With it, all three build a full vmlinux with zero warnings.
>>
>> Two things that might save you some time:
>>
>> - m68k looks like the only arch with this pattern. Grepping arch/ and
>>    include/ for '(&x)->pmd' and '(&(pmd))' finds nothing outside arch/m68k/,
>>    and pmd_none2()/pmd_bad2()/pmd_present2() only exist in mcf_pgtable.h and
>>    sun3_pgtable.h. So your three hunks look complete to me.
>>
>> - No m52xx/m54xx defconfig enables CONFIG_MMU, so the ColdFire path through
>>    mcf_pgtable.h has no defconfig coverage at all; you have to flip it on by
>>    hand (scripts/config -e MMU && make olddefconfig) to reach it. That is
>>    probably how this went unnoticed for so long - my patch has been breaking
>>    m68k since v1, because pmdp_get() returns by value just like
>>    pmdp_get_lockless() does.
> The m5475evb_defconfig is supposed to have MMU enabled:
>
>       $ ARCH=m68k CROSS_COMPILE=m68k-linux-uclibc- make m5475evb_defconfig
>         HOSTCC  scripts/basic/fixdep
>         [snip]
>         HOSTLD  scripts/kconfig/conf
>       #
>       # configuration written to .config
>       #
>       $
>       $ grep CONFIG_MMU .config
>       CONFIG_MMU=y
>       CONFIG_MMU_COLDFIRE=y
>       CONFIG_MMU_GATHER_NO_RANGE=y
>       CONFIG_MMU_GATHER_MERGE_VMAS=y
>       CONFIG_MMU_LAZY_TLB_REFCOUNT=y
>
> But something ends up disabling it when you compile.
> I'll fix this.
>
>
>> I also checked whether your patch changes code generation, by building the
>> same tree with and without it and comparing every object with objdump -d
>> (plain cmp is useless here, timestamps and link layout shift everything).
>> Counting only objects whose disassembly actually differs:
>>
>>    amiga      3733 objects, 0 changed
>>    sun3       3553 objects, 3 changed: mm/memory.o, mm/pgtable-generic.o,
>>                                        mm/vmalloc.o
>>    m5208evb    716 objects, 1 changed: mm/pgtable-generic.o
>>
>> amiga is the interesting one. It is the only config where pmd_val() itself
>> changed, and it comes out bit-for-bit identical, which also says the build
>> is reproducible enough for this comparison to mean anything. sun3 and
>> ColdFire differ because pmd_none() stops taking the address of its
>> argument, and the direction is an improvement. In mm/vmalloc.o on sun3 one
>> function loses a forced spill:
>>
>>    -  subql #4,%sp
>>    -  movel %d0,%sp@(12)     /* spill the pmd value */
>>    -  lea %sp@(12),%a1       /* &(pmd) */
>>    -  moveal %a1@,%a0        /* read it back */
>>
>> and turns into a 13 instruction function with no stack slot at all.
>> Instruction counts are otherwise flat (sun3 mm/memory.o 7373 -> 7373,
>> mm/vmalloc.o 5514 -> 5515), so it is not a size regression.
>>
>> Dragging the cleanup along with the fix sounds right to me. It is a hard
>> prerequisite, and sent on its own it would just look like a cleanup with no
>> urgency. I assume it goes in ahead of 6ab55cfd in core-7.5.misc, so that
>> linux-next never sees a broken m68k in between.
>>
>> One red herring in case anyone else pokes at this: m5208evb with its stock
>> defconfig (CONFIG_MMU not set) fails to build mm/memory.o for reasons that
>> have nothing to do with any of this - implicit declarations of
>> pte_is_uffd_wp_marker() and pmd_pgtable(), plus a parse error in
>> include/linux/mm.h. Pre-existing, and it goes away once MMU is on.
> You should not be able to set MMU=y if configured for the M5208.
> It doesn't have an MMU. You want the 5475, 5485 or 54418 to test
> valid MMU=y configurations.
>
> Regards
> Greg
>
>
>

You are right on both counts, and that mail of mine had three things
wrong.

1. "No m52xx/m54xx defconfig enables CONFIG_MMU" - wrong.  I grepped the
defconfig fragments for CONFIG_MMU=y, but MMU is 'default y', so an
enabled MMU never appears in the fragment.  m5475evb and m54418evb
both give CONFIG_MMU=y, as you show.

2. My "m5208evb + CONFIG_MMU=y" test config was not a valid config.
M520x is 'depends on !MMU', so forcing MMU on leaves the SoC choice
unsatisfiable and olddefconfig silently drops it:

before:  # CONFIG_MMU is not set / CONFIG_M520x=y
after:   CONFIG_MMU=y / CONFIG_MMU_COLDFIRE=y / CONFIG_M520x gone

Please disregard the m5208evb row in my codegen numbers.

3. The "red herring" I flagged was my own doing, not a pre-existing
problem.  mm/Makefile has 'mmu-$(CONFIG_MMU) := ... memory.o', so a
NOMMU config never builds mm/memory.o at all; I asked for a target
that config does not have.  Sorry for the noise on that one.

Retested with the configs you named (7.4-rc1 plus the do_fault_around
change plus David's cleanup, gcc-m68k-linux-gnu 13.3.0):

m5475evb_defconfig   without David's patch: mm/memory.o fails at
mcf_pgtable.h:134 with it: full -j24 build ok, vmlinux 4215344
bytes, 0 warnings

m54418evb_defconfig  without David's patch: same mcf_pgtable.h:134
failure

So the Tested-by I sent covers amiga, sun3 and m5475evb.  It is
build-tested only - I have no m68k hardware here to boot.

On MMU being disabled when you compile: I cannot reproduce that. Here
m5475evb_defconfig gives CONFIG_MMU=y, olddefconfig reports no change,
two consecutive defconfig runs produce an identical .config, and a full
build finishes with CONFIG_MMU=y and CONFIG_M547x=y still set and
.config untouched.  Two differences from your setup that might matter:
I am on a glibc toolchain rather than uclibc, and building out of tree
with O= rather than in tree.

One observation, which may well be unrelated.  m5475evb_defconfig never
sets CONFIG_M547x - it only ends up with it because the "ColdFire SoC
type" choice defaults to M520x, which is unavailable under MMU=y, so
kconfig falls back to the first usable member. m54418evb_defconfig sets
CONFIG_M5441x=y explicitly.  If that fallback is what breaks on your
side, making m5475evb explicit would sidestep it.

Best,
Hui


>

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

* Re: [PATCH v4] mm/memory: use pmdp_get_lockless() for the pmd read in do_fault_around()
  2026-11-09  5:50       ` Hui Zhu
@ 2026-11-09 14:02         ` Greg Ungerer
  2026-11-09 14:04           ` David Hildenbrand (Arm)
  0 siblings, 1 reply; 6+ messages in thread
From: Greg Ungerer @ 2026-11-09 14:02 UTC (permalink / raw)
  To: Hui Zhu, David Hildenbrand (Arm), Andrew Morton, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, linux-mm, linux-kernel,
	Geert Uytterhoeven, Sam Creasey
  Cc: Hui Zhu, stable+noautosel, linux-m68k

Hi Hui,

On 9/11/26 15:50, Hui Zhu wrote:
> 2026/11/6 20:08, Greg Ungerer:
> 
> Hi Greg,
> 
>> Hi Hui,
>>
>> On 6/11/26 13:03, Hui Zhu wrote:
>>> 2026/11/5 20:00, David Hildenbrand (Arm) :
>>>> On 11/4/26 10:10, Hui Zhu wrote:
>>>>> From: Hui Zhu <zhuhui@kylinos.cn>
>>>>>
>>>>> KCSAN reports a data-race in do_read_fault / pmd_install; the same code
>>>>> is in mainline:
>>>>>
>>>>>     write (marked) to 0xffff8d52a75fb810 of 8 bytes by task 27487 on cpu 5:
>>>>>      native_set_pmd arch/x86/include/asm/pgtable_64.h:83 [inline]
>>>>>      pmd_populate arch/x86/include/asm/pgalloc.h:84 [inline]
>>>>>      pmd_install+0xe0/0x120 mm/memory.c:444
>>>>>      finish_fault+0x5bc/0xb00 mm/memory.c:5204
>>>>>      do_cow_fault mm/memory.c:5432 [inline]
>>>>>      do_fault+0x5d0/0x6e0 mm/memory.c:5526
>>>>>      handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>>>>>      __mm_populate+0x144/0x270 mm/gup.c:2207
>>>>>      __do_sys_mlockall+0x2cd/0x380 mm/mlock.c:764
>>>>>
>>>>>     read to 0xffff8d52a75fb810 of 8 bytes by task 27527 on cpu 9:
>>>>>      do_fault_around mm/memory.c:5341 [inline]
>>>>>      do_read_fault+0x162/0x670 mm/memory.c:5381
>>>>>      do_fault+0x31a/0x6e0 mm/memory.c:5524
>>>>>      handle_mm_fault+0x211/0x4c0 mm/memory.c:6218
>>>>>      faultin_page_range+0x227/0x530 mm/gup.c:2156
>>>>>      __x64_sys_madvise+0x67/0x80 mm/madvise.c:1479
>>>>>
>>>>>     value changed: 0x0000000000000000 -> 0x000000001386c067
>>>>>
>>>>> Both paths only hold mmap_lock for read, so do_fault_around() tests
>>>>> vmf->pmd while pmd_install() populates it under the pmd lock.  The race is
>>>>> benign: a stale pmd_none() only wastes the prealloc_pte, which do_fault()
>>>>> frees again, and a pmd seen as populated only makes ->map_pages() find no
>>>>> PTE and do_read_fault() fall back to __do_fault().
>>>>>
>>>>> The load still has to be lockless-safe.  The store side already is: on
>>>>> i386-PAE, pmd_install() publishes both halves of the pmd with a single
>>>>> cmpxchg8b.  The load side is not.  pmdp_get() is a plain READ_ONCE() over
>>>>> a pmd_t that is wider than a word there, so it becomes two 32-bit loads
>>>>> and can return the low half of one pmd together with the high half of
>>>>> another.  Use pmdp_get_lockless(), the accessor for pmd reads that do not
>>>>> hold the pmd lock: it re-reads until both halves come from the same pmd.
>>>>> It is also what __pte_offset_map() already uses on this very pmd a few
>>>>> instructions later.
>>>>>
>>>>> Only X86_32+X86_PAE and SH+X2TLB compile the retry loop, these being the
>>>>> GUP_GET_PXX_LOW_HIGH selectors that have more than two page table levels.
>>>>> mips32 with 64-bit physical addresses selects GUP_GET_PXX_LOW_HIGH too,
>>>>> but is 2-level and so falls back to pmdp_get().  On every other
>>>>> configuration, x86-64 included, pmdp_get_lockless() is pmdp_get(): the
>>>>> pmd is loaded in one go, the READ_ONCE() silences the report the same
>>>>> way, and there is no functional change.
>>>>>
>>>>> Fixes: 7267ec008b5c ("mm: postpone page table allocation until we have page to map")
>>>>> Cc: stable+noautosel@kernel.org # KCSAN annotation only, no functional change
>>>>> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
>>>>> Acked-by: Lorenzo Stoakes <ljs@kernel.org>
>>>>> ---
>>>>> Changelog:
>>>>> v4:
>>>>> Commit message only, the diff is identical to v3.  Spell out the affected
>>>>> arches and add the lore links for v1-v3 (Lorenzo Stoakes).  Reword the
>>>>> cmpxchg8b sentence: the store is already atomic, it is the load that tears.
>>>>> Add the Fixes: tag (David Hildenbrand).
>>>>> v3:
>>>>> https://lore.kernel.org/all/20261103014749.96764-1-hui.zhu@linux.dev/
>>>>> Shorten the comment to one line (David Hildenbrand).
>>>>> v2:
>>>>> https://lore.kernel.org/all/20261102093120.92654-1-hui.zhu@linux.dev/
>>>>> Use pmdp_get_lockless() instead of pmdp_get() (David Hildenbrand).
>>>>> v1:
>>>>> https://lore.kernel.org/all/20261102031308.66177-1-hui.zhu@linux.dev/
>>>>>
>>>>>    mm/memory.c | 3 ++-
>>>>>    1 file changed, 2 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/mm/memory.c b/mm/memory.c
>>>>> index 0fb676dd2bd63..f25c2ce0ef153 100644
>>>>> --- a/mm/memory.c
>>>>> +++ b/mm/memory.c
>>>>> @@ -6017,7 +6017,8 @@ static vm_fault_t do_fault_around(struct vm_fault *vmf)
>>>>>        to_pte = min3(from_pte + nr_pages, (pgoff_t)PTRS_PER_PTE,
>>>>>                  pte_off + vma_pages(vmf->vma) - vma_off) - 1;
>>>>>    -    if (pmd_none(*vmf->pmd)) {
>>>>> +    /* pmd_install() rechecks pmd_none() under the pmd lock. */
>>>>> +    if (pmd_none(pmdp_get_lockless(vmf->pmd))) {
>>>>>            vmf->prealloc_pte = pte_alloc_one(vmf->vma->vm_mm);
>>>>>            if (!vmf->prealloc_pte)
>>>>>                return VM_FAULT_OOM;
>>>> Fun, this breaks some odd m68k configs because they do some legacy weird stuff with pmd_none().
>>>>
>>>> I'll throw in the following cleanup. CCing m68k maintainers: I would drag
>>>> this along with this fix here unless you disagree.
>>>>
>>>>   From e3c739933a04743e3923f6f39a603e93f2700304 Mon Sep 17 00:00:00 2001
>>>> From: "David Hildenbrand (Arm)" <david@kernel.org>
>>>> Date: Thu, 5 Nov 2026 12:49:38 +0100
>>>> Subject: [PATCH] m68k/mm: simplify
>>>>    pmd_none()/pmd_val()/pmd_present()/pmd_bad()
>>>>
>>>> pmd_t on m68k used to contain 16 words. Commit ef22d8abd876 ("m68k:
>>>> mm: Restructure Motorola MMU page-table layout") reduced Motorola PMDs
>>>> to one word; Sun3 and ColdFire use word-sized folded PMDs.
>>>>
>>>> Use direct member access in pmd_val() and replace the Sun3 and ColdFire
>>>> pointer-taking predicate wrappers with inline functions taking values.
>>>>
>>>> This allows us to write something like:
>>>>
>>>>     if (pmd_none(pmdp_get(pmdp)))
>>>>
>>>> While at it remove unused pmd_present2() and commented-out pmd_bad()
>>>> on sun3.
>>>>
>>>> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
>>>> ---
>>>>    arch/m68k/include/asm/mcf_pgtable.h  | 20 +++++++++++++++-----
>>>>    arch/m68k/include/asm/page.h         |  2 +-
>>>>    arch/m68k/include/asm/sun3_pgtable.h | 23 +++++++++++++++--------
>>>>    3 files changed, 31 insertions(+), 14 deletions(-)
>>>>
>>>> diff --git a/arch/m68k/include/asm/mcf_pgtable.h b/arch/m68k/include/asm/mcf_pgtable.h
>>>> index f45a882238dbb..4efa8e3e1e98a 100644
>>>> --- a/arch/m68k/include/asm/mcf_pgtable.h
>>>> +++ b/arch/m68k/include/asm/mcf_pgtable.h
>>>> @@ -130,11 +130,21 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr,
>>>>      #define pte_page(pte)    virt_to_page(__pte_page(pte))
>>>>    -static inline int pmd_none2(pmd_t *pmd) { return !pmd_val(*pmd); }
>>>> -#define pmd_none(pmd) pmd_none2(&(pmd))
>>>> -static inline int pmd_bad2(pmd_t *pmd) { return 0; }
>>>> -#define pmd_bad(pmd) pmd_bad2(&(pmd))
>>>> -#define pmd_present(pmd) (!pmd_none2(&(pmd)))
>>>> +static inline int pmd_none(pmd_t pmd)
>>>> +{
>>>> +    return !pmd_val(pmd);
>>>> +}
>>>> +
>>>> +static inline int pmd_bad(pmd_t pmd)
>>>> +{
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static inline int pmd_present(pmd_t pmd)
>>>> +{
>>>> +    return !pmd_none(pmd);
>>>> +}
>>>> +
>>>>    static inline void pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
>>>>      diff --git a/arch/m68k/include/asm/page.h b/arch/m68k/include/asm/page.h
>>>> index d30f8b2f15927..09a2c020ec992 100644
>>>> --- a/arch/m68k/include/asm/page.h
>>>> +++ b/arch/m68k/include/asm/page.h
>>>> @@ -17,7 +17,7 @@
>>>>     */
>>>>    #if !defined(CONFIG_MMU) || CONFIG_PGTABLE_LEVELS == 3
>>>>    typedef struct { unsigned long pmd; } pmd_t;
>>>> -#define pmd_val(x)    ((&x)->pmd)
>>>> +#define pmd_val(x)    ((x).pmd)
>>>>    #define __pmd(x)    ((pmd_t) { (x) } )
>>>>    #endif
>>>>    diff --git a/arch/m68k/include/asm/sun3_pgtable.h b/arch/m68k/include/asm/sun3_pgtable.h
>>>> index 704442a391fd7..5867d24032655 100644
>>>> --- a/arch/m68k/include/asm/sun3_pgtable.h
>>>> +++ b/arch/m68k/include/asm/sun3_pgtable.h
>>>> @@ -109,14 +109,21 @@ static inline void pte_clear (struct mm_struct *mm, unsigned long addr, pte_t *p
>>>>    #define pmd_page(pmd)        virt_to_page((void *)pmd_page_vaddr(pmd))
>>>>      -static inline int pmd_none2 (pmd_t *pmd) { return !pmd_val (*pmd); }
>>>> -#define pmd_none(pmd) pmd_none2(&(pmd))
>>>> -//static inline int pmd_bad (pmd_t pmd) { return (pmd_val (pmd) & SUN3_PMD_MASK) != SUN3_PMD_MAGIC; }
>>>> -static inline int pmd_bad2 (pmd_t *pmd) { return 0; }
>>>> -#define pmd_bad(pmd) pmd_bad2(&(pmd))
>>>> -static inline int pmd_present2 (pmd_t *pmd) { return pmd_val (*pmd) & SUN3_PMD_VALID; }
>>>> -/* #define pmd_present(pmd) pmd_present2(&(pmd)) */
>>>> -#define pmd_present(pmd) (!pmd_none2(&(pmd)))
>>>> +static inline int pmd_none(pmd_t pmd)
>>>> +{
>>>> +    return !pmd_val(pmd);
>>>> +}
>>>> +
>>>> +static inline int pmd_bad(pmd_t pmd)
>>>> +{
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static inline int pmd_present(pmd_t pmd)
>>>> +{
>>>> +    return !pmd_none(pmd);
>>>> +}
>>>> +
>>>>    static inline void pmd_clear (pmd_t *pmdp) { pmd_val (*pmdp) = 0; }
>>>>      /*
>>> Hi David,
>>>
>>> Thanks for picking this up, and for the m68k cleanup!
>>>
>>> Tested-by: Hui Zhu <zhuhui@kylinos.cn>
>>>
>>> Tested with gcc-m68k-linux-gnu 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04), on
>>> three configs that between them cover all three m68k families:
>>>
>>>    amiga_defconfig                    Motorola MMU, PGTABLE_LEVELS=3
>>>    sun3_defconfig                     SUN3,  PGTABLE_LEVELS=2
>>>    m5208evb_defconfig + CONFIG_MMU=y  ColdFire,  PGTABLE_LEVELS=2
>>>
>>> Without your patch none of the three can build mm/memory.o, and each fails
>>> at a different site:
>>>
>>>    arch/m68k/include/asm/page.h:20:27: error: lvalue required as unary '&' operand
>>>    arch/m68k/include/asm/sun3_pgtable.h:113:33: error: lvalue required as unary '&' operand
>>>    arch/m68k/include/asm/mcf_pgtable.h:134:33: error: lvalue required as unary '&' operand
>>>
>>> With it, all three build a full vmlinux with zero warnings.
>>>
>>> Two things that might save you some time:
>>>
>>> - m68k looks like the only arch with this pattern. Grepping arch/ and
>>>    include/ for '(&x)->pmd' and '(&(pmd))' finds nothing outside arch/m68k/,
>>>    and pmd_none2()/pmd_bad2()/pmd_present2() only exist in mcf_pgtable.h and
>>>    sun3_pgtable.h. So your three hunks look complete to me.
>>>
>>> - No m52xx/m54xx defconfig enables CONFIG_MMU, so the ColdFire path through
>>>    mcf_pgtable.h has no defconfig coverage at all; you have to flip it on by
>>>    hand (scripts/config -e MMU && make olddefconfig) to reach it. That is
>>>    probably how this went unnoticed for so long - my patch has been breaking
>>>    m68k since v1, because pmdp_get() returns by value just like
>>>    pmdp_get_lockless() does.
>> The m5475evb_defconfig is supposed to have MMU enabled:
>>
>>       $ ARCH=m68k CROSS_COMPILE=m68k-linux-uclibc- make m5475evb_defconfig
>>         HOSTCC  scripts/basic/fixdep
>>         [snip]
>>         HOSTLD  scripts/kconfig/conf
>>       #
>>       # configuration written to .config
>>       #
>>       $
>>       $ grep CONFIG_MMU .config
>>       CONFIG_MMU=y
>>       CONFIG_MMU_COLDFIRE=y
>>       CONFIG_MMU_GATHER_NO_RANGE=y
>>       CONFIG_MMU_GATHER_MERGE_VMAS=y
>>       CONFIG_MMU_LAZY_TLB_REFCOUNT=y
>>
>> But something ends up disabling it when you compile.
>> I'll fix this.
>>
>>
>>> I also checked whether your patch changes code generation, by building the
>>> same tree with and without it and comparing every object with objdump -d
>>> (plain cmp is useless here, timestamps and link layout shift everything).
>>> Counting only objects whose disassembly actually differs:
>>>
>>>    amiga      3733 objects, 0 changed
>>>    sun3       3553 objects, 3 changed: mm/memory.o, mm/pgtable-generic.o,
>>>                                        mm/vmalloc.o
>>>    m5208evb    716 objects, 1 changed: mm/pgtable-generic.o
>>>
>>> amiga is the interesting one. It is the only config where pmd_val() itself
>>> changed, and it comes out bit-for-bit identical, which also says the build
>>> is reproducible enough for this comparison to mean anything. sun3 and
>>> ColdFire differ because pmd_none() stops taking the address of its
>>> argument, and the direction is an improvement. In mm/vmalloc.o on sun3 one
>>> function loses a forced spill:
>>>
>>>    -  subql #4,%sp
>>>    -  movel %d0,%sp@(12)     /* spill the pmd value */
>>>    -  lea %sp@(12),%a1       /* &(pmd) */
>>>    -  moveal %a1@,%a0        /* read it back */
>>>
>>> and turns into a 13 instruction function with no stack slot at all.
>>> Instruction counts are otherwise flat (sun3 mm/memory.o 7373 -> 7373,
>>> mm/vmalloc.o 5514 -> 5515), so it is not a size regression.
>>>
>>> Dragging the cleanup along with the fix sounds right to me. It is a hard
>>> prerequisite, and sent on its own it would just look like a cleanup with no
>>> urgency. I assume it goes in ahead of 6ab55cfd in core-7.5.misc, so that
>>> linux-next never sees a broken m68k in between.
>>>
>>> One red herring in case anyone else pokes at this: m5208evb with its stock
>>> defconfig (CONFIG_MMU not set) fails to build mm/memory.o for reasons that
>>> have nothing to do with any of this - implicit declarations of
>>> pte_is_uffd_wp_marker() and pmd_pgtable(), plus a parse error in
>>> include/linux/mm.h. Pre-existing, and it goes away once MMU is on.
>> You should not be able to set MMU=y if configured for the M5208.
>> It doesn't have an MMU. You want the 5475, 5485 or 54418 to test
>> valid MMU=y configurations.
>>
>> Regards
>> Greg
>>
>>
>>
> 
> You are right on both counts, and that mail of mine had three things
> wrong.
> 
> 1. "No m52xx/m54xx defconfig enables CONFIG_MMU" - wrong.  I grepped the
> defconfig fragments for CONFIG_MMU=y, but MMU is 'default y', so an
> enabled MMU never appears in the fragment.  m5475evb and m54418evb
> both give CONFIG_MMU=y, as you show.
> 
> 2. My "m5208evb + CONFIG_MMU=y" test config was not a valid config.
> M520x is 'depends on !MMU', so forcing MMU on leaves the SoC choice
> unsatisfiable and olddefconfig silently drops it:
> 
> before:  # CONFIG_MMU is not set / CONFIG_M520x=y
> after:   CONFIG_MMU=y / CONFIG_MMU_COLDFIRE=y / CONFIG_M520x gone
> 
> Please disregard the m5208evb row in my codegen numbers.
> 
> 3. The "red herring" I flagged was my own doing, not a pre-existing
> problem.  mm/Makefile has 'mmu-$(CONFIG_MMU) := ... memory.o', so a
> NOMMU config never builds mm/memory.o at all; I asked for a target
> that config does not have.  Sorry for the noise on that one.
> 
> Retested with the configs you named (7.4-rc1 plus the do_fault_around
> change plus David's cleanup, gcc-m68k-linux-gnu 13.3.0):
> 
> m5475evb_defconfig   without David's patch: mm/memory.o fails at
> mcf_pgtable.h:134 with it: full -j24 build ok, vmlinux 4215344
> bytes, 0 warnings
> 
> m54418evb_defconfig  without David's patch: same mcf_pgtable.h:134
> failure
> 
> So the Tested-by I sent covers amiga, sun3 and m5475evb.  It is
> build-tested only - I have no m68k hardware here to boot.

Thanks for retesting. Looks good.


> On MMU being disabled when you compile: I cannot reproduce that. Here
> m5475evb_defconfig gives CONFIG_MMU=y, olddefconfig reports no change,
> two consecutive defconfig runs produce an identical .config, and a full
> build finishes with CONFIG_MMU=y and CONFIG_M547x=y still set and
> .config untouched.  Two differences from your setup that might matter:
> I am on a glibc toolchain rather than uclibc, and building out of tree
> with O= rather than in tree.

I can't reproduce either now. I must have done something silly when
quickly double checking that config. Multiple configures and builds
and I think it is all doing the right things at configuration and build.


> One observation, which may well be unrelated.  m5475evb_defconfig never
> sets CONFIG_M547x - it only ends up with it because the "ColdFire SoC
> type" choice defaults to M520x, which is unavailable under MMU=y, so
> kconfig falls back to the first usable member. m54418evb_defconfig sets
> CONFIG_M5441x=y explicitly.  If that fallback is what breaks on your
> side, making m5475evb explicit would sidestep it.

FWIW, running "make savedefconfig" on a fully built m5475evb configuration
results in an identical defconfig to arch/m68k/configs/m5475evb_defconfig.

Anyway, I applied both changes and tested on real hardware. All seems to
work fine. So for ColdFire:

    Tested-by: Greg Ungerer <gerg@linux-m68k.org>

Regards
Greg



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

* Re: [PATCH v4] mm/memory: use pmdp_get_lockless() for the pmd read in do_fault_around()
  2026-11-09 14:02         ` Greg Ungerer
@ 2026-11-09 14:04           ` David Hildenbrand (Arm)
  0 siblings, 0 replies; 6+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-09 14:04 UTC (permalink / raw)
  To: Greg Ungerer, Hui Zhu, Andrew Morton, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, linux-mm, linux-kernel,
	Geert Uytterhoeven, Sam Creasey
  Cc: Hui Zhu, stable+noautosel, linux-m68k

On 11/9/26 15:02, Greg Ungerer wrote:
> Hi Hui,
> 
> On 9/11/26 15:50, Hui Zhu wrote:
>> 2026/11/6 20:08, Greg Ungerer:
>>
>> Hi Greg,
>>
>>> Hi Hui,
>>>
>>> The m5475evb_defconfig is supposed to have MMU enabled:
>>>
>>>       $ ARCH=m68k CROSS_COMPILE=m68k-linux-uclibc- make m5475evb_defconfig
>>>         HOSTCC  scripts/basic/fixdep
>>>         [snip]
>>>         HOSTLD  scripts/kconfig/conf
>>>       #
>>>       # configuration written to .config
>>>       #
>>>       $
>>>       $ grep CONFIG_MMU .config
>>>       CONFIG_MMU=y
>>>       CONFIG_MMU_COLDFIRE=y
>>>       CONFIG_MMU_GATHER_NO_RANGE=y
>>>       CONFIG_MMU_GATHER_MERGE_VMAS=y
>>>       CONFIG_MMU_LAZY_TLB_REFCOUNT=y
>>>
>>> But something ends up disabling it when you compile.
>>> I'll fix this.
>>>
>>>
>>> You should not be able to set MMU=y if configured for the M5208.
>>> It doesn't have an MMU. You want the 5475, 5485 or 54418 to test
>>> valid MMU=y configurations.
>>>
>>> Regards
>>> Greg
>>>
>>>
>>>
>>
>> You are right on both counts, and that mail of mine had three things
>> wrong.
>>
>> 1. "No m52xx/m54xx defconfig enables CONFIG_MMU" - wrong.  I grepped the
>> defconfig fragments for CONFIG_MMU=y, but MMU is 'default y', so an
>> enabled MMU never appears in the fragment.  m5475evb and m54418evb
>> both give CONFIG_MMU=y, as you show.
>>
>> 2. My "m5208evb + CONFIG_MMU=y" test config was not a valid config.
>> M520x is 'depends on !MMU', so forcing MMU on leaves the SoC choice
>> unsatisfiable and olddefconfig silently drops it:
>>
>> before:  # CONFIG_MMU is not set / CONFIG_M520x=y
>> after:   CONFIG_MMU=y / CONFIG_MMU_COLDFIRE=y / CONFIG_M520x gone
>>
>> Please disregard the m5208evb row in my codegen numbers.
>>
>> 3. The "red herring" I flagged was my own doing, not a pre-existing
>> problem.  mm/Makefile has 'mmu-$(CONFIG_MMU) := ... memory.o', so a
>> NOMMU config never builds mm/memory.o at all; I asked for a target
>> that config does not have.  Sorry for the noise on that one.
>>
>> Retested with the configs you named (7.4-rc1 plus the do_fault_around
>> change plus David's cleanup, gcc-m68k-linux-gnu 13.3.0):
>>
>> m5475evb_defconfig   without David's patch: mm/memory.o fails at
>> mcf_pgtable.h:134 with it: full -j24 build ok, vmlinux 4215344
>> bytes, 0 warnings
>>
>> m54418evb_defconfig  without David's patch: same mcf_pgtable.h:134
>> failure
>>
>> So the Tested-by I sent covers amiga, sun3 and m5475evb.  It is
>> build-tested only - I have no m68k hardware here to boot.
> 
> Thanks for retesting. Looks good.
> 
> 
>> On MMU being disabled when you compile: I cannot reproduce that. Here
>> m5475evb_defconfig gives CONFIG_MMU=y, olddefconfig reports no change,
>> two consecutive defconfig runs produce an identical .config, and a full
>> build finishes with CONFIG_MMU=y and CONFIG_M547x=y still set and
>> .config untouched.  Two differences from your setup that might matter:
>> I am on a glibc toolchain rather than uclibc, and building out of tree
>> with O= rather than in tree.
> 
> I can't reproduce either now. I must have done something silly when
> quickly double checking that config. Multiple configures and builds
> and I think it is all doing the right things at configuration and build.
> 
> 
>> One observation, which may well be unrelated.  m5475evb_defconfig never
>> sets CONFIG_M547x - it only ends up with it because the "ColdFire SoC
>> type" choice defaults to M520x, which is unavailable under MMU=y, so
>> kconfig falls back to the first usable member. m54418evb_defconfig sets
>> CONFIG_M5441x=y explicitly.  If that fallback is what breaks on your
>> side, making m5475evb explicit would sidestep it.
> 
> FWIW, running "make savedefconfig" on a fully built m5475evb configuration
> results in an identical defconfig to arch/m68k/configs/m5475evb_defconfig.
> 
> Anyway, I applied both changes and tested on real hardware. All seems to
> work fine. So for ColdFire:
> 
>     Tested-by: Greg Ungerer <gerg@linux-m68k.org>

Thanks!

-- 
Cheers,

David

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

end of thread, other threads:[~2026-11-09 14:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20261104091050.117076-1-hui.zhu@linux.dev>
2026-11-05 12:00 ` [PATCH v4] mm/memory: use pmdp_get_lockless() for the pmd read in do_fault_around() David Hildenbrand (Arm)
2026-11-06  3:03   ` Hui Zhu
2026-11-06 12:08     ` Greg Ungerer
2026-11-09  5:50       ` Hui Zhu
2026-11-09 14:02         ` Greg Ungerer
2026-11-09 14:04           ` David Hildenbrand (Arm)

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