Commit 0c052824 authored by Jinjiang Tu's avatar Jinjiang Tu
Browse files

mm/hugetlb: fix surplus pages in dissolve_free_huge_page()

mainline inclusion
from mainline-v6.14-rc6
commit cb402bbdabcaa5a765068c5b8673bbfc1c264242
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IBV73U
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=cb402bbdabcaa5a765068c5b8673bbfc1c264242

-------------------------------------------

In dissolve_free_huge_page(), free huge pages are dissolved without
adjusting surplus count. However, free huge pages may be accounted as
surplus pages, and will lead to wrong surplus count.

I reproduce this issue on qemu. The steps are:
1) Node1 is memory-less at first. Hot-add memory to node1 by executing
the two commands in qemu monitor:
  object_add memory-backend-ram,id=mem1,size=1G
  device_add pc-dimm,id=dimm1,memdev=mem1,node=1
2) online one memory block of Node1 with:
  echo online_movable > /sys/devices/system/node/node1/memoryX/state
3) create 64 huge pages for node1
4) run a program to reserve (don't consume) all the huge pages
5) echo 0 > nr_huge_pages for node1. After this step, free huge pages in
Node1 are surplus.
6) create 80 huge pages for node0
7) offline memory of node1, The memory range to offline contains the free
surplus huge pages created in step3) ~ step5)
  echo offline > /sys/devices/system/node/node1/memoryX/state
8) kill the program in step 4)

The result:
           Node0     Node1
total       80        0
free        80        0
surplus     0         61

To fix it, adjust surplus when destroying huge pages if the node has
surplus pages in dissolve_free_hugetlb_folio().

The result with this patch:
           Node0     Node1
total       80        0
free        80        0
surplus     0         0

Link: https://lkml.kernel.org/r/20250304132106.2872754-1-tujinjiang@huawei.com


Fixes: c8721bbb ("mm: memory-hotplug: enable memory hotplug to handle hugepage")
Signed-off-by: default avatarJinjiang Tu <tujinjiang@huawei.com>
Acked-by: default avatarDavid Hildenbrand <david@redhat.com>
Acked-by: default avatarOscar Salvador <osalvador@suse.de>
Cc: Jinjiang Tu <tujinjiang@huawei.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nanyong Sun <sunnanyong@huawei.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>

Conflicts:
	mm/hugetlb.c
[Context conflicts, and HVO isn't introduced, so don't need to handle
HVO err case.]
Signed-off-by: default avatarJinjiang Tu <tujinjiang@huawei.com>
parent d54ba3f5
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -1823,6 +1823,7 @@ int dissolve_free_huge_page(struct page *page)
	if (!page_count(page)) {
		struct page *head = compound_head(page);
		struct hstate *h = page_hstate(head);
		bool adjust_surplus = false;
		if (h->free_huge_pages - h->resv_huge_pages == 0)
			goto out;

@@ -1853,7 +1854,9 @@ int dissolve_free_huge_page(struct page *page)
			SetPageHWPoison(page);
			ClearPageHWPoison(head);
		}
		remove_hugetlb_page(h, head, false);
		if (h->surplus_huge_pages_node[page_to_nid(head)])
			adjust_surplus = true;
		remove_hugetlb_page(h, head, adjust_surplus);
		h->max_huge_pages--;
		spin_unlock_irq(&hugetlb_lock);
		update_and_free_page(h, head);