115 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Inject a hwpoison memory failure on a arbitrary pfn */3#include <linux/module.h>4#include <linux/debugfs.h>5#include <linux/kernel.h>6#include <linux/mm.h>7#include <linux/swap.h>8#include <linux/pagemap.h>9#include <linux/hugetlb.h>10#include "internal.h"11 12static struct dentry *hwpoison_dir;13 14static int hwpoison_inject(void *data, u64 val)15{16 unsigned long pfn = val;17 struct page *p;18 struct folio *folio;19 int err;20 21 if (!capable(CAP_SYS_ADMIN))22 return -EPERM;23 24 if (!pfn_valid(pfn))25 return -ENXIO;26 27 p = pfn_to_page(pfn);28 folio = page_folio(p);29 30 if (!hwpoison_filter_enable)31 goto inject;32 33 shake_folio(folio);34 /*35 * This implies unable to support non-LRU pages except free page.36 */37 if (!folio_test_lru(folio) && !folio_test_hugetlb(folio) &&38 !is_free_buddy_page(p))39 return 0;40 41 /*42 * do a racy check to make sure PG_hwpoison will only be set for43 * the targeted owner (or on a free page).44 * memory_failure() will redo the check reliably inside page lock.45 */46 err = hwpoison_filter(&folio->page);47 if (err)48 return 0;49 50inject:51 pr_info("Injecting memory failure at pfn %#lx\n", pfn);52 err = memory_failure(pfn, MF_SW_SIMULATED);53 return (err == -EOPNOTSUPP) ? 0 : err;54}55 56static int hwpoison_unpoison(void *data, u64 val)57{58 if (!capable(CAP_SYS_ADMIN))59 return -EPERM;60 61 return unpoison_memory(val);62}63 64DEFINE_DEBUGFS_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");65DEFINE_DEBUGFS_ATTRIBUTE(unpoison_fops, NULL, hwpoison_unpoison, "%lli\n");66 67static void __exit pfn_inject_exit(void)68{69 hwpoison_filter_enable = 0;70 debugfs_remove_recursive(hwpoison_dir);71}72 73static int __init pfn_inject_init(void)74{75 hwpoison_dir = debugfs_create_dir("hwpoison", NULL);76 77 /*78 * Note that the below poison/unpoison interfaces do not involve79 * hardware status change, hence do not require hardware support.80 * They are mainly for testing hwpoison in software level.81 */82 debugfs_create_file("corrupt-pfn", 0200, hwpoison_dir, NULL,83 &hwpoison_fops);84 85 debugfs_create_file("unpoison-pfn", 0200, hwpoison_dir, NULL,86 &unpoison_fops);87 88 debugfs_create_u32("corrupt-filter-enable", 0600, hwpoison_dir,89 &hwpoison_filter_enable);90 91 debugfs_create_u32("corrupt-filter-dev-major", 0600, hwpoison_dir,92 &hwpoison_filter_dev_major);93 94 debugfs_create_u32("corrupt-filter-dev-minor", 0600, hwpoison_dir,95 &hwpoison_filter_dev_minor);96 97 debugfs_create_u64("corrupt-filter-flags-mask", 0600, hwpoison_dir,98 &hwpoison_filter_flags_mask);99 100 debugfs_create_u64("corrupt-filter-flags-value", 0600, hwpoison_dir,101 &hwpoison_filter_flags_value);102 103#ifdef CONFIG_MEMCG104 debugfs_create_u64("corrupt-filter-memcg", 0600, hwpoison_dir,105 &hwpoison_filter_memcg);106#endif107 108 return 0;109}110 111module_init(pfn_inject_init);112module_exit(pfn_inject_exit);113MODULE_DESCRIPTION("HWPoison pages injector");114MODULE_LICENSE("GPL");115