Skip to content

Commit 9ba91d1

Browse files
Merge patch series "riscv: tlb flush improvements"
Alexandre Ghiti <[email protected]> says: This series optimizes the tlb flushes on riscv which used to simply flush the whole tlb whatever the size of the range to flush or the size of the stride. Patch 3 introduces a threshold that is microarchitecture specific and will very likely be modified by vendors, not sure though which mechanism we'll use to do that (dt? alternatives? vendor initialization code?). * b4-shazam-merge: riscv: Improve flush_tlb_kernel_range() riscv: Make __flush_tlb_range() loop over pte instead of flushing the whole tlb riscv: Improve flush_tlb_range() for hugetlb pages riscv: Improve tlb_flush() Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
2 parents dbfbda3 + 5e22bfd commit 9ba91d1

File tree

5 files changed

+144
-95
lines changed

5 files changed

+144
-95
lines changed

arch/riscv/include/asm/sbi.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,6 @@ void sbi_set_timer(uint64_t stime_value);
273273
void sbi_shutdown(void);
274274
void sbi_send_ipi(unsigned int cpu);
275275
int sbi_remote_fence_i(const struct cpumask *cpu_mask);
276-
int sbi_remote_sfence_vma(const struct cpumask *cpu_mask,
277-
unsigned long start,
278-
unsigned long size);
279276

280277
int sbi_remote_sfence_vma_asid(const struct cpumask *cpu_mask,
281278
unsigned long start,

arch/riscv/include/asm/tlb.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ static void tlb_flush(struct mmu_gather *tlb);
1515

1616
static inline void tlb_flush(struct mmu_gather *tlb)
1717
{
18-
flush_tlb_mm(tlb->mm);
18+
#ifdef CONFIG_MMU
19+
if (tlb->fullmm || tlb->need_flush_all)
20+
flush_tlb_mm(tlb->mm);
21+
else
22+
flush_tlb_mm_range(tlb->mm, tlb->start, tlb->end,
23+
tlb_get_unmap_size(tlb));
24+
#endif
1925
}
2026

2127
#endif /* _ASM_RISCV_TLB_H */

arch/riscv/include/asm/tlbflush.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
#include <asm/smp.h>
1212
#include <asm/errata_list.h>
1313

14+
#define FLUSH_TLB_MAX_SIZE ((unsigned long)-1)
15+
#define FLUSH_TLB_NO_ASID ((unsigned long)-1)
16+
1417
#ifdef CONFIG_MMU
1518
extern unsigned long asid_mask;
1619

@@ -32,9 +35,12 @@ static inline void local_flush_tlb_page(unsigned long addr)
3235
#if defined(CONFIG_SMP) && defined(CONFIG_MMU)
3336
void flush_tlb_all(void);
3437
void flush_tlb_mm(struct mm_struct *mm);
38+
void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
39+
unsigned long end, unsigned int page_size);
3540
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr);
3641
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
3742
unsigned long end);
43+
void flush_tlb_kernel_range(unsigned long start, unsigned long end);
3844
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
3945
#define __HAVE_ARCH_FLUSH_PMD_TLB_RANGE
4046
void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
@@ -51,14 +57,15 @@ static inline void flush_tlb_range(struct vm_area_struct *vma,
5157
local_flush_tlb_all();
5258
}
5359

54-
#define flush_tlb_mm(mm) flush_tlb_all()
55-
#endif /* !CONFIG_SMP || !CONFIG_MMU */
56-
5760
/* Flush a range of kernel pages */
5861
static inline void flush_tlb_kernel_range(unsigned long start,
5962
unsigned long end)
6063
{
61-
flush_tlb_all();
64+
local_flush_tlb_all();
6265
}
6366

67+
#define flush_tlb_mm(mm) flush_tlb_all()
68+
#define flush_tlb_mm_range(mm, start, end, page_size) flush_tlb_all()
69+
#endif /* !CONFIG_SMP || !CONFIG_MMU */
70+
6471
#endif /* _ASM_RISCV_TLBFLUSH_H */

