Skip to content

Commit 6b9a217

Browse files
osalvadorvilardagatorvalds
authored andcommitted
mm,hwpoison: refactor soft_offline_huge_page and __soft_offline_page
Merging soft_offline_huge_page and __soft_offline_page let us get rid of quite some duplicated code, and makes the code much easier to follow. Now, __soft_offline_page will handle both normal and hugetlb pages. Signed-off-by: Oscar Salvador <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Acked-by: Naoya Horiguchi <[email protected]> Cc: "Aneesh Kumar K.V" <[email protected]> Cc: Aneesh Kumar K.V <[email protected]> Cc: Aristeu Rozanski <[email protected]> Cc: Dave Hansen <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Dmitry Yakunin <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Mike Kravetz <[email protected]> Cc: Oscar Salvador <[email protected]> Cc: Qian Cai <[email protected]> Cc: Tony Luck <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 79f5f8f commit 6b9a217

File tree

1 file changed

+82
-100
lines changed

1 file changed

+82
-100
lines changed

mm/memory-failure.c

Lines changed: 82 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,31 @@ int sysctl_memory_failure_recovery __read_mostly = 1;
6565

6666
atomic_long_t num_poisoned_pages __read_mostly = ATOMIC_LONG_INIT(0);
6767

68-
static void page_handle_poison(struct page *page, bool release)
68+
static bool page_handle_poison(struct page *page, bool hugepage_or_freepage, bool release)
6969
{
70+
if (hugepage_or_freepage) {
71+
/*
72+
* Doing this check for free pages is also fine since dissolve_free_huge_page
73+
* returns 0 for non-hugetlb pages as well.
74+
*/
75+
if (dissolve_free_huge_page(page) || !take_page_off_buddy(page))
76+
/*
77+
* We could fail to take off the target page from buddy
78+
* for example due to racy page allocaiton, but that's
79+
* acceptable because soft-offlined page is not broken
80+
* and if someone really want to use it, they should
81+
* take it.
82+
*/
83+
return false;
84+
}
85+
7086
SetPageHWPoison(page);
7187
if (release)
7288
put_page(page);
7389
page_ref_inc(page);
7490
num_poisoned_pages_inc();
91+
92+
return true;
7593
}
7694

7795
#if defined(CONFIG_HWPOISON_INJECT) || defined(CONFIG_HWPOISON_INJECT_MODULE)
@@ -1725,63 +1743,51 @@ static int get_any_page(struct page *page, unsigned long pfn, int flags)
17251743
return ret;
17261744
}
17271745

