to_vmem_altmap() needs to return valid results until
arch_remove_memory() completes. It also needs to be valid for any pfn
in a section regardless of whether that pfn maps to data. This escape
was a result of a bug in the unit test.
The signature of this bug is that free_pagetable() fails to retrieve a
vmem_altmap and goes off into the weeds:
BUG: unable to handle kernel NULL pointer dereference at (null)
IP: [<
ffffffff811d2629>] get_pfnblock_flags_mask+0x49/0x60
[..]
Call Trace:
[<
ffffffff811d3477>] free_hot_cold_page+0x97/0x1d0
[<
ffffffff811d367a>] __free_pages+0x2a/0x40
[<
ffffffff8191e669>] free_pagetable+0x8c/0xd4
[<
ffffffff8191ef4e>] remove_pagetable+0x37a/0x808
[<
ffffffff8191b210>] vmemmap_free+0x10/0x20
Fixes:
4b94ffdc4163 ("x86, mm: introduce vmem_altmap to augment vmemmap_populate()")
Cc: Andrew Morton <akpm@linux-foundation.org>
Reported-by: Jeff Moyer <jmoyer@redhat.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
static void pgmap_radix_release(struct resource *res)
{
static void pgmap_radix_release(struct resource *res)
{
+ resource_size_t key, align_start, align_size, align_end;
+
+ align_start = res->start & ~(SECTION_SIZE - 1);
+ align_size = ALIGN(resource_size(res), SECTION_SIZE);
+ align_end = align_start + align_size - 1;
mutex_lock(&pgmap_lock);
for (key = res->start; key <= res->end; key += SECTION_SIZE)
mutex_lock(&pgmap_lock);
for (key = res->start; key <= res->end; key += SECTION_SIZE)
percpu_ref_put(pgmap->ref);
}
percpu_ref_put(pgmap->ref);
}
- pgmap_radix_release(res);
-
/* pages are dead and unused, undo the arch mapping */
align_start = res->start & ~(SECTION_SIZE - 1);
align_size = ALIGN(resource_size(res), SECTION_SIZE);
arch_remove_memory(align_start, align_size);
/* pages are dead and unused, undo the arch mapping */
align_start = res->start & ~(SECTION_SIZE - 1);
align_size = ALIGN(resource_size(res), SECTION_SIZE);
arch_remove_memory(align_start, align_size);
+ pgmap_radix_release(res);
dev_WARN_ONCE(dev, pgmap->altmap && pgmap->altmap->alloc,
"%s: failed to free all reserved pages\n", __func__);
}
dev_WARN_ONCE(dev, pgmap->altmap && pgmap->altmap->alloc,
"%s: failed to free all reserved pages\n", __func__);
}
{
int is_ram = region_intersects(res->start, resource_size(res),
"System RAM");
{
int is_ram = region_intersects(res->start, resource_size(res),
"System RAM");
- resource_size_t key, align_start, align_size;
+ resource_size_t key, align_start, align_size, align_end;
struct dev_pagemap *pgmap;
struct page_map *page_map;
unsigned long pfn;
struct dev_pagemap *pgmap;
struct page_map *page_map;
unsigned long pfn;
mutex_lock(&pgmap_lock);
error = 0;
mutex_lock(&pgmap_lock);
error = 0;
- for (key = res->start; key <= res->end; key += SECTION_SIZE) {
+ align_start = res->start & ~(SECTION_SIZE - 1);
+ align_size = ALIGN(resource_size(res), SECTION_SIZE);
+ align_end = align_start + align_size - 1;
+ for (key = align_start; key <= align_end; key += SECTION_SIZE) {
struct dev_pagemap *dup;
rcu_read_lock();
struct dev_pagemap *dup;
rcu_read_lock();
if (nid < 0)
nid = numa_mem_id();
if (nid < 0)
nid = numa_mem_id();
- align_start = res->start & ~(SECTION_SIZE - 1);
- align_size = ALIGN(resource_size(res), SECTION_SIZE);
error = arch_add_memory(nid, align_start, align_size, true);
if (error)
goto err_add_memory;
error = arch_add_memory(nid, align_start, align_size, true);
if (error)
goto err_add_memory;