Commit 65bd3727 authored by Liu Shixin's avatar Liu Shixin
Browse files

dhugetlb: keep refcount to 0 while page is actually free in pool

hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IBDF90



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

While we split/merge page in dynamic pool, the refcount of page may be
increased to 1 due to prep_new_page(), and we then set the refcount to 0.
That means there's a time window that the refcount of the free page is
not 0.

split_huge_pages_set() will scan all pages and try to get the refcount
If try to get refcount in the above time window and put_page() after
the refcount is set to 0, the refcount will be decreased to -1.

It's dangerous if the refcount for page is not 0 when split/merge, so try
not to get refcount there.

Fixes: 0bc0d0d5 ("dhugetlb: backport dynamic hugetlb feature")
Signed-off-by: default avatarLiu Shixin <liushixin2@huawei.com>
parent 33267d4a
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -3575,8 +3575,8 @@ static void add_new_huge_page_to_pool(struct dhugetlb_pool *hpool,
		list_add_tail(&page->lru, &hpool->dhugetlb_1G_freelists);
		hpool->free_unreserved_1G++;
	} else {
		prep_new_page(page, PMD_SHIFT - PAGE_SHIFT, __GFP_COMP, 0);
		set_page_count(page, 0);
		prep_new_frozen_free_page(page, PMD_SHIFT - PAGE_SHIFT,
					  __GFP_COMP);
		list_add_tail(&page->lru, &hpool->dhugetlb_2M_freelists);
		hpool->free_unreserved_2M++;
	}
+4 −0
Original line number Diff line number Diff line
@@ -185,6 +185,10 @@ extern void prep_compound_page(struct page *page, unsigned int order);
extern int check_new_page(struct page *page);
extern void post_alloc_hook(struct page *page, unsigned int order,
					gfp_t gfp_flags);
#ifdef CONFIG_DYNAMIC_HUGETLB
extern void prep_new_frozen_free_page(struct page *page, unsigned int order,
				      gfp_t gfp_flags);
#endif
extern int user_min_free_kbytes;

#if defined CONFIG_COMPACTION || defined CONFIG_CMA
+26 −0
Original line number Diff line number Diff line
@@ -2100,6 +2100,32 @@ void prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
		clear_page_pfmemalloc(page);
}

#ifdef CONFIG_DYNAMIC_HUGETLB
/* Similar with prep_new_page without set refcount to 1 */
void prep_new_frozen_free_page(struct page *page, unsigned int order,
			       gfp_t gfp_flags)
{
	int i;

	set_page_private(page, 0);

	arch_alloc_page(page, order);
	kernel_map_pages(page, 1 << order, 1);
	kasan_alloc_pages(page, order);
	kernel_poison_pages(page, 1 << order, 1);
	set_page_owner(page, order, gfp_flags);

	if (!free_pages_prezeroed() && (gfp_flags & __GFP_ZERO))
		for (i = 0; i < (1 << order); i++)
			clear_highpage(page + i);

	if (order && (gfp_flags & __GFP_COMP))
		prep_compound_page(page, order);

	clear_page_pfmemalloc(page);
}
#endif

/*
 * Go through the free lists for the given migratetype and remove
 * the smallest available page from the freelists