arch/riscv/kernel/sbi.c

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/reboot.h>
1212
#include <asm/sbi.h>
1313
#include <asm/smp.h>
14+
#include <asm/tlbflush.h>
1415

1516
/* default SBI version is 0.1 */
1617
unsigned long sbi_spec_version __ro_after_init = SBI_SPEC_VERSION_DEFAULT;
@@ -376,32 +377,15 @@ int sbi_remote_fence_i(const struct cpumask *cpu_mask)
376377
}
377378
EXPORT_SYMBOL(sbi_remote_fence_i);
378379

379-
/**
380-
* sbi_remote_sfence_vma() - Execute SFENCE.VMA instructions on given remote
381-
* harts for the specified virtual address range.
382-
* @cpu_mask: A cpu mask containing all the target harts.
383-
* @start: Start of the virtual address
384-
* @size: Total size of the virtual address range.
385-
*
386-
* Return: 0 on success, appropriate linux error code otherwise.
387-
*/
388-
int sbi_remote_sfence_vma(const struct cpumask *cpu_mask,
389-
unsigned long start,
390-
unsigned long size)
391-
{
392-
return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA,
393-
cpu_mask, start, size, 0, 0);
394-
}
395-
EXPORT_SYMBOL(sbi_remote_sfence_vma);
396-
397380
/**
398381
* sbi_remote_sfence_vma_asid() - Execute SFENCE.VMA instructions on given
399-
* remote harts for a virtual address range belonging to a specific ASID.
382+
* remote harts for a virtual address range belonging to a specific ASID or not.
400383
*
401384
* @cpu_mask: A cpu mask containing all the target harts.
402385
* @start: Start of the virtual address
403386
* @size: Total size of the virtual address range.
404-
* @asid: The value of address space identifier (ASID).
387+
* @asid: The value of address space identifier (ASID), or FLUSH_TLB_NO_ASID
388+
* for flushing all address spaces.
405389
*
406390
* Return: 0 on success, appropriate linux error code otherwise.
407391
*/
@@ -410,8 +394,12 @@ int sbi_remote_sfence_vma_asid(const struct cpumask *cpu_mask,
410394
unsigned long size,
411395
unsigned long asid)
412396
{
413-
return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID,
414-
cpu_mask, start, size, asid, 0);
397+
if (asid == FLUSH_TLB_NO_ASID)
398+
return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA,
399+
cpu_mask, start, size, 0, 0);
400+
else
401+
return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID,
402+
cpu_mask, start, size, asid, 0);
415403
}
416404
EXPORT_SYMBOL(sbi_remote_sfence_vma_asid);
417405

arch/riscv/mm/tlbflush.c

Lines changed: 116 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,67 @@
33
#include <linux/mm.h>
44
#include <linux/smp.h>
55
#include <linux/sched.h>
6+
#include <linux/hugetlb.h>
67
#include <asm/sbi.h>
78
#include <asm/mmu_context.h>
89

910
static inline void local_flush_tlb_all_asid(unsigned long asid)
1011
{
11-
__asm__ __volatile__ ("sfence.vma x0, %0"
12-
:
13-
: "r" (asid)
14-
: "memory");
12+
if (asid != FLUSH_TLB_NO_ASID)
13+
__asm__ __volatile__ ("sfence.vma x0, %0"
14+
:
15+
: "r" (asid)
16+
: "memory");
17+
else
18+
local_flush_tlb_all();
1519
}
1620

1721
static inline void local_flush_tlb_page_asid(unsigned long addr,
1822
unsigned long asid)
1923
{
20-
__asm__ __volatile__ ("sfence.vma %0, %1"
21-
:
22-
: "r" (addr), "r" (asid)
23-
: "memory");
24+
if (asid != FLUSH_TLB_NO_ASID)
25+
__asm__ __volatile__ ("sfence.vma %0, %1"
26+
:
27+
: "r" (addr), "r" (asid)
28+
: "memory");
29+
else
30+
local_flush_tlb_page(addr);
2431
}
2532

