Skip to content

Commit 4e41a30

Browse files
Naoya Horiguchitorvalds
authored andcommitted
mm: hwpoison: adjust for new thp refcounting
Some mm-related BUG_ON()s could trigger from hwpoison code due to recent changes in thp refcounting rule. This patch fixes them up. In the new refcounting, we no longer use tail->_mapcount to keep tail's refcount, and thereby we can simplify get/put_hwpoison_page(). And another change is that tail's refcount is not transferred to the raw page during thp split (more precisely, in new rule we don't take refcount on tail page any more.) So when we need thp split, we have to transfer the refcount properly to the 4kB soft-offlined page before migration. thp split code goes into core code only when precheck (total_mapcount(head) == page_count(head) - 1) passes to avoid useless split, where we assume that one refcount is held by the caller of thp split and the others are taken via mapping. To meet this assumption, this patch moves thp split part in soft_offline_page() after get_any_page(). [[email protected]: remove unneeded #define, per Kirill] Signed-off-by: Naoya Horiguchi <[email protected]> Acked-by: Kirill A. Shutemov <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent d96b339 commit 4e41a30

File tree

2 files changed

+22
-53
lines changed

2 files changed

+22
-53
lines changed

include/linux/mm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2217,7 +2217,7 @@ extern int memory_failure(unsigned long pfn, int trapno, int flags);
22172217
extern void memory_failure_queue(unsigned long pfn, int trapno, int flags);
22182218
extern int unpoison_memory(unsigned long pfn);
22192219
extern int get_hwpoison_page(struct page *page);
2220-
extern void put_hwpoison_page(struct page *page);
2220+
#define put_hwpoison_page(page) put_page(page)
22212221
extern int sysctl_memory_failure_early_kill;
22222222
extern int sysctl_memory_failure_recovery;
22232223
extern void shake_page(struct page *p, int access);

mm/memory-failure.c

Lines changed: 21 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -882,15 +882,7 @@ int get_hwpoison_page(struct page *page)
882882
{
883883
struct page *head = compound_head(page);
884884

885-
if (PageHuge(head))
886-
return get_page_unless_zero(head);
887-
888-
/*
889-
* Thp tail page has special refcounting rule (refcount of tail pages
890-
* is stored in ->_mapcount,) so we can't call get_page_unless_zero()
891-
* directly for tail pages.
892-
*/
893-
if (PageTransHuge(head)) {
885+
if (!PageHuge(head) && PageTransHuge(head)) {
894886
/*
895887
* Non anonymous thp exists only in allocation/free time. We
896888
* can't handle such a case correctly, so let's give it up.
@@ -902,41 +894,12 @@ int get_hwpoison_page(struct page *page)
902894
page_to_pfn(page));
903895
return 0;
904896
}
905-
906-
if (get_page_unless_zero(head)) {
907-
if (PageTail(page))
908-
get_page(page);
909-
return 1;
910-
} else {
911-
return 0;
912-
}
913897
}
914898

915-
return get_page_unless_zero(page);
899+
return get_page_unless_zero(head);
916900
}
917901
EXPORT_SYMBOL_GPL(get_hwpoison_page);
918902

919-
/**
920-
* put_hwpoison_page() - Put refcount for memory error handling:
921-
* @page: raw error page (hit by memory error)
922-
*/
923-
void put_hwpoison_page(struct page *page)
924-
{
925-
struct page *head = compound_head(page);
926-
927-
if (PageHuge(head)) {
928-
put_page(head);
929-
return;
930-
}
931-
932-
if (PageTransHuge(head))
933-
if (page != head)
934-
put_page(head);
935-
936-
put_page(page);
937-
}
938-
EXPORT_SYMBOL_GPL(put_hwpoison_page);
939-
940903
/*
941904
* Do all that is necessary to remove user space mappings. Unmap
942905
* the pages and send SIGBUS to the processes if the data was dirty.
@@ -1162,6 +1125,8 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
11621125
return -EBUSY;
11631126
}
11641127
unlock_page(hpage);
1128+
get_hwpoison_page(p);
1129+
put_hwpoison_page(hpage);
11651130
VM_BUG_ON_PAGE(!page_count(p), p);
11661131
hpage = compound_head(p);
11671132
}
@@ -1753,24 +1718,28 @@ int soft_offline_page(struct page *page, int flags)
17531718
put_hwpoison_page(page);
17541719
return -EBUSY;
17551720
}
1756-
if (!PageHuge(page) && PageTransHuge(hpage)) {
1757-
lock_page(page);
1758-
ret = split_huge_page(hpage);
1759-
unlock_page(page);
1760-
if (unlikely(ret)) {
1761-
pr_info("soft offline: %#lx: failed to split THP\n",
1762-
pfn);
1763-
if (flags & MF_COUNT_INCREASED)
1764-
put_hwpoison_page(page);
1765-
return -EBUSY;
1766-
}
1767-
}
17681721

17691722
get_online_mems();
1770-
17711723
ret = get_any_page(page, pfn, flags);
17721724
put_online_mems();
1725+
17731726
if (ret > 0) { /* for in-use pages */
1727+
if (!PageHuge(page) && PageTransHuge(hpage)) {
1728+
lock_page(hpage);
1729+
ret = split_huge_page(hpage);
1730+
unlock_page(hpage);
1731+
if (unlikely(ret || PageTransCompound(page) ||
1732+
!PageAnon(page))) {
1733+
pr_info("soft offline: %#lx: failed to split THP\n",
1734+
pfn);
1735+
if (flags & MF_COUNT_INCREASED)
1736+
put_hwpoison_page(hpage);
1737+
return -EBUSY;
1738+
}
1739+
get_hwpoison_page(page);
1740+
put_hwpoison_page(hpage);
1741+
}
1742+
17741743
if (PageHuge(page))
17751744
ret = soft_offline_huge_page(page, flags);
17761745
else

0 commit comments

Comments
 (0)