|
@@ -3,16 +3,53 @@
|
|
|
#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;
|
|
|
|
|
|
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 (!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);
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
+
|
|
|
+ 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)
|