| From cce90439b47d4f4c7fcce395bcf38d3cdcbbf121 Mon Sep 17 00:00:00 2001 |
| From: Mike Kravetz <mike.kravetz@oracle.com> |
| Date: Mon, 13 May 2019 17:19:38 -0700 |
| Subject: [PATCH] hugetlbfs: on restore reserve error path retain subpool |
| reservation |
| |
| commit 0919e1b69ab459e06df45d3ba6658d281962db80 upstream. |
| |
| When a huge page is allocated, PagePrivate() is set if the allocation |
| consumed a reservation. When freeing a huge page, PagePrivate is checked. |
| If set, it indicates the reservation should be restored. PagePrivate |
| being set at free huge page time mostly happens on error paths. |
| |
| When huge page reservations are created, a check is made to determine if |
| the mapping is associated with an explicitly mounted filesystem. If so, |
| pages are also reserved within the filesystem. The default action when |
| freeing a huge page is to decrement the usage count in any associated |
| explicitly mounted filesystem. However, if the reservation is to be |
| restored the reservation/use count within the filesystem should not be |
| decrementd. Otherwise, a subsequent page allocation and free for the same |
| mapping location will cause the file filesystem usage to go 'negative'. |
| |
| Filesystem Size Used Avail Use% Mounted on |
| nodev 4.0G -4.0M 4.1G - /opt/hugepool |
| |
| To fix, when freeing a huge page do not adjust filesystem usage if |
| PagePrivate() is set to indicate the reservation should be restored. |
| |
| I did not cc stable as the problem has been around since reserves were |
| added to hugetlbfs and nobody has noticed. |
| |
| Link: http://lkml.kernel.org/r/20190328234704.27083-2-mike.kravetz@oracle.com |
| Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com> |
| Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> |
| Cc: Davidlohr Bueso <dave@stgolabs.net> |
| Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> |
| Cc: Michal Hocko <mhocko@kernel.org> |
| Cc: "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/mm/hugetlb.c b/mm/hugetlb.c |
| index 226f650e9a69..ce84bcde469e 100644 |
| --- a/mm/hugetlb.c |
| +++ b/mm/hugetlb.c |
| @@ -1256,12 +1256,23 @@ void free_huge_page(struct page *page) |
| ClearPagePrivate(page); |
| |
| /* |
| - * A return code of zero implies that the subpool will be under its |
| - * minimum size if the reservation is not restored after page is free. |
| - * Therefore, force restore_reserve operation. |
| + * If PagePrivate() was set on page, page allocation consumed a |
| + * reservation. If the page was associated with a subpool, there |
| + * would have been a page reserved in the subpool before allocation |
| + * via hugepage_subpool_get_pages(). Since we are 'restoring' the |
| + * reservtion, do not call hugepage_subpool_put_pages() as this will |
| + * remove the reserved page from the subpool. |
| */ |
| - if (hugepage_subpool_put_pages(spool, 1) == 0) |
| - restore_reserve = true; |
| + if (!restore_reserve) { |
| + /* |
| + * A return code of zero implies that the subpool will be |
| + * under its minimum size if the reservation is not restored |
| + * after page is free. Therefore, force restore_reserve |
| + * operation. |
| + */ |
| + if (hugepage_subpool_put_pages(spool, 1) == 0) |
| + restore_reserve = true; |
| + } |
| |
| spin_lock(&hugetlb_lock); |
| clear_page_huge_active(page); |
| -- |
| 2.7.4 |
| |