1728-
static int soft_offline_huge_page(struct page *page, int flags)
1746+
static bool isolate_page(struct page *page, struct list_head *pagelist)
17291747
{
1730-
int ret;
1731-
unsigned long pfn = page_to_pfn(page);
1732-
struct page *hpage = compound_head(page);
1733-
LIST_HEAD(pagelist);
1748+
bool isolated = false;
1749+
bool lru = PageLRU(page);
17341750

1735-
/*
1736-
* This double-check of PageHWPoison is to avoid the race with
1737-
* memory_failure(). See also comment in __soft_offline_page().
1738-
*/
1739-
lock_page(hpage);
1740-
if (PageHWPoison(hpage)) {
1741-
unlock_page(hpage);
1742-
put_page(hpage);
1743-
pr_info("soft offline: %#lx hugepage already poisoned\n", pfn);
1744-
return -EBUSY;
1751+
if (PageHuge(page)) {
1752+
isolated = isolate_huge_page(page, pagelist);
1753+
} else {
1754+
if (lru)
1755+
isolated = !isolate_lru_page(page);
1756+
else
1757+
isolated = !isolate_movable_page(page, ISOLATE_UNEVICTABLE);
1758+
1759+
if (isolated)
1760+
list_add(&page->lru, pagelist);
17451761
}
1746-
unlock_page(hpage);
17471762

1748-
ret = isolate_huge_page(hpage, &pagelist);
1763+
if (isolated && lru)
1764+
inc_node_page_state(page, NR_ISOLATED_ANON +
1765+
page_is_file_lru(page));
1766+
17491767
/*
1750-
* get_any_page() and isolate_huge_page() takes a refcount each,
1751-
* so need to drop one here.
1768+
* If we succeed to isolate the page, we grabbed another refcount on
1769+
* the page, so we can safely drop the one we got from get_any_pages().
1770+
* If we failed to isolate the page, it means that we cannot go further
1771+
* and we will return an error, so drop the reference we got from
1772+
* get_any_pages() as well.
17521773
*/
1753-
put_page(hpage);
1754-
if (!ret) {
1755-
pr_info("soft offline: %#lx hugepage failed to isolate\n", pfn);
1756-
return -EBUSY;
1757-
}
1758-
1759-
ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
1760-
MIGRATE_SYNC, MR_MEMORY_FAILURE);
1761-
if (ret) {
1762-
pr_info("soft offline: %#lx: hugepage migration failed %d, type %lx (%pGp)\n",
1763-
pfn, ret, page->flags, &page->flags);
1764-
if (!list_empty(&pagelist))
1765-
putback_movable_pages(&pagelist);
1766-
if (ret > 0)
1767-
ret = -EIO;
1768-
} else {
1769-
/*
1770-
* We set PG_hwpoison only when we were able to take the page
1771-
* off the buddy.
1772-
*/
1773-
if (!dissolve_free_huge_page(page) && take_page_off_buddy(page))
1774-
page_handle_poison(page, false);
1775-
else
1776-
ret = -EBUSY;
1777-
}
1778-
return ret;
1774+
put_page(page);
1775+
return isolated;
17791776
}
17801777

