summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShiraz Hashim <shashim@codeaurora.org>2016-04-16 11:43:02 +0530
committerKyle Yan <kyan@codeaurora.org>2016-05-31 15:26:50 -0700
commit9345b8f94bfd0df479fafdbe3f1c30a3b43aeee3 (patch)
tree43b40f4a18ff7fe0154a8186372fedc0ce1b2bc2
parentaf6c02b830fc2e001f3011f0f8244565eb0cc976 (diff)
mm/memblock: disable local irqs while late memblock changes
There is a possibility of deadlock while doing late memblock configuration as only preemption is disabled and irq can be serviced while seqlock is held and in turn memblock_is_memory can be called from irq context thus trying to claim seqlock again. Following call stack was observed, [<c02136d4>] memblock_search+0x1c [<c021487c>] memblock_is_memory+0x10 [<c01e4684>] free_kmem_pages+0x44 [<c0121c04>] free_task+0x28 [<c0178b30>] rcu_process_callbacks+0x488 [<c0127e30>] __do_softirq+0x150 [<c0128284>] irq_exit+0x84 [<c010c11c>] handle_IPI+0x12c [<c0100588>] gic_handle_irq+0x70 [<c0e9efc0>] __irq_svc+0x40 [<c0214a8c>] memblock_region_resize_late_end+0xc [<c057010c>] removed_alloc+0x110 [<c04ab2c4>] pil_boot+0x2b0 [<c04b7700>] __subsystem_get+0xe0 [<c04b79cc>] subsys_device_open+0x74 [<c0229f20>] chrdev_open+0x12c [<c02246e4>] do_dentry_open+0x280 [<c0232698>] do_last+0x9a4 [<c0232b8c>] path_openat+0x23c [<c0233bf0>] do_filp_open+0x2c Fix it by disabling irqs during late memblock configuration. It is a one time operation which changes memblock related data structures and doesn't carry performance impact. CRs-Fixed: 1003890 Change-Id: I3ff1894f0c80580920b1971cda357915665b5054 Signed-off-by: Shiraz Hashim <shashim@codeaurora.org>
-rw-r--r--drivers/base/dma-removed.c5
-rw-r--r--include/linux/memblock.h4
-rw-r--r--mm/memblock.c16
3 files changed, 16 insertions, 9 deletions
diff --git a/drivers/base/dma-removed.c b/drivers/base/dma-removed.c
index 4482deaab0f2..87c743995a50 100644
--- a/drivers/base/dma-removed.c
+++ b/drivers/base/dma-removed.c
@@ -181,15 +181,16 @@ static void removed_region_fixup(struct removed_region *dma_mem, int index)
{
unsigned long fixup_size;
unsigned long base_pfn;
+ unsigned long flags;
if (index > dma_mem->nr_pages)
return;
/* carve-out */
- memblock_region_resize_late_begin();
+ flags = memblock_region_resize_late_begin();
memblock_free(dma_mem->base, dma_mem->nr_pages * PAGE_SIZE);
memblock_remove(dma_mem->base, index * PAGE_SIZE);
- memblock_region_resize_late_end();
+ memblock_region_resize_late_end(flags);
/* clear page-mappings */
base_pfn = dma_mem->base >> PAGE_SHIFT;
diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index 42b40345119f..3d28c3a18b95 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -83,8 +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_mark_mirror(phys_addr_t base, phys_addr_t size);
ulong choose_memblock_flags(void);
-void memblock_region_resize_late_begin(void);
-void memblock_region_resize_late_end(void);
+unsigned long memblock_region_resize_late_begin(void);
+void memblock_region_resize_late_end(unsigned long);
/* Low level functions */
int memblock_add_range(struct memblock_type *type,
diff --git a/mm/memblock.c b/mm/memblock.c
index bdeb22faafff..7f0a860a357e 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -21,6 +21,7 @@
#include <linux/memblock.h>
#include <linux/preempt.h>
#include <linux/seqlock.h>
+#include <linux/irqflags.h>
#include <asm-generic/sections.h>
#include <linux/io.h>
@@ -1675,30 +1676,35 @@ void __init memblock_allow_resize(void)
memblock_can_resize = 1;
}
-static void __init_memblock memblock_resize_late(int begin)
+static unsigned long __init_memblock
+memblock_resize_late(int begin, unsigned long flags)
{
static int memblock_can_resize_old;
if (begin) {
preempt_disable();
+ local_irq_save(flags);
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;
+ local_irq_restore(flags);
preempt_enable();
}
+
+ return flags;
}
-void __init_memblock memblock_region_resize_late_begin(void)
+unsigned long __init_memblock memblock_region_resize_late_begin(void)
{
- memblock_resize_late(1);
+ return memblock_resize_late(1, 0);
}
-void __init_memblock memblock_region_resize_late_end(void)
+void __init_memblock memblock_region_resize_late_end(unsigned long flags)
{
- memblock_resize_late(0);
+ memblock_resize_late(0, flags);
}
static int __init early_memblock(char *p)