diff options
Diffstat (limited to 'arch/s390/mm/pgalloc.c')
-rw-r--r-- | arch/s390/mm/pgalloc.c | 85 |
1 files changed, 28 insertions, 57 deletions
diff --git a/arch/s390/mm/pgalloc.c b/arch/s390/mm/pgalloc.c index f6c3de26cda8..e8b5962ac12a 100644 --- a/arch/s390/mm/pgalloc.c +++ b/arch/s390/mm/pgalloc.c | |||
@@ -76,81 +76,52 @@ static void __crst_table_upgrade(void *arg) | |||
76 | __tlb_flush_local(); | 76 | __tlb_flush_local(); |
77 | } | 77 | } |
78 | 78 | ||
79 | int crst_table_upgrade(struct mm_struct *mm, unsigned long limit) | 79 | int crst_table_upgrade(struct mm_struct *mm) |
80 | { | 80 | { |
81 | unsigned long *table, *pgd; | 81 | unsigned long *table, *pgd; |
82 | unsigned long entry; | ||
83 | int flush; | ||
84 | 82 | ||
85 | BUG_ON(limit > TASK_MAX_SIZE); | 83 | /* upgrade should only happen from 3 to 4 levels */ |
86 | flush = 0; | 84 | BUG_ON(mm->context.asce_limit != (1UL << 42)); |
87 | repeat: | 85 | |
88 | table = crst_table_alloc(mm); | 86 | table = crst_table_alloc(mm); |
89 | if (!table) | 87 | if (!table) |
90 | return -ENOMEM; | 88 | return -ENOMEM; |
89 | |||
91 | spin_lock_bh(&mm->page_table_lock); | 90 | spin_lock_bh(&mm->page_table_lock); |
92 | if (mm->context.asce_limit < limit) { | 91 | pgd = (unsigned long *) mm->pgd; |
93 | pgd = (unsigned long *) mm->pgd; | 92 | crst_table_init(table, _REGION2_ENTRY_EMPTY); |
94 | if (mm->context.asce_limit <= (1UL << 31)) { | 93 | pgd_populate(mm, (pgd_t *) table, (pud_t *) pgd); |
95 | entry = _REGION3_ENTRY_EMPTY; | 94 | mm->pgd = (pgd_t *) table; |
96 | mm->context.asce_limit = 1UL << 42; | 95 | mm->context.asce_limit = 1UL << 53; |
97 | mm->context.asce_bits = _ASCE_TABLE_LENGTH | | 96 | mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH | |
98 | _ASCE_USER_BITS | | 97 | _ASCE_USER_BITS | _ASCE_TYPE_REGION2; |
99 | _ASCE_TYPE_REGION3; | 98 | mm->task_size = mm->context.asce_limit; |
100 | } else { | ||
101 | entry = _REGION2_ENTRY_EMPTY; | ||
102 | mm->context.asce_limit = 1UL << 53; | ||
103 | mm->context.asce_bits = _ASCE_TABLE_LENGTH | | ||
104 | _ASCE_USER_BITS | | ||
105 | _ASCE_TYPE_REGION2; | ||
106 | } | ||
107 | crst_table_init(table, entry); | ||
108 | pgd_populate(mm, (pgd_t *) table, (pud_t *) pgd); | ||
109 | mm->pgd = (pgd_t *) table; | ||
110 | mm->task_size = mm->context.asce_limit; | ||
111 | table = NULL; | ||
112 | flush = 1; | ||
113 | } | ||
114 | spin_unlock_bh(&mm->page_table_lock); | 99 | spin_unlock_bh(&mm->page_table_lock); |
115 | if (table) | 100 | |
116 | crst_table_free(mm, table); | 101 | on_each_cpu(__crst_table_upgrade, mm, 0); |
117 | if (mm->context.asce_limit < limit) | ||
118 | goto repeat; | ||
119 | if (flush) | ||
120 | on_each_cpu(__crst_table_upgrade, mm, 0); | ||
121 | return 0; | 102 | return 0; |
122 | } | 103 | } |
123 | 104 | ||
124 | void crst_table_downgrade(struct mm_struct *mm, unsigned long limit) | 105 | void crst_table_downgrade(struct mm_struct *mm) |
125 | { | 106 | { |
126 | pgd_t *pgd; | 107 | pgd_t *pgd; |
127 | 108 | ||
109 | /* downgrade should only happen from 3 to 2 levels (compat only) */ | ||
110 | BUG_ON(mm->context.asce_limit != (1UL << 42)); | ||
111 | |||
128 | if (current->active_mm == mm) { | 112 | if (current->active_mm == mm) { |
129 | clear_user_asce(); | 113 | clear_user_asce(); |
130 | __tlb_flush_mm(mm); | 114 | __tlb_flush_mm(mm); |
131 | } | 115 | } |
132 | while (mm->context.asce_limit > limit) { | 116 | |
133 | pgd = mm->pgd; | 117 | pgd = mm->pgd; |
134 | switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) { | 118 | mm->pgd = (pgd_t *) (pgd_val(*pgd) & _REGION_ENTRY_ORIGIN); |
135 | case _REGION_ENTRY_TYPE_R2: | 119 | mm->context.asce_limit = 1UL << 31; |
136 | mm->context.asce_limit = 1UL << 42; | 120 | mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH | |
137 | mm->context.asce_bits = _ASCE_TABLE_LENGTH | | 121 | _ASCE_USER_BITS | _ASCE_TYPE_SEGMENT; |
138 | _ASCE_USER_BITS | | 122 | mm->task_size = mm->context.asce_limit; |
139 | _ASCE_TYPE_REGION3; | 123 | crst_table_free(mm, (unsigned long *) pgd); |
140 | break; | 124 | |
141 | case _REGION_ENTRY_TYPE_R3: | ||
142 | mm->context.asce_limit = 1UL << 31; | ||
143 | mm->context.asce_bits = _ASCE_TABLE_LENGTH | | ||
144 | _ASCE_USER_BITS | | ||
145 | _ASCE_TYPE_SEGMENT; | ||
146 | break; | ||
147 | default: | ||
148 | BUG(); | ||
149 | } | ||
150 | mm->pgd = (pgd_t *) (pgd_val(*pgd) & _REGION_ENTRY_ORIGIN); | ||
151 | mm->task_size = mm->context.asce_limit; | ||
152 | crst_table_free(mm, (unsigned long *) pgd); | ||
153 | } | ||
154 | if (current->active_mm == mm) | 125 | if (current->active_mm == mm) |
155 | set_user_asce(mm); | 126 | set_user_asce(mm); |
156 | } | 127 | } |