[RFC PATCH] mm: control mthp per process/cgroup

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Now the large folio control interfaces is system wide and tend to be
default on: file systems use large folio by default if supported,
mTHP is tend to default enable when boot [1].
When large folio enabled, some workloads have performance benefit,
but some may not and some side effects can happen: the memory usage
may increase, direct reclaim maybe more frequently because of more
large order allocations, result in cpu usage also increases. We observed
this on a product environment which run nginx, the pgscan_direct count
increased a lot than before, can reach to 3000 times per second, and
disable file large folio can fix this.

Now the anon/shmem/file mthp control interfaces is system wide, so the
control api per process or cgroup may be necessary, for example, in one
machine, some containers can use large folios and some can disable them.

This patch present a possible solution:
Extend the existing prctl api(PR_SET_THP_DISABLE), use the third argument
to specify which kind of mTHP to disable for this process.
For example:
  prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0); //keep the original semantics
  prctl(PR_SET_THP_DISABLE, 1, PR_DISABLE_ANON_MTHP, 0, 0);
  prctl(PR_SET_THP_DISABLE, 1, PR_DISABLE_SHMEM_MTHP, 0, 0);
  prctl(PR_SET_THP_DISABLE, 1, PR_DISABLE_FILE_MTHP, 0, 0);

The child processes will inherit the setting so if a seed process had
configured, all processes in the cgroup will inherit the setting.

This patch does not implement control over file mTHP, this is planed to
do after pagecache folio sizes control done[2].

[1] https://lore.kernel.org/linux-mm/20240814085409.124466-1-21cnbao@xxxxxxxxx/T/
[2] https://lore.kernel.org/lkml/20240717071257.4141363-1-ryan.roberts@xxxxxxx/T/

Signed-off-by: Nanyong Sun <sunnanyong@xxxxxxxxxx>
---
This patch is more to discuss the possible directions.

I am not sure occupy 3 bits in the mm flag hole is a good way, do we need
add a new flag in mm_struct to do this?

I think another possible solution(not in this patch) is to go in the
opposite direction of this patch, i.e. only allow the processes/cgroups
who really need large folio to use the mTHP. We can add a new prctl api
to mark the process who can use some specific sizes of mTHP.

