.. | .. |
---|
1690 | 1690 | |
---|
1691 | 1691 | /* |
---|
1692 | 1692 | * Safely get reference count of an arbitrary page. |
---|
1693 | | - * Returns 0 for a free page, -EIO for a zero refcount page |
---|
1694 | | - * that is not free, and 1 for any other page type. |
---|
1695 | | - * For 1 the page is returned with increased page count, otherwise not. |
---|
| 1693 | + * Returns 0 for a free page, 1 for an in-use page, -EIO for a page-type we |
---|
| 1694 | + * cannot handle and -EBUSY if we raced with an allocation. |
---|
| 1695 | + * We only incremented refcount in case the page was already in-use and it is |
---|
| 1696 | + * a known type we can handle. |
---|
1696 | 1697 | */ |
---|
1697 | | -static int __get_any_page(struct page *p, unsigned long pfn, int flags) |
---|
| 1698 | +static int get_any_page(struct page *p, int flags) |
---|
1698 | 1699 | { |
---|
1699 | | - int ret; |
---|
| 1700 | + int ret = 0, pass = 0; |
---|
| 1701 | + bool count_increased = false; |
---|
1700 | 1702 | |
---|
1701 | 1703 | if (flags & MF_COUNT_INCREASED) |
---|
1702 | | - return 1; |
---|
| 1704 | + count_increased = true; |
---|
1703 | 1705 | |
---|
1704 | | - /* |
---|
1705 | | - * When the target page is a free hugepage, just remove it |
---|
1706 | | - * from free hugepage list. |
---|
1707 | | - */ |
---|
1708 | | - if (!get_hwpoison_page(p)) { |
---|
1709 | | - if (PageHuge(p)) { |
---|
1710 | | - pr_info("%s: %#lx free huge page\n", __func__, pfn); |
---|
1711 | | - ret = 0; |
---|
1712 | | - } else if (is_free_buddy_page(p)) { |
---|
1713 | | - pr_info("%s: %#lx free buddy page\n", __func__, pfn); |
---|
1714 | | - ret = 0; |
---|
1715 | | - } else if (page_count(p)) { |
---|
1716 | | - /* raced with allocation */ |
---|
| 1706 | +try_again: |
---|
| 1707 | + if (!count_increased && !get_hwpoison_page(p)) { |
---|
| 1708 | + if (page_count(p)) { |
---|
| 1709 | + /* We raced with an allocation, retry. */ |
---|
| 1710 | + if (pass++ < 3) |
---|
| 1711 | + goto try_again; |
---|
1717 | 1712 | ret = -EBUSY; |
---|
1718 | | - } else { |
---|
1719 | | - pr_info("%s: %#lx: unknown zero refcount page type %lx\n", |
---|
1720 | | - __func__, pfn, p->flags); |
---|
| 1713 | + } else if (!PageHuge(p) && !is_free_buddy_page(p)) { |
---|
| 1714 | + /* We raced with put_page, retry. */ |
---|
| 1715 | + if (pass++ < 3) |
---|
| 1716 | + goto try_again; |
---|
1721 | 1717 | ret = -EIO; |
---|
1722 | 1718 | } |
---|
1723 | 1719 | } else { |
---|
1724 | | - /* Not a free page */ |
---|
1725 | | - ret = 1; |
---|
1726 | | - } |
---|
1727 | | - return ret; |
---|
1728 | | -} |
---|
1729 | | - |
---|
1730 | | -static int get_any_page(struct page *page, unsigned long pfn, int flags) |
---|
1731 | | -{ |
---|
1732 | | - int ret = __get_any_page(page, pfn, flags); |
---|
1733 | | - |
---|
1734 | | - if (ret == -EBUSY) |
---|
1735 | | - ret = __get_any_page(page, pfn, flags); |
---|
1736 | | - |
---|
1737 | | - if (ret == 1 && !PageHuge(page) && |
---|
1738 | | - !PageLRU(page) && !__PageMovable(page)) { |
---|
1739 | | - /* |
---|
1740 | | - * Try to free it. |
---|
1741 | | - */ |
---|
1742 | | - put_page(page); |
---|
1743 | | - shake_page(page, 1); |
---|
1744 | | - |
---|
1745 | | - /* |
---|
1746 | | - * Did it turn free? |
---|
1747 | | - */ |
---|
1748 | | - ret = __get_any_page(page, pfn, 0); |
---|
1749 | | - if (ret == 1 && !PageLRU(page)) { |
---|
1750 | | - /* Drop page reference which is from __get_any_page() */ |
---|
1751 | | - put_page(page); |
---|
1752 | | - pr_info("soft_offline: %#lx: unknown non LRU page type %lx (%pGp)\n", |
---|
1753 | | - pfn, page->flags, &page->flags); |
---|
1754 | | - return -EIO; |
---|
| 1720 | + if (PageHuge(p) || PageLRU(p) || __PageMovable(p)) { |
---|
| 1721 | + ret = 1; |
---|
| 1722 | + } else { |
---|
| 1723 | + /* |
---|
| 1724 | + * A page we cannot handle. Check whether we can turn |
---|
| 1725 | + * it into something we can handle. |
---|
| 1726 | + */ |
---|
| 1727 | + if (pass++ < 3) { |
---|
| 1728 | + put_page(p); |
---|
| 1729 | + shake_page(p, 1); |
---|
| 1730 | + count_increased = false; |
---|
| 1731 | + goto try_again; |
---|
| 1732 | + } |
---|
| 1733 | + put_page(p); |
---|
| 1734 | + ret = -EIO; |
---|
1755 | 1735 | } |
---|
1756 | 1736 | } |
---|
| 1737 | + |
---|
1757 | 1738 | return ret; |
---|
1758 | 1739 | } |
---|
1759 | 1740 | |
---|
.. | .. |
---|
1763 | 1744 | bool lru = PageLRU(page); |
---|
1764 | 1745 | |
---|
1765 | 1746 | if (PageHuge(page)) { |
---|
1766 | | - isolated = isolate_huge_page(page, pagelist); |
---|
| 1747 | + isolated = !isolate_hugetlb(page, pagelist); |
---|
1767 | 1748 | } else { |
---|
1768 | 1749 | if (lru) |
---|
1769 | 1750 | isolated = !isolate_lru_page(page); |
---|
.. | .. |
---|
1876 | 1857 | return __soft_offline_page(page); |
---|
1877 | 1858 | } |
---|
1878 | 1859 | |
---|
1879 | | -static int soft_offline_free_page(struct page *page) |
---|
| 1860 | +static void put_ref_page(struct page *page) |
---|
1880 | 1861 | { |
---|
1881 | | - int rc = 0; |
---|
1882 | | - |
---|
1883 | | - if (!page_handle_poison(page, true, false)) |
---|
1884 | | - rc = -EBUSY; |
---|
1885 | | - |
---|
1886 | | - return rc; |
---|
| 1862 | + if (page) |
---|
| 1863 | + put_page(page); |
---|
1887 | 1864 | } |
---|
1888 | 1865 | |
---|
1889 | 1866 | /** |
---|
.. | .. |
---|
1911 | 1888 | int soft_offline_page(unsigned long pfn, int flags) |
---|
1912 | 1889 | { |
---|
1913 | 1890 | int ret; |
---|
1914 | | - struct page *page; |
---|
1915 | 1891 | bool try_again = true; |
---|
| 1892 | + struct page *page, *ref_page = NULL; |
---|
| 1893 | + |
---|
| 1894 | + WARN_ON_ONCE(!pfn_valid(pfn) && (flags & MF_COUNT_INCREASED)); |
---|
1916 | 1895 | |
---|
1917 | 1896 | if (!pfn_valid(pfn)) |
---|
1918 | 1897 | return -ENXIO; |
---|
| 1898 | + if (flags & MF_COUNT_INCREASED) |
---|
| 1899 | + ref_page = pfn_to_page(pfn); |
---|
| 1900 | + |
---|
1919 | 1901 | /* Only online pages can be soft-offlined (esp., not ZONE_DEVICE). */ |
---|
1920 | 1902 | page = pfn_to_online_page(pfn); |
---|
1921 | | - if (!page) |
---|
| 1903 | + if (!page) { |
---|
| 1904 | + put_ref_page(ref_page); |
---|
1922 | 1905 | return -EIO; |
---|
| 1906 | + } |
---|
1923 | 1907 | |
---|
1924 | 1908 | if (PageHWPoison(page)) { |
---|
1925 | | - pr_info("soft offline: %#lx page already poisoned\n", pfn); |
---|
1926 | | - if (flags & MF_COUNT_INCREASED) |
---|
1927 | | - put_page(page); |
---|
| 1909 | + pr_info("%s: %#lx page already poisoned\n", __func__, pfn); |
---|
| 1910 | + put_ref_page(ref_page); |
---|
1928 | 1911 | return 0; |
---|
1929 | 1912 | } |
---|
1930 | 1913 | |
---|
1931 | 1914 | retry: |
---|
1932 | 1915 | get_online_mems(); |
---|
1933 | | - ret = get_any_page(page, pfn, flags); |
---|
| 1916 | + ret = get_any_page(page, flags); |
---|
1934 | 1917 | put_online_mems(); |
---|
1935 | 1918 | |
---|
1936 | | - if (ret > 0) |
---|
| 1919 | + if (ret > 0) { |
---|
1937 | 1920 | ret = soft_offline_in_use_page(page); |
---|
1938 | | - else if (ret == 0) |
---|
1939 | | - if (soft_offline_free_page(page) && try_again) { |
---|
1940 | | - try_again = false; |
---|
1941 | | - flags &= ~MF_COUNT_INCREASED; |
---|
1942 | | - goto retry; |
---|
| 1921 | + } else if (ret == 0) { |
---|
| 1922 | + if (!page_handle_poison(page, true, false)) { |
---|
| 1923 | + if (try_again) { |
---|
| 1924 | + try_again = false; |
---|
| 1925 | + flags &= ~MF_COUNT_INCREASED; |
---|
| 1926 | + goto retry; |
---|
| 1927 | + } |
---|
| 1928 | + ret = -EBUSY; |
---|
1943 | 1929 | } |
---|
| 1930 | + } else if (ret == -EIO) { |
---|
| 1931 | + pr_info("%s: %#lx: unknown page type: %lx (%pGp)\n", |
---|
| 1932 | + __func__, pfn, page->flags, &page->flags); |
---|
| 1933 | + } |
---|
1944 | 1934 | |
---|
1945 | 1935 | return ret; |
---|
1946 | 1936 | } |
---|