forked from Minki/linux
2923b27e54
* memory_failure() gets confused by dev_pagemap backed mappings. The recovery code has specific enabling for several possible page states that needs new enabling to handle poison in dax mappings. Teach memory_failure() about ZONE_DEVICE pages. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE5DAy15EJMCV1R6v9YGjFFmlTOEoFAlt9ui8ACgkQYGjFFmlT OEpNRw//XGj9s7sezfJFeol4psJlRUd935yii/gmJRgi/yPf2VxxQG9qyM6SMBUc 75jASfOL6FSsfxHz0kplyWzMDNdrTkNNAD+9rv80FmY7GqWgcas9DaJX7jZ994vI 5SRO7pfvNZcXlo7IhqZippDw3yxkIU9Ufi0YQKaEUm7GFieptvCZ0p9x3VYfdvwM BExrxQe0X1XUF4xErp5P78+WUbKxP47DLcucRDig8Q7dmHELUdyNzo3E1SVoc7m+ 3CmvyTj6XuFQgOZw7ZKun1BJYfx/eD5ZlRJLZbx6wJHRtTXv/Uea8mZ8mJ31ykN9 F7QVd0Pmlyxys8lcXfK+nvpL09QBE0/PhwWKjmZBoU8AdgP/ZvBXLDL/D6YuMTg6 T4wwtPNJorfV4lVD06OliFkVI4qbKbmNsfRq43Ns7PCaLueu4U/eMaSwSH99UMaZ MGbO140XW2RZsHiU9yTRUmZq73AplePEjxtzR8oHmnjo45nPDPy8mucWPlkT9kXA oUFMhgiviK7dOo19H4eaPJGqLmHM93+x5tpYxGqTr0dUOXUadKWxMsTnkID+8Yi7 /kzQWCFvySz3VhiEHGuWkW08GZT6aCcpkREDomnRh4MEnETlZI8bblcuXYOCLs6c nNf1SIMtLdlsl7U1fEX89PNeQQ2y237vEDhFQZftaalPeu/JJV0= =Ftop -----END PGP SIGNATURE----- Merge tag 'libnvdimm-for-4.19_dax-memory-failure' of gitolite.kernel.org:pub/scm/linux/kernel/git/nvdimm/nvdimm Pull libnvdimm memory-failure update from Dave Jiang: "As it stands, memory_failure() gets thoroughly confused by dev_pagemap backed mappings. The recovery code has specific enabling for several possible page states and needs new enabling to handle poison in dax mappings. In order to support reliable reverse mapping of user space addresses: 1/ Add new locking in the memory_failure() rmap path to prevent races that would typically be handled by the page lock. 2/ Since dev_pagemap pages are hidden from the page allocator and the "compound page" accounting machinery, add a mechanism to determine the size of the mapping that encompasses a given poisoned pfn. 3/ Given pmem errors can be repaired, change the speculatively accessed poison protection, mce_unmap_kpfn(), to be reversible and otherwise allow ongoing access from the kernel. A side effect of this enabling is that MADV_HWPOISON becomes usable for dax mappings, however the primary motivation is to allow the system to survive userspace consumption of hardware-poison via dax. Specifically the current behavior is: mce: Uncorrected hardware memory error in user-access at af34214200 {1}[Hardware Error]: It has been corrected by h/w and requires no further action mce: [Hardware Error]: Machine check events logged {1}[Hardware Error]: event severity: corrected Memory failure: 0xaf34214: reserved kernel page still referenced by 1 users [..] Memory failure: 0xaf34214: recovery action for reserved kernel page: Failed mce: Memory error not recovered <reboot> ...and with these changes: Injecting memory failure for pfn 0x20cb00 at process virtual address 0x7f763dd00000 Memory failure: 0x20cb00: Killing dax-pmd:5421 due to hardware memory corruption Memory failure: 0x20cb00: recovery action for dax page: Recovered Given all the cross dependencies I propose taking this through nvdimm.git with acks from Naoya, x86/core, x86/RAS, and of course dax folks" * tag 'libnvdimm-for-4.19_dax-memory-failure' of gitolite.kernel.org:pub/scm/linux/kernel/git/nvdimm/nvdimm: libnvdimm, pmem: Restore page attributes when clearing errors x86/memory_failure: Introduce {set, clear}_mce_nospec() x86/mm/pat: Prepare {reserve, free}_memtype() for "decoy" addresses mm, memory_failure: Teach memory_failure() about dev_pagemap pages filesystem-dax: Introduce dax_lock_mapping_entry() mm, memory_failure: Collect mapping size in collect_procs() mm, madvise_inject_error: Let memory_failure() optionally take a page reference mm, dev_pagemap: Do not clear ->mapping on final put mm, madvise_inject_error: Disable MADV_SOFT_OFFLINE for ZONE_DEVICE pages filesystem-dax: Set page->index device-dax: Set page->index device-dax: Enable page_mapping() device-dax: Convert to vmf_insert_mixed and vm_fault_t
135 lines
5.2 KiB
C
135 lines
5.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_X86_SET_MEMORY_H
|
|
#define _ASM_X86_SET_MEMORY_H
|
|
|
|
#include <asm/page.h>
|
|
#include <asm-generic/set_memory.h>
|
|
|
|
/*
|
|
* The set_memory_* API can be used to change various attributes of a virtual
|
|
* address range. The attributes include:
|
|
* Cachability : UnCached, WriteCombining, WriteThrough, WriteBack
|
|
* Executability : eXeutable, NoteXecutable
|
|
* Read/Write : ReadOnly, ReadWrite
|
|
* Presence : NotPresent
|
|
* Encryption : Encrypted, Decrypted
|
|
*
|
|
* Within a category, the attributes are mutually exclusive.
|
|
*
|
|
* The implementation of this API will take care of various aspects that
|
|
* are associated with changing such attributes, such as:
|
|
* - Flushing TLBs
|
|
* - Flushing CPU caches
|
|
* - Making sure aliases of the memory behind the mapping don't violate
|
|
* coherency rules as defined by the CPU in the system.
|
|
*
|
|
* What this API does not do:
|
|
* - Provide exclusion between various callers - including callers that
|
|
* operation on other mappings of the same physical page
|
|
* - Restore default attributes when a page is freed
|
|
* - Guarantee that mappings other than the requested one are
|
|
* in any state, other than that these do not violate rules for
|
|
* the CPU you have. Do not depend on any effects on other mappings,
|
|
* CPUs other than the one you have may have more relaxed rules.
|
|
* The caller is required to take care of these.
|
|
*/
|
|
|
|
int _set_memory_uc(unsigned long addr, int numpages);
|
|
int _set_memory_wc(unsigned long addr, int numpages);
|
|
int _set_memory_wt(unsigned long addr, int numpages);
|
|
int _set_memory_wb(unsigned long addr, int numpages);
|
|
int set_memory_uc(unsigned long addr, int numpages);
|
|
int set_memory_wc(unsigned long addr, int numpages);
|
|
int set_memory_wt(unsigned long addr, int numpages);
|
|
int set_memory_wb(unsigned long addr, int numpages);
|
|
int set_memory_np(unsigned long addr, int numpages);
|
|
int set_memory_4k(unsigned long addr, int numpages);
|
|
int set_memory_encrypted(unsigned long addr, int numpages);
|
|
int set_memory_decrypted(unsigned long addr, int numpages);
|
|
int set_memory_np_noalias(unsigned long addr, int numpages);
|
|
|
|
int set_memory_array_uc(unsigned long *addr, int addrinarray);
|
|
int set_memory_array_wc(unsigned long *addr, int addrinarray);
|
|
int set_memory_array_wt(unsigned long *addr, int addrinarray);
|
|
int set_memory_array_wb(unsigned long *addr, int addrinarray);
|
|
|
|
int set_pages_array_uc(struct page **pages, int addrinarray);
|
|
int set_pages_array_wc(struct page **pages, int addrinarray);
|
|
int set_pages_array_wt(struct page **pages, int addrinarray);
|
|
int set_pages_array_wb(struct page **pages, int addrinarray);
|
|
|
|
/*
|
|
* For legacy compatibility with the old APIs, a few functions
|
|
* are provided that work on a "struct page".
|
|
* These functions operate ONLY on the 1:1 kernel mapping of the
|
|
* memory that the struct page represents, and internally just
|
|
* call the set_memory_* function. See the description of the
|
|
* set_memory_* function for more details on conventions.
|
|
*
|
|
* These APIs should be considered *deprecated* and are likely going to
|
|
* be removed in the future.
|
|
* The reason for this is the implicit operation on the 1:1 mapping only,
|
|
* making this not a generally useful API.
|
|
*
|
|
* Specifically, many users of the old APIs had a virtual address,
|
|
* called virt_to_page() or vmalloc_to_page() on that address to
|
|
* get a struct page* that the old API required.
|
|
* To convert these cases, use set_memory_*() on the original
|
|
* virtual address, do not use these functions.
|
|
*/
|
|
|
|
int set_pages_uc(struct page *page, int numpages);
|
|
int set_pages_wb(struct page *page, int numpages);
|
|
int set_pages_x(struct page *page, int numpages);
|
|
int set_pages_nx(struct page *page, int numpages);
|
|
int set_pages_ro(struct page *page, int numpages);
|
|
int set_pages_rw(struct page *page, int numpages);
|
|
|
|
extern int kernel_set_to_readonly;
|
|
void set_kernel_text_rw(void);
|
|
void set_kernel_text_ro(void);
|
|
|
|
#ifdef CONFIG_X86_64
|
|
static inline int set_mce_nospec(unsigned long pfn)
|
|
{
|
|
unsigned long decoy_addr;
|
|
int rc;
|
|
|
|
/*
|
|
* Mark the linear address as UC to make sure we don't log more
|
|
* errors because of speculative access to the page.
|
|
* We would like to just call:
|
|
* set_memory_uc((unsigned long)pfn_to_kaddr(pfn), 1);
|
|
* but doing that would radically increase the odds of a
|
|
* speculative access to the poison page because we'd have
|
|
* the virtual address of the kernel 1:1 mapping sitting
|
|
* around in registers.
|
|
* Instead we get tricky. We create a non-canonical address
|
|
* that looks just like the one we want, but has bit 63 flipped.
|
|
* This relies on set_memory_uc() properly sanitizing any __pa()
|
|
* results with __PHYSICAL_MASK or PTE_PFN_MASK.
|
|
*/
|
|
decoy_addr = (pfn << PAGE_SHIFT) + (PAGE_OFFSET ^ BIT(63));
|
|
|
|
rc = set_memory_uc(decoy_addr, 1);
|
|
if (rc)
|
|
pr_warn("Could not invalidate pfn=0x%lx from 1:1 map\n", pfn);
|
|
return rc;
|
|
}
|
|
#define set_mce_nospec set_mce_nospec
|
|
|
|
/* Restore full speculative operation to the pfn. */
|
|
static inline int clear_mce_nospec(unsigned long pfn)
|
|
{
|
|
return set_memory_wb((unsigned long) pfn_to_kaddr(pfn), 1);
|
|
}
|
|
#define clear_mce_nospec clear_mce_nospec
|
|
#else
|
|
/*
|
|
* Few people would run a 32-bit kernel on a machine that supports
|
|
* recoverable errors because they have too much memory to boot 32-bit.
|
|
*/
|
|
#endif
|
|
|
|
#endif /* _ASM_X86_SET_MEMORY_H */
|