[RFC 14/14] fork: Dynamic Kernel Stack accounting

From: Pasha Tatashin
Date: Mon Mar 11 2024 - 12:49:45 EST


Add an accounting of amount of stack pages that has been faulted is
currently in use.

Example use case:
$ cat /proc/vmstat | grep stack
nr_kernel_stack 18684
nr_dynamic_stacks_faults 156

The above shows that the kernel stacks use total 18684KiB, out of which
156KiB were faulted in.

Given that the pre-allocated stacks are 4KiB, we can determine the total
number of tasks:

tasks = (nr_kernel_stack - nr_dynamic_stacks_faults) / 4 = 4632.

The amount of kernel stack memory without dynamic stack on this machine
woud be:

4632 * 16 KiB = 74,112 KiB

Therefore, in this example dynamic stacks save: 55,428 KiB

Signed-off-by: Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
---
include/linux/mmzone.h | 3 +++
kernel/fork.c | 13 ++++++++++++-
mm/vmstat.c | 3 +++
3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index a497f189d988..ba4f1d148c3f 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -198,6 +198,9 @@ enum node_stat_item {
NR_FOLL_PIN_ACQUIRED, /* via: pin_user_page(), gup flag: FOLL_PIN */
NR_FOLL_PIN_RELEASED, /* pages returned via unpin_user_page() */
NR_KERNEL_STACK_KB, /* measured in KiB */
+#ifdef CONFIG_DYNAMIC_STACK
+ NR_DYNAMIC_STACKS_FAULTS_KB, /* KiB of faulted kernel stack memory */
+#endif
#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK)
NR_KERNEL_SCS_KB, /* measured in KiB */
#endif
diff --git a/kernel/fork.c b/kernel/fork.c
index 63e1fd661e17..2520583d160a 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -343,6 +343,9 @@ void dynamic_stack_refill_pages(void)

mod_lruvec_page_state(page, NR_KERNEL_STACK_KB,
PAGE_SIZE / 1024);
+ mod_lruvec_page_state(page,
+ NR_DYNAMIC_STACKS_FAULTS_KB,
+ PAGE_SIZE / 1024);

page = alloc_pages(THREADINFO_GFP & ~__GFP_ACCOUNT, 0);
if (unlikely(!page))
@@ -771,9 +774,17 @@ static void account_kernel_stack(struct task_struct *tsk, int account)
int i, nr_pages;

nr_pages = vm->nr_pages;
- for (i = 0; i < nr_pages; i++)
+ for (i = 0; i < nr_pages; i++) {
mod_lruvec_page_state(vm->pages[i], NR_KERNEL_STACK_KB,
account * (PAGE_SIZE / 1024));
+#ifdef CONFIG_DYNAMIC_STACK
+ if (i >= THREAD_PREALLOC_PAGES) {
+ mod_lruvec_page_state(vm->pages[i],
+ NR_DYNAMIC_STACKS_FAULTS_KB,
+ account * (PAGE_SIZE / 1024));
+ }
+#endif
+ }
} else {
void *stack = task_stack_page(tsk);

diff --git a/mm/vmstat.c b/mm/vmstat.c
index db79935e4a54..1ad6eede3d85 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1237,6 +1237,9 @@ const char * const vmstat_text[] = {
"nr_foll_pin_acquired",
"nr_foll_pin_released",
"nr_kernel_stack",
+#ifdef CONFIG_DYNAMIC_STACK
+ "nr_dynamic_stacks_faults",
+#endif
#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK)
"nr_shadow_call_stack",
#endif
--
2.44.0.278.ge034bb2e1d-goog