aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/s390/include/asm/hugetlb.h18
-rw-r--r--arch/s390/include/asm/pgtable.h190
-rw-r--r--arch/s390/include/asm/setup.h5
-rw-r--r--arch/s390/include/asm/tlb.h1
-rw-r--r--arch/s390/kernel/early.c2
-rw-r--r--arch/s390/mm/pgtable.c22
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
84static 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
100static inline void huge_ptep_invalidate(struct mm_struct *mm, 83static 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
111static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, 95static 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)
512extern void pmdp_splitting_flush(struct vm_area_struct *vma, 516extern 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
520extern 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
525extern int pmdp_clear_flush_young(struct vm_area_struct *vma,
526 unsigned long address, pmd_t *pmdp);
527
528#define __HAVE_ARCH_PMD_WRITE
529static inline int pmd_write(pmd_t pmd)
530{
531 return (pmd_val(pmd) & _SEGMENT_ENTRY_RO) == 0;
532}
533
534static inline int pmd_young(pmd_t pmd)
535{
536 return 0;
537}
538
515static inline int pte_none(pte_t pte) 539static 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
1192static 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
1170extern void pgtable_trans_huge_deposit(struct mm_struct *mm, pgtable_t pgtable); 1210extern 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
1220static 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
1226static 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
1240static 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
1247static inline pmd_t pmd_mkhuge(pmd_t pmd)
1248{
1249 pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE;
1250 return pmd;
1251}
1252
1253static inline pmd_t pmd_mkwrite(pmd_t pmd)
1254{
1255 pmd_val(pmd) &= ~_SEGMENT_ENTRY_RO;
1256 return pmd;
1257}
1258
1259static inline pmd_t pmd_wrprotect(pmd_t pmd)
1260{
1261 pmd_val(pmd) |= _SEGMENT_ENTRY_RO;
1262 return pmd;
1263}
1264
1265static inline pmd_t pmd_mkdirty(pmd_t pmd)
1266{
1267 /* No dirty bit in the segment table entry. */
1268 return pmd;
1269}
1270
1271static inline pmd_t pmd_mkold(pmd_t pmd)
1272{
1273 /* No referenced bit in the segment table entry. */
1274 return pmd;
1275}
1276
1277static 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
1284static 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
1318static 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
1329static 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
1336static inline void pmdp_invalidate(struct vm_area_struct *vma,
1337 unsigned long address, pmd_t *pmdp)
1338{
1339 __pmd_idte(address, pmdp);
1340}
1341
1342static 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
1352static inline int pmd_trans_huge(pmd_t pmd)
1353{
1354 return pmd_val(pmd) & _SEGMENT_ENTRY_LARGE;
1355}
1356
1357static inline int has_transparent_hugepage(void)
1358{
1359 return MACHINE_HAS_HPAGE ? 1 : 0;
1360}
1361
1362static 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
901int 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
910int 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
901static void pmdp_splitting_flush_sync(void *arg) 923static void pmdp_splitting_flush_sync(void *arg)
902{ 924{
903 /* Simply deliver the interrupt */ 925 /* Simply deliver the interrupt */