mm: factor helpers for memory_failure_dev_pagemap

memory_failure_dev_pagemap code is a bit complex before introduce RMAP
feature for fsdax.  So it is needed to factor some helper functions to
simplify these code.

[akpm@linux-foundation.org: fix CONFIG_HUGETLB_PAGE=n build]
[zhengbin13@huawei.com: fix redefinition of mf_generic_kill_procs]
  Link: https://lkml.kernel.org/r/20220628112143.1170473-1-zhengbin13@huawei.com
Link: https://lkml.kernel.org/r/20220603053738.1218681-3-ruansy.fnst@fujitsu.com
Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
Signed-off-by: Zheng Bin <zhengbin13@huawei.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Miaohe Lin <linmiaohe@huawei.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dan Williams <dan.j.wiliams@intel.com>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Goldwyn Rodrigues <rgoldwyn@suse.com>
Cc: Goldwyn Rodrigues <rgoldwyn@suse.de>
Cc: Jane Chu <jane.chu@oracle.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Naoya Horiguchi <naoya.horiguchi@nec.com>
Cc: Ritesh Harjani <riteshh@linux.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Shiyang Ruan 2022-06-03 13:37:26 +08:00 committed by akpm
parent 8012b86608
commit 00cc790e00
1 changed files with 95 additions and 76 deletions

View File

@ -1499,6 +1499,95 @@ static int try_to_split_thp_page(struct page *page, const char *msg)
return 0;
}
static void unmap_and_kill(struct list_head *to_kill, unsigned long pfn,
struct address_space *mapping, pgoff_t index, int flags)
{
struct to_kill *tk;
unsigned long size = 0;
list_for_each_entry(tk, to_kill, nd)
if (tk->size_shift)
size = max(size, 1UL << tk->size_shift);
if (size) {
/*
* Unmap the largest mapping to avoid breaking up device-dax
* mappings which are constant size. The actual size of the
* mapping being torn down is communicated in siginfo, see
* kill_proc()
*/
loff_t start = (index << PAGE_SHIFT) & ~(size - 1);
unmap_mapping_range(mapping, start, size, 0);
}
kill_procs(to_kill, flags & MF_MUST_KILL, false, pfn, flags);
}
static int mf_generic_kill_procs(unsigned long long pfn, int flags,
struct dev_pagemap *pgmap)
{
struct page *page = pfn_to_page(pfn);
LIST_HEAD(to_kill);
dax_entry_t cookie;
int rc = 0;
/*
* Pages instantiated by device-dax (not filesystem-dax)
* may be compound pages.
*/
page = compound_head(page);
/*
* Prevent the inode from being freed while we are interrogating
* the address_space, typically this would be handled by
* lock_page(), but dax pages do not use the page lock. This
* also prevents changes to the mapping of this pfn until
* poison signaling is complete.
*/
cookie = dax_lock_page(page);
if (!cookie)
return -EBUSY;
if (hwpoison_filter(page)) {
rc = -EOPNOTSUPP;
goto unlock;
}
switch (pgmap->type) {
case MEMORY_DEVICE_PRIVATE:
case MEMORY_DEVICE_COHERENT:
/*
* TODO: Handle device pages which may need coordination
* with device-side memory.
*/
rc = -ENXIO;
goto unlock;
default:
break;
}
/*
* Use this flag as an indication that the dax page has been
* remapped UC to prevent speculative consumption of poison.
*/
SetPageHWPoison(page);
/*
* Unlike System-RAM there is no possibility to swap in a
* different physical page at a given virtual address, so all
* userspace consumption of ZONE_DEVICE memory necessitates
* SIGBUS (i.e. MF_MUST_KILL)
*/
flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
collect_procs(page, &to_kill, true);
unmap_and_kill(&to_kill, pfn, page->mapping, page->index, flags);
unlock:
dax_unlock_page(page, cookie);
return rc;
}
/*
* Called from hugetlb code with hugetlb_lock held.
*
@ -1634,23 +1723,20 @@ out:
unlock_page(head);
return res;
}
#else
static inline int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb)
{
return 0;
}
#endif
#endif /* CONFIG_HUGETLB_PAGE */
static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
struct dev_pagemap *pgmap)
{
struct page *page = pfn_to_page(pfn);
unsigned long size = 0;
struct to_kill *tk;
LIST_HEAD(tokill);
int rc = -EBUSY;
loff_t start;
dax_entry_t cookie;
int rc = -ENXIO;
if (flags & MF_COUNT_INCREASED)
/*
@ -1659,77 +1745,10 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
put_page(page);
/* device metadata space is not recoverable */
if (!pgmap_pfn_valid(pgmap, pfn)) {
rc = -ENXIO;
goto out;
}
/*
* Pages instantiated by device-dax (not filesystem-dax)
* may be compound pages.
*/
page = compound_head(page);
/*
* Prevent the inode from being freed while we are interrogating
* the address_space, typically this would be handled by
* lock_page(), but dax pages do not use the page lock. This
* also prevents changes to the mapping of this pfn until
* poison signaling is complete.
*/
cookie = dax_lock_page(page);
if (!cookie)
if (!pgmap_pfn_valid(pgmap, pfn))
goto out;
if (hwpoison_filter(page)) {
rc = -EOPNOTSUPP;
goto unlock;
}
switch (pgmap->type) {
case MEMORY_DEVICE_PRIVATE:
case MEMORY_DEVICE_COHERENT:
/*
* TODO: Handle device pages which may need coordination
* with device-side memory.
*/
goto unlock;
default:
break;
}
/*
* Use this flag as an indication that the dax page has been
* remapped UC to prevent speculative consumption of poison.
*/
SetPageHWPoison(page);
/*
* Unlike System-RAM there is no possibility to swap in a
* different physical page at a given virtual address, so all
* userspace consumption of ZONE_DEVICE memory necessitates
* SIGBUS (i.e. MF_MUST_KILL)
*/
flags |= MF_ACTION_REQUIRED | MF_MUST_KILL;
collect_procs(page, &tokill, true);
list_for_each_entry(tk, &tokill, nd)
if (tk->size_shift)
size = max(size, 1UL << tk->size_shift);
if (size) {
/*
* Unmap the largest mapping to avoid breaking up
* device-dax mappings which are constant size. The
* actual size of the mapping being torn down is
* communicated in siginfo, see kill_proc()
*/
start = (page->index << PAGE_SHIFT) & ~(size - 1);
unmap_mapping_range(page->mapping, start, size, 0);
}
kill_procs(&tokill, true, false, pfn, flags);
rc = 0;
unlock:
dax_unlock_page(page, cookie);
rc = mf_generic_kill_procs(pfn, flags, pgmap);
out:
/* drop pgmap ref acquired in caller */
put_dev_pagemap(pgmap);