| .. | .. |
|---|
| 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 | } |
|---|