2011-03-31 05:57:33 +04:00
/* Inject a hwpoison memory failure on a arbitrary pfn */
2009-09-16 13:50:17 +04:00
# include <linux/module.h>
# include <linux/debugfs.h>
# include <linux/kernel.h>
# include <linux/mm.h>
2009-12-16 14:19:59 +03:00
# include <linux/swap.h>
# include <linux/pagemap.h>
2010-05-28 04:29:22 +04:00
# include <linux/hugetlb.h>
2009-12-16 14:19:59 +03:00
# include "internal.h"
2009-09-16 13:50:17 +04:00
2009-12-16 14:19:58 +03:00
static struct dentry * hwpoison_dir ;
2009-09-16 13:50:17 +04:00
static int hwpoison_inject ( void * data , u64 val )
{
2009-12-16 14:19:59 +03:00
unsigned long pfn = val ;
struct page * p ;
2010-05-28 04:29:22 +04:00
struct page * hpage ;
2009-12-16 14:19:59 +03:00
int err ;
2009-09-16 13:50:17 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
2009-12-16 14:19:59 +03:00
if ( ! pfn_valid ( pfn ) )
return - ENXIO ;
p = pfn_to_page ( pfn ) ;
2010-05-28 04:29:22 +04:00
hpage = compound_head ( p ) ;
2009-12-16 14:19:59 +03:00
/*
* This implies unable to support free buddy pages .
*/
2010-05-28 04:29:22 +04:00
if ( ! get_page_unless_zero ( hpage ) )
2009-12-16 14:19:59 +03:00
return 0 ;
2013-10-01 00:45:24 +04:00
if ( ! hwpoison_filter_enable )
goto inject ;
2010-05-28 04:29:22 +04:00
if ( ! PageLRU ( p ) & & ! PageHuge ( p ) )
2009-12-16 14:20:00 +03:00
shake_page ( p , 0 ) ;
2009-12-16 14:19:59 +03:00
/*
* This implies unable to support non - LRU pages .
*/
2010-05-28 04:29:22 +04:00
if ( ! PageLRU ( p ) & & ! PageHuge ( p ) )
2009-12-16 14:19:59 +03:00
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 ( ) .
2011-12-15 22:48:12 +04:00
* memory_failure ( ) will redo the check reliably inside page lock .
2009-12-16 14:19:59 +03:00
*/
2010-05-28 04:29:22 +04:00
lock_page ( hpage ) ;
err = hwpoison_filter ( hpage ) ;
unlock_page ( hpage ) ;
2009-12-16 14:19:59 +03:00
if ( err )
return 0 ;
2009-12-16 14:20:01 +03:00
inject :
2009-12-16 14:19:59 +03:00
printk ( KERN_INFO " Injecting memory failure at pfn %lx \n " , pfn ) ;
2011-12-15 22:48:12 +04:00
return memory_failure ( pfn , 18 , MF_COUNT_INCREASED ) ;
2009-09-16 13:50:17 +04:00
}
2009-12-16 14:19:58 +03:00
static int hwpoison_unpoison ( void * data , u64 val )
{
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
return unpoison_memory ( val ) ;
}
2009-09-16 13:50:17 +04:00
DEFINE_SIMPLE_ATTRIBUTE ( hwpoison_fops , NULL , hwpoison_inject , " %lli \n " ) ;
2009-12-16 14:19:58 +03:00
DEFINE_SIMPLE_ATTRIBUTE ( unpoison_fops , NULL , hwpoison_unpoison , " %lli \n " ) ;
2009-09-16 13:50:17 +04:00
static void pfn_inject_exit ( void )
{
if ( hwpoison_dir )
debugfs_remove_recursive ( hwpoison_dir ) ;
}
static int pfn_inject_init ( void )
{
2009-12-16 14:19:58 +03:00
struct dentry * dentry ;
2009-09-16 13:50:17 +04:00
hwpoison_dir = debugfs_create_dir ( " hwpoison " , NULL ) ;
if ( hwpoison_dir = = NULL )
return - ENOMEM ;
2009-12-16 14:19:58 +03:00
/*
* 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 .
*/
2013-09-12 01:23:00 +04:00
dentry = debugfs_create_file ( " corrupt-pfn " , 0200 , hwpoison_dir ,
2009-09-16 13:50:17 +04:00
NULL , & hwpoison_fops ) ;
2009-12-16 14:19:58 +03:00
if ( ! dentry )
goto fail ;
2013-09-12 01:23:00 +04:00
dentry = debugfs_create_file ( " unpoison-pfn " , 0200 , hwpoison_dir ,
2009-12-16 14:19:58 +03:00
NULL , & unpoison_fops ) ;
if ( ! dentry )
goto fail ;
2009-12-16 14:19:59 +03:00
dentry = debugfs_create_u32 ( " corrupt-filter-enable " , 0600 ,
hwpoison_dir , & hwpoison_filter_enable ) ;
if ( ! dentry )
goto fail ;
2009-12-16 14:19:59 +03:00
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 ;
2009-12-16 14:19:59 +03:00
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 ;
2012-08-01 03:43:02 +04:00
# ifdef CONFIG_MEMCG_SWAP
2009-12-16 14:19:59 +03:00
dentry = debugfs_create_u64 ( " corrupt-filter-memcg " , 0600 ,
hwpoison_dir , & hwpoison_filter_memcg ) ;
if ( ! dentry )
goto fail ;
# endif
2009-09-16 13:50:17 +04:00
return 0 ;
2009-12-16 14:19:58 +03:00
fail :
pfn_inject_exit ( ) ;
return - ENOMEM ;
2009-09-16 13:50:17 +04:00
}
module_init ( pfn_inject_init ) ;
module_exit ( pfn_inject_exit ) ;
MODULE_LICENSE ( " GPL " ) ;