diff options
-rw-r--r-- | arch/s390/include/asm/hugetlb.h | 18 | ||||
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 190 | ||||
-rw-r--r-- | arch/s390/include/asm/setup.h | 5 | ||||
-rw-r--r-- | arch/s390/include/asm/tlb.h | 1 | ||||
-rw-r--r-- | arch/s390/kernel/early.c | 2 | ||||
-rw-r--r-- | arch/s390/mm/pgtable.c | 22 |
6 files changed, 220 insertions, 18 deletions
diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h index fc322421b1cc..593753ee07f3 100644 --- a/arch/s390/include/asm/hugetlb.h +++ b/arch/s390/include/asm/hugetlb.h | |||
@@ -78,23 +78,6 @@ static inline void __pmd_csp(pmd_t *pmdp) | |||
78 | " csp %1,%3" | 78 | " csp %1,%3" |
79 | : "=m" (*pmdp) | 79 | : "=m" (*pmdp) |
80 | : "d" (reg2), "d" (reg3), "d" (reg4), "m" (*pmdp) : "cc"); | 80 | : "d" (reg2), "d" (reg3), "d" (reg4), "m" (*pmdp) : "cc"); |
81 | pmd_val(*pmdp) = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY; | ||
82 | } | ||
83 | |||
84 | static inline void __pmd_idte(unsigned long address, pmd_t *pmdp) | ||
85 | { | ||
86 | unsigned long sto = (unsigned long) pmdp - | ||
87 | pmd_index(address) * sizeof(pmd_t); | ||
88 | |||
89 | if (!(pmd_val(*pmdp) & _SEGMENT_ENTRY_INV)) { | ||
90 | asm volatile( | ||
91 | " .insn rrf,0xb98e0000,%2,%3,0,0" | ||
92 | : "=m" (*pmdp) | ||
93 | : "m" (*pmdp), "a" (sto), | ||
94 | "a" ((address & HPAGE_MASK)) | ||
95 | ); | ||
96 | } | ||
97 | pmd_val(*pmdp) = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY; | ||
98 | } | 81 | } |
99 | 82 | ||
100 | static inline void huge_ptep_invalidate(struct mm_struct *mm, | 83 | static inline void huge_ptep_invalidate(struct mm_struct *mm, |
@@ -106,6 +89,7 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm, | |||
106 | __pmd_idte(address, pmdp); | 89 | __pmd_idte(address, pmdp); |
107 | else | 90 | else |
108 | __pmd_csp(pmdp); | 91 | __pmd_csp(pmdp); |
92 | pmd_val(*pmdp) = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY; | ||
109 | } | 93 | } |
110 | 94 | ||
111 | static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, | 95 | static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, |
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 2c8f00d31cfe..ed14fc2db6e0 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -350,6 +350,10 @@ extern struct page *vmemmap; | |||
350 | #define _SEGMENT_ENTRY_SPLIT_BIT 0 /* THP splitting bit number */ | 350 | #define _SEGMENT_ENTRY_SPLIT_BIT 0 /* THP splitting bit number */ |
351 | #define _SEGMENT_ENTRY_SPLIT (1UL << _SEGMENT_ENTRY_SPLIT_BIT) | 351 | #define _SEGMENT_ENTRY_SPLIT (1UL << _SEGMENT_ENTRY_SPLIT_BIT) |
352 | 352 | ||
353 | /* Set of bits not changed in pmd_modify */ | ||
354 | #define _SEGMENT_CHG_MASK (_SEGMENT_ENTRY_ORIGIN | _SEGMENT_ENTRY_LARGE \ | ||
355 | | _SEGMENT_ENTRY_SPLIT | _SEGMENT_ENTRY_CO) | ||
356 | |||
353 | /* Page status table bits for virtualization */ | 357 | /* Page status table bits for virtualization */ |
354 | #define RCP_ACC_BITS 0xf000000000000000UL | 358 | #define RCP_ACC_BITS 0xf000000000000000UL |
355 | #define RCP_FP_BIT 0x0800000000000000UL | 359 | #define RCP_FP_BIT 0x0800000000000000UL |
@@ -512,6 +516,26 @@ static inline int pmd_bad(pmd_t pmd) | |||
512 | extern void pmdp_splitting_flush(struct vm_area_struct *vma, | 516 | extern void pmdp_splitting_flush(struct vm_area_struct *vma, |
513 | unsigned long addr, pmd_t *pmdp); | 517 | unsigned long addr, pmd_t *pmdp); |
514 | 518 | ||
519 | #define __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS | ||
520 | extern int pmdp_set_access_flags(struct vm_area_struct *vma, | ||
521 | unsigned long address, pmd_t *pmdp, | ||
522 | pmd_t entry, int dirty); | ||
523 | |||
524 | #define __HAVE_ARCH_PMDP_CLEAR_YOUNG_FLUSH | ||
525 | extern int pmdp_clear_flush_young(struct vm_area_struct *vma, | ||
526 | unsigned long address, pmd_t *pmdp); | ||
527 | |||
528 | #define __HAVE_ARCH_PMD_WRITE | ||
529 | static inline int pmd_write(pmd_t pmd) | ||
530 | { | ||
531 | return (pmd_val(pmd) & _SEGMENT_ENTRY_RO) == 0; | ||
532 | } | ||
533 | |||
534 | static inline int pmd_young(pmd_t pmd) | ||
535 | { | ||
536 | return 0; | ||
537 | } | ||
538 | |||
515 | static inline int pte_none(pte_t pte) | 539 | static inline int pte_none(pte_t pte) |
516 | { | 540 | { |
517 | return (pte_val(pte) & _PAGE_INVALID) && !(pte_val(pte) & _PAGE_SWT); | 541 | return (pte_val(pte) & _PAGE_INVALID) && !(pte_val(pte) & _PAGE_SWT); |
@@ -1165,6 +1189,22 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address) | |||
1165 | #define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address) | 1189 | #define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address) |
1166 | #define pte_unmap(pte) do { } while (0) | 1190 | #define pte_unmap(pte) do { } while (0) |
1167 | 1191 | ||
1192 | static inline void __pmd_idte(unsigned long address, pmd_t *pmdp) | ||
1193 | { | ||
1194 | unsigned long sto = (unsigned long) pmdp - | ||
1195 | pmd_index(address) * sizeof(pmd_t); | ||
1196 | |||
1197 | if (!(pmd_val(*pmdp) & _SEGMENT_ENTRY_INV)) { | ||
1198 | asm volatile( | ||
1199 | " .insn rrf,0xb98e0000,%2,%3,0,0" | ||
1200 | : "=m" (*pmdp) | ||
1201 | : "m" (*pmdp), "a" (sto), | ||
1202 | "a" ((address & HPAGE_MASK)) | ||
1203 | : "cc" | ||
1204 | ); | ||
1205 | } | ||
1206 | } | ||
1207 | |||
1168 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 1208 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
1169 | #define __HAVE_ARCH_PGTABLE_DEPOSIT | 1209 | #define __HAVE_ARCH_PGTABLE_DEPOSIT |
1170 | extern void pgtable_trans_huge_deposit(struct mm_struct *mm, pgtable_t pgtable); | 1210 | extern void pgtable_trans_huge_deposit(struct mm_struct *mm, pgtable_t pgtable); |
@@ -1176,6 +1216,156 @@ static inline int pmd_trans_splitting(pmd_t pmd) | |||
1176 | { | 1216 | { |
1177 | return pmd_val(pmd) & _SEGMENT_ENTRY_SPLIT; | 1217 | return pmd_val(pmd) & _SEGMENT_ENTRY_SPLIT; |
1178 | } | 1218 | } |
1219 | |||
1220 | static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, | ||
1221 | pmd_t *pmdp, pmd_t entry) | ||
1222 | { | ||
1223 | *pmdp = entry; | ||
1224 | } | ||
1225 | |||
1226 | static inline unsigned long massage_pgprot_pmd(pgprot_t pgprot) | ||
1227 | { | ||
1228 | unsigned long pgprot_pmd = 0; | ||
1229 | |||
1230 | if (pgprot_val(pgprot) & _PAGE_INVALID) { | ||
1231 | if (pgprot_val(pgprot) & _PAGE_SWT) | ||
1232 | pgprot_pmd |= _HPAGE_TYPE_NONE; | ||
1233 | pgprot_pmd |= _SEGMENT_ENTRY_INV; | ||
1234 | } | ||
1235 | if (pgprot_val(pgprot) & _PAGE_RO) | ||
1236 | pgprot_pmd |= _SEGMENT_ENTRY_RO; | ||
1237 | return pgprot_pmd; | ||
1238 | } | ||
1239 | |||
1240 | static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) | ||
1241 | { | ||
1242 | pmd_val(pmd) &= _SEGMENT_CHG_MASK; | ||
1243 | pmd_val(pmd) |= massage_pgprot_pmd(newprot); | ||
1244 | return pmd; | ||
1245 | } | ||
1246 | |||
1247 | static inline pmd_t pmd_mkhuge(pmd_t pmd) | ||
1248 | { | ||
1249 | pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE; | ||
1250 | return pmd; | ||
1251 | } | ||
1252 | |||
1253 | static inline pmd_t pmd_mkwrite(pmd_t pmd) | ||
1254 | { | ||
1255 | pmd_val(pmd) &= ~_SEGMENT_ENTRY_RO; | ||
1256 | return pmd; | ||
1257 | } | ||
1258 | |||
1259 | static inline pmd_t pmd_wrprotect(pmd_t pmd) | ||
1260 | { | ||
1261 | pmd_val(pmd) |= _SEGMENT_ENTRY_RO; | ||
1262 | return pmd; | ||
1263 | } | ||
1264 | |||
1265 | static inline pmd_t pmd_mkdirty(pmd_t pmd) | ||
1266 | { | ||
1267 | /* No dirty bit in the segment table entry. */ | ||
1268 | return pmd; | ||
1269 | } | ||
1270 | |||
1271 | static inline pmd_t pmd_mkold(pmd_t pmd) | ||
1272 | { | ||
1273 | /* No referenced bit in the segment table entry. */ | ||
1274 | return pmd; | ||
1275 | } | ||
1276 | |||
1277 | static inline pmd_t pmd_mkyoung(pmd_t pmd) | ||
1278 | { | ||
1279 | /* No referenced bit in the segment table entry. */ | ||
1280 | return pmd; | ||
1281 | } | ||
1282 | |||
1283 | #define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG | ||
1284 | static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, | ||
1285 | unsigned long address, pmd_t *pmdp) | ||
1286 | { | ||
1287 | unsigned long pmd_addr = pmd_val(*pmdp) & HPAGE_MASK; | ||
1288 | long tmp, rc; | ||
1289 | int counter; | ||
1290 | |||
1291 | rc = 0; | ||
1292 | if (MACHINE_HAS_RRBM) { | ||
1293 | counter = PTRS_PER_PTE >> 6; | ||
1294 | asm volatile( | ||
1295 | "0: .insn rre,0xb9ae0000,%0,%3\n" /* rrbm */ | ||
1296 | " ogr %1,%0\n" | ||
1297 | " la %3,0(%4,%3)\n" | ||
1298 | " brct %2,0b\n" | ||
1299 | : "=&d" (tmp), "+&d" (rc), "+d" (counter), | ||
1300 | "+a" (pmd_addr) | ||
1301 | : "a" (64 * 4096UL) : "cc"); | ||
1302 | rc = !!rc; | ||
1303 | } else { | ||
1304 | counter = PTRS_PER_PTE; | ||
1305 | asm volatile( | ||
1306 | "0: rrbe 0,%2\n" | ||
1307 | " la %2,0(%3,%2)\n" | ||
1308 | " brc 12,1f\n" | ||
1309 | " lhi %0,1\n" | ||
1310 | "1: brct %1,0b\n" | ||
1311 | : "+d" (rc), "+d" (counter), "+a" (pmd_addr) | ||
1312 | : "a" (4096UL) : "cc"); | ||
1313 | } | ||
1314 | return rc; | ||
1315 | } | ||
1316 | |||
1317 | #define __HAVE_ARCH_PMDP_GET_AND_CLEAR | ||
1318 | static inline pmd_t pmdp_get_and_clear(struct mm_struct *mm, | ||
1319 | unsigned long address, pmd_t *pmdp) | ||
1320 | { | ||
1321 | pmd_t pmd = *pmdp; | ||
1322 | |||
1323 | __pmd_idte(address, pmdp); | ||
1324 | pmd_clear(pmdp); | ||
1325 | return pmd; | ||
1326 | } | ||
1327 | |||
1328 | #define __HAVE_ARCH_PMDP_CLEAR_FLUSH | ||
1329 | static inline pmd_t pmdp_clear_flush(struct vm_area_struct *vma, | ||
1330 | unsigned long address, pmd_t *pmdp) | ||
1331 | { | ||
1332 | return pmdp_get_and_clear(vma->vm_mm, address, pmdp); | ||
1333 | } | ||
1334 | |||
1335 | #define __HAVE_ARCH_PMDP_INVALIDATE | ||
1336 | static inline void pmdp_invalidate(struct vm_area_struct *vma, | ||
1337 | unsigned long address, pmd_t *pmdp) | ||
1338 | { | ||
1339 | __pmd_idte(address, pmdp); | ||
1340 | } | ||
1341 | |||
1342 | static inline pmd_t mk_pmd_phys(unsigned long physpage, pgprot_t pgprot) | ||
1343 | { | ||
1344 | pmd_t __pmd; | ||
1345 | pmd_val(__pmd) = physpage + massage_pgprot_pmd(pgprot); | ||
1346 | return __pmd; | ||
1347 | } | ||
1348 | |||
1349 | #define pfn_pmd(pfn, pgprot) mk_pmd_phys(__pa((pfn) << PAGE_SHIFT), (pgprot)) | ||
1350 | #define mk_pmd(page, pgprot) pfn_pmd(page_to_pfn(page), (pgprot)) | ||
1351 | |||
1352 | static inline int pmd_trans_huge(pmd_t pmd) | ||
1353 | { | ||
1354 | return pmd_val(pmd) & _SEGMENT_ENTRY_LARGE; | ||
1355 | } | ||
1356 | |||
1357 | static inline int has_transparent_hugepage(void) | ||
1358 | { | ||
1359 | return MACHINE_HAS_HPAGE ? 1 : 0; | ||
1360 | } | ||
1361 | |||
1362 | static inline unsigned long pmd_pfn(pmd_t pmd) | ||
1363 | { | ||
1364 | if (pmd_trans_huge(pmd)) | ||
1365 | return pmd_val(pmd) >> HPAGE_SHIFT; | ||
1366 | else | ||
1367 | return pmd_val(pmd) >> PAGE_SHIFT; | ||
1368 | } | ||
1179 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | 1369 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ |
1180 | 1370 | ||
1181 | /* | 1371 | /* |
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 87b47ca954f1..8cfd731a18d8 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h | |||
@@ -81,6 +81,7 @@ extern unsigned int s390_user_mode; | |||
81 | #define MACHINE_FLAG_SPP (1UL << 13) | 81 | #define MACHINE_FLAG_SPP (1UL << 13) |
82 | #define MACHINE_FLAG_TOPOLOGY (1UL << 14) | 82 | #define MACHINE_FLAG_TOPOLOGY (1UL << 14) |
83 | #define MACHINE_FLAG_TE (1UL << 15) | 83 | #define MACHINE_FLAG_TE (1UL << 15) |
84 | #define MACHINE_FLAG_RRBM (1UL << 16) | ||
84 | 85 | ||
85 | #define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) | 86 | #define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) |
86 | #define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) | 87 | #define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) |
@@ -99,7 +100,8 @@ extern unsigned int s390_user_mode; | |||
99 | #define MACHINE_HAS_PFMF (0) | 100 | #define MACHINE_HAS_PFMF (0) |
100 | #define MACHINE_HAS_SPP (0) | 101 | #define MACHINE_HAS_SPP (0) |
101 | #define MACHINE_HAS_TOPOLOGY (0) | 102 | #define MACHINE_HAS_TOPOLOGY (0) |
102 | #define MACHINE_HAS_TE (0) | 103 | #define MACHINE_HAS_TE (0) |
104 | #define MACHINE_HAS_RRBM (0) | ||
103 | #else /* CONFIG_64BIT */ | 105 | #else /* CONFIG_64BIT */ |
104 | #define MACHINE_HAS_IEEE (1) | 106 | #define MACHINE_HAS_IEEE (1) |
105 | #define MACHINE_HAS_CSP (1) | 107 | #define MACHINE_HAS_CSP (1) |
@@ -112,6 +114,7 @@ extern unsigned int s390_user_mode; | |||
112 | #define MACHINE_HAS_SPP (S390_lowcore.machine_flags & MACHINE_FLAG_SPP) | 114 | #define MACHINE_HAS_SPP (S390_lowcore.machine_flags & MACHINE_FLAG_SPP) |
113 | #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) | 115 | #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) |
114 | #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) | 116 | #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) |
117 | #define MACHINE_HAS_RRBM (S390_lowcore.machine_flags & MACHINE_FLAG_RRBM) | ||
115 | #endif /* CONFIG_64BIT */ | 118 | #endif /* CONFIG_64BIT */ |
116 | 119 | ||
117 | #define ZFCPDUMP_HSA_SIZE (32UL<<20) | 120 | #define ZFCPDUMP_HSA_SIZE (32UL<<20) |
diff --git a/arch/s390/include/asm/tlb.h b/arch/s390/include/asm/tlb.h index 06e5acbc84bd..b75d7d686684 100644 --- a/arch/s390/include/asm/tlb.h +++ b/arch/s390/include/asm/tlb.h | |||
@@ -137,6 +137,7 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud, | |||
137 | #define tlb_start_vma(tlb, vma) do { } while (0) | 137 | #define tlb_start_vma(tlb, vma) do { } while (0) |
138 | #define tlb_end_vma(tlb, vma) do { } while (0) | 138 | #define tlb_end_vma(tlb, vma) do { } while (0) |
139 | #define tlb_remove_tlb_entry(tlb, ptep, addr) do { } while (0) | 139 | #define tlb_remove_tlb_entry(tlb, ptep, addr) do { } while (0) |
140 | #define tlb_remove_pmd_tlb_entry(tlb, pmdp, addr) do { } while (0) | ||
140 | #define tlb_migrate_finish(mm) do { } while (0) | 141 | #define tlb_migrate_finish(mm) do { } while (0) |
141 | 142 | ||
142 | #endif /* _S390_TLB_H */ | 143 | #endif /* _S390_TLB_H */ |
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 7f4717675c19..00d114445068 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c | |||
@@ -388,6 +388,8 @@ static __init void detect_machine_facilities(void) | |||
388 | S390_lowcore.machine_flags |= MACHINE_FLAG_SPP; | 388 | S390_lowcore.machine_flags |= MACHINE_FLAG_SPP; |
389 | if (test_facility(50) && test_facility(73)) | 389 | if (test_facility(50) && test_facility(73)) |
390 | S390_lowcore.machine_flags |= MACHINE_FLAG_TE; | 390 | S390_lowcore.machine_flags |= MACHINE_FLAG_TE; |
391 | if (test_facility(66)) | ||
392 | S390_lowcore.machine_flags |= MACHINE_FLAG_RRBM; | ||
391 | #endif | 393 | #endif |
392 | } | 394 | } |
393 | 395 | ||
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index e86a523875eb..c8188a18af05 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c | |||
@@ -898,6 +898,28 @@ bool kernel_page_present(struct page *page) | |||
898 | #endif /* CONFIG_HIBERNATION && CONFIG_DEBUG_PAGEALLOC */ | 898 | #endif /* CONFIG_HIBERNATION && CONFIG_DEBUG_PAGEALLOC */ |
899 | 899 | ||
900 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 900 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
901 | int pmdp_clear_flush_young(struct vm_area_struct *vma, unsigned long address, | ||
902 | pmd_t *pmdp) | ||
903 | { | ||
904 | VM_BUG_ON(address & ~HPAGE_PMD_MASK); | ||
905 | /* No need to flush TLB | ||
906 | * On s390 reference bits are in storage key and never in TLB */ | ||
907 | return pmdp_test_and_clear_young(vma, address, pmdp); | ||
908 | } | ||
909 | |||
910 | int pmdp_set_access_flags(struct vm_area_struct *vma, | ||
911 | unsigned long address, pmd_t *pmdp, | ||
912 | pmd_t entry, int dirty) | ||
913 | { | ||
914 | VM_BUG_ON(address & ~HPAGE_PMD_MASK); | ||
915 | |||
916 | if (pmd_same(*pmdp, entry)) | ||
917 | return 0; | ||
918 | pmdp_invalidate(vma, address, pmdp); | ||
919 | set_pmd_at(vma->vm_mm, address, pmdp, entry); | ||
920 | return 1; | ||
921 | } | ||
922 | |||
901 | static void pmdp_splitting_flush_sync(void *arg) | 923 | static void pmdp_splitting_flush_sync(void *arg) |
902 | { | 924 | { |
903 | /* Simply deliver the interrupt */ | 925 | /* Simply deliver the interrupt */ |