Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mm/memory_hotplug: fix possible race in scan_movable_pages()

Patch series "mm: memory hot(un)plug and SPARSEMEM cleanups", v2.

Some cleanups around memory hot(un)plug and SPARSEMEM. In essence, we can
limit CONFIG_MEMORY_HOTPLUG to CONFIG_SPARSEMEM_VMEMMAP, remove some dead
code, and move all the hotplug bits over to mm/sparse-vmemmap.c.

Some further/related cleanups around other unnecessary code (memory hole
handling and complicated usemap allocation).

I have some further sparse.c cleanups lying around, and I'm planning on
getting rid of bootmem_info.c entirely.


This patch (of 15):

If a hugetlb folio gets freed while we are in scan_movable_pages(),
folio_nr_pages() could return 0, resulting in or'ing "0 - 1 = -1" to the
PFN, resulting in PFN = -1. We're not holding any locks or references
that would prevent that.

for_each_valid_pfn() would then search for the next valid PFN, and could
return a PFN that is outside of the range of the original requested range.
do_migrate_page() would then try to migrate quite a big range, which is
certainly undesirable.

To fix it, simply test for valid folio_nr_pages() values. While at it, as
PageHuge() really just does a page_folio() internally, we can just use
folio_test_hugetlb() on the folio directly.

scan_movable_pages() is expected to be fast, and we try to avoid taking
locks or grabbing references. We cannot use folio_try_get() as that does
not work for free hugetlb folios. We could grab the hugetlb_lock, but
that just adds complexity.

The race is unlikely to trigger in practice, so we won't be CCing stable.

Link: https://lkml.kernel.org/r/20260320-sparsemem_cleanups-v2-0-096addc8800d@kernel.org
Link: https://lkml.kernel.org/r/20260320-sparsemem_cleanups-v2-1-096addc8800d@kernel.org
Fixes: 16540dae959d ("mm/hugetlb: mm/memory_hotplug: use a folio in scan_movable_pages()")
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Sidhartha Kumar <sidhartha.kumar@oracle.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Wei Xu <weixugc@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

David Hildenbrand (Arm) and committed by
Andrew Morton
89e69c7d 42561b34

+8 -3
+8 -3
mm/memory_hotplug.c
··· 1746 1746 unsigned long pfn; 1747 1747 1748 1748 for_each_valid_pfn(pfn, start, end) { 1749 + unsigned long nr_pages; 1749 1750 struct page *page; 1750 1751 struct folio *folio; 1751 1752 ··· 1763 1762 if (PageOffline(page) && page_count(page)) 1764 1763 return -EBUSY; 1765 1764 1766 - if (!PageHuge(page)) 1767 - continue; 1768 1765 folio = page_folio(page); 1766 + if (!folio_test_hugetlb(folio)) 1767 + continue; 1769 1768 /* 1770 1769 * This test is racy as we hold no reference or lock. The 1771 1770 * hugetlb page could have been free'ed and head is no longer ··· 1775 1774 */ 1776 1775 if (folio_test_hugetlb_migratable(folio)) 1777 1776 goto found; 1778 - pfn |= folio_nr_pages(folio) - 1; 1777 + nr_pages = folio_nr_pages(folio); 1778 + if (unlikely(nr_pages < 1 || nr_pages > MAX_FOLIO_NR_PAGES || 1779 + !is_power_of_2(nr_pages))) 1780 + continue; 1781 + pfn |= nr_pages - 1; 1779 1782 } 1780 1783 return -ENOENT; 1781 1784 found: