diff options
author | Shiraz Hashim <shashim@codeaurora.org> | 2016-04-16 11:43:02 +0530 |
---|---|---|
committer | Kyle Yan <kyan@codeaurora.org> | 2016-05-31 15:26:50 -0700 |
commit | 9345b8f94bfd0df479fafdbe3f1c30a3b43aeee3 (patch) | |
tree | 43b40f4a18ff7fe0154a8186372fedc0ce1b2bc2 /mm | |
parent | af6c02b830fc2e001f3011f0f8244565eb0cc976 (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>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memblock.c | 16 |
1 files changed, 11 insertions, 5 deletions
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) |