diff options
author | Dave Jiang <dave.jiang@intel.com> | 2018-07-26 19:37:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-07-26 22:38:03 -0400 |
commit | 15d36fecd0bdc7510b70a0e5ec6671140b3fce0c (patch) | |
tree | d0fed717ca47e0337028141b5c5860b3f0c57a4c | |
parent | 03758dbbe2b5c0627b361ad5b4a2c60f9964ccde (diff) |
mm: disallow mappings that conflict for devm_memremap_pages()
When pmem namespaces created are smaller than section size, this can
cause an issue during removal and gpf was observed:
general protection fault: 0000 1 SMP PTI
CPU: 36 PID: 3941 Comm: ndctl Tainted: G W 4.14.28-1.el7uek.x86_64 #2
task: ffff88acda150000 task.stack: ffffc900233a4000
RIP: 0010:__put_page+0x56/0x79
Call Trace:
devm_memremap_pages_release+0x155/0x23a
release_nodes+0x21e/0x260
devres_release_all+0x3c/0x48
device_release_driver_internal+0x15c/0x207
device_release_driver+0x12/0x14
unbind_store+0xba/0xd8
drv_attr_store+0x27/0x31
sysfs_kf_write+0x3f/0x46
kernfs_fop_write+0x10f/0x18b
__vfs_write+0x3a/0x16d
vfs_write+0xb2/0x1a1
SyS_write+0x55/0xb9
do_syscall_64+0x79/0x1ae
entry_SYSCALL_64_after_hwframe+0x3d/0x0
Add code to check whether we have a mapping already in the same section
and prevent additional mappings from being created if that is the case.
Link: http://lkml.kernel.org/r/152909478401.50143.312364396244072931.stgit@djiang5-desk3.ch.intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Robert Elliott <elliott@hpe.com>
Cc: Jeff Moyer <jmoyer@redhat.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | kernel/memremap.c | 18 |
1 files changed, 17 insertions, 1 deletions
diff --git a/kernel/memremap.c b/kernel/memremap.c index 5857267a4af5..a734b1747466 100644 --- a/kernel/memremap.c +++ b/kernel/memremap.c | |||
@@ -176,10 +176,27 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap) | |||
176 | unsigned long pfn, pgoff, order; | 176 | unsigned long pfn, pgoff, order; |
177 | pgprot_t pgprot = PAGE_KERNEL; | 177 | pgprot_t pgprot = PAGE_KERNEL; |
178 | int error, nid, is_ram; | 178 | int error, nid, is_ram; |
179 | struct dev_pagemap *conflict_pgmap; | ||
179 | 180 | ||
180 | align_start = res->start & ~(SECTION_SIZE - 1); | 181 | align_start = res->start & ~(SECTION_SIZE - 1); |
181 | align_size = ALIGN(res->start + resource_size(res), SECTION_SIZE) | 182 | align_size = ALIGN(res->start + resource_size(res), SECTION_SIZE) |
182 | - align_start; | 183 | - align_start; |
184 | align_end = align_start + align_size - 1; | ||
185 | |||
186 | conflict_pgmap = get_dev_pagemap(PHYS_PFN(align_start), NULL); | ||
187 | if (conflict_pgmap) { | ||
188 | dev_WARN(dev, "Conflicting mapping in same section\n"); | ||
189 | put_dev_pagemap(conflict_pgmap); | ||
190 | return ERR_PTR(-ENOMEM); | ||
191 | } | ||
192 | |||
193 | conflict_pgmap = get_dev_pagemap(PHYS_PFN(align_end), NULL); | ||
194 | if (conflict_pgmap) { | ||
195 | dev_WARN(dev, "Conflicting mapping in same section\n"); | ||
196 | put_dev_pagemap(conflict_pgmap); | ||
197 | return ERR_PTR(-ENOMEM); | ||
198 | } | ||
199 | |||
183 | is_ram = region_intersects(align_start, align_size, | 200 | is_ram = region_intersects(align_start, align_size, |
184 | IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE); | 201 | IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE); |
185 | 202 | ||
@@ -199,7 +216,6 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap) | |||
199 | 216 | ||
200 | mutex_lock(&pgmap_lock); | 217 | mutex_lock(&pgmap_lock); |
201 | error = 0; | 218 | error = 0; |
202 | align_end = align_start + align_size - 1; | ||
203 | 219 | ||
204 | foreach_order_pgoff(res, order, pgoff) { | 220 | foreach_order_pgoff(res, order, pgoff) { |
205 | error = __radix_tree_insert(&pgmap_radix, | 221 | error = __radix_tree_insert(&pgmap_radix, |