| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | #ifndef _S390_TLB_H | 
|  | 2 | #define _S390_TLB_H | 
|  | 3 |  | 
|  | 4 | /* | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 5 | * TLB flushing on s390 is complicated. The following requirement | 
|  | 6 | * from the principles of operation is the most arduous: | 
|  | 7 | * | 
|  | 8 | * "A valid table entry must not be changed while it is attached | 
|  | 9 | * to any CPU and may be used for translation by that CPU except to | 
|  | 10 | * (1) invalidate the entry by using INVALIDATE PAGE TABLE ENTRY, | 
|  | 11 | * or INVALIDATE DAT TABLE ENTRY, (2) alter bits 56-63 of a page | 
|  | 12 | * table entry, or (3) make a change by means of a COMPARE AND SWAP | 
|  | 13 | * AND PURGE instruction that purges the TLB." | 
|  | 14 | * | 
|  | 15 | * The modification of a pte of an active mm struct therefore is | 
|  | 16 | * a two step process: i) invalidate the pte, ii) store the new pte. | 
|  | 17 | * This is true for the page protection bit as well. | 
|  | 18 | * The only possible optimization is to flush at the beginning of | 
|  | 19 | * a tlb_gather_mmu cycle if the mm_struct is currently not in use. | 
|  | 20 | * | 
|  | 21 | * Pages used for the page tables is a different story. FIXME: more | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 22 | */ | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 23 |  | 
|  | 24 | #include <linux/mm.h> | 
|  | 25 | #include <linux/swap.h> | 
|  | 26 | #include <asm/processor.h> | 
|  | 27 | #include <asm/pgalloc.h> | 
|  | 28 | #include <asm/smp.h> | 
|  | 29 | #include <asm/tlbflush.h> | 
|  | 30 |  | 
|  | 31 | #ifndef CONFIG_SMP | 
|  | 32 | #define TLB_NR_PTRS	1 | 
|  | 33 | #else | 
|  | 34 | #define TLB_NR_PTRS	508 | 
|  | 35 | #endif | 
|  | 36 |  | 
|  | 37 | struct mmu_gather { | 
|  | 38 | struct mm_struct *mm; | 
|  | 39 | unsigned int fullmm; | 
|  | 40 | unsigned int nr_ptes; | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 41 | unsigned int nr_pxds; | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 42 | void *array[TLB_NR_PTRS]; | 
|  | 43 | }; | 
|  | 44 |  | 
|  | 45 | DECLARE_PER_CPU(struct mmu_gather, mmu_gathers); | 
|  | 46 |  | 
|  | 47 | static inline struct mmu_gather *tlb_gather_mmu(struct mm_struct *mm, | 
|  | 48 | unsigned int full_mm_flush) | 
|  | 49 | { | 
|  | 50 | struct mmu_gather *tlb = &get_cpu_var(mmu_gathers); | 
|  | 51 |  | 
|  | 52 | tlb->mm = mm; | 
|  | 53 | tlb->fullmm = full_mm_flush || (num_online_cpus() == 1) || | 
|  | 54 | (atomic_read(&mm->mm_users) <= 1 && mm == current->active_mm); | 
|  | 55 | tlb->nr_ptes = 0; | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 56 | tlb->nr_pxds = TLB_NR_PTRS; | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 57 | if (tlb->fullmm) | 
|  | 58 | __tlb_flush_mm(mm); | 
|  | 59 | return tlb; | 
|  | 60 | } | 
|  | 61 |  | 
|  | 62 | static inline void tlb_flush_mmu(struct mmu_gather *tlb, | 
|  | 63 | unsigned long start, unsigned long end) | 
|  | 64 | { | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 65 | if (!tlb->fullmm && (tlb->nr_ptes > 0 || tlb->nr_pxds < TLB_NR_PTRS)) | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 66 | __tlb_flush_mm(tlb->mm); | 
|  | 67 | while (tlb->nr_ptes > 0) | 
| Benjamin Herrenschmidt | 5e54197 | 2008-02-04 22:29:14 -0800 | [diff] [blame] | 68 | pte_free(tlb->mm, tlb->array[--tlb->nr_ptes]); | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 69 | while (tlb->nr_pxds < TLB_NR_PTRS) | 
|  | 70 | /* pgd_free frees the pointer as region or segment table */ | 
|  | 71 | pgd_free(tlb->mm, tlb->array[tlb->nr_pxds++]); | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 72 | } | 
|  | 73 |  | 
|  | 74 | static inline void tlb_finish_mmu(struct mmu_gather *tlb, | 
|  | 75 | unsigned long start, unsigned long end) | 
|  | 76 | { | 
|  | 77 | tlb_flush_mmu(tlb, start, end); | 
|  | 78 |  | 
|  | 79 | /* keep the page table cache within bounds */ | 
|  | 80 | check_pgt_cache(); | 
|  | 81 |  | 
|  | 82 | put_cpu_var(mmu_gathers); | 
|  | 83 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 |  | 
|  | 85 | /* | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 86 | * Release the page cache reference for a pte removed by | 
|  | 87 | * tlb_ptep_clear_flush. In both flush modes the tlb fo a page cache page | 
|  | 88 | * has already been freed, so just do free_page_and_swap_cache. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 | */ | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 90 | static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) | 
|  | 91 | { | 
|  | 92 | free_page_and_swap_cache(page); | 
|  | 93 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 94 |  | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 95 | /* | 
|  | 96 | * pte_free_tlb frees a pte table and clears the CRSTE for the | 
|  | 97 | * page table from the tlb. | 
|  | 98 | */ | 
| Martin Schwidefsky | 146e4b3 | 2008-02-09 18:24:35 +0100 | [diff] [blame] | 99 | static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte) | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 100 | { | 
|  | 101 | if (!tlb->fullmm) { | 
| Martin Schwidefsky | 146e4b3 | 2008-02-09 18:24:35 +0100 | [diff] [blame] | 102 | tlb->array[tlb->nr_ptes++] = pte; | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 103 | if (tlb->nr_ptes >= tlb->nr_pxds) | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 104 | tlb_flush_mmu(tlb, 0, 0); | 
|  | 105 | } else | 
| Martin Schwidefsky | 146e4b3 | 2008-02-09 18:24:35 +0100 | [diff] [blame] | 106 | pte_free(tlb->mm, pte); | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 107 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 |  | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 109 | /* | 
|  | 110 | * pmd_free_tlb frees a pmd table and clears the CRSTE for the | 
|  | 111 | * segment table entry from the tlb. | 
| Martin Schwidefsky | 6252d70 | 2008-02-09 18:24:37 +0100 | [diff] [blame] | 112 | * If the mm uses a two level page table the single pmd is freed | 
|  | 113 | * as the pgd. pmd_free_tlb checks the asce_limit against 2GB | 
|  | 114 | * to avoid the double free of the pmd in this case. | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 115 | */ | 
|  | 116 | static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd) | 
|  | 117 | { | 
|  | 118 | #ifdef __s390x__ | 
| Martin Schwidefsky | 6252d70 | 2008-02-09 18:24:37 +0100 | [diff] [blame] | 119 | if (tlb->mm->context.asce_limit <= (1UL << 31)) | 
|  | 120 | return; | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 121 | if (!tlb->fullmm) { | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 122 | tlb->array[--tlb->nr_pxds] = pmd; | 
|  | 123 | if (tlb->nr_ptes >= tlb->nr_pxds) | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 124 | tlb_flush_mmu(tlb, 0, 0); | 
|  | 125 | } else | 
| Benjamin Herrenschmidt | 5e54197 | 2008-02-04 22:29:14 -0800 | [diff] [blame] | 126 | pmd_free(tlb->mm, pmd); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 127 | #endif | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 128 | } | 
|  | 129 |  | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 130 | /* | 
|  | 131 | * pud_free_tlb frees a pud table and clears the CRSTE for the | 
|  | 132 | * region third table entry from the tlb. | 
| Martin Schwidefsky | 6252d70 | 2008-02-09 18:24:37 +0100 | [diff] [blame] | 133 | * If the mm uses a three level page table the single pud is freed | 
|  | 134 | * as the pgd. pud_free_tlb checks the asce_limit against 4TB | 
|  | 135 | * to avoid the double free of the pud in this case. | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 136 | */ | 
|  | 137 | static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud) | 
|  | 138 | { | 
|  | 139 | #ifdef __s390x__ | 
| Martin Schwidefsky | 6252d70 | 2008-02-09 18:24:37 +0100 | [diff] [blame] | 140 | if (tlb->mm->context.asce_limit <= (1UL << 42)) | 
|  | 141 | return; | 
| Martin Schwidefsky | 5a216a2 | 2008-02-09 18:24:36 +0100 | [diff] [blame] | 142 | if (!tlb->fullmm) { | 
|  | 143 | tlb->array[--tlb->nr_pxds] = pud; | 
|  | 144 | if (tlb->nr_ptes >= tlb->nr_pxds) | 
|  | 145 | tlb_flush_mmu(tlb, 0, 0); | 
|  | 146 | } else | 
|  | 147 | pud_free(tlb->mm, pud); | 
|  | 148 | #endif | 
|  | 149 | } | 
| Martin Schwidefsky | 190a1d7 | 2007-10-22 12:52:48 +0200 | [diff] [blame] | 150 |  | 
| Martin Schwidefsky | ba8a922 | 2007-10-22 12:52:44 +0200 | [diff] [blame] | 151 | #define tlb_start_vma(tlb, vma)			do { } while (0) | 
|  | 152 | #define tlb_end_vma(tlb, vma)			do { } while (0) | 
|  | 153 | #define tlb_remove_tlb_entry(tlb, ptep, addr)	do { } while (0) | 
|  | 154 | #define tlb_migrate_finish(mm)			do { } while (0) | 
|  | 155 |  | 
|  | 156 | #endif /* _S390_TLB_H */ |