mm: convert free_huge_page() to free_huge_folio()

Pass a folio instead of the head page to save a few instructions.  Update
the documentation, at least in English.

Link: https://lkml.kernel.org/r/20230816151201.3655946-4-willy@infradead.org
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Sidhartha Kumar <sidhartha.kumar@oracle.com>
Cc: Yanteng Si <siyanteng@loongson.cn>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Matthew Wilcox (Oracle) 2023-08-16 16:11:51 +01:00 committed by Andrew Morton
parent dd6fa0b618
commit 454a00c40a
5 changed files with 34 additions and 36 deletions

View File

@ -271,12 +271,12 @@ to the global reservation count (resv_huge_pages).
Freeing Huge Pages Freeing Huge Pages
================== ==================
Huge page freeing is performed by the routine free_huge_page(). This routine Huge pages are freed by free_huge_folio(). It is only passed a pointer
is the destructor for hugetlbfs compound pages. As a result, it is only to the folio as it is called from the generic MM code. When a huge page
passed a pointer to the page struct. When a huge page is freed, reservation is freed, reservation accounting may need to be performed. This would
accounting may need to be performed. This would be the case if the page was be the case if the page was associated with a subpool that contained
associated with a subpool that contained reserves, or the page is being freed reserves, or the page is being freed on an error path where a global
on an error path where a global reserve count must be restored. reserve count must be restored.
The page->private field points to any subpool associated with the page. The page->private field points to any subpool associated with the page.
If the PagePrivate flag is set, it indicates the global reserve count should If the PagePrivate flag is set, it indicates the global reserve count should
@ -525,7 +525,7 @@ However, there are several instances where errors are encountered after a huge
page is allocated but before it is instantiated. In this case, the page page is allocated but before it is instantiated. In this case, the page
allocation has consumed the reservation and made the appropriate subpool, allocation has consumed the reservation and made the appropriate subpool,
reservation map and global count adjustments. If the page is freed at this reservation map and global count adjustments. If the page is freed at this
time (before instantiation and clearing of PagePrivate), then free_huge_page time (before instantiation and clearing of PagePrivate), then free_huge_folio
will increment the global reservation count. However, the reservation map will increment the global reservation count. However, the reservation map
indicates the reservation was consumed. This resulting inconsistent state indicates the reservation was consumed. This resulting inconsistent state
will cause the 'leak' of a reserved huge page. The global reserve count will will cause the 'leak' of a reserved huge page. The global reserve count will

View File

@ -219,7 +219,7 @@ vma_commit_reservation()之间预留映射有可能被改变。如果hugetlb_
释放巨页 释放巨页
======== ========
巨页释放是由函数free_huge_page()执行的。这个函数是hugetlbfs复合页的析构器。因此它只传 巨页释放是由函数free_huge_folio()执行的。这个函数是hugetlbfs复合页的析构器。因此它只传
递一个指向页面结构体的指针。当一个巨页被释放时,可能需要进行预留计算。如果该页与包含保 递一个指向页面结构体的指针。当一个巨页被释放时,可能需要进行预留计算。如果该页与包含保
留的子池相关联,或者该页在错误路径上被释放,必须恢复全局预留计数,就会出现这种情况。 留的子池相关联,或者该页在错误路径上被释放,必须恢复全局预留计数,就会出现这种情况。
@ -387,7 +387,7 @@ region_count()在解除私有巨页映射时被调用。在私有映射中,预
然而,有几种情况是,在一个巨页被分配后,但在它被实例化之前,就遇到了错误。在这种情况下, 然而,有几种情况是,在一个巨页被分配后,但在它被实例化之前,就遇到了错误。在这种情况下,
页面分配已经消耗了预留,并进行了适当的子池、预留映射和全局计数调整。如果页面在这个时候被释放 页面分配已经消耗了预留,并进行了适当的子池、预留映射和全局计数调整。如果页面在这个时候被释放
在实例化和清除PagePrivate之前那么free_huge_page将增加全局预留计数。然而,预留映射 在实例化和清除PagePrivate之前那么free_huge_folio将增加全局预留计数。然而,预留映射
显示报留被消耗了。这种不一致的状态将导致预留的巨页的 “泄漏” 。全局预留计数将比它原本的要高, 显示报留被消耗了。这种不一致的状态将导致预留的巨页的 “泄漏” 。全局预留计数将比它原本的要高,
并阻止分配一个预先分配的页面。 并阻止分配一个预先分配的页面。

View File

@ -26,7 +26,7 @@ typedef struct { unsigned long pd; } hugepd_t;
#define __hugepd(x) ((hugepd_t) { (x) }) #define __hugepd(x) ((hugepd_t) { (x) })
#endif #endif
void free_huge_page(struct page *page); void free_huge_folio(struct folio *folio);
#ifdef CONFIG_HUGETLB_PAGE #ifdef CONFIG_HUGETLB_PAGE

View File

