Skip to contentSkip to conversation

mm: introduce per-order mTHP split counters

PATCH Superseded Lance Yang 4 participants 14 messages Jun 28, 2024 – Jul 1, 2024

Subsystems (4)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Lance Yang

author
#0 lore raw

v2 cover mm: introduce per-order mTHP split counters

Hi all,

Currently, the split counters in THP statistics no longer include PTE-mapped mTHP. Therefore, we propose introducing per-order mTHP split counters to monitor the frequency of mTHP splits. This will help developers better analyze and optimize system performance.

/sys/kernel/mm/transparent_hugepage/hugepages-<size>/stats
split
split_failed
split_deferred

---

Changes since v1 [1]
====================
- mm: add per-order mTHP split counters
- Update the changelog
- Drop '_page' from mTHP split counter names (per David and Ryan)
- Store the order of the folio in a variable and reuse it later (per Bang)
- mm: add docs for per-order mTHP split counters
- Improve the doc suggested by Ryan

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

Lance Yang (2):
mm: add per-order mTHP split counters
mm: add docs for per-order mTHP split counters

 Documentation/admin-guide/mm/transhuge.rst | 16 ++++++++++++++++
 include/linux/huge_mm.h                    |  3 +++
 mm/huge_memory.c                           | 19 ++++++++++++++-----
 3 files changed, 33 insertions(+), 5 deletions(-)
Signature · 1 line
2.45.2
author
#1 lore raw

PATCH v2 1/2 mm: add per-order mTHP split counters

Currently, the split counters in THP statistics no longer include PTE-mapped mTHP. Therefore, we propose introducing per-order mTHP split counters to monitor the frequency of mTHP splits. This will help developers better analyze and optimize system performance.

/sys/kernel/mm/transparent_hugepage/hugepages-<size>/stats
split
split_failed
split_deferred

---
 include/linux/huge_mm.h |  3 +++
 mm/huge_memory.c        | 19 ++++++++++++++-----
 2 files changed, 17 insertions(+), 5 deletions(-)
include/linux/huge_mm.h +3 -0
index 212cca384d7e..cee3c5da8f0e 100644
@@ -284,6 +284,9 @@ enum mthp_stat_item {
 	MTHP_STAT_FILE_ALLOC,
 	MTHP_STAT_FILE_FALLBACK,
 	MTHP_STAT_FILE_FALLBACK_CHARGE,
+	MTHP_STAT_SPLIT,
+	MTHP_STAT_SPLIT_FAILED,
+	MTHP_STAT_SPLIT_DEFERRED,
 	__MTHP_STAT_COUNT
 };
 
mm/huge_memory.c +14 -5
index c7ce28f6b7f3..a633206375af 100644
@@ -559,6 +559,9 @@ DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_alloc, MTHP_STAT_FILE_ALLOC);
 DEFINE_MTHP_STAT_ATTR(file_fallback, MTHP_STAT_FILE_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_fallback_charge, MTHP_STAT_FILE_FALLBACK_CHARGE);
