diff options
author | Jan H. Schönherr <jschoenh@amazon.de> | 2018-01-19 19:26:33 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2018-01-19 19:29:56 -0500 |
commit | 77dd66a3c67c93ab401ccc15efff25578be281fd (patch) | |
tree | 3787433794cb87a82998f80b7505da548a0c4649 /kernel | |
parent | 10a0cd6e4932b5078215b1ec2c896597eec0eff9 (diff) |
mm: Fix devm_memremap_pages() collision handling
If devm_memremap_pages() detects a collision while adding entries
to the radix-tree, we call pgmap_radix_release(). Unfortunately,
the function removes *all* entries for the range -- including the
entries that caused the collision in the first place.
Modify pgmap_radix_release() to take an additional argument to
indicate where to stop, so that only newly added entries are removed
from the tree.
Cc: <stable@vger.kernel.org>
Fixes: 9476df7d80df ("mm: introduce find_dev_pagemap()")
Signed-off-by: Jan H. Schönherr <jschoenh@amazon.de>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/memremap.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/kernel/memremap.c b/kernel/memremap.c index 4ef97525a4ff..4849be5f9b3c 100644 --- a/kernel/memremap.c +++ b/kernel/memremap.c | |||
@@ -241,13 +241,16 @@ int device_private_entry_fault(struct vm_area_struct *vma, | |||
241 | EXPORT_SYMBOL(device_private_entry_fault); | 241 | EXPORT_SYMBOL(device_private_entry_fault); |
242 | #endif /* CONFIG_DEVICE_PRIVATE */ | 242 | #endif /* CONFIG_DEVICE_PRIVATE */ |
243 | 243 | ||
244 | static void pgmap_radix_release(struct resource *res) | 244 | static void pgmap_radix_release(struct resource *res, unsigned long end_pgoff) |
245 | { | 245 | { |
246 | unsigned long pgoff, order; | 246 | unsigned long pgoff, order; |
247 | 247 | ||
248 | mutex_lock(&pgmap_lock); | 248 | mutex_lock(&pgmap_lock); |
249 | foreach_order_pgoff(res, order, pgoff) | 249 | foreach_order_pgoff(res, order, pgoff) { |
250 | if (pgoff >= end_pgoff) | ||
251 | break; | ||
250 | radix_tree_delete(&pgmap_radix, PHYS_PFN(res->start) + pgoff); | 252 | radix_tree_delete(&pgmap_radix, PHYS_PFN(res->start) + pgoff); |
253 | } | ||
251 | mutex_unlock(&pgmap_lock); | 254 | mutex_unlock(&pgmap_lock); |
252 | 255 | ||
253 | synchronize_rcu(); | 256 | synchronize_rcu(); |
@@ -302,7 +305,7 @@ static void devm_memremap_pages_release(void *data) | |||
302 | mem_hotplug_done(); | 305 | mem_hotplug_done(); |
303 | 306 | ||
304 | untrack_pfn(NULL, PHYS_PFN(align_start), align_size); | 307 | untrack_pfn(NULL, PHYS_PFN(align_start), align_size); |
305 | pgmap_radix_release(res); | 308 | pgmap_radix_release(res, -1); |
306 | dev_WARN_ONCE(dev, pgmap->altmap.alloc, | 309 | dev_WARN_ONCE(dev, pgmap->altmap.alloc, |
307 | "%s: failed to free all reserved pages\n", __func__); | 310 | "%s: failed to free all reserved pages\n", __func__); |
308 | } | 311 | } |
@@ -418,7 +421,7 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap) | |||
418 | untrack_pfn(NULL, PHYS_PFN(align_start), align_size); | 421 | untrack_pfn(NULL, PHYS_PFN(align_start), align_size); |
419 | err_pfn_remap: | 422 | err_pfn_remap: |
420 | err_radix: | 423 | err_radix: |
421 | pgmap_radix_release(res); | 424 | pgmap_radix_release(res, pgoff); |
422 | devres_free(pgmap); | 425 | devres_free(pgmap); |
423 | return ERR_PTR(error); | 426 | return ERR_PTR(error); |
424 | } | 427 | } |