You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'hwpoison' of git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-mce-2.6
* 'hwpoison' of git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-mce-2.6: (34 commits) HWPOISON: Remove stray phrase in a comment HWPOISON: Try to allocate migration page on the same node HWPOISON: Don't do early filtering if filter is disabled HWPOISON: Add a madvise() injector for soft page offlining HWPOISON: Add soft page offline support HWPOISON: Undefine short-hand macros after use to avoid namespace conflict HWPOISON: Use new shake_page in memory_failure HWPOISON: Use correct name for MADV_HWPOISON in documentation HWPOISON: mention HWPoison in Kconfig entry HWPOISON: Use get_user_page_fast in hwpoison madvise HWPOISON: add an interface to switch off/on all the page filters HWPOISON: add memory cgroup filter memcg: add accessor to mem_cgroup.css memcg: rename and export try_get_mem_cgroup_from_page() HWPOISON: add page flags filter mm: export stable page flags HWPOISON: limit hwpoison injector to known page types HWPOISON: add fs/device filters HWPOISON: return 0 to indicate success reliably HWPOISON: make semantics of IGNORED/DELAYED clear ...
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
What: /sys/devices/system/memory/soft_offline_page
|
||||
Date: Sep 2009
|
||||
KernelVersion: 2.6.33
|
||||
Contact: andi@firstfloor.org
|
||||
Description:
|
||||
Soft-offline the memory page containing the physical address
|
||||
written into this file. Input is a hex number specifying the
|
||||
physical address of the page. The kernel will then attempt
|
||||
to soft-offline it, by moving the contents elsewhere or
|
||||
dropping it if possible. The kernel will then be placed
|
||||
on the bad page list and never be reused.
|
||||
|
||||
The offlining is done in kernel specific granuality.
|
||||
Normally it's the base page size of the kernel, but
|
||||
this might change.
|
||||
|
||||
The page must be still accessible, not poisoned. The
|
||||
kernel will never kill anything for this, but rather
|
||||
fail the offline. Return value is the size of the
|
||||
number, or a error when the offlining failed. Reading
|
||||
the file is not allowed.
|
||||
|
||||
What: /sys/devices/system/memory/hard_offline_page
|
||||
Date: Sep 2009
|
||||
KernelVersion: 2.6.33
|
||||
Contact: andi@firstfloor.org
|
||||
Description:
|
||||
Hard-offline the memory page containing the physical
|
||||
address written into this file. Input is a hex number
|
||||
specifying the physical address of the page. The
|
||||
kernel will then attempt to hard-offline the page, by
|
||||
trying to drop the page or killing any owner or
|
||||
triggering IO errors if needed. Note this may kill
|
||||
any processes owning the page. The kernel will avoid
|
||||
to access this page assuming it's poisoned by the
|
||||
hardware.
|
||||
|
||||
The offlining is done in kernel specific granuality.
|
||||
Normally it's the base page size of the kernel, but
|
||||
this might change.
|
||||
|
||||
Return value is the size of the number, or a error when
|
||||
the offlining failed.
|
||||
Reading the file is not allowed.
|
||||
@@ -92,16 +92,62 @@ PR_MCE_KILL_GET
|
||||
|
||||
Testing:
|
||||
|
||||
madvise(MADV_POISON, ....)
|
||||
madvise(MADV_HWPOISON, ....)
|
||||
(as root)
|
||||
Poison a page in the process for testing
|
||||
|
||||
|
||||
hwpoison-inject module through debugfs
|
||||
/sys/debug/hwpoison/corrupt-pfn
|
||||
|
||||
Inject hwpoison fault at PFN echoed into this file
|
||||
/sys/debug/hwpoison/
|
||||
|
||||
corrupt-pfn
|
||||
|
||||
Inject hwpoison fault at PFN echoed into this file. This does
|
||||
some early filtering to avoid corrupted unintended pages in test suites.
|
||||
|
||||
unpoison-pfn
|
||||
|
||||
Software-unpoison page at PFN echoed into this file. This
|
||||
way a page can be reused again.
|
||||
This only works for Linux injected failures, not for real
|
||||
memory failures.
|
||||
|
||||
Note these injection interfaces are not stable and might change between
|
||||
kernel versions
|
||||
|
||||
corrupt-filter-dev-major
|
||||
corrupt-filter-dev-minor
|
||||
|
||||
Only handle memory failures to pages associated with the file system defined
|
||||
by block device major/minor. -1U is the wildcard value.
|
||||
This should be only used for testing with artificial injection.
|
||||
|
||||
corrupt-filter-memcg
|
||||
|
||||
Limit injection to pages owned by memgroup. Specified by inode number
|
||||
of the memcg.
|
||||
|
||||
Example:
|
||||
mkdir /cgroup/hwpoison
|
||||
|
||||
usemem -m 100 -s 1000 &
|
||||
echo `jobs -p` > /cgroup/hwpoison/tasks
|
||||
|
||||
memcg_ino=$(ls -id /cgroup/hwpoison | cut -f1 -d' ')
|
||||
echo $memcg_ino > /debug/hwpoison/corrupt-filter-memcg
|
||||
|
||||
page-types -p `pidof init` --hwpoison # shall do nothing
|
||||
page-types -p `pidof usemem` --hwpoison # poison its pages
|
||||
|
||||
corrupt-filter-flags-mask
|
||||
corrupt-filter-flags-value
|
||||
|
||||
When specified, only poison pages if ((page_flags & mask) == value).
|
||||
This allows stress testing of many kinds of pages. The page_flags
|
||||
are the same as in /proc/kpageflags. The flag bits are defined in
|
||||
include/linux/kernel-page-flags.h and documented in
|
||||
Documentation/vm/pagemap.txt
|
||||
|
||||
Architecture specific MCE injector
|
||||
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
/*
|
||||
* page-types: Tool for querying page flags
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should find a copy of v2 of the GNU General Public License somewhere on
|
||||
* your Linux system; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* Copyright (C) 2009 Intel corporation
|
||||
*
|
||||
* Authors: Wu Fengguang <fengguang.wu@intel.com>
|
||||
*
|
||||
* Released under the General Public License (GPL).
|
||||
*/
|
||||
|
||||
#define _LARGEFILE64_SOURCE
|
||||
|
||||
@@ -2377,6 +2377,15 @@ W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/
|
||||
S: Maintained
|
||||
F: drivers/hwmon/hdaps.c
|
||||
|
||||
HWPOISON MEMORY FAILURE HANDLING
|
||||
M: Andi Kleen <andi@firstfloor.org>
|
||||
L: linux-mm@kvack.org
|
||||
L: linux-kernel@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-mce-2.6.git hwpoison
|
||||
S: Maintained
|
||||
F: mm/memory-failure.c
|
||||
F: mm/hwpoison-inject.c
|
||||
|
||||
HYPERVISOR VIRTUAL CONSOLE DRIVER
|
||||
L: linuxppc-dev@ozlabs.org
|
||||
S: Odd Fixes
|
||||
|
||||
@@ -341,6 +341,64 @@ static inline int memory_probe_init(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MEMORY_FAILURE
|
||||
/*
|
||||
* Support for offlining pages of memory
|
||||
*/
|
||||
|
||||
/* Soft offline a page */
|
||||
static ssize_t
|
||||
store_soft_offline_page(struct class *class, const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
u64 pfn;
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
if (strict_strtoull(buf, 0, &pfn) < 0)
|
||||
return -EINVAL;
|
||||
pfn >>= PAGE_SHIFT;
|
||||
if (!pfn_valid(pfn))
|
||||
return -ENXIO;
|
||||
ret = soft_offline_page(pfn_to_page(pfn), 0);
|
||||
return ret == 0 ? count : ret;
|
||||
}
|
||||
|
||||
/* Forcibly offline a page, including killing processes. */
|
||||
static ssize_t
|
||||
store_hard_offline_page(struct class *class, const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
u64 pfn;
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
if (strict_strtoull(buf, 0, &pfn) < 0)
|
||||
return -EINVAL;
|
||||
pfn >>= PAGE_SHIFT;
|
||||
ret = __memory_failure(pfn, 0, 0);
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static CLASS_ATTR(soft_offline_page, 0644, NULL, store_soft_offline_page);
|
||||
static CLASS_ATTR(hard_offline_page, 0644, NULL, store_hard_offline_page);
|
||||
|
||||
static __init int memory_fail_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sysfs_create_file(&memory_sysdev_class.kset.kobj,
|
||||
&class_attr_soft_offline_page.attr);
|
||||
if (!err)
|
||||
err = sysfs_create_file(&memory_sysdev_class.kset.kobj,
|
||||
&class_attr_hard_offline_page.attr);
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
static inline int memory_fail_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Note that phys_device is optional. It is here to allow for
|
||||
* differentiation between which *physical* devices each
|
||||
@@ -471,6 +529,9 @@ int __init memory_dev_init(void)
|
||||
}
|
||||
|
||||
err = memory_probe_init();
|
||||
if (!ret)
|
||||
ret = err;
|
||||
err = memory_fail_init();
|
||||
if (!ret)
|
||||
ret = err;
|
||||
err = block_size_init();
|
||||
|
||||
+3
-42
@@ -8,6 +8,7 @@
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/kernel-page-flags.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
|
||||
@@ -71,52 +72,12 @@ static const struct file_operations proc_kpagecount_operations = {
|
||||
* physical page flags.
|
||||
*/
|
||||
|
||||
/* These macros are used to decouple internal flags from exported ones */
|
||||
|
||||
#define KPF_LOCKED 0
|
||||
#define KPF_ERROR 1
|
||||
#define KPF_REFERENCED 2
|
||||
#define KPF_UPTODATE 3
|
||||
#define KPF_DIRTY 4
|
||||
#define KPF_LRU 5
|
||||
#define KPF_ACTIVE 6
|
||||
#define KPF_SLAB 7
|
||||
#define KPF_WRITEBACK 8
|
||||
#define KPF_RECLAIM 9
|
||||
#define KPF_BUDDY 10
|
||||
|
||||
/* 11-20: new additions in 2.6.31 */
|
||||
#define KPF_MMAP 11
|
||||
#define KPF_ANON 12
|
||||
#define KPF_SWAPCACHE 13
|
||||
#define KPF_SWAPBACKED 14
|
||||
#define KPF_COMPOUND_HEAD 15
|
||||
#define KPF_COMPOUND_TAIL 16
|
||||
#define KPF_HUGE 17
|
||||
#define KPF_UNEVICTABLE 18
|
||||
#define KPF_HWPOISON 19
|
||||
#define KPF_NOPAGE 20
|
||||
|
||||
#define KPF_KSM 21
|
||||
|
||||
/* kernel hacking assistances
|
||||
* WARNING: subject to change, never rely on them!
|
||||
*/
|
||||
#define KPF_RESERVED 32
|
||||
#define KPF_MLOCKED 33
|
||||
#define KPF_MAPPEDTODISK 34
|
||||
#define KPF_PRIVATE 35
|
||||
#define KPF_PRIVATE_2 36
|
||||
#define KPF_OWNER_PRIVATE 37
|
||||
#define KPF_ARCH 38
|
||||
#define KPF_UNCACHED 39
|
||||
|
||||
static inline u64 kpf_copy_bit(u64 kflags, int ubit, int kbit)
|
||||
{
|
||||
return ((kflags >> kbit) & 1) << ubit;
|
||||
}
|
||||
|
||||
static u64 get_uflags(struct page *page)
|
||||
u64 stable_page_flags(struct page *page)
|
||||
{
|
||||
u64 k;
|
||||
u64 u;
|
||||
@@ -219,7 +180,7 @@ static ssize_t kpageflags_read(struct file *file, char __user *buf,
|
||||
else
|
||||
ppage = NULL;
|
||||
|
||||
if (put_user(get_uflags(ppage), out)) {
|
||||
if (put_user(stable_page_flags(ppage), out)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#define MADV_DONTFORK 10 /* don't inherit across fork */
|
||||
#define MADV_DOFORK 11 /* do inherit across fork */
|
||||
#define MADV_HWPOISON 100 /* poison a page for testing */
|
||||
#define MADV_SOFT_OFFLINE 101 /* soft offline page for testing */
|
||||
|
||||
#define MADV_MERGEABLE 12 /* KSM may merge identical pages */
|
||||
#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef LINUX_KERNEL_PAGE_FLAGS_H
|
||||
#define LINUX_KERNEL_PAGE_FLAGS_H
|
||||
|
||||
/*
|
||||
* Stable page flag bits exported to user space
|
||||
*/
|
||||
|
||||
#define KPF_LOCKED 0
|
||||
#define KPF_ERROR 1
|
||||
#define KPF_REFERENCED 2
|
||||
#define KPF_UPTODATE 3
|
||||
#define KPF_DIRTY 4
|
||||
#define KPF_LRU 5
|
||||
#define KPF_ACTIVE 6
|
||||
#define KPF_SLAB 7
|
||||
#define KPF_WRITEBACK 8
|
||||
#define KPF_RECLAIM 9
|
||||
#define KPF_BUDDY 10
|
||||
|
||||
/* 11-20: new additions in 2.6.31 */
|
||||
#define KPF_MMAP 11
|
||||
#define KPF_ANON 12
|
||||
#define KPF_SWAPCACHE 13
|
||||
#define KPF_SWAPBACKED 14
|
||||
#define KPF_COMPOUND_HEAD 15
|
||||
#define KPF_COMPOUND_TAIL 16
|
||||
#define KPF_HUGE 17
|
||||
#define KPF_UNEVICTABLE 18
|
||||
#define KPF_HWPOISON 19
|
||||
#define KPF_NOPAGE 20
|
||||
|
||||
#define KPF_KSM 21
|
||||
|
||||
/* kernel hacking assistances
|
||||
* WARNING: subject to change, never rely on them!
|
||||
*/
|
||||
#define KPF_RESERVED 32
|
||||
#define KPF_MLOCKED 33
|
||||
#define KPF_MAPPEDTODISK 34
|
||||
#define KPF_PRIVATE 35
|
||||
#define KPF_PRIVATE_2 36
|
||||
#define KPF_OWNER_PRIVATE 37
|
||||
#define KPF_ARCH 38
|
||||
#define KPF_UNCACHED 39
|
||||
|
||||
#endif /* LINUX_KERNEL_PAGE_FLAGS_H */
|
||||
@@ -73,6 +73,7 @@ extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan,
|
||||
extern void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask);
|
||||
int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);
|
||||
|
||||
extern struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page);
|
||||
extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p);
|
||||
|
||||
static inline
|
||||
@@ -85,6 +86,8 @@ int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup)
|
||||
return cgroup == mem;
|
||||
}
|
||||
|
||||
extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *mem);
|
||||
|
||||
extern int
|
||||
mem_cgroup_prepare_migration(struct page *page, struct mem_cgroup **ptr);
|
||||
extern void mem_cgroup_end_migration(struct mem_cgroup *mem,
|
||||
@@ -202,6 +205,11 @@ mem_cgroup_move_lists(struct page *page, enum lru_list from, enum lru_list to)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int mm_match_cgroup(struct mm_struct *mm, struct mem_cgroup *mem)
|
||||
{
|
||||
return 1;
|
||||
@@ -213,6 +221,11 @@ static inline int task_in_mem_cgroup(struct task_struct *task,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *mem)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mem_cgroup_prepare_migration(struct page *page, struct mem_cgroup **ptr)
|
||||
{
|
||||
|
||||
+7
-1
@@ -1331,11 +1331,17 @@ 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);
|
||||
|
||||
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 ref);
|
||||
extern int __memory_failure(unsigned long pfn, int trapno, int flags);
|
||||
extern int unpoison_memory(unsigned long pfn);
|
||||
extern int sysctl_memory_failure_early_kill;
|
||||
extern int sysctl_memory_failure_recovery;
|
||||
extern void shake_page(struct page *p, int access);
|
||||
extern atomic_long_t mce_bad_pages;
|
||||
extern int soft_offline_page(struct page *page, int flags);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _LINUX_MM_H */
|
||||
|
||||
@@ -275,13 +275,15 @@ PAGEFLAG_FALSE(Uncached)
|
||||
|
||||
#ifdef CONFIG_MEMORY_FAILURE
|
||||
PAGEFLAG(HWPoison, hwpoison)
|
||||
TESTSETFLAG(HWPoison, hwpoison)
|
||||
TESTSCFLAG(HWPoison, hwpoison)
|
||||
#define __PG_HWPOISON (1UL << PG_hwpoison)
|
||||
#else
|
||||
PAGEFLAG_FALSE(HWPoison)
|
||||
#define __PG_HWPOISON 0
|
||||
#endif
|
||||
|
||||
u64 stable_page_flags(struct page *page);
|
||||
|
||||
static inline int PageUptodate(struct page *page)
|
||||
{
|
||||
int ret = test_bit(PG_uptodate, &(page)->flags);
|
||||
|
||||
+2
-1
@@ -251,8 +251,9 @@ config MEMORY_FAILURE
|
||||
special hardware support and typically ECC memory.
|
||||
|
||||
config HWPOISON_INJECT
|
||||
tristate "Poison pages injector"
|
||||
tristate "HWPoison pages injector"
|
||||
depends on MEMORY_FAILURE && DEBUG_KERNEL
|
||||
select PROC_PAGE_MONITOR
|
||||
|
||||
config NOMMU_INITIAL_TRIM_EXCESS
|
||||
int "Turn on mmap() excess space trimming before booting"
|
||||
|
||||
+105
-8
@@ -3,18 +3,68 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "internal.h"
|
||||
|
||||
static struct dentry *hwpoison_dir, *corrupt_pfn;
|
||||
static struct dentry *hwpoison_dir;
|
||||
|
||||
static int hwpoison_inject(void *data, u64 val)
|
||||
{
|
||||
unsigned long pfn = val;
|
||||
struct page *p;
|
||||
int err;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
printk(KERN_INFO "Injecting memory failure at pfn %Lx\n", val);
|
||||
return __memory_failure(val, 18, 0);
|
||||
|
||||
if (!hwpoison_filter_enable)
|
||||
goto inject;
|
||||
if (!pfn_valid(pfn))
|
||||
return -ENXIO;
|
||||
|
||||
p = pfn_to_page(pfn);
|
||||
/*
|
||||
* This implies unable to support free buddy pages.
|
||||
*/
|
||||
if (!get_page_unless_zero(p))
|
||||
return 0;
|
||||
|
||||
if (!PageLRU(p))
|
||||
shake_page(p, 0);
|
||||
/*
|
||||
* This implies unable to support non-LRU pages.
|
||||
*/
|
||||
if (!PageLRU(p))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
lock_page(p);
|
||||
err = hwpoison_filter(p);
|
||||
unlock_page(p);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
inject:
|
||||
printk(KERN_INFO "Injecting memory failure at pfn %lx\n", pfn);
|
||||
return __memory_failure(pfn, 18, MF_COUNT_INCREASED);
|
||||
}
|
||||
|
||||
static int hwpoison_unpoison(void *data, u64 val)
|
||||
{
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
return unpoison_memory(val);
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");
|
||||
|
||||
static void pfn_inject_exit(void)
|
||||
{
|
||||
@@ -24,16 +74,63 @@ static void pfn_inject_exit(void)
|
||||
|
||||
static int pfn_inject_init(void)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
hwpoison_dir = debugfs_create_dir("hwpoison", NULL);
|
||||
if (hwpoison_dir == NULL)
|
||||
return -ENOMEM;
|
||||
corrupt_pfn = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir,
|
||||
|
||||
/*
|
||||
* Note that the below poison/unpoison interfaces do not involve
|
||||
* hardware status change, hence do not require hardware support.
|
||||
* They are mainly for testing hwpoison in software level.
|
||||
*/
|
||||
dentry = debugfs_create_file("corrupt-pfn", 0600, hwpoison_dir,
|
||||
NULL, &hwpoison_fops);
|
||||
if (corrupt_pfn == NULL) {
|
||||
pfn_inject_exit();
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!dentry)
|
||||
goto fail;
|
||||
|
||||
dentry = debugfs_create_file("unpoison-pfn", 0600, hwpoison_dir,
|
||||
NULL, &unpoison_fops);
|
||||
if (!dentry)
|
||||
goto fail;
|
||||
|
||||
dentry = debugfs_create_u32("corrupt-filter-enable", 0600,
|
||||
hwpoison_dir, &hwpoison_filter_enable);
|
||||
if (!dentry)
|
||||
goto fail;
|
||||
|
||||
dentry = debugfs_create_u32("corrupt-filter-dev-major", 0600,
|
||||
hwpoison_dir, &hwpoison_filter_dev_major);
|
||||
if (!dentry)
|
||||
goto fail;
|
||||
|
||||
dentry = debugfs_create_u32("corrupt-filter-dev-minor", 0600,
|
||||
hwpoison_dir, &hwpoison_filter_dev_minor);
|
||||
if (!dentry)
|
||||
goto fail;
|
||||
|
||||
dentry = debugfs_create_u64("corrupt-filter-flags-mask", 0600,
|
||||
hwpoison_dir, &hwpoison_filter_flags_mask);
|
||||
if (!dentry)
|
||||
goto fail;
|
||||
|
||||
dentry = debugfs_create_u64("corrupt-filter-flags-value", 0600,
|
||||
hwpoison_dir, &hwpoison_filter_flags_value);
|
||||
if (!dentry)
|
||||
goto fail;
|
||||
|
||||
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
|
||||
dentry = debugfs_create_u64("corrupt-filter-memcg", 0600,
|
||||
hwpoison_dir, &hwpoison_filter_memcg);
|
||||
if (!dentry)
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
pfn_inject_exit();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
module_init(pfn_inject_init);
|
||||
|
||||
@@ -50,6 +50,9 @@ extern void putback_lru_page(struct page *page);
|
||||
*/
|
||||
extern void __free_pages_bootmem(struct page *page, unsigned int order);
|
||||
extern void prep_compound_page(struct page *page, unsigned long order);
|
||||
#ifdef CONFIG_MEMORY_FAILURE
|
||||
extern bool is_free_buddy_page(struct page *page);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -247,3 +250,12 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
|
||||
#define ZONE_RECLAIM_SOME 0
|
||||
#define ZONE_RECLAIM_SUCCESS 1
|
||||
#endif
|
||||
|
||||
extern int hwpoison_filter(struct page *p);
|
||||
|
||||
extern u32 hwpoison_filter_dev_major;
|
||||
extern u32 hwpoison_filter_dev_minor;
|
||||
extern u64 hwpoison_filter_flags_mask;
|
||||
extern u64 hwpoison_filter_flags_value;
|
||||
extern u64 hwpoison_filter_memcg;
|
||||
extern u32 hwpoison_filter_enable;
|
||||
|
||||
+14
-7
@@ -9,6 +9,7 @@
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/mempolicy.h>
|
||||
#include <linux/page-isolation.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ksm.h>
|
||||
@@ -222,7 +223,7 @@ static long madvise_remove(struct vm_area_struct *vma,
|
||||
/*
|
||||
* Error injection support for memory error handling.
|
||||
*/
|
||||
static int madvise_hwpoison(unsigned long start, unsigned long end)
|
||||
static int madvise_hwpoison(int bhv, unsigned long start, unsigned long end)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@@ -230,15 +231,21 @@ static int madvise_hwpoison(unsigned long start, unsigned long end)
|
||||
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);
|
||||
int ret = get_user_pages_fast(start, 1, 0, &p);
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
if (bhv == MADV_SOFT_OFFLINE) {
|
||||
printk(KERN_INFO "Soft offlining page %lx at %lx\n",
|
||||
page_to_pfn(p), start);
|
||||
ret = soft_offline_page(p, MF_COUNT_INCREASED);
|
||||
if (ret)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
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, 1);
|
||||
put_page(p);
|
||||
__memory_failure(page_to_pfn(p), 0, MF_COUNT_INCREASED);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -335,8 +342,8 @@ SYSCALL_DEFINE3(madvise, unsigned long, start, size_t, len_in, int, behavior)
|
||||
size_t len;
|
||||
|
||||
#ifdef CONFIG_MEMORY_FAILURE
|
||||
if (behavior == MADV_HWPOISON)
|
||||
return madvise_hwpoison(start, start+len_in);
|
||||
if (behavior == MADV_HWPOISON || behavior == MADV_SOFT_OFFLINE)
|
||||
return madvise_hwpoison(behavior, start, start+len_in);
|
||||
#endif
|
||||
if (!madvise_behavior_valid(behavior))
|
||||
return error;
|
||||
|
||||
+9
-7
@@ -283,6 +283,11 @@ mem_cgroup_zoneinfo(struct mem_cgroup *mem, int nid, int zid)
|
||||
return &mem->info.nodeinfo[nid]->zoneinfo[zid];
|
||||
}
|
||||
|
||||
struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *mem)
|
||||
{
|
||||
return &mem->css;
|
||||
}
|
||||
|
||||
static struct mem_cgroup_per_zone *
|
||||
page_cgroup_zoneinfo(struct page_cgroup *pc)
|
||||
{
|
||||
@@ -1536,25 +1541,22 @@ static struct mem_cgroup *mem_cgroup_lookup(unsigned short id)
|
||||
return container_of(css, struct mem_cgroup, css);
|
||||
}
|
||||
|
||||
static struct mem_cgroup *try_get_mem_cgroup_from_swapcache(struct page *page)
|
||||
struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page)
|
||||
{
|
||||
struct mem_cgroup *mem;
|
||||
struct mem_cgroup *mem = NULL;
|
||||
struct page_cgroup *pc;
|
||||
unsigned short id;
|
||||
swp_entry_t ent;
|
||||
|
||||
VM_BUG_ON(!PageLocked(page));
|
||||
|
||||
if (!PageSwapCache(page))
|
||||
return NULL;
|
||||
|
||||
pc = lookup_page_cgroup(page);
|
||||
lock_page_cgroup(pc);
|
||||
if (PageCgroupUsed(pc)) {
|
||||
mem = pc->mem_cgroup;
|
||||
if (mem && !css_tryget(&mem->css))
|
||||
mem = NULL;
|
||||
} else {
|
||||
} else if (PageSwapCache(page)) {
|
||||
ent.val = page_private(page);
|
||||
id = lookup_swap_cgroup(ent);
|
||||
rcu_read_lock();
|
||||
@@ -1874,7 +1876,7 @@ int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
|
||||
*/
|
||||
if (!PageSwapCache(page))
|
||||
goto charge_cur_mm;
|
||||
mem = try_get_mem_cgroup_from_swapcache(page);
|
||||
mem = try_get_mem_cgroup_from_page(page);
|
||||
if (!mem)
|
||||
goto charge_cur_mm;
|
||||
*ptr = mem;
|
||||
|
||||
+507
-55
File diff suppressed because it is too large
Load Diff
@@ -2555,6 +2555,10 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||
ret = VM_FAULT_MAJOR;
|
||||
count_vm_event(PGMAJFAULT);
|
||||
} else if (PageHWPoison(page)) {
|
||||
/*
|
||||
* hwpoisoned dirty swapcache pages are kept for killing
|
||||
* owner processes (which may be unknown at hwpoison time)
|
||||
*/
|
||||
ret = VM_FAULT_HWPOISON;
|
||||
delayacct_clear_flag(DELAYACCT_PF_SWAPIN);
|
||||
goto out_release;
|
||||
|
||||
@@ -5091,3 +5091,24 @@ __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn)
|
||||
spin_unlock_irqrestore(&zone->lock, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MEMORY_FAILURE
|
||||
bool is_free_buddy_page(struct page *page)
|
||||
{
|
||||
struct zone *zone = page_zone(page);
|
||||
unsigned long pfn = page_to_pfn(page);
|
||||
unsigned long flags;
|
||||
int order;
|
||||
|
||||
spin_lock_irqsave(&zone->lock, flags);
|
||||
for (order = 0; order < MAX_ORDER; order++) {
|
||||
struct page *page_head = page - (pfn & ((1 << order) - 1));
|
||||
|
||||
if (PageBuddy(page_head) && page_order(page_head) >= order)
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&zone->lock, flags);
|
||||
|
||||
return order < MAX_ORDER;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user