diff options
Diffstat (limited to 'arch/s390/include/asm/pgtable.h')
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 105 |
1 files changed, 72 insertions, 33 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 66101f6c6d81..50a75d96f939 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -229,6 +229,7 @@ extern unsigned long MODULES_END; | |||
229 | #define _PAGE_READ 0x010 /* SW pte read bit */ | 229 | #define _PAGE_READ 0x010 /* SW pte read bit */ |
230 | #define _PAGE_WRITE 0x020 /* SW pte write bit */ | 230 | #define _PAGE_WRITE 0x020 /* SW pte write bit */ |
231 | #define _PAGE_SPECIAL 0x040 /* SW associated with special page */ | 231 | #define _PAGE_SPECIAL 0x040 /* SW associated with special page */ |
232 | #define _PAGE_UNUSED 0x080 /* SW bit for pgste usage state */ | ||
232 | #define __HAVE_ARCH_PTE_SPECIAL | 233 | #define __HAVE_ARCH_PTE_SPECIAL |
233 | 234 | ||
234 | /* Set of bits not changed in pte_modify */ | 235 | /* Set of bits not changed in pte_modify */ |
@@ -394,6 +395,12 @@ extern unsigned long MODULES_END; | |||
394 | 395 | ||
395 | #endif /* CONFIG_64BIT */ | 396 | #endif /* CONFIG_64BIT */ |
396 | 397 | ||
398 | /* Guest Page State used for virtualization */ | ||
399 | #define _PGSTE_GPS_ZERO 0x0000000080000000UL | ||
400 | #define _PGSTE_GPS_USAGE_MASK 0x0000000003000000UL | ||
401 | #define _PGSTE_GPS_USAGE_STABLE 0x0000000000000000UL | ||
402 | #define _PGSTE_GPS_USAGE_UNUSED 0x0000000001000000UL | ||
403 | |||
397 | /* | 404 | /* |
398 | * A user page table pointer has the space-switch-event bit, the | 405 | * A user page table pointer has the space-switch-event bit, the |
399 | * private-space-control bit and the storage-alteration-event-control | 406 | * private-space-control bit and the storage-alteration-event-control |
@@ -617,6 +624,14 @@ static inline int pte_none(pte_t pte) | |||
617 | return pte_val(pte) == _PAGE_INVALID; | 624 | return pte_val(pte) == _PAGE_INVALID; |
618 | } | 625 | } |
619 | 626 | ||
627 | static inline int pte_swap(pte_t pte) | ||
628 | { | ||
629 | /* Bit pattern: (pte & 0x603) == 0x402 */ | ||
630 | return (pte_val(pte) & (_PAGE_INVALID | _PAGE_PROTECT | | ||
631 | _PAGE_TYPE | _PAGE_PRESENT)) | ||
632 | == (_PAGE_INVALID | _PAGE_TYPE); | ||
633 | } | ||
634 | |||
620 | static inline int pte_file(pte_t pte) | 635 | static inline int pte_file(pte_t pte) |
621 | { | 636 | { |
622 | /* Bit pattern: (pte & 0x601) == 0x600 */ | 637 | /* Bit pattern: (pte & 0x601) == 0x600 */ |
@@ -823,20 +838,20 @@ unsigned long gmap_translate(unsigned long address, struct gmap *); | |||
823 | unsigned long __gmap_fault(unsigned long address, struct gmap *); | 838 | unsigned long __gmap_fault(unsigned long address, struct gmap *); |
824 | unsigned long gmap_fault(unsigned long address, struct gmap *); | 839 | unsigned long gmap_fault(unsigned long address, struct gmap *); |
825 | void gmap_discard(unsigned long from, unsigned long to, struct gmap *); | 840 | void gmap_discard(unsigned long from, unsigned long to, struct gmap *); |
841 | void __gmap_zap(unsigned long address, struct gmap *); | ||
826 | 842 | ||
827 | void gmap_register_ipte_notifier(struct gmap_notifier *); | 843 | void gmap_register_ipte_notifier(struct gmap_notifier *); |
828 | void gmap_unregister_ipte_notifier(struct gmap_notifier *); | 844 | void gmap_unregister_ipte_notifier(struct gmap_notifier *); |
829 | int gmap_ipte_notify(struct gmap *, unsigned long start, unsigned long len); | 845 | int gmap_ipte_notify(struct gmap *, unsigned long start, unsigned long len); |
830 | void gmap_do_ipte_notify(struct mm_struct *, unsigned long addr, pte_t *); | 846 | void gmap_do_ipte_notify(struct mm_struct *, pte_t *); |
831 | 847 | ||
832 | static inline pgste_t pgste_ipte_notify(struct mm_struct *mm, | 848 | static inline pgste_t pgste_ipte_notify(struct mm_struct *mm, |
833 | unsigned long addr, | ||
834 | pte_t *ptep, pgste_t pgste) | 849 | pte_t *ptep, pgste_t pgste) |
835 | { | 850 | { |
836 | #ifdef CONFIG_PGSTE | 851 | #ifdef CONFIG_PGSTE |
837 | if (pgste_val(pgste) & PGSTE_IN_BIT) { | 852 | if (pgste_val(pgste) & PGSTE_IN_BIT) { |
838 | pgste_val(pgste) &= ~PGSTE_IN_BIT; | 853 | pgste_val(pgste) &= ~PGSTE_IN_BIT; |
839 | gmap_do_ipte_notify(mm, addr, ptep); | 854 | gmap_do_ipte_notify(mm, ptep); |
840 | } | 855 | } |
841 | #endif | 856 | #endif |
842 | return pgste; | 857 | return pgste; |
@@ -854,6 +869,7 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, | |||
854 | 869 | ||
855 | if (mm_has_pgste(mm)) { | 870 | if (mm_has_pgste(mm)) { |
856 | pgste = pgste_get_lock(ptep); | 871 | pgste = pgste_get_lock(ptep); |
872 | pgste_val(pgste) &= ~_PGSTE_GPS_ZERO; | ||
857 | pgste_set_key(ptep, pgste, entry); | 873 | pgste_set_key(ptep, pgste, entry); |
858 | pgste_set_pte(ptep, entry); | 874 | pgste_set_pte(ptep, entry); |
859 | pgste_set_unlock(ptep, pgste); | 875 | pgste_set_unlock(ptep, pgste); |
@@ -883,6 +899,12 @@ static inline int pte_young(pte_t pte) | |||
883 | return (pte_val(pte) & _PAGE_YOUNG) != 0; | 899 | return (pte_val(pte) & _PAGE_YOUNG) != 0; |
884 | } | 900 | } |
885 | 901 | ||
902 | #define __HAVE_ARCH_PTE_UNUSED | ||
903 | static inline int pte_unused(pte_t pte) | ||
904 | { | ||
905 | return pte_val(pte) & _PAGE_UNUSED; | ||
906 | } | ||
907 | |||
886 | /* | 908 | /* |
887 | * pgd/pmd/pte modification functions | 909 | * pgd/pmd/pte modification functions |
888 | */ | 910 | */ |
@@ -1036,30 +1058,41 @@ static inline int ptep_test_and_clear_user_young(struct mm_struct *mm, | |||
1036 | 1058 | ||
1037 | static inline void __ptep_ipte(unsigned long address, pte_t *ptep) | 1059 | static inline void __ptep_ipte(unsigned long address, pte_t *ptep) |
1038 | { | 1060 | { |
1039 | if (!(pte_val(*ptep) & _PAGE_INVALID)) { | 1061 | unsigned long pto = (unsigned long) ptep; |
1062 | |||
1040 | #ifndef CONFIG_64BIT | 1063 | #ifndef CONFIG_64BIT |
1041 | /* pto must point to the start of the segment table */ | 1064 | /* pto in ESA mode must point to the start of the segment table */ |
1042 | pte_t *pto = (pte_t *) (((unsigned long) ptep) & 0x7ffffc00); | 1065 | pto &= 0x7ffffc00; |
1043 | #else | ||
1044 | /* ipte in zarch mode can do the math */ | ||
1045 | pte_t *pto = ptep; | ||
1046 | #endif | 1066 | #endif |
1047 | asm volatile( | 1067 | /* Invalidation + global TLB flush for the pte */ |
1048 | " ipte %2,%3" | 1068 | asm volatile( |
1049 | : "=m" (*ptep) : "m" (*ptep), | 1069 | " ipte %2,%3" |
1050 | "a" (pto), "a" (address)); | 1070 | : "=m" (*ptep) : "m" (*ptep), "a" (pto), "a" (address)); |
1051 | } | 1071 | } |
1072 | |||
1073 | static inline void ptep_flush_direct(struct mm_struct *mm, | ||
1074 | unsigned long address, pte_t *ptep) | ||
1075 | { | ||
1076 | if (pte_val(*ptep) & _PAGE_INVALID) | ||
1077 | return; | ||
1078 | __ptep_ipte(address, ptep); | ||
1052 | } | 1079 | } |
1053 | 1080 | ||
1054 | static inline void ptep_flush_lazy(struct mm_struct *mm, | 1081 | static inline void ptep_flush_lazy(struct mm_struct *mm, |
1055 | unsigned long address, pte_t *ptep) | 1082 | unsigned long address, pte_t *ptep) |
1056 | { | 1083 | { |
1057 | int active = (mm == current->active_mm) ? 1 : 0; | 1084 | int active, count; |
1058 | 1085 | ||
1059 | if (atomic_read(&mm->context.attach_count) > active) | 1086 | if (pte_val(*ptep) & _PAGE_INVALID) |
1060 | __ptep_ipte(address, ptep); | 1087 | return; |
1061 | else | 1088 | active = (mm == current->active_mm) ? 1 : 0; |
1089 | count = atomic_add_return(0x10000, &mm->context.attach_count); | ||
1090 | if ((count & 0xffff) <= active) { | ||
1091 | pte_val(*ptep) |= _PAGE_INVALID; | ||
1062 | mm->context.flush_mm = 1; | 1092 | mm->context.flush_mm = 1; |
1093 | } else | ||
1094 | __ptep_ipte(address, ptep); | ||
1095 | atomic_sub(0x10000, &mm->context.attach_count); | ||
1063 | } | 1096 | } |
1064 | 1097 | ||
1065 | #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG | 1098 | #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG |
@@ -1072,11 +1105,11 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, | |||
1072 | 1105 | ||
1073 | if (mm_has_pgste(vma->vm_mm)) { | 1106 | if (mm_has_pgste(vma->vm_mm)) { |
1074 | pgste = pgste_get_lock(ptep); | 1107 | pgste = pgste_get_lock(ptep); |
1075 | pgste = pgste_ipte_notify(vma->vm_mm, addr, ptep, pgste); | 1108 | pgste = pgste_ipte_notify(vma->vm_mm, ptep, pgste); |
1076 | } | 1109 | } |
1077 | 1110 | ||
1078 | pte = *ptep; | 1111 | pte = *ptep; |
1079 | __ptep_ipte(addr, ptep); | 1112 | ptep_flush_direct(vma->vm_mm, addr, ptep); |
1080 | young = pte_young(pte); | 1113 | young = pte_young(pte); |
1081 | pte = pte_mkold(pte); | 1114 | pte = pte_mkold(pte); |
1082 | 1115 | ||
@@ -1118,7 +1151,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, | |||
1118 | 1151 | ||
1119 | if (mm_has_pgste(mm)) { | 1152 | if (mm_has_pgste(mm)) { |
1120 | pgste = pgste_get_lock(ptep); | 1153 | pgste = pgste_get_lock(ptep); |
1121 | pgste = pgste_ipte_notify(mm, address, ptep, pgste); | 1154 | pgste = pgste_ipte_notify(mm, ptep, pgste); |
1122 | } | 1155 | } |
1123 | 1156 | ||
1124 | pte = *ptep; | 1157 | pte = *ptep; |
@@ -1142,12 +1175,11 @@ static inline pte_t ptep_modify_prot_start(struct mm_struct *mm, | |||
1142 | 1175 | ||
1143 | if (mm_has_pgste(mm)) { | 1176 | if (mm_has_pgste(mm)) { |
1144 | pgste = pgste_get_lock(ptep); | 1177 | pgste = pgste_get_lock(ptep); |
1145 | pgste_ipte_notify(mm, address, ptep, pgste); | 1178 | pgste_ipte_notify(mm, ptep, pgste); |
1146 | } | 1179 | } |
1147 | 1180 | ||
1148 | pte = *ptep; | 1181 | pte = *ptep; |
1149 | ptep_flush_lazy(mm, address, ptep); | 1182 | ptep_flush_lazy(mm, address, ptep); |
1150 | pte_val(*ptep) |= _PAGE_INVALID; | ||
1151 | 1183 | ||
1152 | if (mm_has_pgste(mm)) { | 1184 | if (mm_has_pgste(mm)) { |
1153 | pgste = pgste_update_all(&pte, pgste); | 1185 | pgste = pgste_update_all(&pte, pgste); |
@@ -1180,14 +1212,17 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma, | |||
1180 | 1212 | ||
1181 | if (mm_has_pgste(vma->vm_mm)) { | 1213 | if (mm_has_pgste(vma->vm_mm)) { |
1182 | pgste = pgste_get_lock(ptep); | 1214 | pgste = pgste_get_lock(ptep); |
1183 | pgste = pgste_ipte_notify(vma->vm_mm, address, ptep, pgste); | 1215 | pgste = pgste_ipte_notify(vma->vm_mm, ptep, pgste); |
1184 | } | 1216 | } |
1185 | 1217 | ||
1186 | pte = *ptep; | 1218 | pte = *ptep; |
1187 | __ptep_ipte(address, ptep); | 1219 | ptep_flush_direct(vma->vm_mm, address, ptep); |
1188 | pte_val(*ptep) = _PAGE_INVALID; | 1220 | pte_val(*ptep) = _PAGE_INVALID; |
1189 | 1221 | ||
1190 | if (mm_has_pgste(vma->vm_mm)) { | 1222 | if (mm_has_pgste(vma->vm_mm)) { |
1223 | if ((pgste_val(pgste) & _PGSTE_GPS_USAGE_MASK) == | ||
1224 | _PGSTE_GPS_USAGE_UNUSED) | ||
1225 | pte_val(pte) |= _PAGE_UNUSED; | ||
1191 | pgste = pgste_update_all(&pte, pgste); | 1226 | pgste = pgste_update_all(&pte, pgste); |
1192 | pgste_set_unlock(ptep, pgste); | 1227 | pgste_set_unlock(ptep, pgste); |
1193 | } | 1228 | } |
@@ -1211,7 +1246,7 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, | |||
1211 | 1246 | ||
1212 | if (!full && mm_has_pgste(mm)) { | 1247 | if (!full && mm_has_pgste(mm)) { |
1213 | pgste = pgste_get_lock(ptep); | 1248 | pgste = pgste_get_lock(ptep); |
1214 | pgste = pgste_ipte_notify(mm, address, ptep, pgste); | 1249 | pgste = pgste_ipte_notify(mm, ptep, pgste); |
1215 | } | 1250 | } |
1216 | 1251 | ||
1217 | pte = *ptep; | 1252 | pte = *ptep; |
@@ -1236,7 +1271,7 @@ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm, | |||
1236 | if (pte_write(pte)) { | 1271 | if (pte_write(pte)) { |
1237 | if (mm_has_pgste(mm)) { | 1272 | if (mm_has_pgste(mm)) { |
1238 | pgste = pgste_get_lock(ptep); | 1273 | pgste = pgste_get_lock(ptep); |
1239 | pgste = pgste_ipte_notify(mm, address, ptep, pgste); | 1274 | pgste = pgste_ipte_notify(mm, ptep, pgste); |
1240 | } | 1275 | } |
1241 | 1276 | ||
1242 | ptep_flush_lazy(mm, address, ptep); | 1277 | ptep_flush_lazy(mm, address, ptep); |
@@ -1262,10 +1297,10 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma, | |||
1262 | return 0; | 1297 | return 0; |
1263 | if (mm_has_pgste(vma->vm_mm)) { | 1298 | if (mm_has_pgste(vma->vm_mm)) { |
1264 | pgste = pgste_get_lock(ptep); | 1299 | pgste = pgste_get_lock(ptep); |
1265 | pgste = pgste_ipte_notify(vma->vm_mm, address, ptep, pgste); | 1300 | pgste = pgste_ipte_notify(vma->vm_mm, ptep, pgste); |
1266 | } | 1301 | } |
1267 | 1302 | ||
1268 | __ptep_ipte(address, ptep); | 1303 | ptep_flush_direct(vma->vm_mm, address, ptep); |
1269 | 1304 | ||
1270 | if (mm_has_pgste(vma->vm_mm)) { | 1305 | if (mm_has_pgste(vma->vm_mm)) { |
1271 | pgste_set_pte(ptep, entry); | 1306 | pgste_set_pte(ptep, entry); |
@@ -1449,12 +1484,16 @@ static inline pmd_t pmd_mkwrite(pmd_t pmd) | |||
1449 | static inline void pmdp_flush_lazy(struct mm_struct *mm, | 1484 | static inline void pmdp_flush_lazy(struct mm_struct *mm, |
1450 | unsigned long address, pmd_t *pmdp) | 1485 | unsigned long address, pmd_t *pmdp) |
1451 | { | 1486 | { |
1452 | int active = (mm == current->active_mm) ? 1 : 0; | 1487 | int active, count; |
1453 | 1488 | ||
1454 | if ((atomic_read(&mm->context.attach_count) & 0xffff) > active) | 1489 | active = (mm == current->active_mm) ? 1 : 0; |
1455 | __pmd_idte(address, pmdp); | 1490 | count = atomic_add_return(0x10000, &mm->context.attach_count); |
1456 | else | 1491 | if ((count & 0xffff) <= active) { |
1492 | pmd_val(*pmdp) |= _SEGMENT_ENTRY_INVALID; | ||
1457 | mm->context.flush_mm = 1; | 1493 | mm->context.flush_mm = 1; |
1494 | } else | ||
1495 | __pmd_idte(address, pmdp); | ||
1496 | atomic_sub(0x10000, &mm->context.attach_count); | ||
1458 | } | 1497 | } |
1459 | 1498 | ||
1460 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 1499 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |