Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3eaf6e6e authored by Shiraz Hashim's avatar Shiraz Hashim Committed by Patrick Daly
Browse files

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: default avatarShiraz Hashim <shashim@codeaurora.org>
Signed-off-by: default avatarPatrick Daly <pdaly@codeaurora.org>
parent ba724788
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -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;
+2 −2
Original line number Diff line number Diff line
@@ -93,8 +93,8 @@ 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_nomap(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,
+11 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/memblock.h>
#include <linux/preempt.h>
#include <linux/seqlock.h>
#include <linux/irqflags.h>

#include <asm/sections.h>
#include <linux/io.h>
@@ -1734,30 +1735,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)