26-
static inline void local_flush_tlb_range(unsigned long start,
27-
unsigned long size, unsigned long stride)
33+
/*
34+
* Flush entire TLB if number of entries to be flushed is greater
35+
* than the threshold below.
36+
*/
37+
static unsigned long tlb_flush_all_threshold __read_mostly = 64;
38+
39+
static void local_flush_tlb_range_threshold_asid(unsigned long start,
40+
unsigned long size,
41+
unsigned long stride,
42+
unsigned long asid)
2843
{
29-
if (size <= stride)
30-
local_flush_tlb_page(start);
31-
else
32-
local_flush_tlb_all();
44+
unsigned long nr_ptes_in_range = DIV_ROUND_UP(size, stride);
45+
int i;
46+
47+
if (nr_ptes_in_range > tlb_flush_all_threshold) {
48+
local_flush_tlb_all_asid(asid);
49+
return;
50+
}
51+
52+
for (i = 0; i < nr_ptes_in_range; ++i) {
53+
local_flush_tlb_page_asid(start, asid);
54+
start += stride;
55+
}
3356
}
3457

3558
static inline void local_flush_tlb_range_asid(unsigned long start,
3659
unsigned long size, unsigned long stride, unsigned long asid)
3760
{
3861
if (size <= stride)
3962
local_flush_tlb_page_asid(start, asid);
40-
else
63+
else if (size == FLUSH_TLB_MAX_SIZE)
4164
local_flush_tlb_all_asid(asid);
65+
else
66+
local_flush_tlb_range_threshold_asid(start, size, stride, asid);
4267
}
4368

4469
static void __ipi_flush_tlb_all(void *info)
@@ -51,7 +76,7 @@ void flush_tlb_all(void)
5176
if (riscv_use_ipi_for_rfence())
5277
on_each_cpu(__ipi_flush_tlb_all, NULL, 1);
5378
else
54-
sbi_remote_sfence_vma(NULL, 0, -1);
79+
sbi_remote_sfence_vma_asid(NULL, 0, FLUSH_TLB_MAX_SIZE, FLUSH_TLB_NO_ASID);
5580
}
5681

5782
struct flush_tlb_range_data {
@@ -68,68 +93,62 @@ static void __ipi_flush_tlb_range_asid(void *info)
6893
local_flush_tlb_range_asid(d->start, d->size, d->stride, d->asid);
6994
}
7095