1781-
static int __soft_offline_page(struct page *page, int flags)
1778+
/*
1779+
* __soft_offline_page handles hugetlb-pages and non-hugetlb pages.
1780+
* If the page is a non-dirty unmapped page-cache page, it simply invalidates.
1781+
* If the page is mapped, it migrates the contents over.
1782+
*/
1783+
static int __soft_offline_page(struct page *page)
17821784
{
1783-
int ret;
1785+
int ret = 0;
17841786
unsigned long pfn = page_to_pfn(page);
1787+
struct page *hpage = compound_head(page);
1788+
char const *msg_page[] = {"page", "hugepage"};
1789+
bool huge = PageHuge(page);
1790+
LIST_HEAD(pagelist);
17851791

17861792
/*
17871793
* Check PageHWPoison again inside page lock because PageHWPoison
@@ -1790,98 +1796,74 @@ static int __soft_offline_page(struct page *page, int flags)
17901796
* so there's no race between soft_offline_page() and memory_failure().
17911797
*/
17921798
lock_page(page);
1793-
wait_on_page_writeback(page);
1799+
if (!PageHuge(page))
1800+
wait_on_page_writeback(page);
17941801
if (PageHWPoison(page)) {
17951802
unlock_page(page);
17961803
put_page(page);
17971804
pr_info("soft offline: %#lx page already poisoned\n", pfn);
17981805
return -EBUSY;
17991806
}
1800-
/*
1801-
* Try to invalidate first. This should work for
1802-
* non dirty unmapped page cache pages.
1803-
*/
1804-
ret = invalidate_inode_page(page);
1807+
1808+
if (!PageHuge(page))
1809+
/*
1810+
* Try to invalidate first. This should work for
1811+
* non dirty unmapped page cache pages.
1812+
*/
1813+
ret = invalidate_inode_page(page);
18051814
unlock_page(page);
1815+
18061816
/*
18071817
* RED-PEN would be better to keep it isolated here, but we
18081818
* would need to fix isolation locking first.
18091819
*/
1810-
if (ret == 1) {
1820+
if (ret) {
18111821
pr_info("soft_offline: %#lx: invalidated\n", pfn);
1812-
page_handle_poison(page, true);
1822+
page_handle_poison(page, false, true);
18131823
return 0;
18141824
}
18151825

1816-
/*
1817-
* Simple invalidation didn't work.
1818-
* Try to migrate to a new page instead. migrate.c
1819-
* handles a large number of cases for us.
1820-
*/
1821-
if (PageLRU(page))
1822-
ret = isolate_lru_page(page);
1823-
else
1824-
ret = isolate_movable_page(page, ISOLATE_UNEVICTABLE);
1825-
/*
1826-
* Drop page reference which is came from get_any_page()
1827-
* successful isolate_lru_page() already took another one.
1828-
*/
1829-
put_page(page);
1830-
if (!ret) {
1831-
LIST_HEAD(pagelist);
1832-
/*
1833-
* After isolated lru page, the PageLRU will be cleared,
1834-
* so use !__PageMovable instead for LRU page's mapping
1835-
* cannot have PAGE_MAPPING_MOVABLE.
1836-
*/
1837-
if (!__PageMovable(page))
1838-
inc_node_page_state(page, NR_ISOLATED_ANON +
1839-
page_is_file_lru(page));
1840-
list_add(&page->lru, &pagelist);
1826+
if (isolate_page(hpage, &pagelist)) {
18411827
ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
18421828
MIGRATE_SYNC, MR_MEMORY_FAILURE);
18431829
if (!ret) {
1844-
page_handle_poison(page, true);
1830+
bool release = !huge;
1831+
1832+
if (!page_handle_poison(page, huge, release))
1833+
ret = -EBUSY;
18451834
} else {
18461835
if (!list_empty(&pagelist))
18471836
putback_movable_pages(&pagelist);
18481837

1849-
pr_info("soft offline: %#lx: migration failed %d, type %lx (%pGp)\n",
1850-
pfn, ret, page->flags, &page->flags);
1838+
pr_info("soft offline: %#lx: %s migration failed %d, type %lx (%pGp)\n",
1839+
pfn, msg_page[huge], ret, page->flags, &page->flags);
18511840
if (ret > 0)
18521841
ret = -EIO;
18531842
}
18541843
} else {
1855-
pr_info("soft offline: %#lx: isolation failed: %d, page count %d, type %lx (%pGp)\n",
1856-
pfn, ret, page_count(page), page->flags, &page->flags);
1844+
pr_info("soft offline: %#lx: %s isolation failed: %d, page count %d, type %lx (%pGp)\n",
1845+
pfn, msg_page[huge], ret, page_count(page), page->flags, &page->flags);
1846+
ret = -EBUSY;
18571847
}
18581848
return ret;
18591849
}
18601850

1861-
static int soft_offline_in_use_page(struct page *page, int flags)
1851+
static int soft_offline_in_use_page(struct page *page)
18621852
{
1863-
int ret;
18641853
struct page *hpage = compound_head(page);
18651854

18661855
if (!PageHuge(page) && PageTransHuge(hpage))
18671856
if (try_to_split_thp_page(page, "soft offline") < 0)
18681857
return -EBUSY;
1869-
1870-
if (PageHuge(page))
1871-
ret = soft_offline_huge_page(page, flags);
1872-
else
1873-
ret = __soft_offline_page(page, flags);
1874-
return ret;
1858+
return __soft_offline_page(page);
18751859
}
18761860

18771861
static int soft_offline_free_page(struct page *page)
18781862
{
1879-
int rc = -EBUSY;
1863+
int rc = 0;
18801864

1881-
if (!dissolve_free_huge_page(page) && take_page_off_buddy(page)) {
1882-
page_handle_poison(page, false);
1883-
rc = 0;
1884-
}
1865+
if (!page_handle_poison(page, true, false))
1866+
rc = -EBUSY;
18851867

18861868
return rc;
18871869
}
@@ -1932,7 +1914,7 @@ int soft_offline_page(unsigned long pfn, int flags)
19321914
put_online_mems();
19331915

19341916
if (ret > 0)
1935-
ret = soft_offline_in_use_page(page, flags);
1917+
ret = soft_offline_in_use_page(page);
19361918
else if (ret == 0)
19371919
ret = soft_offline_free_page(page);
19381920

0 commit comments

Comments
 (0)