* 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