diff mbox series

riscv: mm: execute local TLB flush after populating vmemmap

Message ID 20230320065324.1045276-1-vincent.chen@sifive.com (mailing list archive)
State Superseded
Headers show
Series riscv: mm: execute local TLB flush after populating vmemmap | expand

Checks

Context Check Description
conchuod/cover_letter success Single patches do not need cover letters
conchuod/tree_selection success Guessed tree name to be for-next
conchuod/fixes_present success Fixes tag not required for -next series
conchuod/maintainers_pattern success MAINTAINERS pattern errors before the patch: 1 and now 1
conchuod/verify_signedoff success Signed-off-by tag matches author and committer
conchuod/kdoc success Errors and warnings before: 0 this patch: 0
conchuod/build_rv64_clang_allmodconfig success Errors and warnings before: 2275 this patch: 2275
conchuod/module_param success Was 0 now: 0
conchuod/build_rv64_gcc_allmodconfig success Errors and warnings before: 17624 this patch: 17624
conchuod/build_rv32_defconfig success Build OK
conchuod/dtb_warn_rv64 success Errors and warnings before: 3 this patch: 3
conchuod/header_inline success No static functions without inline keyword in header files
conchuod/checkpatch success total: 0 errors, 0 warnings, 0 checks, 20 lines checked
conchuod/source_inline success Was 0 now: 0
conchuod/build_rv64_nommu_k210_defconfig success Build OK
conchuod/verify_fixes success No Fixes tag
conchuod/build_rv64_nommu_virt_defconfig success Build OK

Commit Message

Vincent Chen March 20, 2023, 6:53 a.m. UTC
The vmemmap_populate() creates VA to PA mapping for the VMEMMAP area, where
all "strcut page" are located once CONFIG_SPARSEMEM_VMEMMAP is defined.
These "struct page" are later initialized in the zone_sizes_init()
function. However, during this process, no sfence.vma instruction is
executed for this VMEMMAP area. This omission may cause the core to fail to
perform page table work because some data related to the address
translation is invisible to the core. To solve this issue, here let
zone_sizes_init() call local_flush_tlb_kernel_range() to execute a
sfence.vma instruction for the VMEMMAP area, ensuring that all data related
to the address translation is visible to the core.

Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
---
 arch/riscv/include/asm/tlbflush.h | 7 +++++++
 arch/riscv/mm/init.c              | 3 +++
 2 files changed, 10 insertions(+)

Comments

Alexandre Ghiti March 21, 2023, 4:11 p.m. UTC | #1
Hi Vincent,

On 3/20/23 07:53, Vincent Chen wrote:
> The vmemmap_populate() creates VA to PA mapping for the VMEMMAP area, where
> all "strcut page" are located once CONFIG_SPARSEMEM_VMEMMAP is defined.
> These "struct page" are later initialized in the zone_sizes_init()
> function. However, during this process, no sfence.vma instruction is
> executed for this VMEMMAP area. This omission may cause the core to fail to
> perform page table work because some data related to the address
> translation is invisible to the core. To solve this issue, here let
> zone_sizes_init() call local_flush_tlb_kernel_range() to execute a
> sfence.vma instruction for the VMEMMAP area, ensuring that all data related
> to the address translation is visible to the core.
>
> Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
> ---
>   arch/riscv/include/asm/tlbflush.h | 7 +++++++
>   arch/riscv/mm/init.c              | 3 +++
>   2 files changed, 10 insertions(+)
>
> diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h
> index 801019381dea..b7696974e3c6 100644
> --- a/arch/riscv/include/asm/tlbflush.h
> +++ b/arch/riscv/include/asm/tlbflush.h
> @@ -59,4 +59,11 @@ static inline void flush_tlb_kernel_range(unsigned long start,
>   	flush_tlb_all();
>   }
>   
> +/* Flush a range of kernel pages without broadcasting */
> +static inline void local_flush_tlb_kernel_range(unsigned long start,
> +						unsigned long end)
> +{
> +	local_flush_tlb_all();
> +}
> +
>   #endif /* _ASM_RISCV_TLBFLUSH_H */
> diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
> index 2c4a64e97aec..62eea656e341 100644
> --- a/arch/riscv/mm/init.c
> +++ b/arch/riscv/mm/init.c
> @@ -72,6 +72,9 @@ static void __init zone_sizes_init(void)
>   #endif
>   	max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
>   
> +#ifdef CONFIG_SPARSEMEM_VMEMMAP
> +	local_flush_tlb_kernel_range(VMEMMAP_START, VMEMMAP_END);
> +#endif
>   	free_area_init(max_zone_pfns);
>   }
>   

Just one nit: I would have added the flush to the vmemmap_populate 
function instead, so that we don't flush uselessly when !SPARSE_VMEMMAP 
and we know exactly why we flush.

Otherwise, you can add:

Reviewed-by: Alexandre Ghiti <alexghiti@rivosinc.com>

Thanks,

Alex
Alexandre Ghiti April 13, 2023, 2:33 p.m. UTC | #2
Hi Vincent,

