.. | .. |
---|
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ |
---|
1 | 2 | /* |
---|
2 | 3 | * Based on arch/arm/include/asm/tlb.h |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2002 Russell King |
---|
5 | 6 | * Copyright (C) 2012 ARM Ltd. |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License version 2 as |
---|
9 | | - * published by the Free Software Foundation. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | | - * |
---|
16 | | - * You should have received a copy of the GNU General Public License |
---|
17 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
18 | 7 | */ |
---|
19 | 8 | #ifndef __ASM_TLB_H |
---|
20 | 9 | #define __ASM_TLB_H |
---|
.. | .. |
---|
22 | 11 | #include <linux/pagemap.h> |
---|
23 | 12 | #include <linux/swap.h> |
---|
24 | 13 | |
---|
25 | | -#ifdef CONFIG_HAVE_RCU_TABLE_FREE |
---|
26 | | - |
---|
27 | | -#define tlb_remove_entry(tlb, entry) tlb_remove_table(tlb, entry) |
---|
28 | 14 | static inline void __tlb_remove_table(void *_table) |
---|
29 | 15 | { |
---|
30 | 16 | free_page_and_swap_cache((struct page *)_table); |
---|
31 | 17 | } |
---|
32 | | -#else |
---|
33 | | -#define tlb_remove_entry(tlb, entry) tlb_remove_page(tlb, entry) |
---|
34 | | -#endif /* CONFIG_HAVE_RCU_TABLE_FREE */ |
---|
35 | 18 | |
---|
| 19 | +#define tlb_flush tlb_flush |
---|
36 | 20 | static void tlb_flush(struct mmu_gather *tlb); |
---|
37 | 21 | |
---|
38 | 22 | #include <asm-generic/tlb.h> |
---|
39 | 23 | |
---|
| 24 | +/* |
---|
| 25 | + * get the tlbi levels in arm64. Default value is 0 if more than one |
---|
| 26 | + * of cleared_* is set or neither is set. |
---|
| 27 | + * Arm64 doesn't support p4ds now. |
---|
| 28 | + */ |
---|
| 29 | +static inline int tlb_get_level(struct mmu_gather *tlb) |
---|
| 30 | +{ |
---|
| 31 | + /* The TTL field is only valid for the leaf entry. */ |
---|
| 32 | + if (tlb->freed_tables) |
---|
| 33 | + return 0; |
---|
| 34 | + |
---|
| 35 | + if (tlb->cleared_ptes && !(tlb->cleared_pmds || |
---|
| 36 | + tlb->cleared_puds || |
---|
| 37 | + tlb->cleared_p4ds)) |
---|
| 38 | + return 3; |
---|
| 39 | + |
---|
| 40 | + if (tlb->cleared_pmds && !(tlb->cleared_ptes || |
---|
| 41 | + tlb->cleared_puds || |
---|
| 42 | + tlb->cleared_p4ds)) |
---|
| 43 | + return 2; |
---|
| 44 | + |
---|
| 45 | + if (tlb->cleared_puds && !(tlb->cleared_ptes || |
---|
| 46 | + tlb->cleared_pmds || |
---|
| 47 | + tlb->cleared_p4ds)) |
---|
| 48 | + return 1; |
---|
| 49 | + |
---|
| 50 | + return 0; |
---|
| 51 | +} |
---|
| 52 | + |
---|
40 | 53 | static inline void tlb_flush(struct mmu_gather *tlb) |
---|
41 | 54 | { |
---|
42 | 55 | struct vm_area_struct vma = TLB_FLUSH_VMA(tlb->mm, 0); |
---|
| 56 | + bool last_level = !tlb->freed_tables; |
---|
| 57 | + unsigned long stride = tlb_get_unmap_size(tlb); |
---|
| 58 | + int tlb_level = tlb_get_level(tlb); |
---|
43 | 59 | |
---|
44 | 60 | /* |
---|
45 | | - * The ASID allocator will either invalidate the ASID or mark |
---|
46 | | - * it as used. |
---|
| 61 | + * If we're tearing down the address space then we only care about |
---|
| 62 | + * invalidating the walk-cache, since the ASID allocator won't |
---|
| 63 | + * reallocate our ASID without invalidating the entire TLB. |
---|
47 | 64 | */ |
---|
48 | | - if (tlb->fullmm) |
---|
| 65 | + if (tlb->fullmm) { |
---|
| 66 | + if (!last_level) |
---|
| 67 | + flush_tlb_mm(tlb->mm); |
---|
49 | 68 | return; |
---|
| 69 | + } |
---|
50 | 70 | |
---|
51 | | - /* |
---|
52 | | - * The intermediate page table levels are already handled by |
---|
53 | | - * the __(pte|pmd|pud)_free_tlb() functions, so last level |
---|
54 | | - * TLBI is sufficient here. |
---|
55 | | - */ |
---|
56 | | - __flush_tlb_range(&vma, tlb->start, tlb->end, true); |
---|
| 71 | + __flush_tlb_range(&vma, tlb->start, tlb->end, stride, |
---|
| 72 | + last_level, tlb_level); |
---|
57 | 73 | } |
---|
58 | 74 | |
---|
59 | 75 | static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, |
---|
60 | 76 | unsigned long addr) |
---|
61 | 77 | { |
---|
62 | | - __flush_tlb_pgtable(tlb->mm, addr); |
---|
63 | | - pgtable_page_dtor(pte); |
---|
64 | | - tlb_remove_entry(tlb, pte); |
---|
| 78 | + pgtable_pte_page_dtor(pte); |
---|
| 79 | + tlb_remove_table(tlb, pte); |
---|
65 | 80 | } |
---|
66 | 81 | |
---|
67 | 82 | #if CONFIG_PGTABLE_LEVELS > 2 |
---|
68 | 83 | static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp, |
---|
69 | 84 | unsigned long addr) |
---|
70 | 85 | { |
---|
71 | | - __flush_tlb_pgtable(tlb->mm, addr); |
---|
72 | | - tlb_remove_entry(tlb, virt_to_page(pmdp)); |
---|
| 86 | + struct page *page = virt_to_page(pmdp); |
---|
| 87 | + |
---|
| 88 | + pgtable_pmd_page_dtor(page); |
---|
| 89 | + tlb_remove_table(tlb, page); |
---|
73 | 90 | } |
---|
74 | 91 | #endif |
---|
75 | 92 | |
---|
.. | .. |
---|
77 | 94 | static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp, |
---|
78 | 95 | unsigned long addr) |
---|
79 | 96 | { |
---|
80 | | - __flush_tlb_pgtable(tlb->mm, addr); |
---|
81 | | - tlb_remove_entry(tlb, virt_to_page(pudp)); |
---|
| 97 | + tlb_remove_table(tlb, virt_to_page(pudp)); |
---|
82 | 98 | } |
---|
83 | 99 | #endif |
---|
84 | 100 | |
---|