[RFC PATCH 2/3] support for broken memory modules (BadRAM)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



BadRAM is a mechanism to exclude memory addresses (pages) from being used by
the system. The addresses are given to the kernel via kernel command line.
This is useful for systems with defective RAM modules, especially if the RAM
modules cannot be replaced.

command line parameter: badram=<addr>,<mask>[,...]

Patterns for the command line parameter can be obtained by running Memtest86.
In Memtest86 press "c" for configuration, select "Error Report Mode" and
finally "BadRAM Patterns"

This has already been done by Rick van Rein a long time ago but it never found
it's way into the kernel.

Signed-off-by: Stefan Assmann <sassmann@xxxxxxxxx>
---
 mm/memory-failure.c |   95 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 95 insertions(+), 0 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 0207c2f..dac506c 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -52,6 +52,7 @@
 #include <linux/swapops.h>
 #include <linux/hugetlb.h>
 #include <linux/memory_hotplug.h>
+#include <linux/memblock.h>
 #include "internal.h"
 
 int sysctl_memory_failure_early_kill __read_mostly = 0;
@@ -1519,3 +1520,97 @@ int is_hwpoison_address(unsigned long addr)
 	return is_hwpoison_entry(entry);
 }
 EXPORT_SYMBOL_GPL(is_hwpoison_address);
+
+/*
+ * Return 0 if no address found else return 1, new address is stored in addrp.
+ **/
+static int __init next_masked_address(unsigned long *addrp, unsigned long mask)
+{
+	unsigned long total_mem = (max_pfn + 1) << PAGE_SHIFT;
+	unsigned long tmp_addr = *addrp;
+	unsigned long inc = 1;
+
+	while (inc & mask)
+		inc = inc << 1;
+
+	while (inc != 0) {
+		tmp_addr += inc;
+		tmp_addr &= ~mask;
+		tmp_addr |= ((*addrp) & mask);
+
+		/* address is bigger than phys memory */
+		if (tmp_addr >= total_mem)
+			return 0;
+
+		/* address found */
+		if (tmp_addr > *addrp) {
+			*addrp = tmp_addr;
+			return 1;
+		}
+
+		while (inc & ~mask)
+			inc = inc << 1;
+		inc = inc << 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Set hwpoison pageflag on all pages specified by addr/mask.
+ */
+static int __init badram_mark_pages(unsigned long addr, unsigned long mask)
+{
+	unsigned long pagecount = 0;
+
+	mask |= ~PAGE_MASK; /* smallest chunk is a page */
+	addr &= mask;
+
+	printk(KERN_INFO "BadRAM: mark 0x%lx with mask 0x%0lx\n", addr, mask);
+
+	do {
+		unsigned long pfn = addr >> PAGE_SHIFT;
+		struct page *page = pfn_to_page(pfn);
+
+		if (!pfn_valid(pfn))
+			continue;
+		if (memblock_is_reserved(addr)) {
+			printk(KERN_DEBUG
+			       "BadRAM: page %lu reserved by kernel\n", pfn);
+			continue;
+		}
+
+		SetPageHWPoison(page);
+		atomic_long_add(1, &mce_bad_pages);
+		pagecount++;
+		pr_debug("BadRAM: page %lu (addr 0x%0lx) marked bad "
+			 "[total %lu]\n", pfn, addr, pagecount);
+	} while (next_masked_address(&addr, mask));
+
+	return pagecount;
+}
+
+static int __init badram_setup(char *str)
+{
+	printk(KERN_DEBUG "BadRAM: cmdline option is %s\n", str);
+
+	if (*str++ != '=')
+		return 0;
+
+	while (*str) {
+		unsigned long addr = 0, mask = 0, pagecount = 0;
+
+		if (!get_next_ulong(&str, &addr, ',', 16)) {
+			printk(KERN_WARNING "BadRAM: parsing error\n");
+			return 0;
+		}
+		if (!get_next_ulong(&str, &mask, ',', 16))
+			mask = ~(0UL);
+
+		pagecount = badram_mark_pages(addr, mask);
+		printk(KERN_INFO "BadRAM: %lu page(s) bad\n", pagecount);
+	}
+
+	return 0;
+}
+__setup("badram", badram_setup);
-- 
1.7.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxxx  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>


[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]