diff options
Diffstat (limited to 'arch/s390/mm/hugetlbpage.c')
-rw-r--r-- | arch/s390/mm/hugetlbpage.c | 124 |
1 files changed, 115 insertions, 9 deletions
diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index 121089d57802..248445f92604 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c | |||
@@ -8,21 +8,127 @@ | |||
8 | #include <linux/mm.h> | 8 | #include <linux/mm.h> |
9 | #include <linux/hugetlb.h> | 9 | #include <linux/hugetlb.h> |
10 | 10 | ||
11 | static inline pmd_t __pte_to_pmd(pte_t pte) | ||
12 | { | ||
13 | int none, young, prot; | ||
14 | pmd_t pmd; | ||
15 | |||
16 | /* | ||
17 | * Convert encoding pte bits pmd bits | ||
18 | * .IR...wrdytp ..R...I...y. | ||
19 | * empty .10...000000 -> ..0...1...0. | ||
20 | * prot-none, clean, old .11...000001 -> ..0...1...1. | ||
21 | * prot-none, clean, young .11...000101 -> ..1...1...1. | ||
22 | * prot-none, dirty, old .10...001001 -> ..0...1...1. | ||
23 | * prot-none, dirty, young .10...001101 -> ..1...1...1. | ||
24 | * read-only, clean, old .11...010001 -> ..1...1...0. | ||
25 | * read-only, clean, young .01...010101 -> ..1...0...1. | ||
26 | * read-only, dirty, old .11...011001 -> ..1...1...0. | ||
27 | * read-only, dirty, young .01...011101 -> ..1...0...1. | ||
28 | * read-write, clean, old .11...110001 -> ..0...1...0. | ||
29 | * read-write, clean, young .01...110101 -> ..0...0...1. | ||
30 | * read-write, dirty, old .10...111001 -> ..0...1...0. | ||
31 | * read-write, dirty, young .00...111101 -> ..0...0...1. | ||
32 | * Huge ptes are dirty by definition, a clean pte is made dirty | ||
33 | * by the conversion. | ||
34 | */ | ||
35 | if (pte_present(pte)) { | ||
36 | pmd_val(pmd) = pte_val(pte) & PAGE_MASK; | ||
37 | if (pte_val(pte) & _PAGE_INVALID) | ||
38 | pmd_val(pmd) |= _SEGMENT_ENTRY_INVALID; | ||
39 | none = (pte_val(pte) & _PAGE_PRESENT) && | ||
40 | !(pte_val(pte) & _PAGE_READ) && | ||
41 | !(pte_val(pte) & _PAGE_WRITE); | ||
42 | prot = (pte_val(pte) & _PAGE_PROTECT) && | ||
43 | !(pte_val(pte) & _PAGE_WRITE); | ||
44 | young = pte_val(pte) & _PAGE_YOUNG; | ||
45 | if (none || young) | ||
46 | pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG; | ||
47 | if (prot || (none && young)) | ||
48 | pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT; | ||
49 | } else | ||
50 | pmd_val(pmd) = _SEGMENT_ENTRY_INVALID; | ||
51 | return pmd; | ||
52 | } | ||
53 | |||
54 | static inline pte_t __pmd_to_pte(pmd_t pmd) | ||
55 | { | ||
56 | pte_t pte; | ||
57 | |||
58 | /* | ||
59 | * Convert encoding pmd bits pte bits | ||
60 | * ..R...I...y. .IR...wrdytp | ||
61 | * empty ..0...1...0. -> .10...000000 | ||
62 | * prot-none, old ..0...1...1. -> .10...001001 | ||
63 | * prot-none, young ..1...1...1. -> .10...001101 | ||
64 | * read-only, old ..1...1...0. -> .11...011001 | ||
65 | * read-only, young ..1...0...1. -> .01...011101 | ||
66 | * read-write, old ..0...1...0. -> .10...111001 | ||
67 | * read-write, young ..0...0...1. -> .00...111101 | ||
68 | * Huge ptes are dirty by definition | ||
69 | */ | ||
70 | if (pmd_present(pmd)) { | ||
71 | pte_val(pte) = _PAGE_PRESENT | _PAGE_LARGE | _PAGE_DIRTY | | ||
72 | (pmd_val(pmd) & PAGE_MASK); | ||
73 | if (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) | ||
74 | pte_val(pte) |= _PAGE_INVALID; | ||
75 | if (pmd_prot_none(pmd)) { | ||
76 | if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) | ||
77 | pte_val(pte) |= _PAGE_YOUNG; | ||
78 | } else { | ||
79 | pte_val(pte) |= _PAGE_READ; | ||
80 | if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) | ||
81 | pte_val(pte) |= _PAGE_PROTECT; | ||
82 | else | ||
83 | pte_val(pte) |= _PAGE_WRITE; | ||
84 | if (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) | ||
85 | pte_val(pte) |= _PAGE_YOUNG; | ||
86 | } | ||
87 | } else | ||
88 | pte_val(pte) = _PAGE_INVALID; | ||
89 | return pte; | ||
90 | } | ||
11 | 91 | ||
12 | void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | 92 | void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, |
13 | pte_t *pteptr, pte_t pteval) | 93 | pte_t *ptep, pte_t pte) |
14 | { | 94 | { |
15 | pmd_t *pmdp = (pmd_t *) pteptr; | 95 | pmd_t pmd; |
16 | unsigned long mask; | ||
17 | 96 | ||
97 | pmd = __pte_to_pmd(pte); | ||
18 | if (!MACHINE_HAS_HPAGE) { | 98 | if (!MACHINE_HAS_HPAGE) { |
19 | pteptr = (pte_t *) pte_page(pteval)[1].index; | 99 | pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; |
20 | mask = pte_val(pteval) & | 100 | pmd_val(pmd) |= pte_page(pte)[1].index; |
21 | (_SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO); | 101 | } else |
22 | pte_val(pteval) = (_SEGMENT_ENTRY + __pa(pteptr)) | mask; | 102 | pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO; |
103 | *(pmd_t *) ptep = pmd; | ||
104 | } | ||
105 | |||
106 | pte_t huge_ptep_get(pte_t *ptep) | ||
107 | { | ||
108 | unsigned long origin; | ||
109 | pmd_t pmd; | ||
110 | |||
111 | pmd = *(pmd_t *) ptep; | ||
112 | if (!MACHINE_HAS_HPAGE && pmd_present(pmd)) { | ||
113 | origin = pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN; | ||
114 | pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; | ||
115 | pmd_val(pmd) |= *(unsigned long *) origin; | ||
23 | } | 116 | } |
117 | return __pmd_to_pte(pmd); | ||
118 | } | ||
24 | 119 | ||
25 | pmd_val(*pmdp) = pte_val(pteval); | 120 | pte_t huge_ptep_get_and_clear(struct mm_struct *mm, |
121 | unsigned long addr, pte_t *ptep) | ||
122 | { | ||
123 | pmd_t *pmdp = (pmd_t *) ptep; | ||
124 | pte_t pte = huge_ptep_get(ptep); | ||
125 | |||
126 | if (MACHINE_HAS_IDTE) | ||
127 | __pmd_idte(addr, pmdp); | ||
128 | else | ||
129 | __pmd_csp(pmdp); | ||
130 | pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY; | ||
131 | return pte; | ||
26 | } | 132 | } |
27 | 133 | ||
28 | int arch_prepare_hugepage(struct page *page) | 134 | int arch_prepare_hugepage(struct page *page) |
@@ -58,7 +164,7 @@ void arch_release_hugepage(struct page *page) | |||
58 | ptep = (pte_t *) page[1].index; | 164 | ptep = (pte_t *) page[1].index; |
59 | if (!ptep) | 165 | if (!ptep) |
60 | return; | 166 | return; |
61 | clear_table((unsigned long *) ptep, _PAGE_TYPE_EMPTY, | 167 | clear_table((unsigned long *) ptep, _PAGE_INVALID, |
62 | PTRS_PER_PTE * sizeof(pte_t)); | 168 | PTRS_PER_PTE * sizeof(pte_t)); |
63 | page_table_free(&init_mm, (unsigned long *) ptep); | 169 | page_table_free(&init_mm, (unsigned long *) ptep); |
64 | page[1].index = 0; | 170 | page[1].index = 0; |