linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
@ 2025-10-23 21:52 Yang Shi
  2025-11-10 23:06 ` Yang Shi
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Yang Shi @ 2025-10-23 21:52 UTC (permalink / raw)
  To: ryan.roberts, cl, catalin.marinas, will
  Cc: yang, linux-arm-kernel, linux-kernel

Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
rodata=full"), the direct mapping may be split on some machines instead
keeping static since boot. It makes more sense to show the direct mapping
use in /proc/meminfo than before.
This patch will make /proc/meminfo show the direct mapping use like the
below (4K base page size):
DirectMap4K:	   94792 kB
DirectMap64K:	  134208 kB
DirectMap2M:	 1173504 kB
DirectMap32M:	 5636096 kB
DirectMap1G:	529530880 kB

Although just the machines which support BBML2_NOABORT can split the
direct mapping, show it on all machines regardless of BBML2_NOABORT so
that the users have consistent view in order to avoid confusion.

Although ptdump also can tell the direct map use, but it needs to dump
the whole kernel page table. It is costly and overkilling. It is also
in debugfs which may not be enabled by all distros. So showing direct
map use in /proc/meminfo seems more convenient and has less overhead.

Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
 arch/arm64/mm/mmu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

v2: * Counted in size instead of the number of entries per Ryan
    * Removed shift array per Ryan
    * Use lower case "k" per Ryan
    * Fixed a couple of build warnings reported by kernel test robot
    * Fixed a couple of poential miscounts 

diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index b8d37eb037fc..7207b55d0046 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -29,6 +29,7 @@
 #include <linux/mm_inline.h>
 #include <linux/pagewalk.h>
 #include <linux/stop_machine.h>
+#include <linux/proc_fs.h>
 
 #include <asm/barrier.h>
 #include <asm/cputype.h>
@@ -51,6 +52,17 @@
 
 DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
 
+enum direct_map_type {
+	PTE,
+	CONT_PTE,
+	PMD,
+	CONT_PMD,
+	PUD,
+	NR_DIRECT_MAP_TYPE,
+};
+
+static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];
+
 u64 kimage_voffset __ro_after_init;
 EXPORT_SYMBOL(kimage_voffset);
 
@@ -171,6 +183,45 @@ static void init_clear_pgtable(void *table)
 	dsb(ishst);
 }
 
+#ifdef CONFIG_PROC_FS
+void arch_report_meminfo(struct seq_file *m)
+{
+	char *size[NR_DIRECT_MAP_TYPE];
+
+#if defined(CONFIG_ARM64_4K_PAGES)
+	size[PTE] = "4k";
+	size[CONT_PTE] = "64k";
+	size[PMD] = "2M";
+	size[CONT_PMD] = "32M";
+	size[PUD] = "1G";
+#elif defined(CONFIG_ARM64_16K_PAGES)
+	size[PTE] = "16k";
+	size[CONT_PTE] = "2M";
+	size[PMD] = "32M";
+	size[CONT_PMD] = "1G";
+#elif defined(CONFIG_ARM64_64K_PAGES)
+	size[PTE] = "64k";
+	size[CONT_PTE] = "2M";
+	size[PMD] = "512M";
+	size[CONT_PMD] = "16G";
+#endif
+
+	seq_printf(m, "DirectMap%s:	%8lu kB\n",
+			size[PTE], direct_map_size[PTE] >> 10);
+	seq_printf(m, "DirectMap%s:	%8lu kB\n",
+			size[CONT_PTE],
+			direct_map_size[CONT_PTE] >> 10);
+	seq_printf(m, "DirectMap%s:	%8lu kB\n",
+			size[PMD], direct_map_size[PMD] >> 10);
+	seq_printf(m, "DirectMap%s:	%8lu kB\n",
+			size[CONT_PMD],
+			direct_map_size[CONT_PMD] >> 10);
+	if (pud_sect_supported())
+		seq_printf(m, "DirectMap%s:	%8lu kB\n",
+			size[PUD], direct_map_size[PUD] >> 10);
+}
+#endif
+
 static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
 		     phys_addr_t phys, pgprot_t prot)
 {
@@ -234,6 +285,11 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
 
 		init_pte(ptep, addr, next, phys, __prot);
 
+		if (pgprot_val(__prot) & PTE_CONT)
+			direct_map_size[CONT_PTE] += next - addr;
+		else
+			direct_map_size[PTE] += next - addr;
+
 		ptep += pte_index(next) - pte_index(addr);
 		phys += next - addr;
 	} while (addr = next, addr != end);
@@ -262,6 +318,17 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
 		    (flags & NO_BLOCK_MAPPINGS) == 0) {
 			pmd_set_huge(pmdp, phys, prot);
 
+			/*
+			 * It is possible to have mappings allow cont mapping
+			 * but disallow block mapping. For example,
+			 * map_entry_trampoline().
+			 * So we have to increase CONT_PMD and PMD size here
+			 * to avoid double counting.
+			 */
+			if (pgprot_val(prot) & PTE_CONT)
+				direct_map_size[CONT_PMD] += next - addr;
+			else
+				direct_map_size[PMD] += next - addr;
 			/*
 			 * After the PMD entry has been populated once, we
 			 * only allow updates to the permission attributes.
@@ -368,6 +435,7 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
 		    (flags & NO_BLOCK_MAPPINGS) == 0) {
 			pud_set_huge(pudp, phys, prot);
 
+			direct_map_size[PUD] += next - addr;
 			/*
 			 * After the PUD entry has been populated once, we
 			 * only allow updates to the permission attributes.
@@ -532,9 +600,13 @@ static void split_contpte(pte_t *ptep)
 {
 	int i;
 
+	direct_map_size[CONT_PTE] -= CONT_PTE_SIZE;
+
 	ptep = PTR_ALIGN_DOWN(ptep, sizeof(*ptep) * CONT_PTES);
 	for (i = 0; i < CONT_PTES; i++, ptep++)
 		__set_pte(ptep, pte_mknoncont(__ptep_get(ptep)));
+
+	direct_map_size[PTE] += CONT_PTE_SIZE;
 }
 
 static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
@@ -559,8 +631,13 @@ static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
 	if (to_cont)
 		prot = __pgprot(pgprot_val(prot) | PTE_CONT);
 
+	direct_map_size[PMD] -= PMD_SIZE;
 	for (i = 0; i < PTRS_PER_PTE; i++, ptep++, pfn++)
 		__set_pte(ptep, pfn_pte(pfn, prot));
+	if (to_cont)
+		direct_map_size[CONT_PTE] += PMD_SIZE;
+	else
+		direct_map_size[PTE] += PMD_SIZE;
 
 	/*
 	 * Ensure the pte entries are visible to the table walker by the time
@@ -576,9 +653,13 @@ static void split_contpmd(pmd_t *pmdp)
 {
 	int i;
 
+	direct_map_size[CONT_PMD] -= CONT_PMD_SIZE;
+
 	pmdp = PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS);
 	for (i = 0; i < CONT_PMDS; i++, pmdp++)
 		set_pmd(pmdp, pmd_mknoncont(pmdp_get(pmdp)));
+
+	direct_map_size[PMD] += CONT_PMD_SIZE;
 }
 
 static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
@@ -604,8 +685,13 @@ static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
 	if (to_cont)
 		prot = __pgprot(pgprot_val(prot) | PTE_CONT);
 
+	direct_map_size[PUD] -= PUD_SIZE;
 	for (i = 0; i < PTRS_PER_PMD; i++, pmdp++, pfn += step)
 		set_pmd(pmdp, pfn_pmd(pfn, prot));
+	if (to_cont)
+		direct_map_size[CONT_PMD] += PUD_SIZE;
+	else
+		direct_map_size[PMD] += PUD_SIZE;
 
 	/*
 	 * Ensure the pmd entries are visible to the table walker by the time
-- 
2.47.0



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-10-23 21:52 [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo Yang Shi
@ 2025-11-10 23:06 ` Yang Shi
  2025-11-12 10:16 ` Ryan Roberts
  2025-11-19 19:12 ` Catalin Marinas
  2 siblings, 0 replies; 14+ messages in thread
From: Yang Shi @ 2025-11-10 23:06 UTC (permalink / raw)
  To: ryan.roberts, cl, catalin.marinas, will; +Cc: linux-arm-kernel, linux-kernel

Hi Ryan,

Gently ping. Thanks for reviewing the v1 patch. Any comment for this 
version?

Yang