+DEFINE_MTHP_STAT_ATTR(split, MTHP_STAT_SPLIT);
+DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED);
+DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED);
 
 static struct attribute *stats_attrs[] = {
 	&anon_fault_alloc_attr.attr,
@@ -569,6 +572,9 @@ static struct attribute *stats_attrs[] = {
 	&file_alloc_attr.attr,
 	&file_fallback_attr.attr,
 	&file_fallback_charge_attr.attr,
+	&split_attr.attr,
+	&split_failed_attr.attr,
+	&split_deferred_attr.attr,
 	NULL,
 };
 
@@ -3068,7 +3074,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 	XA_STATE_ORDER(xas, &folio->mapping->i_pages, folio->index, new_order);
 	struct anon_vma *anon_vma = NULL;
 	struct address_space *mapping = NULL;
-	bool is_thp = folio_test_pmd_mappable(folio);
+	int order = folio_order(folio);
 	int extra_pins, ret;
 	pgoff_t end;
 	bool is_hzp;
@@ -3076,7 +3082,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 	VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
 	VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
 
-	if (new_order >= folio_order(folio))
+	if (new_order >= order)
 		return -EINVAL;
 
 	if (folio_test_anon(folio)) {
@@ -3253,8 +3259,9 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 		i_mmap_unlock_read(mapping);
 out:
 	xas_destroy(&xas);
-	if (is_thp)
+	if (order >= HPAGE_PMD_ORDER)
 		count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
+	count_mthp_stat(order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
 	return ret;
 }
 
@@ -3278,13 +3285,14 @@ void deferred_split_folio(struct folio *folio)
 #ifdef CONFIG_MEMCG
 	struct mem_cgroup *memcg = folio_memcg(folio);
 #endif
+	int order = folio_order(folio);
 	unsigned long flags;
 
 	/*
 	 * Order 1 folios have no space for a deferred list, but we also
 	 * won't waste much memory by not adding them to the deferred list.
 	 */
-	if (folio_order(folio) <= 1)
+	if (order <= 1)
 		return;
 
 	/*
@@ -3305,8 +3313,9 @@ void deferred_split_folio(struct folio *folio)
 
 	spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
 	if (list_empty(&folio->_deferred_list)) {
-		if (folio_test_pmd_mappable(folio))
+		if (order >= HPAGE_PMD_ORDER)
 			count_vm_event(THP_DEFERRED_SPLIT_PAGE);
+		count_mthp_stat(order, MTHP_STAT_SPLIT_DEFERRED);
 		list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
 		ds_queue->split_queue_len++;
 #ifdef CONFIG_MEMCG
Signature · 1 line
2.45.2
Acked-by on v2 1/2
#2 lore raw

Re: [PATCH v2 1/2] mm: add per-order mTHP split counters

Quoted 6 lines · Lance Yang

Currently, the split counters in THP statistics no longer include PTE-mapped mTHP. Therefore, we propose introducing per-order mTHP split counters to monitor the frequency of mTHP splits. This will help developers better analyze and optimize system performance.

Quoted 7 lines

/sys/kernel/mm/transparent_hugepage/hugepages-<size>/stats
split
split_failed
split_deferred

Personally, I'm not convinced that using a temporary variable order makes the code
more readable. Functions like folio_test_pmd_mappable() seem more readable to me. In any case, it's a minor issue.

Quoted 102 lines
---
 include/linux/huge_mm.h |  3 +++
 mm/huge_memory.c        | 19 ++++++++++++++-----
 2 files changed, 17 insertions(+), 5 deletions(-)
include/linux/huge_mm.h +3 -0
index 212cca384d7e..cee3c5da8f0e 100644
@@ -284,6 +284,9 @@ enum mthp_stat_item {
        MTHP_STAT_FILE_ALLOC,
        MTHP_STAT_FILE_FALLBACK,
        MTHP_STAT_FILE_FALLBACK_CHARGE,
+       MTHP_STAT_SPLIT,
+       MTHP_STAT_SPLIT_FAILED,
+       MTHP_STAT_SPLIT_DEFERRED,
        __MTHP_STAT_COUNT
 };
 
mm/huge_memory.c +14 -6
index c7ce28f6b7f3..a633206375af 100644
@@ -559,6 +559,9 @@ DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_alloc, MTHP_STAT_FILE_ALLOC);
 DEFINE_MTHP_STAT_ATTR(file_fallback, MTHP_STAT_FILE_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_fallback_charge, MTHP_STAT_FILE_FALLBACK_CHARGE);
+DEFINE_MTHP_STAT_ATTR(split, MTHP_STAT_SPLIT);
+DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED);
+DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED);
 
 static struct attribute *stats_attrs[] = {
        &anon_fault_alloc_attr.attr,
@@ -569,6 +572,9 @@ static struct attribute *stats_attrs[] = {
        &file_alloc_attr.attr,
        &file_fallback_attr.attr,
        &file_fallback_charge_attr.attr,
+       &split_attr.attr,
+       &split_failed_attr.attr,
+       &split_deferred_attr.attr,
        NULL,
 };
 
@@ -3068,7 +3074,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
        XA_STATE_ORDER(xas, &folio->mapping->i_pages, folio->index, new_order);
        struct anon_vma *anon_vma = NULL;
        struct address_space *mapping = NULL;
-       bool is_thp = folio_test_pmd_mappable(folio);
+       int order = folio_order(folio);
        int extra_pins, ret;
        pgoff_t end;
        bool is_hzp;
@@ -3076,7 +3082,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
        VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
        VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
 
-       if (new_order >= folio_order(folio))
+       if (new_order >= order)
                return -EINVAL;
 
        if (folio_test_anon(folio)) {
@@ -3253,8 +3259,9 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
                i_mmap_unlock_read(mapping);
 out:
        xas_destroy(&xas);
-       if (is_thp)
+       if (order >= HPAGE_PMD_ORDER)
                count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
+       count_mthp_stat(order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
        return ret;
 }
 
@@ -3278,13 +3285,14 @@ void deferred_split_folio(struct folio *folio)
 #ifdef CONFIG_MEMCG
        struct mem_cgroup *memcg = folio_memcg(folio);
 #endif
+       int order = folio_order(folio);
        unsigned long flags;
 
        /*
         * Order 1 folios have no space for a deferred list, but we also
         * won't waste much memory by not adding them to the deferred list.
         */
-       if (folio_order(folio) <= 1)
+       if (order <= 1)
                return;
 
        /*
@@ -3305,8 +3313,9 @@ void deferred_split_folio(struct folio *folio)
 
        spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
        if (list_empty(&folio->_deferred_list)) {
-               if (folio_test_pmd_mappable(folio))
+               if (order >= HPAGE_PMD_ORDER)
                        count_vm_event(THP_DEFERRED_SPLIT_PAGE);
+               count_mthp_stat(order, MTHP_STAT_SPLIT_DEFERRED);
                list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
                ds_queue->split_queue_len++;
 #ifdef CONFIG_MEMCG
--

2.45.2

author
#3 lore raw

Re: [PATCH v2 1/2] mm: add per-order mTHP split counters

Hi Barry,

Thanks for taking time to review!

Quoted 13 lines · Barry Song
Quoted 11 lines · Lance Yang

Currently, the split counters in THP statistics no longer include PTE-mapped mTHP. Therefore, we propose introducing per-order mTHP split counters to monitor the frequency of mTHP splits. This will help developers better analyze and optimize system performance.

/sys/kernel/mm/transparent_hugepage/hugepages-<size>/stats
split
split_failed
split_deferred

Quoted 6 lines
Quoted 2 lines

Personally, I'm not convinced that using a temporary variable order makes the code
more readable. Functions like folio_test_pmd_mappable() seem more readable to

Agreed. Using functions like folio_test_pmd_mappable() is more readable for THP checks.

Quoted 1 line

me. In any case, it's a minor issue.

I'd like to hear other opinions as well ;)

Quoted 2 lines

Thanks again for your time!
Lance

Quoted 103 lines
Quoted 103 lines
---
 include/linux/huge_mm.h |  3 +++
 mm/huge_memory.c        | 19 ++++++++++++++-----
 2 files changed, 17 insertions(+), 5 deletions(-)
include/linux/huge_mm.h +3 -0
index 212cca384d7e..cee3c5da8f0e 100644
@@ -284,6 +284,9 @@ enum mthp_stat_item {
        MTHP_STAT_FILE_ALLOC,
        MTHP_STAT_FILE_FALLBACK,
        MTHP_STAT_FILE_FALLBACK_CHARGE,
+       MTHP_STAT_SPLIT,
+       MTHP_STAT_SPLIT_FAILED,
+       MTHP_STAT_SPLIT_DEFERRED,
        __MTHP_STAT_COUNT
 };
 
mm/huge_memory.c +14 -6
index c7ce28f6b7f3..a633206375af 100644
@@ -559,6 +559,9 @@ DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_alloc, MTHP_STAT_FILE_ALLOC);
 DEFINE_MTHP_STAT_ATTR(file_fallback, MTHP_STAT_FILE_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_fallback_charge, MTHP_STAT_FILE_FALLBACK_CHARGE);
+DEFINE_MTHP_STAT_ATTR(split, MTHP_STAT_SPLIT);
+DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED);
+DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED);
 
 static struct attribute *stats_attrs[] = {
        &anon_fault_alloc_attr.attr,
@@ -569,6 +572,9 @@ static struct attribute *stats_attrs[] = {
        &file_alloc_attr.attr,
        &file_fallback_attr.attr,
        &file_fallback_charge_attr.attr,
+       &split_attr.attr,
+       &split_failed_attr.attr,
+       &split_deferred_attr.attr,
        NULL,
 };
 
@@ -3068,7 +3074,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
        XA_STATE_ORDER(xas, &folio->mapping->i_pages, folio->index, new_order);
        struct anon_vma *anon_vma = NULL;
        struct address_space *mapping = NULL;
-       bool is_thp = folio_test_pmd_mappable(folio);
+       int order = folio_order(folio);
        int extra_pins, ret;
        pgoff_t end;
        bool is_hzp;
@@ -3076,7 +3082,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
        VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
        VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
 
-       if (new_order >= folio_order(folio))
+       if (new_order >= order)
                return -EINVAL;
 
        if (folio_test_anon(folio)) {
@@ -3253,8 +3259,9 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
                i_mmap_unlock_read(mapping);
 out:
        xas_destroy(&xas);
-       if (is_thp)
+       if (order >= HPAGE_PMD_ORDER)
                count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
+       count_mthp_stat(order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
        return ret;
 }
 
@@ -3278,13 +3285,14 @@ void deferred_split_folio(struct folio *folio)
 #ifdef CONFIG_MEMCG
        struct mem_cgroup *memcg = folio_memcg(folio);
 #endif
+       int order = folio_order(folio);
        unsigned long flags;
 
        /*
         * Order 1 folios have no space for a deferred list, but we also
         * won't waste much memory by not adding them to the deferred list.
         */
-       if (folio_order(folio) <= 1)
+       if (order <= 1)
                return;
 
        /*
@@ -3305,8 +3313,9 @@ void deferred_split_folio(struct folio *folio)
 
        spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
        if (list_empty(&folio->_deferred_list)) {
-               if (folio_test_pmd_mappable(folio))
+               if (order >= HPAGE_PMD_ORDER)
                        count_vm_event(THP_DEFERRED_SPLIT_PAGE);
+               count_mthp_stat(order, MTHP_STAT_SPLIT_DEFERRED);
                list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
                ds_queue->split_queue_len++;
 #ifdef CONFIG_MEMCG
--

2.45.2

reviewer Reviewed-by
#4 lore raw

Re: [PATCH v2 1/2] mm: add per-order mTHP split counters

In reply to Barry Song
Quoted 12 lines · Barry Song
Quoted 11 lines · Lance Yang

Currently, the split counters in THP statistics no longer include PTE-mapped mTHP. Therefore, we propose introducing per-order mTHP split counters to monitor the frequency of mTHP splits. This will help developers better analyze and optimize system performance.

/sys/kernel/mm/transparent_hugepage/hugepages-<size>/stats
split
split_failed
split_deferred

Quoted 7 lines
Quoted 2 lines

Personally, I'm not convinced that using a temporary variable order makes the code
more readable. Functions like folio_test_pmd_mappable() seem more readable to me. In any case, it's a minor issue.

Yes, I have the same opinion as Barry. With that:

Quoted 104 lines
Quoted 102 lines
---
  include/linux/huge_mm.h |  3 +++
  mm/huge_memory.c        | 19 ++++++++++++++-----
  2 files changed, 17 insertions(+), 5 deletions(-)
include/linux/huge_mm.h +3 -0
index 212cca384d7e..cee3c5da8f0e 100644
@@ -284,6 +284,9 @@ enum mthp_stat_item {
         MTHP_STAT_FILE_ALLOC,
         MTHP_STAT_FILE_FALLBACK,
         MTHP_STAT_FILE_FALLBACK_CHARGE,
+       MTHP_STAT_SPLIT,
+       MTHP_STAT_SPLIT_FAILED,
+       MTHP_STAT_SPLIT_DEFERRED,
         __MTHP_STAT_COUNT
  };
 
mm/huge_memory.c +14 -6
index c7ce28f6b7f3..a633206375af 100644
@@ -559,6 +559,9 @@ DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK);
  DEFINE_MTHP_STAT_ATTR(file_alloc, MTHP_STAT_FILE_ALLOC);
  DEFINE_MTHP_STAT_ATTR(file_fallback, MTHP_STAT_FILE_FALLBACK);
  DEFINE_MTHP_STAT_ATTR(file_fallback_charge, MTHP_STAT_FILE_FALLBACK_CHARGE);
+DEFINE_MTHP_STAT_ATTR(split, MTHP_STAT_SPLIT);
+DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED);
+DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED);
 
  static struct attribute *stats_attrs[] = {
         &anon_fault_alloc_attr.attr,
@@ -569,6 +572,9 @@ static struct attribute *stats_attrs[] = {
         &file_alloc_attr.attr,
         &file_fallback_attr.attr,
         &file_fallback_charge_attr.attr,
+       &split_attr.attr,
+       &split_failed_attr.attr,
+       &split_deferred_attr.attr,
         NULL,
  };
 
@@ -3068,7 +3074,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
         XA_STATE_ORDER(xas, &folio->mapping->i_pages, folio->index, new_order);
         struct anon_vma *anon_vma = NULL;
         struct address_space *mapping = NULL;
-       bool is_thp = folio_test_pmd_mappable(folio);
+       int order = folio_order(folio);
         int extra_pins, ret;
         pgoff_t end;
         bool is_hzp;
@@ -3076,7 +3082,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
         VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
         VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
 
-       if (new_order >= folio_order(folio))
+       if (new_order >= order)
                 return -EINVAL;
 
         if (folio_test_anon(folio)) {
@@ -3253,8 +3259,9 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
                 i_mmap_unlock_read(mapping);
  out:
         xas_destroy(&xas);
-       if (is_thp)
+       if (order >= HPAGE_PMD_ORDER)
                 count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
+       count_mthp_stat(order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
         return ret;
  }
 
@@ -3278,13 +3285,14 @@ void deferred_split_folio(struct folio *folio)
  #ifdef CONFIG_MEMCG
         struct mem_cgroup *memcg = folio_memcg(folio);
  #endif
+       int order = folio_order(folio);
         unsigned long flags;
 
         /*
          * Order 1 folios have no space for a deferred list, but we also
          * won't waste much memory by not adding them to the deferred list.
          */
-       if (folio_order(folio) <= 1)
+       if (order <= 1)
                 return;
 
         /*
@@ -3305,8 +3313,9 @@ void deferred_split_folio(struct folio *folio)
 
         spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
         if (list_empty(&folio->_deferred_list)) {
-               if (folio_test_pmd_mappable(folio))
+               if (order >= HPAGE_PMD_ORDER)
                         count_vm_event(THP_DEFERRED_SPLIT_PAGE);
+               count_mthp_stat(order, MTHP_STAT_SPLIT_DEFERRED);
                 list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
                 ds_queue->split_queue_len++;
  #ifdef CONFIG_MEMCG
--

2.45.2

author
#5 lore raw

Re: [PATCH v2 1/2] mm: add per-order mTHP split counters

Hi Baolin,

Thanks for taking time to review!

Quoted 19 lines · Baolin Wang
Quoted 15 lines · Barry Song
Quoted 13 lines · Lance Yang

Currently, the split counters in THP statistics no longer include PTE-mapped mTHP. Therefore, we propose introducing per-order mTHP split counters to monitor the frequency of mTHP splits. This will help developers better analyze and optimize system performance.

/sys/kernel/mm/transparent_hugepage/hugepages-<size>/stats
split
split_failed
split_deferred

Quoted 7 lines
Quoted 4 lines

Personally, I'm not convinced that using a temporary variable order makes the code
more readable. Functions like folio_test_pmd_mappable() seem more readable to me. In any case, it's a minor issue.

Yes, I have the same opinion as Barry. With that:

Thanks again for your opinion!
Lance

Quoted 106 lines
Quoted 106 lines
Quoted 102 lines
---
  include/linux/huge_mm.h |  3 +++
  mm/huge_memory.c        | 19 ++++++++++++++-----
  2 files changed, 17 insertions(+), 5 deletions(-)
include/linux/huge_mm.h +3 -0
index 212cca384d7e..cee3c5da8f0e 100644
@@ -284,6 +284,9 @@ enum mthp_stat_item {
         MTHP_STAT_FILE_ALLOC,
         MTHP_STAT_FILE_FALLBACK,
         MTHP_STAT_FILE_FALLBACK_CHARGE,
+       MTHP_STAT_SPLIT,
+       MTHP_STAT_SPLIT_FAILED,
+       MTHP_STAT_SPLIT_DEFERRED,
         __MTHP_STAT_COUNT
  };
 
mm/huge_memory.c +14 -6
index c7ce28f6b7f3..a633206375af 100644
@@ -559,6 +559,9 @@ DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK);
  DEFINE_MTHP_STAT_ATTR(file_alloc, MTHP_STAT_FILE_ALLOC);
  DEFINE_MTHP_STAT_ATTR(file_fallback, MTHP_STAT_FILE_FALLBACK);
  DEFINE_MTHP_STAT_ATTR(file_fallback_charge, MTHP_STAT_FILE_FALLBACK_CHARGE);
+DEFINE_MTHP_STAT_ATTR(split, MTHP_STAT_SPLIT);
+DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED);
+DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED);
 
  static struct attribute *stats_attrs[] = {
         &anon_fault_alloc_attr.attr,
@@ -569,6 +572,9 @@ static struct attribute *stats_attrs[] = {
         &file_alloc_attr.attr,
         &file_fallback_attr.attr,
         &file_fallback_charge_attr.attr,
+       &split_attr.attr,
+       &split_failed_attr.attr,
+       &split_deferred_attr.attr,
         NULL,
  };
 
@@ -3068,7 +3074,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
         XA_STATE_ORDER(xas, &folio->mapping->i_pages, folio->index, new_order);
         struct anon_vma *anon_vma = NULL;
         struct address_space *mapping = NULL;
-       bool is_thp = folio_test_pmd_mappable(folio);
+       int order = folio_order(folio);
         int extra_pins, ret;
         pgoff_t end;
         bool is_hzp;
@@ -3076,7 +3082,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
         VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
         VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
 
-       if (new_order >= folio_order(folio))
+       if (new_order >= order)
                 return -EINVAL;
 
         if (folio_test_anon(folio)) {
@@ -3253,8 +3259,9 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
                 i_mmap_unlock_read(mapping);
  out:
         xas_destroy(&xas);
-       if (is_thp)
+       if (order >= HPAGE_PMD_ORDER)
                 count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
+       count_mthp_stat(order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
         return ret;
  }
 
@@ -3278,13 +3285,14 @@ void deferred_split_folio(struct folio *folio)
  #ifdef CONFIG_MEMCG
         struct mem_cgroup *memcg = folio_memcg(folio);
  #endif
+       int order = folio_order(folio);
         unsigned long flags;
 
         /*
          * Order 1 folios have no space for a deferred list, but we also
          * won't waste much memory by not adding them to the deferred list.
          */
-       if (folio_order(folio) <= 1)
+       if (order <= 1)
                 return;
 
         /*
@@ -3305,8 +3313,9 @@ void deferred_split_folio(struct folio *folio)
 
         spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
         if (list_empty(&folio->_deferred_list)) {
-               if (folio_test_pmd_mappable(folio))
+               if (order >= HPAGE_PMD_ORDER)
                         count_vm_event(THP_DEFERRED_SPLIT_PAGE);
+               count_mthp_stat(order, MTHP_STAT_SPLIT_DEFERRED);
                 list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
                 ds_queue->split_queue_len++;
  #ifdef CONFIG_MEMCG
--

2.45.2

reviewer Reviewed-by on v2 1/2
#6 lore raw

Re: [PATCH v2 1/2] mm: add per-order mTHP split counters

In reply to Lance Yang
Quoted 5 lines · Lance Yang

Currently, the split counters in THP statistics no longer include PTE-mapped mTHP. Therefore, we propose introducing per-order mTHP split counters to monitor the frequency of mTHP splits. This will help developers better analyze and optimize system performance.

Quoted 7 lines

/sys/kernel/mm/transparent_hugepage/hugepages-<size>/stats
split
split_failed
split_deferred

LGTM!

Quoted 99 lines
---
 include/linux/huge_mm.h |  3 +++
 mm/huge_memory.c        | 19 ++++++++++++++-----
 2 files changed, 17 insertions(+), 5 deletions(-)
include/linux/huge_mm.h +3 -0
index 212cca384d7e..cee3c5da8f0e 100644
@@ -284,6 +284,9 @@ enum mthp_stat_item {
 	MTHP_STAT_FILE_ALLOC,
 	MTHP_STAT_FILE_FALLBACK,
 	MTHP_STAT_FILE_FALLBACK_CHARGE,
+	MTHP_STAT_SPLIT,
+	MTHP_STAT_SPLIT_FAILED,
+	MTHP_STAT_SPLIT_DEFERRED,
 	__MTHP_STAT_COUNT
 };
 
mm/huge_memory.c +14 -5
index c7ce28f6b7f3..a633206375af 100644
@@ -559,6 +559,9 @@ DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_alloc, MTHP_STAT_FILE_ALLOC);
 DEFINE_MTHP_STAT_ATTR(file_fallback, MTHP_STAT_FILE_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_fallback_charge, MTHP_STAT_FILE_FALLBACK_CHARGE);
+DEFINE_MTHP_STAT_ATTR(split, MTHP_STAT_SPLIT);
+DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED);
+DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED);
 
 static struct attribute *stats_attrs[] = {
 	&anon_fault_alloc_attr.attr,
@@ -569,6 +572,9 @@ static struct attribute *stats_attrs[] = {
 	&file_alloc_attr.attr,
 	&file_fallback_attr.attr,
 	&file_fallback_charge_attr.attr,
+	&split_attr.attr,
+	&split_failed_attr.attr,
+	&split_deferred_attr.attr,
 	NULL,
 };
 
@@ -3068,7 +3074,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 	XA_STATE_ORDER(xas, &folio->mapping->i_pages, folio->index, new_order);
 	struct anon_vma *anon_vma = NULL;
 	struct address_space *mapping = NULL;
-	bool is_thp = folio_test_pmd_mappable(folio);
+	int order = folio_order(folio);
 	int extra_pins, ret;
 	pgoff_t end;
 	bool is_hzp;
@@ -3076,7 +3082,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 	VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
 	VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
 
-	if (new_order >= folio_order(folio))
+	if (new_order >= order)
 		return -EINVAL;
 
 	if (folio_test_anon(folio)) {
@@ -3253,8 +3259,9 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 		i_mmap_unlock_read(mapping);
 out:
 	xas_destroy(&xas);
-	if (is_thp)
+	if (order >= HPAGE_PMD_ORDER)
 		count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
+	count_mthp_stat(order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
 	return ret;
 }
 
@@ -3278,13 +3285,14 @@ void deferred_split_folio(struct folio *folio)
 #ifdef CONFIG_MEMCG
 	struct mem_cgroup *memcg = folio_memcg(folio);
 #endif
+	int order = folio_order(folio);
 	unsigned long flags;
 
 	/*
 	 * Order 1 folios have no space for a deferred list, but we also
 	 * won't waste much memory by not adding them to the deferred list.
 	 */
-	if (folio_order(folio) <= 1)
+	if (order <= 1)
 		return;
 
 	/*
@@ -3305,8 +3313,9 @@ void deferred_split_folio(struct folio *folio)
 
 	spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
 	if (list_empty(&folio->_deferred_list)) {
-		if (folio_test_pmd_mappable(folio))
+		if (order >= HPAGE_PMD_ORDER)
 			count_vm_event(THP_DEFERRED_SPLIT_PAGE);
+		count_mthp_stat(order, MTHP_STAT_SPLIT_DEFERRED);
 		list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
 		ds_queue->split_queue_len++;
 #ifdef CONFIG_MEMCG
author
#7 lore raw

Re: [PATCH v2 1/2] mm: add per-order mTHP split counters

Hi Ryan,

Quoted 12 lines · Ryan Roberts
Quoted 10 lines · Lance Yang

Currently, the split counters in THP statistics no longer include PTE-mapped mTHP. Therefore, we propose introducing per-order mTHP split counters to monitor the frequency of mTHP splits. This will help developers better analyze and optimize system performance.

/sys/kernel/mm/transparent_hugepage/hugepages-<size>/stats
split
split_failed
split_deferred

Quoted 6 lines
Quoted 2 lines

LGTM!

Thanks for taking time to review!
Lance

Quoted 101 lines
Quoted 100 lines
---
 include/linux/huge_mm.h |  3 +++
 mm/huge_memory.c        | 19 ++++++++++++++-----
 2 files changed, 17 insertions(+), 5 deletions(-)
include/linux/huge_mm.h +3 -0
index 212cca384d7e..cee3c5da8f0e 100644
@@ -284,6 +284,9 @@ enum mthp_stat_item {
      MTHP_STAT_FILE_ALLOC,
      MTHP_STAT_FILE_FALLBACK,
      MTHP_STAT_FILE_FALLBACK_CHARGE,
+     MTHP_STAT_SPLIT,
+     MTHP_STAT_SPLIT_FAILED,
+     MTHP_STAT_SPLIT_DEFERRED,
      __MTHP_STAT_COUNT
 };
 
mm/huge_memory.c +14 -5
index c7ce28f6b7f3..a633206375af 100644
@@ -559,6 +559,9 @@ DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_alloc, MTHP_STAT_FILE_ALLOC);
 DEFINE_MTHP_STAT_ATTR(file_fallback, MTHP_STAT_FILE_FALLBACK);
 DEFINE_MTHP_STAT_ATTR(file_fallback_charge, MTHP_STAT_FILE_FALLBACK_CHARGE);
+DEFINE_MTHP_STAT_ATTR(split, MTHP_STAT_SPLIT);
+DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED);
+DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED);
 
 static struct attribute *stats_attrs[] = {
      &anon_fault_alloc_attr.attr,
@@ -569,6 +572,9 @@ static struct attribute *stats_attrs[] = {
      &file_alloc_attr.attr,
      &file_fallback_attr.attr,
      &file_fallback_charge_attr.attr,
+     &split_attr.attr,
+     &split_failed_attr.attr,
+     &split_deferred_attr.attr,
      NULL,
 };
 
@@ -3068,7 +3074,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
      XA_STATE_ORDER(xas, &folio->mapping->i_pages, folio->index, new_order);
      struct anon_vma *anon_vma = NULL;
      struct address_space *mapping = NULL;
-     bool is_thp = folio_test_pmd_mappable(folio);
+     int order = folio_order(folio);
      int extra_pins, ret;
      pgoff_t end;
      bool is_hzp;
@@ -3076,7 +3082,7 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
      VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
      VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
 
-     if (new_order >= folio_order(folio))
+     if (new_order >= order)
              return -EINVAL;
 
      if (folio_test_anon(folio)) {
@@ -3253,8 +3259,9 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
              i_mmap_unlock_read(mapping);
 out:
      xas_destroy(&xas);
-     if (is_thp)
+     if (order >= HPAGE_PMD_ORDER)
              count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
+     count_mthp_stat(order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
      return ret;
 }
 
@@ -3278,13 +3285,14 @@ void deferred_split_folio(struct folio *folio)
 #ifdef CONFIG_MEMCG
      struct mem_cgroup *memcg = folio_memcg(folio);
 #endif
+     int order = folio_order(folio);
      unsigned long flags;
 
      /*
       * Order 1 folios have no space for a deferred list, but we also
       * won't waste much memory by not adding them to the deferred list.
       */
-     if (folio_order(folio) <= 1)
+     if (order <= 1)
              return;
 
      /*
@@ -3305,8 +3313,9 @@ void deferred_split_folio(struct folio *folio)
 
      spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
      if (list_empty(&folio->_deferred_list)) {
-             if (folio_test_pmd_mappable(folio))
+             if (order >= HPAGE_PMD_ORDER)
                      count_vm_event(THP_DEFERRED_SPLIT_PAGE);
+             count_mthp_stat(order, MTHP_STAT_SPLIT_DEFERRED);
              list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
              ds_queue->split_queue_len++;
 #ifdef CONFIG_MEMCG
author
#8 lore raw

PATCH v2 2/2 mm: add docs for per-order mTHP split counters

In reply to Lance Yang

This commit introduces documentation for mTHP split counters in transhuge.rst.

---
 Documentation/admin-guide/mm/transhuge.rst | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
Documentation/admin-guide/mm/transhuge.rst +16 -0
index 1f72b00af5d3..709fe10b60f4 100644
@@ -514,6 +514,22 @@ file_fallback_charge
 	falls back to using small pages even though the allocation was
 	successful.
 
+split
+	is incremented every time a huge page is successfully split into
+	base pages. This can happen for a variety of reasons but a common
+	reason is that a huge page is old and is being reclaimed.
+	This action implies splitting any block mappings into PTEs.
+
+split_failed
+	is incremented if kernel fails to split huge
+	page. This can happen if the page was pinned by somebody.
+
+split_deferred
+	is incremented when a huge page is put onto split
+	queue. This happens when a huge page is partially unmapped and
+	splitting it would free up some memory. Pages on split queue are
+	going to be split under memory pressure.
+
 As the system ages, allocating huge pages may be expensive as the
 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
Signature · 1 line
2.45.2
Reviewed-by on v2 2/2
#9 lore raw

Re: [PATCH v2 2/2] mm: add docs for per-order mTHP split counters

Quoted 6 lines · Lance Yang

This commit introduces documentation for mTHP split counters in transhuge.rst.

Quoted 34 lines
---
 Documentation/admin-guide/mm/transhuge.rst | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
Documentation/admin-guide/mm/transhuge.rst +16 -1
index 1f72b00af5d3..709fe10b60f4 100644
@@ -514,6 +514,22 @@ file_fallback_charge
        falls back to using small pages even though the allocation was
        successful.
 
+split
+       is incremented every time a huge page is successfully split into
+       base pages. This can happen for a variety of reasons but a common
+       reason is that a huge page is old and is being reclaimed.
+       This action implies splitting any block mappings into PTEs.
+
+split_failed
+       is incremented if kernel fails to split huge
+       page. This can happen if the page was pinned by somebody.
+
+split_deferred
+       is incremented when a huge page is put onto split
+       queue. This happens when a huge page is partially unmapped and
+       splitting it would free up some memory. Pages on split queue are
+       going to be split under memory pressure.
+
 As the system ages, allocating huge pages may be expensive as the
 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
--

2.45.2

author
#10 lore raw

Re: [PATCH v2 2/2] mm: add docs for per-order mTHP split counters

Hi Barry,

Thanks a lot for taking time to review!

Quoted 10 lines · Barry Song
Quoted 6 lines · Lance Yang

This commit introduces documentation for mTHP split counters in transhuge.rst.

Have a nice weekend ;)
Lance

Quoted 35 lines
Quoted 35 lines
---
 Documentation/admin-guide/mm/transhuge.rst | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
Documentation/admin-guide/mm/transhuge.rst +16 -1
index 1f72b00af5d3..709fe10b60f4 100644
@@ -514,6 +514,22 @@ file_fallback_charge
        falls back to using small pages even though the allocation was
        successful.
 
+split
+       is incremented every time a huge page is successfully split into
+       base pages. This can happen for a variety of reasons but a common
+       reason is that a huge page is old and is being reclaimed.
+       This action implies splitting any block mappings into PTEs.
+
+split_failed
+       is incremented if kernel fails to split huge
+       page. This can happen if the page was pinned by somebody.
+
+split_deferred
+       is incremented when a huge page is put onto split
+       queue. This happens when a huge page is partially unmapped and
+       splitting it would free up some memory. Pages on split queue are
+       going to be split under memory pressure.
+
 As the system ages, allocating huge pages may be expensive as the
 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
--

2.45.2

reviewer
#11 lore raw

Re: [PATCH v2 2/2] mm: add docs for per-order mTHP split counters

In reply to Lance Yang
Quoted 14 lines · Lance Yang

This commit introduces documentation for mTHP split counters in transhuge.rst.

---
 Documentation/admin-guide/mm/transhuge.rst | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
Documentation/admin-guide/mm/transhuge.rst +0 -0
index 1f72b00af5d3..709fe10b60f4 100644

Documentation/admin-guide/mm/transhuge.rst file_fallback_charge · line 515

Quoted 2 lines
Documentation/admin-guide/mm/transhuge.rst +0 -0
@@ -514,6 +514,22 @@ file_fallback_charge
 	falls back to using small pages even though the allocation was
 	successful.

I note at the top of this section there is a note:

Monitoring usage
================

.. note::
Currently the below counters only record events relating to PMD-sized THP. Events relating to other THP sizes are not included.

Which is out of date, now that we support mTHP stats. Perhaps it should be removed?

Documentation/admin-guide/mm/transhuge.rst file_fallback_charge · line 521

Quoted 6 lines
Documentation/admin-guide/mm/transhuge.rst +5 -0
+split
+	is incremented every time a huge page is successfully split into
+	base pages. This can happen for a variety of reasons but a common
+	reason is that a huge page is old and is being reclaimed.
+	This action implies splitting any block mappings into PTEs.

Now that I'm reading this, I'm reminded that Yang Shi suggested at LSFMM that a potential aid so solving the swap-out fragmentation problem is to split high orders to lower (but not 0) orders. I don't know if we would take that route, but in principle it sounds like splitting mTHP to smaller mTHP might be something we want some day. I wonder if we should spec this counter to also include splits to smaller orders and not just splits to base pages?

Actually looking at the code, I think split_huge_page_to_list_to_order(order>0) would already increment this counter without actually splitting to base pages. So the documantation should probably just reflect that.

Quoted 14 lines
Documentation/admin-guide/mm/transhuge.rst +11 -0
+
+split_failed
+	is incremented if kernel fails to split huge
+	page. This can happen if the page was pinned by somebody.
+
+split_deferred
+	is incremented when a huge page is put onto split
+	queue. This happens when a huge page is partially unmapped and
+	splitting it would free up some memory. Pages on split queue are
+	going to be split under memory pressure.
+
 As the system ages, allocating huge pages may be expensive as the
 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
author
#12 lore raw

Re: [PATCH v2 2/2] mm: add docs for per-order mTHP split counters

Quoted 22 lines · Ryan Roberts
Quoted 14 lines · Lance Yang

This commit introduces documentation for mTHP split counters in transhuge.rst.

---
 Documentation/admin-guide/mm/transhuge.rst | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
Documentation/admin-guide/mm/transhuge.rst +0 -0
index 1f72b00af5d3..709fe10b60f4 100644

Documentation/admin-guide/mm/transhuge.rst file_fallback_charge · line 515

Quoted 2 lines
Documentation/admin-guide/mm/transhuge.rst +0 -0
@@ -514,6 +514,22 @@ file_fallback_charge
      falls back to using small pages even though the allocation was
      successful.

I note at the top of this section there is a note:

Quoted 8 lines

Monitoring usage
================

.. note::
Currently the below counters only record events relating to PMD-sized THP. Events relating to other THP sizes are not included.

Which is out of date, now that we support mTHP stats. Perhaps it should be removed?

Good catch! Let's remove that in this patch ;)

Quoted 15 lines

Documentation/admin-guide/mm/transhuge.rst file_fallback_charge · line 521

Quoted 7 lines
Documentation/admin-guide/mm/transhuge.rst +5 -0
+split
+     is incremented every time a huge page is successfully split into
+     base pages. This can happen for a variety of reasons but a common
+     reason is that a huge page is old and is being reclaimed.
+     This action implies splitting any block mappings into PTEs.

Now that I'm reading this, I'm reminded that Yang Shi suggested at LSFMM that a potential aid so solving the swap-out fragmentation problem is to split high orders to lower (but not 0) orders. I don't know if we would take that route, but in principle it sounds like splitting mTHP to smaller mTHP might be something we want some day. I wonder if we should spec this counter to also include splits to smaller orders and not just splits to base pages?

Quoted 3 lines

Actually looking at the code, I think split_huge_page_to_list_to_order(order>0) would already increment this counter without actually splitting to base pages. So the documantation should probably just reflect that.

Yep, you're right.

It’s important that the documentation reflects that to ensure consistency.

How about "... is successfully split into smaller orders. This can..."?

Thanks,
Lance

Quoted 16 lines
Quoted 15 lines
Documentation/admin-guide/mm/transhuge.rst +11 -0
+
+split_failed
+     is incremented if kernel fails to split huge
+     page. This can happen if the page was pinned by somebody.
+
+split_deferred
+     is incremented when a huge page is put onto split
+     queue. This happens when a huge page is partially unmapped and
+     splitting it would free up some memory. Pages on split queue are
+     going to be split under memory pressure.
+
 As the system ages, allocating huge pages may be expensive as the
 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
reviewer
#13 lore raw

Re: [PATCH v2 2/2] mm: add docs for per-order mTHP split counters

Quoted 53 lines · Lance Yang
Quoted 22 lines · Ryan Roberts
Quoted 14 lines · Lance Yang

This commit introduces documentation for mTHP split counters in transhuge.rst.

---
 Documentation/admin-guide/mm/transhuge.rst | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
Documentation/admin-guide/mm/transhuge.rst +0 -0
index 1f72b00af5d3..709fe10b60f4 100644

Documentation/admin-guide/mm/transhuge.rst file_fallback_charge · line 515

Quoted 2 lines
Documentation/admin-guide/mm/transhuge.rst +0 -0
@@ -514,6 +514,22 @@ file_fallback_charge
      falls back to using small pages even though the allocation was
      successful.

I note at the top of this section there is a note:

Quoted 8 lines

Monitoring usage
================

.. note::
Currently the below counters only record events relating to PMD-sized THP. Events relating to other THP sizes are not included.

Which is out of date, now that we support mTHP stats. Perhaps it should be removed?

Good catch! Let's remove that in this patch ;)

Quoted 15 lines

Documentation/admin-guide/mm/transhuge.rst file_fallback_charge · line 521

Quoted 7 lines
Documentation/admin-guide/mm/transhuge.rst +5 -0
+split
+     is incremented every time a huge page is successfully split into
+     base pages. This can happen for a variety of reasons but a common
+     reason is that a huge page is old and is being reclaimed.
+     This action implies splitting any block mappings into PTEs.

Now that I'm reading this, I'm reminded that Yang Shi suggested at LSFMM that a potential aid so solving the swap-out fragmentation problem is to split high orders to lower (but not 0) orders. I don't know if we would take that route, but in principle it sounds like splitting mTHP to smaller mTHP might be something we want some day. I wonder if we should spec this counter to also include splits to smaller orders and not just splits to base pages?

Quoted 3 lines

Actually looking at the code, I think split_huge_page_to_list_to_order(order>0) would already increment this counter without actually splitting to base pages. So the documantation should probably just reflect that.

Quoted 5 lines

Yep, you're right.

It’s important that the documentation reflects that to ensure consistency.

How about "... is successfully split into smaller orders. This can..."?

fine by me.

Quoted 20 lines

Thanks,
Lance

Quoted 16 lines
Quoted 15 lines
Documentation/admin-guide/mm/transhuge.rst +11 -0
+
+split_failed
+     is incremented if kernel fails to split huge
+     page. This can happen if the page was pinned by somebody.
+
+split_deferred
+     is incremented when a huge page is put onto split
+     queue. This happens when a huge page is partially unmapped and
+     splitting it would free up some memory. Pages on split queue are
+     going to be split under memory pressure.
+
 As the system ages, allocating huge pages may be expensive as the
 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