Skip to contentSkip to conversation

Find mirrored memory, use for boot time allocations

PATCH In review Tony Luck 4 participants 12 messages May 8, 2015 – Jun 1, 2015

Subsystems (8)
lore
Share & tools
lore

Tony Luck

author
#0 lore raw

v2 cover Find mirrored memory, use for boot time allocations

Some high end Intel Xeon systems report uncorrectable memory errors as a recoverable machine check. Linux has included code for some time to process these and just signal the affected processes (or even recover completely if the error was in a read only page that can be replaced by reading from disk).

But we have no recovery path for errors encountered during kernel code execution. Except for some very specific cases were are unlikely to ever be able to recover.

Enter memory mirroring. Actually 3rd generation of memory mirroing.

Gen1: All memory is mirrored
Pro: No s/w enabling - h/w just gets good data from other side of the mirror
Con: Halves effective memory capacity available to OS/applications
Gen2: Partial memory mirror - just mirror memory begind some memory controllers
Pro: Keep more of the capacity
Con: Nightmare to enable. Have to choose between allocating from
mirrored memory for safety vs. NUMA local memory for performance
Gen3: Address range partial memory mirror - some mirror on each memory controller
Pro: Can tune the amount of mirror and keep NUMA performance
Con: I have to write memory management code to implement

The current plan is just to use mirrored memory for kernel allocations. This has been broken into two phases:
1) This patch series - find the mirrored memory, use it for boot time allocations
2) Wade into mm/page_alloc.c and define a ZONE_MIRROR to pick up the unused mirrored memory from mm/memblock.c and only give it out to select kernel allocations (this is still being scoped because page_alloc.c is scary).

Tony Luck (3):
mm/memblock: Add extra "flags" to memblock to allow selection of
memory based on attribute
mm/memblock: Allocate boot time data structures from mirrored memory x86, mirror: x86 enabling - find mirrored memory ranges

 arch/s390/kernel/crash_dump.c |   5 +-
 arch/sparc/mm/init_64.c       |   6 ++-
 arch/x86/kernel/check.c       |   3 +-
 arch/x86/kernel/e820.c        |   3 +-
 arch/x86/kernel/setup.c       |   3 ++
 arch/x86/mm/init_32.c         |   2 +-
 arch/x86/platform/efi/efi.c   |  21 ++++++++
 include/linux/efi.h           |   3 ++
 include/linux/memblock.h      |  49 +++++++++++------
 mm/cma.c                      |   6 ++-
 mm/memblock.c                 | 123 +++++++++++++++++++++++++++++++++---------
 mm/memtest.c                  |   3 +-
 mm/nobootmem.c                |  14 ++++-
 13 files changed, 188 insertions(+), 53 deletions(-)
Signature · 1 line
2.1.4
author
#1 lore raw

PATCH v2 1/3 mm/memblock: Add extra "flags" to memblock to allow selection of memory based on attribute

No functional changes

---

v1->v2:
Use name "flags" everywhere instead of mix of "flag" and "flags" Change type of flags from u32 to ulong (consistent with memblock_region.flags) Use enum for values of flags defining MEMBLOCK_NONE = 0 and use as argument

 arch/s390/kernel/crash_dump.c |  5 ++--
 arch/sparc/mm/init_64.c       |  6 +++--
 arch/x86/kernel/check.c       |  3 ++-
 arch/x86/kernel/e820.c        |  3 ++-
 arch/x86/mm/init_32.c         |  2 +-
 include/linux/memblock.h      | 41 ++++++++++++++++++++------------
 mm/cma.c                      |  6 +++--
 mm/memblock.c                 | 55 +++++++++++++++++++++++++++----------------
 mm/memtest.c                  |  3 ++-
 mm/nobootmem.c                |  6 +++--
 10 files changed, 83 insertions(+), 47 deletions(-)
arch/s390/kernel/crash_dump.c +3 -2
index 9f73c8059022..120a18283483 100644
@@ -33,11 +33,12 @@ static struct memblock_type oldmem_type = {
 };
 
 #define for_each_dump_mem_range(i, nid, p_start, p_end, p_nid)		\
