Skip to content

Commit e5bbc8a

Browse files
mjkravetztorvalds
authored andcommitted
mm/hugetlb.c: fix reservation race when freeing surplus pages
return_unused_surplus_pages() decrements the global reservation count, and frees any unused surplus pages that were backing the reservation. Commit 7848a4b ("mm/hugetlb.c: add cond_resched_lock() in return_unused_surplus_pages()") added a call to cond_resched_lock in the loop freeing the pages. As a result, the hugetlb_lock could be dropped, and someone else could use the pages that will be freed in subsequent iterations of the loop. This could result in inconsistent global hugetlb page state, application api failures (such as mmap) failures or application crashes. When dropping the lock in return_unused_surplus_pages, make sure that the global reservation count (resv_huge_pages) remains sufficiently large to prevent someone else from claiming pages about to be freed. Analyzed by Paul Cassella. Fixes: 7848a4b ("mm/hugetlb.c: add cond_resched_lock() in return_unused_surplus_pages()") Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Mike Kravetz <[email protected]> Reported-by: Paul Cassella <[email protected]> Suggested-by: Michal Hocko <[email protected]> Cc: Masayoshi Mizuma <[email protected]> Cc: Naoya Horiguchi <[email protected]> Cc: Aneesh Kumar <[email protected]> Cc: Hillf Danton <[email protected]> Cc: <[email protected]> [3.15+] Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent c4e490c commit e5bbc8a

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

mm/hugetlb.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,23 +1773,32 @@ static int gather_surplus_pages(struct hstate *h, int delta)
17731773
}
17741774

17751775
/*
1776-
* When releasing a hugetlb pool reservation, any surplus pages that were
1777-
* allocated to satisfy the reservation must be explicitly freed if they were
1778-
* never used.
1779-
* Called with hugetlb_lock held.
1776+
* This routine has two main purposes:
1777+
* 1) Decrement the reservation count (resv_huge_pages) by the value passed
1778+
* in unused_resv_pages. This corresponds to the prior adjustments made
1779+
* to the associated reservation map.
1780+
* 2) Free any unused surplus pages that may have been allocated to satisfy
1781+
* the reservation. As many as unused_resv_pages may be freed.
1782+
*
1783+
* Called with hugetlb_lock held. However, the lock could be dropped (and
1784+
* reacquired) during calls to cond_resched_lock. Whenever dropping the lock,
1785+
* we must make sure nobody else can claim pages we are in the process of
1786+
* freeing. Do this by ensuring resv_huge_page always is greater than the
1787+
* number of huge pages we plan to free when dropping the lock.
17801788
*/
17811789
static void return_unused_surplus_pages(struct hstate *h,
17821790
unsigned long unused_resv_pages)
17831791
{
17841792
unsigned long nr_pages;
17851793

1786-
/* Uncommit the reservation */
1787-
h->resv_huge_pages -= unused_resv_pages;
1788-
17891794
/* Cannot return gigantic pages currently */
17901795
if (hstate_is_gigantic(h))
1791-
return;
1796+
goto out;
17921797

1798+
/*
1799+
* Part (or even all) of the reservation could have been backed
1800+
* by pre-allocated pages. Only free surplus pages.
1801+
*/
17931802
nr_pages = min(unused_resv_pages, h->surplus_huge_pages);
17941803

17951804
/*
@@ -1799,12 +1808,22 @@ static void return_unused_surplus_pages(struct hstate *h,
17991808
* when the nodes with surplus pages have no free pages.
18001809
* free_pool_huge_page() will balance the the freed pages across the
18011810
* on-line nodes with memory and will handle the hstate accounting.
1811+
*
1812+
* Note that we decrement resv_huge_pages as we free the pages. If
1813+
* we drop the lock, resv_huge_pages will still be sufficiently large
1814+
* to cover subsequent pages we may free.
18021815
*/
18031816
while (nr_pages--) {
1817+
h->resv_huge_pages--;
1818+
unused_resv_pages--;
18041819
if (!free_pool_huge_page(h, &node_states[N_MEMORY], 1))
1805-
break;
1820+
goto out;
18061821
cond_resched_lock(&hugetlb_lock);
18071822
}
1823+
1824+
out:
1825+
/* Fully uncommit the reservation */
1826+
h->resv_huge_pages -= unused_resv_pages;
18081827
}
18091828

18101829

0 commit comments

Comments
 (0)