diff options
-rw-r--r-- | drivers/nvdimm/pmem.c | 26 | ||||
-rw-r--r-- | drivers/nvdimm/pmem.h | 13 |
2 files changed, 39 insertions, 0 deletions
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 8b1fd7f1a224..55c7a69751d3 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/hdreg.h> | 20 | #include <linux/hdreg.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/set_memory.h> | ||
23 | #include <linux/module.h> | 24 | #include <linux/module.h> |
24 | #include <linux/moduleparam.h> | 25 | #include <linux/moduleparam.h> |
25 | #include <linux/badblocks.h> | 26 | #include <linux/badblocks.h> |
@@ -51,6 +52,30 @@ static struct nd_region *to_region(struct pmem_device *pmem) | |||
51 | return to_nd_region(to_dev(pmem)->parent); | 52 | return to_nd_region(to_dev(pmem)->parent); |
52 | } | 53 | } |
53 | 54 | ||
55 | static void hwpoison_clear(struct pmem_device *pmem, | ||
56 | phys_addr_t phys, unsigned int len) | ||
57 | { | ||
58 | unsigned long pfn_start, pfn_end, pfn; | ||
59 | |||
60 | /* only pmem in the linear map supports HWPoison */ | ||
61 | if (is_vmalloc_addr(pmem->virt_addr)) | ||
62 | return; | ||
63 | |||
64 | pfn_start = PHYS_PFN(phys); | ||
65 | pfn_end = pfn_start + PHYS_PFN(len); | ||
66 | for (pfn = pfn_start; pfn < pfn_end; pfn++) { | ||
67 | struct page *page = pfn_to_page(pfn); | ||
68 | |||
69 | /* | ||
70 | * Note, no need to hold a get_dev_pagemap() reference | ||
71 | * here since we're in the driver I/O path and | ||
72 | * outstanding I/O requests pin the dev_pagemap. | ||
73 | */ | ||
74 | if (test_and_clear_pmem_poison(page)) | ||
75 | clear_mce_nospec(pfn); | ||
76 | } | ||
77 | } | ||
78 | |||
54 | static blk_status_t pmem_clear_poison(struct pmem_device *pmem, | 79 | static blk_status_t pmem_clear_poison(struct pmem_device *pmem, |
55 | phys_addr_t offset, unsigned int len) | 80 | phys_addr_t offset, unsigned int len) |
56 | { | 81 | { |
@@ -65,6 +90,7 @@ static blk_status_t pmem_clear_poison(struct pmem_device *pmem, | |||
65 | if (cleared < len) | 90 | if (cleared < len) |
66 | rc = BLK_STS_IOERR; | 91 | rc = BLK_STS_IOERR; |
67 | if (cleared > 0 && cleared / 512) { | 92 | if (cleared > 0 && cleared / 512) { |
93 | hwpoison_clear(pmem, pmem->phys_addr + offset, cleared); | ||
68 | cleared /= 512; | 94 | cleared /= 512; |
69 | dev_dbg(dev, "%#llx clear %ld sector%s\n", | 95 | dev_dbg(dev, "%#llx clear %ld sector%s\n", |
70 | (unsigned long long) sector, cleared, | 96 | (unsigned long long) sector, cleared, |
diff --git a/drivers/nvdimm/pmem.h b/drivers/nvdimm/pmem.h index a64ebc78b5df..59cfe13ea8a8 100644 --- a/drivers/nvdimm/pmem.h +++ b/drivers/nvdimm/pmem.h | |||
@@ -1,6 +1,7 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef __NVDIMM_PMEM_H__ | 2 | #ifndef __NVDIMM_PMEM_H__ |
3 | #define __NVDIMM_PMEM_H__ | 3 | #define __NVDIMM_PMEM_H__ |
4 | #include <linux/page-flags.h> | ||
4 | #include <linux/badblocks.h> | 5 | #include <linux/badblocks.h> |
5 | #include <linux/types.h> | 6 | #include <linux/types.h> |
6 | #include <linux/pfn_t.h> | 7 | #include <linux/pfn_t.h> |
@@ -27,4 +28,16 @@ struct pmem_device { | |||
27 | 28 | ||
28 | long __pmem_direct_access(struct pmem_device *pmem, pgoff_t pgoff, | 29 | long __pmem_direct_access(struct pmem_device *pmem, pgoff_t pgoff, |
29 | long nr_pages, void **kaddr, pfn_t *pfn); | 30 | long nr_pages, void **kaddr, pfn_t *pfn); |
31 | |||
32 | #ifdef CONFIG_MEMORY_FAILURE | ||
33 | static inline bool test_and_clear_pmem_poison(struct page *page) | ||
34 | { | ||
35 | return TestClearPageHWPoison(page); | ||
36 | } | ||
37 | #else | ||
38 | static inline bool test_and_clear_pmem_poison(struct page *page) | ||
39 | { | ||
40 | return false; | ||
41 | } | ||
42 | #endif | ||
30 | #endif /* __NVDIMM_PMEM_H__ */ | 43 | #endif /* __NVDIMM_PMEM_H__ */ |