On 4/13/23 02:48, Vincent Chen wrote:
>
>
> On Wed, Mar 22, 2023 at 12:11 AM Alexandre Ghiti <alex@ghiti.fr> wrote:
>
>     Hi Vincent,
>
>     On 3/20/23 07:53, Vincent Chen wrote:
>     > The vmemmap_populate() creates VA to PA mapping for the VMEMMAP
>     area, where
>     > all "strcut page" are located once CONFIG_SPARSEMEM_VMEMMAP is
>     defined.
>     > These "struct page" are later initialized in the zone_sizes_init()
>     > function. However, during this process, no sfence.vma instruction is
>     > executed for this VMEMMAP area. This omission may cause the core
>     to fail to
>     > perform page table work because some data related to the address
>     > translation is invisible to the core. To solve this issue, here let
>     > zone_sizes_init() call local_flush_tlb_kernel_range() to execute a
>     > sfence.vma instruction for the VMEMMAP area, ensuring that all
>     data related
>     > to the address translation is visible to the core.
>     >
>     > Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
>     > ---
>     >   arch/riscv/include/asm/tlbflush.h | 7 +++++++
>     >   arch/riscv/mm/init.c              | 3 +++
>     >   2 files changed, 10 insertions(+)
>     >
>     > diff --git a/arch/riscv/include/asm/tlbflush.h
>     b/arch/riscv/include/asm/tlbflush.h
>     > index 801019381dea..b7696974e3c6 100644
>     > --- a/arch/riscv/include/asm/tlbflush.h
>     > +++ b/arch/riscv/include/asm/tlbflush.h
>     > @@ -59,4 +59,11 @@ static inline void
>     flush_tlb_kernel_range(unsigned long start,
>     >       flush_tlb_all();
>     >   }
>     >
>     > +/* Flush a range of kernel pages without broadcasting */
>     > +static inline void local_flush_tlb_kernel_range(unsigned long
>     start,
>     > +                                             unsigned long end)
>     > +{
>     > +     local_flush_tlb_all();
>     > +}
>     > +
>     >   #endif /* _ASM_RISCV_TLBFLUSH_H */
>     > diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
>     > index 2c4a64e97aec..62eea656e341 100644
>     > --- a/arch/riscv/mm/init.c
>     > +++ b/arch/riscv/mm/init.c
>     > @@ -72,6 +72,9 @@ static void __init zone_sizes_init(void)
>     >   #endif
>     >       max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
>     >
>     > +#ifdef CONFIG_SPARSEMEM_VMEMMAP
>     > +     local_flush_tlb_kernel_range(VMEMMAP_START, VMEMMAP_END);
>     > +#endif
>     >       free_area_init(max_zone_pfns);
>     >   }
>     >
>
>     Just one nit: I would have added the flush to the vmemmap_populate
>     function instead, so that we don't flush uselessly when
>     !SPARSE_VMEMMAP
>     and we know exactly why we flush.
>
>
> Sorry for the late reply.
>
> To be honest, initially, I placed the TLB flush in the 
> vmemmap_populate function like you suggested. However, I later 
> discovered that this function could be called multiple times during 
> the booting process, causing the TLB flush to be executed multiple 
> times. To improve performance, I decided to move the TLB flush to the 
> zone_sizes_init function instead, as this is where the kernel first 
> accesses the VMEMMAP region after vmemmap_populate. This allows the 
> kernel to perform only one TLB flush for the region, but the tradeoff 
> is that the code readability is reduced. Do you think this tradeoff is 
> worth it? or Maybe I can add some comments in the vmemmap_populate 
> function to describe the purpose of this TLB flush. Is it OK with you?


I see, I did not know but it is indeed called many times. To me 
zone_sizes_init() is not the right place, what about adding this flush 
(with a comment) right after sparse_init() in misc_mem_init()? So that 
if someday we move zone_sizes_init(), we don't lose the flush!

Thanks,

Alex


>
>
>     Otherwise, you can add:
>
>     Reviewed-by: Alexandre Ghiti <alexghiti@rivosinc.com>
>
>     Thanks,
>
>     Alex
>
diff mbox series

Patch

diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h
index 801019381dea..b7696974e3c6 100644
--- a/arch/riscv/include/asm/tlbflush.h
+++ b/arch/riscv/include/asm/tlbflush.h
@@ -59,4 +59,11 @@  static inline void flush_tlb_kernel_range(unsigned long start,
 	flush_tlb_all();
 }
 
+/* Flush a range of kernel pages without broadcasting */
+static inline void local_flush_tlb_kernel_range(unsigned long start,
+						unsigned long end)
+{
+	local_flush_tlb_all();
+}
+
 #endif /* _ASM_RISCV_TLBFLUSH_H */
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index 2c4a64e97aec..62eea656e341 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -72,6 +72,9 @@  static void __init zone_sizes_init(void)
 #endif
 	max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
 
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+	local_flush_tlb_kernel_range(VMEMMAP_START, VMEMMAP_END);
+#endif
 	free_area_init(max_zone_pfns);
 }