x86/mm, mm/hwpoison: Don't unconditionally unmap kernel 1:1 pages.
Subsystems (5)
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH v2 1/1 x86/mm, mm/hwpoison: Don't unconditionally unmap kernel 1:1 pages.
In ce0fa3e56ad2 ("x86/mm, mm/hwpoison: Clear PRESENT bit for kernel 1:1 mappings of poison pages") we added code to memory_failure() to unmap the page from the kernel 1:1 virtual address space to avoid speculative access to the page logging additional errors.
But memory_failure() may not always succeed in taking the page offline, especially if the page belongs to the kernel. This can happen if there are too many corrected errors on a page and either mcelog(8) or drivers/ras/cec.c asks to take a page offline.
Since we remove the 1:1 mapping early in memory_failure(), we can end up with the page unmapped, but still in use. On the next access the kernel crashes :-(
There are also various debug paths that call memory_failure() to simulate occurrence of an error. Since there is no actual error in memory, we don't need to map out the page for those cases.
Revert most of the previous attempt and keep the solution local to arch/x86/kernel/cpu/mcheck/mce.c. Unmap the page only when:
1) there is a real error
2) memory_failure() succeeds.
All of this only applies to 64-bit systems. 32-bit kernel doesn't map all of memory into kernel space. It isn't worth adding the code to unmap the piece that is mapped because nobody would run a 32-bit kernel on a machine that has recoverable machine checks.
---v1->v2:
0-day reported an ARCH=i386 warning
arch/x86//kernel/cpu/mcheck/mce.c: In function 'mce_unmap_kpfn':
include/linux/bitops.h:7:24: warning: left shift count >= width of type [-Wshift-count-overflow]
#define BIT(nr) (1UL << (nr)) The real problem is that we shouldn't even be trying to do this on 32-bit. arch/x86/include/asm/page_64.h | 4 -- arch/x86/kernel/cpu/mcheck/mce.c | 87 ++++++++++++++++++++-------------------- include/linux/mm_inline.h | 6 --- mm/memory-failure.c | 2 - 4 files changed, 44 insertions(+), 55 deletions(-)
arch/x86/include/asm/page_64.h +0 -4
@@ -52,10 +52,6 @@ static inline void clear_page(void *page)void copy_page (void *to,void *from); -#ifdef CONFIG_X86_MCE -#define arch_unmap_kpfn arch_unmap_kpfn -#endif -#endif /* !__ASSEMBLY__ */ #ifdef CONFIG_X86_VSYSCALL_EMULATION
arch/x86/kernel/cpu/mcheck/mce.c +44 -43
108 lines not on this page · raw message
include/linux/mm_inline.h +0 -6
@@ -127,10 +127,4 @@ static __always_inline enum lru_list page_lru(struct page *page)#define lru_to_page (head) (list_entry((head)->prev, struct page, lru)) -#ifdef arch_unmap_kpfn -extern void arch_unmap_kpfn (unsigned long pfn); -#else -static __always_inline void arch_unmap_kpfn(unsigned long pfn) { } -#endif -#endif
mm/memory-failure.c +0 -2
@@ -1146,8 +1146,6 @@ int memory_failure(unsigned long pfn, int trapno, int flags)return 0 ; } -arch_unmap_kpfn (pfn); - orig_head = hpage =compound_head (p);num_poisoned_pages_inc ();
Signature · 7 lines
2.14.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
In ce0fa3e56ad2 ("x86/mm, mm/hwpoison: Clear PRESENT bit for kernel 1:1 mappings of poison pages") we added code to memory_failure() to unmap the page from the kernel 1:1 virtual address space to avoid speculative access to the page logging additional errors.
But memory_failure() may not always succeed in taking the page offline, especially if the page belongs to the kernel. This can happen if there are too many corrected errors on a page and either mcelog(8) or drivers/ras/cec.c asks to take a page offline.
Since we remove the 1:1 mapping early in memory_failure(), we can end up with the page unmapped, but still in use. On the next access the kernel crashes :-(
There are also various debug paths that call memory_failure() to simulate occurrence of an error. Since there is no actual error in memory, we don't need to map out the page for those cases.
Revert most of the previous attempt and keep the solution local to arch/x86/kernel/cpu/mcheck/mce.c. Unmap the page only when:
1) there is a real error
2) memory_failure() succeeds.
All of this only applies to 64-bit systems. 32-bit kernel doesn't map all of memory into kernel space. It isn't worth adding the code to unmap the piece that is mapped because nobody would run a 32-bit kernel on a machine that has recoverable machine checks.
---
v1->v2 0-day reported a build warning on 32-bit. Don't do 32-bit (see comment at end of commit message). This fixed the build error, but then discussion on the list went quiet. Repost to wake things up.
arch/x86/include/asm/page_64.h | 4 -- arch/x86/kernel/cpu/mcheck/mce.c | 87 ++++++++++++++++++++-------------------- include/linux/mm_inline.h | 6 --- mm/memory-failure.c | 2 - 4 files changed, 44 insertions(+), 55 deletions(-)
arch/x86/include/asm/page_64.h +0 -4
@@ -52,10 +52,6 @@ static inline void clear_page(void *page)void copy_page (void *to,void *from); -#ifdef CONFIG_X86_MCE -#define arch_unmap_kpfn arch_unmap_kpfn -#endif -#endif /* !__ASSEMBLY__ */ #ifdef CONFIG_X86_VSYSCALL_EMULATION
arch/x86/kernel/cpu/mcheck/mce.c +44 -43
108 lines not on this page · raw message
include/linux/mm_inline.h +0 -6
@@ -127,10 +127,4 @@ static __always_inline enum lru_list page_lru(struct page *page)#define lru_to_page (head) (list_entry((head)->prev, struct page, lru)) -#ifdef arch_unmap_kpfn -extern void arch_unmap_kpfn (unsigned long pfn); -#else -static __always_inline void arch_unmap_kpfn(unsigned long pfn) { } -#endif -#endif
mm/memory-failure.c +0 -2
@@ -1146,8 +1146,6 @@ int memory_failure(unsigned long pfn, int trapno, int flags)return 0 ; } -arch_unmap_kpfn (pfn); - orig_head = hpage =compound_head (p);num_poisoned_pages_inc ();
Signature · 7 lines
2.14.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 3 lines · Tony Luck
v1->v20-day reported a build warning on 32-bit. Don't do 32-bit (see comment at end of commit message). This fixed the build error, but then discussion on the list went quiet. Repost to wake things up.
It seems dubious to me to introduce a difference in behavior on 32-bit:
arch/x86/kernel/cpu/mcheck/mce.c static struct notifier_block first_nb = { · line 611
Quoted 8 lines
arch/x86/kernel/cpu/mcheck/mce.c +7
-0+static void mce_unmap_kpfn (unsigned long pfn) +{ +#ifdef CONFIG_X86_64 +unsigned long decoy_addr; +if (set_memory_np (decoy_addr,1 )) +pr_warn ("Could not invalidate pfn=0x%lx from 1:1 map\n" , pfn); +#endif
... to fix a build warning?
32-bit kernels might be under-tested, but if it's supposed to work I don't think we should bifurcate the behavior and uglify the code here.
Thanks,
Ingo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 14 lines · Ingo Molnar
Quoted 3 lines · Tony Luck
v1->v20-day reported a build warning on 32-bit. Don't do 32-bit (see comment at end of commit message). This fixed the build error, but then discussion on the list went quiet. Repost to wake things up.It seems dubious to me to introduce a difference in behavior on 32-bit:
Quoted 4 lines
arch/x86/kernel/cpu/mcheck/mce.c +4
-0+static void mce_unmap_kpfn (unsigned long pfn) +{ +#ifdef CONFIG_X86_64 +unsigned long decoy_addr;
Quoted 8 lines
arch/x86/kernel/cpu/mcheck/mce.c static struct notifier_block first_nb = { · line 611
Quoted 3 lines
arch/x86/kernel/cpu/mcheck/mce.c +3
-0+if (set_memory_np (decoy_addr,1 )) +pr_warn ("Could not invalidate pfn=0x%lx from 1:1 map\n" , pfn); +#endif ... to fix a build warning?
32-bit kernels might be under-tested, but if it's supposed to work I don't think we should bifurcate the behavior and uglify the code here.
I glossed over the issue in the commit message with this text:
All of this only applies to 64-bit systems. 32-bit kernel doesn't map all of memory into kernel space. It isn't worth adding the code to unmap the piece that is mapped because nobody would run a 32-bit kernel on a machine that has recoverable machine checks.
Here's some more detail on *why* I believe nobody will need this on 32-bit:
Recoverable machine checks are only supported on Xeon-E7 from IvyBridge to Broadwell, and on the "Gold" and "Platinum" Skylake models.
These are all intended for use in 4 socket systems.
To keep the high number of cores on these busy, you need good memory bandwidth. So any sane configuration will have a minimum of on DIMM per memory channel, so we can interleave across as many channels as possible.
So that's either 24 or 32 DIMMs (depending on 6 or 8 channels per socket).
So on the oldest of those systems (IvyBridge) with teeny 4GB DIMMs, we have 128GB.
Which doesn't boot on 32-bit (all "low" memory is used for "struct page").
But maybe a crazy person didn't populate all channels? Or booted with "mem=32G".
They still (mostly) don't need this. Most of their memory isn't mapped 1:1 because they don't have the virtual space for it. So the majority of errors would be in HIGMMEM ... and so not mapped.
So is this worth adding code for some hypothetical user running 32-bit who is somehow worried about the 800MB or so that is mapped 1:1
-Tony
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>