diff options
Diffstat (limited to 'arch/s390/mm/pgtable.c')
-rw-r--r-- | arch/s390/mm/pgtable.c | 43 |
1 files changed, 31 insertions, 12 deletions
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index de8cbc30dcd1..0a2e5e086749 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c | |||
@@ -48,12 +48,23 @@ void crst_table_free(struct mm_struct *mm, unsigned long *table) | |||
48 | } | 48 | } |
49 | 49 | ||
50 | #ifdef CONFIG_64BIT | 50 | #ifdef CONFIG_64BIT |
51 | static void __crst_table_upgrade(void *arg) | ||
52 | { | ||
53 | struct mm_struct *mm = arg; | ||
54 | |||
55 | if (current->active_mm == mm) | ||
56 | update_mm(mm, current); | ||
57 | __tlb_flush_local(); | ||
58 | } | ||
59 | |||
51 | int crst_table_upgrade(struct mm_struct *mm, unsigned long limit) | 60 | int crst_table_upgrade(struct mm_struct *mm, unsigned long limit) |
52 | { | 61 | { |
53 | unsigned long *table, *pgd; | 62 | unsigned long *table, *pgd; |
54 | unsigned long entry; | 63 | unsigned long entry; |
64 | int flush; | ||
55 | 65 | ||
56 | BUG_ON(limit > (1UL << 53)); | 66 | BUG_ON(limit > (1UL << 53)); |
67 | flush = 0; | ||
57 | repeat: | 68 | repeat: |
58 | table = crst_table_alloc(mm); | 69 | table = crst_table_alloc(mm); |
59 | if (!table) | 70 | if (!table) |
@@ -79,12 +90,15 @@ repeat: | |||
79 | mm->pgd = (pgd_t *) table; | 90 | mm->pgd = (pgd_t *) table; |
80 | mm->task_size = mm->context.asce_limit; | 91 | mm->task_size = mm->context.asce_limit; |
81 | table = NULL; | 92 | table = NULL; |
93 | flush = 1; | ||
82 | } | 94 | } |
83 | spin_unlock_bh(&mm->page_table_lock); | 95 | spin_unlock_bh(&mm->page_table_lock); |
84 | if (table) | 96 | if (table) |
85 | crst_table_free(mm, table); | 97 | crst_table_free(mm, table); |
86 | if (mm->context.asce_limit < limit) | 98 | if (mm->context.asce_limit < limit) |
87 | goto repeat; | 99 | goto repeat; |
100 | if (flush) | ||
101 | on_each_cpu(__crst_table_upgrade, mm, 0); | ||
88 | return 0; | 102 | return 0; |
89 | } | 103 | } |
90 | 104 | ||
@@ -92,6 +106,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit) | |||
92 | { | 106 | { |
93 | pgd_t *pgd; | 107 | pgd_t *pgd; |
94 | 108 | ||
109 | if (current->active_mm == mm) | ||
110 | __tlb_flush_mm(mm); | ||
95 | while (mm->context.asce_limit > limit) { | 111 | while (mm->context.asce_limit > limit) { |
96 | pgd = mm->pgd; | 112 | pgd = mm->pgd; |
97 | switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) { | 113 | switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) { |
@@ -114,6 +130,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit) | |||
114 | mm->task_size = mm->context.asce_limit; | 130 | mm->task_size = mm->context.asce_limit; |
115 | crst_table_free(mm, (unsigned long *) pgd); | 131 | crst_table_free(mm, (unsigned long *) pgd); |
116 | } | 132 | } |
133 | if (current->active_mm == mm) | ||
134 | update_mm(mm, current); | ||
117 | } | 135 | } |
118 | #endif | 136 | #endif |
119 | 137 | ||
@@ -1087,10 +1105,9 @@ again: | |||
1087 | continue; | 1105 | continue; |
1088 | /* Allocate new page table with pgstes */ | 1106 | /* Allocate new page table with pgstes */ |
1089 | new = page_table_alloc_pgste(mm, addr); | 1107 | new = page_table_alloc_pgste(mm, addr); |
1090 | if (!new) { | 1108 | if (!new) |
1091 | mm->context.has_pgste = 0; | 1109 | return -ENOMEM; |
1092 | continue; | 1110 | |
1093 | } | ||
1094 | spin_lock(&mm->page_table_lock); | 1111 | spin_lock(&mm->page_table_lock); |
1095 | if (likely((unsigned long *) pmd_deref(*pmd) == table)) { | 1112 | if (likely((unsigned long *) pmd_deref(*pmd) == table)) { |
1096 | /* Nuke pmd entry pointing to the "short" page table */ | 1113 | /* Nuke pmd entry pointing to the "short" page table */ |
@@ -1128,13 +1145,15 @@ static unsigned long page_table_realloc_pud(struct mmu_gather *tlb, | |||
1128 | if (pud_none_or_clear_bad(pud)) | 1145 | if (pud_none_or_clear_bad(pud)) |
1129 | continue; | 1146 | continue; |
1130 | next = page_table_realloc_pmd(tlb, mm, pud, addr, next); | 1147 | next = page_table_realloc_pmd(tlb, mm, pud, addr, next); |
1148 | if (unlikely(IS_ERR_VALUE(next))) | ||
1149 | return next; | ||
1131 | } while (pud++, addr = next, addr != end); | 1150 | } while (pud++, addr = next, addr != end); |
1132 | 1151 | ||
1133 | return addr; | 1152 | return addr; |
1134 | } | 1153 | } |
1135 | 1154 | ||
1136 | static void page_table_realloc(struct mmu_gather *tlb, struct mm_struct *mm, | 1155 | static unsigned long page_table_realloc(struct mmu_gather *tlb, struct mm_struct *mm, |
1137 | unsigned long addr, unsigned long end) | 1156 | unsigned long addr, unsigned long end) |
1138 | { | 1157 | { |
1139 | unsigned long next; | 1158 | unsigned long next; |
1140 | pgd_t *pgd; | 1159 | pgd_t *pgd; |
@@ -1145,7 +1164,11 @@ static void page_table_realloc(struct mmu_gather *tlb, struct mm_struct *mm, | |||
1145 | if (pgd_none_or_clear_bad(pgd)) | 1164 | if (pgd_none_or_clear_bad(pgd)) |
1146 | continue; | 1165 | continue; |
1147 | next = page_table_realloc_pud(tlb, mm, pgd, addr, next); | 1166 | next = page_table_realloc_pud(tlb, mm, pgd, addr, next); |
1167 | if (unlikely(IS_ERR_VALUE(next))) | ||
1168 | return next; | ||
1148 | } while (pgd++, addr = next, addr != end); | 1169 | } while (pgd++, addr = next, addr != end); |
1170 | |||
1171 | return 0; | ||
1149 | } | 1172 | } |
1150 | 1173 | ||
1151 | /* | 1174 | /* |
@@ -1157,10 +1180,6 @@ int s390_enable_sie(void) | |||
1157 | struct mm_struct *mm = tsk->mm; | 1180 | struct mm_struct *mm = tsk->mm; |
1158 | struct mmu_gather tlb; | 1181 | struct mmu_gather tlb; |
1159 | 1182 | ||
1160 | /* Do we have switched amode? If no, we cannot do sie */ | ||
1161 | if (s390_user_mode == HOME_SPACE_MODE) | ||
1162 | return -EINVAL; | ||
1163 | |||
1164 | /* Do we have pgstes? if yes, we are done */ | 1183 | /* Do we have pgstes? if yes, we are done */ |
1165 | if (mm_has_pgste(tsk->mm)) | 1184 | if (mm_has_pgste(tsk->mm)) |
1166 | return 0; | 1185 | return 0; |
@@ -1169,9 +1188,9 @@ int s390_enable_sie(void) | |||
1169 | /* split thp mappings and disable thp for future mappings */ | 1188 | /* split thp mappings and disable thp for future mappings */ |
1170 | thp_split_mm(mm); | 1189 | thp_split_mm(mm); |
1171 | /* Reallocate the page tables with pgstes */ | 1190 | /* Reallocate the page tables with pgstes */ |
1172 | mm->context.has_pgste = 1; | ||
1173 | tlb_gather_mmu(&tlb, mm, 0, TASK_SIZE); | 1191 | tlb_gather_mmu(&tlb, mm, 0, TASK_SIZE); |
1174 | page_table_realloc(&tlb, mm, 0, TASK_SIZE); | 1192 | if (!page_table_realloc(&tlb, mm, 0, TASK_SIZE)) |
1193 | mm->context.has_pgste = 1; | ||
1175 | tlb_finish_mmu(&tlb, 0, TASK_SIZE); | 1194 | tlb_finish_mmu(&tlb, 0, TASK_SIZE); |
1176 | up_write(&mm->mmap_sem); | 1195 | up_write(&mm->mmap_sem); |
1177 | return mm->context.has_pgste ? 0 : -ENOMEM; | 1196 | return mm->context.has_pgste ? 0 : -ENOMEM; |