@ -1706,10 +1706,10 @@ static void add_hugetlb_folio(struct hstate *h, struct folio *folio,
zeroed = folio_put_testzero(folio); zeroed = folio_put_testzero(folio);
if (unlikely(!zeroed)) if (unlikely(!zeroed))
/* /*
* It is VERY unlikely soneone else has taken a ref on * It is VERY unlikely soneone else has taken a ref
* the page. In this case, we simply return as the * on the folio. In this case, we simply return as
* hugetlb destructor (free_huge_page) will be called * free_huge_folio() will be called when this other ref
* when this other ref is dropped. * is dropped.
*/ */
return; return;
@ -1875,13 +1875,12 @@ struct hstate *size_to_hstate(unsigned long size)
return NULL; return NULL;
} }
void free_huge_page(struct page *page) void free_huge_folio(struct folio *folio)
{ {
/* /*
* Can't pass hstate in here because it is called from the * Can't pass hstate in here because it is called from the
* compound page destructor. * compound page destructor.
*/ */
struct folio *folio = page_folio(page);
struct hstate *h = folio_hstate(folio); struct hstate *h = folio_hstate(folio);
int nid = folio_nid(folio); int nid = folio_nid(folio);
struct hugepage_subpool *spool = hugetlb_folio_subpool(folio); struct hugepage_subpool *spool = hugetlb_folio_subpool(folio);
@ -1936,7 +1935,7 @@ void free_huge_page(struct page *page)
spin_unlock_irqrestore(&hugetlb_lock, flags); spin_unlock_irqrestore(&hugetlb_lock, flags);
update_and_free_hugetlb_folio(h, folio, true); update_and_free_hugetlb_folio(h, folio, true);
} else { } else {
arch_clear_hugepage_flags(page); arch_clear_hugepage_flags(&folio->page);
enqueue_hugetlb_folio(h, folio); enqueue_hugetlb_folio(h, folio);
spin_unlock_irqrestore(&hugetlb_lock, flags); spin_unlock_irqrestore(&hugetlb_lock, flags);
} }
@ -2246,7 +2245,7 @@ static int alloc_pool_huge_page(struct hstate *h, nodemask_t *nodes_allowed,
folio = alloc_fresh_hugetlb_folio(h, gfp_mask, node, folio = alloc_fresh_hugetlb_folio(h, gfp_mask, node,
nodes_allowed, node_alloc_noretry); nodes_allowed, node_alloc_noretry);
if (folio) { if (folio) {
free_huge_page(&folio->page); /* free it into the hugepage allocator */ free_huge_folio(folio); /* free it into the hugepage allocator */
return 1; return 1;
} }
} }
@ -2429,13 +2428,13 @@ static struct folio *alloc_surplus_hugetlb_folio(struct hstate *h,
* We could have raced with the pool size change. * We could have raced with the pool size change.
* Double check that and simply deallocate the new page * Double check that and simply deallocate the new page
* if we would end up overcommiting the surpluses. Abuse * if we would end up overcommiting the surpluses. Abuse
* temporary page to workaround the nasty free_huge_page * temporary page to workaround the nasty free_huge_folio
* codeflow * codeflow
*/ */
if (h->surplus_huge_pages >= h->nr_overcommit_huge_pages) { if (h->surplus_huge_pages >= h->nr_overcommit_huge_pages) {
folio_set_hugetlb_temporary(folio); folio_set_hugetlb_temporary(folio);
spin_unlock_irq(&hugetlb_lock); spin_unlock_irq(&hugetlb_lock);
free_huge_page(&folio->page); free_huge_folio(folio);
return NULL; return NULL;
} }
@ -2547,8 +2546,7 @@ static int gather_surplus_pages(struct hstate *h, long delta)
__must_hold(&hugetlb_lock) __must_hold(&hugetlb_lock)
{ {
LIST_HEAD(surplus_list); LIST_HEAD(surplus_list);
struct folio *folio; struct folio *folio, *tmp;
struct page *page, *tmp;
int ret; int ret;
long i; long i;
long needed, allocated; long needed, allocated;
@ -2608,21 +2606,21 @@ static int gather_surplus_pages(struct hstate *h, long delta)
ret = 0; ret = 0;
/* Free the needed pages to the hugetlb pool */ /* Free the needed pages to the hugetlb pool */
list_for_each_entry_safe(page, tmp, &surplus_list, lru) { list_for_each_entry_safe(folio, tmp, &surplus_list, lru) {
if ((--needed) < 0) if ((--needed) < 0)
break; break;
/* Add the page to the hugetlb allocator */ /* Add the page to the hugetlb allocator */
enqueue_hugetlb_folio(h, page_folio(page)); enqueue_hugetlb_folio(h, folio);
} }
free: free:
spin_unlock_irq(&hugetlb_lock); spin_unlock_irq(&hugetlb_lock);
/* /*
* Free unnecessary surplus pages to the buddy allocator. * Free unnecessary surplus pages to the buddy allocator.
* Pages have no ref count, call free_huge_page directly. * Pages have no ref count, call free_huge_folio directly.
*/ */
list_for_each_entry_safe(page, tmp, &surplus_list, lru) list_for_each_entry_safe(folio, tmp, &surplus_list, lru)
free_huge_page(page); free_huge_folio(folio);
spin_lock_irq(&hugetlb_lock); spin_lock_irq(&hugetlb_lock);
return ret; return ret;
@ -2836,11 +2834,11 @@ static long vma_del_reservation(struct hstate *h,
* 2) No reservation was in place for the page, so hugetlb_restore_reserve is * 2) No reservation was in place for the page, so hugetlb_restore_reserve is
* not set. However, alloc_hugetlb_folio always updates the reserve map. * not set. However, alloc_hugetlb_folio always updates the reserve map.
* *
* In case 1, free_huge_page later in the error path will increment the * In case 1, free_huge_folio later in the error path will increment the
* global reserve count. But, free_huge_page does not have enough context * global reserve count. But, free_huge_folio does not have enough context
* to adjust the reservation map. This case deals primarily with private * to adjust the reservation map. This case deals primarily with private
* mappings. Adjust the reserve map here to be consistent with global * mappings. Adjust the reserve map here to be consistent with global
* reserve count adjustments to be made by free_huge_page. Make sure the * reserve count adjustments to be made by free_huge_folio. Make sure the
* reserve map indicates there is a reservation present. * reserve map indicates there is a reservation present.
* *
* In case 2, simply undo reserve map modifications done by alloc_hugetlb_folio. * In case 2, simply undo reserve map modifications done by alloc_hugetlb_folio.
@ -2856,7 +2854,7 @@ void restore_reserve_on_error(struct hstate *h, struct vm_area_struct *vma,
* Rare out of memory condition in reserve map * Rare out of memory condition in reserve map
* manipulation. Clear hugetlb_restore_reserve so * manipulation. Clear hugetlb_restore_reserve so
* that global reserve count will not be incremented * that global reserve count will not be incremented
* by free_huge_page. This will make it appear * by free_huge_folio. This will make it appear
* as though the reservation for this folio was * as though the reservation for this folio was
* consumed. This may prevent the task from * consumed. This may prevent the task from
* faulting in the folio at a later time. This * faulting in the folio at a later time. This
@ -3232,7 +3230,7 @@ static void __init gather_bootmem_prealloc(void)
if (prep_compound_gigantic_folio(folio, huge_page_order(h))) { if (prep_compound_gigantic_folio(folio, huge_page_order(h))) {
WARN_ON(folio_test_reserved(folio)); WARN_ON(folio_test_reserved(folio));
prep_new_hugetlb_folio(h, folio, folio_nid(folio)); prep_new_hugetlb_folio(h, folio, folio_nid(folio));
free_huge_page(page); /* add to the hugepage allocator */ free_huge_folio(folio); /* add to the hugepage allocator */
} else { } else {
/* VERY unlikely inflated ref count on a tail page */ /* VERY unlikely inflated ref count on a tail page */
free_gigantic_folio(folio, huge_page_order(h)); free_gigantic_folio(folio, huge_page_order(h));
@ -3264,7 +3262,7 @@ static void __init hugetlb_hstate_alloc_pages_onenode(struct hstate *h, int nid)
&node_states[N_MEMORY], NULL); &node_states[N_MEMORY], NULL);
if (!folio) if (!folio)
break; break;
free_huge_page(&folio->page); /* free it into the hugepage allocator */ free_huge_folio(folio); /* free it into the hugepage allocator */
} }
cond_resched(); cond_resched();
} }
@ -3542,7 +3540,7 @@ static int set_max_huge_pages(struct hstate *h, unsigned long count, int nid,
while (count > persistent_huge_pages(h)) { while (count > persistent_huge_pages(h)) {
/* /*
* If this allocation races such that we no longer need the * If this allocation races such that we no longer need the
* page, free_huge_page will handle it by freeing the page * page, free_huge_folio will handle it by freeing the page
* and reducing the surplus. * and reducing the surplus.
*/ */
spin_unlock_irq(&hugetlb_lock); spin_unlock_irq(&hugetlb_lock);
@ -3658,7 +3656,7 @@ static int demote_free_hugetlb_folio(struct hstate *h, struct folio *folio)
prep_compound_page(subpage, target_hstate->order); prep_compound_page(subpage, target_hstate->order);
folio_change_private(inner_folio, NULL); folio_change_private(inner_folio, NULL);
prep_new_hugetlb_folio(target_hstate, inner_folio, nid); prep_new_hugetlb_folio(target_hstate, inner_folio, nid);
free_huge_page(subpage); free_huge_folio(inner_folio);
} }
mutex_unlock(&target_hstate->resize_lock); mutex_unlock(&target_hstate->resize_lock);

View File

@ -610,7 +610,7 @@ void destroy_large_folio(struct folio *folio)
enum compound_dtor_id dtor = folio->_folio_dtor; enum compound_dtor_id dtor = folio->_folio_dtor;
if (folio_test_hugetlb(folio)) { if (folio_test_hugetlb(folio)) {
free_huge_page(&folio->page); free_huge_folio(folio);
return; return;
} }