TODO:
Need modify reference manual for the change of prctl PR_SET_THP_DISABLE.

 include/linux/huge_mm.h        |  3 +++
 include/linux/sched/coredump.h |  9 +++++++--
 include/uapi/linux/prctl.h     |  5 +++++
 kernel/sys.c                   | 36 ++++++++++++++++++++++++++++------
 mm/shmem.c                     |  7 +++++--
 5 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index e25d9ebfdf89..8c0b62b732b7 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -247,6 +247,9 @@ unsigned long thp_vma_allowable_orders(struct vm_area_struct *vma,
 	if ((tva_flags & TVA_ENFORCE_SYSFS) && vma_is_anonymous(vma)) {
 		unsigned long mask = READ_ONCE(huge_anon_orders_always);
 
+		if (test_bit(MMF_DISABLE_ANON_MTHP, &vma->vm_mm->flags))
+			return 0;
+
 		if (vm_flags & VM_HUGEPAGE)
 			mask |= READ_ONCE(huge_anon_orders_madvise);
 		if (hugepage_global_always() ||
diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
index e62ff805cfc9..0935b4790e6f 100644
--- a/include/linux/sched/coredump.h
+++ b/include/linux/sched/coredump.h
@@ -56,6 +56,10 @@ static inline int get_dumpable(struct mm_struct *mm)
 # define MMF_DUMP_MASK_DEFAULT_ELF	0
 #endif
 					/* leave room for more dump flags */
+#define MMF_DISABLE_ANON_MTHP	13
+#define MMF_DISABLE_SHMEM_MTHP	14
+#define MMF_DISABLE_FILE_MTHP	15
+#define MMF_DISABLE_MTHP_MASK	(7 << MMF_DISABLE_ANON_MTHP)
 #define MMF_VM_MERGEABLE	16	/* KSM may merge identical pages */
 #define MMF_VM_HUGEPAGE		17	/* set when mm is available for
 					   khugepaged */
@@ -96,8 +100,9 @@ static inline int get_dumpable(struct mm_struct *mm)
 #define MMF_TOPDOWN_MASK	(1 << MMF_TOPDOWN)
 
 #define MMF_INIT_MASK		(MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\
-				 MMF_DISABLE_THP_MASK | MMF_HAS_MDWE_MASK |\
-				 MMF_VM_MERGE_ANY_MASK | MMF_TOPDOWN_MASK)
+				 MMF_DISABLE_THP_MASK | MMF_DISABLE_MTHP_MASK |\
+				 MMF_HAS_MDWE_MASK | MMF_VM_MERGE_ANY_MASK |\
+				 MMF_TOPDOWN_MASK)
 
 static inline unsigned long mmf_init_flags(unsigned long flags)
 {
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 35791791a879..584ac45f4ec8 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -178,6 +178,11 @@ struct prctl_mm_map {
 #define PR_GET_TID_ADDRESS	40
 
 #define PR_SET_THP_DISABLE	41
+# define PR_DISABLE_ANON_MTHP	(1UL << 1)
+# define PR_DISABLE_SHMEM_MTHP	(1UL << 2)
+# define PR_DISABLE_FILE_MTHP	(1UL << 3)
+# define DISABLE_MTHP_ALL_MASK	(PR_DISABLE_ANON_MTHP | PR_DISABLE_SHMEM_MTHP |\
+				 PR_DISABLE_FILE_MTHP)
 #define PR_GET_THP_DISABLE	42
 
 /*
diff --git a/kernel/sys.c b/kernel/sys.c
index 3a2df1bd9f64..06f2b1de46a7 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2627,17 +2627,41 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
 	case PR_GET_THP_DISABLE:
 		if (arg2 || arg3 || arg4 || arg5)
 			return -EINVAL;
-		error = !!test_bit(MMF_DISABLE_THP, &me->mm->flags);
+		if (test_bit(MMF_DISABLE_THP, &me->mm->flags))
+			error = 1;
+		if (test_bit(MMF_DISABLE_ANON_MTHP, &me->mm->flags))
+			error |= PR_DISABLE_ANON_MTHP;
+		if (test_bit(MMF_DISABLE_SHMEM_MTHP, &me->mm->flags))
+			error |= PR_DISABLE_SHMEM_MTHP;
+		if (test_bit(MMF_DISABLE_FILE_MTHP, &me->mm->flags))
+			error |= PR_DISABLE_FILE_MTHP;
 		break;
 	case PR_SET_THP_DISABLE:
-		if (arg3 || arg4 || arg5)
+		if (arg4 || arg5)
+			return -EINVAL;
+		if (arg3 && (arg3 & ~DISABLE_MTHP_ALL_MASK))
 			return -EINVAL;
 		if (mmap_write_lock_killable(me->mm))
 			return -EINTR;
-		if (arg2)
-			set_bit(MMF_DISABLE_THP, &me->mm->flags);
-		else
-			clear_bit(MMF_DISABLE_THP, &me->mm->flags);
+		if (arg2) {
+			if (!arg3)
+				set_bit(MMF_DISABLE_THP, &me->mm->flags);
+			if (arg3 & PR_DISABLE_ANON_MTHP)
+				set_bit(MMF_DISABLE_ANON_MTHP, &me->mm->flags);
+			if (arg3 & PR_DISABLE_SHMEM_MTHP)
+				set_bit(MMF_DISABLE_SHMEM_MTHP, &me->mm->flags);
+			if (arg3 & PR_DISABLE_FILE_MTHP)
+				set_bit(MMF_DISABLE_FILE_MTHP, &me->mm->flags);
+		} else {
+			if (!arg3)
+				clear_bit(MMF_DISABLE_THP, &me->mm->flags);
+			if (arg3 & PR_DISABLE_ANON_MTHP)
+				clear_bit(MMF_DISABLE_ANON_MTHP, &me->mm->flags);
+			if (arg3 & PR_DISABLE_SHMEM_MTHP)
+				clear_bit(MMF_DISABLE_SHMEM_MTHP, &me->mm->flags);
+			if (arg3 & PR_DISABLE_FILE_MTHP)
+				clear_bit(MMF_DISABLE_FILE_MTHP, &me->mm->flags);
+		}
 		mmap_write_unlock(me->mm);
 		break;
 	case PR_MPX_ENABLE_MANAGEMENT:
diff --git a/mm/shmem.c b/mm/shmem.c
index 5a77acf6ac6a..f4272883df77 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -556,7 +556,9 @@ static bool __shmem_is_huge(struct inode *inode, pgoff_t index,
 
 	if (!S_ISREG(inode->i_mode))
 		return false;
-	if (mm && ((vm_flags & VM_NOHUGEPAGE) || test_bit(MMF_DISABLE_THP, &mm->flags)))
+	if (mm && ((vm_flags & VM_NOHUGEPAGE) ||
+				test_bit(MMF_DISABLE_THP, &mm->flags) ||
+				test_bit(MMF_DISABLE_SHMEM_MTHP, &mm->flags)))
 		return false;
 	if (shmem_huge == SHMEM_HUGE_DENY)
 		return false;
@@ -1633,7 +1635,8 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	int order;
 
 	if ((vm_flags & VM_NOHUGEPAGE) ||
-	    test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
+	    test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags) ||
+	    test_bit(MMF_DISABLE_SHMEM_MTHP, &vma->vm_mm->flags))
 		return 0;
 
 	/* If the hardware/firmware marked hugepage support disabled. */
-- 
2.33.0





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux