hugetlb: create remove_inode_single_folio to remove single file folio

Create the new routine remove_inode_single_folio that will remove a single
folio from a file.  This is refactored code from remove_inode_hugepages. 
It checks for the uncommon case in which the folio is still mapped and
unmaps.

No functional change.  This refactoring will be put to use and expanded
upon in a subsequent patches.

Link: https://lkml.kernel.org/r/20220914221810.95771-5-mike.kravetz@oracle.com
Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com>
Reviewed-by: Miaohe Lin <linmiaohe@huawei.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: James Houghton <jthoughton@google.com>
Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mina Almasry <almasrymina@google.com>
Cc: Muchun Song <songmuchun@bytedance.com>
Cc: Naoya Horiguchi <naoya.horiguchi@linux.dev>
Cc: Pasha Tatashin <pasha.tatashin@soleen.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Prakash Sangappa <prakash.sangappa@oracle.com>
Cc: Sven Schnelle <svens@linux.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Mike Kravetz 2022-09-14 15:18:05 -07:00 committed by Andrew Morton
parent 7e1813d48d
commit c86272287b

View File

@ -411,6 +411,60 @@ hugetlb_vmdelete_list(struct rb_root_cached *root, pgoff_t start, pgoff_t end,
}
}
/*
* Called with hugetlb fault mutex held.
* Returns true if page was actually removed, false otherwise.
*/
static bool remove_inode_single_folio(struct hstate *h, struct inode *inode,
struct address_space *mapping,
struct folio *folio, pgoff_t index,
bool truncate_op)
{
bool ret = false;
/*
* If folio is mapped, it was faulted in after being
* unmapped in caller. Unmap (again) while holding
* the fault mutex. The mutex will prevent faults
* until we finish removing the folio.
*/
if (unlikely(folio_mapped(folio))) {
i_mmap_lock_write(mapping);
hugetlb_vmdelete_list(&mapping->i_mmap,
index * pages_per_huge_page(h),
(index + 1) * pages_per_huge_page(h),
ZAP_FLAG_DROP_MARKER);
i_mmap_unlock_write(mapping);
}
folio_lock(folio);
/*
* After locking page, make sure mapping is the same.
* We could have raced with page fault populate and
* backout code.
*/
if (folio_mapping(folio) == mapping) {
/*
* We must remove the folio from page cache before removing
* the region/ reserve map (hugetlb_unreserve_pages). In
* rare out of memory conditions, removal of the region/reserve
* map could fail. Correspondingly, the subpool and global
* reserve usage count can need to be adjusted.
*/
VM_BUG_ON(HPageRestoreReserve(&folio->page));
hugetlb_delete_from_page_cache(&folio->page);
ret = true;
if (!truncate_op) {
if (unlikely(hugetlb_unreserve_pages(inode, index,
index + 1, 1)))
hugetlb_fix_reserve_counts(inode);
}
}
folio_unlock(folio);
return ret;
}
/*
* remove_inode_hugepages handles two distinct cases: truncation and hole
* punch. There are subtle differences in operation for each case.
@ -418,11 +472,10 @@ hugetlb_vmdelete_list(struct rb_root_cached *root, pgoff_t start, pgoff_t end,
* truncation is indicated by end of range being LLONG_MAX
* In this case, we first scan the range and release found pages.
* After releasing pages, hugetlb_unreserve_pages cleans up region/reserve
* maps and global counts. Page faults can not race with truncation
* in this routine. hugetlb_no_page() prevents page faults in the
* truncated range. It checks i_size before allocation, and again after
* with the page table lock for the page held. The same lock must be
* acquired to unmap a page.
* maps and global counts. Page faults can race with truncation.
* During faults, hugetlb_no_page() checks i_size before page allocation,
* and again after obtaining page table lock. It will 'back out'
* allocations in the truncated range.
* hole punch is indicated if end is not LLONG_MAX
* In the hole punch case we scan the range and release found pages.
* Only when releasing a page is the associated region/reserve map
@ -456,44 +509,12 @@ static void remove_inode_hugepages(struct inode *inode, loff_t lstart,
mutex_lock(&hugetlb_fault_mutex_table[hash]);
/*
* If folio is mapped, it was faulted in after being
* unmapped in caller. Unmap (again) now after taking
* the fault mutex. The mutex will prevent faults
* until we finish removing the folio.
*
* This race can only happen in the hole punch case.
* Getting here in a truncate operation is a bug.
* Remove folio that was part of folio_batch.
*/
if (unlikely(folio_mapped(folio))) {
BUG_ON(truncate_op);
if (remove_inode_single_folio(h, inode, mapping, folio,
index, truncate_op))
freed++;
i_mmap_lock_write(mapping);
hugetlb_vmdelete_list(&mapping->i_mmap,
index * pages_per_huge_page(h),
(index + 1) * pages_per_huge_page(h),
ZAP_FLAG_DROP_MARKER);
i_mmap_unlock_write(mapping);
}
folio_lock(folio);
/*
* We must free the huge page and remove from page
* cache BEFORE removing the region/reserve map
* (hugetlb_unreserve_pages). In rare out of memory
* conditions, removal of the region/reserve map could
* fail. Correspondingly, the subpool and global
* reserve usage count can need to be adjusted.
*/
VM_BUG_ON(HPageRestoreReserve(&folio->page));
hugetlb_delete_from_page_cache(&folio->page);
freed++;
if (!truncate_op) {
if (unlikely(hugetlb_unreserve_pages(inode,
index, index + 1, 1)))
hugetlb_fix_reserve_counts(inode);
}
folio_unlock(folio);
mutex_unlock(&hugetlb_fault_mutex_table[hash]);
}
folio_batch_release(&fbatch);