.. | .. |
---|
1334 | 1334 | |
---|
1335 | 1335 | if (PageHuge(page)) { |
---|
1336 | 1336 | pfn = page_to_pfn(head) + compound_nr(head) - 1; |
---|
1337 | | - isolate_huge_page(head, &source); |
---|
| 1337 | + isolate_hugetlb(head, &source); |
---|
1338 | 1338 | continue; |
---|
1339 | 1339 | } else if (PageTransHuge(page)) |
---|
1340 | 1340 | pfn = page_to_pfn(head) + thp_nr_pages(page) - 1; |
---|
.. | .. |
---|
1865 | 1865 | } |
---|
1866 | 1866 | EXPORT_SYMBOL_GPL(remove_memory_subsection); |
---|
1867 | 1867 | |
---|
| 1868 | +static int try_offline_memory_block(struct memory_block *mem, void *arg) |
---|
| 1869 | +{ |
---|
| 1870 | + uint8_t online_type = MMOP_ONLINE_KERNEL; |
---|
| 1871 | + uint8_t **online_types = arg; |
---|
| 1872 | + struct page *page; |
---|
| 1873 | + int rc; |
---|
| 1874 | + |
---|
| 1875 | + /* |
---|
| 1876 | + * Sense the online_type via the zone of the memory block. Offlining |
---|
| 1877 | + * with multiple zones within one memory block will be rejected |
---|
| 1878 | + * by offlining code ... so we don't care about that. |
---|
| 1879 | + */ |
---|
| 1880 | + page = pfn_to_online_page(section_nr_to_pfn(mem->start_section_nr)); |
---|
| 1881 | + if (page && zone_idx(page_zone(page)) == ZONE_MOVABLE) |
---|
| 1882 | + online_type = MMOP_ONLINE_MOVABLE; |
---|
| 1883 | + |
---|
| 1884 | + rc = device_offline(&mem->dev); |
---|
| 1885 | + /* |
---|
| 1886 | + * Default is MMOP_OFFLINE - change it only if offlining succeeded, |
---|
| 1887 | + * so try_reonline_memory_block() can do the right thing. |
---|
| 1888 | + */ |
---|
| 1889 | + if (!rc) |
---|
| 1890 | + **online_types = online_type; |
---|
| 1891 | + |
---|
| 1892 | + (*online_types)++; |
---|
| 1893 | + /* Ignore if already offline. */ |
---|
| 1894 | + return rc < 0 ? rc : 0; |
---|
| 1895 | +} |
---|
| 1896 | + |
---|
| 1897 | +static int try_reonline_memory_block(struct memory_block *mem, void *arg) |
---|
| 1898 | +{ |
---|
| 1899 | + uint8_t **online_types = arg; |
---|
| 1900 | + int rc; |
---|
| 1901 | + |
---|
| 1902 | + if (**online_types != MMOP_OFFLINE) { |
---|
| 1903 | + mem->online_type = **online_types; |
---|
| 1904 | + rc = device_online(&mem->dev); |
---|
| 1905 | + if (rc < 0) |
---|
| 1906 | + pr_warn("%s: Failed to re-online memory: %d", |
---|
| 1907 | + __func__, rc); |
---|
| 1908 | + } |
---|
| 1909 | + |
---|
| 1910 | + /* Continue processing all remaining memory blocks. */ |
---|
| 1911 | + (*online_types)++; |
---|
| 1912 | + return 0; |
---|
| 1913 | +} |
---|
| 1914 | + |
---|
1868 | 1915 | /* |
---|
1869 | | - * Try to offline and remove a memory block. Might take a long time to |
---|
1870 | | - * finish in case memory is still in use. Primarily useful for memory devices |
---|
1871 | | - * that logically unplugged all memory (so it's no longer in use) and want to |
---|
1872 | | - * offline + remove the memory block. |
---|
| 1916 | + * Try to offline and remove memory. Might take a long time to finish in case |
---|
| 1917 | + * memory is still in use. Primarily useful for memory devices that logically |
---|
| 1918 | + * unplugged all memory (so it's no longer in use) and want to offline + remove |
---|
| 1919 | + * that memory. |
---|
1873 | 1920 | */ |
---|
1874 | 1921 | int offline_and_remove_memory(int nid, u64 start, u64 size) |
---|
1875 | 1922 | { |
---|
1876 | | - struct memory_block *mem; |
---|
1877 | | - int rc = -EINVAL; |
---|
| 1923 | + const unsigned long mb_count = size / memory_block_size_bytes(); |
---|
| 1924 | + uint8_t *online_types, *tmp; |
---|
| 1925 | + int rc; |
---|
1878 | 1926 | |
---|
1879 | 1927 | if (!IS_ALIGNED(start, memory_block_size_bytes()) || |
---|
1880 | | - size != memory_block_size_bytes()) |
---|
1881 | | - return rc; |
---|
1882 | | - |
---|
1883 | | - lock_device_hotplug(); |
---|
1884 | | - mem = find_memory_block(__pfn_to_section(PFN_DOWN(start))); |
---|
1885 | | - if (mem) |
---|
1886 | | - rc = device_offline(&mem->dev); |
---|
1887 | | - /* Ignore if the device is already offline. */ |
---|
1888 | | - if (rc > 0) |
---|
1889 | | - rc = 0; |
---|
| 1928 | + !IS_ALIGNED(size, memory_block_size_bytes()) || !size) |
---|
| 1929 | + return -EINVAL; |
---|
1890 | 1930 | |
---|
1891 | 1931 | /* |
---|
1892 | | - * In case we succeeded to offline the memory block, remove it. |
---|
| 1932 | + * We'll remember the old online type of each memory block, so we can |
---|
| 1933 | + * try to revert whatever we did when offlining one memory block fails |
---|
| 1934 | + * after offlining some others succeeded. |
---|
| 1935 | + */ |
---|
| 1936 | + online_types = kmalloc_array(mb_count, sizeof(*online_types), |
---|
| 1937 | + GFP_KERNEL); |
---|
| 1938 | + if (!online_types) |
---|
| 1939 | + return -ENOMEM; |
---|
| 1940 | + /* |
---|
| 1941 | + * Initialize all states to MMOP_OFFLINE, so when we abort processing in |
---|
| 1942 | + * try_offline_memory_block(), we'll skip all unprocessed blocks in |
---|
| 1943 | + * try_reonline_memory_block(). |
---|
| 1944 | + */ |
---|
| 1945 | + memset(online_types, MMOP_OFFLINE, mb_count); |
---|
| 1946 | + |
---|
| 1947 | + lock_device_hotplug(); |
---|
| 1948 | + |
---|
| 1949 | + tmp = online_types; |
---|
| 1950 | + rc = walk_memory_blocks(start, size, &tmp, try_offline_memory_block); |
---|
| 1951 | + |
---|
| 1952 | + /* |
---|
| 1953 | + * In case we succeeded to offline all memory, remove it. |
---|
1893 | 1954 | * This cannot fail as it cannot get onlined in the meantime. |
---|
1894 | 1955 | */ |
---|
1895 | 1956 | if (!rc) { |
---|
1896 | 1957 | rc = try_remove_memory(nid, start, size); |
---|
1897 | | - WARN_ON_ONCE(rc); |
---|
| 1958 | + if (rc) |
---|
| 1959 | + pr_err("%s: Failed to remove memory: %d", __func__, rc); |
---|
| 1960 | + } |
---|
| 1961 | + |
---|
| 1962 | + /* |
---|
| 1963 | + * Rollback what we did. While memory onlining might theoretically fail |
---|
| 1964 | + * (nacked by a notifier), it barely ever happens. |
---|
| 1965 | + */ |
---|
| 1966 | + if (rc) { |
---|
| 1967 | + tmp = online_types; |
---|
| 1968 | + walk_memory_blocks(start, size, &tmp, |
---|
| 1969 | + try_reonline_memory_block); |
---|
1898 | 1970 | } |
---|
1899 | 1971 | unlock_device_hotplug(); |
---|
1900 | 1972 | |
---|
| 1973 | + kfree(online_types); |
---|
1901 | 1974 | return rc; |
---|
1902 | 1975 | } |
---|
1903 | 1976 | EXPORT_SYMBOL_GPL(offline_and_remove_memory); |
---|