71-
static void __ipi_flush_tlb_range(void *info)
72-
{
73-
struct flush_tlb_range_data *d = info;
74-
75-
local_flush_tlb_range(d->start, d->size, d->stride);
76-
}
77-
7896
static void __flush_tlb_range(struct mm_struct *mm, unsigned long start,
7997
unsigned long size, unsigned long stride)
8098
{
8199
struct flush_tlb_range_data ftd;
82-
struct cpumask *cmask = mm_cpumask(mm);
83-
unsigned int cpuid;
100+
const struct cpumask *cmask;
101+
unsigned long asid = FLUSH_TLB_NO_ASID;
84102
bool broadcast;
85103

86-
if (cpumask_empty(cmask))
87-
return;
104+
if (mm) {
105+
unsigned int cpuid;
88106

89-
cpuid = get_cpu();
90-
/* check if the tlbflush needs to be sent to other CPUs */
91-
broadcast = cpumask_any_but(cmask, cpuid) < nr_cpu_ids;
92-
if (static_branch_unlikely(&use_asid_allocator)) {
93-
unsigned long asid = atomic_long_read(&mm->context.id) & asid_mask;
94-
95-
if (broadcast) {
96-
if (riscv_use_ipi_for_rfence()) {
97-
ftd.asid = asid;
98-
ftd.start = start;
99-
ftd.size = size;
100-
ftd.stride = stride;
101-
on_each_cpu_mask(cmask,
102-
__ipi_flush_tlb_range_asid,
103-
&ftd, 1);
104-
} else
105-
sbi_remote_sfence_vma_asid(cmask,
106-
start, size, asid);
107-
} else {
108-
local_flush_tlb_range_asid(start, size, stride, asid);
109-
}
107+
cmask = mm_cpumask(mm);
108+
if (cpumask_empty(cmask))
109+
return;
110+
111+
cpuid = get_cpu();
112+
/* check if the tlbflush needs to be sent to other CPUs */
113+
broadcast = cpumask_any_but(cmask, cpuid) < nr_cpu_ids;
114+
115+
if (static_branch_unlikely(&use_asid_allocator))
116+
asid = atomic_long_read(&mm->context.id) & asid_mask;
110117
} else {
111-
if (broadcast) {
112-
if (riscv_use_ipi_for_rfence()) {
113-
ftd.asid = 0;
114-
ftd.start = start;
115-
ftd.size = size;
116-
ftd.stride = stride;
117-
on_each_cpu_mask(cmask,
118-
__ipi_flush_tlb_range,
119-
&ftd, 1);
120-
} else
121-
sbi_remote_sfence_vma(cmask, start, size);
122-
} else {
123-
local_flush_tlb_range(start, size, stride);
124-
}
118+
cmask = cpu_online_mask;
119+
broadcast = true;
125120
}
126121

127-
put_cpu();
122+
if (broadcast) {
123+
if (riscv_use_ipi_for_rfence()) {
124+
ftd.asid = asid;
125+
ftd.start = start;
126+
ftd.size = size;
127+
ftd.stride = stride;
128+
on_each_cpu_mask(cmask,
129+
__ipi_flush_tlb_range_asid,
130+
&ftd, 1);
131+
} else
132+
sbi_remote_sfence_vma_asid(cmask,
133+
start, size, asid);
134+
} else {
135+
local_flush_tlb_range_asid(start, size, stride, asid);
136+
}
137+
138+
if (mm)
139+
put_cpu();
128140
}
129141

130142
void flush_tlb_mm(struct mm_struct *mm)
131143
{
132-
__flush_tlb_range(mm, 0, -1, PAGE_SIZE);
144+
__flush_tlb_range(mm, 0, FLUSH_TLB_MAX_SIZE, PAGE_SIZE);
145+
}
146+
147+
void flush_tlb_mm_range(struct mm_struct *mm,
148+
unsigned long start, unsigned long end,
149+
unsigned int page_size)
150+
{
151+
__flush_tlb_range(mm, start, end - start, page_size);
133152
}
134153

135154
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
@@ -140,8 +159,40 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
140159
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
141160
unsigned long end)
142161
{
143-
__flush_tlb_range(vma->vm_mm, start, end - start, PAGE_SIZE);
162+
unsigned long stride_size;
163+
164+
if (!is_vm_hugetlb_page(vma)) {
165+
stride_size = PAGE_SIZE;
166+
} else {
167+
stride_size = huge_page_size(hstate_vma(vma));
168+
169+
/*
170+
* As stated in the privileged specification, every PTE in a
171+
* NAPOT region must be invalidated, so reset the stride in that
172+
* case.
173+
*/
174+
if (has_svnapot()) {
175+
if (stride_size >= PGDIR_SIZE)
176+
stride_size = PGDIR_SIZE;
177+
else if (stride_size >= P4D_SIZE)
178+
stride_size = P4D_SIZE;
179+
else if (stride_size >= PUD_SIZE)
180+
stride_size = PUD_SIZE;
181+
else if (stride_size >= PMD_SIZE)
182+
stride_size = PMD_SIZE;
183+
else
184+
stride_size = PAGE_SIZE;
185+
}
186+
}
187+
188+
__flush_tlb_range(vma->vm_mm, start, end - start, stride_size);
189+
}
190+
191+
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
192+
{
193+
__flush_tlb_range(NULL, start, end - start, PAGE_SIZE);
144194
}
195+
145196
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
146197
void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
147198
unsigned long end)

0 commit comments

Comments
 (0)