-	for (i = 0, __next_mem_range(&i, nid, &memblock.physmem,	\
+	for (i = 0, __next_mem_range(&i, nid, MEMBLOCK_NONE,		\
+				     &memblock.physmem,			\
 				     &oldmem_type, p_start,		\
 				     p_end, p_nid);			\
 	     i != (u64)ULLONG_MAX;					\
-	     __next_mem_range(&i, nid, &memblock.physmem,		\
+	     __next_mem_range(&i, nid, MEMBLOCK_NONE, &memblock.physmem,\
 			      &oldmem_type,				\
 			      p_start, p_end, p_nid))
 
arch/sparc/mm/init_64.c +4 -2
index 4ca0d6ba5ec8..6f662d1d92ae 100644
@@ -1952,7 +1952,8 @@ static phys_addr_t __init available_memory(void)
 	phys_addr_t pa_start, pa_end;
 	u64 i;
 
-	for_each_free_mem_range(i, NUMA_NO_NODE, &pa_start, &pa_end, NULL)
+	for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &pa_start,
+				&pa_end, NULL)
 		available = available + (pa_end  - pa_start);
 
 	return available;
@@ -1971,7 +1972,8 @@ static void __init reduce_memory(phys_addr_t limit_ram)
 	if (limit_ram >= avail_ram)
 		return;
 
-	for_each_free_mem_range(i, NUMA_NO_NODE, &pa_start, &pa_end, NULL) {
+	for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &pa_start,
+				&pa_end, NULL) {
 		phys_addr_t region_size = pa_end - pa_start;
 		phys_addr_t clip_start = pa_start;
 
arch/x86/kernel/check.c +2 -1
index 83a7995625a6..58118e207a69 100644
@@ -91,7 +91,8 @@ void __init setup_bios_corruption_check(void)
 
 	corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
 
-	for_each_free_mem_range(i, NUMA_NO_NODE, &start, &end, NULL) {
+	for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end,
+				NULL) {
 		start = clamp_t(phys_addr_t, round_up(start, PAGE_SIZE),
 				PAGE_SIZE, corruption_check_size);
 		end = clamp_t(phys_addr_t, round_down(end, PAGE_SIZE),
arch/x86/kernel/e820.c +2 -1
index e2ce85db2283..c8dda42cb6a3 100644
@@ -1123,7 +1123,8 @@ void __init memblock_find_dma_reserve(void)
 		nr_pages += end_pfn - start_pfn;
 	}
 
-	for_each_free_mem_range(u, NUMA_NO_NODE, &start, &end, NULL) {
+	for_each_free_mem_range(u, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end,
+				NULL) {
 		start_pfn = min_t(unsigned long, PFN_UP(start), MAX_DMA_PFN);
 		end_pfn = min_t(unsigned long, PFN_DOWN(end), MAX_DMA_PFN);
 		if (start_pfn < end_pfn)
arch/x86/mm/init_32.c +1 -1
index c8140e12816a..8340e45c891a 100644
@@ -433,7 +433,7 @@ void __init add_highpages_with_active_regions(int nid,
 	phys_addr_t start, end;
 	u64 i;
 
-	for_each_free_mem_range(i, nid, &start, &end, NULL) {
+	for_each_free_mem_range(i, nid, MEMBLOCK_NONE, &start, &end, NULL) {
 		unsigned long pfn = clamp_t(unsigned long, PFN_UP(start),
 					    start_pfn, end_pfn);
 		unsigned long e_pfn = clamp_t(unsigned long, PFN_DOWN(end),
include/linux/memblock.h +26 -15

117 lines not on this page · raw message

mm/cma.c +4 -2
index 3a7a67b93394..3ba03d7ab169 100644
@@ -316,13 +316,15 @@ int __init cma_declare_contiguous(phys_addr_t base,
 		 */
 		if (base < highmem_start && limit > highmem_start) {
 			addr = memblock_alloc_range(size, alignment,
-						    highmem_start, limit);
+						    highmem_start, limit,
+						    MEMBLOCK_NONE);
 			limit = highmem_start;
 		}
 
 		if (!addr) {
 			addr = memblock_alloc_range(size, alignment, base,
-						    limit);
+						    limit,
+						    MEMBLOCK_NONE);
 			if (!addr) {
 				ret = -ENOMEM;
 				goto err;
mm/memblock.c +35 -20

184 lines not on this page · raw message

mm/memtest.c +2 -1
index 1997d934b13b..0a1cc133f6d7 100644
@@ -74,7 +74,8 @@ static void __init do_one_pass(u64 pattern, phys_addr_t start, phys_addr_t end)
 	u64 i;
 	phys_addr_t this_start, this_end;
 
-	for_each_free_mem_range(i, NUMA_NO_NODE, &this_start, &this_end, NULL) {
+	for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &this_start,
+				&this_end, NULL) {
 		this_start = clamp(this_start, start, end);
 		this_end = clamp(this_end, start, end);
 		if (this_start < this_end) {
mm/nobootmem.c +4 -2
index 90b50468333e..ad3641dcdbe7 100644
@@ -41,7 +41,8 @@ static void * __init __alloc_memory_core_early(int nid, u64 size, u64 align,
 	if (limit > memblock.current_limit)
 		limit = memblock.current_limit;
 
-	addr = memblock_find_in_range_node(size, align, goal, limit, nid);
+	addr = memblock_find_in_range_node(size, align, goal, limit, nid,
+					   MEMBLOCK_NONE);
 	if (!addr)
 		return NULL;
 
@@ -121,7 +122,8 @@ static unsigned long __init free_low_memory_core_early(void)
 
 	memblock_clear_hotplug(0, -1);
 
-	for_each_free_mem_range(i, NUMA_NO_NODE, &start, &end, NULL)
+	for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end,
+				NULL)
 		count += __free_memory_core(start, end);
 
 #ifdef CONFIG_ARCH_DISCARD_MEMBLOCK
Signature · 1 line
2.1.4
author
#2 lore raw

PATCH v2 2/3 mm/memblock: Allocate boot time data structures from mirrored memory

In reply to Tony Luck

Try to allocate all boot time kernel data structures from mirrored memory. If we run out of mirrored memory print warnings, but fall back to using non-mirrored memory to make sure that we still boot.

---

v1->v2:
Better name for memblock_has_mirror() - now choose_memblock_flags() Print phys_addr_t with %pap instead of %lld
Don't fall back by clearing flags. Use: flags &= ~MEMBLOCK_MIRROR; Keep to 80 columns

N.B just one checkpatch warning about initializing a static to 0:
+static bool system_has_some_mirror __initdata_memblock = false; This fits with existing style in the file ... but zap the " = false" if you like - I have no strong feelings about this.

 include/linux/memblock.h |  8 +++++
 mm/memblock.c            | 78 +++++++++++++++++++++++++++++++++++++++++-------
 mm/nobootmem.c           | 10 ++++++-
 3 files changed, 84 insertions(+), 12 deletions(-)
include/linux/memblock.h +8 -0
index 7aeec0cb4c27..0215ffd63069 100644
@@ -24,6 +24,7 @@
 enum {
 	MEMBLOCK_NONE		= 0x0,	/* No special request */
 	MEMBLOCK_HOTPLUG	= 0x1,	/* hotpluggable region */
+	MEMBLOCK_MIRROR		= 0x2,	/* mirrored region */
 };
 
 struct memblock_region {
@@ -78,6 +79,8 @@ int memblock_reserve(phys_addr_t base, phys_addr_t size);
 void memblock_trim_memory(phys_addr_t align);
 int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size);
 int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size);
+int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
+ulong choose_memblock_flags(void);
 
 /* Low level functions */
 int memblock_add_range(struct memblock_type *type,
@@ -160,6 +163,11 @@ static inline bool movable_node_is_enabled(void)
 }
 #endif
 
+static inline bool memblock_is_mirror(struct memblock_region *m)
+{
+	return m->flags & MEMBLOCK_MIRROR;
+}
+
 #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
 int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn,
 			    unsigned long  *end_pfn);
mm/memblock.c +67 -11

150 lines not on this page · raw message

mm/nobootmem.c +9 -1
index ad3641dcdbe7..5258386fa1be 100644
@@ -37,12 +37,20 @@ static void * __init __alloc_memory_core_early(int nid, u64 size, u64 align,
 {
 	void *ptr;
 	u64 addr;
+	ulong flags = choose_memblock_flags();
 
 	if (limit > memblock.current_limit)
 		limit = memblock.current_limit;
 
+again:
 	addr = memblock_find_in_range_node(size, align, goal, limit, nid,
-					   MEMBLOCK_NONE);
+					   flags);
+	if (!addr && (flags & MEMBLOCK_MIRROR)) {
+		flags &= ~MEMBLOCK_MIRROR;
+		pr_warn("Could not allocate %pap bytes of mirrored memory\n",
+			&size);
+		goto again;
+	}
 	if (!addr)
 		return NULL;
 
Signature · 7 lines
2.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
author
#3 lore raw

PATCH v3 2/3 mm/memblock: Allocate boot time data structures from mirrored memory

Try to allocate all boot time kernel data structures from mirrored memory. If we run out of mirrored memory print warnings, but fall back to using non-mirrored memory to make sure that we still boot.

By number of bytes, most of what we allocate at boot time is the page structures. 64 bytes per 4K page on x86_64 ... or about 1.5% of total system memory. For workloads where the bulk of memory is allocated to applications this may represent a useful improvement to system availability since 1.5% of total memory might be a third of the memory allocated to the kernel.

---

v2->v3:
Beefed up commit description saying why this change is useful even before we get run time mirror allocation.
[Parts 1 & 3 unchanged - not reposted]
v1->v2:
Better name for memblock_has_mirror() - now choose_memblock_flags() Print phys_addr_t with %pap instead of %lld
Don't fall back by clearing flags. Use: flags &= ~MEMBLOCK_MIRROR; Keep to 80 columns

 include/linux/memblock.h |  8 +++++
 mm/memblock.c            | 78 +++++++++++++++++++++++++++++++++++++++++-------
 mm/nobootmem.c           | 10 ++++++-
 3 files changed, 84 insertions(+), 12 deletions(-)
include/linux/memblock.h +8 -0
index 7aeec0cb4c27..0215ffd63069 100644
@@ -24,6 +24,7 @@
 enum {
 	MEMBLOCK_NONE		= 0x0,	/* No special request */
 	MEMBLOCK_HOTPLUG	= 0x1,	/* hotpluggable region */
+	MEMBLOCK_MIRROR		= 0x2,	/* mirrored region */
 };
 
 struct memblock_region {
@@ -78,6 +79,8 @@ int memblock_reserve(phys_addr_t base, phys_addr_t size);
 void memblock_trim_memory(phys_addr_t align);
 int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size);
 int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size);
+int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
+ulong choose_memblock_flags(void);
 
 /* Low level functions */
 int memblock_add_range(struct memblock_type *type,
@@ -160,6 +163,11 @@ static inline bool movable_node_is_enabled(void)
 }
 #endif
 
+static inline bool memblock_is_mirror(struct memblock_region *m)
+{
+	return m->flags & MEMBLOCK_MIRROR;
+}
+
 #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
 int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn,
 			    unsigned long  *end_pfn);
mm/memblock.c +67 -11

150 lines not on this page · raw message

mm/nobootmem.c +9 -1
index ad3641dcdbe7..5258386fa1be 100644
@@ -37,12 +37,20 @@ static void * __init __alloc_memory_core_early(int nid, u64 size, u64 align,
 {
 	void *ptr;
 	u64 addr;
+	ulong flags = choose_memblock_flags();
 
 	if (limit > memblock.current_limit)
 		limit = memblock.current_limit;
 
+again:
 	addr = memblock_find_in_range_node(size, align, goal, limit, nid,
-					   MEMBLOCK_NONE);
+					   flags);
+	if (!addr && (flags & MEMBLOCK_MIRROR)) {
+		flags &= ~MEMBLOCK_MIRROR;
+		pr_warn("Could not allocate %pap bytes of mirrored memory\n",
+			&size);
+		goto again;
+	}
 	if (!addr)
 		return NULL;
 
Signature · 7 lines
2.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
author
#4 lore raw

PATCH v2 3/3 x86, mirror: x86 enabling - find mirrored memory ranges

In reply to Tony Luck

UEFI GetMemoryMap() uses a new attribute bit to mark mirrored memory address ranges. See UEFI 2.5 spec pages 157-158:

http://www.uefi.org/sites/default/files/resources/UEFI%202_5.pdf

On EFI enabled systems scan the memory map and tell memblock about any mirrored ranges.

---

v1->v2:
Use u64 instead of "unsigned long long"

Just one checkpatch warning for this patch:
WARNING: line over 80 characters
#86: FILE: include/linux/efi.h:100:
+ ((u64)0x0000000000010000ULL) /* higher reliability */
But this fits the style for all the other attribute defintions (all of which are above 80 columns - one even longer than this new one)

 arch/x86/kernel/setup.c     |  3 +++
 arch/x86/platform/efi/efi.c | 21 +++++++++++++++++++++
 include/linux/efi.h         |  3 +++
 3 files changed, 27 insertions(+)
arch/x86/kernel/setup.c +3 -0
index d74ac33290ae..ac85a1775661 100644
@@ -1103,6 +1103,9 @@ void __init setup_arch(char **cmdline_p)
 	memblock_set_current_limit(ISA_END_ADDRESS);
 	memblock_x86_fill();
 
+	if (efi_enabled(EFI_BOOT))
+		efi_find_mirror();
+
 	/*
 	 * The EFI specification says that boot service code won't be called
 	 * after ExitBootServices(). This is, in fact, a lie.
arch/x86/platform/efi/efi.c +21 -0
index 02744df576d5..8b1d8dfa3c5c 100644
@@ -117,6 +117,27 @@ void efi_get_time(struct timespec *now)
 	now->tv_nsec = 0;
 }
 
+void __init efi_find_mirror(void)
+{
+	void *p;
+	u64 mirror_size = 0, total_size = 0;
+
+	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
+		efi_memory_desc_t *md = p;
+		unsigned long long start = md->phys_addr;
+		unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
+
+		total_size += size;
+		if (md->attribute & EFI_MEMORY_MORE_RELIABLE) {
+			memblock_mark_mirror(start, size);
+			mirror_size += size;
+		}
+	}
+	if (mirror_size)
+		pr_info("Memory: %lldM/%lldM mirrored memory\n",
+			mirror_size>>20, total_size>>20);
+}
+
 /*
  * Tell the kernel about the EFI memory map.  This might include
  * more than the max 128 entries that can fit in the e820 legacy
include/linux/efi.h +3 -0
index af5be0368dec..8d4efdb9dfe9 100644
@@ -96,6 +96,8 @@ typedef	struct {
 #define EFI_MEMORY_WP		((u64)0x0000000000001000ULL)	/* write-protect */
 #define EFI_MEMORY_RP		((u64)0x0000000000002000ULL)	/* read-protect */
 #define EFI_MEMORY_XP		((u64)0x0000000000004000ULL)	/* execute-protect */
+#define EFI_MEMORY_MORE_RELIABLE \
+				((u64)0x0000000000010000ULL)	/* higher reliability */
 #define EFI_MEMORY_RUNTIME	((u64)0x8000000000000000ULL)	/* range requires runtime mapping */
 #define EFI_MEMORY_DESCRIPTOR_VERSION	1
 
@@ -864,6 +866,7 @@ extern void efi_enter_virtual_mode (void);	/* switch EFI to virtual mode, if pos
 extern void efi_late_init(void);
 extern void efi_free_boot_services(void);
 extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size);
+extern void efi_find_mirror(void);
 #else
 static inline void efi_late_init(void) {}
 static inline void efi_free_boot_services(void) {}
Signature · 1 line
2.1.4
maintainer
#5 lore raw
In reply to Tony Luck
Quoted 23 lines · On Fri, 8 May 2015 09:44:21 -0700 Tony Luck <[email protected]> wrote:

Some high end Intel Xeon systems report uncorrectable memory errors as a recoverable machine check. Linux has included code for some time to process these and just signal the affected processes (or even recover completely if the error was in a read only page that can be replaced by reading from disk).

But we have no recovery path for errors encountered during kernel code execution. Except for some very specific cases were are unlikely to ever be able to recover.

Enter memory mirroring. Actually 3rd generation of memory mirroing.

Gen1: All memory is mirrored
Pro: No s/w enabling - h/w just gets good data from other side of the mirror
Con: Halves effective memory capacity available to OS/applications
Gen2: Partial memory mirror - just mirror memory begind some memory controllers
Pro: Keep more of the capacity
Con: Nightmare to enable. Have to choose between allocating from
mirrored memory for safety vs. NUMA local memory for performance
Gen3: Address range partial memory mirror - some mirror on each memory controller
Pro: Can tune the amount of mirror and keep NUMA performance
Con: I have to write memory management code to implement

Quoted 6 lines

The current plan is just to use mirrored memory for kernel allocations. This has been broken into two phases:
1) This patch series - find the mirrored memory, use it for boot time allocations
2) Wade into mm/page_alloc.c and define a ZONE_MIRROR to pick up the unused mirrored memory from mm/memblock.c and only give it out to select kernel allocations (this is still being scoped because page_alloc.c is scary).

Looks good to me. What happens to these patches while ZONE_MIRROR is being worked on?

I'm wondering about phase II. What does "select kernel allocations" mean? I assume we can't say "all kernel allocations" because that can sometimes be "almost all memory". How are you planning on implementing this? A new __GFP_foo flag, then sprinkle that into selected sites?

Will surplus ZONE_MIRROR memory be available for regular old movable allocations?

I suggest you run the design ideas by Mel before getting into implementation.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#6 lore raw
Quoted 2 lines · Andrew Morton

Looks good to me. What happens to these patches while ZONE_MIRROR is being worked on?

I think these patches can go into the kernel now while I figure out the next phase - there is some value in just this part. We'll have all memory <4GB mirrored to cover the kernel code/data. Adding the boot time allocations mostly means the page structures (in terms of total amount of memory).

Quoted 4 lines

I'm wondering about phase II. What does "select kernel allocations" mean? I assume we can't say "all kernel allocations" because that can sometimes be "almost all memory". How are you planning on implementing this? A new __GFP_foo flag, then sprinkle that into selected sites?

Some of that is TBD - there are some clear places where we have bounded amounts of memory that we'd like to pull into the mirror area. E.g. loadable modules - on a specific machine an administrator can easily see which modules are loaded, tally up the sizes, and then adjust the amount of mirrored memory. I don't think we necessarily need to get to 100% ... if we can avoid 9/10 errors crashing the machine - that moves the reliability needle enough to make a difference. Phase 2 may turn into phase 2a, 2b, 2c etc. as we pick on certain areas.

Oh - they'll be some sysfs or debugfs stats too - so people can check that they have the right amount of mirror memory under application load. Too little and they'll be at risk because kernel allocations will fall back to non-mirrored. Too much, and they are wasting memory.

Quoted 2 lines

Will surplus ZONE_MIRROR memory be available for regular old movable allocations?

ZONE_MIRROR and ZONE_MOVABLE are pretty much opposites. We only want kernel allocations in mirror memory, and we can't allow any kernel allocations in movable (cause they'll pin it).

Quoted 2 lines

I suggest you run the design ideas by Mel before getting into implementation.

Good idea - when I have something fit to be seen, I'll share with Mel.

-Tony

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#7 lore raw
Quoted 5 lines · On Fri, 8 May 2015 13:38:52 -0700 Tony Luck <[email protected]> wrote:
Quoted 2 lines

Will surplus ZONE_MIRROR memory be available for regular old movable allocations?

ZONE_MIRROR and ZONE_MOVABLE are pretty much opposites. We only want kernel allocations in mirror memory, and we can't allow any kernel allocations in movable (cause they'll pin it).

What I mean is: allow userspace to consume ZONE_MIRROR memory because we can snatch it back if it is needed for kernel memory.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#8 lore raw
Quoted 2 lines · Andrew Morton

What I mean is: allow userspace to consume ZONE_MIRROR memory because we can snatch it back if it is needed for kernel memory.

For suitable interpretations of "snatch it back" ... if there is none free in a GFP_NOWAIT request, then we are doomed. But we could maintain some high/low watermarks to arrange the snatching when mirrored memory is getting low, rather than all the way out.

It's worth a look - but perhaps at phase three. It would make life a bit easier for people to get the right amount of mirror. If they guess too high they are still wasting some memory because every mirrored page has two pages in DIMM. But without this sort of trick all the extra mirrored memory would be totally wasted.

-Tony

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#9 lore raw
In reply to Tony Luck
Quoted 23 lines · Tony Luck

Some high end Intel Xeon systems report uncorrectable memory errors as a recoverable machine check. Linux has included code for some time to process these and just signal the affected processes (or even recover completely if the error was in a read only page that can be replaced by reading from disk).

But we have no recovery path for errors encountered during kernel code execution. Except for some very specific cases were are unlikely to ever be able to recover.

Enter memory mirroring. Actually 3rd generation of memory mirroing.

Gen1: All memory is mirrored
Pro: No s/w enabling - h/w just gets good data from other side of the mirror
Con: Halves effective memory capacity available to OS/applications
Gen2: Partial memory mirror - just mirror memory begind some memory controllers
Pro: Keep more of the capacity
Con: Nightmare to enable. Have to choose between allocating from
mirrored memory for safety vs. NUMA local memory for performance
Gen3: Address range partial memory mirror - some mirror on each memory controller
Pro: Can tune the amount of mirror and keep NUMA performance
Con: I have to write memory management code to implement

Quoted 7 lines

The current plan is just to use mirrored memory for kernel allocations. This has been broken into two phases:
1) This patch series - find the mirrored memory, use it for boot time allocations
2) Wade into mm/page_alloc.c and define a ZONE_MIRROR to pick up the unused mirrored memory from mm/memblock.c and only give it out to select kernel allocations (this is still being scoped because page_alloc.c is scary).

Hi Tony,

In part2, does it means the memory allocated from kernel should use mirrored memory?

I have heard of this feature(address range mirroring) before, and I changed some code to test it(implement memory allocations in specific physical areas).

In my opinion, add a new zone(ZONE_MIRROR) to fill the mirrored memory is not a good idea. If there are XX discontiguous mirrored areas in one numa node, there should be XX ZONE_MIRROR zones in one pgdat, it is impossible, right?

I think add a new migrate type(MIGRATE_MIRROR) will be better, the following print is from my changed kernel.

[root@localhost ~]# cat /proc/pagetypeinfo

Page block order: 9
Pages per block:  512
Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10
Node    0, zone      DMA, type    Unmovable      1      1      1      0      2      1      1      0      1      0      0
Node    0, zone      DMA, type  Reclaimable      0      0      0      0      0      0      0      0      0      0      0
Node    0, zone      DMA, type      Movable      0      0      0      0      0      0      0      0      0      0      3
Node    0, zone      DMA, type       Mirror      0      0      0      0      0      0      0      0      0      0      0
Node    0, zone      DMA, type      Reserve      0      0      0      0      0      0      0      0      0      1      0
Node    0, zone      DMA, type          CMA      0      0      0      0      0      0      0      0      0      0      0
Node    0, zone      DMA, type      Isolate      0      0      0      0      0      0      0      0      0      0      0
Node    0, zone    DMA32, type    Unmovable     14      7      6      1      3      0      1      0      0      0      0
Node    0, zone    DMA32, type  Reclaimable     15      2      2      1      1      2      1      1      0      0      0
Node    0, zone    DMA32, type      Movable      3     24     52     58     31      2      1      1      1      3    231
Node    0, zone    DMA32, type       Mirror      0      0      0      0      0      0      0      0      0      0      0
Node    0, zone    DMA32, type      Reserve      0      0      0      0      0      0      0      0      0      0      1
Node    0, zone    DMA32, type          CMA      0      0      0      0      0      0      0      0      0      0      0
Node    0, zone    DMA32, type      Isolate      0      0      0      0      0      0      0      0      0      0      0
Node    0, zone   Normal, type    Unmovable     80     12      6      7      3      1     67     58     23     11      0
Node    0, zone   Normal, type  Reclaimable      6      6      8     11      5      3      0      1      0      0      0
Node    0, zone   Normal, type      Movable      6    198    618    675    363     13      4      3      0      2   4074
Node    0, zone   Normal, type       Mirror      0      0      0      0      0      0      0      0      0      0   1024
Node    0, zone   Normal, type      Reserve      0      0      0      0      0      0      0      0      0      0      1
Node    0, zone   Normal, type          CMA      0      0      0      0      0      0      0      0      0      0      0
Node    0, zone   Normal, type      Isolate      0      0      0      0      0      0      0      0      0      0      0

Number of blocks type Unmovable Reclaimable Movable Mirror Reserve CMA Isolate

Node 0, zone      DMA            1            0            6            0            1            0            0
Node 0, zone    DMA32            8           32          975            0            1            0            0
Node 0, zone   Normal          216          334        12760         2048            2            0            0
Page block order: 9
Pages per block:  512
Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10
Node    1, zone   Normal, type    Unmovable     18      2     19      3     21     28     13      0      1      1      0
Node    1, zone   Normal, type  Reclaimable      0      1      1      1      0      0      1      0      0      1      0
Node    1, zone   Normal, type      Movable      6     13      9      3      0      4      5      0      1      0   6970
Node    1, zone   Normal, type       Mirror      0      0      0      0      0      0      0      0      0      0   1024
Node    1, zone   Normal, type      Reserve      0      0      0      0      0      0      0      0      0      0      1
Node    1, zone   Normal, type          CMA      0      0      0      0      0      0      0      0      0      0      0
Node    1, zone   Normal, type      Isolate      0      0      0      0      0      0      0      0      0      0      0

Number of blocks type Unmovable Reclaimable Movable Mirror Reserve CMA Isolate
Node 1, zone Normal 112 4 14218 2048 2 0 0

Also I add a new flag(GFP_MIRROR), then we can use the mirrored form both kernel-space and user-space. If there is no mirrored memory, we will allocate other types memory.

1) kernel-space(pcp, page buddy, slab/slub ...):

	-> use mirrored memory(e.g. /proc/sys/vm/mirrorable)
		-> __alloc_pages_nodemask()
			->gfpflags_to_migratetype()
				-> use MIGRATE_MIRROR list
2) user-space(syscall, madvise, mmap ...):
	-> add VM_MIRROR flag in the vma
		-> add GFP_MIRROR when page fault in the vma
			-> __alloc_pages_nodemask()
				-> use MIGRATE_MIRROR list

Thanks,
Xishi Qiu

Quoted 21 lines

Tony Luck (3):
mm/memblock: Add extra "flags" to memblock to allow selection of
memory based on attribute
mm/memblock: Allocate boot time data structures from mirrored memory x86, mirror: x86 enabling - find mirrored memory ranges

 arch/s390/kernel/crash_dump.c |   5 +-
 arch/sparc/mm/init_64.c       |   6 ++-
 arch/x86/kernel/check.c       |   3 +-
 arch/x86/kernel/e820.c        |   3 +-
 arch/x86/kernel/setup.c       |   3 ++
 arch/x86/mm/init_32.c         |   2 +-
 arch/x86/platform/efi/efi.c   |  21 ++++++++
 include/linux/efi.h           |   3 ++
 include/linux/memblock.h      |  49 +++++++++++------
 mm/cma.c                      |   6 ++-
 mm/memblock.c                 | 123 +++++++++++++++++++++++++++++++++---------
 mm/memtest.c                  |   3 +-
 mm/nobootmem.c                |  14 ++++-
 13 files changed, 188 insertions(+), 53 deletions(-)
#10 lore raw
Quoted 1 line · Xishi Qiu

In part2, does it means the memory allocated from kernel should use mirrored memory?

Yes. I want to use mirrored memory for all (or as many as possible) kernel allocations.

Quoted 6 lines

I have heard of this feature(address range mirroring) before, and I changed some code to test it(implement memory allocations in specific physical areas).

In my opinion, add a new zone(ZONE_MIRROR) to fill the mirrored memory is not a good idea. If there are XX discontiguous mirrored areas in one numa node, there should be XX ZONE_MIRROR zones in one pgdat, it is impossible, right?

With current h/w implementations XX is at most 2, and is possibly only 1 on most nodes. But we shouldn't depend on that.

Quoted 2 lines

I think add a new migrate type(MIGRATE_MIRROR) will be better, the following print is from my changed kernel.

This sounds interesting.

Quoted 5 lines

[root@localhost ~]# cat /proc/pagetypeinfo

Page block order: 9
Pages per block:  512

Free pages count per migrate type at order 0 1 2 3 4 5 6 7 8 9 10

...

Quoted 1 line

Node 0, zone DMA, type Mirror 0 0 0 0 0 0 0 0 0 0 0

...

Quoted 1 line

Node 0, zone DMA32, type Mirror 0 0 0 0 0 0 0 0 0 0 0

I see all zero counts here ... which is fine. I expect that systems will mirror all memory below 4GB ... but we should probably ignore the attribute for this range because we want to make sure that the memory is still available for users that depend on getting memory that legacy devices can access. On systems that support address range mirror the <4GB area is <2% of even a small system (128GB seems to be the minimum rational configuration for a 4 socket machine ... you end up with that much if you populate every channel with just one 4GB DIMM). On a big system (in the TB range) <4GB area is a trivial rounding error.

Quoted 3 lines

Also I add a new flag(GFP_MIRROR), then we can use the mirrored form both kernel-space and user-space. If there is no mirrored memory, we will allocate other types memory.

But I *think* I want all kernel and no users to allocate mirror memory. I'd like to not have to touch every place that allocates memory to add/clear this flag.

Quoted 5 lines

1) kernel-space(pcp, page buddy, slab/slub ...):

        -> use mirrored memory(e.g. /proc/sys/vm/mirrorable)
                -> __alloc_pages_nodemask()
                        ->gfpflags_to_migratetype()
                                -> use MIGRATE_MIRROR list

I think you are telling me that we can do this, but I don't understand how the code would look.

Quoted 5 lines

2) user-space(syscall, madvise, mmap ...):

        -> add VM_MIRROR flag in the vma
                -> add GFP_MIRROR when page fault in the vma
                        -> __alloc_pages_nodemask()
                                -> use MIGRATE_MIRROR list

If we do let users have access to mirrored memory, then madvise/mmap seem a plausible way to allow it. Not sure what access privileges are appropriate to allow it. I expect mirrored memory to be in short supply (the whole point of address range mirror is to make do with a minimal amount of mirrored memory ... if you expect to want/have lots of mirrored memory, then just take the 50% hit in capacity and mirror everything and ignore all the s/w complexity).

Are your patches ready to be shared?

-Tony

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#11 lore raw
Quoted 26 lines · Tony Luck
Quoted 1 line · Xishi Qiu

In part2, does it means the memory allocated from kernel should use mirrored memory?

Yes. I want to use mirrored memory for all (or as many as possible) kernel allocations.

Quoted 6 lines

I have heard of this feature(address range mirroring) before, and I changed some code to test it(implement memory allocations in specific physical areas).

In my opinion, add a new zone(ZONE_MIRROR) to fill the mirrored memory is not a good idea. If there are XX discontiguous mirrored areas in one numa node, there should be XX ZONE_MIRROR zones in one pgdat, it is impossible, right?

With current h/w implementations XX is at most 2, and is possibly only 1 on most nodes. But we shouldn't depend on that.

Quoted 2 lines

I think add a new migrate type(MIGRATE_MIRROR) will be better, the following print is from my changed kernel.

This sounds interesting.

Quoted 5 lines

[root@localhost ~]# cat /proc/pagetypeinfo

Page block order: 9
Pages per block:  512

Free pages count per migrate type at order 0 1 2 3 4 5 6 7 8 9 10

Quoted 8 lines

...

Quoted 1 line

Node 0, zone DMA, type Mirror 0 0 0 0 0 0 0 0 0 0 0

...

Quoted 1 line

Node 0, zone DMA32, type Mirror 0 0 0 0 0 0 0 0 0 0 0

I see all zero counts here ... which is fine. I expect that systems will mirror all memory below 4GB ... but we should probably ignore the attribute for this range because we want to make

Hi Tony,

I think 0-4G will be all mirrored, so I change nothing, just ignore the mirror flag.(e.g. 4 socket machine, every socket has 32G memory, then node0: 0-4G, 4-8G mirrored, node1: 32-36G mirrored, node2:64-68G mirrored, node3: 96-100G mirrored)

Quoted 8 lines

sure that the memory is still available for users that depend on getting memory that legacy devices can access. On systems that support address range mirror the <4GB area is <2% of even a small system (128GB seems to be the minimum rational configuration for a 4 socket machine ... you end up with that much if you populate every channel with just one 4GB DIMM). On a big system (in the TB range) <4GB area is a trivial rounding error.

Quoted 8 lines
Quoted 3 lines

Also I add a new flag(GFP_MIRROR), then we can use the mirrored form both kernel-space and user-space. If there is no mirrored memory, we will allocate other types memory.

But I *think* I want all kernel and no users to allocate mirror memory. I'd like to not have to touch every place that allocates memory to add/clear this flag.

If only want kernel to use the mirrored memory, it is much easier. I have some patches, but it's a little ugly and implement both user and kernel.

Quoted 15 lines
Quoted 5 lines

1) kernel-space(pcp, page buddy, slab/slub ...):

        -> use mirrored memory(e.g. /proc/sys/vm/mirrorable)
                -> __alloc_pages_nodemask()
                        ->gfpflags_to_migratetype()
                                -> use MIGRATE_MIRROR list

I think you are telling me that we can do this, but I don't understand how the code would look.

Quoted 5 lines

2) user-space(syscall, madvise, mmap ...):

        -> add VM_MIRROR flag in the vma
                -> add GFP_MIRROR when page fault in the vma
                        -> __alloc_pages_nodemask()
                                -> use MIGRATE_MIRROR list
Quoted 4 lines

If we do let users have access to mirrored memory, then madvise/mmap seem a plausible way to allow it. Not sure what access privileges are appropriate to allow it. I expect mirrored memory to be in short supply (the whole point of

I think allocations from some key process(e.g. date base) are as important as kernel, and in most cases MCE just kill them if memory failure, so let user can access the mirrored memory may be a good way to solve the problem.

Quoted 6 lines

address range mirror is to make do with a minimal amount of mirrored memory ... if you expect to want/have lots of mirrored memory, then just take the 50% hit in capacity and mirror everything and ignore all the s/w complexity).

Are your patches ready to be shared?

I'll rewrite and send them soon.

Thanks,
Xishi Qiu

Quoted 5 lines

-Tony

.