mm: memblock: dynamically calculate memory hotplug range
We need to support memory hotplug, but the range reserved for hotplug changes for different ddr size. Calculating range for each ddr size manually and configuring it in cmdline will cause us to release separate kernel image for each possible ddr size. This is not acceptable, so we need to calculate it dynamically during booting. We define two new early params. "no_hotplug_area" tells us which area is not suitable for memory hotplug so we should skip those areas when calculating. "dyn_memhotplug" tells us we need this feature(dynamically memory hotplug area calculating). We find aligned memory blocks in memory layout automatically except those in the no_hotplug_area and we expose the areas we get through sysfs to userspace so some service can hotplug those areas back after system fully boots up. Change-Id: I72a3685bef38acc391e2ef36fc7b2517223bcde3 Signed-off-by: Jiacheng Zheng <jiaczhen@codeaurora.org>
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/memblock.h>
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/uaccess.h>
|
||||
@@ -494,6 +495,18 @@ static ssize_t show_allocated_bytes(struct device *dev,
|
||||
|
||||
return snprintf(buf, 100, "%lu\n", used);
|
||||
}
|
||||
|
||||
static ssize_t show_aligned_blocks_addr(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return memblock_dump_aligned_blocks_addr(buf);
|
||||
}
|
||||
|
||||
static ssize_t show_aligned_blocks_num(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return memblock_dump_aligned_blocks_num(buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static DEVICE_ATTR(phys_index, 0444, show_mem_start_phys_index, NULL);
|
||||
@@ -502,6 +515,8 @@ static DEVICE_ATTR(phys_device, 0444, show_phys_device, NULL);
|
||||
static DEVICE_ATTR(removable, 0444, show_mem_removable, NULL);
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
static DEVICE_ATTR(allocated_bytes, 0444, show_allocated_bytes, NULL);
|
||||
static DEVICE_ATTR(aligned_blocks_addr, 0444, show_aligned_blocks_addr, NULL);
|
||||
static DEVICE_ATTR(aligned_blocks_num, 0444, show_aligned_blocks_num, NULL);
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -886,6 +901,10 @@ static struct attribute *memory_root_attrs[] = {
|
||||
|
||||
&dev_attr_block_size_bytes.attr,
|
||||
&dev_attr_auto_online_blocks.attr,
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
&dev_attr_aligned_blocks_addr.attr,
|
||||
&dev_attr_aligned_blocks_num.attr,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
@@ -89,6 +89,10 @@ 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);
|
||||
int memblock_clear_nomap(phys_addr_t base, phys_addr_t size);
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
int memblock_dump_aligned_blocks_addr(char *buf);
|
||||
int memblock_dump_aligned_blocks_num(char *buf);
|
||||
#endif
|
||||
ulong choose_memblock_flags(void);
|
||||
|
||||
/* Low level functions */
|
||||
|
||||
111
mm/memblock.c
111
mm/memblock.c
@@ -19,6 +19,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/memory.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include <linux/io.h>
|
||||
@@ -1777,6 +1778,116 @@ static int __init early_memblock(char *p)
|
||||
}
|
||||
early_param("memblock", early_memblock);
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
static phys_addr_t no_hotplug_area[8];
|
||||
static phys_addr_t aligned_blocks[32];
|
||||
|
||||
static int __init early_no_hotplug_area(char *p)
|
||||
{
|
||||
phys_addr_t base, size;
|
||||
int idx = 0;
|
||||
char *endp = p;
|
||||
|
||||
while (1) {
|
||||
base = memparse(endp, &endp);
|
||||
if (base && (*endp == ',')) {
|
||||
size = memparse(endp + 1, &endp);
|
||||
if (size) {
|
||||
no_hotplug_area[idx++] = base;
|
||||
no_hotplug_area[idx++] = base+size;
|
||||
|
||||
if ((*endp == ';') && (idx <= 6))
|
||||
endp++;
|
||||
else
|
||||
break;
|
||||
} else
|
||||
break;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
early_param("no_hotplug_area", early_no_hotplug_area);
|
||||
|
||||
static bool __init memblock_in_no_hotplug_area(phys_addr_t addr)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
while (idx < 8) {
|
||||
if (!no_hotplug_area[idx])
|
||||
break;
|
||||
|
||||
if ((addr + MIN_MEMORY_BLOCK_SIZE <= no_hotplug_area[idx])
|
||||
|| (addr >= no_hotplug_area[idx+1])) {
|
||||
idx += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __init early_dyn_memhotplug(char *p)
|
||||
{
|
||||
int idx = 0;
|
||||
phys_addr_t addr, rgn_end;
|
||||
struct memblock_region *rgn;
|
||||
int blk = 0;
|
||||
|
||||
while ((idx++) < memblock.memory.cnt) {
|
||||
rgn = &memblock.memory.regions[idx];
|
||||
addr = ALIGN(rgn->base, MIN_MEMORY_BLOCK_SIZE);
|
||||
rgn_end = rgn->base + rgn->size;
|
||||
while (addr + MIN_MEMORY_BLOCK_SIZE <= rgn_end) {
|
||||
if (!memblock_in_no_hotplug_area(addr)) {
|
||||
aligned_blocks[blk++] = addr;
|
||||
memblock_remove(addr, MIN_MEMORY_BLOCK_SIZE);
|
||||
}
|
||||
addr += MIN_MEMORY_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
early_param("dyn_memhotplug", early_dyn_memhotplug);
|
||||
|
||||
int memblock_dump_aligned_blocks_addr(char *buf)
|
||||
{
|
||||
int idx = 0;
|
||||
int size = 0;
|
||||
|
||||
if (aligned_blocks[idx]) {
|
||||
size += snprintf(buf+size, 32, "0x%llx", aligned_blocks[idx]);
|
||||
idx++;
|
||||
}
|
||||
|
||||
while (aligned_blocks[idx]) {
|
||||
size += snprintf(buf+size, 32, ",0x%llx", aligned_blocks[idx]);
|
||||
idx++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int memblock_dump_aligned_blocks_num(char *buf)
|
||||
{
|
||||
int idx = 0;
|
||||
int size = 0;
|
||||
|
||||
if (aligned_blocks[idx]) {
|
||||
size += snprintf(buf+size, 16, "%d",
|
||||
(int)(aligned_blocks[idx] >> SECTION_SIZE_BITS));
|
||||
idx++;
|
||||
}
|
||||
|
||||
while (aligned_blocks[idx]) {
|
||||
size += snprintf(buf+size, 16, ",%d",
|
||||
(int)(aligned_blocks[idx] >> SECTION_SIZE_BITS));
|
||||
idx++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_ARCH_DISCARD_MEMBLOCK)
|
||||
|
||||
static int memblock_debug_show(struct seq_file *m, void *private)
|
||||
|
||||
Reference in New Issue
Block a user