Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8c321fb authored by Tony Luck's avatar Tony Luck
Browse files

x86/mce: Handle "action required" errors



All 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.

Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
parent af104e39
Loading
Loading
Loading
Loading
+53 −42
Original line number Original line Diff line number Diff line
@@ -982,7 +982,9 @@ void do_machine_check(struct pt_regs *regs, long error_code)
	barrier();
	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))
	if (!(m.mcgstatus & MCG_STATUS_RIPV))
		kill_it = 1;
		kill_it = 1;
@@ -1036,12 +1038,6 @@ void do_machine_check(struct pt_regs *regs, long error_code)
			continue;
			continue;
		}
		}


		/*
		 * Kill on action required.
		 */
		if (severity == MCE_AR_SEVERITY)
			kill_it = 1;

		mce_read_aux(&m, i);
		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)
	if (!no_way_out)
		mce_clear_state(toclear);
		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;
		no_way_out = worst >= MCE_PANIC_SEVERITY;


	/*
	/*
	 * If we have decided that we just CAN'T continue, and the user
	 * At insane "tolerant" levels we take no action. Otherwise
	 * has not set tolerant to an insane level, give up and die.
	 * we only die if we have no other choice. For less serious
	 *
	 * issues we try to recover, or limit damage to the current
	 * This is mainly used in the case when the system doesn't
	 * process.
	 * support MCE broadcasting or it has been disabled.
	 */
	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 (tolerant < 3) {
	if (kill_it && tolerant < 3)
		if (no_way_out)
		force_sig(SIGBUS, current);
			mce_panic("Fatal machine check on current CPU", &m, msg);

		if (worst == MCE_AR_SEVERITY) {
	/* notify userspace ASAP */
			/* schedule action before return to userland */
			mce_save_info(m.addr);
			set_thread_flag(TIF_MCE_NOTIFY);
			set_thread_flag(TIF_MCE_NOTIFY);
		} else if (kill_it) {
			force_sig(SIGBUS, current);
		}
	}


	if (worst > 0)
	if (worst > 0)
		mce_report_event(regs);
		mce_report_event(regs);
@@ -1107,6 +1101,8 @@ EXPORT_SYMBOL_GPL(do_machine_check);
#ifndef CONFIG_MEMORY_FAILURE
#ifndef CONFIG_MEMORY_FAILURE
int memory_failure(unsigned long pfn, int vector, int flags)
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"
	printk(KERN_ERR "Uncorrected memory error in page 0x%lx ignored\n"
		"Rebuild kernel with CONFIG_MEMORY_FAILURE=y for smarter handling\n", pfn);
		"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
#endif


/*
/*
 * Called after mce notification in process context. This code
 * Called in process context that interrupted by MCE and marked with
 * is allowed to sleep. Call the high level VM handler to process
 * TIF_MCE_NOTIFY, just before returning to erroneous userland.
 * any corrupted pages.
 * This code is allowed to sleep.
 * Assume that the work queue code only calls this one at a time
 * Attempt possible recovery such as calling the high level VM handler to
 * per CPU.
 * process any corrupted pages, and kill/signal current process if required.
 * Note we don't disable preemption, so this code might run on the wrong
 * Action required errors are handled here.
 * 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.
 */
 */
void mce_notify_process(void)
void mce_notify_process(void)
{
{
	unsigned long pfn;
	unsigned long pfn;
	mce_notify_irq();
	struct mce_info *mi = mce_find_info();
	while (mce_ring_get(&pfn))

		memory_failure(pfn, MCE_VECTOR, 0);
	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)
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
#ifdef CONFIG_X86_MCE_INTEL
@@ -1225,8 +1238,6 @@ int mce_notify_irq(void)
	/* Not more than two messages every minute */
	/* Not more than two messages every minute */
	static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2);
	static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2);


	clear_thread_flag(TIF_MCE_NOTIFY);

	if (test_and_clear_bit(0, &mce_need_notify)) {
	if (test_and_clear_bit(0, &mce_need_notify)) {
		/* wake processes polling /dev/mcelog */
		/* wake processes polling /dev/mcelog */
		wake_up_interruptible(&mce_chrdev_wait);
		wake_up_interruptible(&mce_chrdev_wait);