ia64: Call migration code on correctable errors v2
PATCH 3/3 ia64: Call migration code on correctable errors v2
Migrate data off pages with correctable memory errors. This patch is the ia64 specific piece. It connects the CPE handler to the page migration code. It is implemented as a kernel loadable module, similar to the mca recovery code (mca_recovery.ko). This allows the feature to be turned off by uninstalling the module. Creates /proc/badram to display bad page information and free bad pages.
--- arch/ia64/Kconfig | 3 arch/ia64/kernel/Makefile | 1 arch/ia64/kernel/cpe_migrate.c | 382 +++++++++++++++++++++++++++++++++++++++++ arch/ia64/kernel/mca.c | 34 +++ include/asm-ia64/mca.h | 5 include/asm-ia64/page.h | 1 include/linux/migrate.h | 2 mm/migrate.c | 7 mm/page_alloc.c | 1 9 files changed, 434 insertions(+), 2 deletions(-)
Index: linus/arch/ia64/kernel/Makefile
=================================--- linus.orig/arch/ia64/kernel/Makefile 2008-05-01 19:36:40.000000000 -0500
+++ linus/arch/ia64/kernel/Makefile 2008-05-01 19:36:49.000000000 -0500
@@ -27,6 +27,7 @@ obj-$(CONFIG_PERFMON) += perfmon_defaul
obj-$(CONFIG_IA64_CYCLONE) += cyclone.o
obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o
+obj-$(CONFIG_IA64_CPE_MIGRATE) += cpe_migrate.o
obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
Index: linus/arch/ia64/kernel/cpe_migrate.c
=================================--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linus/arch/ia64/kernel/cpe_migrate.c 2008-05-01 19:36:49.000000000 -0500
@@ -0,0 +1,382 @@
+/*
+ * File: cpe_migrate.c
+ * Purpose: Migrate data from physical pages with excessive correctable
+ * errors to new physical pages. Keep the old pages on a discard
+ * list.
+ *
+ * Copyright (C) 2008 SGI - Silicon Graphics Inc.
+ * Copyright (C) 2008 Russ Anderson <[email protected]>
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kallsyms.h>
+#include <linux/bootmem.h>
+#include <linux/acpi.h>
+#include <linux/timer.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/workqueue.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/delay.h>
+#include <linux/ptrace.h>
+#include <linux/irq.h>
+#include <linux/vmalloc.h>
+#include <linux/migrate.h>
+#include <linux/page-isolation.h>
+#include <linux/rmap.h>
+#include <linux/memcontrol.h>
+
+#include <asm/machvec.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/sal.h>
+#include <asm/hw_irq.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/mca.h>
+
+#define BADRAM_BASENAME "badram"
+#define CE_HISTORY_LENGTH 30
+
+static u64 cpe_paddr[CE_HISTORY_LENGTH];
+static u16 cpe_node[CE_HISTORY_LENGTH];
+static int cpe_polling_enabled = 1;
+static int cpe_head;
+static int cpe_tail;
+
+int work_scheduled;
+spinlock_t cpe_migrate_lock;
+
+void
+get_physical_address(void *buffer, u64 *paddr, u16 *node) +{
+ sal_log_record_header_t *rh;
+ sal_log_mem_dev_err_info_t *mdei;
+ ia64_err_rec_t *err_rec;
+ sal_log_platform_err_info_t *plat_err;
+ efi_guid_t guid;
+
+ err_rec = (ia64_err_rec_t *)buffer;
+ rh = (sal_log_record_header_t *)&err_rec->sal_elog_header;
+ *paddr = 0;
+ *node = 0;
+
+ /*
+ * Make sure it is a corrected error.
+ */
+ if (rh->severity != sal_log_severity_corrected)
+ return;
+
+ plat_err = (sal_log_platform_err_info_t *)&err_rec->proc_err;
+
+ guid = (efi_guid_t)plat_err->mem_dev_err.header.guid;
+ if (efi_guidcmp(guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID) = 0) {
+ /*
+ * Memory cpe
+ */
+ mdei = (sal_log_mem_dev_err_info_t *)&plat_err->mem_dev_err;
+ if (mdei->valid.oem_data) {
+ if (mdei->valid.physical_addr)
+ *paddr = mdei->physical_addr;
+
+ if (mdei->valid.node) {
+ if (ia64_platform_is("sn2"))
+ *node = nasid_to_cnodeid(mdei->node);
+ else
+ *node = mdei->node;
+ }
+ return;
+ }
+ }
+ return;
+}
+
+struct page *
+alloc_migrate_page(struct page *ignored, unsigned long node, int **x) +{
+
+ return alloc_pages_node(node, GFP_HIGHUSER_MOVABLE, 0);
+}
+
+static int
+ia64_mca_cpe_move_page(u64 paddr, u32 node)
+{
+ LIST_HEAD(pagelist);
+ struct page *page;
+ int ret;
+ unsigned long irq_flags;
+
+ local_irq_save(irq_flags);
+ /*
+ * Validate page
+ */
+ if (!paddr)
+ return -1;
+ if (!ia64_phys_addr_valid(paddr))
+ return -1;
+ if (!pfn_valid(paddr >> PAGE_SHIFT))
+ return -1;
+
+ /*
+ * convert physical address to page number
+ */
+ page = phys_to_page(paddr);
+
+ if (!spin_trylock(&cpe_migrate_lock)) {
+ local_irq_restore(irq_flags);
+ return -1;
+ }
+
+ preempt_disable();
+ migrate_prep();
+ ret = isolate_lru_page(page, &pagelist);
+ preempt_enable();
+ if (ret) {
+ spin_unlock_irqrestore(&cpe_migrate_lock, irq_flags);
+ return ret;
+ }
+
+ SetPageMemError(page); /* Mark the page as bad */
+ ret = migrate_pages(&pagelist, alloc_migrate_page, node);
+ if (ret = 0)
+ list_add_tail(&page->lru, &badpagelist);
+
+ spin_unlock_irqrestore(&cpe_migrate_lock, irq_flags);
+ return 0;
+}
+
+/*
+ * ia64_mca_cpe_migrate
+ * The worker that does the actual migration. It pulls a
+ * physical address off the list and calls the migration code.
+ *
+ * Inputs
+ * none
+ * Outputs
+ * none
+ */
+static void
+ia64_mca_cpe_migrate(struct work_struct *unused)
+{
+ int ret;
+ u64 paddr;
+ u16 node;
+
+ do {
+ if (cpe_paddr[cpe_tail]) {
+ paddr = cpe_paddr[cpe_tail];
+ node = cpe_node[cpe_tail];
+
+ ret = ia64_mca_cpe_move_page(paddr, node);
+ if (ret <= 0)
+ /*
+ * Even though the return status is negative,
+ * clear the entry. If the same address has
+ * another CPE it will be re-added to the list.
+ */
+ cpe_paddr[cpe_tail] = 0;
+
+ }
+ if (++cpe_tail >= CE_HISTORY_LENGTH)
+ cpe_tail = 0;
+
+ } while (cpe_tail != cpe_head);
+ work_scheduled = 0;
+}
+static DECLARE_WORK(cpe_enable_work, ia64_mca_cpe_migrate); +
+/*
+ * ce_setup_migrate
+ * Get the physical address out of the CPE record, add it
+ * to the list of addresses to migrate (if not already on),
+ * and schedule the back end worker task. This is called
+ * in interrupt context so cannot directly call the migration
+ * code.
+ *
+ * Inputs
+ * rec The CPE record
+ * Outputs
+ * 1 on Success, -1 on failure
+ */
+static int
+ce_setup_migrate(void *rec)
+{
+ u64 paddr;
+ u16 node;
+ /* int head, tail; */
+ int i;
+
+ if (!rec)
+ return -1;
+
+ get_physical_address(rec, &paddr, &node);
+ if (!paddr)
+ return -1;
+
+ if (!((cpe_head = cpe_tail) && (cpe_paddr[cpe_head] = 0)))
+ /*
+ * List not empty
+ */
+ for (i = 0; i < CE_HISTORY_LENGTH; i++)
+ if ((PAGE_ALIGN(cpe_paddr[i])) = PAGE_ALIGN(paddr))
+ return 1; /* already on the list */
+
+ if (cpe_paddr[cpe_head] = 0) {
+ cpe_paddr[cpe_head] = paddr;
+ cpe_node[cpe_head] = node;
+
+ if (++cpe_head >= CE_HISTORY_LENGTH)
+ cpe_head = 0;
+ }
+
+ if (!work_scheduled) {
+ work_scheduled = 1;
+ schedule_work(&cpe_enable_work);
+ }
+
+ return 1;
+}
+
+/*
+ * ======================================+ */
+
+int
+freeOneBadPage(unsigned long paddr)
+{
+ struct page *page, *page2, *target;
+
+ /*
+ * Verify page address
+ */
+ target = phys_to_page(paddr);
+ list_for_each_entry_safe(page, page2, &badpagelist, lru) {
+ if (page != target)
+ continue;
+
+ ClearPageMemError(page); /* Mark the page as good */
+ move_to_lru(page);
+ list_del(&page->lru);
+ totalbad_pages--;
+ break;
+ }
+ return 0;
+}
+
+int
+freeAllBadPages(void)
+{
+ struct page *page, *page2;
+
+ list_for_each_entry_safe(page, page2, &badpagelist, lru) {
+ ClearPageMemError(page); /* Mark the page as good */
+ totalbad_pages--;
+ }
+ putback_lru_pages(&badpagelist);
+ return 0;
+}
+
+#define OPT_LEN 16
+
+static ssize_t
+padpage_write(struct file *file, const char __user *user,
+ size_t count, loff_t *data)
+{
+ char optstr[OPT_LEN];
+ unsigned long opt;
+ int len = OPT_LEN;
+
+ if (count < len)
+ len = count;
+
+ if (copy_from_user(optstr, user, len))
+ return -EFAULT;
+ optstr[len] = '\0';
+
+ opt = simple_strtoul(optstr, NULL, 0);
+ if (opt = 0)
+ freeAllBadPages();
+ else
+ freeOneBadPage(opt);
+
+ return count;
+}
+
+int
+badpage_show(struct seq_file *file, void *data)
+{
+ struct page *page, *page2;
+ int i = 0;
+
+ seq_printf(file, "Bad RAM: %d kB, %d pages marked bad\n"
+ "List of bad physical pages\n",
+ totalbad_pages << (PAGE_SHIFT - 10), totalbad_pages);
+ list_for_each_entry_safe(page, page2, &badpagelist, lru) {
+ seq_printf(file, " 0x%011lx", page_to_phys(page));
+ if (!(++i % 5))
+ seq_printf(file, "\n");
+ }
+ seq_printf(file, "\n");
+ return 0;
+}
+
+int
+badpage_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, badpage_show, NULL);
+}
+
+static struct file_operations proc_badpage_operations = {
+ .open = badpage_open,
+ .write = padpage_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static struct proc_dir_entry *proc_badpage;
+
+int __init cpe_migrate_external_handler_init(void)
+{
+ spin_lock_init(&cpe_migrate_lock);
+
+ /* register external ce handler */
+ if (ia64_reg_CE_extension(ce_setup_migrate)) {
+ printk(KERN_ERR "ia64_reg_CE_extension failed.\n");
+ return -EFAULT;
+ }
+ cpe_poll_enabled = cpe_polling_enabled;
+
+ proc_badpage = create_proc_entry(BADRAM_BASENAME, 0644, NULL);
+ if (proc_badpage = NULL) {
+ printk(KERN_ERR "unable to create %s proc entry",
+ BADRAM_BASENAME);
+ return -EINVAL;
+ }
+ proc_badpage->proc_fops = &proc_badpage_operations;
+ printk(KERN_INFO "Registered badram Driver\n");
+ return 0;
+}
+
+void __exit cpe_migrate_external_handler_exit(void)
+{
+ /* unregister external mca handlers */
+ ia64_unreg_CE_extension();
+ remove_proc_entry(BADRAM_BASENAME, NULL);
+}
+
+module_init(cpe_migrate_external_handler_init); +module_exit(cpe_migrate_external_handler_exit);
+
+module_param(cpe_polling_enabled, int, 0644); +MODULE_PARM_DESC(cpe_polling_enabled,
+ "Enable polling with migration");
+
+MODULE_AUTHOR("Russ Anderson <[email protected]>"); +MODULE_DESCRIPTION("ia64 Corrected Error page migration driver"); +MODULE_LICENSE("GPL");
Index: linus/arch/ia64/kernel/mca.c
=================================--- linus.orig/arch/ia64/kernel/mca.c 2008-05-01 19:36:40.000000000 -0500
+++ linus/arch/ia64/kernel/mca.c 2008-05-01 19:36:49.000000000 -0500
@@ -68,6 +68,9 @@
*
* 2007-04-27 Russ Anderson <[email protected]>
* Support multiple cpus going through OS_MCA in the same event.
+ *
+ * 2008-04-22 Russ Anderson <[email protected]>
+ * Migrate data off pages with correctable memory errors.
*/
#include <linux/jiffies.h>
#include <linux/types.h>
@@ -163,7 +166,11 @@ static int cmc_polling_enabled = 1;
* but encounters problems retrieving CPE logs. This should only be
* necessary for debugging.
*/
-static int cpe_poll_enabled = 1;
+int cpe_poll_enabled = 1; +EXPORT_SYMBOL(cpe_poll_enabled);
+
+LIST_HEAD(badpagelist);
+EXPORT_SYMBOL(badpagelist);
extern void salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe);
@@ -525,6 +532,28 @@ EXPORT_SYMBOL_GPL(mca_recover_range);
#ifdef CONFIG_ACPI
+/* Function pointer to Corrected Error memory migration driver */ +int (*ia64_mca_ce_extension)(void *) = NULL;
+
+int
+ia64_reg_CE_extension(int (*fn)(void *))
+{
+ if (ia64_mca_ce_extension)
+ return 1;
+
+ ia64_mca_ce_extension = fn;
+ return 0;
+}
+EXPORT_SYMBOL(ia64_reg_CE_extension);
+
+void
+ia64_unreg_CE_extension(void)
+{
+ if (ia64_mca_ce_extension)
+ ia64_mca_ce_extension = NULL;
+}
+EXPORT_SYMBOL(ia64_unreg_CE_extension);
+
int cpe_vector = -1;
int ia64_cpe_irq = -1;
@@ -534,6 +563,7 @@ ia64_mca_cpe_int_handler (int cpe_irq, v
static unsigned long cpe_history[CPE_HISTORY_LENGTH];
static int index;
static DEFINE_SPINLOCK(cpe_history_lock);
+ int recover;
IA64_MCA_DEBUG("%s: received interrupt vector = %#x on CPU %d\n",
__func__, cpe_irq, smp_processor_id());
@@ -580,6 +610,8 @@ ia64_mca_cpe_int_handler (int cpe_irq, v
out:
/* Get the CPE error record and log it */ ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE);
+ recover = (ia64_mca_ce_extension && ia64_mca_ce_extension(
+ IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_CPE)));
return IRQ_HANDLED;
}
Index: linus/arch/ia64/Kconfig
=================================--- linus.orig/arch/ia64/Kconfig 2008-05-01 19:36:40.000000000 -0500
+++ linus/arch/ia64/Kconfig 2008-05-01 19:36:49.000000000 -0500
@@ -456,6 +456,9 @@ config COMPAT_FOR_U64_ALIGNMENT
config IA64_MCA_RECOVERY
tristate "MCA recovery from errors other than TLB."
+config IA64_CPE_MIGRATE
+ tristate "Migrate data off pages with correctable errors"
+
config PERFMON
bool "Performance monitor support"
help
Index: linus/include/asm-ia64/mca.h
=================================--- linus.orig/include/asm-ia64/mca.h 2008-05-01 19:36:40.000000000 -0500
+++ linus/include/asm-ia64/mca.h 2008-05-01 19:36:49.000000000 -0500
@@ -137,6 +137,7 @@ extern unsigned long __per_cpu_mca[NR_CP
extern int cpe_vector;
extern int ia64_cpe_irq;
+extern int cpe_poll_enabled;
extern void ia64_mca_init(void);
extern void ia64_mca_cpu_init(void *);
extern void ia64_os_mca_dispatch(void);
@@ -150,9 +151,13 @@ extern void ia64_slave_init_handler(void
extern void ia64_mca_cmc_vector_setup(void);
extern int ia64_reg_MCA_extension(int (*fn)(void *, struct ia64_sal_os_state *)); extern void ia64_unreg_MCA_extension(void);
+extern int ia64_reg_CE_extension(int (*fn)(void *));
+extern void ia64_unreg_CE_extension(void);
extern u64 ia64_get_rnat(u64 *);
extern void ia64_mca_printk(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
+
+extern struct list_head badpagelist;
struct ia64_mca_notify_die {
struct ia64_sal_os_state *sos;
Index: linus/mm/migrate.c
=================================--- linus.orig/mm/migrate.c 2008-05-01 19:36:40.000000000 -0500
+++ linus/mm/migrate.c 2008-05-01 19:36:49.000000000 -0500
@@ -64,6 +64,7 @@ int isolate_lru_page(struct page *page,
}
return ret;
}
+EXPORT_SYMBOL(isolate_lru_page);
/*
* migrate_prep() needs to be called before we start compiling a list of pages
@@ -81,8 +82,9 @@ int migrate_prep(void)
return 0;
}
+EXPORT_SYMBOL(migrate_prep);
-static inline void move_to_lru(struct page *page) +inline void move_to_lru(struct page *page)
{
if (PageActive(page)) {
/*
@@ -96,6 +98,7 @@ static inline void move_to_lru(struct pa
}
put_page(page);
}
+EXPORT_SYMBOL(move_to_lru);
/*
* Add isolated pages on the list back to the LRU.
@@ -115,6 +118,7 @@ int putback_lru_pages(struct list_head *
}
return count;
}
+EXPORT_SYMBOL(putback_lru_pages);
/*
* Restore a potential migration pte to a working pte entry
@@ -805,6 +809,7 @@ out:
return nr_failed + retry;
}
+EXPORT_SYMBOL(migrate_pages);
#ifdef CONFIG_NUMA
/*
Index: linus/include/asm-ia64/page.h
=================================--- linus.orig/include/asm-ia64/page.h 2008-05-01 19:36:40.000000000 -0500
+++ linus/include/asm-ia64/page.h 2008-05-01 19:36:49.000000000 -0500
@@ -122,6 +122,7 @@ extern unsigned long max_low_pfn;
#endif
#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT)
+#define phys_to_page(kaddr) (pfn_to_page(kaddr >> PAGE_SHIFT))
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
Index: linus/mm/page_alloc.c
=================================--- linus.orig/mm/page_alloc.c 2008-05-01 19:36:40.000000000 -0500
+++ linus/mm/page_alloc.c 2008-05-01 19:36:49.000000000 -0500
@@ -72,6 +72,7 @@ unsigned long totalreserve_pages __read_
long nr_swap_pages;
int percpu_pagelist_fraction;
unsigned int totalbad_pages;
+EXPORT_SYMBOL(totalbad_pages);
#ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
int pageblock_order __read_mostly;
Index: linus/include/linux/migrate.h
=================================--- linus.orig/include/linux/migrate.h 2008-05-01 19:36:40.000000000 -0500
+++ linus/include/linux/migrate.h 2008-05-01 19:36:49.000000000 -0500
@@ -38,6 +38,7 @@ extern int migrate_prep(void);
extern int migrate_vmas(struct mm_struct *mm,
const nodemask_t *from, const nodemask_t *to, unsigned long flags);
+extern void move_to_lru(struct page *);
#else
static inline int vma_migratable(struct vm_area_struct *vma)
{ return 0; }
@@ -59,6 +60,7 @@ static inline int migrate_vmas(struct mm
{
return -ENOSYS;
}
+static inline void move_to_lru(struct page *p) { return; }
/* Possible settings for the migrate_page() method in address_operations */ #define migrate_page NULL
Signature · 2 lines
Russ Anderson, OS RAS/Partitioning Project Lead SGI - Silicon Graphics Inc [email protected]
Quoted 6 lines · Russ Anderson
(quoted diff) +6
-0+int +freeOneBadPage(unsigned long paddr) +{ + struct page *page, *page2, *target; + + /*
Review
Quoted 10 lines
(quoted diff) +10
-0+ * Verify page address + */ + target = phys_to_page(paddr); + list_for_each_entry_safe(page, page2, &badpagelist, lru) { + if (page != target) + continue; + + ClearPageMemError(page); /* Mark the page as good */ + move_to_lru(page); + list_del(&page->lru);
This is the wrong order. move_to_lru() uses the lru field. So do the list_del first then the move. Note that there is a function putback_lru_pages() that is used to put a list of pages back to the lru if migration has failed and we give up on them. Could you use putback_lru_pages() and then avoid the exporting of move_to_lru()?
Review
Quoted 13 lines
(quoted diff) +13
-0+ +int +freeAllBadPages(void) +{ + struct page *page, *page2; + + list_for_each_entry_safe(page, page2, &badpagelist, lru) { + ClearPageMemError(page); /* Mark the page as good */ + totalbad_pages--; + } + putback_lru_pages(&badpagelist); + return 0; +}
Ahh.. Here you use it.
Review extern unsigned long max_low_pfn; · line 125
Quoted 8 lines
Index:
linus/include/asm-ia64/page.h
=================================> ---linus.orig/include/asm-ia64/page.h2008-05-01 19:36:40.000000000 -0500
+++linus/include/asm-ia64/page.h2008-05-01 19:36:49.000000000 -0500(quoted diff) +1
-0@@ -122,6 +122,7 @@ extern unsigned long max_low_pfn; #endif #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) +#define phys_to_page(kaddr) (pfn_to_page(kaddr >> PAGE_SHIFT))
#define phys_to_page virt_to_page ?
Quoted 16 lines · Christoph Lameter
Quoted 14 lines · Russ Anderson
(quoted diff) +14
-0+int +freeOneBadPage(unsigned long paddr) +{ + struct page *page, *page2, *target; + + /* + * Verify page address + */ + target = phys_to_page(paddr); + list_for_each_entry_safe(page, page2, &badpagelist, lru) { + if (page != target) + continue; + + ClearPageMemError(page); /* Mark the page as good */
Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ move_to_lru(page); + list_del(&page->lru);This is the wrong order.
move_to_lru()uses the lru field. So do thelist_delfirst then the move. Note that there is a functionputback_lru_pages()that is used to put a list of pages back to the lru if migration has failed and we give up on them. Could you useputback_lru_pages()and then avoid the exporting ofmove_to_lru()?
I'll put the page on a different list and call putback_lru_pages().
Quoted 7 lines
Quoted 7 lines
(quoted diff) +7
-0+ +int +freeAllBadPages(void) +{ + struct page *page, *page2; + + list_for_each_entry_safe(page, page2, &badpagelist, lru) {
Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ ClearPageMemError(page); /* Mark the page as good */ + totalbad_pages--; + } + putback_lru_pages(&badpagelist); + return 0; +}Ahh.. Here you use it.
This is when deleting all the pages on the list.
Quoted 10 lines
Review extern unsigned long max_low_pfn; · line 125
Quoted 8 lines
Index:
linus/include/asm-ia64/page.h
=================================> > ---linus.orig/include/asm-ia64/page.h2008-05-01 19:36:40.000000000 -0500
+++linus/include/asm-ia64/page.h2008-05-01 19:36:49.000000000 -0500(quoted diff) +1
-0@@ -122,6 +122,7 @@ extern unsigned long max_low_pfn; #endif #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) +#define phys_to_page(kaddr) (pfn_to_page(kaddr >> PAGE_SHIFT))#define
phys_to_pagevirt_to_page?
I'm not sure what you are asking.
#define phys_to_page(kaddr) (pfn_to_page(kaddr >> PAGE_SHIFT))
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
Do you mean change virt_to_page() like this?
#define virt_to_page(kaddr) phys_to_page(__pa(kaddr))
Thanks,
Signature · 2 lines
Russ Anderson, OS RAS/Partitioning Project Lead SGI - Silicon Graphics Inc [email protected]
Hi Russ,
Quoted 8 lines · Russ Anderson
Migrate data off pages with correctable memory errors. This patch is the
ia64 specific piece. It connects the CPE handler to the page migration code. It is implemented as a kernel loadable module, similar to the mca recovery code (mca_recovery.ko). This allows the feature to be turned off by uninstalling the module. Creates/proc/badramto display bad page information and free bad pages.Signed-off-by Russ Anderson <[email protected]>
I think sparse and checkpatch would have caught most of these but here goes:
Quoted 2 lines
+int
work_scheduled;
+spinlock_tcpe_migrate_lock;
static?
Quoted 3 lines
+void
+get_physical_address(void *buffer, u64 *paddr, u16 *node) +{
static?
Quoted 7 lines
+ sal_log_record_header_t *rh; + sal_log_mem_dev_err_info_t *mdei; + ia64_err_rec_t *err_rec; + sal_log_platform_err_info_t *plat_err; + efi_guid_t guid;+
+err_rec= (ia64_err_rec_t*)buffer;
Unnecessary cast from void *.
Quoted 21 lines
+ rh = (sal_log_record_header_t *)&err_rec->sal_elog_header; + *paddr = 0; + *node = 0;+
+ /*
+ * Make sure it is a corrected error.
+ */
+ if (rh->severity!=sal_log_severity_corrected)
+ return;
+
+plat_err= (sal_log_platform_err_info_t*)&err_rec->proc_err;
+
+ guid = (efi_guid_t)plat_err->mem_dev_err.header.guid;
+ if (efi_guidcmp(guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID)= 0) {
+ /*
+ * Memory cpe
+ */
+ mdei = (sal_log_mem_dev_err_info_t*)&plat_err->mem_dev_err;
+ if (mdei->valid.oem_data) {
+ if (mdei->valid.physical_addr)
+ *paddr =mdei->physical_addr;
Quoted 8 lines
+
+ if (mdei->valid.node) {
+ if (ia64_platform_is("sn2"))
+ *node =nasid_to_cnodeid(mdei->node);
+ else
+ *node =mdei->node;
+ }
+ return;
Unnecessary return statement.
Quoted 3 lines
+ }
+ }
+ return;
And here.
Quoted 4 lines
+}
+
+struct page *
+alloc_migrate_page(struct page *ignored, unsigned long node, int **x)
static
Quoted 12 lines
+{
+
+ returnalloc_pages_node(node, GFP_HIGHUSER_MOVABLE, 0);
+}
+
+static int
+ia64_mca_cpe_move_page(u64 paddr, u32 node)
+{+ LIST_HEAD(pagelist); + struct page *page; + int ret; + unsigned long irq_flags;
Quoted 7 lines
+
+local_irq_save(irq_flags);
+ /*
+ * Validate page
+ */
+ if (!paddr)
+ return -1;
-EINVAL ?
Quoted 2 lines
+ if (!ia64_phys_addr_valid(paddr))
+ return -1;
and here
Quoted 2 lines
+ if (!pfn_valid(paddr >>
PAGE_SHIFT))
+ return -1;
ditto
Quoted 9 lines
+
+ /*
+ * convert physical address to page number
+ */
+ page =phys_to_page(paddr);
+
+ if (!spin_trylock(&cpe_migrate_lock)) {
+local_irq_restore(irq_flags);
+ return -1;
-EBUSY?
Quoted 3 lines
+ }
+
+preempt_disable();
Why do we need to disable preempt here?
Quoted 6 lines
+ migrate_prep(); + ret = isolate_lru_page(page, &pagelist); + preempt_enable(); + if (ret) {+
spin_unlock_irqrestore(&cpe_migrate_lock, irq_flags);
+ return ret;
goto would make the error handling cleaner here
Quoted 5 lines
+ }
++ SetPageMemError(page); /* Mark the page as bad */ + ret = migrate_pages(&pagelist, alloc_migrate_page, node); + if (ret = 0)
Quoted 16 lines
+
list_add_tail(&page->lru, &badpagelist);
+
+spin_unlock_irqrestore(&cpe_migrate_lock, irq_flags);
+ return 0;
+}
+
+/*
+ *ia64_mca_cpe_migrate
+ * The worker that does the actual migration. It pulls a
+ * physical address off the list and calls the migration code.
+ *
+ * Inputs
+ * none
+ * Outputs
+ * none
+ */
kerneldoc?
Quoted 50 lines
+static void
+ia64_mca_cpe_migrate(structwork_struct*unused) +{+ int ret; + u64 paddr; + u16 node; + + do { + if (cpe_paddr[cpe_tail]) { + paddr = cpe_paddr[cpe_tail]; + node = cpe_node[cpe_tail]; + + ret = ia64_mca_cpe_move_page(paddr, node); + if (ret <= 0) + /* + * Even though the return status is negative, + * clear the entry. If the same address has + * another CPE it will be re-added to the list. + */ + cpe_paddr[cpe_tail] = 0; + + } + if (++cpe_tail >= CE_HISTORY_LENGTH) + cpe_tail = 0; + + } while (cpe_tail != cpe_head); + work_scheduled = 0; +} +static DECLARE_WORK(cpe_enable_work, ia64_mca_cpe_migrate); + +/* + * ce_setup_migrate + * Get the physical address out of the CPE record, add it + * to the list of addresses to migrate (if not already on), + * and schedule the back end worker task. This is called + * in interrupt context so cannot directly call the migration + * code. + * + * Inputs + * rec The CPE record + * Outputs + * 1 on Success, -1 on failure + */ +static int +ce_setup_migrate(void *rec) +{ + u64 paddr; + u16 node; + /* int head, tail; */ + int i;
Quoted 3 lines
+
+ if (!rec)
+ return -1;
-EINVAL?
Quoted 4 lines
+
+get_physical_address(rec, &paddr, &node);
+ if (!paddr)
+ return -1;
and here
Quoted 12 lines
+
+ if (!((cpe_head =cpe_tail) && (cpe_paddr[cpe_head] = 0)))
+ /*
+ * List not empty
+ */
+ for (i = 0; i <CE_HISTORY_LENGTH; i++)
+ if ((PAGE_ALIGN(cpe_paddr[i])) =PAGE_ALIGN(paddr))
+ return 1; /* already on the list */
+
+ if (cpe_paddr[cpe_head] = 0) {
+ cpe_paddr[cpe_head] = paddr;
+ cpe_node[cpe_head] = node;
Quoted 8 lines
+
+ if (++cpe_head >=CE_HISTORY_LENGTH)
+cpe_head= 0;
+ }
+
+ if (!work_scheduled) {
+work_scheduled= 1;
+schedule_work(&cpe_enable_work);
So you must not schedule cpe_enable_work if it's already in progress. Why?
Quoted 10 lines
+ }
+
+ return 1;
+}
+
+/*
+ * ======================================> + */ +
+int
+freeOneBadPage(unsigned long paddr)
naming and static
Quoted 16 lines
+{
+ struct page *page, *page2, *target;
+
+ /*
+ * Verify page address
+ */
+ target =phys_to_page(paddr);
+list_for_each_entry_safe(page, page2, &badpagelist, lru){
+ if (page != target)
+ continue;
++ ClearPageMemError(page); /* Mark the page as good */ + move_to_lru(page); + list_del(&page->lru); + totalbad_pages--; + break;
Quoted 6 lines
+ }
+ return 0;
+}
+
+int
+freeAllBadPages(void)
here as well
Quoted 67 lines
+{
+ struct page *page, *page2;
+
+list_for_each_entry_safe(page, page2, &badpagelist, lru){
+ ClearPageMemError(page); /* Mark the page as good */
+ totalbad_pages--;+ } + putback_lru_pages(&badpagelist); + return 0; +} + +#define OPT_LEN 16 + +static ssize_t +padpage_write(struct file *file, const char __user *user, + size_t count, loff_t *data) +{ + char optstr[OPT_LEN]; + unsigned long opt; + int len = OPT_LEN; + + if (count < len) + len = count; + + if (copy_from_user(optstr, user, len)) + return -EFAULT; + optstr[len] = '\0'; + + opt = simple_strtoul(optstr, NULL, 0); + if (opt = 0) + freeAllBadPages(); + else + freeOneBadPage(opt); + + return count; +} + +int +badpage_show(struct seq_file *file, void *data) +{ + struct page *page, *page2; + int i = 0; + + seq_printf(file, "Bad RAM: %d kB, %d pages marked bad\n" + "List of bad physical pages\n", + totalbad_pages << (PAGE_SHIFT - 10), totalbad_pages); + list_for_each_entry_safe(page, page2, &badpagelist, lru) { + seq_printf(file, " 0x%011lx", page_to_phys(page)); + if (!(++i % 5)) + seq_printf(file, "\n"); + } + seq_printf(file, "\n"); + return 0; +} + +int +badpage_open(struct inode *inode, struct file *file) +{ + return single_open(file, badpage_show, NULL); +} + +static struct file_operations proc_badpage_operations = { + .open = badpage_open, + .write = padpage_write, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release,
Quoted 16 lines
+};
+
+static structproc_dir_entry*proc_badpage; +
+int__initcpe_migrate_external_handler_init(void)+{
+spin_lock_init(&cpe_migrate_lock);
+
+ /* register external ce handler */
+ if (ia64_reg_CE_extension(ce_setup_migrate)) {
+printk(KERN_ERR "ia64_reg_CE_extension failed.\n");
+ return-EFAULT;
+ }
+cpe_poll_enabled=cpe_polling_enabled;
+
+proc_badpage=create_proc_entry(BADRAM_BASENAME, 0644, NULL);
Why is this file not in sysfs?
Quoted 215 lines
+ if (
proc_badpage= NULL) {
+ printk(KERN_ERR "unable to create %s proc entry",
+BADRAM_BASENAME);
+ return-EINVAL;+ } + proc_badpage->proc_fops = &proc_badpage_operations; + printk(KERN_INFO "Registered badram Driver\n"); + return 0; +} + +void __exit cpe_migrate_external_handler_exit(void) +{ + /* unregister external mca handlers */ + ia64_unreg_CE_extension(); + remove_proc_entry(BADRAM_BASENAME, NULL);+}
+
+module_init(cpe_migrate_external_handler_init); +module_exit(cpe_migrate_external_handler_exit);
+
+module_param(cpe_polling_enabled, int, 0644); +MODULE_PARM_DESC(cpe_polling_enabled,
+ "Enable polling with migration");
+
+MODULE_AUTHOR("Russ Anderson <[email protected]>"); +MODULE_DESCRIPTION("ia64 Corrected Error page migration driver"); +MODULE_LICENSE("GPL");
Index:linus/arch/ia64/kernel/mca.c
=================================> ---linus.orig/arch/ia64/kernel/mca.c2008-05-01 19:36:40.000000000 -0500
+++linus/arch/ia64/kernel/mca.c2008-05-01 19:36:49.000000000 -0500
@@ -68,6 +68,9 @@
*
* 2007-04-27 Russ Anderson <[email protected]>
* Support multiple cpus going throughOS_MCAin the same event.
+ *
+ * 2008-04-22 Russ Anderson <[email protected]>
+ * Migrate data off pages with correctable memory errors.
*/
#include <linux/jiffies.h>
#include <linux/types.h>
@@ -163,7 +166,11 @@ static intcmc_polling_enabled= 1;
* but encounters problems retrieving CPE logs. This should only be
* necessary for debugging.
*/
-static intcpe_poll_enabled= 1;
+intcpe_poll_enabled= 1; +EXPORT_SYMBOL(cpe_poll_enabled);
+
+LIST_HEAD(badpagelist);
+EXPORT_SYMBOL(badpagelist);extern void
salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe);@@ -525,6 +532,28 @@
EXPORT_SYMBOL_GPL(mca_recover_range);#ifdef CONFIG_ACPI+/* Function pointer to Corrected Error memory migration driver */ +int (*ia64_mca_ce_extension)(void *) = NULL;
+
+int
+ia64_reg_CE_extension(int (*fn)(void *))
+{
+ if (ia64_mca_ce_extension)
+ return 1;
+
+ia64_mca_ce_extension= fn;
+ return 0;
+}
+EXPORT_SYMBOL(ia64_reg_CE_extension);
+
+void
+ia64_unreg_CE_extension(void)
+{
+ if (ia64_mca_ce_extension)
+ia64_mca_ce_extension= NULL;
+}
+EXPORT_SYMBOL(ia64_unreg_CE_extension);
+
intcpe_vector= -1;
intia64_cpe_irq= -1;@@ -534,6 +563,7 @@
ia64_mca_cpe_int_handler(intcpe_irq, vstatic unsigned long cpe_history[CPE_HISTORY_LENGTH]; static int index; static DEFINE_SPINLOCK(cpe_history_lock);+ int recover;
IA64_MCA_DEBUG("%s: received interrupt vector = %#x on CPU %d\n",
__func__,cpe_irq,smp_processor_id());
@@ -580,6 +610,8 @@ia64_mca_cpe_int_handler(intcpe_irq, v
out:/* Get the CPE error record and log it */ ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE);+ recover = (
ia64_mca_ce_extension&& ia64_mca_ce_extension(
+IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_CPE)));return IRQ_HANDLED;}
Index: linus/arch/ia64/Kconfig
=================================> --- linus.orig/arch/ia64/Kconfig 2008-05-01 19:36:40.000000000 -0500
+++ linus/arch/ia64/Kconfig 2008-05-01 19:36:49.000000000 -0500
@@ -456,6 +456,9 @@ configCOMPAT_FOR_U64_ALIGNMENT
configIA64_MCA_RECOVERY
tristate "MCA recovery from errors other than TLB."+config
IA64_CPE_MIGRATE
+ tristate "Migrate data off pages with correctable errors"
+
config PERFMON
bool "Performance monitor support"
help
Index:linus/include/asm-ia64/mca.h
=================================> ---linus.orig/include/asm-ia64/mca.h2008-05-01 19:36:40.000000000 -0500
+++linus/include/asm-ia64/mca.h2008-05-01 19:36:49.000000000 -0500
@@ -137,6 +137,7 @@ extern unsigned long __per_cpu_mca[NR_CPextern int
cpe_vector;
extern intia64_cpe_irq;
+extern intcpe_poll_enabled;
extern voidia64_mca_init(void);
extern voidia64_mca_cpu_init(void *);
extern voidia64_os_mca_dispatch(void);
@@ -150,9 +151,13 @@ extern void ia64_slave_init_handler(void
extern voidia64_mca_cmc_vector_setup(void);
extern intia64_reg_MCA_extension(int (*fn)(void *, struct ia64_sal_os_state *)); extern voidia64_unreg_MCA_extension(void);
+extern intia64_reg_CE_extension(int (*fn)(void *));
+extern voidia64_unreg_CE_extension(void);
extern u64ia64_get_rnat(u64 *);
extern voidia64_mca_printk(const char * fmt, ...)__attribute__ ((format (printf, 1, 2)));+
+extern structlist_headbadpagelist;struct ia64_mca_notify_die { struct ia64_sal_os_state *sos; Index: linus/mm/migrate.c =================================> --- linus.orig/mm/migrate.c 2008-05-01 19:36:40.000000000 -0500 +++ linus/mm/migrate.c 2008-05-01 19:36:49.000000000 -0500 @@ -64,6 +64,7 @@ int isolate_lru_page(struct page *page, } return ret;}
+EXPORT_SYMBOL(isolate_lru_page);/*
*migrate_prep()needs to be called before we start compiling a list of pages
@@ -81,8 +82,9 @@ intmigrate_prep(void)return 0;}
+EXPORT_SYMBOL(migrate_prep);-static inline void
move_to_lru(struct page *page)+inline voidmove_to_lru(struct page *page){ if (PageActive(page)) { /* @@ -96,6 +98,7 @@ static inline void move_to_lru(struct pa } put_page(page);}
+EXPORT_SYMBOL(move_to_lru);/*
* Add isolated pages on the list back to the LRU.
@@ -115,6 +118,7 @@ int putback_lru_pages(structlist_head*} return count;}
+EXPORT_SYMBOL(putback_lru_pages);/*
* Restore a potential migration pte to a working pte entry
@@ -805,6 +809,7 @@ out:return nr_failed + retry;}
+EXPORT_SYMBOL(migrate_pages);#ifdef
CONFIG_NUMA
/*
Index:linus/include/asm-ia64/page.h
=================================> ---linus.orig/include/asm-ia64/page.h2008-05-01 19:36:40.000000000 -0500
+++linus/include/asm-ia64/page.h2008-05-01 19:36:49.000000000 -0500
@@ -122,6 +122,7 @@ extern unsigned longmax_low_pfn;
#endif#define
page_to_phys(page)(page_to_pfn(page)<<PAGE_SHIFT)
+#definephys_to_page(kaddr)(pfn_to_page(kaddr >> PAGE_SHIFT))
#definevirt_to_page(kaddr)pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
#definepfn_to_kaddr(pfn)__va((pfn) << PAGE_SHIFT)Index:
linus/mm/page_alloc.c
=================================> ---linus.orig/mm/page_alloc.c2008-05-01 19:36:40.000000000 -0500
+++linus/mm/page_alloc.c2008-05-01 19:36:49.000000000 -0500
@@ -72,6 +72,7 @@ unsigned longtotalreserve_pages__read_
longnr_swap_pages;
intpercpu_pagelist_fraction;
unsigned inttotalbad_pages;
+EXPORT_SYMBOL(totalbad_pages);#ifdef
CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
intpageblock_order__read_mostly;
Index:linus/include/linux/migrate.h
=================================> ---linus.orig/include/linux/migrate.h2008-05-01 19:36:40.000000000 -0500
+++linus/include/linux/migrate.h2008-05-01 19:36:49.000000000 -0500
@@ -38,6 +38,7 @@ extern intmigrate_prep(void);
extern int migrate_vmas(structmm_struct*mm,
constnodemask_t*from, constnodemask_t*to,
unsigned long flags);
+extern voidmove_to_lru(struct page *);
#else
static inline intvma_migratable(struct vm_area_struct *vma)
Quoted 6 lines
{ return 0; } @@ -59,6 +60,7 @@ static inline int migrate_vmas(struct mm { return -ENOSYS;}
+static inline voidmove_to_lru(struct page *p){ return; }
unnecessary return statement
Quoted 12 lines
/* Possible settings for the
migrate_page()method inaddress_operations*/ #definemigrate_pageNULL
--
Russ Anderson, OS RAS/Partitioning Project Lead
SGI - Silicon Graphics Inc [email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Quoted 10 lines · Pekka Enberg
Hi Russ,
Quoted 7 lines · Russ Anderson
Migrate data off pages with correctable memory errors. This patch is the
ia64 specific piece. It connects the CPE handler to the page migration code. It is implemented as a kernel loadable module, similar to the mca recovery code (mca_recovery.ko). This allows the feature to be turned off by uninstalling the module. Creates/proc/badramto display bad page information and free bad pages.
Quoted 3 lines
Quoted 1 line
Signed-off-by Russ Anderson <[email protected]>I think sparse and checkpatch would have caught most of these but here goes:
I did run checkpatch.pl. The two warnings were a false positive and the other I let slide. I'll make the rest of your suggested changes.
---
linux> scripts/checkpatch.pl ../patches/cpe.migrate.v2
WARNING: Use #include <linux/mca.h> instead of <asm/mca.h> #80: FILE: arch/ia64/kernel/cpe_migrate.c:41:
+#include <asm/mca.h>
WARNING: consider using strict_strtoul in preference to simple_strtoul #340: FILE: arch/ia64/kernel/cpe_migrate.c:301:
+ opt = simple_strtoul(optstr, NULL, 0);
total: 0 errors, 2 warnings, 548 lines checked
---
Quoted 12 lines
Quoted 11 lines
+ if (cpe_paddr[cpe_head] = 0) {
+ cpe_paddr[cpe_head] = paddr;
+ cpe_node[cpe_head] = node;
+
+ if (++cpe_head >=CE_HISTORY_LENGTH)
+cpe_head= 0;
+ }
+
+ if (!work_scheduled) {
+work_scheduled= 1;
+schedule_work(&cpe_enable_work);
Quoted 1 line
So you must not schedule
cpe_enable_workif it's already in progress. Why?
If there is already a worker thread scheduled, it will process all
the addresses on the queue, including new entries. So all ce_setup_migrate() needs to do is add the new entry to the queue. The CPE interrupt can come in faster than the worker thread can migrate the pages. Scheduling another worker thread on each CPE interrupt when there is already one scheduled/running would be overkill.
Quoted 3 lines
Quoted 1 line
+
proc_badpage=create_proc_entry(BADRAM_BASENAME, 0644, NULL);Why is this file not in sysfs?
/proc seemed like a appropriate place. Where would you suggest in sysfs?
Signature · 2 lines
Russ Anderson, OS RAS/Partitioning Project Lead SGI - Silicon Graphics Inc [email protected]
Hi Russ,
Quoted 8 lines · Russ Anderson
Quoted 1 line
I think sparse and checkpatch would have caught most of these but here goes:
I did run checkpatch.pl. The two warnings were a false positive and the other I let slide. I'll make the rest of your suggested changes.
WARNING: consider using
strict_strtoulin preference tosimple_strtoul#340: FILE:arch/ia64/kernel/cpe_migrate.c:301:
+ opt =simple_strtoul(optstr, NULL, 0);
Why do you think this is a false positive? We just converted SLUB over to use strict_stroul() as suggested by Andrew.
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
+ if (cpe_paddr[cpe_head] = 0) {
+ cpe_paddr[cpe_head] = paddr;
+ cpe_node[cpe_head] = node;
+
+ if (++cpe_head >=CE_HISTORY_LENGTH)
+cpe_head= 0;
+ }
+
+ if (!work_scheduled) {
+work_scheduled= 1;
+schedule_work(&cpe_enable_work);
Quoted 8 lines
Quoted 1 line
So you must not schedule
cpe_enable_workif it's already in progress. Why?If there is already a worker thread scheduled, it will process all
the addresses on the queue, including new entries. So allce_setup_migrate()needs to do is add the new entry to the queue. The CPE interrupt can come in faster than the worker thread can migrate the pages. Scheduling another worker thread on each CPE interrupt when there is already one scheduled/running would be overkill.
Okay. Maybe a kthread would be cleaner here then (which sleeps when the buffer is empty)? I didn't notice any locking for cpe_paddr and cpe_node. Why is that?
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
+
proc_badpage=create_proc_entry(BADRAM_BASENAME, 0644, NULL);Why is this file not in sysfs?
/proc seemed like a appropriate place. Where would you suggest in sysfs?
Oh, /proc is for _process specific_ files although historically it has been (ab)used for other files as well. I think something like /sys/kernel/badram would be appropriate. Christoph, Greg?
Pekka
Quoted 5 lines · Pekka J Enberg
Hi Russ,
Quoted 2 lines · Russ Anderson
Quoted 1 line
I think sparse and checkpatch would have caught most of these but here goes:
Quoted 8 lines
Quoted 6 lines
I did run checkpatch.pl. The two warnings were a false positive and the other I let slide. I'll make the rest of your suggested changes.
WARNING: consider using
strict_strtoulin preference tosimple_strtoul#340: FILE:arch/ia64/kernel/cpe_migrate.c:301:
+ opt =simple_strtoul(optstr, NULL, 0);Why do you think this is a false positive?
The false positive is:
---
WARNING: Use #include <linux/mca.h> instead of <asm/mca.h> #77: FILE: arch/ia64/kernel/cpe_migrate.c:40:
+#include <asm/mca.h>
---
linux/mca.h is a different file than asm-ia64/mca.h, so using <linux/mca.h> instead of <asm/mca.h> will cause build problems.
Quoted 2 lines
We just converted SLUB over to
usestrict_stroul()as suggested by Andrew.
I'll use strict_stroul().
Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
+ if (cpe_paddr[cpe_head] = 0) {
+ cpe_paddr[cpe_head] = paddr;
+ cpe_node[cpe_head] = node;
+
+ if (++cpe_head >=CE_HISTORY_LENGTH)
+cpe_head= 0;
+ }
+
+ if (!work_scheduled) {
+work_scheduled= 1;
+schedule_work(&cpe_enable_work);Quoted 8 lines
Quoted 1 line
So you must not schedule
cpe_enable_workif it's already in progress. Why?If there is already a worker thread scheduled, it will process all
the addresses on the queue, including new entries. So allce_setup_migrate()needs to do is add the new entry to the queue. The CPE interrupt can come in faster than the worker thread can migrate the pages. Scheduling another worker thread on each CPE interrupt when there is already one scheduled/running would be overkill.
Quoted 3 lines
Okay. Maybe a kthread would be cleaner here then (which sleeps when the buffer is empty)? I didn't notice any locking for
cpe_paddrandcpe_node. Why is that?
Do you mean a lock so that cpe_paddr & cpe_node are updated atomicly? The hole being the new page is allocated on a different node than the old page.
Signature · 2 lines
Russ Anderson, OS RAS/Partitioning Project Lead SGI - Silicon Graphics Inc [email protected]
Quoted 6 lines · Russ Anderson
Migrate data off pages with correctable memory errors. This patch is the ia64 specific piece. It connects the CPE handler to the page migration code. It is implemented as a kernel loadable module, similar to the mca recovery code (mca_recovery.ko). This allows the feature to be turned off by uninstalling the module. Creates
/proc/badramto display bad page information and free bad pages.
How do you know what pages have excessive errors? And how is excessive defined? Surely you don't keep a per page error count? It's unclear from your patch.
Anyways I don't think this should be ia64 specific, but generic code.
I also have my doubts about making such small code subsystems modules. Modules always get rounded to pages so it ultimatively just wastes memory.
-Andi
Quoted 11 lines · Andi Kleen
Quoted 6 lines · Russ Anderson
Migrate data off pages with correctable memory errors. This patch is the ia64 specific piece. It connects the CPE handler to the page migration code. It is implemented as a kernel loadable module, similar to the mca recovery code (mca_recovery.ko). This allows the feature to be turned off by uninstalling the module. Creates
/proc/badramto display bad page information and free bad pages.How do you know what pages have excessive errors? And how is excessive defined? Surely you don't keep a per page error count? It's unclear from your patch.
The code migrates on the first correctable error on a page, so "excessive" is one. Yes, keeping a per page count gets problematic, especially as memories get larger. The issue of the right metric for when to migrate will always be debatable and the "right" answer likely will depend on the physical memory technology.
Quoted 1 line
Anyways I don't think this should be ia64 specific, but generic code.
The actual migration code is generic, in mm/migrate.c.
The ia64 kernel module piece is very arch specific. It ties into the ia64 CPE handler. It gets the page address from the CPE record, based on the ia64 error handling spec. Each arch will have a different way of determining the physical address of the correctable error (for example).
Quoted 2 lines
I also have my doubts about making such small code subsystems modules. Modules always get rounded to pages so it ultimatively just wastes memory.
CONFIG_IA64_CPE_MIGRATE=m builds it as module. CONFIG_IA64_CPE_MIGRATE=y builds it as part of the kernel.
Signature · 2 lines
Russ Anderson, OS RAS/Partitioning Project Lead SGI - Silicon Graphics Inc [email protected]
Quoted 9 lines · Russ Anderson
Quoted 8 lines · Andi Kleen
Quoted 6 lines · Russ Anderson
Migrate data off pages with correctable memory errors. This patch is the ia64 specific piece. It connects the CPE handler to the page migration code. It is implemented as a kernel loadable module, similar to the mca recovery code (mca_recovery.ko). This allows the feature to be turned off by uninstalling the module. Creates
/proc/badramto display bad page information and free bad pages.
Quoted 8 lines
Quoted 2 lines
How do you know what pages have excessive errors? And how is excessive defined? Surely you don't keep a per page error count? It's unclear from your patch.
The code migrates on the first correctable error on a page, so "excessive" is one. Yes, keeping a per page count gets problematic, especially as memories get larger. The issue of the right metric for when to migrate will always be debatable and the "right" answer likely will depend on the physical memory technology.
That doesn't sound like a good metric. What happens for example if you have a flakey memory controller or DIMM socket that just occasionally adds bit errors on transfer without the DIMM actually being bad? I doubt such a simple heuristic is a good one.
The x86-64 machine check design has a database connected to a user space program that can keep track of errors. I suspect something like this is a better solution.
Quoted 9 lines
Quoted 2 lines
Anyways I don't think this should be ia64 specific, but generic code.
The actual migration code is generic, in
mm/migrate.c.The ia64 kernel module piece is very arch specific. It ties into the ia64 CPE handler. It gets the page address from the CPE record, based on the ia64 error handling spec. Each arch will have a different way of determining the physical address of the correctable error (for example).
Still at least part of the code should be generic.
Also in general it's good that you finally realized that trying to write lockless machine check handlers was a bad idea and that to do any meaninful handling requires a proper process context. But I still wonder when you move the rest of the ia64 machine check handling to that too so that e.g. the sched.c hacks for lockless handling can be removed?
Quoted 4 lines
Quoted 2 lines
I also have my doubts about making such small code subsystems modules. Modules always get rounded to pages so it ultimatively just wastes memory.
CONFIG_IA64_CPE_MIGRATE=mbuilds it as module.
It's far too small for it.
-Andi
Quoted 8 lines · Andi Kleen
Quoted 6 lines · Russ Anderson
The code migrates on the first correctable error on a page, so "excessive" is one. Yes, keeping a per page count gets problematic, especially as memories get larger. The issue of the right metric for when to migrate will always be debatable and the "right" answer likely will depend on the physical memory technology.
Quoted 4 lines
That doesn't sound like a good metric. What happens for example if you have a flakey memory controller or DIMM socket that just occasionally adds bit errors on transfer without the DIMM actually being bad? I doubt such a simple heuristic is a good one.
Then occasionally pages on that DIMM get marked bad and are no longer used. There have been cases where there only one or two correctable errors before it degrades to and uncorrectable. The "right" answer likely will depend on the physical memory technology.
Keep in mind the discarded pages can be later be freed.
Quoted 3 lines
The x86-64 machine check design has a database connected to a user space program that can keep track of errors. I suspect something like this is a better solution.
I'll have to find out more.
Quoted 10 lines
Quoted 8 lines
Quoted 1 line
Anyways I don't think this should be ia64 specific, but generic code.
The actual migration code is generic, in
mm/migrate.c.The ia64 kernel module piece is very arch specific. It ties into the ia64 CPE handler. It gets the page address from the CPE record, based on the ia64 error handling spec. Each arch will have a different way of determining the physical address of the correctable error (for example).
Still at least part of the code should be generic.
Part is generic. What other parts do you want generic? I'm not opposed to the idea, but things like parsing the CPE record is platform specific.
Quoted 5 lines
Also in general it's good that you finally realized that trying to write lockless machine check handlers was a bad idea and that to do any meaninful handling requires a proper process context. But I still wonder when you move the rest of the ia64 machine check handling to that too so that e.g. the
sched.chacks for lockless handling can be removed?
Are you refereing to ia64 MCAs that can surface at any time? That is an Intel CPU design issue far outside anything I can control. CPEs are regular interrupts, which is why they can be handled in a sane manner.
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
I also have my doubts about making such small code subsystems modules. Modules always get rounded to pages so it ultimatively just wastes memory.
CONFIG_IA64_CPE_MIGRATE=mbuilds it as module.It's far too small for it.
Maybe I do not understand the issue. Is the issue wasting part of one
page? Does building CONFIG_IA64_CPE_MIGRATE=y waste memory?
Signature · 2 lines
Russ Anderson, OS RAS/Partitioning Project Lead SGI - Silicon Graphics Inc [email protected]