HWPOISON: Intro
Subsystems (17)
cover HWPOISON: Intro
Latest version of the hwpoison patchkit, addressing all earlier review comments.
Should be good to go now.
Andrew, Please consider for merging.
Thanks,
-Andi
---
Upcoming Intel CPUs have support for recovering from some memory errors (``MCA recovery''). This requires the OS to declare a page "poisoned", kill the processes associated with it and avoid using it in the future.
This patchkit implements the necessary infrastructure in the VM.
To quote the overview comment:
* High level machine check handler. Handles pages reported by the
* hardware as being corrupted usually due to a 2bit ECC memory or cache
* failure.
*
* This focusses on pages detected as corrupted in the background.
* When the current CPU tries to consume corruption the currently
* running process can just be killed directly instead. This implies
* that if the error cannot be handled for some reason it's safe to
* just ignore it because no corruption has been consumed yet. Instead
* when that happens another machine check will happen.
*
* Handles page cache pages in various states. The tricky part
* here is that we can access any page asynchronous to other VM
* users, because memory failures could happen anytime and anywhere,
* possibly violating some of their assumptions. This is why this code
* has to be extremely careful. Generally it tries to use normal locking
* rules, as in get the standard locks, even if that means the
* error handling takes potentially a long time.
*
* Some of the operations here are somewhat inefficient and have non
* linear algorithmic complexity, because the data structures have not
* been optimized for this case. This is in particular the case
* for the mapping from a vma to a process. Since this case is expected
* to be rare we hope we can get away with this.
The code consists of a the high level handler in mm/memory-failure.c, a new page poison bit and various checks in the VM to handle poisoned pages.
The main target right now is KVM guests, but it works for all kinds of applications.
For the KVM use there was need for a new signal type so that KVM can inject the machine check into the guest with the proper address. This in theory allows other applications to handle memory failures too. The expection is that near all applications won't do that, but some very specialized ones might.
This is not fully complete yet, in particular there are still ways to access poison through various ways (crash dump, /proc/kcore etc.) that need to be plugged too.
Also undoubtedly the high level handler still has bugs and cases it cannot recover from. For example nonlinear mappings deadlock right now and a few other cases lose references. Huge pages are not supported yet. Any additional testing, reviewing etc. welcome.
The patch series requires the earlier x86 MCE feature series for the x86 specific action optional part. The code can be tested without the x86 specific part using the injector, this only requires to enable the Kconfig entry manually in some Kconfig file (by default it is implicitely enabled by the architecture)
v2: Lots of smaller changes in the series based on review feedback. Rename Poison to HWPoison after akpm's request.
A new pfn based injector based on feedback.
A lot of improvements mostly from Fengguang Wu
See comments in the individual patches.
v3: Various updates, see changelogs in individual patches.
v4: Various updates, see changelogs in individual patches.
-Andi
--
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>
PATCH 1/15 HWPOISON: Add page flag for poisoned pages
Hardware poisoned pages need special handling in the VM and shouldn't be touched again. This requires a new page flag. Define it here.
The page flags wars seem to be over, so it shouldn't be a problem to get a new one.
v2: Add TestSetHWPoison (suggested by Johannes Weiner)
--- include/linux/page-flags.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
include/linux/page-flags.h +16 -1
@@ -51,6 +51,9 @@* PG_buddy is set to indicate that the page is free and in the buddy system * (see mm/page_alloc.c). * +* PG_hwpoison indicates that a page got corrupted in hardware and contains +* data with incorrect ECC bits that triggered a machine check. Accessing is +* not safe since it may cause another machine check. Don't touch! */ /*
@@ -102,6 +105,9 @@ enum pageflags {#ifdef CONFIG_IA64_UNCACHED_ALLOCATOR PG_uncached, /* Page has been mapped as uncached */#endif +#ifdef CONFIG_MEMORY_FAILURE + PG_hwpoison,/* hardware poisoned page. Don't touch */ +#endif __NR_PAGEFLAGS,/* Filesystems */
@@ -263,6 +269,15 @@ PAGEFLAG(Uncached, uncached) PAGEFLAG_FALSE(Uncached)#endif +#ifdef CONFIG_MEMORY_FAILURE +PAGEFLAG(HWPoison, hwpoison) +TESTSETFLAG(HWPoison, hwpoison) +#define __PG_HWPOISON (1UL << PG_hwpoison) +#else +PAGEFLAG_FALSE(HWPoison) +#define __PG_HWPOISON 0 +#endif +static inline int PageUptodate (struct page *page) {int ret =test_bit (PG_uptodate, &(page)->flags);
@@ -387,7 +402,7 @@ static inline void __ClearPageTail(struc1 << PG_private |1 << PG_private_2 | \1 << PG_buddy |1 << PG_writeback |1 << PG_reserved | \1 << PG_slab |1 << PG_swapcache |1 << PG_active | \ - 1 << PG_unevictable | __PG_MLOCKED) + 1 << PG_unevictable | __PG_MLOCKED | __PG_HWPOISON)/* * Flags checked when a page is prepped for return by the page allocator.
--
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>
PATCH 2/15 HWPOISON: Export some rmap vma locking to outside world
In reply to Andi KleenNeeded for later patch that walks rmap entries on its own.
This used to be very frowned upon, but memory-failure.c does some rather specialized rmap walking and rmap has been stable for quite some time, so I think it's ok now to export it.
--- include/linux/rmap.h | 6 ++++++ mm/rmap.c | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-)
include/linux/rmap.h +6 -0
@@ -115,6 +115,12 @@ int try_to_munlock(struct page *);int page_wrprotect (struct page *page,int *odirect_sync,int count_offset);#endif +/* +* Called by memory-failure.c to kill processes. +*/ +struct anon_vma *page_lock_anon_vma (struct page *page); +void page_unlock_anon_vma (struct anon_vma *anon_vma); +#else /* !CONFIG_MMU */ #define anon_vma_init () do {} while (0)
mm/rmap.c +2 -2
@@ -191,7 +191,7 @@ void __init anon_vma_init(void)* Getting a lock on a stable anon_vma from a page off the LRU is * tricky: page_lock_anon_vma rely on RCU to guard against the races. */ -static struct anon_vma *page_lock_anon_vma (struct page *page) +struct anon_vma *page_lock_anon_vma (struct page *page) {struct anon_vma *anon_vma;unsigned long anon_mapping;
@@ -211,7 +211,7 @@ out:return NULL ; } -static void page_unlock_anon_vma (struct anon_vma *anon_vma) +void page_unlock_anon_vma (struct anon_vma *anon_vma) {spin_unlock (&anon_vma->lock);rcu_read_unlock ();
--
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>
PATCH 3/15 HWPOISON: Add support for poison swap entries v2
In reply to Andi KleenMemory migration uses special swap entry types to trigger special actions on page faults. Extend this mechanism to also support poisoned swap entries, to trigger poison handling on page faults. This allows follow-on patches to prevent processes from faulting in poisoned pages again.
v2: Fix overflow in MAX_SWAPFILES (Fengguang Wu)
v3: Better overflow fix (Hidehiro Kawai)
--- include/linux/swap.h | 34 ++++++++++++++++++++++++++++------ include/linux/swapops.h | 38 ++++++++++++++++++++++++++++++++++++++ mm/swapfile.c | 4 ++-- 3 files changed, 68 insertions(+), 8 deletions(-)
include/linux/swap.h +28 -6
@@ -34,16 +34,38 @@ static inline int current_is_kswapd(void* the type/offset into the pte as 5/27 as well. */ #define MAX_SWAPFILES_SHIFT 5 -#ifndef CONFIG_MIGRATION -#define MAX_SWAPFILES (1 << MAX_SWAPFILES_SHIFT) + +/* +* Use some of the swap files numbers for other purposes. This +* is a convenient way to hook into the VM to trigger special +* actions on faults. +*/ + +/* +* NUMA node memory migration support +*/ +#ifdef CONFIG_MIGRATION +#define SWP_MIGRATION_NUM 2 +#define SWP_MIGRATION_READ (MAX_SWAPFILES + SWP_HWPOISON_NUM) +#define SWP_MIGRATION_WRITE (MAX_SWAPFILES + SWP_HWPOISON_NUM + 1)#else -/* Use last two entries for page migration swap entries */ -#define MAX_SWAPFILES ((1 << MAX_SWAPFILES_SHIFT)-2) -#define SWP_MIGRATION_READ MAX_SWAPFILES -#define SWP_MIGRATION_WRITE (MAX_SWAPFILES + 1) +#define SWP_MIGRATION_NUM 0#endif /* +* Handling of hardware poisoned pages with memory corruption. +*/ +#ifdef CONFIG_MEMORY_FAILURE +#define SWP_HWPOISON_NUM 1 +#define SWP_HWPOISON MAX_SWAPFILES +#else +#define SWP_HWPOISON_NUM 0 +#endif + +#define MAX_SWAPFILES \ + ((1 << MAX_SWAPFILES_SHIFT) - SWP_MIGRATION_NUM - SWP_HWPOISON_NUM) + +/* * Magic header for a swap area. The first part of the union is * what the swap magic looks like for the old (limited to 128MB) * swap area format, the second part of the union adds - in the
include/linux/swapops.h +38 -0
@@ -131,3 +131,41 @@ static inline int is_write_migration_ent#endif +#ifdef CONFIG_MEMORY_FAILURE +/* +* Support for hardware poisoned pages +*/ +static inline swp_entry_t make_hwpoison_entry (struct page *page) +{ +BUG_ON (!PageLocked (page)); +return swp_entry (SWP_HWPOISON ,page_to_pfn (page)); +} + +static inline int is_hwpoison_entry (swp_entry_t entry) +{ +return swp_type (entry) ==SWP_HWPOISON ; +} +#else + +static inline swp_entry_t make_hwpoison_entry (struct page *page) +{ +return swp_entry (0 ,0 ); +} + +static inline int is_hwpoison_entry (swp_entry_t swp) +{ +return 0 ; +} +#endif + +#if defined(CONFIG_MEMORY_FAILURE ) || defined(CONFIG_MIGRATION ) +static inline int non_swap_entry (swp_entry_t entry) +{ +return swp_type (entry) >=MAX_SWAPFILES ; +} +#else +static inline int non_swap_entry (swp_entry_t entry) +{ +return 0 ; +} +#endif
mm/swapfile.c +2 -2
@@ -697,7 +697,7 @@ int free_swap_and_cache(swp_entry_t entrstruct swap_info_struct *p;struct page *page =NULL ; -if (is_migration_entry (entry)) +if (non_swap_entry (entry))return 1 ; p =swap_info_get (entry);
@@ -2083,7 +2083,7 @@ static int __swap_duplicate(swp_entry_tint count;bool has_cache; -if (is_migration_entry (entry)) +if (non_swap_entry (entry))return -EINVAL ; type =swp_type (entry);
--
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>
PATCH 4/15 HWPOISON: Add new SIGBUS error codes for hardware poison signals
In reply to Andi KleenAdd new SIGBUS codes for reporting machine checks as signals. When the hardware detects an uncorrected ECC error it can trigger these signals.
This is needed for telling KVM's qemu about machine checks that happen to guests, so that it can inject them, but might be also useful for other programs. I find it useful in my test programs.
This patch merely defines the new types.
- Define two new si_codes for SIGBUS. BUS_MCEERR_AO and BUS_MCEERR_AR
* BUS_MCEERR_AO is for "Action Optional" machine checks, which means that some corruption has been detected in the background, but nothing has been consumed so far. The program can ignore those if it wants (but most programs would already get killed)
* BUS_MCEERR_AR is for "Action Required" machine checks. This happens when corrupted data is consumed or the application ran into an area which has been known to be corrupted earlier. These require immediate action and cannot just returned to. Most programs would kill themselves.
- They report the address of the corruption in the user address space in si_addr.
- Define a new si_addr_lsb field that reports the extent of the corruption to user space. That's currently always a (small) page. The user application cannot tell where in this page the corruption happened.
AK: I plan to write a man page update before anyone asks.
--- include/asm-generic/siginfo.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
include/asm-generic/siginfo.h +7 -1
@@ -82,6 +82,7 @@ typedef struct siginfo {#ifdef __ARCH_SI_TRAPNOint _trapno;/* TRAP # which caused the signal */ #endif +short _addr_lsb;/* LSB of the reported address */ } _sigfault;/* SIGPOLL */
@@ -112,6 +113,7 @@ typedef struct siginfo {#ifdef __ARCH_SI_TRAPNO#define si_trapno _sifields._sigfault._trapno#endif +#define si_addr_lsb _sifields._sigfault._addr_lsb#define si_band _sifields._sigpoll._band#define si_fd _sifields._sigpoll._fd
@@ -192,7 +194,11 @@ typedef struct siginfo {#define BUS_ADRALN (__SI_FAULT|1)/* invalid address alignment */ #define BUS_ADRERR (__SI_FAULT|2)/* non-existant physical address */ #define BUS_OBJERR (__SI_FAULT|3)/* object specific hardware error */ -#define NSIGBUS 3 +/* hardware memory error consumed on a machine check: action required */ +#define BUS_MCEERR_AR (__SI_FAULT|4) +/* hardware memory error detected in process but not consumed: action optional*/ +#define BUS_MCEERR_AO (__SI_FAULT|5) +#define NSIGBUS 5/* * SIGTRAP si_codes
--
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>
PATCH 5/15 HWPOISON: Add basic support for poisoned pages in fault handler v3
In reply to Andi Kleen- Add a new VM_FAULT_HWPOISON error code to handle_mm_fault. Right now architectures have to explicitely enable poison page support, so this is forward compatible to all architectures. They only need to add it when they enable poison page support.
- Add poison page handling in swap in fault code
v2: Add missing delayacct_clear_flag (Hidehiro Kawai)
v3: Really use delayacct_clear_flag (Hidehiro Kawai)
--- include/linux/mm.h | 3 ++- mm/memory.c | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-)
mm/memory.c +15 -3
@@ -1315,7 +1315,8 @@ int __get_user_pages(struct task_structif (ret &VM_FAULT_ERROR ) {if (ret &VM_FAULT_OOM )return i ? i : -ENOMEM ; -else if (ret &VM_FAULT_SIGBUS ) +if (ret & + (VM_FAULT_HWPOISON |VM_FAULT_SIGBUS ))return i ? i : -EFAULT ;BUG (); }
@@ -2597,8 +2598,15 @@ static int do_swap_page(struct mm_struct goto out; entry =pte_to_swp_entry (orig_pte); -if (is_migration_entry (entry)) { -migration_entry_wait (mm, pmd, address); +if (unlikely (non_swap_entry (entry))) { +if (is_migration_entry (entry)) { +migration_entry_wait (mm, pmd, address); + }else if (is_hwpoison_entry (entry)) { + ret =VM_FAULT_HWPOISON ; + }else { +print_bad_pte (vma, address, pte,NULL ); + ret =VM_FAULT_OOM ; + } goto out; }delayacct_set_flag (DELAYACCT_PF_SWAPIN );
@@ -2622,6 +2630,10 @@ static int do_swap_page(struct mm_struct/* Had to read the page from swap area: Major fault */ ret =VM_FAULT_MAJOR ;count_vm_event (PGMAJFAULT ); + }else if (PageHWPoison (page)) { + ret =VM_FAULT_HWPOISON ; +delayacct_clear_flag (DELAYACCT_PF_SWAPIN ); + goto out; }lock_page (page);
include/linux/mm.h +2 -1
@@ -702,11 +702,12 @@ static inline int page_mapped(struct pag#define VM_FAULT_SIGBUS 0x0002#define VM_FAULT_MAJOR 0x0004#define VM_FAULT_WRITE 0x0008/* Special case for get_user_pages */ +#define VM_FAULT_HWPOISON 0x0010/* Hit poisoned page */ #define VM_FAULT_NOPAGE 0x0100/* ->fault installed the pte, not return page */ #define VM_FAULT_LOCKED 0x0200/* ->fault locked the returned page */ -#define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS) +#define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS | VM_FAULT_HWPOISON)/* * Can be called by the pagefault handler when it gets a VM_FAULT_OOM.
--
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>
PATCH 6/15 HWPOISON: Add various poison checks in mm/memory.c v2
In reply to Andi KleenBail out early when hardware poisoned pages are found in page fault handling. Since they are poisoned they should not be mapped freshly into processes, because that would cause another (potentially deadly) machine check
This is generally handled in the same way as OOM, just a different error code is returned to the architecture code.
v2: Do a page unlock if needed (Fengguang Wu)
--- mm/memory.c | 6 ++++++ 1 file changed, 6 insertions(+)
mm/memory.c +6 -0
@@ -2797,6 +2797,12 @@ static int __do_fault(struct mm_struct *if (unlikely (ret & (VM_FAULT_ERROR |VM_FAULT_NOPAGE )))return ret; +if (unlikely (PageHWPoison (vmf.page))) { +if (ret &VM_FAULT_LOCKED ) +unlock_page (vmf.page); +return VM_FAULT_HWPOISON ; + } +/* * For consistency in subsequent calls, make the faulted page always * locked.
--
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>
PATCH 7/15 HWPOISON: x86: Add VM_FAULT_HWPOISON handling to x86 page fault handler v2
In reply to Andi KleenAdd VM_FAULT_HWPOISON handling to the x86 page fault handler. This is very similar to VM_FAULT_OOM, the only difference is that a different si_code is passed to user space and the new addr_lsb field is initialized.
v2: Make the printk more verbose/unique
--- arch/x86/mm/fault.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
arch/x86/mm/fault.c +15 -4
@@ -166,6 +166,7 @@ force_sig_info_fault(int si_signo, int s info.si_errno =0 ; info.si_code = si_code; info.si_addr = (void __user *)address; + info.si_addr_lsb = si_code ==BUS_MCEERR_AR ?PAGE_SHIFT :0 ;force_sig_info (si_signo, &info, tsk); }
@@ -797,10 +798,12 @@ out_of_memory(struct pt_regs *regs, unsi }static void -do_sigbus (struct pt_regs *regs,unsigned long error_code,unsigned long address) +do_sigbus (struct pt_regs *regs,unsigned long error_code,unsigned long address, +unsigned int fault) {struct task_struct *tsk = current;struct mm_struct *mm = tsk->mm; +int code =BUS_ADRERR ;up_read (&mm->mmap_sem);
@@ -816,7 +819,15 @@ do_sigbus(struct pt_regs *regs, unsigned tsk->thread.error_code = error_code; tsk->thread.trap_no =14 ; -force_sig_info_fault (SIGBUS ,BUS_ADRERR , address, tsk); +#ifdef CONFIG_MEMORY_FAILURE +if (fault &VM_FAULT_HWPOISON ) { +printk (KERN_ERR +"MCE: Killing %s:%d due to hardware memory corruption fault at %lx\n" , + tsk->comm, tsk->pid, address); + code =BUS_MCEERR_AR ; + } +#endif +force_sig_info_fault (SIGBUS , code, address, tsk); }static noinline void
@@ -826,8 +837,8 @@ mm_fault_error(struct pt_regs *regs, unsif (fault &VM_FAULT_OOM ) {out_of_memory (regs, error_code, address); }else { -if (fault &VM_FAULT_SIGBUS ) -do_sigbus (regs, error_code, address); +if (fault & (VM_FAULT_SIGBUS |VM_FAULT_HWPOISON )) +do_sigbus (regs, error_code, address, fault);else BUG (); }
--
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>
PATCH 8/15 HWPOISON: Use bitmask/action code for try_to_unmap behaviour
In reply to Andi Kleentry_to_unmap currently has multiple modi (migration, munlock, normal unmap) which are selected by magic flag variables. The logic is not very straight forward, because each of these flag change multiple behaviours (e.g. migration turns off aging, not only sets up migration ptes etc.) Also the different flags interact in magic ways.
A later patch in this series adds another mode to try_to_unmap, so this becomes quickly unmanageable.
Replace the different flags with a action code (migration, munlock, munmap) and some additional flags as modifiers (ignore mlock, ignore aging). This makes the logic more straight forward and allows easier extension to new behaviours. Change all the caller to declare what they want to do.
This patch is supposed to be a nop in behaviour. If anyone can prove it is not that would be a bug.
--- include/linux/rmap.h | 14 +++++++++++++- mm/migrate.c | 2 +- mm/rmap.c | 40 ++++++++++++++++++++++------------------ mm/vmscan.c | 2 +- 4 files changed, 37 insertions(+), 21 deletions(-)
include/linux/rmap.h +13 -1
@@ -84,7 +84,19 @@ static inline void page_dup_rmap(struct* Called from mm/vmscan.c to handle paging out */ int page_referenced (struct page *,int is_locked,struct mem_cgroup *cnt); -int try_to_unmap (struct page *,int ignore_refs); + +enum ttu_flags { +TTU_UNMAP =0 ,/* unmap mode */ +TTU_MIGRATION =1 ,/* migration mode */ +TTU_MUNLOCK =2 ,/* munlock mode */ +TTU_ACTION_MASK =0xff , + +TTU_IGNORE_MLOCK = (1 <<8 ),/* ignore mlock */ +TTU_IGNORE_ACCESS = (1 <<9 ),/* don't age */ +}; +#define TTU_ACTION (x) ((x) & TTU_ACTION_MASK) + +int try_to_unmap (struct page *,enum ttu_flags flags);/* * Called from mm/filemap_xip.c to unmap empty zero page
mm/rmap.c +22 -18
@@ -897,7 +897,7 @@ void page_remove_rmap(struct page *page)* repeatedly from either try_to_unmap_anon or try_to_unmap_file. */ static int try_to_unmap_one (struct page *page,struct vm_area_struct *vma, -int migration) +enum ttu_flags flags) {struct mm_struct *mm = vma->vm_mm;unsigned long address;
@@ -919,11 +919,13 @@ static int try_to_unmap_one(struct page* If it's recently referenced (perhaps page_referenced * skipped over this mm) then we should reactivate it. */ -if (!migration) { +if (!(flags &TTU_IGNORE_MLOCK )) {if (vma->vm_flags &VM_LOCKED ) { ret =SWAP_MLOCK ; goto out_unmap; } + } +if (!(flags &TTU_IGNORE_ACCESS )) {if (ptep_clear_flush_young_notify (vma, address, pte)) { ret =SWAP_FAIL ; goto out_unmap;
@@ -963,12 +965,12 @@ static int try_to_unmap_one(struct page* pte. do_swap_page() will wait until the migration * pte is removed and then restart fault handling. */ -BUG_ON (!migration); +BUG_ON (TTU_ACTION (flags) !=TTU_MIGRATION ); entry =make_migration_entry (page,pte_write (pteval)); }set_pte_at (mm, address, pte,swp_entry_to_pte (entry));BUG_ON (pte_file (*pte)); - }else if (PAGE_MIGRATION && migration) { + }else if (PAGE_MIGRATION && (TTU_ACTION (flags) ==TTU_MIGRATION )) {/* Establish migration entry for a file page */ swp_entry_t entry; entry =make_migration_entry (page,pte_write (pteval));
@@ -1137,12 +1139,13 @@ static int try_to_mlock_page(struct page* vm_flags for that VMA. That should be OK, because that vma shouldn't be * 'LOCKED. */ -static int try_to_unmap_anon (struct page *page,int unlock,int migration) +static int try_to_unmap_anon (struct page *page,enum ttu_flags flags) {struct anon_vma *anon_vma;struct vm_area_struct *vma;unsigned int mlocked =0 ;int ret =SWAP_AGAIN ; +int unlock =TTU_ACTION (flags) ==TTU_MUNLOCK ;if (MLOCK_PAGES &&unlikely (unlock)) ret =SWAP_SUCCESS ;/* default for try_to_munlock() */
@@ -1158,7 +1161,7 @@ static int try_to_unmap_anon(struct pagecontinue ;/* must visit all unlocked vmas */ ret =SWAP_MLOCK ;/* saw at least one mlocked vma */ }else { - ret =try_to_unmap_one (page, vma, migration); + ret =try_to_unmap_one (page, vma, flags);if (ret ==SWAP_FAIL || !page_mapped (page))break ; }
@@ -1182,8 +1185,7 @@ static int try_to_unmap_anon(struct page/** * try_to_unmap_file - unmap/unlock file page using the object-based rmap method * @page: the page to unmap/unlock -* @unlock: request for unlock rather than unmap [unlikely] -* @migration: unmapping for migration - ignored if @unlock +* @flags: action and flags * * Find all the mappings of a page using the mapping pointer and the vma chains * contained in the address_space struct it points to.
@@ -1195,7 +1197,7 @@ static int try_to_unmap_anon(struct page* vm_flags for that VMA. That should be OK, because that vma shouldn't be * 'LOCKED. */ -static int try_to_unmap_file (struct page *page,int unlock,int migration) +static int try_to_unmap_file (struct page *page,enum ttu_flags flags) {struct address_space *mapping = page->mapping;pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT -PAGE_SHIFT );
@@ -1207,6 +1209,7 @@ static int try_to_unmap_file(struct pageunsigned long max_nl_size =0 ;unsigned int mapcount;unsigned int mlocked =0 ; +int unlock =TTU_ACTION (flags) ==TTU_MUNLOCK ;if (MLOCK_PAGES &&unlikely (unlock)) ret =SWAP_SUCCESS ;/* default for try_to_munlock() */
@@ -1219,7 +1222,7 @@ static int try_to_unmap_file(struct pagecontinue ;/* must visit all vmas */ ret =SWAP_MLOCK ; }else { - ret =try_to_unmap_one (page, vma, migration); + ret =try_to_unmap_one (page, vma, flags);if (ret ==SWAP_FAIL || !page_mapped (page)) goto out; }
@@ -1244,7 +1247,8 @@ static int try_to_unmap_file(struct page ret =SWAP_MLOCK ;/* leave mlocked == 0 */ goto out;/* no need to look further */ } -if (!MLOCK_PAGES && !migration && (vma->vm_flags &VM_LOCKED )) +if (!MLOCK_PAGES && !(flags &TTU_IGNORE_MLOCK ) && + (vma->vm_flags &VM_LOCKED ))continue ; cursor = (unsigned long ) vma->vm_private_data;if (cursor > max_nl_cursor)
@@ -1278,7 +1282,7 @@ static int try_to_unmap_file(struct page do { list_for_each_entry(vma, &mapping->i_mmap_nonlinear, shared.vm_set.list) { - if (!MLOCK_PAGES && !migration && + if (!MLOCK_PAGES && !(flags & TTU_IGNORE_MLOCK) && (vma->vm_flags & VM_LOCKED)) continue; cursor = (unsigned long) vma->vm_private_data;
@@ -1318,7 +1322,7 @@ out:/** * try_to_unmap - try to remove all page table mappings to a page * @page: the page to get unmapped -* @migration: migration flag +* @flags: action and flags * * Tries to remove all the page table entries which are mapping this * page, used in the pageout path. Caller must hold the page lock.
@@ -1329,16 +1333,16 @@ out:* SWAP_FAIL - the page is unswappable * SWAP_MLOCK - page is mlocked. */ -int try_to_unmap (struct page *page,int migration) +int try_to_unmap (struct page *page,enum ttu_flags flags) {int ret;BUG_ON (!PageLocked (page));if (PageAnon (page)) - ret =try_to_unmap_anon (page,0 , migration); + ret =try_to_unmap_anon (page, flags);else - ret =try_to_unmap_file (page,0 , migration); + ret =try_to_unmap_file (page, flags);if (ret !=SWAP_MLOCK && !page_mapped (page)) ret =SWAP_SUCCESS ;return ret;
@@ -1363,8 +1367,8 @@ int try_to_munlock(struct page *page)VM_BUG_ON (!PageLocked (page) ||PageLRU (page));if (PageAnon (page)) -return try_to_unmap_anon (page,1 ,0 ); +return try_to_unmap_anon (page,TTU_MUNLOCK );else -return try_to_unmap_file (page,1 ,0 ); +return try_to_unmap_file (page,TTU_MUNLOCK ); }
mm/vmscan.c +1 -1
@@ -659,7 +659,7 @@ static unsigned long shrink_page_list(st* processes. Try to unmap it here. */ if (page_mapped (page) && mapping) { -switch (try_to_unmap (page,0 )) { +switch (try_to_unmap (page,TTU_UNMAP )) {case SWAP_FAIL : goto activate_locked;case SWAP_AGAIN :
mm/migrate.c +1 -1
@@ -669,7 +669,7 @@ static int unmap_and_move(new_page_t get }/* Establish migration ptes or remove ptes */ -try_to_unmap (page,1 ); +try_to_unmap (page,TTU_MIGRATION |TTU_IGNORE_MLOCK |TTU_IGNORE_ACCESS );if (!page_mapped (page)) rc =move_to_new_page (newpage, page);
--
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>
PATCH 9/15 HWPOISON: Handle hardware poisoned pages in try_to_unmap
In reply to Andi KleenWhen a page has the poison bit set replace the PTE with a poison entry. This causes the right error handling to be done later when a process runs into it.
Also add a new flag to not do that (needed for the memory-failure handler later)
--- include/linux/rmap.h | 1 + mm/rmap.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-)
mm/rmap.c +8 -1
@@ -943,7 +943,14 @@ static int try_to_unmap_one(struct page/* Update high watermark before we lower rss */ update_hiwater_rss (mm); -if (PageAnon (page)) { +if (PageHWPoison (page) && !(flags &TTU_IGNORE_HWPOISON )) { +if (PageAnon (page)) +dec_mm_counter (mm, anon_rss); +else if (!is_migration_entry (pte_to_swp_entry (*pte))) +dec_mm_counter (mm, file_rss); +set_pte_at (mm, address, pte, +swp_entry_to_pte (make_hwpoison_entry (page))); + }else if (PageAnon (page)) {swp_entry_t entry = { .val =page_private (page) };if (PageSwapCache (page)) {
include/linux/rmap.h +1 -0
@@ -93,6 +93,7 @@ enum ttu_flags {TTU_IGNORE_MLOCK = (1 <<8 ),/* ignore mlock */ TTU_IGNORE_ACCESS = (1 <<9 ),/* don't age */ + TTU_IGNORE_HWPOISON = (1 << 10),/* corrupted page is recoverable */ };#define TTU_ACTION (x) ((x) & TTU_ACTION_MASK)
--
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>
PATCH 10/15 HWPOISON: check and isolate corrupted free pages v2
In reply to Andi KleenFrom: Wu Fengguang <[email protected]>
If memory corruption hits the free buddy pages, we can safely ignore them. No one will access them until page allocation time, then prep_new_page() will automatically check and isolate PG_hwpoison page for us (for 0-order allocation).
This patch expands prep_new_page() to check every component page in a high order page allocation, in order to completely stop PG_hwpoison pages from being recirculated.
Note that the common case -- only allocating a single page, doesn't do any more work than before. Allocating > order 0 does a bit more work, but that's relatively uncommon.
This simple implementation may drop some innocent neighbor pages, hopefully it is not a big problem because the event should be rare enough.
This patch adds some runtime costs to high order page users.
[AK: Improved description]
v2: Andi Kleen:
Port to -mm code
Move check into separate function.
Don't dump stack in bad_pages for hwpoisoned pages.
--- mm/page_alloc.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
mm/page_alloc.c +19 -1
@@ -237,6 +237,12 @@ static void bad_page(struct page *page)static unsigned long nr_shown;static unsigned long nr_unshown; +/* Don't complain about poisoned pages */ +if (PageHWPoison (page)) { +__ClearPageBuddy (page); +return ; + } +/* * Allow a burst of 60 reports, then keep quiet for that minute; * or allow a steady drip of one report per second.
@@ -650,7 +656,7 @@ static inline void expand(struct zone *z/* * This page is about to be returned from the page allocator */ -static int prep_new_page (struct page *page,int order,gfp_t gfp_flags) +static inline int check_new_page (struct page *page) { if (unlikely(page_mapcount(page) | (page->mapping != NULL) |
@@ -659,6 +665,18 @@ static int prep_new_page(struct page *pabad_page (page);return 1 ; } +return 0 ; +} + +static int prep_new_page (struct page *page,int order,gfp_t gfp_flags) +{ +int i; + +for (i =0 ; i < (1 << order); i++) { +struct page *p = page + i; +if (unlikely (check_new_page (p))) +return 1 ; + }set_page_private (page,0 );set_page_refcounted (page);
--
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>
PATCH 11/15 HWPOISON: Refactor truncate to allow direct truncating of page v2
In reply to Andi KleenFrom: Nick Piggin <[email protected]>
Extract out truncate_inode_page() out of the truncate path so that it can be used by memory-failure.c
[AK: description, headers, fix typos]
v2: Some white space changes from Fengguang Wu
--- include/linux/mm.h | 2 ++ mm/truncate.c | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-)
mm/truncate.c +12 -12
@@ -135,6 +135,16 @@ invalidate_complete_page(struct address_return ret; } +void truncate_inode_page (struct address_space *mapping,struct page *page) +{ +if (page_mapped (page)) { +unmap_mapping_range (mapping, + (loff_t )page->index <<PAGE_CACHE_SHIFT , +PAGE_CACHE_SIZE ,0 ); + } +truncate_complete_page (mapping, page); +} +/** * truncate_inode_pages - truncate range of pages specified by start & end byte offsets * @mapping: mapping to truncate
@@ -196,12 +206,7 @@ void truncate_inode_pages_range(struct aunlock_page (page);continue ; } -if (page_mapped (page)) { -unmap_mapping_range (mapping, - (loff_t )page_index<<PAGE_CACHE_SHIFT , -PAGE_CACHE_SIZE ,0 ); - } -truncate_complete_page (mapping, page); +truncate_inode_page (mapping, page);unlock_page (page); }pagevec_release (&pvec);
@@ -238,15 +243,10 @@ void truncate_inode_pages_range(struct abreak ;lock_page (page);wait_on_page_writeback (page); -if (page_mapped (page)) { -unmap_mapping_range (mapping, - (loff_t )page->index<<PAGE_CACHE_SHIFT , -PAGE_CACHE_SIZE ,0 ); - } +truncate_inode_page (mapping, page);if (page->index > next) next = page->index; next++; -truncate_complete_page (mapping, page);unlock_page (page); }pagevec_release (&pvec);
include/linux/mm.h +2 -0
@@ -811,6 +811,8 @@ static inline void unmap_shared_mapping_extern int vmtruncate (struct inode * inode,loff_t offset);extern int vmtruncate_range (struct inode * inode,loff_t offset,loff_t end); +void truncate_inode_page (struct address_space *mapping,struct page *page); +#ifdef CONFIG_MMU extern int handle_mm_fault (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long address,int write_access);
--
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>
Re: [PATCH] [11/15] HWPOISON: Refactor truncate to allow direct truncating of page v2
Quoted 10 lines · Andi Kleen
From: Nick Piggin <[email protected]>
Extract out
truncate_inode_page()out of the truncate path so that it can be used bymemory-failure.c[AK: description, headers, fix typos]
v2: Some white space changes from Fengguang WuSigned-off-by Andi Kleen <[email protected]>
Thank you muchly :) Seems the description is still missing? Something like the below?
mm/truncate.c invalidate_complete_page(struct address_ · line 137
Quoted 13 lines
--- include/linux/mm.h | 2 ++ mm/truncate.c | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-)Index:
linux/mm/truncate.c
===================================================================mm/truncate.c +0
-0@@ -135,6 +135,16 @@ invalidate_complete_page(struct address_return ret; }
/*
* Remove one page from its pagecache mapping. The page must be locked.
* This does not truncate the file on disk, it performs the pagecache
* side of the truncate operation. Dirty data will be discarded, and
* concurrent page references are ignored.
*
* Generic mm/fs code cannot call this on filesystem metadata mappings
* because those can assume that a page reference is enough to pin the
* page to its mapping.
*/
Quoted 49 lines
mm/truncate.c +12
-12+void truncate_inode_page (struct address_space *mapping,struct page *page) +{ +if (page_mapped (page)) { +unmap_mapping_range (mapping, + (loff_t )page->index <<PAGE_CACHE_SHIFT , +PAGE_CACHE_SIZE ,0 ); + } +truncate_complete_page (mapping, page); +} +/** * truncate_inode_pages - truncate range of pages specified by start & end byte offsets * @mapping: mapping to truncate @@ -196,12 +206,7 @@ void truncate_inode_pages_range(struct aunlock_page (page);continue ; } -if (page_mapped (page)) { -unmap_mapping_range (mapping, - (loff_t )page_index<<PAGE_CACHE_SHIFT , -PAGE_CACHE_SIZE ,0 ); - } -truncate_complete_page (mapping, page); +truncate_inode_page (mapping, page);unlock_page (page); }pagevec_release (&pvec);@@ -238,15 +243,10 @@ void truncate_inode_pages_range(struct abreak ;lock_page (page);wait_on_page_writeback (page); -if (page_mapped (page)) { -unmap_mapping_range (mapping, - (loff_t )page->index<<PAGE_CACHE_SHIFT , -PAGE_CACHE_SIZE ,0 ); - } +truncate_inode_page (mapping, page);if (page->index > next) next = page->index; next++; -truncate_complete_page (mapping, page);unlock_page (page); }pagevec_release (&pvec);Index:
linux/include/linux/mm.h
===================================================================
include/linux/mm.h static inline void unmap_shared_mapping_ · line 818
Quoted 8 lines
include/linux/mm.h +2
-0@@ -811,6 +811,8 @@ static inline void unmap_shared_mapping_extern int vmtruncate (struct inode * inode,loff_t offset);extern int vmtruncate_range (struct inode * inode,loff_t offset,loff_t end); +void truncate_inode_page (struct address_space *mapping,struct page *page); +#ifdef CONFIG_MMU extern int handle_mm_fault (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long address,int write_access);
--
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>
PATCH 11/15 HWPOISON: Refactor truncate to allow direct truncating of page v3
Quoted 10 lines · Nick Piggin
Quoted 9 lines · Andi Kleen
From: Nick Piggin <[email protected]>
Extract out
truncate_inode_page()out of the truncate path so that it can be used bymemory-failure.c[AK: description, headers, fix typos]
v2: Some white space changes from Fengguang Wu
Quoted 6 lines
Quoted 1 line
Signed-off-by Andi Kleen <[email protected]>Thank you muchly :) Seems the description is still missing? Something like the below?
Signed-off-by Nick Piggin <[email protected]>
Andi is on vocation, so let me do the updates :)
Thanks,
Fengguang
---
HWPOISON: Refactor truncate to allow direct truncating of page v3
From: Nick Piggin <[email protected]>
Extract out truncate_inode_page() out of the truncate path so that it can be used by memory-failure.c
[AK: description, headers, fix typos]
v2: Some white space changes from Fengguang Wu
v3: add comments
--- include/linux/mm.h | 2 ++ mm/truncate.c | 34 ++++++++++++++++++++++------------ 2 files changed, 24 insertions(+), 12 deletions(-)
mm/truncate.c +22 -12
@@ -135,6 +135,26 @@ invalidate_complete_page(struct address_return ret; } +/* +* Remove one page from its pagecache mapping. The page must be locked. +* This does not truncate the file on disk, it performs the pagecache +* side of the truncate operation. Dirty data will be discarded, and +* concurrent page references are ignored. +* +* Generic mm/fs code cannot call this on filesystem metadata mappings +* because those can assume that a page reference is enough to pin the +* page to its mapping. +*/ +void truncate_inode_page (struct address_space *mapping,struct page *page) +{ +if (page_mapped (page)) { +unmap_mapping_range (mapping, + (loff_t )page->index <<PAGE_CACHE_SHIFT , +PAGE_CACHE_SIZE ,0 ); + } +truncate_complete_page (mapping, page); +} +/** * truncate_inode_pages - truncate range of pages specified by start & end byte offsets * @mapping: mapping to truncate
@@ -196,12 +216,7 @@ void truncate_inode_pages_range(struct aunlock_page (page);continue ; } -if (page_mapped (page)) { -unmap_mapping_range (mapping, - (loff_t )page_index<<PAGE_CACHE_SHIFT , -PAGE_CACHE_SIZE ,0 ); - } -truncate_complete_page (mapping, page); +truncate_inode_page (mapping, page);unlock_page (page); }pagevec_release (&pvec);
@@ -238,15 +253,10 @@ void truncate_inode_pages_range(struct abreak ;lock_page (page);wait_on_page_writeback (page); -if (page_mapped (page)) { -unmap_mapping_range (mapping, - (loff_t )page->index<<PAGE_CACHE_SHIFT , -PAGE_CACHE_SIZE ,0 ); - } +truncate_inode_page (mapping, page);if (page->index > next) next = page->index; next++; -truncate_complete_page (mapping, page);unlock_page (page); }pagevec_release (&pvec);
include/linux/mm.h +2 -0
@@ -808,6 +808,8 @@ static inline void unmap_shared_mapping_extern int vmtruncate (struct inode * inode,loff_t offset);extern int vmtruncate_range (struct inode * inode,loff_t offset,loff_t end); +void truncate_inode_page (struct address_space *mapping,struct page *page); +#ifdef CONFIG_MMU extern int handle_mm_fault (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long address,int write_access);
--
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>
PATCH 12/15 HWPOISON: The high level memory error handler in the VM v6
In reply to Andi KleenAdd the high level memory handler that poisons pages
that got corrupted by hardware (typically by a two bit flip in a DIMM or a cache) on the Linux level. The goal is to prevent everyone from accessing these pages in the future.
This done at the VM level by marking a page hwpoisoned and doing the appropriate action based on the type of page it is.
The code that does this is portable and lives in mm/memory-failure.c
To quote the overview comment:
* High level machine check handler. Handles pages reported by the
* hardware as being corrupted usually due to a 2bit ECC memory or cache
* failure.
*
* This focuses on pages detected as corrupted in the background.
* When the current CPU tries to consume corruption the currently
* running process can just be killed directly instead. This implies
* that if the error cannot be handled for some reason it's safe to
* just ignore it because no corruption has been consumed yet. Instead
* when that happens another machine check will happen.
*
* Handles page cache pages in various states. The tricky part
* here is that we can access any page asynchronous to other VM
* users, because memory failures could happen anytime and anywhere,
* possibly violating some of their assumptions. This is why this code
* has to be extremely careful. Generally it tries to use normal locking
* rules, as in get the standard locks, even if that means the
* error handling takes potentially a long time.
*
* Some of the operations here are somewhat inefficient and have non
* linear algorithmic complexity, because the data structures have not
* been optimized for this case. This is in particular the case
* for the mapping from a vma to a process. Since this case is expected
* to be rare we hope we can get away with this.
There are in principle two strategies to kill processes on poison:
- just unmap the data and wait for an actual reference before killing
- kill as soon as corruption is detected.
Both have advantages and disadvantages and should be used in different situations. Right now both are implemented and can be switched with a new sysctl vm.memory_failure_early_kill The default is early kill.
The patch does some rmap data structure walking on its own to collect processes to kill. This is unusual because normally all rmap data structure knowledge is in rmap.c only. I put it here for now to keep everything together and rmap knowledge has been seeping out anyways
v2: Fix anon vma unlock crash (noticed by Johannes Weiner <[email protected]>) Handle pages on free list correctly (also noticed by Johannes)
Fix inverted try_to_release_page check (found by Chris Mason)
Add documentation for the new sysctl.
Various other cleanups/comment fixes.
v3: Use blockable signal for AO SIGBUS for better qemu handling. Numerous fixes from Fengguang Wu:
New code layout for the table (redone by AK)
Move the hwpoison bit setting before the lock (Fengguang Wu)
Some code cleanups (Fengguang Wu, AK)
Add missing lru_drain (Fengguang Wu)
Do more checks for valid mappings (inspired by patch from Fengguang) Handle free pages and fixes for clean pages (Fengguang)
Removed swap cache handling for now, needs more work
Better mapping checks to avoid races (Fengguang)
Fix swapcache (Fengguang)
Handle private2 pages too (Fengguang)
v4: Various fixes based on review comments from Nick Piggin Document locking order.
Improved comments.
Slightly improved description
Remove bogus hunk.
Wait properly for writeback pages (Nick Piggin)
v5: Improve various comments
Handle page_address_in_vma() failure better by SIGKILL and also
make message debugging only
Clean up printks
Remove redundant PageWriteback check (Nick Piggin)
Add missing clear_page_mlock
Reformat state table to be <80 columns again
Use truncate helper instead of manual truncate in me_pagecache_* Check for metadata buffer pages and reject them.
A few cleanups.
v6:
Fix a printk broken in the last round of cleanups.
More minor cleanups and fixes based on comments from Fengguang Wu. Rename /proc/meminfo Header to "HardwareCorrupted"
Add a printk for the failed mapping case (Fengguang Wu)
Better clean page check (Fengguang Wu)
--- Documentation/sysctl/vm.txt | 28 + fs/proc/meminfo.c | 9 include/linux/mm.h | 5 kernel/sysctl.c | 14 mm/Kconfig | 3 mm/Makefile | 1 mm/filemap.c | 4 mm/memory-failure.c | 763 ++++++++++++++++++++++++++++++++++++++++++++ mm/rmap.c | 4 9 files changed, 829 insertions(+), 2 deletions(-)
mm/Makefile +1 -0
@@ -42,5 +42,6 @@ obj-$(CONFIG_SMP) += allocpercpu.o endif obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o +obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
mm/memory-failure.c new file +763 -0
763 lines not on this page · raw message
include/linux/mm.h +5 -0
@@ -1326,5 +1326,10 @@ void vmemmap_populate_print_last(void);extern int account_locked_memory (struct mm_struct *mm,struct rlimit *rlim,size_t size);extern void refund_locked_memory (struct mm_struct *mm,size_t size); + +extern void memory_failure (unsigned long pfn,int trapno); +extern int sysctl_memory_failure_early_kill; +extern atomic_long_t mce_bad_pages; +#endif /* __KERNEL__ */ #endif /* _LINUX_MM_H */
kernel/sysctl.c +14 -0
@@ -1311,6 +1311,20 @@ static struct ctl_table vm_table[] = { .mode = 0644, .proc_handler = &scan_unevictable_handler, }, +#ifdef CONFIG_MEMORY_FAILURE + { + .ctl_name = CTL_UNNUMBERED, + .procname = "memory_failure_early_kill", + .data = &sysctl_memory_failure_early_kill, + .maxlen = sizeof(sysctl_memory_failure_early_kill), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &zero, + .extra2 = &one, + }, +#endif + /* * NOTE: do not add new entries to this table unless you have read * Documentation/sysctl/ctl_unnumbered.txt
fs/proc/meminfo.c +8 -1
@@ -95,7 +95,11 @@ static int meminfo_proc_show(struct seq_"Committed_AS: %8lu kB\n" "VmallocTotal: %8lu kB\n" "VmallocUsed: %8lu kB\n" -"VmallocChunk: %8lu kB\n" , +"VmallocChunk: %8lu kB\n" +#ifdef CONFIG_MEMORY_FAILURE +"HardwareCorrupted: %8lu kB\n" +#endif + ,K (i.totalram),K (i.freeram), K(i.bufferram),
@@ -140,6 +144,9 @@ static int meminfo_proc_show(struct seq_ (unsigned long )VMALLOC_TOTAL >>10 , vmi.used >>10 , vmi.largest_chunk >> 10 +#ifdef CONFIG_MEMORY_FAILURE + ,atomic_long_read (&mce_bad_pages) << (PAGE_SHIFT -10 ) +#endif );hugetlb_report_meminfo (m);
mm/Kconfig +3 -0
@@ -220,6 +220,9 @@ config KSM Enable the KSM kernel module to allow page sharing of equal pages among different tasks. +config MEMORY_FAILURE + bool + config NOMMU_INITIAL_TRIM_EXCESS int "Turn on mmap() excess space trimming before booting" depends on !MMU
Documentation/sysctl/vm.txt +27 -1
@@ -32,6 +32,7 @@ Currently, these files are in /proc/sys/ - legacy_va_layout - lowmem_reserve_ratio - max_map_count +- memory_failure_early_kill - min_free_kbytes - min_slab_ratio - min_unmapped_ratio
@@ -53,7 +54,6 @@ Currently, these files are in /proc/sys/ - vfs_cache_pressure - zone_reclaim_mode - ============================================================== block_dump
@@ -275,6 +275,32 @@ e.g., up to one or two maps per allocati The default value is 65536. +============================================================= + +memory_failure_early_kill: + +Control how to kill processes when uncorrected memory error (typically +a 2bit error in a memory module) is detected in the background by hardware +that cannot be handled by the kernel. In some cases (like the page +still having a valid copy on disk) the kernel will handle the failure +transparently without affecting any applications. But if there is +no other uptodate copy of the data it will kill to prevent any data +corruptions from propagating. + +1: Kill all processes that have the corrupted and not reloadable page mapped +as soon as the corruption is detected. Note this is not supported +for a few types of pages, like kernel internally allocated data or +the swap cache, but works for the majority of user pages. + +0: Only unmap the corrupted page from all processes and only kill a process +who tries to access it. + +The kill is done using a catchable SIGBUS with BUS_MCEERR_AO, so processes can +handle this if they want to. + +This is only active on architectures/platforms with advanced machine +check handling and depends on the hardware capabilities. + ============================================================== min_free_kbytes:
mm/filemap.c +4 -0
@@ -105,6 +105,10 @@* * ->task->proc_lock * ->dcache_lock (proc_pid_lookup) +* +* (code doesn't rely on that order, so you could switch it around) +* ->tasklist_lock (memory_failure, collect_procs_ao) +* ->i_mmap_lock */ /*
mm/rmap.c +4 -0
@@ -36,6 +36,10 @@* mapping->tree_lock (widely used, in set_page_dirty, * in arch-dependent flush_dcache_mmap_lock, * within inode_lock in __sync_single_inode) +* +* (code doesn't rely on that order so it could be switched around) +* ->tasklist_lock +* anon_vma->lock (memory_failure, collect_procs_anon) */ #include <linux/mm.h>
--
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>
PATCH 13/15 HWPOISON: FOR TESTING: Enable memory failure code unconditionally
In reply to Andi KleenNormally the memory-failure.c code is enabled by the architecture, but for easier testing independent of architecture changes enable it unconditionally.
This should not be merged into mainline.
--- mm/Kconfig | 2 ++ 1 file changed, 2 insertions(+)
mm/Kconfig +2 -0
@@ -222,6 +222,8 @@ config KSM config MEMORY_FAILURE bool + default y + depends on MMU config NOMMU_INITIAL_TRIM_EXCESS int "Turn on mmap() excess space trimming before booting"
--
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>
PATCH 14/15 HWPOISON: Add madvise() based injector for hardware poisoned pages v3
In reply to Andi KleenImpact: optional, useful for debugging
Add a new madvice sub command to inject poison for some pages in a process' address space. This is useful for testing the poison page handling.
Open issues:
- This patch allows root to tie up arbitary amounts of memory. Should this be disabled inside containers?
- There's a small race window between getting the page and injecting. The patch drops the ref count because otherwise memory_failure complains about dangling references. In theory with a multi threaded injector one could inject poison for a process foreign page this way. Not a serious issue right now.
v2: Use write flag for get_user_pages to make sure to always get a fresh page
v3: Don't request write mapping (Fengguang Wu)
--- include/asm-generic/mman-common.h | 1 + mm/madvise.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+)
mm/madvise.c +36 -0
@@ -207,6 +207,38 @@ static long madvise_remove(struct vm_arereturn error; } +#ifdef CONFIG_MEMORY_FAILURE +/* +* Error injection support for memory error handling. +*/ +static int madvise_hwpoison (unsigned long start,unsigned long end) +{ +/* +* RED-PEN +* This allows to tie up arbitary amounts of memory. +* Might be a good idea to disable it inside containers even for root. +*/ +if (!capable (CAP_SYS_ADMIN )) +return -EPERM ; +for (; start < end; start +=PAGE_SIZE ) { +struct page *p; +int ret =get_user_pages (current, current->mm, start,1 , +0 ,0 , &p,NULL ); +if (ret !=1 ) +return ret; +put_page (p); +/* +* RED-PEN page can be reused in a short window, but otherwise +* we'll have to fight with the reference count. +*/ +printk (KERN_INFO "Injecting memory failure for page %lx at %lx\n" , +page_to_pfn (p), start); +memory_failure (page_to_pfn (p),0 ); + } +return 0 ; +} +#endif +static long madvise_vma (struct vm_area_struct *vma,struct vm_area_struct **prev,unsigned long start,unsigned long end,int behavior)
@@ -307,6 +339,10 @@ SYSCALL_DEFINE3(madvise, unsigned long,int write;size_t len; +#ifdef CONFIG_MEMORY_FAILURE +if (behavior ==MADV_HWPOISON ) +return madvise_hwpoison (start, start+len_in); +#endif if (!madvise_behavior_valid (behavior))return error;
include/asm-generic/mman-common.h +1 -0
@@ -34,6 +34,7 @@#define MADV_REMOVE 9/* remove these pages & resources */ #define MADV_DONTFORK 10/* don't inherit across fork */ #define MADV_DOFORK 11/* do inherit across fork */ +#define MADV_HWPOISON 12/* poison a page for testing */ /* compatibility flags */ #define MAP_FILE 0
--
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>
PATCH 15/15 HWPOISON: Add simple debugfs interface to inject hwpoison on arbitary PFNs
In reply to Andi KleenUseful for some testing scenarios, although specific testing is often done better through MADV_POISON
This can be done with the x86 level MCE injector too, but this interface allows it to do independently from low level x86 changes.
Open issues:
Should be disabled for cgroups.
--- mm/Kconfig | 4 ++++ mm/Makefile | 1 + mm/hwpoison-inject.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+)
mm/hwpoison-inject.c new file +41 -0
@@ -0,0 +1,41 @@ +/* Inject a hwpoison memory failure on a arbitary pfn */ +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/kernel.h> +#include <linux/mm.h> + +static struct dentry *hwpoison_dir, *corrupt_pfn; + +static int hwpoison_inject (void *data,u64 val) +{ +if (!capable (CAP_SYS_ADMIN )) +return -EPERM ; +printk (KERN_INFO "Injecting memory failure at pfn %Lx\n" , val); +memory_failure (val,18 ); +return 0 ; +} + +DEFINE_SIMPLE_ATTRIBUTE (hwpoison_fops,NULL , hwpoison_inject,"%lli\n" ); + +static void pfn_inject_exit (void ) +{ +if (hwpoison_dir) +debugfs_remove_recursive (hwpoison_dir); +} + +static int pfn_inject_init (void ) +{ + hwpoison_dir =debugfs_create_dir ("hwpoison" ,NULL ); +if (hwpoison_dir ==NULL ) +return -ENOMEM ; + corrupt_pfn =debugfs_create_file ("corrupt-pfn" ,0600 , hwpoison_dir, +NULL , &hwpoison_fops); +if (corrupt_pfn ==NULL ) { +pfn_inject_exit (); +return -ENOMEM ; + } +return 0 ; +} + +module_init (pfn_inject_init); +module_exit (pfn_inject_exit);
mm/Kconfig +4 -0
@@ -225,6 +225,10 @@ config MEMORY_FAILURE default y depends on MMU +config HWPOISON_INJECT + tristate "Poison pages injector" + depends on MEMORY_FAILURE && DEBUG_KERNEL + config NOMMU_INITIAL_TRIM_EXCESS int "Turn on mmap() excess space trimming before booting" depends on !MMU
mm/Makefile +1 -0
@@ -43,5 +43,6 @@ endif obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o +obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
--
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>