mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 15:04:27 +08:00
5da8e4a658
The motivations to go rework memcpy_mcsafe() are that the benefit of
doing slow and careful copies is obviated on newer CPUs, and that the
current opt-in list of CPUs to instrument recovery is broken relative to
those CPUs. There is no need to keep an opt-in list up to date on an
ongoing basis if pmem/dax operations are instrumented for recovery by
default. With recovery enabled by default the old "mcsafe_key" opt-in to
careful copying can be made a "fragile" opt-out. Where the "fragile"
list takes steps to not consume poison across cachelines.
The discussion with Linus made clear that the current "_mcsafe" suffix
was imprecise to a fault. The operations that are needed by pmem/dax are
to copy from a source address that might throw #MC to a destination that
may write-fault, if it is a user page.
So copy_to_user_mcsafe() becomes copy_mc_to_user() to indicate
the separate precautions taken on source and destination.
copy_mc_to_kernel() is introduced as a non-SMAP version that does not
expect write-faults on the destination, but is still prepared to abort
with an error code upon taking #MC.
The original copy_mc_fragile() implementation had negative performance
implications since it did not use the fast-string instruction sequence
to perform copies. For this reason copy_mc_to_kernel() fell back to
plain memcpy() to preserve performance on platforms that did not indicate
the capability to recover from machine check exceptions. However, that
capability detection was not architectural and now that some platforms
can recover from fast-string consumption of memory errors the memcpy()
fallback now causes these more capable platforms to fail.
Introduce copy_mc_enhanced_fast_string() as the fast default
implementation of copy_mc_to_kernel() and finalize the transition of
copy_mc_fragile() to be a platform quirk to indicate 'copy-carefully'.
With this in place, copy_mc_to_kernel() is fast and recovery-ready by
default regardless of hardware capability.
Thanks to Vivek for identifying that copy_user_generic() is not suitable
as the copy_mc_to_user() backend since the #MC handler explicitly checks
ex_has_fault_handler(). Thanks to the 0day robot for catching a
performance bug in the x86/copy_mc_to_user implementation.
[ bp: Add the "why" for this change from the 0/2th message, massage. ]
Fixes: 92b0729c34
("x86/mm, x86/mce: Add memcpy_mcsafe()")
Reported-by: Erwin Tsaur <erwin.tsaur@intel.com>
Reported-by: 0day robot <lkp@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Tony Luck <tony.luck@intel.com>
Tested-by: Erwin Tsaur <erwin.tsaur@intel.com>
Cc: <stable@vger.kernel.org>
Link: https://lkml.kernel.org/r/160195562556.2163339.18063423034951948973.stgit@dwillia2-desk3.amr.corp.intel.com
97 lines
2.5 KiB
C
97 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright(c) 2016-2020 Intel Corporation. All rights reserved. */
|
|
|
|
#include <linux/jump_label.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/export.h>
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <asm/mce.h>
|
|
|
|
#ifdef CONFIG_X86_MCE
|
|
/*
|
|
* See COPY_MC_TEST for self-test of the copy_mc_fragile()
|
|
* implementation.
|
|
*/
|
|
static DEFINE_STATIC_KEY_FALSE(copy_mc_fragile_key);
|
|
|
|
void enable_copy_mc_fragile(void)
|
|
{
|
|
static_branch_inc(©_mc_fragile_key);
|
|
}
|
|
#define copy_mc_fragile_enabled (static_branch_unlikely(©_mc_fragile_key))
|
|
|
|
/*
|
|
* Similar to copy_user_handle_tail, probe for the write fault point, or
|
|
* source exception point.
|
|
*/
|
|
__visible notrace unsigned long
|
|
copy_mc_fragile_handle_tail(char *to, char *from, unsigned len)
|
|
{
|
|
for (; len; --len, to++, from++)
|
|
if (copy_mc_fragile(to, from, 1))
|
|
break;
|
|
return len;
|
|
}
|
|
#else
|
|
/*
|
|
* No point in doing careful copying, or consulting a static key when
|
|
* there is no #MC handler in the CONFIG_X86_MCE=n case.
|
|
*/
|
|
void enable_copy_mc_fragile(void)
|
|
{
|
|
}
|
|
#define copy_mc_fragile_enabled (0)
|
|
#endif
|
|
|
|
unsigned long copy_mc_enhanced_fast_string(void *dst, const void *src, unsigned len);
|
|
|
|
/**
|
|
* copy_mc_to_kernel - memory copy that handles source exceptions
|
|
*
|
|
* @dst: destination address
|
|
* @src: source address
|
|
* @len: number of bytes to copy
|
|
*
|
|
* Call into the 'fragile' version on systems that benefit from avoiding
|
|
* corner case poison consumption scenarios, For example, accessing
|
|
* poison across 2 cachelines with a single instruction. Almost all
|
|
* other uses case can use copy_mc_enhanced_fast_string() for a fast
|
|
* recoverable copy, or fallback to plain memcpy.
|
|
*
|
|
* Return 0 for success, or number of bytes not copied if there was an
|
|
* exception.
|
|
*/
|
|
unsigned long __must_check copy_mc_to_kernel(void *dst, const void *src, unsigned len)
|
|
{
|
|
if (copy_mc_fragile_enabled)
|
|
return copy_mc_fragile(dst, src, len);
|
|
if (static_cpu_has(X86_FEATURE_ERMS))
|
|
return copy_mc_enhanced_fast_string(dst, src, len);
|
|
memcpy(dst, src, len);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(copy_mc_to_kernel);
|
|
|
|
unsigned long __must_check copy_mc_to_user(void *dst, const void *src, unsigned len)
|
|
{
|
|
unsigned long ret;
|
|
|
|
if (copy_mc_fragile_enabled) {
|
|
__uaccess_begin();
|
|
ret = copy_mc_fragile(dst, src, len);
|
|
__uaccess_end();
|
|
return ret;
|
|
}
|
|
|
|
if (static_cpu_has(X86_FEATURE_ERMS)) {
|
|
__uaccess_begin();
|
|
ret = copy_mc_enhanced_fast_string(dst, src, len);
|
|
__uaccess_end();
|
|
return ret;
|
|
}
|
|
|
|
return copy_user_generic(dst, src, len);
|
|
}
|