On 10/23/25 2:52 PM, Yang Shi wrote:
> Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
> rodata=full"), the direct mapping may be split on some machines instead
> keeping static since boot. It makes more sense to show the direct mapping
> use in /proc/meminfo than before.
> This patch will make /proc/meminfo show the direct mapping use like the
> below (4K base page size):
> DirectMap4K:	   94792 kB
> DirectMap64K:	  134208 kB
> DirectMap2M:	 1173504 kB
> DirectMap32M:	 5636096 kB
> DirectMap1G:	529530880 kB
>
> Although just the machines which support BBML2_NOABORT can split the
> direct mapping, show it on all machines regardless of BBML2_NOABORT so
> that the users have consistent view in order to avoid confusion.
>
> Although ptdump also can tell the direct map use, but it needs to dump
> the whole kernel page table. It is costly and overkilling. It is also
> in debugfs which may not be enabled by all distros. So showing direct
> map use in /proc/meminfo seems more convenient and has less overhead.
>
> Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
> ---
>   arch/arm64/mm/mmu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 86 insertions(+)
>
> v2: * Counted in size instead of the number of entries per Ryan
>      * Removed shift array per Ryan
>      * Use lower case "k" per Ryan
>      * Fixed a couple of build warnings reported by kernel test robot
>      * Fixed a couple of poential miscounts
>
> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> index b8d37eb037fc..7207b55d0046 100644
> --- a/arch/arm64/mm/mmu.c
> +++ b/arch/arm64/mm/mmu.c
> @@ -29,6 +29,7 @@
>   #include <linux/mm_inline.h>
>   #include <linux/pagewalk.h>
>   #include <linux/stop_machine.h>
> +#include <linux/proc_fs.h>
>   
>   #include <asm/barrier.h>
>   #include <asm/cputype.h>
> @@ -51,6 +52,17 @@
>   
>   DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
>   
> +enum direct_map_type {
> +	PTE,
> +	CONT_PTE,
> +	PMD,
> +	CONT_PMD,
> +	PUD,
> +	NR_DIRECT_MAP_TYPE,
> +};
> +
> +static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];
> +
>   u64 kimage_voffset __ro_after_init;
>   EXPORT_SYMBOL(kimage_voffset);
>   
> @@ -171,6 +183,45 @@ static void init_clear_pgtable(void *table)
>   	dsb(ishst);
>   }
>   
> +#ifdef CONFIG_PROC_FS
> +void arch_report_meminfo(struct seq_file *m)
> +{
> +	char *size[NR_DIRECT_MAP_TYPE];
> +
> +#if defined(CONFIG_ARM64_4K_PAGES)
> +	size[PTE] = "4k";
> +	size[CONT_PTE] = "64k";
> +	size[PMD] = "2M";
> +	size[CONT_PMD] = "32M";
> +	size[PUD] = "1G";
> +#elif defined(CONFIG_ARM64_16K_PAGES)
> +	size[PTE] = "16k";
> +	size[CONT_PTE] = "2M";
> +	size[PMD] = "32M";
> +	size[CONT_PMD] = "1G";
> +#elif defined(CONFIG_ARM64_64K_PAGES)
> +	size[PTE] = "64k";
> +	size[CONT_PTE] = "2M";
> +	size[PMD] = "512M";
> +	size[CONT_PMD] = "16G";
> +#endif
> +
> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[PTE], direct_map_size[PTE] >> 10);
> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[CONT_PTE],
> +			direct_map_size[CONT_PTE] >> 10);
> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[PMD], direct_map_size[PMD] >> 10);
> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[CONT_PMD],
> +			direct_map_size[CONT_PMD] >> 10);
> +	if (pud_sect_supported())
> +		seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[PUD], direct_map_size[PUD] >> 10);
> +}
> +#endif
> +
>   static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
>   		     phys_addr_t phys, pgprot_t prot)
>   {
> @@ -234,6 +285,11 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
>   
>   		init_pte(ptep, addr, next, phys, __prot);
>   
> +		if (pgprot_val(__prot) & PTE_CONT)
> +			direct_map_size[CONT_PTE] += next - addr;
> +		else
> +			direct_map_size[PTE] += next - addr;
> +
>   		ptep += pte_index(next) - pte_index(addr);
>   		phys += next - addr;
>   	} while (addr = next, addr != end);
> @@ -262,6 +318,17 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
>   		    (flags & NO_BLOCK_MAPPINGS) == 0) {
>   			pmd_set_huge(pmdp, phys, prot);
>   
> +			/*
> +			 * It is possible to have mappings allow cont mapping
> +			 * but disallow block mapping. For example,
> +			 * map_entry_trampoline().
> +			 * So we have to increase CONT_PMD and PMD size here
> +			 * to avoid double counting.
> +			 */
> +			if (pgprot_val(prot) & PTE_CONT)
> +				direct_map_size[CONT_PMD] += next - addr;
> +			else
> +				direct_map_size[PMD] += next - addr;
>   			/*
>   			 * After the PMD entry has been populated once, we
>   			 * only allow updates to the permission attributes.
> @@ -368,6 +435,7 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
>   		    (flags & NO_BLOCK_MAPPINGS) == 0) {
>   			pud_set_huge(pudp, phys, prot);
>   
> +			direct_map_size[PUD] += next - addr;
>   			/*
>   			 * After the PUD entry has been populated once, we
>   			 * only allow updates to the permission attributes.
> @@ -532,9 +600,13 @@ static void split_contpte(pte_t *ptep)
>   {
>   	int i;
>   
> +	direct_map_size[CONT_PTE] -= CONT_PTE_SIZE;
> +
>   	ptep = PTR_ALIGN_DOWN(ptep, sizeof(*ptep) * CONT_PTES);
>   	for (i = 0; i < CONT_PTES; i++, ptep++)
>   		__set_pte(ptep, pte_mknoncont(__ptep_get(ptep)));
> +
> +	direct_map_size[PTE] += CONT_PTE_SIZE;
>   }
>   
>   static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
> @@ -559,8 +631,13 @@ static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
>   	if (to_cont)
>   		prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>   
> +	direct_map_size[PMD] -= PMD_SIZE;
>   	for (i = 0; i < PTRS_PER_PTE; i++, ptep++, pfn++)
>   		__set_pte(ptep, pfn_pte(pfn, prot));
> +	if (to_cont)
> +		direct_map_size[CONT_PTE] += PMD_SIZE;
> +	else
> +		direct_map_size[PTE] += PMD_SIZE;
>   
>   	/*
>   	 * Ensure the pte entries are visible to the table walker by the time
> @@ -576,9 +653,13 @@ static void split_contpmd(pmd_t *pmdp)
>   {
>   	int i;
>   
> +	direct_map_size[CONT_PMD] -= CONT_PMD_SIZE;
> +
>   	pmdp = PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS);
>   	for (i = 0; i < CONT_PMDS; i++, pmdp++)
>   		set_pmd(pmdp, pmd_mknoncont(pmdp_get(pmdp)));
> +
> +	direct_map_size[PMD] += CONT_PMD_SIZE;
>   }
>   
>   static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
> @@ -604,8 +685,13 @@ static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
>   	if (to_cont)
>   		prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>   
> +	direct_map_size[PUD] -= PUD_SIZE;
>   	for (i = 0; i < PTRS_PER_PMD; i++, pmdp++, pfn += step)
>   		set_pmd(pmdp, pfn_pmd(pfn, prot));
> +	if (to_cont)
> +		direct_map_size[CONT_PMD] += PUD_SIZE;
> +	else
> +		direct_map_size[PMD] += PUD_SIZE;
>   
>   	/*
>   	 * Ensure the pmd entries are visible to the table walker by the time



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-10-23 21:52 [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo Yang Shi
  2025-11-10 23:06 ` Yang Shi
@ 2025-11-12 10:16 ` Ryan Roberts
  2025-11-12 17:11   ` Christoph Lameter (Ampere)
  2025-11-12 22:24   ` Yang Shi
  2025-11-19 19:12 ` Catalin Marinas
  2 siblings, 2 replies; 14+ messages in thread
From: Ryan Roberts @ 2025-11-12 10:16 UTC (permalink / raw)
  To: Yang Shi, cl, catalin.marinas, will; +Cc: linux-arm-kernel, linux-kernel

Hi Yang,


On 23/10/2025 22:52, Yang Shi wrote:
> Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
> rodata=full"), the direct mapping may be split on some machines instead
> keeping static since boot. It makes more sense to show the direct mapping
> use in /proc/meminfo than before.
> This patch will make /proc/meminfo show the direct mapping use like the
> below (4K base page size):
> DirectMap4K:	   94792 kB
> DirectMap64K:	  134208 kB
> DirectMap2M:	 1173504 kB
> DirectMap32M:	 5636096 kB
> DirectMap1G:	529530880 kB

I have a long-term aspiration to enable "per-process page size", where each user
space process can use a different page size. The first step is to be able to
emulate a page size to the process which is larger than the kernel's. For that
reason, I really dislike introducing new ABI that exposes the geometry of the
kernel page tables to user space. I'd really like to be clear on what use case
benefits from this sort of information before we add it.

nit: arm64 tends to use the term "linear map" not "direct map". I'm not sure why
or what the history is. Given this is arch-specific should we be aligning on the
architecture's terminology here? I don't know...

> 
> Although just the machines which support BBML2_NOABORT can split the
> direct mapping, show it on all machines regardless of BBML2_NOABORT so
> that the users have consistent view in order to avoid confusion.
> 
> Although ptdump also can tell the direct map use, but it needs to dump
> the whole kernel page table. It is costly and overkilling. It is also
> in debugfs which may not be enabled by all distros. So showing direct
> map use in /proc/meminfo seems more convenient and has less overhead.
> 
> Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
> ---
>  arch/arm64/mm/mmu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 86 insertions(+)
> 
> v2: * Counted in size instead of the number of entries per Ryan
>     * Removed shift array per Ryan
>     * Use lower case "k" per Ryan
>     * Fixed a couple of build warnings reported by kernel test robot
>     * Fixed a couple of poential miscounts 
> 
> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> index b8d37eb037fc..7207b55d0046 100644
> --- a/arch/arm64/mm/mmu.c
> +++ b/arch/arm64/mm/mmu.c
> @@ -29,6 +29,7 @@
>  #include <linux/mm_inline.h>
>  #include <linux/pagewalk.h>
>  #include <linux/stop_machine.h>
> +#include <linux/proc_fs.h>
>  
>  #include <asm/barrier.h>
>  #include <asm/cputype.h>
> @@ -51,6 +52,17 @@
>  
>  DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
>  
> +enum direct_map_type {
> +	PTE,
> +	CONT_PTE,
> +	PMD,
> +	CONT_PMD,
> +	PUD,
> +	NR_DIRECT_MAP_TYPE,
> +};
> +
> +static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];

I wonder if you should wrap all the adds and subtracts into a helper function,
which can then be defined as a nop when !CONFIG_PROC_FS. It means we only need
direct_map_size[] when PROC_FS is enabled too.

e.g.

#ifdef CONFIG_PROC_FS
static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];

static inline void direct_map_meminfo_add(unsigned long size,
					  enum direct_map_type type)
{
	direct_map_size[type] += size;
}

static inline void direct_map_meminfo_sub(unsigned long size,
					  enum direct_map_type type)
{
	direct_map_size[type] -= size;
}
#else
static inline void direct_map_meminfo_add(unsigned long size,
					  enum direct_map_type type) {}
static inline void direct_map_meminfo_sub(unsigned long size,
					  enum direct_map_type type) {}
#endif

Then use it like this:
direct_map_meminfo_sub(next - addr, PMD);
direct_map_meminfo_add(next - addr, to_cont ? CONT_PTE : PTE);

> +
>  u64 kimage_voffset __ro_after_init;
>  EXPORT_SYMBOL(kimage_voffset);
>  
> @@ -171,6 +183,45 @@ static void init_clear_pgtable(void *table)
>  	dsb(ishst);
>  }
>  
> +#ifdef CONFIG_PROC_FS
> +void arch_report_meminfo(struct seq_file *m)
> +{
> +	char *size[NR_DIRECT_MAP_TYPE];
> +
> +#if defined(CONFIG_ARM64_4K_PAGES)
> +	size[PTE] = "4k";
> +	size[CONT_PTE] = "64k";
> +	size[PMD] = "2M";
> +	size[CONT_PMD] = "32M";
> +	size[PUD] = "1G";
> +#elif defined(CONFIG_ARM64_16K_PAGES)
> +	size[PTE] = "16k";
> +	size[CONT_PTE] = "2M";
> +	size[PMD] = "32M";
> +	size[CONT_PMD] = "1G";
> +#elif defined(CONFIG_ARM64_64K_PAGES)
> +	size[PTE] = "64k";
> +	size[CONT_PTE] = "2M";
> +	size[PMD] = "512M";
> +	size[CONT_PMD] = "16G";
> +#endif
> +
> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[PTE], direct_map_size[PTE] >> 10);
> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[CONT_PTE],
> +			direct_map_size[CONT_PTE] >> 10);
> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[PMD], direct_map_size[PMD] >> 10);
> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[CONT_PMD],
> +			direct_map_size[CONT_PMD] >> 10);
> +	if (pud_sect_supported())
> +		seq_printf(m, "DirectMap%s:	%8lu kB\n",
> +			size[PUD], direct_map_size[PUD] >> 10);
> +}
> +#endif
> +
>  static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
>  		     phys_addr_t phys, pgprot_t prot)
>  {
> @@ -234,6 +285,11 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
>  
>  		init_pte(ptep, addr, next, phys, __prot);
>  
> +		if (pgprot_val(__prot) & PTE_CONT)
> +			direct_map_size[CONT_PTE] += next - addr;
> +		else
> +			direct_map_size[PTE] += next - addr;
> +
>  		ptep += pte_index(next) - pte_index(addr);
>  		phys += next - addr;
>  	} while (addr = next, addr != end);
> @@ -262,6 +318,17 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
>  		    (flags & NO_BLOCK_MAPPINGS) == 0) {
>  			pmd_set_huge(pmdp, phys, prot);
>  
> +			/*
> +			 * It is possible to have mappings allow cont mapping
> +			 * but disallow block mapping. For example,
> +			 * map_entry_trampoline().
> +			 * So we have to increase CONT_PMD and PMD size here
> +			 * to avoid double counting.
> +			 */
> +			if (pgprot_val(prot) & PTE_CONT)
> +				direct_map_size[CONT_PMD] += next - addr;
> +			else
> +				direct_map_size[PMD] += next - addr;
>  			/*
>  			 * After the PMD entry has been populated once, we
>  			 * only allow updates to the permission attributes.
> @@ -368,6 +435,7 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
>  		    (flags & NO_BLOCK_MAPPINGS) == 0) {
>  			pud_set_huge(pudp, phys, prot);
>  
> +			direct_map_size[PUD] += next - addr;

I think this (and all the lower levels) are likely over-accounting. For example,
__kpti_install_ng_mappings() and map_entry_trampoline() reuse the infra to
create separate pgtables. Then you have fixmap, which uses
create_mapping_noalloc(), efi which uses create_pgd_mapping() and
update_mapping_prot() used to change permissions for various parts of the kernel
image. They all reuse the infra too.

>  			/*
>  			 * After the PUD entry has been populated once, we
>  			 * only allow updates to the permission attributes.
> @@ -532,9 +600,13 @@ static void split_contpte(pte_t *ptep)
>  {
>  	int i;
>  
> +	direct_map_size[CONT_PTE] -= CONT_PTE_SIZE;
> +
>  	ptep = PTR_ALIGN_DOWN(ptep, sizeof(*ptep) * CONT_PTES);
>  	for (i = 0; i < CONT_PTES; i++, ptep++)
>  		__set_pte(ptep, pte_mknoncont(__ptep_get(ptep)));
> +
> +	direct_map_size[PTE] += CONT_PTE_SIZE;

Similar issue: we aspire to reuse this split_* infra for regions other than the
linear map - e.g. vmalloc. So I don't like the idea of baking in an assumption
that any split is definitely targetting the linear map.

I guess if you pass the start and end VA to the add/subtract function, it could
fitler based on whether the region is within the linear map region?

Overall, I'm personally not a huge fan of adding this capability. I'd need to
understand the use case to change my mind. But I'm not the maintainer so perhaps
my opinion isn't all that important ;-)

Thanks,
Ryan

>  }
>  
>  static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
> @@ -559,8 +631,13 @@ static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
>  	if (to_cont)
>  		prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>  
> +	direct_map_size[PMD] -= PMD_SIZE;
>  	for (i = 0; i < PTRS_PER_PTE; i++, ptep++, pfn++)
>  		__set_pte(ptep, pfn_pte(pfn, prot));
> +	if (to_cont)
> +		direct_map_size[CONT_PTE] += PMD_SIZE;
> +	else
> +		direct_map_size[PTE] += PMD_SIZE;
>  
>  	/*
>  	 * Ensure the pte entries are visible to the table walker by the time
> @@ -576,9 +653,13 @@ static void split_contpmd(pmd_t *pmdp)
>  {
>  	int i;
>  
> +	direct_map_size[CONT_PMD] -= CONT_PMD_SIZE;
> +
>  	pmdp = PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS);
>  	for (i = 0; i < CONT_PMDS; i++, pmdp++)
>  		set_pmd(pmdp, pmd_mknoncont(pmdp_get(pmdp)));
> +
> +	direct_map_size[PMD] += CONT_PMD_SIZE;
>  }
>  
>  static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
> @@ -604,8 +685,13 @@ static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
>  	if (to_cont)
>  		prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>  
> +	direct_map_size[PUD] -= PUD_SIZE;
>  	for (i = 0; i < PTRS_PER_PMD; i++, pmdp++, pfn += step)
>  		set_pmd(pmdp, pfn_pmd(pfn, prot));
> +	if (to_cont)
> +		direct_map_size[CONT_PMD] += PUD_SIZE;
> +	else
> +		direct_map_size[PMD] += PUD_SIZE;
>  
>  	/*
>  	 * Ensure the pmd entries are visible to the table walker by the time



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-12 10:16 ` Ryan Roberts
@ 2025-11-12 17:11   ` Christoph Lameter (Ampere)
  2025-11-13 11:29     ` Ryan Roberts
  2025-11-12 22:24   ` Yang Shi
  1 sibling, 1 reply; 14+ messages in thread
From: Christoph Lameter (Ampere) @ 2025-11-12 17:11 UTC (permalink / raw)
  To: Ryan Roberts
  Cc: Yang Shi, catalin.marinas, will, linux-arm-kernel, linux-kernel

On Wed, 12 Nov 2025, Ryan Roberts wrote:

> I have a long-term aspiration to enable "per-process page size", where each user
> space process can use a different page size. The first step is to be able to
> emulate a page size to the process which is larger than the kernel's. For that
> reason, I really dislike introducing new ABI that exposes the geometry of the
> kernel page tables to user space. I'd really like to be clear on what use case
> benefits from this sort of information before we add it.

One is user space where you want to "emulate" other page sizes and the
other is kernel space.

The per process page size is likely going to end up
being a per VMA page size since these address spaces can be shared and the
VMA is already containing information about huge pages, memory policies
and other stuff relatd to memory layout. And yes it would be great to have
an accounting of the page sizes used in a VMA.


> nit: arm64 tends to use the term "linear map" not "direct map". I'm not sure why
> or what the history is. Given this is arch-specific should we be aligning on the
> architecture's terminology here? I don't know...

Other architectures are already exposing this data via the terminology
used here. The information is useful for seeing if there is an issue with
small pages that could be impacting kernel performance. It is surprising
coming from oter architectures that this information is not readily
available.



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-12 10:16 ` Ryan Roberts
  2025-11-12 17:11   ` Christoph Lameter (Ampere)
@ 2025-11-12 22:24   ` Yang Shi
  2025-11-13 11:28     ` Ryan Roberts
  1 sibling, 1 reply; 14+ messages in thread
From: Yang Shi @ 2025-11-12 22:24 UTC (permalink / raw)
  To: Ryan Roberts, cl, catalin.marinas, will; +Cc: linux-arm-kernel, linux-kernel



On 11/12/25 2:16 AM, Ryan Roberts wrote:
> Hi Yang,
>
>
> On 23/10/2025 22:52, Yang Shi wrote:
>> Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
>> rodata=full"), the direct mapping may be split on some machines instead
>> keeping static since boot. It makes more sense to show the direct mapping
>> use in /proc/meminfo than before.
>> This patch will make /proc/meminfo show the direct mapping use like the
>> below (4K base page size):
>> DirectMap4K:	   94792 kB
>> DirectMap64K:	  134208 kB
>> DirectMap2M:	 1173504 kB
>> DirectMap32M:	 5636096 kB
>> DirectMap1G:	529530880 kB
> I have a long-term aspiration to enable "per-process page size", where each user
> space process can use a different page size. The first step is to be able to
> emulate a page size to the process which is larger than the kernel's. For that
> reason, I really dislike introducing new ABI that exposes the geometry of the
> kernel page tables to user space. I'd really like to be clear on what use case
> benefits from this sort of information before we add it.

Thanks for the information. I'm not sure what "per-process page size" 
exactly is. But isn't it just user space thing? I have hard time to 
understand how exposing kernel page table geometry will have impact on it.

The direct map use information is quite useful for tracking direct map 
fragmentation which may have negative impact to performance and help 
diagnose and debug such issues quickly.

>
> nit: arm64 tends to use the term "linear map" not "direct map". I'm not sure why
> or what the history is. Given this is arch-specific should we be aligning on the
> architecture's terminology here? I don't know...

I actually didn't notice that. They are basically interchangeable. Just 
try to keep the consistency with other architectures, for example, x86. 
The users may have arm64 and x86 machines deployed at the same time and 
they should prefer as few churn as possible for maintaining multiple 
architectures.

>
>> Although just the machines which support BBML2_NOABORT can split the
>> direct mapping, show it on all machines regardless of BBML2_NOABORT so
>> that the users have consistent view in order to avoid confusion.
>>
>> Although ptdump also can tell the direct map use, but it needs to dump
>> the whole kernel page table. It is costly and overkilling. It is also
>> in debugfs which may not be enabled by all distros. So showing direct
>> map use in /proc/meminfo seems more convenient and has less overhead.
>>
>> Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
>> ---
>>   arch/arm64/mm/mmu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 86 insertions(+)
>>
>> v2: * Counted in size instead of the number of entries per Ryan
>>      * Removed shift array per Ryan
>>      * Use lower case "k" per Ryan
>>      * Fixed a couple of build warnings reported by kernel test robot
>>      * Fixed a couple of poential miscounts
>>
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index b8d37eb037fc..7207b55d0046 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -29,6 +29,7 @@
>>   #include <linux/mm_inline.h>
>>   #include <linux/pagewalk.h>
>>   #include <linux/stop_machine.h>
>> +#include <linux/proc_fs.h>
>>   
>>   #include <asm/barrier.h>
>>   #include <asm/cputype.h>
>> @@ -51,6 +52,17 @@
>>   
>>   DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
>>   
>> +enum direct_map_type {
>> +	PTE,
>> +	CONT_PTE,
>> +	PMD,
>> +	CONT_PMD,
>> +	PUD,
>> +	NR_DIRECT_MAP_TYPE,
>> +};
>> +
>> +static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];
> I wonder if you should wrap all the adds and subtracts into a helper function,
> which can then be defined as a nop when !CONFIG_PROC_FS. It means we only need
> direct_map_size[] when PROC_FS is enabled too.
>
> e.g.
>
> #ifdef CONFIG_PROC_FS
> static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];
>
> static inline void direct_map_meminfo_add(unsigned long size,
> 					  enum direct_map_type type)
> {
> 	direct_map_size[type] += size;
> }
>
> static inline void direct_map_meminfo_sub(unsigned long size,
> 					  enum direct_map_type type)
> {
> 	direct_map_size[type] -= size;
> }
> #else
> static inline void direct_map_meminfo_add(unsigned long size,
> 					  enum direct_map_type type) {}
> static inline void direct_map_meminfo_sub(unsigned long size,
> 					  enum direct_map_type type) {}
> #endif
>
> Then use it like this:
> direct_map_meminfo_sub(next - addr, PMD);
> direct_map_meminfo_add(next - addr, to_cont ? CONT_PTE : PTE);

Thanks for the suggestion. It seems good and it also should be able to 
make solve the over-accounting problem mentioned below easier.

>
>> +
>>   u64 kimage_voffset __ro_after_init;
>>   EXPORT_SYMBOL(kimage_voffset);
>>   
>> @@ -171,6 +183,45 @@ static void init_clear_pgtable(void *table)
>>   	dsb(ishst);
>>   }
>>   
>> +#ifdef CONFIG_PROC_FS
>> +void arch_report_meminfo(struct seq_file *m)
>> +{
>> +	char *size[NR_DIRECT_MAP_TYPE];
>> +
>> +#if defined(CONFIG_ARM64_4K_PAGES)
>> +	size[PTE] = "4k";
>> +	size[CONT_PTE] = "64k";
>> +	size[PMD] = "2M";
>> +	size[CONT_PMD] = "32M";
>> +	size[PUD] = "1G";
>> +#elif defined(CONFIG_ARM64_16K_PAGES)
>> +	size[PTE] = "16k";
>> +	size[CONT_PTE] = "2M";
>> +	size[PMD] = "32M";
>> +	size[CONT_PMD] = "1G";
>> +#elif defined(CONFIG_ARM64_64K_PAGES)
>> +	size[PTE] = "64k";
>> +	size[CONT_PTE] = "2M";
>> +	size[PMD] = "512M";
>> +	size[CONT_PMD] = "16G";
>> +#endif
>> +
>> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
>> +			size[PTE], direct_map_size[PTE] >> 10);
>> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
>> +			size[CONT_PTE],
>> +			direct_map_size[CONT_PTE] >> 10);
>> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
>> +			size[PMD], direct_map_size[PMD] >> 10);
>> +	seq_printf(m, "DirectMap%s:	%8lu kB\n",
>> +			size[CONT_PMD],
>> +			direct_map_size[CONT_PMD] >> 10);
>> +	if (pud_sect_supported())
>> +		seq_printf(m, "DirectMap%s:	%8lu kB\n",
>> +			size[PUD], direct_map_size[PUD] >> 10);
>> +}
>> +#endif
>> +
>>   static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
>>   		     phys_addr_t phys, pgprot_t prot)
>>   {
>> @@ -234,6 +285,11 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
>>   
>>   		init_pte(ptep, addr, next, phys, __prot);
>>   
>> +		if (pgprot_val(__prot) & PTE_CONT)
>> +			direct_map_size[CONT_PTE] += next - addr;
>> +		else
>> +			direct_map_size[PTE] += next - addr;
>> +
>>   		ptep += pte_index(next) - pte_index(addr);
>>   		phys += next - addr;
>>   	} while (addr = next, addr != end);
>> @@ -262,6 +318,17 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
>>   		    (flags & NO_BLOCK_MAPPINGS) == 0) {
>>   			pmd_set_huge(pmdp, phys, prot);
>>   
>> +			/*
>> +			 * It is possible to have mappings allow cont mapping
>> +			 * but disallow block mapping. For example,
>> +			 * map_entry_trampoline().
>> +			 * So we have to increase CONT_PMD and PMD size here
>> +			 * to avoid double counting.
>> +			 */
>> +			if (pgprot_val(prot) & PTE_CONT)
>> +				direct_map_size[CONT_PMD] += next - addr;
>> +			else
>> +				direct_map_size[PMD] += next - addr;
>>   			/*
>>   			 * After the PMD entry has been populated once, we
>>   			 * only allow updates to the permission attributes.
>> @@ -368,6 +435,7 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
>>   		    (flags & NO_BLOCK_MAPPINGS) == 0) {
>>   			pud_set_huge(pudp, phys, prot);
>>   
>> +			direct_map_size[PUD] += next - addr;
> I think this (and all the lower levels) are likely over-accounting. For example,
> __kpti_install_ng_mappings() and map_entry_trampoline() reuse the infra to
> create separate pgtables. Then you have fixmap, which uses
> create_mapping_noalloc(), efi which uses create_pgd_mapping() and
> update_mapping_prot() used to change permissions for various parts of the kernel
> image. They all reuse the infra too.

Yes, thanks for catching this.

>
>>   			/*
>>   			 * After the PUD entry has been populated once, we
>>   			 * only allow updates to the permission attributes.
>> @@ -532,9 +600,13 @@ static void split_contpte(pte_t *ptep)
>>   {
>>   	int i;
>>   
>> +	direct_map_size[CONT_PTE] -= CONT_PTE_SIZE;
>> +
>>   	ptep = PTR_ALIGN_DOWN(ptep, sizeof(*ptep) * CONT_PTES);
>>   	for (i = 0; i < CONT_PTES; i++, ptep++)
>>   		__set_pte(ptep, pte_mknoncont(__ptep_get(ptep)));
>> +
>> +	direct_map_size[PTE] += CONT_PTE_SIZE;
> Similar issue: we aspire to reuse this split_* infra for regions other than the
> linear map - e.g. vmalloc. So I don't like the idea of baking in an assumption
> that any split is definitely targetting the linear map.

Yeah, this needs to tell whether it is splitting linear map or not.

>
> I guess if you pass the start and end VA to the add/subtract function, it could
> fitler based on whether the region is within the linear map region?

I think it could. It seems ok for kpti, tramp and efi too because their 
virtual addresses are not in the range of linear map IIUC. And it should 
be able to exclude update_mapping_prot() as well because 
update_mapping_prot() is just called on kernel text and data segments 
whose virtual addresses are not in the range of linear map either.

And it seems using start address alone is good enough? I don't think 
kernel install page table crossing virtual address space areas. So the 
add/sub ops should seem like:

static inline void direct_map_meminfo_add(unsigned long start, unsigned 
long size,
                       enum direct_map_type type)
{
     if (is_linear_map_addr(start))
         direct_map_use[type] += size;
}

>
> Overall, I'm personally not a huge fan of adding this capability. I'd need to
> understand the use case to change my mind. But I'm not the maintainer so perhaps
> my opinion isn't all that important ;-)

Understood. I think this is quite helpful IMHO :-) Thanks for the 
valuable inputs.

Thanks,
Yang

>
> Thanks,
> Ryan
>
>>   }
>>   
>>   static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
>> @@ -559,8 +631,13 @@ static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
>>   	if (to_cont)
>>   		prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>>   
>> +	direct_map_size[PMD] -= PMD_SIZE;
>>   	for (i = 0; i < PTRS_PER_PTE; i++, ptep++, pfn++)
>>   		__set_pte(ptep, pfn_pte(pfn, prot));
>> +	if (to_cont)
>> +		direct_map_size[CONT_PTE] += PMD_SIZE;
>> +	else
>> +		direct_map_size[PTE] += PMD_SIZE;
>>   
>>   	/*
>>   	 * Ensure the pte entries are visible to the table walker by the time
>> @@ -576,9 +653,13 @@ static void split_contpmd(pmd_t *pmdp)
>>   {
>>   	int i;
>>   
>> +	direct_map_size[CONT_PMD] -= CONT_PMD_SIZE;
>> +
>>   	pmdp = PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS);
>>   	for (i = 0; i < CONT_PMDS; i++, pmdp++)
>>   		set_pmd(pmdp, pmd_mknoncont(pmdp_get(pmdp)));
>> +
>> +	direct_map_size[PMD] += CONT_PMD_SIZE;
>>   }
>>   
>>   static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
>> @@ -604,8 +685,13 @@ static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
>>   	if (to_cont)
>>   		prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>>   
>> +	direct_map_size[PUD] -= PUD_SIZE;
>>   	for (i = 0; i < PTRS_PER_PMD; i++, pmdp++, pfn += step)
>>   		set_pmd(pmdp, pfn_pmd(pfn, prot));
>> +	if (to_cont)
>> +		direct_map_size[CONT_PMD] += PUD_SIZE;
>> +	else
>> +		direct_map_size[PMD] += PUD_SIZE;
>>   
>>   	/*
>>   	 * Ensure the pmd entries are visible to the table walker by the time



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-12 22:24   ` Yang Shi
@ 2025-11-13 11:28     ` Ryan Roberts
  2025-11-13 18:14       ` Yang Shi
  0 siblings, 1 reply; 14+ messages in thread
From: Ryan Roberts @ 2025-11-13 11:28 UTC (permalink / raw)
  To: Yang Shi, cl, catalin.marinas, will; +Cc: linux-arm-kernel, linux-kernel

On 12/11/2025 22:24, Yang Shi wrote:
> 
> 
> On 11/12/25 2:16 AM, Ryan Roberts wrote:
>> Hi Yang,
>>
>>
>> On 23/10/2025 22:52, Yang Shi wrote:
>>> Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
>>> rodata=full"), the direct mapping may be split on some machines instead
>>> keeping static since boot. It makes more sense to show the direct mapping
>>> use in /proc/meminfo than before.
>>> This patch will make /proc/meminfo show the direct mapping use like the
>>> below (4K base page size):
>>> DirectMap4K:       94792 kB
>>> DirectMap64K:      134208 kB
>>> DirectMap2M:     1173504 kB
>>> DirectMap32M:     5636096 kB
>>> DirectMap1G:    529530880 kB
>> I have a long-term aspiration to enable "per-process page size", where each user
>> space process can use a different page size. The first step is to be able to
>> emulate a page size to the process which is larger than the kernel's. For that
>> reason, I really dislike introducing new ABI that exposes the geometry of the
>> kernel page tables to user space. I'd really like to be clear on what use case
>> benefits from this sort of information before we add it.
> 
> Thanks for the information. I'm not sure what "per-process page size" exactly
> is. But isn't it just user space thing? I have hard time to understand how
> exposing kernel page table geometry will have impact on it.

It's a feature I'm working on/thinking about that, if I'm honest, has a fairly
low probability of making it upstream. arm64 supports multiple base page sizes;
4K, 16K, 64K. The idea is to allow different processes to use a different base
page size and then actually use the native page table for that size in TTBR0.
The idea is to have the kernel use 4K internally and most processes would use 4K
to save memory. But performance critical processes could use 64K.

Currently the kernel page size always matches the user page size and there is
certain data passed through procfs where that assumption becomes apparent. First
step is to be able to emulate the process page size to the process. Exposing the
kernel page table geometry makes this harder.

But really this is my problem to solve, so I doubt a real consideration for this
patch.

> 
> The direct map use information is quite useful for tracking direct map
> fragmentation which may have negative impact to performance and help diagnose
> and debug such issues quickly.
> 
>>
>> nit: arm64 tends to use the term "linear map" not "direct map". I'm not sure why
>> or what the history is. Given this is arch-specific should we be aligning on the
>> architecture's terminology here? I don't know...
> 
> I actually didn't notice that. They are basically interchangeable. Just try to
> keep the consistency with other architectures, for example, x86. The users may
> have arm64 and x86 machines deployed at the same time and they should prefer as
> few churn as possible for maintaining multiple architectures.

Yeah fair enough.

> 
>>
>>> Although just the machines which support BBML2_NOABORT can split the
>>> direct mapping, show it on all machines regardless of BBML2_NOABORT so
>>> that the users have consistent view in order to avoid confusion.
>>>
>>> Although ptdump also can tell the direct map use, but it needs to dump
>>> the whole kernel page table. It is costly and overkilling. It is also
>>> in debugfs which may not be enabled by all distros. So showing direct
>>> map use in /proc/meminfo seems more convenient and has less overhead.
>>>
>>> Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
>>> ---
>>>   arch/arm64/mm/mmu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++
>>>   1 file changed, 86 insertions(+)
>>>
>>> v2: * Counted in size instead of the number of entries per Ryan
>>>      * Removed shift array per Ryan
>>>      * Use lower case "k" per Ryan
>>>      * Fixed a couple of build warnings reported by kernel test robot
>>>      * Fixed a couple of poential miscounts
>>>
>>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>>> index b8d37eb037fc..7207b55d0046 100644
>>> --- a/arch/arm64/mm/mmu.c
>>> +++ b/arch/arm64/mm/mmu.c
>>> @@ -29,6 +29,7 @@
>>>   #include <linux/mm_inline.h>
>>>   #include <linux/pagewalk.h>
>>>   #include <linux/stop_machine.h>
>>> +#include <linux/proc_fs.h>
>>>     #include <asm/barrier.h>
>>>   #include <asm/cputype.h>
>>> @@ -51,6 +52,17 @@
>>>     DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
>>>   +enum direct_map_type {
>>> +    PTE,
>>> +    CONT_PTE,
>>> +    PMD,
>>> +    CONT_PMD,
>>> +    PUD,
>>> +    NR_DIRECT_MAP_TYPE,
>>> +};
>>> +
>>> +static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];
>> I wonder if you should wrap all the adds and subtracts into a helper function,
>> which can then be defined as a nop when !CONFIG_PROC_FS. It means we only need
>> direct_map_size[] when PROC_FS is enabled too.
>>
>> e.g.
>>
>> #ifdef CONFIG_PROC_FS
>> static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];
>>
>> static inline void direct_map_meminfo_add(unsigned long size,
>>                       enum direct_map_type type)
>> {
>>     direct_map_size[type] += size;
>> }
>>
>> static inline void direct_map_meminfo_sub(unsigned long size,
>>                       enum direct_map_type type)
>> {
>>     direct_map_size[type] -= size;
>> }
>> #else
>> static inline void direct_map_meminfo_add(unsigned long size,
>>                       enum direct_map_type type) {}
>> static inline void direct_map_meminfo_sub(unsigned long size,
>>                       enum direct_map_type type) {}
>> #endif
>>
>> Then use it like this:
>> direct_map_meminfo_sub(next - addr, PMD);
>> direct_map_meminfo_add(next - addr, to_cont ? CONT_PTE : PTE);
> 
> Thanks for the suggestion. It seems good and it also should be able to make
> solve the over-accounting problem mentioned below easier.
> 
>>
>>> +
>>>   u64 kimage_voffset __ro_after_init;
>>>   EXPORT_SYMBOL(kimage_voffset);
>>>   @@ -171,6 +183,45 @@ static void init_clear_pgtable(void *table)
>>>       dsb(ishst);
>>>   }
>>>   +#ifdef CONFIG_PROC_FS
>>> +void arch_report_meminfo(struct seq_file *m)
>>> +{
>>> +    char *size[NR_DIRECT_MAP_TYPE];
>>> +
>>> +#if defined(CONFIG_ARM64_4K_PAGES)
>>> +    size[PTE] = "4k";
>>> +    size[CONT_PTE] = "64k";
>>> +    size[PMD] = "2M";
>>> +    size[CONT_PMD] = "32M";
>>> +    size[PUD] = "1G";
>>> +#elif defined(CONFIG_ARM64_16K_PAGES)
>>> +    size[PTE] = "16k";
>>> +    size[CONT_PTE] = "2M";
>>> +    size[PMD] = "32M";
>>> +    size[CONT_PMD] = "1G";
>>> +#elif defined(CONFIG_ARM64_64K_PAGES)
>>> +    size[PTE] = "64k";
>>> +    size[CONT_PTE] = "2M";
>>> +    size[PMD] = "512M";
>>> +    size[CONT_PMD] = "16G";
>>> +#endif
>>> +
>>> +    seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>> +            size[PTE], direct_map_size[PTE] >> 10);
>>> +    seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>> +            size[CONT_PTE],
>>> +            direct_map_size[CONT_PTE] >> 10);
>>> +    seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>> +            size[PMD], direct_map_size[PMD] >> 10);
>>> +    seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>> +            size[CONT_PMD],
>>> +            direct_map_size[CONT_PMD] >> 10);
>>> +    if (pud_sect_supported())
>>> +        seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>> +            size[PUD], direct_map_size[PUD] >> 10);
>>> +}
>>> +#endif
>>> +
>>>   static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
>>>                phys_addr_t phys, pgprot_t prot)
>>>   {
>>> @@ -234,6 +285,11 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned
>>> long addr,
>>>             init_pte(ptep, addr, next, phys, __prot);
>>>   +        if (pgprot_val(__prot) & PTE_CONT)
>>> +            direct_map_size[CONT_PTE] += next - addr;
>>> +        else
>>> +            direct_map_size[PTE] += next - addr;
>>> +
>>>           ptep += pte_index(next) - pte_index(addr);
>>>           phys += next - addr;
>>>       } while (addr = next, addr != end);
>>> @@ -262,6 +318,17 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr,
>>> unsigned long end,
>>>               (flags & NO_BLOCK_MAPPINGS) == 0) {
>>>               pmd_set_huge(pmdp, phys, prot);
>>>   +            /*
>>> +             * It is possible to have mappings allow cont mapping
>>> +             * but disallow block mapping. For example,
>>> +             * map_entry_trampoline().
>>> +             * So we have to increase CONT_PMD and PMD size here
>>> +             * to avoid double counting.
>>> +             */
>>> +            if (pgprot_val(prot) & PTE_CONT)
>>> +                direct_map_size[CONT_PMD] += next - addr;
>>> +            else
>>> +                direct_map_size[PMD] += next - addr;
>>>               /*
>>>                * After the PMD entry has been populated once, we
>>>                * only allow updates to the permission attributes.
>>> @@ -368,6 +435,7 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long
>>> addr, unsigned long end,
>>>               (flags & NO_BLOCK_MAPPINGS) == 0) {
>>>               pud_set_huge(pudp, phys, prot);
>>>   +            direct_map_size[PUD] += next - addr;
>> I think this (and all the lower levels) are likely over-accounting. For example,
>> __kpti_install_ng_mappings() and map_entry_trampoline() reuse the infra to
>> create separate pgtables. Then you have fixmap, which uses
>> create_mapping_noalloc(), efi which uses create_pgd_mapping() and
>> update_mapping_prot() used to change permissions for various parts of the kernel
>> image. They all reuse the infra too.
> 
> Yes, thanks for catching this.
> 
>>
>>>               /*
>>>                * After the PUD entry has been populated once, we
>>>                * only allow updates to the permission attributes.
>>> @@ -532,9 +600,13 @@ static void split_contpte(pte_t *ptep)
>>>   {
>>>       int i;
>>>   +    direct_map_size[CONT_PTE] -= CONT_PTE_SIZE;
>>> +
>>>       ptep = PTR_ALIGN_DOWN(ptep, sizeof(*ptep) * CONT_PTES);
>>>       for (i = 0; i < CONT_PTES; i++, ptep++)
>>>           __set_pte(ptep, pte_mknoncont(__ptep_get(ptep)));
>>> +
>>> +    direct_map_size[PTE] += CONT_PTE_SIZE;
>> Similar issue: we aspire to reuse this split_* infra for regions other than the
>> linear map - e.g. vmalloc. So I don't like the idea of baking in an assumption
>> that any split is definitely targetting the linear map.
> 
> Yeah, this needs to tell whether it is splitting linear map or not.
> 
>>
>> I guess if you pass the start and end VA to the add/subtract function, it could
>> fitler based on whether the region is within the linear map region?
> 
> I think it could. It seems ok for kpti, tramp and efi too because their virtual
> addresses are not in the range of linear map IIUC. And it should be able to
> exclude update_mapping_prot() as well because update_mapping_prot() is just
> called on kernel text and data segments whose virtual addresses are not in the
> range of linear map either.

I'm not sure if there are cases where we will walk a range of the linear map
multiple times? I guess not. Probably worth double checking and documenting.

> 
> And it seems using start address alone is good enough? I don't think kernel
> install page table crossing virtual address space areas. 

Agreed. I suggested passing start/end instead of start/size because you have
start/end at the callsites. Then you can calculate size in the function instead
of having to do it at every callsite. But looking again, the split_ functions
don't even have start. I think go with start/end vs start/size based on which
will look neater more of the time...

> So the add/sub ops
> should seem like:
> 
> static inline void direct_map_meminfo_add(unsigned long start, unsigned long size,
>                       enum direct_map_type type)
> {
>     if (is_linear_map_addr(start))
>         direct_map_use[type] += size;
> }
> 
>>
>> Overall, I'm personally not a huge fan of adding this capability. I'd need to
>> understand the use case to change my mind. But I'm not the maintainer so perhaps
>> my opinion isn't all that important ;-)
> 
> Understood. I think this is quite helpful IMHO :-) Thanks for the valuable inputs.
> 
> Thanks,
> Yang
> 
>>
>> Thanks,
>> Ryan
>>
>>>   }
>>>     static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
>>> @@ -559,8 +631,13 @@ static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp,
>>> bool to_cont)
>>>       if (to_cont)
>>>           prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>>>   +    direct_map_size[PMD] -= PMD_SIZE;
>>>       for (i = 0; i < PTRS_PER_PTE; i++, ptep++, pfn++)
>>>           __set_pte(ptep, pfn_pte(pfn, prot));
>>> +    if (to_cont)
>>> +        direct_map_size[CONT_PTE] += PMD_SIZE;
>>> +    else
>>> +        direct_map_size[PTE] += PMD_SIZE;
>>>         /*
>>>        * Ensure the pte entries are visible to the table walker by the time
>>> @@ -576,9 +653,13 @@ static void split_contpmd(pmd_t *pmdp)
>>>   {
>>>       int i;
>>>   +    direct_map_size[CONT_PMD] -= CONT_PMD_SIZE;
>>> +
>>>       pmdp = PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS);
>>>       for (i = 0; i < CONT_PMDS; i++, pmdp++)
>>>           set_pmd(pmdp, pmd_mknoncont(pmdp_get(pmdp)));
>>> +
>>> +    direct_map_size[PMD] += CONT_PMD_SIZE;
>>>   }
>>>     static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
>>> @@ -604,8 +685,13 @@ static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp,
>>> bool to_cont)
>>>       if (to_cont)
>>>           prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>>>   +    direct_map_size[PUD] -= PUD_SIZE;
>>>       for (i = 0; i < PTRS_PER_PMD; i++, pmdp++, pfn += step)
>>>           set_pmd(pmdp, pfn_pmd(pfn, prot));
>>> +    if (to_cont)
>>> +        direct_map_size[CONT_PMD] += PUD_SIZE;
>>> +    else
>>> +        direct_map_size[PMD] += PUD_SIZE;
>>>         /*
>>>        * Ensure the pmd entries are visible to the table walker by the time
> 



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-12 17:11   ` Christoph Lameter (Ampere)
@ 2025-11-13 11:29     ` Ryan Roberts
  0 siblings, 0 replies; 14+ messages in thread
From: Ryan Roberts @ 2025-11-13 11:29 UTC (permalink / raw)
  To: Christoph Lameter (Ampere)
  Cc: Yang Shi, catalin.marinas, will, linux-arm-kernel, linux-kernel

On 12/11/2025 17:11, Christoph Lameter (Ampere) wrote:
> On Wed, 12 Nov 2025, Ryan Roberts wrote:
> 
>> I have a long-term aspiration to enable "per-process page size", where each user
>> space process can use a different page size. The first step is to be able to
>> emulate a page size to the process which is larger than the kernel's. For that
>> reason, I really dislike introducing new ABI that exposes the geometry of the
>> kernel page tables to user space. I'd really like to be clear on what use case
>> benefits from this sort of information before we add it.
> 
> One is user space where you want to "emulate" other page sizes and the
> other is kernel space.
> 
> The per process page size is likely going to end up
> being a per VMA page size since these address spaces can be shared and the
> VMA is already containing information about huge pages, memory policies
> and other stuff relatd to memory layout. And yes it would be great to have
> an accounting of the page sizes used in a VMA.

See my response to Yang. I suspect my issue shouldn't really be a consideration
for this patch.

> 
> 
>> nit: arm64 tends to use the term "linear map" not "direct map". I'm not sure why
>> or what the history is. Given this is arch-specific should we be aligning on the
>> architecture's terminology here? I don't know...
> 
> Other architectures are already exposing this data via the terminology
> used here. The information is useful for seeing if there is an issue with
> small pages that could be impacting kernel performance. It is surprising
> coming from oter architectures that this information is not readily
> available.
> 



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-13 11:28     ` Ryan Roberts
@ 2025-11-13 18:14       ` Yang Shi
  2025-11-20  8:38         ` Ryan Roberts
  0 siblings, 1 reply; 14+ messages in thread
From: Yang Shi @ 2025-11-13 18:14 UTC (permalink / raw)
  To: Ryan Roberts, cl, catalin.marinas, will; +Cc: linux-arm-kernel, linux-kernel



On 11/13/25 3:28 AM, Ryan Roberts wrote:
> On 12/11/2025 22:24, Yang Shi wrote:
>>
>> On 11/12/25 2:16 AM, Ryan Roberts wrote:
>>> Hi Yang,
>>>
>>>
>>> On 23/10/2025 22:52, Yang Shi wrote:
>>>> Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
>>>> rodata=full"), the direct mapping may be split on some machines instead
>>>> keeping static since boot. It makes more sense to show the direct mapping
>>>> use in /proc/meminfo than before.
>>>> This patch will make /proc/meminfo show the direct mapping use like the
>>>> below (4K base page size):
>>>> DirectMap4K:       94792 kB
>>>> DirectMap64K:      134208 kB
>>>> DirectMap2M:     1173504 kB
>>>> DirectMap32M:     5636096 kB
>>>> DirectMap1G:    529530880 kB
>>> I have a long-term aspiration to enable "per-process page size", where each user
>>> space process can use a different page size. The first step is to be able to
>>> emulate a page size to the process which is larger than the kernel's. For that
>>> reason, I really dislike introducing new ABI that exposes the geometry of the
>>> kernel page tables to user space. I'd really like to be clear on what use case
>>> benefits from this sort of information before we add it.
>> Thanks for the information. I'm not sure what "per-process page size" exactly
>> is. But isn't it just user space thing? I have hard time to understand how
>> exposing kernel page table geometry will have impact on it.
> It's a feature I'm working on/thinking about that, if I'm honest, has a fairly
> low probability of making it upstream. arm64 supports multiple base page sizes;
> 4K, 16K, 64K. The idea is to allow different processes to use a different base
> page size and then actually use the native page table for that size in TTBR0.
> The idea is to have the kernel use 4K internally and most processes would use 4K
> to save memory. But performance critical processes could use 64K.

Aha, I see. I thought you were talking about mTHP. IIUC, userspace may 
have 4K, 16K or 64K base page size, but kernel still uses 4K base page 
size? Can arm64 support have different base page sizes for userspace and 
kernel? It seems surprising to me if it does. If it doesn't, it sounds 
you need at least 3 kernel page tables for 4K, 16K and 64K respectively, 
right?

I'm wondering what kind usecase really needs this. Isn't mTHP good 
enough for the most usecases? We can have auto mTHP size support on per 
VMA basis. If I remember correctly, this has been raised a couple of 
times when we discussed about mTHP. Anyway this may be a little bit off 
the topic.

>
> Currently the kernel page size always matches the user page size and there is
> certain data passed through procfs where that assumption becomes apparent. First
> step is to be able to emulate the process page size to the process. Exposing the
> kernel page table geometry makes this harder.
>
> But really this is my problem to solve, so I doubt a real consideration for this
> patch.

Thank you.

>
>> The direct map use information is quite useful for tracking direct map
>> fragmentation which may have negative impact to performance and help diagnose
>> and debug such issues quickly.
>>
>>> nit: arm64 tends to use the term "linear map" not "direct map". I'm not sure why
>>> or what the history is. Given this is arch-specific should we be aligning on the
>>> architecture's terminology here? I don't know...
>> I actually didn't notice that. They are basically interchangeable. Just try to
>> keep the consistency with other architectures, for example, x86. The users may
>> have arm64 and x86 machines deployed at the same time and they should prefer as
>> few churn as possible for maintaining multiple architectures.
> Yeah fair enough.
>
>>>> Although just the machines which support BBML2_NOABORT can split the
>>>> direct mapping, show it on all machines regardless of BBML2_NOABORT so
>>>> that the users have consistent view in order to avoid confusion.
>>>>
>>>> Although ptdump also can tell the direct map use, but it needs to dump
>>>> the whole kernel page table. It is costly and overkilling. It is also
>>>> in debugfs which may not be enabled by all distros. So showing direct
>>>> map use in /proc/meminfo seems more convenient and has less overhead.
>>>>
>>>> Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
>>>> ---
>>>>    arch/arm64/mm/mmu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++
>>>>    1 file changed, 86 insertions(+)
>>>>
>>>> v2: * Counted in size instead of the number of entries per Ryan
>>>>       * Removed shift array per Ryan
>>>>       * Use lower case "k" per Ryan
>>>>       * Fixed a couple of build warnings reported by kernel test robot
>>>>       * Fixed a couple of poential miscounts
>>>>
>>>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>>>> index b8d37eb037fc..7207b55d0046 100644
>>>> --- a/arch/arm64/mm/mmu.c
>>>> +++ b/arch/arm64/mm/mmu.c
>>>> @@ -29,6 +29,7 @@
>>>>    #include <linux/mm_inline.h>
>>>>    #include <linux/pagewalk.h>
>>>>    #include <linux/stop_machine.h>
>>>> +#include <linux/proc_fs.h>
>>>>      #include <asm/barrier.h>
>>>>    #include <asm/cputype.h>
>>>> @@ -51,6 +52,17 @@
>>>>      DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
>>>>    +enum direct_map_type {
>>>> +    PTE,
>>>> +    CONT_PTE,
>>>> +    PMD,
>>>> +    CONT_PMD,
>>>> +    PUD,
>>>> +    NR_DIRECT_MAP_TYPE,
>>>> +};
>>>> +
>>>> +static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];
>>> I wonder if you should wrap all the adds and subtracts into a helper function,
>>> which can then be defined as a nop when !CONFIG_PROC_FS. It means we only need
>>> direct_map_size[] when PROC_FS is enabled too.
>>>
>>> e.g.
>>>
>>> #ifdef CONFIG_PROC_FS
>>> static unsigned long direct_map_size[NR_DIRECT_MAP_TYPE];
>>>
>>> static inline void direct_map_meminfo_add(unsigned long size,
>>>                        enum direct_map_type type)
>>> {
>>>      direct_map_size[type] += size;
>>> }
>>>
>>> static inline void direct_map_meminfo_sub(unsigned long size,
>>>                        enum direct_map_type type)
>>> {
>>>      direct_map_size[type] -= size;
>>> }
>>> #else
>>> static inline void direct_map_meminfo_add(unsigned long size,
>>>                        enum direct_map_type type) {}
>>> static inline void direct_map_meminfo_sub(unsigned long size,
>>>                        enum direct_map_type type) {}
>>> #endif
>>>
>>> Then use it like this:
>>> direct_map_meminfo_sub(next - addr, PMD);
>>> direct_map_meminfo_add(next - addr, to_cont ? CONT_PTE : PTE);
>> Thanks for the suggestion. It seems good and it also should be able to make
>> solve the over-accounting problem mentioned below easier.
>>
>>>> +
>>>>    u64 kimage_voffset __ro_after_init;
>>>>    EXPORT_SYMBOL(kimage_voffset);
>>>>    @@ -171,6 +183,45 @@ static void init_clear_pgtable(void *table)
>>>>        dsb(ishst);
>>>>    }
>>>>    +#ifdef CONFIG_PROC_FS
>>>> +void arch_report_meminfo(struct seq_file *m)
>>>> +{
>>>> +    char *size[NR_DIRECT_MAP_TYPE];
>>>> +
>>>> +#if defined(CONFIG_ARM64_4K_PAGES)
>>>> +    size[PTE] = "4k";
>>>> +    size[CONT_PTE] = "64k";
>>>> +    size[PMD] = "2M";
>>>> +    size[CONT_PMD] = "32M";
>>>> +    size[PUD] = "1G";
>>>> +#elif defined(CONFIG_ARM64_16K_PAGES)
>>>> +    size[PTE] = "16k";
>>>> +    size[CONT_PTE] = "2M";
>>>> +    size[PMD] = "32M";
>>>> +    size[CONT_PMD] = "1G";
>>>> +#elif defined(CONFIG_ARM64_64K_PAGES)
>>>> +    size[PTE] = "64k";
>>>> +    size[CONT_PTE] = "2M";
>>>> +    size[PMD] = "512M";
>>>> +    size[CONT_PMD] = "16G";
>>>> +#endif
>>>> +
>>>> +    seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>>> +            size[PTE], direct_map_size[PTE] >> 10);
>>>> +    seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>>> +            size[CONT_PTE],
>>>> +            direct_map_size[CONT_PTE] >> 10);
>>>> +    seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>>> +            size[PMD], direct_map_size[PMD] >> 10);
>>>> +    seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>>> +            size[CONT_PMD],
>>>> +            direct_map_size[CONT_PMD] >> 10);
>>>> +    if (pud_sect_supported())
>>>> +        seq_printf(m, "DirectMap%s:    %8lu kB\n",
>>>> +            size[PUD], direct_map_size[PUD] >> 10);
>>>> +}
>>>> +#endif
>>>> +
>>>>    static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
>>>>                 phys_addr_t phys, pgprot_t prot)
>>>>    {
>>>> @@ -234,6 +285,11 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned
>>>> long addr,
>>>>              init_pte(ptep, addr, next, phys, __prot);
>>>>    +        if (pgprot_val(__prot) & PTE_CONT)
>>>> +            direct_map_size[CONT_PTE] += next - addr;
>>>> +        else
>>>> +            direct_map_size[PTE] += next - addr;
>>>> +
>>>>            ptep += pte_index(next) - pte_index(addr);
>>>>            phys += next - addr;
>>>>        } while (addr = next, addr != end);
>>>> @@ -262,6 +318,17 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr,
>>>> unsigned long end,
>>>>                (flags & NO_BLOCK_MAPPINGS) == 0) {
>>>>                pmd_set_huge(pmdp, phys, prot);
>>>>    +            /*
>>>> +             * It is possible to have mappings allow cont mapping
>>>> +             * but disallow block mapping. For example,
>>>> +             * map_entry_trampoline().
>>>> +             * So we have to increase CONT_PMD and PMD size here
>>>> +             * to avoid double counting.
>>>> +             */
>>>> +            if (pgprot_val(prot) & PTE_CONT)
>>>> +                direct_map_size[CONT_PMD] += next - addr;
>>>> +            else
>>>> +                direct_map_size[PMD] += next - addr;
>>>>                /*
>>>>                 * After the PMD entry has been populated once, we
>>>>                 * only allow updates to the permission attributes.
>>>> @@ -368,6 +435,7 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long
>>>> addr, unsigned long end,
>>>>                (flags & NO_BLOCK_MAPPINGS) == 0) {
>>>>                pud_set_huge(pudp, phys, prot);
>>>>    +            direct_map_size[PUD] += next - addr;
>>> I think this (and all the lower levels) are likely over-accounting. For example,
>>> __kpti_install_ng_mappings() and map_entry_trampoline() reuse the infra to
>>> create separate pgtables. Then you have fixmap, which uses
>>> create_mapping_noalloc(), efi which uses create_pgd_mapping() and
>>> update_mapping_prot() used to change permissions for various parts of the kernel
>>> image. They all reuse the infra too.
>> Yes, thanks for catching this.
>>
>>>>                /*
>>>>                 * After the PUD entry has been populated once, we
>>>>                 * only allow updates to the permission attributes.
>>>> @@ -532,9 +600,13 @@ static void split_contpte(pte_t *ptep)
>>>>    {
>>>>        int i;
>>>>    +    direct_map_size[CONT_PTE] -= CONT_PTE_SIZE;
>>>> +
>>>>        ptep = PTR_ALIGN_DOWN(ptep, sizeof(*ptep) * CONT_PTES);
>>>>        for (i = 0; i < CONT_PTES; i++, ptep++)
>>>>            __set_pte(ptep, pte_mknoncont(__ptep_get(ptep)));
>>>> +
>>>> +    direct_map_size[PTE] += CONT_PTE_SIZE;
>>> Similar issue: we aspire to reuse this split_* infra for regions other than the
>>> linear map - e.g. vmalloc. So I don't like the idea of baking in an assumption
>>> that any split is definitely targetting the linear map.
>> Yeah, this needs to tell whether it is splitting linear map or not.
>>
>>> I guess if you pass the start and end VA to the add/subtract function, it could
>>> fitler based on whether the region is within the linear map region?
>> I think it could. It seems ok for kpti, tramp and efi too because their virtual
>> addresses are not in the range of linear map IIUC. And it should be able to
>> exclude update_mapping_prot() as well because update_mapping_prot() is just
>> called on kernel text and data segments whose virtual addresses are not in the
>> range of linear map either.
> I'm not sure if there are cases where we will walk a range of the linear map
> multiple times? I guess not. Probably worth double checking and documenting.

AFAICT, I'm not aware of it either.

>
>> And it seems using start address alone is good enough? I don't think kernel
>> install page table crossing virtual address space areas.
> Agreed. I suggested passing start/end instead of start/size because you have
> start/end at the callsites. Then you can calculate size in the function instead
> of having to do it at every callsite. But looking again, the split_ functions
> don't even have start. I think go with start/end vs start/size based on which
> will look neater more of the time...

Yes, split_ functions just pass in pudp/pmdp/ptep. But we can make them 
pass in "addr" (either start or end). For start/end, I don't think it is 
going to work well for split because we just pass in either start or 
end, never both for split, right? And we know the size for split because 
we know what level we are splitting.  But if we pass in start/end, 
"end-start" may be not the size we need to deduct. We need check what 
type it is, then deduct the proper size. So passing size should make 
split much easier. We just deduct the size for the level directly.

Thanks,
Yang

>
>> So the add/sub ops
>> should seem like:
>>
>> static inline void direct_map_meminfo_add(unsigned long start, unsigned long size,
>>                        enum direct_map_type type)
>> {
>>      if (is_linear_map_addr(start))
>>          direct_map_use[type] += size;
>> }
>>
>>> Overall, I'm personally not a huge fan of adding this capability. I'd need to
>>> understand the use case to change my mind. But I'm not the maintainer so perhaps
>>> my opinion isn't all that important ;-)
>> Understood. I think this is quite helpful IMHO :-) Thanks for the valuable inputs.
>>
>> Thanks,
>> Yang
>>
>>> Thanks,
>>> Ryan
>>>
>>>>    }
>>>>      static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
>>>> @@ -559,8 +631,13 @@ static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp,
>>>> bool to_cont)
>>>>        if (to_cont)
>>>>            prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>>>>    +    direct_map_size[PMD] -= PMD_SIZE;
>>>>        for (i = 0; i < PTRS_PER_PTE; i++, ptep++, pfn++)
>>>>            __set_pte(ptep, pfn_pte(pfn, prot));
>>>> +    if (to_cont)
>>>> +        direct_map_size[CONT_PTE] += PMD_SIZE;
>>>> +    else
>>>> +        direct_map_size[PTE] += PMD_SIZE;
>>>>          /*
>>>>         * Ensure the pte entries are visible to the table walker by the time
>>>> @@ -576,9 +653,13 @@ static void split_contpmd(pmd_t *pmdp)
>>>>    {
>>>>        int i;
>>>>    +    direct_map_size[CONT_PMD] -= CONT_PMD_SIZE;
>>>> +
>>>>        pmdp = PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS);
>>>>        for (i = 0; i < CONT_PMDS; i++, pmdp++)
>>>>            set_pmd(pmdp, pmd_mknoncont(pmdp_get(pmdp)));
>>>> +
>>>> +    direct_map_size[PMD] += CONT_PMD_SIZE;
>>>>    }
>>>>      static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
>>>> @@ -604,8 +685,13 @@ static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp,
>>>> bool to_cont)
>>>>        if (to_cont)
>>>>            prot = __pgprot(pgprot_val(prot) | PTE_CONT);
>>>>    +    direct_map_size[PUD] -= PUD_SIZE;
>>>>        for (i = 0; i < PTRS_PER_PMD; i++, pmdp++, pfn += step)
>>>>            set_pmd(pmdp, pfn_pmd(pfn, prot));
>>>> +    if (to_cont)
>>>> +        direct_map_size[CONT_PMD] += PUD_SIZE;
>>>> +    else
>>>> +        direct_map_size[PMD] += PUD_SIZE;
>>>>          /*
>>>>         * Ensure the pmd entries are visible to the table walker by the time



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-10-23 21:52 [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo Yang Shi
  2025-11-10 23:06 ` Yang Shi
  2025-11-12 10:16 ` Ryan Roberts
@ 2025-11-19 19:12 ` Catalin Marinas
  2025-11-19 19:37   ` Yang Shi
  2 siblings, 1 reply; 14+ messages in thread
From: Catalin Marinas @ 2025-11-19 19:12 UTC (permalink / raw)
  To: ryan.roberts, cl, will, Yang Shi
  Cc: Catalin Marinas, linux-arm-kernel, linux-kernel

From: Catalin Marinas <catalin.marinas@arm.com>

On Thu, 23 Oct 2025 14:52:10 -0700, Yang Shi wrote:
> Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
> rodata=full"), the direct mapping may be split on some machines instead
> keeping static since boot. It makes more sense to show the direct mapping
> use in /proc/meminfo than before.
> This patch will make /proc/meminfo show the direct mapping use like the
> below (4K base page size):
> DirectMap4K:	   94792 kB
> DirectMap64K:	  134208 kB
> DirectMap2M:	 1173504 kB
> DirectMap32M:	 5636096 kB
> DirectMap1G:	529530880 kB
> 
> [...]

It matches Documentation/filesystems/proc.rst, so I'm fine to align
arm64 with it.

Applied to arm64 (for-next/misc), thanks!

[1/1] arm64: mm: show direct mapping use in /proc/meminfo
      https://git.kernel.org/arm64/c/1102778cb023

-- 
Catalin



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-19 19:12 ` Catalin Marinas
@ 2025-11-19 19:37   ` Yang Shi
  2025-11-19 21:10     ` Catalin Marinas
  0 siblings, 1 reply; 14+ messages in thread
From: Yang Shi @ 2025-11-19 19:37 UTC (permalink / raw)
  To: Catalin Marinas, ryan.roberts, cl, will
  Cc: Catalin Marinas, linux-arm-kernel, linux-kernel



On 11/19/25 11:12 AM, Catalin Marinas wrote:
> From: Catalin Marinas <catalin.marinas@arm.com>
>
> On Thu, 23 Oct 2025 14:52:10 -0700, Yang Shi wrote:
>> Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
>> rodata=full"), the direct mapping may be split on some machines instead
>> keeping static since boot. It makes more sense to show the direct mapping
>> use in /proc/meminfo than before.
>> This patch will make /proc/meminfo show the direct mapping use like the
>> below (4K base page size):
>> DirectMap4K:	   94792 kB
>> DirectMap64K:	  134208 kB
>> DirectMap2M:	 1173504 kB
>> DirectMap32M:	 5636096 kB
>> DirectMap1G:	529530880 kB
>>
>> [...]
> It matches Documentation/filesystems/proc.rst, so I'm fine to align
> arm64 with it.

Yes, it does.

>
> Applied to arm64 (for-next/misc), thanks!
>
> [1/1] arm64: mm: show direct mapping use in /proc/meminfo
>        https://git.kernel.org/arm64/c/1102778cb023

Thanks for taking the patch. However, Ryan noticed some over-accounting 
problems and had some suggestions to code cleanup as well. I'm going to 
submit v3 to the mailing list soon.

Yang
>



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-19 19:37   ` Yang Shi
@ 2025-11-19 21:10     ` Catalin Marinas
  2025-11-19 22:26       ` Yang Shi
  0 siblings, 1 reply; 14+ messages in thread
From: Catalin Marinas @ 2025-11-19 21:10 UTC (permalink / raw)
  To: Yang Shi; +Cc: ryan.roberts, cl, will, linux-arm-kernel, linux-kernel

On Wed, Nov 19, 2025 at 11:37:13AM -0800, Yang Shi wrote:
> On 11/19/25 11:12 AM, Catalin Marinas wrote:
> > From: Catalin Marinas <catalin.marinas@arm.com>
> > 
> > On Thu, 23 Oct 2025 14:52:10 -0700, Yang Shi wrote:
> > > Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
> > > rodata=full"), the direct mapping may be split on some machines instead
> > > keeping static since boot. It makes more sense to show the direct mapping
> > > use in /proc/meminfo than before.
> > > This patch will make /proc/meminfo show the direct mapping use like the
> > > below (4K base page size):
> > > DirectMap4K:	   94792 kB
> > > DirectMap64K:	  134208 kB
> > > DirectMap2M:	 1173504 kB
> > > DirectMap32M:	 5636096 kB
> > > DirectMap1G:	529530880 kB
> > > 
> > > [...]
> > It matches Documentation/filesystems/proc.rst, so I'm fine to align
> > arm64 with it.
> 
> Yes, it does.
> 
> > 
> > Applied to arm64 (for-next/misc), thanks!
> > 
> > [1/1] arm64: mm: show direct mapping use in /proc/meminfo
> >        https://git.kernel.org/arm64/c/1102778cb023
> 
> Thanks for taking the patch. However, Ryan noticed some over-accounting
> problems and had some suggestions to code cleanup as well. I'm going to
> submit v3 to the mailing list soon.

Ah, yes, I forgot about that. If the change is small, can you submit a
fix instead? I tend not to rebase the for-next/* branches. Otherwise I
can revert and apply a new one.

Thanks.

-- 
Catalin


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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-19 21:10     ` Catalin Marinas
@ 2025-11-19 22:26       ` Yang Shi
  0 siblings, 0 replies; 14+ messages in thread
From: Yang Shi @ 2025-11-19 22:26 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: ryan.roberts, cl, will, linux-arm-kernel, linux-kernel



On 11/19/25 1:10 PM, Catalin Marinas wrote:
> On Wed, Nov 19, 2025 at 11:37:13AM -0800, Yang Shi wrote:
>> On 11/19/25 11:12 AM, Catalin Marinas wrote:
>>> From: Catalin Marinas <catalin.marinas@arm.com>
>>>
>>> On Thu, 23 Oct 2025 14:52:10 -0700, Yang Shi wrote:
>>>> Since commit a166563e7ec3 ("arm64: mm: support large block mapping when
>>>> rodata=full"), the direct mapping may be split on some machines instead
>>>> keeping static since boot. It makes more sense to show the direct mapping
>>>> use in /proc/meminfo than before.
>>>> This patch will make /proc/meminfo show the direct mapping use like the
>>>> below (4K base page size):
>>>> DirectMap4K:	   94792 kB
>>>> DirectMap64K:	  134208 kB
>>>> DirectMap2M:	 1173504 kB
>>>> DirectMap32M:	 5636096 kB
>>>> DirectMap1G:	529530880 kB
>>>>
>>>> [...]
>>> It matches Documentation/filesystems/proc.rst, so I'm fine to align
>>> arm64 with it.
>> Yes, it does.
>>
>>> Applied to arm64 (for-next/misc), thanks!
>>>
>>> [1/1] arm64: mm: show direct mapping use in /proc/meminfo
>>>         https://git.kernel.org/arm64/c/1102778cb023
>> Thanks for taking the patch. However, Ryan noticed some over-accounting
>> problems and had some suggestions to code cleanup as well. I'm going to
>> submit v3 to the mailing list soon.
> Ah, yes, I forgot about that. If the change is small, can you submit a
> fix instead? I tend not to rebase the for-next/* branches. Otherwise I
> can revert and apply a new one.

The fix is basically simple. It just added a check about whether the 
address is linear map address or not, and the add and sub operations are 
encapsulated in helpers, however we need pass in the "addr" in split 
functions. Anyway I'm going to send the fix.

Thanks,
Yang

>
> Thanks.
>



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-13 18:14       ` Yang Shi
@ 2025-11-20  8:38         ` Ryan Roberts
  2025-11-20 17:33           ` Yang Shi
  0 siblings, 1 reply; 14+ messages in thread
From: Ryan Roberts @ 2025-11-20  8:38 UTC (permalink / raw)
  To: Yang Shi, cl, catalin.marinas, will; +Cc: linux-arm-kernel, linux-kernel


>>>> I have a long-term aspiration to enable "per-process page size", where each
>>>> user
>>>> space process can use a different page size. The first step is to be able to
>>>> emulate a page size to the process which is larger than the kernel's. For that
>>>> reason, I really dislike introducing new ABI that exposes the geometry of the
>>>> kernel page tables to user space. I'd really like to be clear on what use case
>>>> benefits from this sort of information before we add it.
>>> Thanks for the information. I'm not sure what "per-process page size" exactly
>>> is. But isn't it just user space thing? I have hard time to understand how
>>> exposing kernel page table geometry will have impact on it.
>> It's a feature I'm working on/thinking about that, if I'm honest, has a fairly
>> low probability of making it upstream. arm64 supports multiple base page sizes;
>> 4K, 16K, 64K. The idea is to allow different processes to use a different base
>> page size and then actually use the native page table for that size in TTBR0.
>> The idea is to have the kernel use 4K internally and most processes would use 4K
>> to save memory. But performance critical processes could use 64K.
> 
> Aha, I see. I thought you were talking about mTHP. IIUC, userspace may have 4K,
> 16K or 64K base page size, but kernel still uses 4K base page size? Can arm64
> support have different base page sizes for userspace and kernel? It seems
> surprising to me if it does. 

Yes arm64 supports exactly this; User page tables are mapped via TTBR0 and
kernel page tables are mapped via TTBR1. They are both independent structures
and base page size can be set independently.

> If it doesn't, it sounds you need at least 3 kernel
> page tables for 4K, 16K and 64K respectively, right?

No; for my design, the kernel always uses a 4K page table. Only user space page
tables have different sizes.

> 
> I'm wondering what kind usecase really needs this. Isn't mTHP good enough for
> the most usecases? We can have auto mTHP size support on per VMA basis. If I
> remember correctly, this has been raised a couple of times when we discussed
> about mTHP. Anyway this may be a little bit off the topic.

There is still a performance gap between 4K+CONT vs 64K. There are basically 4
aspects that affect HW performance as the base page size gets bigger:

 - TLB reach (how much memory a single TLB entry can describe)
 - Walk cache reach (how much memory a single walk cache entry can describe)
 - number of levels of look up (how many loads are required for full table walk)
 - data cache efficiency (how efficiently the mappings are described in memory)

4K+CONT (i.e. 64K-sized mTHP) only solves the first item.

But as I said, I think there is a high risk of this not actually going anywhere...

Thanks,
Ryan



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

* Re: [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo
  2025-11-20  8:38         ` Ryan Roberts
@ 2025-11-20 17:33           ` Yang Shi
  0 siblings, 0 replies; 14+ messages in thread
From: Yang Shi @ 2025-11-20 17:33 UTC (permalink / raw)
  To: Ryan Roberts, cl, catalin.marinas, will; +Cc: linux-arm-kernel, linux-kernel



On 11/20/25 12:38 AM, Ryan Roberts wrote:
>>>>> I have a long-term aspiration to enable "per-process page size", where each
>>>>> user
>>>>> space process can use a different page size. The first step is to be able to
>>>>> emulate a page size to the process which is larger than the kernel's. For that
>>>>> reason, I really dislike introducing new ABI that exposes the geometry of the
>>>>> kernel page tables to user space. I'd really like to be clear on what use case
>>>>> benefits from this sort of information before we add it.
>>>> Thanks for the information. I'm not sure what "per-process page size" exactly
>>>> is. But isn't it just user space thing? I have hard time to understand how
>>>> exposing kernel page table geometry will have impact on it.
>>> It's a feature I'm working on/thinking about that, if I'm honest, has a fairly
>>> low probability of making it upstream. arm64 supports multiple base page sizes;
>>> 4K, 16K, 64K. The idea is to allow different processes to use a different base
>>> page size and then actually use the native page table for that size in TTBR0.
>>> The idea is to have the kernel use 4K internally and most processes would use 4K
>>> to save memory. But performance critical processes could use 64K.
>> Aha, I see. I thought you were talking about mTHP. IIUC, userspace may have 4K,
>> 16K or 64K base page size, but kernel still uses 4K base page size? Can arm64
>> support have different base page sizes for userspace and kernel? It seems
>> surprising to me if it does.
> Yes arm64 supports exactly this; User page tables are mapped via TTBR0 and
> kernel page tables are mapped via TTBR1. They are both independent structures
> and base page size can be set independently.
>
>> If it doesn't, it sounds you need at least 3 kernel
>> page tables for 4K, 16K and 64K respectively, right?
> No; for my design, the kernel always uses a 4K page table. Only user space page
> tables have different sizes.

I see. IIUC if so you need to let the page fault handler know you need 
16K or 64K so that kernel can allocate 16K or 64K instead of 4K. And you 
also need to let kernel install one single PTE instead of multiple PTEs.

>
>> I'm wondering what kind usecase really needs this. Isn't mTHP good enough for
>> the most usecases? We can have auto mTHP size support on per VMA basis. If I
>> remember correctly, this has been raised a couple of times when we discussed
>> about mTHP. Anyway this may be a little bit off the topic.
> There is still a performance gap between 4K+CONT vs 64K. There are basically 4
> aspects that affect HW performance as the base page size gets bigger:
>
>   - TLB reach (how much memory a single TLB entry can describe)
>   - Walk cache reach (how much memory a single walk cache entry can describe)
>   - number of levels of look up (how many loads are required for full table walk)
>   - data cache efficiency (how efficiently the mappings are described in memory)
>
> 4K+CONT (i.e. 64K-sized mTHP) only solves the first item.

Oh yeah, this is what our benchmarks showed. The cost of page table walk 
is not improved due to the number of levels of look up. So some 
benchmarks don't get too much benefit.

Thanks,
Yang
>
> But as I said, I think there is a high risk of this not actually going anywhere...
>
> Thanks,
> Ryan
>



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

end of thread, other threads:[~2025-11-20 17:33 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-23 21:52 [v2 PATCH] arm64: mm: show direct mapping use in /proc/meminfo Yang Shi
2025-11-10 23:06 ` Yang Shi
2025-11-12 10:16 ` Ryan Roberts
2025-11-12 17:11   ` Christoph Lameter (Ampere)
2025-11-13 11:29     ` Ryan Roberts
2025-11-12 22:24   ` Yang Shi
2025-11-13 11:28     ` Ryan Roberts
2025-11-13 18:14       ` Yang Shi
2025-11-20  8:38         ` Ryan Roberts
2025-11-20 17:33           ` Yang Shi
2025-11-19 19:12 ` Catalin Marinas
2025-11-19 19:37   ` Yang Shi
2025-11-19 21:10     ` Catalin Marinas
2025-11-19 22:26       ` Yang Shi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).