Skip to contentSkip to conversation

FAILED: patch "[PATCH] mm/hugetlb: expand restore_reserve_on_error functionality" failed to apply to 4.14-stable tree

PATCH [email protected] 1 participant 1 message Jun 21, 2021

Subsystems (3)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 4.14-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From 846be08578edb81f02bc8534577e6c367ef34f41 Mon Sep 17 00:00:00 2001
From: Mike Kravetz <[email protected]>
Date: Tue, 15 Jun 2021 18:23:29 -0700
Subject: [PATCH] mm/hugetlb: expand restore_reserve_on_error functionality

The routine restore_reserve_on_error is called to restore reservation information when an error occurs after page allocation. The routine alloc_huge_page modifies the mapping reserve map and potentially the reserve count during allocation. If code calling alloc_huge_page encounters an error after allocation and needs to free the page, the reservation information needs to be adjusted.

Currently, restore_reserve_on_error only takes action on pages for which the reserve count was adjusted(HPageRestoreReserve flag). There is nothing wrong with these adjustments. However, alloc_huge_page ALWAYS modifies the reserve map during allocation even if the reserve count is not adjusted. This can cause issues as observed during development of this patch [1].

One specific series of operations causing an issue is:

- Create a shared hugetlb mapping
Reservations for all pages created by default

- Fault in a page in the mapping
Reservation exists so reservation count is decremented

- Punch a hole in the file/mapping at index previously faulted Reservation and any associated pages will be removed

- Allocate a page to fill the hole
No reservation entry, so reserve count unmodified Reservation entry added to map by alloc_huge_page

- Error after allocation and before instantiating the page Reservation entry remains in map

- Allocate a page to fill the hole
Reservation entry exists, so decrement reservation count

This will cause a reservation count underflow as the reservation count was decremented twice for the same index.

A user would observe a very large number for HugePages_Rsvd in /proc/meminfo. This would also likely cause subsequent allocations of hugetlb pages to fail as it would 'appear' that all pages are reserved.

This sequence of operations is unlikely to happen, however they were easily reproduced and observed using hacked up code as described in [1].

Address the issue by having the routine restore_reserve_on_error take action on pages where HPageRestoreReserve is not set. In this case, we need to remove any reserve map entry created by alloc_huge_page. A new helper routine vma_del_reservation assists with this operation.

There are three callers of alloc_huge_page which do not currently call restore_reserve_on error before freeing a page on error paths. Add those missing calls.

[1] https://lore.kernel.org/linux-mm/[email protected]/

Fixes 96b96a96ddee ("mm/hugetlb: fix huge page reservation leak in private mapping error paths"
Cc Axel Rasmussen <[email protected]>
Cc Peter Xu <[email protected]>
Cc Muchun Song <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Naoya Horiguchi <[email protected]>
fs/hugetlbfs/inode.c +1 -0
index 55efd3dd04f6..30dee68458c7 100644
@@ -735,6 +735,7 @@ static long hugetlbfs_fallocate(struct file *file, int mode, loff_t offset,
 		__SetPageUptodate(page);
 		error = huge_add_to_page_cache(page, mapping, index);
 		if (unlikely(error)) {
+			restore_reserve_on_error(h, &pseudo_vma, addr, page);
 			put_page(page);
 			mutex_unlock(&hugetlb_fault_mutex_table[hash]);
 			goto out;
include/linux/hugetlb.h +2 -0
index 790ae618548d..6504346a1947 100644
@@ -610,6 +610,8 @@ struct page *alloc_huge_page_vma(struct hstate *h, struct vm_area_struct *vma,
 				unsigned long address);
 int huge_add_to_page_cache(struct page *page, struct address_space *mapping,
 			pgoff_t idx);
+void restore_reserve_on_error(struct hstate *h, struct vm_area_struct *vma,
+				unsigned long address, struct page *page);
 
 /* arch callback */
 int __init __alloc_bootmem_huge_page(struct hstate *h);
mm/hugetlb.c +97 -23

173 lines not on this page · raw message