mm/memblock: fix a race between search and remove
no-map-fixup feature for dma-removed reserve region does a late memblock remove. This late removal is a slight deviation from conventional memblock flow. That leads to a race between pfn_valid->memblock_search, and memblock_remove. To fix this, use a read seqlock in memblock_search, which would ensure minimum overhead in search path. And export two APIs to let code doing late memblock remove apply write seqlock. write seqlock would ensure that search retries if the list is updated concurrently. The two exported APIs which should be called before and after modifying memblock regions, late in boot, are - memblock_region_resize_late_begin - memblock_region_resize_late_end The code to alter memblock regions should be guarded by these APIs. CRs-fixed: 967728 Change-Id: I6a10c3e980002048aafeaf829a16119848c6a099 Signed-off-by: Shiraz Hashim <shashim@codeaurora.org>
This commit is contained in:
parent
f1b365a43b
commit
2f879b6dcb
3 changed files with 47 additions and 1 deletions
|
@ -184,8 +184,10 @@ static void removed_region_fixup(struct removed_region *dma_mem, int index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* carve-out */
|
/* carve-out */
|
||||||
|
memblock_region_resize_late_begin();
|
||||||
memblock_free(dma_mem->base, dma_mem->nr_pages * PAGE_SIZE);
|
memblock_free(dma_mem->base, dma_mem->nr_pages * PAGE_SIZE);
|
||||||
memblock_remove(dma_mem->base, index * PAGE_SIZE);
|
memblock_remove(dma_mem->base, index * PAGE_SIZE);
|
||||||
|
memblock_region_resize_late_end();
|
||||||
|
|
||||||
/* clear page-mappings */
|
/* clear page-mappings */
|
||||||
base_pfn = dma_mem->base >> PAGE_SHIFT;
|
base_pfn = dma_mem->base >> PAGE_SHIFT;
|
||||||
|
|
|
@ -83,6 +83,8 @@ int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size);
|
||||||
int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size);
|
int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size);
|
||||||
int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
|
int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
|
||||||
ulong choose_memblock_flags(void);
|
ulong choose_memblock_flags(void);
|
||||||
|
void memblock_region_resize_late_begin(void);
|
||||||
|
void memblock_region_resize_late_end(void);
|
||||||
|
|
||||||
/* Low level functions */
|
/* Low level functions */
|
||||||
int memblock_add_range(struct memblock_type *type,
|
int memblock_add_range(struct memblock_type *type,
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/memblock.h>
|
#include <linux/memblock.h>
|
||||||
|
#include <linux/preempt.h>
|
||||||
|
#include <linux/seqlock.h>
|
||||||
|
|
||||||
#include <asm-generic/sections.h>
|
#include <asm-generic/sections.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
@ -31,6 +33,7 @@ static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIO
|
||||||
static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS] __initdata_memblock;
|
static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS] __initdata_memblock;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static seqcount_t memblock_seq;
|
||||||
struct memblock memblock __initdata_memblock = {
|
struct memblock memblock __initdata_memblock = {
|
||||||
.memory.regions = memblock_memory_init_regions,
|
.memory.regions = memblock_memory_init_regions,
|
||||||
.memory.cnt = 1, /* empty dummy entry */
|
.memory.cnt = 1, /* empty dummy entry */
|
||||||
|
@ -1493,7 +1496,7 @@ void __init memblock_enforce_memory_limit(phys_addr_t limit)
|
||||||
(phys_addr_t)ULLONG_MAX);
|
(phys_addr_t)ULLONG_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init_memblock memblock_search(struct memblock_type *type, phys_addr_t addr)
|
static int __init_memblock __memblock_search(struct memblock_type *type, phys_addr_t addr)
|
||||||
{
|
{
|
||||||
unsigned int left = 0, right = type->cnt;
|
unsigned int left = 0, right = type->cnt;
|
||||||
|
|
||||||
|
@ -1511,6 +1514,19 @@ static int __init_memblock memblock_search(struct memblock_type *type, phys_addr
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __init_memblock memblock_search(struct memblock_type *type, phys_addr_t addr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned long seq;
|
||||||
|
|
||||||
|
do {
|
||||||
|
seq = raw_read_seqcount_begin(&memblock_seq);
|
||||||
|
ret = __memblock_search(type, addr);
|
||||||
|
} while (unlikely(read_seqcount_retry(&memblock_seq, seq)));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int __init memblock_is_reserved(phys_addr_t addr)
|
int __init memblock_is_reserved(phys_addr_t addr)
|
||||||
{
|
{
|
||||||
return memblock_search(&memblock.reserved, addr) != -1;
|
return memblock_search(&memblock.reserved, addr) != -1;
|
||||||
|
@ -1659,6 +1675,32 @@ void __init memblock_allow_resize(void)
|
||||||
memblock_can_resize = 1;
|
memblock_can_resize = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __init_memblock memblock_resize_late(int begin)
|
||||||
|
{
|
||||||
|
static int memblock_can_resize_old;
|
||||||
|
|
||||||
|
if (begin) {
|
||||||
|
preempt_disable();
|
||||||
|
memblock_can_resize_old = memblock_can_resize;
|
||||||
|
memblock_can_resize = 0;
|
||||||
|
raw_write_seqcount_begin(&memblock_seq);
|
||||||
|
} else {
|
||||||
|
raw_write_seqcount_end(&memblock_seq);
|
||||||
|
memblock_can_resize = memblock_can_resize_old;
|
||||||
|
preempt_enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __init_memblock memblock_region_resize_late_begin(void)
|
||||||
|
{
|
||||||
|
memblock_resize_late(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __init_memblock memblock_region_resize_late_end(void)
|
||||||
|
{
|
||||||
|
memblock_resize_late(0);
|
||||||
|
}
|
||||||
|
|
||||||
static int __init early_memblock(char *p)
|
static int __init early_memblock(char *p)
|
||||||
{
|
{
|
||||||
if (p && strstr(p, "debug"))
|
if (p && strstr(p, "debug"))
|
||||||
|
|
Loading…
Add table
Reference in a new issue