|
@@ -15,15 +15,26 @@
|
|
|
#include <linux/bitmap.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/topology.h>
|
|
|
+#include <linux/bootmem.h>
|
|
|
+#include <linux/mm.h>
|
|
|
#include <asm/proto.h>
|
|
|
#include <asm/numa.h>
|
|
|
#include <asm/e820.h>
|
|
|
|
|
|
+#if (defined(CONFIG_ACPI_HOTPLUG_MEMORY) || \
|
|
|
+ defined(CONFIG_ACPI_HOTPLUG_MEMORY_MODULE)) \
|
|
|
+ && !defined(CONFIG_MEMORY_HOTPLUG)
|
|
|
+#define RESERVE_HOTADD 1
|
|
|
+#endif
|
|
|
+
|
|
|
static struct acpi_table_slit *acpi_slit;
|
|
|
|
|
|
static nodemask_t nodes_parsed __initdata;
|
|
|
static nodemask_t nodes_found __initdata;
|
|
|
static struct bootnode nodes[MAX_NUMNODES] __initdata;
|
|
|
+static struct bootnode nodes_add[MAX_NUMNODES] __initdata;
|
|
|
+static int found_add_area __initdata;
|
|
|
+int hotadd_percent __initdata = 10;
|
|
|
static u8 pxm2node[256] = { [0 ... 255] = 0xff };
|
|
|
|
|
|
/* Too small nodes confuse the VM badly. Usually they result
|
|
@@ -71,6 +82,10 @@ static __init int conflicting_nodes(unsigned long start, unsigned long end)
|
|
|
static __init void cutoff_node(int i, unsigned long start, unsigned long end)
|
|
|
{
|
|
|
struct bootnode *nd = &nodes[i];
|
|
|
+
|
|
|
+ if (found_add_area)
|
|
|
+ return;
|
|
|
+
|
|
|
if (nd->start < start) {
|
|
|
nd->start = start;
|
|
|
if (nd->end < nd->start)
|
|
@@ -90,6 +105,8 @@ static __init void bad_srat(void)
|
|
|
acpi_numa = -1;
|
|
|
for (i = 0; i < MAX_LOCAL_APIC; i++)
|
|
|
apicid_to_node[i] = NUMA_NO_NODE;
|
|
|
+ for (i = 0; i < MAX_NUMNODES; i++)
|
|
|
+ nodes_add[i].start = nodes[i].end = 0;
|
|
|
}
|
|
|
|
|
|
static __init inline int srat_disabled(void)
|
|
@@ -155,11 +172,114 @@ acpi_numa_processor_affinity_init(struct acpi_table_processor_affinity *pa)
|
|
|
pxm, pa->apic_id, node);
|
|
|
}
|
|
|
|
|
|
+#ifdef RESERVE_HOTADD
|
|
|
+/*
|
|
|
+ * Protect against too large hotadd areas that would fill up memory.
|
|
|
+ */
|
|
|
+static int hotadd_enough_memory(struct bootnode *nd)
|
|
|
+{
|
|
|
+ static unsigned long allocated;
|
|
|
+ static unsigned long last_area_end;
|
|
|
+ unsigned long pages = (nd->end - nd->start) >> PAGE_SHIFT;
|
|
|
+ long mem = pages * sizeof(struct page);
|
|
|
+ unsigned long addr;
|
|
|
+ unsigned long allowed;
|
|
|
+ unsigned long oldpages = pages;
|
|
|
+
|
|
|
+ if (mem < 0)
|
|
|
+ return 0;
|
|
|
+ allowed = (end_pfn - e820_hole_size(0, end_pfn)) * PAGE_SIZE;
|
|
|
+ allowed = (allowed / 100) * hotadd_percent;
|
|
|
+ if (allocated + mem > allowed) {
|
|
|
+ /* Give them at least part of their hotadd memory upto hotadd_percent
|
|
|
+ It would be better to spread the limit out
|
|
|
+ over multiple hotplug areas, but that is too complicated
|
|
|
+ right now */
|
|
|
+ if (allocated >= allowed)
|
|
|
+ return 0;
|
|
|
+ pages = (allowed - allocated + mem) / sizeof(struct page);
|
|
|
+ mem = pages * sizeof(struct page);
|
|
|
+ nd->end = nd->start + pages*PAGE_SIZE;
|
|
|
+ }
|
|
|
+ /* Not completely fool proof, but a good sanity check */
|
|
|
+ addr = find_e820_area(last_area_end, end_pfn<<PAGE_SHIFT, mem);
|
|
|
+ if (addr == -1UL)
|
|
|
+ return 0;
|
|
|
+ if (pages != oldpages)
|
|
|
+ printk(KERN_NOTICE "SRAT: Hotadd area limited to %lu bytes\n",
|
|
|
+ pages << PAGE_SHIFT);
|
|
|
+ last_area_end = addr + mem;
|
|
|
+ allocated += mem;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * It is fine to add this area to the nodes data it will be used later
|
|
|
+ * This code supports one contigious hot add area per node.
|
|
|
+ */
|
|
|
+static int reserve_hotadd(int node, unsigned long start, unsigned long end)
|
|
|
+{
|
|
|
+ unsigned long s_pfn = start >> PAGE_SHIFT;
|
|
|
+ unsigned long e_pfn = end >> PAGE_SHIFT;
|
|
|
+ int changed = 0;
|
|
|
+ struct bootnode *nd = &nodes_add[node];
|
|
|
+
|
|
|
+ /* I had some trouble with strange memory hotadd regions breaking
|
|
|
+ the boot. Be very strict here and reject anything unexpected.
|
|
|
+ If you want working memory hotadd write correct SRATs.
|
|
|
+
|
|
|
+ The node size check is a basic sanity check to guard against
|
|
|
+ mistakes */
|
|
|
+ if ((signed long)(end - start) < NODE_MIN_SIZE) {
|
|
|
+ printk(KERN_ERR "SRAT: Hotplug area too small\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* This check might be a bit too strict, but I'm keeping it for now. */
|
|
|
+ if (e820_hole_size(s_pfn, e_pfn) != e_pfn - s_pfn) {
|
|
|
+ printk(KERN_ERR "SRAT: Hotplug area has existing memory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!hotadd_enough_memory(&nodes_add[node])) {
|
|
|
+ printk(KERN_ERR "SRAT: Hotplug area too large\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Looks good */
|
|
|
+
|
|
|
+ found_add_area = 1;
|
|
|
+ if (nd->start == nd->end) {
|
|
|
+ nd->start = start;
|
|
|
+ nd->end = end;
|
|
|
+ changed = 1;
|
|
|
+ } else {
|
|
|
+ if (nd->start == end) {
|
|
|
+ nd->start = start;
|
|
|
+ changed = 1;
|
|
|
+ }
|
|
|
+ if (nd->end == start) {
|
|
|
+ nd->end = end;
|
|
|
+ changed = 1;
|
|
|
+ }
|
|
|
+ if (!changed)
|
|
|
+ printk(KERN_ERR "SRAT: Hotplug zone not continuous. Partly ignored\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((nd->end >> PAGE_SHIFT) > end_pfn)
|
|
|
+ end_pfn = nd->end >> PAGE_SHIFT;
|
|
|
+
|
|
|
+ if (changed)
|
|
|
+ printk(KERN_INFO "SRAT: hot plug zone found %Lx - %Lx\n", nd->start, nd->end);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
|
|
|
void __init
|
|
|
acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma)
|
|
|
{
|
|
|
- struct bootnode *nd;
|
|
|
+ struct bootnode *nd, oldnode;
|
|
|
unsigned long start, end;
|
|
|
int node, pxm;
|
|
|
int i;
|
|
@@ -172,6 +292,8 @@ acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma)
|
|
|
}
|
|
|
if (ma->flags.enabled == 0)
|
|
|
return;
|
|
|
+ if (ma->flags.hot_pluggable && hotadd_percent == 0)
|
|
|
+ return;
|
|
|
start = ma->base_addr_lo | ((u64)ma->base_addr_hi << 32);
|
|
|
end = start + (ma->length_lo | ((u64)ma->length_hi << 32));
|
|
|
pxm = ma->proximity_domain;
|
|
@@ -181,10 +303,6 @@ acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma)
|
|
|
bad_srat();
|
|
|
return;
|
|
|
}
|
|
|
- /* It is fine to add this area to the nodes data it will be used later*/
|
|
|
- if (ma->flags.hot_pluggable == 1)
|
|
|
- printk(KERN_INFO "SRAT: hot plug zone found %lx - %lx \n",
|
|
|
- start, end);
|
|
|
i = conflicting_nodes(start, end);
|
|
|
if (i == node) {
|
|
|
printk(KERN_WARNING
|
|
@@ -199,6 +317,7 @@ acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma)
|
|
|
return;
|
|
|
}
|
|
|
nd = &nodes[node];
|
|
|
+ oldnode = *nd;
|
|
|
if (!node_test_and_set(node, nodes_parsed)) {
|
|
|
nd->start = start;
|
|
|
nd->end = end;
|
|
@@ -208,8 +327,19 @@ acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma)
|
|
|
if (nd->end < end)
|
|
|
nd->end = end;
|
|
|
}
|
|
|
+
|
|
|
printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm,
|
|
|
nd->start, nd->end);
|
|
|
+
|
|
|
+#ifdef RESERVE_HOTADD
|
|
|
+ if (ma->flags.hot_pluggable && reserve_hotadd(node, start, end) < 0) {
|
|
|
+ /* Ignore hotadd region. Undo damage */
|
|
|
+ printk(KERN_NOTICE "SRAT: Hotplug region ignored\n");
|
|
|
+ *nd = oldnode;
|
|
|
+ if ((nd->start | nd->end) == 0)
|
|
|
+ node_clear(node, nodes_parsed);
|
|
|
+ }
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
/* Sanity check to catch more bad SRATs (they are amazingly common).
|
|
@@ -225,6 +355,9 @@ static int nodes_cover_memory(void)
|
|
|
unsigned long e = nodes[i].end >> PAGE_SHIFT;
|
|
|
pxmram += e - s;
|
|
|
pxmram -= e820_hole_size(s, e);
|
|
|
+ pxmram -= nodes_add[i].end - nodes_add[i].start;
|
|
|
+ if ((long)pxmram < 0)
|
|
|
+ pxmram = 0;
|
|
|
}
|
|
|
|
|
|
e820ram = end_pfn - e820_hole_size(0, end_pfn);
|
|
@@ -258,7 +391,7 @@ int __init acpi_scan_nodes(unsigned long start, unsigned long end)
|
|
|
|
|
|
/* First clean up the node list */
|
|
|
for (i = 0; i < MAX_NUMNODES; i++) {
|
|
|
- cutoff_node(i, start, end);
|
|
|
+ cutoff_node(i, start, end);
|
|
|
if ((nodes[i].end - nodes[i].start) < NODE_MIN_SIZE)
|
|
|
unparse_node(i);
|
|
|
}
|
|
@@ -303,6 +436,25 @@ static int node_to_pxm(int n)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+void __init srat_reserve_add_area(int nodeid)
|
|
|
+{
|
|
|
+ if (found_add_area && nodes_add[nodeid].end) {
|
|
|
+ u64 total_mb;
|
|
|
+
|
|
|
+ printk(KERN_INFO "SRAT: Reserving hot-add memory space "
|
|
|
+ "for node %d at %Lx-%Lx\n",
|
|
|
+ nodeid, nodes_add[nodeid].start, nodes_add[nodeid].end);
|
|
|
+ total_mb = (nodes_add[nodeid].end - nodes_add[nodeid].start)
|
|
|
+ >> PAGE_SHIFT;
|
|
|
+ total_mb *= sizeof(struct page);
|
|
|
+ total_mb >>= 20;
|
|
|
+ printk(KERN_INFO "SRAT: This will cost you %Lu MB of "
|
|
|
+ "pre-allocated memory.\n", (unsigned long long)total_mb);
|
|
|
+ reserve_bootmem_node(NODE_DATA(nodeid), nodes_add[nodeid].start,
|
|
|
+ nodes_add[nodeid].end - nodes_add[nodeid].start);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
int __node_distance(int a, int b)
|
|
|
{
|
|
|
int index;
|