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:
Jiacheng Zheng
2019-12-18 16:34:14 +08:00
parent 9c34f5b721
commit 9e24634404
3 changed files with 134 additions and 0 deletions

View File

@@ -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
};

View File

@@ -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 */

View File

@@ -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)