diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-04-28 13:43:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-04-28 13:43:15 -0400 |
commit | 0d82044e1b7e5497c2177abd39b31e9ba27be8b7 (patch) | |
tree | 0d9f3ea99c192831c428e76c9e6789170e7fe484 | |
parent | 975a0f400f2e1b5f585fec0b8b4c5942c3b05792 (diff) | |
parent | 7a3a4d763837d3aa654cd1059030950410c04d77 (diff) |
Merge tag 'powerpc-5.1-6' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc fixes from Michael Ellerman:
"A one-liner to make our Radix MMU support depend on HUGETLB_PAGE. We
use some of the hugetlb inlines (eg. pud_huge()) when operating on the
linear mapping and if they're compiled into empty wrappers we can
corrupt memory.
Then two fixes to our VFIO IOMMU code. The first is not a regression
but fixes the locking to avoid a user-triggerable deadlock.
The second does fix a regression since rc1, and depends on the first
fix. It makes it possible to run guests with large amounts of memory
again (~256GB).
Thanks to Alexey Kardashevskiy"
* tag 'powerpc-5.1-6' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux:
powerpc/mm_iommu: Allow pinning large regions
powerpc/mm_iommu: Fix potential deadlock
powerpc/mm/radix: Make Radix require HUGETLB_PAGE
-rw-r--r-- | arch/powerpc/configs/skiroot_defconfig | 1 | ||||
-rw-r--r-- | arch/powerpc/mm/mmu_context_iommu.c | 97 | ||||
-rw-r--r-- | arch/powerpc/platforms/Kconfig.cputype | 2 |
3 files changed, 60 insertions, 40 deletions
diff --git a/arch/powerpc/configs/skiroot_defconfig b/arch/powerpc/configs/skiroot_defconfig index 5ba131c30f6b..1bcd468ab422 100644 --- a/arch/powerpc/configs/skiroot_defconfig +++ b/arch/powerpc/configs/skiroot_defconfig | |||
@@ -266,6 +266,7 @@ CONFIG_UDF_FS=m | |||
266 | CONFIG_MSDOS_FS=m | 266 | CONFIG_MSDOS_FS=m |
267 | CONFIG_VFAT_FS=m | 267 | CONFIG_VFAT_FS=m |
268 | CONFIG_PROC_KCORE=y | 268 | CONFIG_PROC_KCORE=y |
269 | CONFIG_HUGETLBFS=y | ||
269 | # CONFIG_MISC_FILESYSTEMS is not set | 270 | # CONFIG_MISC_FILESYSTEMS is not set |
270 | # CONFIG_NETWORK_FILESYSTEMS is not set | 271 | # CONFIG_NETWORK_FILESYSTEMS is not set |
271 | CONFIG_NLS=y | 272 | CONFIG_NLS=y |
diff --git a/arch/powerpc/mm/mmu_context_iommu.c b/arch/powerpc/mm/mmu_context_iommu.c index e7a9c4f6bfca..8330f135294f 100644 --- a/arch/powerpc/mm/mmu_context_iommu.c +++ b/arch/powerpc/mm/mmu_context_iommu.c | |||
@@ -95,28 +95,15 @@ static long mm_iommu_do_alloc(struct mm_struct *mm, unsigned long ua, | |||
95 | unsigned long entries, unsigned long dev_hpa, | 95 | unsigned long entries, unsigned long dev_hpa, |
96 | struct mm_iommu_table_group_mem_t **pmem) | 96 | struct mm_iommu_table_group_mem_t **pmem) |
97 | { | 97 | { |
98 | struct mm_iommu_table_group_mem_t *mem; | 98 | struct mm_iommu_table_group_mem_t *mem, *mem2; |
99 | long i, ret, locked_entries = 0; | 99 | long i, ret, locked_entries = 0, pinned = 0; |
100 | unsigned int pageshift; | 100 | unsigned int pageshift; |
101 | 101 | unsigned long entry, chunk; | |
102 | mutex_lock(&mem_list_mutex); | ||
103 | |||
104 | list_for_each_entry_rcu(mem, &mm->context.iommu_group_mem_list, | ||
105 | next) { | ||
106 | /* Overlap? */ | ||
107 | if ((mem->ua < (ua + (entries << PAGE_SHIFT))) && | ||
108 | (ua < (mem->ua + | ||
109 | (mem->entries << PAGE_SHIFT)))) { | ||
110 | ret = -EINVAL; | ||
111 | goto unlock_exit; | ||
112 | } | ||
113 | |||
114 | } | ||
115 | 102 | ||
116 | if (dev_hpa == MM_IOMMU_TABLE_INVALID_HPA) { | 103 | if (dev_hpa == MM_IOMMU_TABLE_INVALID_HPA) { |
117 | ret = mm_iommu_adjust_locked_vm(mm, entries, true); | 104 | ret = mm_iommu_adjust_locked_vm(mm, entries, true); |
118 | if (ret) | 105 | if (ret) |
119 | goto unlock_exit; | 106 | return ret; |
120 | 107 | ||
121 | locked_entries = entries; | 108 | locked_entries = entries; |
122 | } | 109 | } |
@@ -148,17 +135,27 @@ static long mm_iommu_do_alloc(struct mm_struct *mm, unsigned long ua, | |||
148 | } | 135 | } |
149 | 136 | ||
150 | down_read(&mm->mmap_sem); | 137 | down_read(&mm->mmap_sem); |
151 | ret = get_user_pages_longterm(ua, entries, FOLL_WRITE, mem->hpages, NULL); | 138 | chunk = (1UL << (PAGE_SHIFT + MAX_ORDER - 1)) / |
139 | sizeof(struct vm_area_struct *); | ||
140 | chunk = min(chunk, entries); | ||
141 | for (entry = 0; entry < entries; entry += chunk) { | ||
142 | unsigned long n = min(entries - entry, chunk); | ||
143 | |||
144 | ret = get_user_pages_longterm(ua + (entry << PAGE_SHIFT), n, | ||
145 | FOLL_WRITE, mem->hpages + entry, NULL); | ||
146 | if (ret == n) { | ||
147 | pinned += n; | ||
148 | continue; | ||
149 | } | ||
150 | if (ret > 0) | ||
151 | pinned += ret; | ||
152 | break; | ||
153 | } | ||
152 | up_read(&mm->mmap_sem); | 154 | up_read(&mm->mmap_sem); |
153 | if (ret != entries) { | 155 | if (pinned != entries) { |
154 | /* free the reference taken */ | 156 | if (!ret) |
155 | for (i = 0; i < ret; i++) | 157 | ret = -EFAULT; |
156 | put_page(mem->hpages[i]); | 158 | goto free_exit; |
157 | |||
158 | vfree(mem->hpas); | ||
159 | kfree(mem); | ||
160 | ret = -EFAULT; | ||
161 | goto unlock_exit; | ||
162 | } | 159 | } |
163 | 160 | ||
164 | pageshift = PAGE_SHIFT; | 161 | pageshift = PAGE_SHIFT; |
@@ -183,21 +180,43 @@ static long mm_iommu_do_alloc(struct mm_struct *mm, unsigned long ua, | |||
183 | } | 180 | } |
184 | 181 | ||
185 | good_exit: | 182 | good_exit: |
186 | ret = 0; | ||
187 | atomic64_set(&mem->mapped, 1); | 183 | atomic64_set(&mem->mapped, 1); |
188 | mem->used = 1; | 184 | mem->used = 1; |
189 | mem->ua = ua; | 185 | mem->ua = ua; |
190 | mem->entries = entries; | 186 | mem->entries = entries; |
191 | *pmem = mem; | ||
192 | 187 | ||
193 | list_add_rcu(&mem->next, &mm->context.iommu_group_mem_list); | 188 | mutex_lock(&mem_list_mutex); |
194 | 189 | ||
195 | unlock_exit: | 190 | list_for_each_entry_rcu(mem2, &mm->context.iommu_group_mem_list, next) { |
196 | if (locked_entries && ret) | 191 | /* Overlap? */ |
197 | mm_iommu_adjust_locked_vm(mm, locked_entries, false); | 192 | if ((mem2->ua < (ua + (entries << PAGE_SHIFT))) && |
193 | (ua < (mem2->ua + | ||
194 | (mem2->entries << PAGE_SHIFT)))) { | ||
195 | ret = -EINVAL; | ||
196 | mutex_unlock(&mem_list_mutex); | ||
197 | goto free_exit; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | list_add_rcu(&mem->next, &mm->context.iommu_group_mem_list); | ||
198 | 202 | ||
199 | mutex_unlock(&mem_list_mutex); | 203 | mutex_unlock(&mem_list_mutex); |
200 | 204 | ||
205 | *pmem = mem; | ||
206 | |||
207 | return 0; | ||
208 | |||
209 | free_exit: | ||
210 | /* free the reference taken */ | ||
211 | for (i = 0; i < pinned; i++) | ||
212 | put_page(mem->hpages[i]); | ||
213 | |||
214 | vfree(mem->hpas); | ||
215 | kfree(mem); | ||
216 | |||
217 | unlock_exit: | ||
218 | mm_iommu_adjust_locked_vm(mm, locked_entries, false); | ||
219 | |||
201 | return ret; | 220 | return ret; |
202 | } | 221 | } |
203 | 222 | ||
@@ -266,7 +285,7 @@ static void mm_iommu_release(struct mm_iommu_table_group_mem_t *mem) | |||
266 | long mm_iommu_put(struct mm_struct *mm, struct mm_iommu_table_group_mem_t *mem) | 285 | long mm_iommu_put(struct mm_struct *mm, struct mm_iommu_table_group_mem_t *mem) |
267 | { | 286 | { |
268 | long ret = 0; | 287 | long ret = 0; |
269 | unsigned long entries, dev_hpa; | 288 | unsigned long unlock_entries = 0; |
270 | 289 | ||
271 | mutex_lock(&mem_list_mutex); | 290 | mutex_lock(&mem_list_mutex); |
272 | 291 | ||
@@ -287,17 +306,17 @@ long mm_iommu_put(struct mm_struct *mm, struct mm_iommu_table_group_mem_t *mem) | |||
287 | goto unlock_exit; | 306 | goto unlock_exit; |
288 | } | 307 | } |
289 | 308 | ||
309 | if (mem->dev_hpa == MM_IOMMU_TABLE_INVALID_HPA) | ||
310 | unlock_entries = mem->entries; | ||
311 | |||
290 | /* @mapped became 0 so now mappings are disabled, release the region */ | 312 | /* @mapped became 0 so now mappings are disabled, release the region */ |
291 | entries = mem->entries; | ||
292 | dev_hpa = mem->dev_hpa; | ||
293 | mm_iommu_release(mem); | 313 | mm_iommu_release(mem); |
294 | 314 | ||
295 | if (dev_hpa == MM_IOMMU_TABLE_INVALID_HPA) | ||
296 | mm_iommu_adjust_locked_vm(mm, entries, false); | ||
297 | |||
298 | unlock_exit: | 315 | unlock_exit: |
299 | mutex_unlock(&mem_list_mutex); | 316 | mutex_unlock(&mem_list_mutex); |
300 | 317 | ||
318 | mm_iommu_adjust_locked_vm(mm, unlock_entries, false); | ||
319 | |||
301 | return ret; | 320 | return ret; |
302 | } | 321 | } |
303 | EXPORT_SYMBOL_GPL(mm_iommu_put); | 322 | EXPORT_SYMBOL_GPL(mm_iommu_put); |
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 842b2c7e156a..50cd09b4e05d 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype | |||
@@ -324,7 +324,7 @@ config ARCH_ENABLE_SPLIT_PMD_PTLOCK | |||
324 | 324 | ||
325 | config PPC_RADIX_MMU | 325 | config PPC_RADIX_MMU |
326 | bool "Radix MMU Support" | 326 | bool "Radix MMU Support" |
327 | depends on PPC_BOOK3S_64 | 327 | depends on PPC_BOOK3S_64 && HUGETLB_PAGE |
328 | select ARCH_HAS_GIGANTIC_PAGE if (MEMORY_ISOLATION && COMPACTION) || CMA | 328 | select ARCH_HAS_GIGANTIC_PAGE if (MEMORY_ISOLATION && COMPACTION) || CMA |
329 | default y | 329 | default y |
330 | help | 330 | help |