x86, mce: machine check recovery for applications
Subsystems (7)
cover x86, mce: machine check recovery for applications
This series adds code to recognise the machine check signature for a recoverable error in the data path (Advanced SKUs of "Sandy Bridge" server processors are the first to be able to allow s/w recovery for this case), save the required information in the machine check handler and then call to the generic memory_failure() code to try for graceful error recovery (sending SIGBUS to affected process(es)).
Updates since last version (December 15th)
Part1-4: unchanged
Part5: Changed stub function for CONFIG_MEMORY_FAILURE=n case to BUG_ON if it is handed an MF_ACTION_REQUIRED case (this indicates an error in severity calculation). Drop "Memory error recovered" message (enough chatter already).
Part6: Only pass back an ACTION_REQUIRED severity to a kernel if it is built with CONFIG_MEMORY_FAILURE=y (i.e. has the code to take the action).
Whole series is available in:
git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras.git mce-recovery
Tony Luck (6):
HWPOISON: clean up memory_failure() vs. __memory_failure()
HWPOISON: Add code to handle "action required" errors. x86, mce: create helper function to save addr/misc when needed x86, mce: Add mechanism to safely save information in MCE handler x86, mce: handle "action required" errors
x86, mce: Recognise machine check bank signature for data path error
arch/x86/kernel/cpu/mcheck/mce-severity.c | 16 +++- arch/x86/kernel/cpu/mcheck/mce.c | 179 ++++++++++++++++++++--------- drivers/base/memory.c | 2 +- include/linux/mm.h | 4 +- mm/hwpoison-inject.c | 4 +- mm/madvise.c | 2 +- mm/memory-failure.c | 96 ++++++++-------- 7 files changed, 197 insertions(+), 106 deletions(-)
Signature · 1 line
1.7.3.1
PATCH 1/6 HWPOISON: Clean up memory_failure() vs. __memory_failure()
There is only one caller of memory_failure(), all other users call __memory_failure() and pass in the flags argument explicitly. The lone user of memory_failure() will soon need to pass flags too.
Add flags argument to the callsite in mce.c. Delete the old memory_failure() function, and then rename __memory_failure() without the leading "__".
Provide clearer message when action optional memory errors are ignored.
--- arch/x86/kernel/cpu/mcheck/mce.c | 12 ++++++--- drivers/base/memory.c | 2 +- include/linux/mm.h | 3 +- mm/hwpoison-inject.c | 4 +- mm/madvise.c | 2 +- mm/memory-failure.c | 46 +++++++++++++++++-------------------- 6 files changed, 34 insertions(+), 35 deletions(-)
arch/x86/kernel/cpu/mcheck/mce.c +8 -4
@@ -1046,11 +1046,15 @@ out: }EXPORT_SYMBOL_GPL (do_machine_check); -/* dummy to break dependency. actual code is in mm/memory-failure.c */ -void __attribute__((weak))memory_failure (unsigned long pfn,int vector) +#ifndef CONFIG_MEMORY_FAILURE +int memory_failure (unsigned long pfn,int vector,int flags) { -printk (KERN_ERR "Action optional memory failure at %lx ignored\n" , pfn); +printk (KERN_ERR "Uncorrected memory error in page 0x%lx ignored\n" +"Rebuild kernel with CONFIG_MEMORY_FAILURE=y for smarter handling\n" , pfn); + +return 0 ; } +#endif /* * Called after mce notification in process context. This code
@@ -1068,7 +1072,7 @@ void mce_notify_process(void)unsigned long pfn;mce_notify_irq ();while (mce_ring_get (&pfn)) -memory_failure (pfn,MCE_VECTOR ); +memory_failure (pfn,MCE_VECTOR ,0 ); }static void mce_process_work (struct work_struct *dummy)
drivers/base/memory.c +1 -1
@@ -474,7 +474,7 @@ store_hard_offline_page(struct class *class,if (strict_strtoull (buf,0 , &pfn) <0 )return -EINVAL ; pfn >>=PAGE_SHIFT ; - ret =__memory_failure (pfn,0 ,0 ); + ret =memory_failure (pfn,0 ,0 );return ret ? ret : count; }
include/linux/mm.h +1 -2
@@ -1607,8 +1607,7 @@ void vmemmap_populate_print_last(void);enum mf_flags {MF_COUNT_INCREASED =1 <<0 , }; -extern void memory_failure (unsigned long pfn,int trapno); -extern int __memory_failure (unsigned long pfn,int trapno,int flags); +extern int memory_failure (unsigned long pfn,int trapno,int flags);extern void memory_failure_queue (unsigned long pfn,int trapno,int flags);extern int unpoison_memory (unsigned long pfn);extern int sysctl_memory_failure_early_kill;
mm/hwpoison-inject.c +2 -2
@@ -45,7 +45,7 @@ static int hwpoison_inject(void *data, u64 val)* do a racy check with elevated page count, to make sure PG_hwpoison * will only be set for the targeted owner (or on a free page). * We temporarily take page lock for try_get_mem_cgroup_from_page(). -* __memory_failure() will redo the check reliably inside page lock. +* memory_failure() will redo the check reliably inside page lock. */ lock_page (hpage); err =hwpoison_filter (hpage);
@@ -55,7 +55,7 @@ static int hwpoison_inject(void *data, u64 val) inject:printk (KERN_INFO "Injecting memory failure at pfn %lx\n" , pfn); -return __memory_failure (pfn,18 ,MF_COUNT_INCREASED ); +return memory_failure (pfn,18 ,MF_COUNT_INCREASED ); }static int hwpoison_unpoison (void *data,u64 val)
mm/madvise.c +1 -1
@@ -251,7 +251,7 @@ static int madvise_hwpoison(int bhv, unsigned long start, unsigned long end)printk (KERN_INFO "Injecting memory failure for page %lx at %lx\n" ,page_to_pfn (p), start);/* Ignore return value for now */ -__memory_failure (page_to_pfn (p),0 ,MF_COUNT_INCREASED ); +memory_failure (page_to_pfn (p),0 ,MF_COUNT_INCREASED ); }return ret; }
mm/memory-failure.c +21 -25
64 lines not on this page · raw message
Signature · 1 line
1.7.3.1
PATCH 2/6 HWPOISON: Add code to handle "action required" errors.
In reply to Tony LuckAdd new flag bit "MF_ACTION_REQUIRED" to be used by machine check code to force a signal with si_code = BUS_MCEERR_AR in the case where the error occurs in processor execution context. Pass the flags argument along call chain:
memory_failure() hwpoison_user_mappings() kill_procs() kill_proc()
Drop the "_ao" suffix from kill_procs_ao() and kill_proc_ao() since they can now handle "action required" as well as "action optional" errors.
--- include/linux/mm.h | 1 + mm/memory-failure.c | 50 +++++++++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 21 deletions(-)
include/linux/mm.h +1 -0
@@ -1606,6 +1606,7 @@ void vmemmap_populate_print_last(void);enum mf_flags {MF_COUNT_INCREASED =1 <<0 , +MF_ACTION_REQUIRED =1 <<1 , };extern int memory_failure (unsigned long pfn,int trapno,int flags);extern void memory_failure_queue (unsigned long pfn,int trapno,int flags);
mm/memory-failure.c +29 -21
100 lines not on this page · raw message
Signature · 1 line
1.7.3.1
PATCH 3/6 x86/mce: Create helper function to save addr/misc when needed
In reply to Tony LuckThe MCI_STATUS_MISCV and MCI_STATUS_ADDRV bits in the bank status registers define whether the MISC and ADDR registers respectively contain valid data - provide a helper function to check these bits and read the registers when needed.
In addition, processors that support software error recovery (as indicated by the MCG_SER_P bit in the MCG_CAP register) may include some undefined bits in the ADDR register - mask these out.
--- arch/x86/kernel/cpu/mcheck/mce.c | 31 +++++++++++++++++++++++-------- 1 files changed, 23 insertions(+), 8 deletions(-)
arch/x86/kernel/cpu/mcheck/mce.c +23 -8
@@ -492,6 +492,27 @@ static void mce_report_event(struct pt_regs *regs)irq_work_queue (&__get_cpu_var (mce_irq_work)); } +/* +* Read ADDR and MISC registers. +*/ +static void mce_read_aux (struct mce *m,int i) +{ +if (m->status &MCI_STATUS_MISCV ) + m->misc =mce_rdmsrl (MSR_IA32_MCx_MISC (i)); +if (m->status &MCI_STATUS_ADDRV ) { + m->addr =mce_rdmsrl (MSR_IA32_MCx_ADDR (i)); + +/* +* Mask the reported address by the reported granularity. +*/ +if (mce_ser && (m->status &MCI_STATUS_MISCV )) { +u8 shift =MCI_MISC_ADDR_LSB (m->misc); + m->addr >>= shift; + m->addr <<= shift; + } + } +} +DEFINE_PER_CPU (unsigned, mce_poll_count);/*
@@ -542,10 +563,7 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b) (m.status & (mce_ser ? MCI_STATUS_S : MCI_STATUS_UC))) continue; -if (m.status &MCI_STATUS_MISCV ) - m.misc =mce_rdmsrl (MSR_IA32_MCx_MISC (i)); -if (m.status &MCI_STATUS_ADDRV ) - m.addr =mce_rdmsrl (MSR_IA32_MCx_ADDR (i)); +mce_read_aux (&m, i);if (!(flags &MCP_TIMESTAMP )) m.tsc =0 ;
@@ -981,10 +999,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)if (severity ==MCE_AR_SEVERITY ) kill_it =1 ; -if (m.status &MCI_STATUS_MISCV ) - m.misc =mce_rdmsrl (MSR_IA32_MCx_MISC (i)); -if (m.status &MCI_STATUS_ADDRV ) - m.addr =mce_rdmsrl (MSR_IA32_MCx_ADDR (i)); +mce_read_aux (&m, i);/* * Action optional error. Queue address for later processing.
Signature · 1 line
1.7.3.1
PATCH 4/6 x86/mce: Add mechanism to safely save information in MCE handler
In reply to Tony LuckMachine checks on Intel cpus interrupt execution on all cpus, regardless of interrupt masking. We have a need to save some data about the cause of the machine check (physical address) in the machine check handler that can be retrieved later to attempt recovery in a more flexible execution state.
--- arch/x86/kernel/cpu/mcheck/mce.c | 43 ++++++++++++++++++++++++++++++++++++++ 1 files changed, 43 insertions(+), 0 deletions(-)
arch/x86/kernel/cpu/mcheck/mce.c +43 -0
@@ -887,6 +887,49 @@ static void mce_clear_state(unsigned long *toclear) }/* +* Need to save faulting physical address associated with a process +* in the machine check handler some place where we can grab it back +* later in mce_notify_process() +*/ +#define MCE_INFO_MAX 16 + +struct mce_info { +atomic_t inuse; +struct task_struct *t; +__u64 paddr; +} mce_info[MCE_INFO_MAX ]; + +static void mce_save_info (__u64 addr) +{ +struct mce_info *mi; + +for (mi = mce_info; mi < &mce_info[MCE_INFO_MAX ]; mi++) { +if (atomic_cmpxchg (&mi->inuse,0 ,1 ) ==0 ) { + mi->t = current; + mi->paddr = addr; +return ; + } + } + +mce_panic ("Too many concurrent recoverable errors" ,NULL ,NULL ); +} + +static struct mce_info *mce_find_info (void ) +{ +struct mce_info *mi; + +for (mi = mce_info; mi < &mce_info[MCE_INFO_MAX ]; mi++) +if (atomic_read (&mi->inuse) && mi->t == current) +return mi; +return NULL ; +} + +static void mce_clear_info (struct mce_info *mi) +{ +atomic_set (&mi->inuse,0 ); +} + +/* * The actual machine check handler. This only handles real * exceptions when something got corrupted coming in through int 18. *
Signature · 1 line
1.7.3.1
PATCH 5/6 x86/mce: Handle "action required" errors
In reply to Tony LuckAll non-urgent actions (reporting low severity errors and handling "action-optional" errors) are now handled by a work queue. This means that TIF_MCE_NOTIFY can be used to block execution for a thread experiencing an "action-required" fault until we get all cpus out of the machine check handler (and the thread that hit the fault into mce_notify_process().
We use the new mce_{save,find,clear}_info() API to get information from do_machine_check() to mce_notify_process(), and then use the newly improved memory_failure(..., MF_ACTION_REQUIRED) to handle the error (possibly signalling the process).
Update some comments to make the new code flows clearer.
--- arch/x86/kernel/cpu/mcheck/mce.c | 95 +++++++++++++++++++++----------------- 1 files changed, 53 insertions(+), 42 deletions(-)
arch/x86/kernel/cpu/mcheck/mce.c +53 -42
@@ -982,7 +982,9 @@ void do_machine_check(struct pt_regs *regs, long error_code)barrier ();/* -* When no restart IP must always kill or panic. +* When no restart IP might need to kill or panic. +* Assume the worst for now, but if we find the +* severity is MCE_AR_SEVERITY we have other options. */ if (!(m.mcgstatus &MCG_STATUS_RIPV )) kill_it =1 ;
@@ -1036,12 +1038,6 @@ void do_machine_check(struct pt_regs *regs, long error_code)continue ; } -/* -* Kill on action required. -*/ -if (severity ==MCE_AR_SEVERITY ) - kill_it =1 ; -mce_read_aux (&m, i);/*
@@ -1062,6 +1058,9 @@ void do_machine_check(struct pt_regs *regs, long error_code) } } +/* mce_clear_state will clear *final, save locally for use later */ + m = *final; +if (!no_way_out)mce_clear_state (toclear);
@@ -1073,27 +1072,22 @@ void do_machine_check(struct pt_regs *regs, long error_code) no_way_out = worst >=MCE_PANIC_SEVERITY ;/* -* If we have decided that we just CAN'T continue, and the user -* has not set tolerant to an insane level, give up and die. -* -* This is mainly used in the case when the system doesn't -* support MCE broadcasting or it has been disabled. +* At insane "tolerant" levels we take no action. Otherwise +* we only die if we have no other choice. For less serious +* issues we try to recover, or limit damage to the current +* process. */ -if (no_way_out && tolerant <3 ) -mce_panic ("Fatal machine check on current CPU" , final, msg); - -/* -* If the error seems to be unrecoverable, something should be -* done. Try to kill as little as possible. If we can kill just -* one task, do that. If the user has set the tolerance very -* high, don't try to do anything at all. -*/ - -if (kill_it && tolerant <3 ) -force_sig (SIGBUS , current); - -/* notify userspace ASAP */ -set_thread_flag (TIF_MCE_NOTIFY ); +if (tolerant <3 ) { +if (no_way_out) +mce_panic ("Fatal machine check on current CPU" , &m, msg); +if (worst ==MCE_AR_SEVERITY ) { +/* schedule action before return to userland */ +mce_save_info (m.addr); +set_thread_flag (TIF_MCE_NOTIFY ); + }else if (kill_it) { +force_sig (SIGBUS , current); + } + }if (worst >0 )mce_report_event (regs);
@@ -1107,6 +1101,8 @@ EXPORT_SYMBOL_GPL(do_machine_check);#ifndef CONFIG_MEMORY_FAILURE int memory_failure (unsigned long pfn,int vector,int flags) { +/* mce_severity() should not hand us an ACTION_REQUIRED error */ +BUG_ON (flags &MF_ACTION_REQUIRED );printk (KERN_ERR "Uncorrected memory error in page 0x%lx ignored\n" "Rebuild kernel with CONFIG_MEMORY_FAILURE=y for smarter handling\n" , pfn);
@@ -1115,27 +1111,44 @@ int memory_failure(unsigned long pfn, int vector, int flags)#endif /* -* Called after mce notification in process context. This code -* is allowed to sleep. Call the high level VM handler to process -* any corrupted pages. -* Assume that the work queue code only calls this one at a time -* per CPU. -* Note we don't disable preemption, so this code might run on the wrong -* CPU. In this case the event is picked up by the scheduled work queue. -* This is merely a fast path to expedite processing in some common -* cases. +* Called in process context that interrupted by MCE and marked with +* TIF_MCE_NOTIFY, just before returning to erroneous userland. +* This code is allowed to sleep. +* Attempt possible recovery such as calling the high level VM handler to +* process any corrupted pages, and kill/signal current process if required. +* Action required errors are handled here. */ void mce_notify_process (void ) {unsigned long pfn; -mce_notify_irq (); -while (mce_ring_get (&pfn)) -memory_failure (pfn,MCE_VECTOR ,0 ); +struct mce_info *mi =mce_find_info (); + +if (!mi) +mce_panic ("Lost physical address for unconsumed uncorrectable error" ,NULL ,NULL ); + pfn = mi->paddr >>PAGE_SHIFT ; + +clear_thread_flag (TIF_MCE_NOTIFY ); + +pr_err ("Uncorrected hardware memory error in user-access at %llx" , + mi->paddr); +if (memory_failure (pfn,MCE_VECTOR ,MF_ACTION_REQUIRED ) <0 ) { +pr_err ("Memory error not recovered" ); +force_sig (SIGBUS , current); + } +mce_clear_info (mi); } +/* +* Action optional processing happens here (picking up +* from the list of faulting pages that do_machine_check() +* placed into the "ring"). +*/ static void mce_process_work (struct work_struct *dummy) { -mce_notify_process (); +unsigned long pfn; + +while (mce_ring_get (&pfn)) +memory_failure (pfn,MCE_VECTOR ,0 ); }#ifdef CONFIG_X86_MCE_INTEL
@@ -1225,8 +1238,6 @@ int mce_notify_irq(void)/* Not more than two messages every minute */ static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); -clear_thread_flag (TIF_MCE_NOTIFY ); -if (test_and_clear_bit (0 , &mce_need_notify)) {/* wake processes polling /dev/mcelog */ wake_up_interruptible (&mce_chrdev_wait);
Signature · 1 line
1.7.3.1
PATCH 6/6 x86/mce: Recognise machine check bank signature for data path error
In reply to Tony LuckAction required data path signature is defined in table 15-19 of SDM:
+-----------------------------------------------------------------------------+ | SRAR Error | Valid | OVER | UC | EN | MISCV | ADDRV | PCC | S | AR | MCACOD | | Data Load | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0x134 | +-----------------------------------------------------------------------------+
Recognise this, and pass MCE_AR_SEVERITY code back to do_machine_check() if we have the action handler configured (CONFIG_MEMORY_FAILURE=y)
--- arch/x86/kernel/cpu/mcheck/mce-severity.c | 16 +++++++++++++++- 1 files changed, 15 insertions(+), 1 deletions(-)
arch/x86/kernel/cpu/mcheck/mce-severity.c +15 -1
@@ -54,6 +54,7 @@ static struct severity {#define MASK (x, y) .mask = x, .result = y#define MCI_UC_S (MCI_STATUS_UC|MCI_STATUS_S)#define MCI_UC_SAR (MCI_STATUS_UC|MCI_STATUS_S|MCI_STATUS_AR) +#define MCI_ADDR (MCI_STATUS_ADDRV|MCI_STATUS_MISCV)#define MCACOD 0xffff MCESEV(
@@ -102,11 +103,24 @@ static struct severity { SER, BITCLR(MCI_STATUS_S) ), -/* AR add known MCACODs here */ MCESEV( PANIC, "Action required with lost events", SER, BITSET(MCI_STATUS_OVER|MCI_UC_SAR) ), + +/* known AR MCACODs: */ +#ifdef CONFIG_MEMORY_FAILURE +MCESEV ( +KEEP ,"HT thread notices Action required: data load error" , +SER ,MASK (MCI_STATUS_OVER |MCI_UC_SAR |MCI_ADDR |MCACOD ,MCI_UC_SAR |MCI_ADDR |0x0134 ), +MCGMASK (MCG_STATUS_EIPV ,0 ) + ), +MCESEV ( +AR ,"Action required: data load error" , +SER ,MASK (MCI_STATUS_OVER |MCI_UC_SAR |MCI_ADDR |MCACOD ,MCI_UC_SAR |MCI_ADDR |0x0134 ), +USER + ), +#endifMCESEV (PANIC ,"Action required: unknown MCACOD" , SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR, MCI_UC_SAR)
Signature · 1 line
1.7.3.1