BACKPORT: mm: count the number of partially mapped anonymous THPs per size

When a THP is added to the deferred_list due to partially mapped, its
partial pages are unused, leading to wasted memory and potentially
increasing memory reclamation pressure.

Detailing the specifics of how unmapping occurs is quite difficult and not
that useful, so we adopt a simple approach: each time a THP enters the
deferred_list, we increment the count by 1; whenever it leaves for any
reason, we decrement the count by 1.

Link: https://lkml.kernel.org/r/20240824010441.21308-3-21cnbao@gmail.com
Change-Id: Ie4473bc34ddfa7ce748bcd380927697610faa5a8
Signed-off-by: Barry Song <v-songbaohua@oppo.com>
Acked-by: David Hildenbrand <david@redhat.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Chris Li <chrisl@kernel.org>
Cc: Chuanhua Han <hanchuanhua@oppo.com>
Cc: Kairui Song <kasong@tencent.com>
Cc: Kalesh Singh <kaleshsingh@google.com>
Cc: Lance Yang <ioworker0@gmail.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Shuai Yuan <yuanshuai@oppo.com>
Cc: Usama Arif <usamaarif642@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
(cherry picked from commit 8175ebfd302abe6fbdca9037f763ecbfdb8db572)
[ Fix trivial conflicts in huge_memory.c, huge_mm.h, transhuge.rst
  - Kalesh Singh ]
Bug: 419599659
Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
This commit is contained in:
Barry Song
2024-08-24 13:04:41 +12:00
committed by Kalesh Singh
parent 545db6094c
commit c6f085c328
3 changed files with 14 additions and 0 deletions

View File

@@ -515,6 +515,13 @@ nr_anon
might be currently entirely mapped or have partially unmapped/unused might be currently entirely mapped or have partially unmapped/unused
subpages. subpages.
nr_anon_partially_mapped
the number of anonymous THP which are likely partially mapped, possibly
wasting memory, and have been queued for deferred memory reclamation.
Note that in corner some cases (e.g., failed migration), we might detect
an anonymous THP as "partially mapped" and count it here, even though it
is not actually partially mapped anymore.
As the system ages, allocating huge pages may be expensive as the As the system ages, allocating huge pages may be expensive as the
system uses memory compaction to copy data around memory to free a system uses memory compaction to copy data around memory to free a
huge page for use. There are some counters in ``/proc/vmstat`` to help huge page for use. There are some counters in ``/proc/vmstat`` to help

View File

@@ -268,6 +268,7 @@ enum mthp_stat_item {
MTHP_STAT_SPLIT_FAILED, MTHP_STAT_SPLIT_FAILED,
MTHP_STAT_SPLIT_DEFERRED, MTHP_STAT_SPLIT_DEFERRED,
MTHP_STAT_NR_ANON, MTHP_STAT_NR_ANON,
MTHP_STAT_NR_ANON_PARTIALLY_MAPPED,
__MTHP_STAT_COUNT __MTHP_STAT_COUNT
}; };

View File

@@ -559,6 +559,7 @@ DEFINE_MTHP_STAT_ATTR(split, MTHP_STAT_SPLIT);
DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED); DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED);
DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED); DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED);
DEFINE_MTHP_STAT_ATTR(nr_anon, MTHP_STAT_NR_ANON); DEFINE_MTHP_STAT_ATTR(nr_anon, MTHP_STAT_NR_ANON);
DEFINE_MTHP_STAT_ATTR(nr_anon_partially_mapped, MTHP_STAT_NR_ANON_PARTIALLY_MAPPED);
static struct attribute *stats_attrs[] = { static struct attribute *stats_attrs[] = {
&anon_fault_alloc_attr.attr, &anon_fault_alloc_attr.attr,
@@ -570,6 +571,7 @@ static struct attribute *stats_attrs[] = {
&split_failed_attr.attr, &split_failed_attr.attr,
&split_deferred_attr.attr, &split_deferred_attr.attr,
&nr_anon_attr.attr, &nr_anon_attr.attr,
&nr_anon_partially_mapped_attr.attr,
NULL, NULL,
}; };
@@ -3499,6 +3501,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
if (folio_order(folio) > 1 && if (folio_order(folio) > 1 &&
!list_empty(&folio->_deferred_list)) { !list_empty(&folio->_deferred_list)) {
ds_queue->split_queue_len--; ds_queue->split_queue_len--;
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
list_del_init(&folio->_deferred_list); list_del_init(&folio->_deferred_list);
} }
spin_unlock(&ds_queue->split_queue_lock); spin_unlock(&ds_queue->split_queue_lock);
@@ -3563,6 +3566,7 @@ void __folio_undo_large_rmappable(struct folio *folio)
spin_lock_irqsave(&ds_queue->split_queue_lock, flags); spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
if (!list_empty(&folio->_deferred_list)) { if (!list_empty(&folio->_deferred_list)) {
ds_queue->split_queue_len--; ds_queue->split_queue_len--;
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
list_del_init(&folio->_deferred_list); list_del_init(&folio->_deferred_list);
} }
spin_unlock_irqrestore(&ds_queue->split_queue_lock, flags); spin_unlock_irqrestore(&ds_queue->split_queue_lock, flags);
@@ -3603,6 +3607,7 @@ void deferred_split_folio(struct folio *folio)
if (list_empty(&folio->_deferred_list)) { if (list_empty(&folio->_deferred_list)) {
count_vm_event(THP_DEFERRED_SPLIT_PAGE); count_vm_event(THP_DEFERRED_SPLIT_PAGE);
count_mthp_stat(folio_order(folio), MTHP_STAT_SPLIT_DEFERRED); count_mthp_stat(folio_order(folio), MTHP_STAT_SPLIT_DEFERRED);
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, 1);
list_add_tail(&folio->_deferred_list, &ds_queue->split_queue); list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
ds_queue->split_queue_len++; ds_queue->split_queue_len++;
#ifdef CONFIG_MEMCG #ifdef CONFIG_MEMCG
@@ -3654,6 +3659,7 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
list_move(&folio->_deferred_list, &list); list_move(&folio->_deferred_list, &list);
} else { } else {
/* We lost race with folio_put() */ /* We lost race with folio_put() */
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
list_del_init(&folio->_deferred_list); list_del_init(&folio->_deferred_list);
ds_queue->split_queue_len--; ds_queue->split_queue_len--;
} }