xref: /kernel/linux/linux-6.6/arch/riscv/mm/tlbflush.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/mm.h>
462306a36Sopenharmony_ci#include <linux/smp.h>
562306a36Sopenharmony_ci#include <linux/sched.h>
662306a36Sopenharmony_ci#include <asm/sbi.h>
762306a36Sopenharmony_ci#include <asm/mmu_context.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistatic inline void local_flush_tlb_all_asid(unsigned long asid)
1062306a36Sopenharmony_ci{
1162306a36Sopenharmony_ci	if (asid != FLUSH_TLB_NO_ASID)
1262306a36Sopenharmony_ci		__asm__ __volatile__ ("sfence.vma x0, %0"
1362306a36Sopenharmony_ci				:
1462306a36Sopenharmony_ci				: "r" (asid)
1562306a36Sopenharmony_ci				: "memory");
1662306a36Sopenharmony_ci	else
1762306a36Sopenharmony_ci		local_flush_tlb_all();
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic inline void local_flush_tlb_page_asid(unsigned long addr,
2162306a36Sopenharmony_ci		unsigned long asid)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	if (asid != FLUSH_TLB_NO_ASID)
2462306a36Sopenharmony_ci		__asm__ __volatile__ ("sfence.vma %0, %1"
2562306a36Sopenharmony_ci				:
2662306a36Sopenharmony_ci				: "r" (addr), "r" (asid)
2762306a36Sopenharmony_ci				: "memory");
2862306a36Sopenharmony_ci	else
2962306a36Sopenharmony_ci		local_flush_tlb_page(addr);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Flush entire TLB if number of entries to be flushed is greater
3462306a36Sopenharmony_ci * than the threshold below.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_cistatic unsigned long tlb_flush_all_threshold __read_mostly = 64;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void local_flush_tlb_range_threshold_asid(unsigned long start,
3962306a36Sopenharmony_ci						 unsigned long size,
4062306a36Sopenharmony_ci						 unsigned long stride,
4162306a36Sopenharmony_ci						 unsigned long asid)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	unsigned long nr_ptes_in_range = DIV_ROUND_UP(size, stride);
4462306a36Sopenharmony_ci	int i;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (nr_ptes_in_range > tlb_flush_all_threshold) {
4762306a36Sopenharmony_ci		local_flush_tlb_all_asid(asid);
4862306a36Sopenharmony_ci		return;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	for (i = 0; i < nr_ptes_in_range; ++i) {
5262306a36Sopenharmony_ci		local_flush_tlb_page_asid(start, asid);
5362306a36Sopenharmony_ci		start += stride;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic inline void local_flush_tlb_range_asid(unsigned long start,
5862306a36Sopenharmony_ci		unsigned long size, unsigned long stride, unsigned long asid)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	if (size <= stride)
6162306a36Sopenharmony_ci		local_flush_tlb_page_asid(start, asid);
6262306a36Sopenharmony_ci	else if (size == FLUSH_TLB_MAX_SIZE)
6362306a36Sopenharmony_ci		local_flush_tlb_all_asid(asid);
6462306a36Sopenharmony_ci	else
6562306a36Sopenharmony_ci		local_flush_tlb_range_threshold_asid(start, size, stride, asid);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* Flush a range of kernel pages without broadcasting */
6962306a36Sopenharmony_civoid local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	local_flush_tlb_range_asid(start, end - start, PAGE_SIZE, FLUSH_TLB_NO_ASID);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void __ipi_flush_tlb_all(void *info)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	local_flush_tlb_all();
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid flush_tlb_all(void)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	if (riscv_use_ipi_for_rfence())
8262306a36Sopenharmony_ci		on_each_cpu(__ipi_flush_tlb_all, NULL, 1);
8362306a36Sopenharmony_ci	else
8462306a36Sopenharmony_ci		sbi_remote_sfence_vma_asid(NULL, 0, FLUSH_TLB_MAX_SIZE, FLUSH_TLB_NO_ASID);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistruct flush_tlb_range_data {
8862306a36Sopenharmony_ci	unsigned long asid;
8962306a36Sopenharmony_ci	unsigned long start;
9062306a36Sopenharmony_ci	unsigned long size;
9162306a36Sopenharmony_ci	unsigned long stride;
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void __ipi_flush_tlb_range_asid(void *info)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct flush_tlb_range_data *d = info;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	local_flush_tlb_range_asid(d->start, d->size, d->stride, d->asid);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void __flush_tlb_range(struct mm_struct *mm, unsigned long start,
10262306a36Sopenharmony_ci			      unsigned long size, unsigned long stride)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct flush_tlb_range_data ftd;
10562306a36Sopenharmony_ci	const struct cpumask *cmask;
10662306a36Sopenharmony_ci	unsigned long asid = FLUSH_TLB_NO_ASID;
10762306a36Sopenharmony_ci	bool broadcast;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (mm) {
11062306a36Sopenharmony_ci		unsigned int cpuid;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		cmask = mm_cpumask(mm);
11362306a36Sopenharmony_ci		if (cpumask_empty(cmask))
11462306a36Sopenharmony_ci			return;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		cpuid = get_cpu();
11762306a36Sopenharmony_ci		/* check if the tlbflush needs to be sent to other CPUs */
11862306a36Sopenharmony_ci		broadcast = cpumask_any_but(cmask, cpuid) < nr_cpu_ids;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		if (static_branch_unlikely(&use_asid_allocator))
12162306a36Sopenharmony_ci			asid = atomic_long_read(&mm->context.id) & asid_mask;
12262306a36Sopenharmony_ci	} else {
12362306a36Sopenharmony_ci		cmask = cpu_online_mask;
12462306a36Sopenharmony_ci		broadcast = true;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (broadcast) {
12862306a36Sopenharmony_ci		if (riscv_use_ipi_for_rfence()) {
12962306a36Sopenharmony_ci			ftd.asid = asid;
13062306a36Sopenharmony_ci			ftd.start = start;
13162306a36Sopenharmony_ci			ftd.size = size;
13262306a36Sopenharmony_ci			ftd.stride = stride;
13362306a36Sopenharmony_ci			on_each_cpu_mask(cmask,
13462306a36Sopenharmony_ci					 __ipi_flush_tlb_range_asid,
13562306a36Sopenharmony_ci					 &ftd, 1);
13662306a36Sopenharmony_ci		} else
13762306a36Sopenharmony_ci			sbi_remote_sfence_vma_asid(cmask,
13862306a36Sopenharmony_ci						   start, size, asid);
13962306a36Sopenharmony_ci	} else {
14062306a36Sopenharmony_ci		local_flush_tlb_range_asid(start, size, stride, asid);
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (mm)
14462306a36Sopenharmony_ci		put_cpu();
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_civoid flush_tlb_mm(struct mm_struct *mm)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	__flush_tlb_range(mm, 0, FLUSH_TLB_MAX_SIZE, PAGE_SIZE);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_civoid flush_tlb_mm_range(struct mm_struct *mm,
15362306a36Sopenharmony_ci			unsigned long start, unsigned long end,
15462306a36Sopenharmony_ci			unsigned int page_size)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	__flush_tlb_range(mm, start, end - start, page_size);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_civoid flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	__flush_tlb_range(vma->vm_mm, addr, PAGE_SIZE, PAGE_SIZE);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_civoid flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
16562306a36Sopenharmony_ci		     unsigned long end)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	__flush_tlb_range(vma->vm_mm, start, end - start, PAGE_SIZE);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_civoid flush_tlb_kernel_range(unsigned long start, unsigned long end)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	__flush_tlb_range(NULL, start, end - start, PAGE_SIZE);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
17662306a36Sopenharmony_civoid flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
17762306a36Sopenharmony_ci			unsigned long end)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	__flush_tlb_range(vma->vm_mm, start, end - start, PMD_SIZE);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci#endif
182