hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/mm/memory_hotplug.c
....@@ -1334,7 +1334,7 @@
13341334
13351335 if (PageHuge(page)) {
13361336 pfn = page_to_pfn(head) + compound_nr(head) - 1;
1337
- isolate_huge_page(head, &source);
1337
+ isolate_hugetlb(head, &source);
13381338 continue;
13391339 } else if (PageTransHuge(page))
13401340 pfn = page_to_pfn(head) + thp_nr_pages(page) - 1;
....@@ -1865,39 +1865,112 @@
18651865 }
18661866 EXPORT_SYMBOL_GPL(remove_memory_subsection);
18671867
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
+
18681915 /*
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.
18731920 */
18741921 int offline_and_remove_memory(int nid, u64 start, u64 size)
18751922 {
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;
18781926
18791927 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;
18901930
18911931 /*
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.
18931954 * This cannot fail as it cannot get onlined in the meantime.
18941955 */
18951956 if (!rc) {
18961957 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);
18981970 }
18991971 unlock_device_hotplug();
19001972
1973
+ kfree(online_types);
19011974 return rc;
19021975 }
19031976 EXPORT_SYMBOL_GPL(offline_and_remove_memory);