* [RFC PATCH 01/15] fork: Remove assumption that vm_area->nr_pages equals to THREAD_SIZE
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 02/15] fork: Don't assume fully populated stack during reuse Mostafa Saleh
` (13 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Pasha Tatashin,
Linus Walleij, David Stevens, Mostafa Saleh
From: Pasha Tatashin <pasha.tatashin@soleen.com>
In many places number of pages in the stack is determined via
(THREAD_SIZE / PAGE_SIZE). There is also a BUG_ON() that ensures that
(THREAD_SIZE / PAGE_SIZE) is indeed equals to vm_area->nr_pages.
In the next patches the kernel stack size will not be a compile
time constant and would be less than or equal THREAD_SIZE based
on a command line arg. Therefore, use vm_area->nr_pages to
determine the actual number of pages allocated in stack.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
[Rebased, also skipped intermediary helper variable nr_pages]
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: David Stevens <stevensd@google.com>
[Update commit message]
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
kernel/fork.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index 5ef413368912..2065693e762c 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -315,9 +315,7 @@ static int memcg_charge_kernel_stack(struct vm_struct *vm_area)
int ret;
int nr_charged = 0;
- BUG_ON(vm_area->nr_pages != THREAD_SIZE / PAGE_SIZE);
-
- for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) {
+ for (i = 0; i < vm_area->nr_pages; i++) {
ret = memcg_kmem_charge_page(vm_area->pages[i], GFP_KERNEL, 0);
if (ret)
goto err;
@@ -488,7 +486,7 @@ static void account_kernel_stack(struct task_struct *tsk, int account)
struct vm_struct *vm_area = task_stack_vm_area(tsk);
int i;
- for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++)
+ for (i = 0; i < vm_area->nr_pages; i++)
mod_lruvec_page_state(vm_area->pages[i], NR_KERNEL_STACK_KB,
account * (PAGE_SIZE / 1024));
} else {
@@ -509,7 +507,7 @@ void exit_task_stack_account(struct task_struct *tsk)
int i;
vm_area = task_stack_vm_area(tsk);
- for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++)
+ for (i = 0; i < vm_area->nr_pages; i++)
memcg_kmem_uncharge_page(vm_area->pages[i], 0);
}
}
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 02/15] fork: Don't assume fully populated stack during reuse
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 01/15] fork: Remove assumption that vm_area->nr_pages equals to THREAD_SIZE Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 03/15] fork: Move vm_stack to the beginning of the stack Mostafa Saleh
` (12 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, David Stevens,
Mostafa Saleh
From: David Stevens <stevensd@google.com>
In preparation for partially populated kernel stacks, don't assume
that vm_area->nr_pages matches THREAD_SIZE when clearing a stack
for reuse.
Note that the VA range is still THREAD_SIZE in that case but it
might not be fully allocated.
Signed-off-by: David Stevens <stevensd@google.com>
[Resolve conflict and updated commit msg]
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
kernel/fork.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index 2065693e762c..ec4431bf309a 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -335,11 +335,16 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
vm_area = alloc_thread_stack_node_from_cache(tsk, node);
if (vm_area) {
+ unsigned long offset = 0;
+
if (memcg_charge_kernel_stack(vm_area)) {
vfree(vm_area->addr);
return -ENOMEM;
}
+ if (!IS_ENABLED(CONFIG_STACK_GROWSUP))
+ offset = THREAD_SIZE - vm_area->nr_pages * PAGE_SIZE;
+
/* Reset stack metadata. */
if (!kasan_hw_tags_enabled())
kasan_unpoison_range(vm_area->addr, THREAD_SIZE);
@@ -347,7 +352,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
stack = kasan_reset_tag(vm_area->addr);
/* Clear stale pointers from reused stack. */
- clear_pages(vm_area->addr, vm_area->nr_pages);
+ clear_pages(vm_area->addr + offset, vm_area->nr_pages);
tsk->stack_vm_area = vm_area;
tsk->stack = stack;
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 03/15] fork: Move vm_stack to the beginning of the stack
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 01/15] fork: Remove assumption that vm_area->nr_pages equals to THREAD_SIZE Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 02/15] fork: Don't assume fully populated stack during reuse Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 04/15] fork: Separate vmap stack allocation and free calls Mostafa Saleh
` (11 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, David Stevens,
Mostafa Saleh
From: David Stevens <stevensd@google.com>
The vm_stack struct used to free stacks via an RCU callback is stored
directly in the stack being freed. Make sure it's stored at the
beginning of the stack regardless of stack growth direction, to avoid
faults on partially allocated stacks.
Signed-off-by: David Stevens <stevensd@google.com>
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
kernel/fork.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index ec4431bf309a..473878d7f62f 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -285,7 +285,12 @@ static void thread_stack_free_rcu(struct rcu_head *rh)
static void thread_stack_delayed_free(struct task_struct *tsk)
{
- struct vm_stack *vm_stack = tsk->stack;
+ struct vm_stack *vm_stack;
+
+ if (IS_ENABLED(CONFIG_STACK_GROWSUP))
+ vm_stack = tsk->stack;
+ else
+ vm_stack = tsk->stack + THREAD_SIZE - sizeof(*vm_stack);
vm_stack->stack_vm_area = tsk->stack_vm_area;
call_rcu(&vm_stack->rcu, thread_stack_free_rcu);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 04/15] fork: Separate vmap stack allocation and free calls
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (2 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 03/15] fork: Move vm_stack to the beginning of the stack Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 05/15] sched/task_stack: Add helpers for stack high/low Mostafa Saleh
` (10 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Pasha Tatashin,
Linus Walleij, David Stevens, Mostafa Saleh
From: Pasha Tatashin <pasha.tatashin@soleen.com>
In preparation for the partially populated stacks, separate out the
__vmalloc_node and vfree calls from the vmap based stack allocations.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
[Fix a bug in original patch: free_vmap_stack(vm_area->addr)]
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
[Add missing free_vmap_stack conversion, fix typos, rebase]
Signed-off-by: David Stevens <stevensd@google.com>
[Resolved conflict in alloc_thread_stack_node(), update commit msg]
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
kernel/fork.c | 35 +++++++++++++++++++++++------------
1 file changed, 23 insertions(+), 12 deletions(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index 473878d7f62f..3f1c46d8f7ec 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -272,6 +272,21 @@ static bool try_release_thread_stack_to_cache(struct vm_struct *vm_area)
return false;
}
+static inline struct vm_struct *alloc_vmap_stack(int node)
+{
+ void *stack;
+
+ stack = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN, GFP_VMAP_STACK,
+ node, __builtin_return_address(0));
+
+ return stack ? find_vm_area(stack) : NULL;
+}
+
+static inline void free_vmap_stack(struct vm_struct *vm_area)
+{
+ vfree(vm_area->addr);
+}
+
static void thread_stack_free_rcu(struct rcu_head *rh)
{
struct vm_stack *vm_stack = container_of(rh, struct vm_stack, rcu);
@@ -280,7 +295,7 @@ static void thread_stack_free_rcu(struct rcu_head *rh)
if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
return;
- vfree(vm_area->addr);
+ free_vmap_stack(vm_area);
}
static void thread_stack_delayed_free(struct task_struct *tsk)
@@ -307,7 +322,7 @@ static int free_vm_stack_cache(unsigned int cpu)
if (!vm_area)
continue;
- vfree(vm_area->addr);
+ free_vmap_stack(vm_area);
cached_vm_stack_areas[i] = NULL;
}
@@ -336,14 +351,14 @@ static int memcg_charge_kernel_stack(struct vm_struct *vm_area)
static int alloc_thread_stack_node(struct task_struct *tsk, int node)
{
struct vm_struct *vm_area;
- void *stack;
vm_area = alloc_thread_stack_node_from_cache(tsk, node);
if (vm_area) {
unsigned long offset = 0;
+ void *stack;
if (memcg_charge_kernel_stack(vm_area)) {
- vfree(vm_area->addr);
+ free_vmap_stack(vm_area);
return -ENOMEM;
}
@@ -364,15 +379,12 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
return 0;
}
- stack = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN,
- GFP_VMAP_STACK,
- node, __builtin_return_address(0));
- if (!stack)
+ vm_area = alloc_vmap_stack(node);
+ if (!vm_area)
return -ENOMEM;
- vm_area = find_vm_area(stack);
if (memcg_charge_kernel_stack(vm_area)) {
- vfree(stack);
+ free_vmap_stack(vm_area);
return -ENOMEM;
}
/*
@@ -381,8 +393,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
* so cache the vm_struct.
*/
tsk->stack_vm_area = vm_area;
- stack = kasan_reset_tag(stack);
- tsk->stack = stack;
+ tsk->stack = kasan_reset_tag(vm_area->addr);
return 0;
}
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 05/15] sched/task_stack: Add helpers for stack high/low
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (3 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 04/15] fork: Separate vmap stack allocation and free calls Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 06/15] exit: Don't assume the kernel stack size Mostafa Saleh
` (9 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh
Soon, memory mapped for a kernel stack can be less than THREAD_SIZE.
However, many parts of the kernel assume it is.
Add new helpers for the kernel stack that can be called regardless of
CONFIG_STACK_GROWSUP or the kernel stack size:
- task_stack_low(): Lowest usable address on the stack.
- task_stack_high(): Highest usable address on the stack.
- task_stack_size(): The size of a task kernel stack, it is still
THREAD_SIZE at the moment but that will change soon.
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
include/linux/sched/task_stack.h | 56 ++++++++++++++++++++++++++++++--
1 file changed, 53 insertions(+), 3 deletions(-)
diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h
index 1fab7e9043a3..4a808f9c4631 100644
--- a/include/linux/sched/task_stack.h
+++ b/include/linux/sched/task_stack.h
@@ -13,6 +13,8 @@
#ifdef CONFIG_THREAD_INFO_IN_TASK
+static __always_inline void *task_stack_low(const struct task_struct *task);
+
/*
* When accessing the stack of a non-current task that might exit, use
* try_get_task_stack() instead. task_stack_page will return a pointer
@@ -30,7 +32,7 @@ static __always_inline unsigned long *end_of_stack(const struct task_struct *tas
#ifdef CONFIG_STACK_GROWSUP
return (unsigned long *)((unsigned long)task->stack + THREAD_SIZE) - 1;
#else
- return task->stack;
+ return task_stack_low(task);
#endif
}
@@ -64,6 +66,53 @@ static inline unsigned long *end_of_stack(const struct task_struct *p)
#endif
+/*
+ * Kernel stack layout, higher addresses at the top. task_stack_page() is
+ * the start of the THREAD_SIZE area, task_stack_low() => task_stack_high()
+ * is the usable stack.
+ *
+ * Case 1) Stack grows down: Only the top task_stack_size() bytes are mapped:
+ *
+ * +-------------+ <- task_stack_high() = task_stack_page() + THREAD_SIZE
+ * | stack |
+ * | | | task_stack_size() bytes
+ * | v |
+ * +-------------+ <- task_stack_low() = end_of_stack() (*)
+ * | may be |
+ * | unmapped | THREAD_SIZE - task_stack_size() bytes
+ * +-------------+ <- task_stack_page()
+ *
+ * (*) Depending on CONFIG_THREAD_INFO_IN_TASK, end_of_stack() might be just
+ * above it.
+ *
+ * Case 2) Stack grows up (CONFIG_STACK_GROWSUP): The whole area is used.
+ * No support for partially mapped stacks.
+ *
+ * +-------------+ <- task_stack_high() = task_stack_page() + THREAD_SIZE
+ * | ^ | end_of_stack() is the last unsigned long below it
+ * | | |
+ * | stack | THREAD_SIZE bytes
+ * +-------------+ <- task_stack_low() = task_stack_page()
+ */
+static __always_inline unsigned long task_stack_size(const struct task_struct *task)
+{
+ return THREAD_SIZE;
+}
+
+static __always_inline void *task_stack_low(const struct task_struct *task)
+{
+#if defined(CONFIG_STACK_GROWSUP)
+ return task_stack_page(task);
+#else
+ return task_stack_page(task) + THREAD_SIZE - task_stack_size(task);
+#endif
+}
+
+static __always_inline void *task_stack_high(const struct task_struct *task)
+{
+ return task_stack_low(task) + task_stack_size(task);
+}
+
#ifdef CONFIG_THREAD_INFO_IN_TASK
static inline void *try_get_task_stack(struct task_struct *tsk)
{
@@ -88,10 +137,11 @@ void exit_task_stack_account(struct task_struct *tsk);
static inline int object_is_on_stack(const void *obj)
{
- void *stack = task_stack_page(current);
+ void *stack_base = task_stack_low(current);
+ void *stack_end = task_stack_high(current);
obj = kasan_reset_tag(obj);
- return (obj >= stack) && (obj < (stack + THREAD_SIZE));
+ return (obj >= stack_base) && (obj < stack_end);
}
extern void thread_stack_cache_init(void);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 06/15] exit: Don't assume the kernel stack size
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (4 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 05/15] sched/task_stack: Add helpers for stack high/low Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 07/15] usercopy: " Mostafa Saleh
` (8 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh
check_stack_usage() assumes that the kernel stack size is THREAD_SIZE.
The allocated kernel stack size may not be THREAD_SIZE anymore, rely
on the newly introduced helpers task_stack_size()
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
kernel/exit.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/exit.c b/kernel/exit.c
index 424c44a42a4d..899d3bf048d4 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -883,7 +883,7 @@ static void check_stack_usage(void)
unsigned long free;
free = stack_not_used(current);
- kstack_histogram(THREAD_SIZE - free);
+ kstack_histogram(task_stack_size(current) - free);
if (free >= lowest_to_date)
return;
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 07/15] usercopy: Don't assume the kernel stack size
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (5 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 06/15] exit: Don't assume the kernel stack size Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 08/15] mm: kmemleak: " Mostafa Saleh
` (7 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh
check_stack_object() assumes that the kernel stack size is
THREAD_SIZE.
The allocated kernel stack size may not be THREAD_SIZE anymore, rely
on the newly introduced helper task_stack_{low, high}()
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
mm/usercopy.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mm/usercopy.c b/mm/usercopy.c
index 5de7a518b1b1..f31a42b9f9c4 100644
--- a/mm/usercopy.c
+++ b/mm/usercopy.c
@@ -36,8 +36,8 @@
*/
static noinline int check_stack_object(const void *obj, unsigned long len)
{
- const void * const stack = task_stack_page(current);
- const void * const stackend = stack + THREAD_SIZE;
+ const void * const stack = task_stack_low(current);
+ const void * const stackend = task_stack_high(current);
int ret;
/* Object is not on the stack at all. */
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 08/15] mm: kmemleak: Don't assume the kernel stack size
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (6 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 07/15] usercopy: " Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 09/15] arm64: " Mostafa Saleh
` (6 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh
kmemleak_scan_task_stacks() assumes that the kernel stack size is
THREAD_SIZE.
The allocated kernel stack size may not be THREAD_SIZE anymore, rely
on the newly introduced helpers task_stack_{low, high}()
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
mm/kmemleak.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 8fa409a4f9fb..72e2c628a20b 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -1749,7 +1749,7 @@ static void kmemleak_scan_task_stacks(void)
void *stack = try_get_task_stack(p);
if (stack) {
- stop = scan_block(stack, stack + THREAD_SIZE, NULL);
+ stop = scan_block(task_stack_low(p), task_stack_high(p), NULL);
put_task_stack(p);
}
put_task_struct(p);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 09/15] arm64: Don't assume the kernel stack size
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (7 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 08/15] mm: kmemleak: " Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 10/15] mm/vmalloc: Add a get_vm_area_node() Mostafa Saleh
` (5 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh
stacktrace and panic functions assume that the kernel stack size is
THREAD_SIZE.
The allocated kernel stack size may not be THREAD_SIZE anymore, rely
on the newly introduced helpers task_stack_{low, high}()
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
arch/arm64/include/asm/stacktrace.h | 4 ++--
arch/arm64/kernel/traps.c | 5 +++--
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h
index eb18d7cee62f..cbc93cf213a7 100644
--- a/arch/arm64/include/asm/stacktrace.h
+++ b/arch/arm64/include/asm/stacktrace.h
@@ -41,8 +41,8 @@ static inline bool on_irq_stack(unsigned long sp, unsigned long size)
static inline struct stack_info stackinfo_get_task(const struct task_struct *tsk)
{
- unsigned long low = (unsigned long)task_stack_page(tsk);
- unsigned long high = low + THREAD_SIZE;
+ unsigned long low = (unsigned long)task_stack_low(tsk);
+ unsigned long high = (unsigned long)task_stack_high(tsk);
return (struct stack_info) {
.low = low,
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index ce0e213d772d..b91b154843ef 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -902,7 +902,8 @@ DEFINE_PER_CPU(unsigned long [KERNEL_EXC_STACK_SIZE/sizeof(long)],
void __noreturn panic_bad_stack(struct pt_regs *regs, unsigned long esr, unsigned long far)
{
- unsigned long tsk_stk = (unsigned long)current->stack;
+ unsigned long tsk_stk = (unsigned long)task_stack_low(current);
+ unsigned long tsk_stk_end = (unsigned long)task_stack_high(current);
unsigned long irq_stk = (unsigned long)this_cpu_read(irq_stack_ptr);
unsigned long exc_stk = (unsigned long)this_cpu_ptr(kernel_exception_stack);
@@ -913,7 +914,7 @@ void __noreturn panic_bad_stack(struct pt_regs *regs, unsigned long esr, unsigne
pr_emerg("FAR: 0x%016lx\n", far);
pr_emerg("Task stack: [0x%016lx..0x%016lx]\n",
- tsk_stk, tsk_stk + THREAD_SIZE);
+ tsk_stk, tsk_stk_end);
pr_emerg("IRQ stack: [0x%016lx..0x%016lx]\n",
irq_stk, irq_stk + IRQ_STACK_SIZE);
pr_emerg("Exception stack: [0x%016lx..0x%016lx]\n",
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 10/15] mm/vmalloc: Add a get_vm_area_node()
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (8 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 09/15] arm64: " Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 11/15] fork: Move vmap stack freeing to work queue Mostafa Saleh
` (4 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Pasha Tatashin,
Linus Walleij, David Stevens, Mostafa Saleh
From: Pasha Tatashin <pasha.tatashin@soleen.com>
get_vm_area_node()
Unlike the other public get_vm_area_* variants, this one accepts node
from which to allocate data structure, and also the align, which allows
to create vm area with a specific alignment.
This call is going to be used by partially populated stacks in order
to allocate the VA space for the kernel stacks that might be larger
than the memory mapped behind it.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
[Switched to vmap_pages_range instead of noflush variant, fix typos]
Signed-off-by: David Stevens <stevensd@google.com>
[vmap_pages_range() already in the header, and remove the export as it
was not needed, update commit msg]
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
include/linux/vmalloc.h | 3 +++
mm/vmalloc.c | 24 ++++++++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index aed121d729b0..e6a9c1bbe789 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -250,6 +250,9 @@ extern struct vm_struct *__get_vm_area_caller(unsigned long size,
unsigned long flags,
unsigned long start, unsigned long end,
const void *caller);
+struct vm_struct *get_vm_area_node(unsigned long size, unsigned long align,
+ unsigned long flags, int node, gfp_t gfp,
+ const void *caller);
void free_vm_area(struct vm_struct *area);
extern struct vm_struct *remove_vm_area(const void *addr);
extern struct vm_struct *find_vm_area(const void *addr);
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index bea9f76ed7e7..9f4634c7977e 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3301,6 +3301,30 @@ struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,
NUMA_NO_NODE, GFP_KERNEL, caller);
}
+/**
+ * get_vm_area_node - reserve a contiguous and aligned kernel virtual area
+ * @size: size of the area
+ * @align: alignment of the start address of the area
+ * @flags: %VM_IOREMAP for I/O mappings
+ * @node: NUMA node from which to allocate the area data structure
+ * @gfp: Flags to pass to the allocator
+ * @caller: Caller to be stored in the vm area data structure
+ *
+ * Search for an area of @size/align in the kernel virtual mapping area and
+ * reserve it for our purposes. Returns the area descriptor on success or %NULL
+ * on failure.
+ *
+ * Return: the area descriptor on success or %NULL on failure.
+ */
+struct vm_struct *get_vm_area_node(unsigned long size, unsigned long align,
+ unsigned long flags, int node, gfp_t gfp,
+ const void *caller)
+{
+ return __get_vm_area_node(size, align, PAGE_SHIFT, flags,
+ VMALLOC_START, VMALLOC_END,
+ node, gfp, caller);
+}
+
/**
* find_vm_area - find a continuous kernel virtual area
* @addr: base address
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 11/15] fork: Move vmap stack freeing to work queue
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (9 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 10/15] mm/vmalloc: Add a get_vm_area_node() Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 12/15] sched/task_stack: Introduce ARCH_HAS_VARIABLE_STACK_SIZE Mostafa Saleh
` (3 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, David Stevens,
Mostafa Saleh
From: David Stevens <stevensd@google.com>
For vmap stacks not immediately released into the stack cache, free them
in a workqueue instead of via call_rcu(). In an RCU context, vfree
already schedules the actual freeing on the per-cpu system workqueue, so
this change only affects when exactly the second attempt to put the
stack into the stack cache occurs.
Moving freeing to a workqueue will allow for freeing partially
populated stacks in a sleepable context (for remove_vm_area),
rather than relying on vfree dispatching to a workqueue via
vfree_atomic.
Signed-off-by: David Stevens <stevensd@google.com>
[Use system_percpu_wq instead of system_wq as it is deprecated and
update commit msg]
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
kernel/fork.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index 3f1c46d8f7ec..ca8e68882316 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -208,7 +208,7 @@ static DEFINE_PER_CPU(struct vm_struct *, cached_stacks[NR_CACHED_STACKS]);
#define GFP_VMAP_STACK (GFP_KERNEL | __GFP_ZERO | __GFP_SKIP_KASAN)
struct vm_stack {
- struct rcu_head rcu;
+ struct rcu_work work;
struct vm_struct *stack_vm_area;
};
@@ -287,9 +287,9 @@ static inline void free_vmap_stack(struct vm_struct *vm_area)
vfree(vm_area->addr);
}
-static void thread_stack_free_rcu(struct rcu_head *rh)
+static void thread_stack_free_work(struct work_struct *work)
{
- struct vm_stack *vm_stack = container_of(rh, struct vm_stack, rcu);
+ struct vm_stack *vm_stack = container_of(to_rcu_work(work), struct vm_stack, work);
struct vm_struct *vm_area = vm_stack->stack_vm_area;
if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
@@ -308,7 +308,8 @@ static void thread_stack_delayed_free(struct task_struct *tsk)
vm_stack = tsk->stack + THREAD_SIZE - sizeof(*vm_stack);
vm_stack->stack_vm_area = tsk->stack_vm_area;
- call_rcu(&vm_stack->rcu, thread_stack_free_rcu);
+ INIT_RCU_WORK(&vm_stack->work, thread_stack_free_work);
+ queue_rcu_work(system_percpu_wq, &vm_stack->work);
}
static int free_vm_stack_cache(unsigned int cpu)
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 12/15] sched/task_stack: Introduce ARCH_HAS_VARIABLE_STACK_SIZE
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (10 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 11/15] fork: Move vmap stack freeing to work queue Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 20:37 ` Randy Dunlap
2026-09-28 17:41 ` [RFC PATCH 13/15] fork: Implement partial VMAP stack allocation Mostafa Saleh
` (2 subsequent siblings)
14 siblings, 1 reply; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh
When CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE is enabled, the kernel stack
may not be fully populated with the THREAD_SIZE virtual area.
Teach task_stack_size() to return the size of the vm area and not a
fixed size.
This propagates to end_of_stack() which makes functions like
stack_not_used() and set_task_stack_end_magic() to use the lowest
valid stack address instead of THREAD_SIZE which would cause data
aborts.
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
arch/Kconfig | 10 ++++++++++
include/linux/sched/task_stack.h | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig
index 45c657772362..d6fa2ccb0599 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -431,6 +431,16 @@ config HAVE_ASM_MODVERSIONS
<asm/asm-prototypes.h> to support the module versioning for symbols
exported from assembly code.
+config ARCH_HAS_VARIABLE_STACK_SIZE
+ bool
+ depends on VMAP_STACK
+ depends on !STACK_GROWSUP
+ depends on THREAD_INFO_IN_TASK
+ help
+ An arch should select this if it supports dynamically configuring the
+ kernel stack size.
+ For example the kernel stack size can be from the command line.
+
config HAVE_REGS_AND_STACK_ACCESS_API
bool
help
diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h
index 4a808f9c4631..07d73adaecd1 100644
--- a/include/linux/sched/task_stack.h
+++ b/include/linux/sched/task_stack.h
@@ -10,6 +10,7 @@
#include <linux/magic.h>
#include <linux/refcount.h>
#include <linux/kasan.h>
+#include <linux/vmalloc.h>
#ifdef CONFIG_THREAD_INFO_IN_TASK
@@ -96,6 +97,10 @@ static inline unsigned long *end_of_stack(const struct task_struct *p)
*/
static __always_inline unsigned long task_stack_size(const struct task_struct *task)
{
+#ifdef CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE
+ if (task->stack_vm_area)
+ return task->stack_vm_area->nr_pages * PAGE_SIZE;
+#endif
return THREAD_SIZE;
}
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [RFC PATCH 12/15] sched/task_stack: Introduce ARCH_HAS_VARIABLE_STACK_SIZE
2026-09-28 17:41 ` [RFC PATCH 12/15] sched/task_stack: Introduce ARCH_HAS_VARIABLE_STACK_SIZE Mostafa Saleh
@ 2026-09-28 20:37 ` Randy Dunlap
2026-09-29 10:27 ` Mostafa Saleh
0 siblings, 1 reply; 18+ messages in thread
From: Randy Dunlap @ 2026-09-28 20:37 UTC (permalink / raw)
To: Mostafa Saleh, linux-doc, linux-kernel, linux-arm-kernel,
linux-mm, linux-hardening, linux-rt-devel
Cc: corbet, skhan, catalin.marinas, will, mark.rutland, akpm, urezki,
mingo, peterz, juri.lelli, vincent.guittot, dietmar.eggemann,
rostedt, bsegall, mgorman, vschneid, kprateek.nayak, kees, david,
ljs, liam, vbabka, rppt, surenb, mhocko, gustavoars, bigeasy,
clrkwllms
On 9/28/26 10:41 AM, Mostafa Saleh wrote:
> When CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE is enabled, the kernel stack
> may not be fully populated with the THREAD_SIZE virtual area.
>
> Teach task_stack_size() to return the size of the vm area and not a
> fixed size.
>
> This propagates to end_of_stack() which makes functions like
> stack_not_used() and set_task_stack_end_magic() to use the lowest
> valid stack address instead of THREAD_SIZE which would cause data
> aborts.
>
> Signed-off-by: Mostafa Saleh <smostafa@google.com>
> ---
> arch/Kconfig | 10 ++++++++++
> include/linux/sched/task_stack.h | 5 +++++
> 2 files changed, 15 insertions(+)
>
> diff --git a/arch/Kconfig b/arch/Kconfig
> index 45c657772362..d6fa2ccb0599 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -431,6 +431,16 @@ config HAVE_ASM_MODVERSIONS
> <asm/asm-prototypes.h> to support the module versioning for symbols
> exported from assembly code.
>
> +config ARCH_HAS_VARIABLE_STACK_SIZE
> + bool
> + depends on VMAP_STACK
> + depends on !STACK_GROWSUP
> + depends on THREAD_INFO_IN_TASK
> + help
> + An arch should select this if it supports dynamically configuring the
> + kernel stack size.
> + For example the kernel stack size can be from the command line.
can be set from the command line.
?
> +
> config HAVE_REGS_AND_STACK_ACCESS_API
> bool
> help
> diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h
> index 4a808f9c4631..07d73adaecd1 100644
> --- a/include/linux/sched/task_stack.h
> +++ b/include/linux/sched/task_stack.h
> @@ -10,6 +10,7 @@
> #include <linux/magic.h>
> #include <linux/refcount.h>
> #include <linux/kasan.h>
> +#include <linux/vmalloc.h>
>
> #ifdef CONFIG_THREAD_INFO_IN_TASK
>
> @@ -96,6 +97,10 @@ static inline unsigned long *end_of_stack(const struct task_struct *p)
> */
> static __always_inline unsigned long task_stack_size(const struct task_struct *task)
> {
> +#ifdef CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE
> + if (task->stack_vm_area)
> + return task->stack_vm_area->nr_pages * PAGE_SIZE;
> +#endif
> return THREAD_SIZE;
> }
>
--
~Randy
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [RFC PATCH 12/15] sched/task_stack: Introduce ARCH_HAS_VARIABLE_STACK_SIZE
2026-09-28 20:37 ` Randy Dunlap
@ 2026-09-29 10:27 ` Mostafa Saleh
0 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-29 10:27 UTC (permalink / raw)
To: Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel, corbet, skhan, catalin.marinas,
will, mark.rutland, akpm, urezki, mingo, peterz, juri.lelli,
vincent.guittot, dietmar.eggemann, rostedt, bsegall, mgorman,
vschneid, kprateek.nayak, kees, david, ljs, liam, vbabka, rppt,
surenb, mhocko, gustavoars, bigeasy, clrkwllms
On Mon, Sep 28, 2026 at 01:37:37PM -0700, Randy Dunlap wrote:
>
>
> On 9/28/26 10:41 AM, Mostafa Saleh wrote:
> > When CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE is enabled, the kernel stack
> > may not be fully populated with the THREAD_SIZE virtual area.
> >
> > Teach task_stack_size() to return the size of the vm area and not a
> > fixed size.
> >
> > This propagates to end_of_stack() which makes functions like
> > stack_not_used() and set_task_stack_end_magic() to use the lowest
> > valid stack address instead of THREAD_SIZE which would cause data
> > aborts.
> >
> > Signed-off-by: Mostafa Saleh <smostafa@google.com>
> > ---
> > arch/Kconfig | 10 ++++++++++
> > include/linux/sched/task_stack.h | 5 +++++
> > 2 files changed, 15 insertions(+)
> >
> > diff --git a/arch/Kconfig b/arch/Kconfig
> > index 45c657772362..d6fa2ccb0599 100644
> > --- a/arch/Kconfig
> > +++ b/arch/Kconfig
> > @@ -431,6 +431,16 @@ config HAVE_ASM_MODVERSIONS
> > <asm/asm-prototypes.h> to support the module versioning for symbols
> > exported from assembly code.
> >
> > +config ARCH_HAS_VARIABLE_STACK_SIZE
> > + bool
> > + depends on VMAP_STACK
> > + depends on !STACK_GROWSUP
> > + depends on THREAD_INFO_IN_TASK
> > + help
> > + An arch should select this if it supports dynamically configuring the
> > + kernel stack size.
> > + For example the kernel stack size can be from the command line.
>
> can be set from the command line.
> ?
>
I will fix it in v2.
Thanks,
Mostafa
> > +
> > config HAVE_REGS_AND_STACK_ACCESS_API
> > bool
> > help
> > diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h
> > index 4a808f9c4631..07d73adaecd1 100644
> > --- a/include/linux/sched/task_stack.h
> > +++ b/include/linux/sched/task_stack.h
> > @@ -10,6 +10,7 @@
> > #include <linux/magic.h>
> > #include <linux/refcount.h>
> > #include <linux/kasan.h>
> > +#include <linux/vmalloc.h>
> >
> > #ifdef CONFIG_THREAD_INFO_IN_TASK
> >
> > @@ -96,6 +97,10 @@ static inline unsigned long *end_of_stack(const struct task_struct *p)
> > */
> > static __always_inline unsigned long task_stack_size(const struct task_struct *task)
> > {
> > +#ifdef CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE
> > + if (task->stack_vm_area)
> > + return task->stack_vm_area->nr_pages * PAGE_SIZE;
> > +#endif
> > return THREAD_SIZE;
> > }
> >
>
> --
> ~Randy
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH 13/15] fork: Implement partial VMAP stack allocation
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (11 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 12/15] sched/task_stack: Introduce ARCH_HAS_VARIABLE_STACK_SIZE Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 14/15] arm64: mm: Relax kernel stack alignment Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 15/15] arm64: mm: Set stack size from the kernel command line Mostafa Saleh
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh,
Pasha Tatashin, Linus Walleij, David Stevens
When CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE is enabled, architectures can
allocate a VMAP stack that is smaller than the full THREAD_SIZE.
This introduces arch abstraction arch_vm_stack_pages() which defaults
to THREAD_SIZE / PAGE_SIZE.
During stack allocation, the kernel will map only the requested number
of pages at the top of the THREAD_SIZE virtual area (as the stack
grows down), leaving the bottom unmapped. This saves memory while
retaining the same virtual alignment and guard page overflow detection
characteristics.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
[Rebased, used vm_area->nr_pages directly in one instance]
[Depends on !PREEMPT_RT]
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
[Fix races around accounting]
[Use GFP_ATOMIC when executing in the scheduler]
[Depend on INIT_STACK_ALL_* config]
[Fix bugs in some error paths and edge cases]
[Don't cache partially faulted stacks]
[Added out-var to tell if address is on target stack]
Signed-off-by: David Stevens <stevensd@google.com>
[Remove dynamic stack related code, and update commit message]
[Fix memory leak in error path of alloc_vmap_stack()] and use
VM_UNINITIALIZED before installing the pages]
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
include/linux/thread_info.h | 8 +++++
kernel/fork.c | 68 +++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+)
diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h
index 307b8390fc67..8f052ec82402 100644
--- a/include/linux/thread_info.h
+++ b/include/linux/thread_info.h
@@ -92,6 +92,14 @@ static inline long set_restart_fn(struct restart_block *restart,
#define THREAD_ALIGN THREAD_SIZE
#endif
+/*
+ * Arch selecting CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE should override this
+ * with kernel stack size.
+ */
+#ifndef arch_vm_stack_pages
+#define arch_vm_stack_pages() (THREAD_SIZE / PAGE_SIZE)
+#endif
+
#define THREADINFO_GFP (GFP_KERNEL_ACCOUNT | __GFP_ZERO | __GFP_SKIP_KASAN)
/*
diff --git a/kernel/fork.c b/kernel/fork.c
index ca8e68882316..66fbad3d4cbd 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -119,6 +119,8 @@
/* For dup_mmap(). */
#include "../mm/internal.h"
+/* For clear_vm_uninitialized_flag(). */
+#include "../mm/vmalloc.h"
#include <trace/events/sched.h>
@@ -272,6 +274,71 @@ static bool try_release_thread_stack_to_cache(struct vm_struct *vm_area)
return false;
}
+#ifdef CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE
+static struct vm_struct *alloc_vmap_stack(int node)
+{
+ gfp_t gfp = GFP_VMAP_STACK;
+ unsigned long addr, end;
+ struct vm_struct *vm_area;
+ int err, i;
+
+ vm_area = get_vm_area_node(THREAD_SIZE, THREAD_ALIGN,
+ VM_MAP | VM_UNINITIALIZED, node, gfp,
+ __builtin_return_address(0));
+ if (!vm_area)
+ return NULL;
+
+ vm_area->pages = kmalloc_node(sizeof(void *) * (THREAD_SIZE >> PAGE_SHIFT),
+ gfp, node);
+ if (!vm_area->pages)
+ goto cleanup_err;
+
+ for (i = 0; i < arch_vm_stack_pages(); i++) {
+ vm_area->pages[i] = alloc_pages_node(node, gfp, 0);
+ if (!vm_area->pages[i])
+ goto cleanup_err;
+ vm_area->nr_pages++;
+ mod_lruvec_page_state(vm_area->pages[i], NR_VMALLOC, 1);
+ }
+
+ end = (unsigned long)kasan_reset_tag(vm_area->addr) + THREAD_SIZE;
+ addr = end - (arch_vm_stack_pages() * PAGE_SIZE);
+
+ err = vmap_pages_range(addr, end, PAGE_KERNEL, vm_area->pages, PAGE_SHIFT);
+ if (err)
+ goto cleanup_err;
+
+ clear_vm_uninitialized_flag(vm_area);
+ return vm_area;
+
+cleanup_err:
+ remove_vm_area(vm_area->addr);
+ if (vm_area->pages) {
+ for (i = 0; i < vm_area->nr_pages; i++) {
+ mod_lruvec_page_state(vm_area->pages[i], NR_VMALLOC, -1);
+ __free_page(vm_area->pages[i]);
+ }
+ kfree(vm_area->pages);
+ }
+ kfree(vm_area);
+ return NULL;
+}
+
+static void free_vmap_stack(struct vm_struct *vm_area)
+{
+ int i;
+
+ remove_vm_area(vm_area->addr);
+
+ for (i = 0; i < vm_area->nr_pages; i++) {
+ mod_lruvec_page_state(vm_area->pages[i], NR_VMALLOC, -1);
+ __free_page(vm_area->pages[i]);
+ }
+
+ kfree(vm_area->pages);
+ kfree(vm_area);
+}
+#else
static inline struct vm_struct *alloc_vmap_stack(int node)
{
void *stack;
@@ -286,6 +353,7 @@ static inline void free_vmap_stack(struct vm_struct *vm_area)
{
vfree(vm_area->addr);
}
+#endif /* CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE */
static void thread_stack_free_work(struct work_struct *work)
{
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 14/15] arm64: mm: Relax kernel stack alignment
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (12 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 13/15] fork: Implement partial VMAP stack allocation Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
2026-09-28 17:41 ` [RFC PATCH 15/15] arm64: mm: Set stack size from the kernel command line Mostafa Saleh
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh
On the kernel entry it was not possible to use the stack until it
was checked for overflow which was done by a clever trick relying
on aligning the kernel stack to double it's size, so if the
bit at THREAD_SHIFT was set it means that the stack pointer has
overflowed.
Now, we can easily switch the sp to sp_el1 which is the overflow
stack, push some registers and execute more complex flow.
Rework the kernel entry code to eliminate the tbnz check and the
alignment requirement.
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
arch/arm64/include/asm/memory.h | 7 +--
arch/arm64/kernel/entry.S | 87 ++++++++++++++++++++++++++++-----
2 files changed, 77 insertions(+), 17 deletions(-)
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 93ce6ef65573..4545d9b39bd5 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -130,12 +130,7 @@
#define THREAD_SIZE (UL(1) << THREAD_SHIFT)
-/*
- * By aligning VMAP'd stacks to 2 * THREAD_SIZE, we can detect overflow by
- * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the entry
- * assembly.
- */
-#define THREAD_ALIGN (2 * THREAD_SIZE)
+#define THREAD_ALIGN THREAD_SIZE
#define IRQ_STACK_SIZE THREAD_SIZE
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index a31ef890a2ee..ea733b673970 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -62,15 +62,33 @@
sub sp, sp, #PT_REGS_SIZE
/*
- * Test whether the SP has overflowed, without corrupting a GPR.
- * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT)
- * should always be zero.
+ * Test whether the SP has overflowed, using the overflow stack.
+ * As we do not know if the CPU was in process or irq context, first
+ * check against the task stack if that failed it means it might have
+ * been an interrupt, so check against an interrupt stack, if both
+ * failed it means that at least one of the stacks overflowed.
*/
- add sp, sp, x0 // sp' = sp + x0
- sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp
- tbnz x0, #THREAD_SHIFT, __bad_stack
- sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0
- sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp
+ msr spsel, #1
+ stp x0, x1, [sp, #-16]!
+
+ mrs x0, tpidrro_el0
+ ldr x0, [x0, #TSK_STACK]
+ msr spsel, #0
+ mov x1, sp
+ msr spsel, #1
+ sub x1, x1, x0
+ cmp x1, #THREAD_SIZE
+ b.lo .Lstack_ok\@
+
+ /* Check whether we are on the IRQ, SDEI or EFI stacks. */
+ stp x2, x30, [sp, #-16]!
+ bl __check_ext_stacks
+ ldp x2, x30, [sp], #16
+
+.Lstack_ok\@:
+ ldp x0, x1, [sp], #16
+ msr spsel, #0
+
b el1t_\regsize\()_\label
.endm
@@ -528,13 +546,60 @@ SYM_CODE_START(vectors)
kernel_ventry 0, t, 32, error // Error 32-bit EL0
SYM_CODE_END(vectors)
+
+ .macro check_stack_overflow type, ptr, size
+ .ifc \type, percpu
+ ldr_this_cpu x1, \ptr, x0
+ .else
+ adr_l x1, \ptr
+ ldr x1, [x1]
+ .endif
+ cbz x1, 1f
+ .ifc \type, top
+ sub x1, x1, #\size
+ .endif
+ msr spsel, #0
+ mov x0, sp
+ msr spsel, #1
+ sub x0, x0, x1
+ cmp x0, #\size
+ b.lo 2f
+1:
+ .endm
+
+SYM_CODE_START_LOCAL(__check_ext_stacks)
+ /* IRQ stack check */
+ check_stack_overflow percpu, irq_stack_ptr, IRQ_STACK_SIZE
+#ifdef CONFIG_ARM_SDE_INTERFACE
+ /* SDEI normal stack check */
+ check_stack_overflow percpu, sdei_stack_normal_ptr, IRQ_STACK_SIZE
+ /* SDEI critical stack check */
+ check_stack_overflow percpu, sdei_stack_critical_ptr, IRQ_STACK_SIZE
+#endif
+
+#ifdef CONFIG_EFI
+ /* EFI runtime stack check */
+ check_stack_overflow top, efi_rt_stack_top, THREAD_SIZE
+#endif
+
+ /* All checks failed => it's a real overflow */
+ ldp x2, x30, [sp], #16
+ b __bad_stack
+
+2:
+ /* A check succeeded => return to kernel_ventry */
+ ret
+SYM_CODE_END(__check_ext_stacks)
+
SYM_CODE_START_LOCAL(__bad_stack)
/*
* We detected an overflow in kernel_ventry.
- * Restore SP and X0.
+ * Restore X0 and X1, and pop the overflow stack.
*/
- sub x0, sp, x0
- sub sp, sp, x0
+ ldp x0, x1, [sp], #16
+
+ /* Restore SP_EL0 */
+ msr spsel, #0
add sp, sp, #PT_REGS_SIZE
/* Switch to the overflow stack */
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [RFC PATCH 15/15] arm64: mm: Set stack size from the kernel command line
2026-09-28 17:41 [RFC PATCH 00/15] arm64: Set kernel stack size from cmdline Mostafa Saleh
` (13 preceding siblings ...)
2026-09-28 17:41 ` [RFC PATCH 14/15] arm64: mm: Relax kernel stack alignment Mostafa Saleh
@ 2026-09-28 17:41 ` Mostafa Saleh
14 siblings, 0 replies; 18+ messages in thread
From: Mostafa Saleh @ 2026-09-28 17:41 UTC (permalink / raw)
To: linux-doc, linux-kernel, linux-arm-kernel, linux-mm,
linux-hardening, linux-rt-devel
Cc: corbet, skhan, rdunlap, catalin.marinas, will, mark.rutland,
akpm, urezki, mingo, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, kees, david, ljs, liam, vbabka, rppt, surenb,
mhocko, gustavoars, bigeasy, clrkwllms, Mostafa Saleh
Select CONFIG_ARCH_HAS_VARIABLE_STACK_SIZE for arm64 and override
arch_vm_stack_pages() to a value set from the command line, this
value have the following properties:
- Aligned to page size (doesn't need to be a power of 2), so it can
be 12kB for example
- Min value is chosen to be 8kB
- Max value will be the default stack size on arm64 which is used to
allocate the VA range for the kernel stack.
Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
.../admin-guide/kernel-parameters.txt | 7 ++++++
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/memory.h | 12 ++++++++++
arch/arm64/kernel/entry.S | 10 +++++---
arch/arm64/kernel/setup.c | 24 +++++++++++++++++++
5 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 33cd30996e47..e6ef22f01e01 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2920,6 +2920,13 @@ Kernel parameters
keepinitrd [HW,ARM] See retain_initrd.
+ kernel_stack_pages=
+ [ARM64,EARLY]
+ Format: <integer>
+ Number of pages mapped for each task kernel stack.
+ The virtual area stays THREAD_SIZE and only its top
+ pages are mapped.
+
kernelcore= [KNL,X86,PPC,EARLY]
Format: nn[KMGTPE] | nn% | "mirror"
This parameter specifies the amount of memory usable by
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 020fcaf36b1b..5e09ae904e34 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -58,6 +58,7 @@ config ARM64
select ARCH_HAS_SYSCALL_WRAPPER
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_HAS_ZONE_DMA_SET if EXPERT
+ select ARCH_HAS_VARIABLE_STACK_SIZE
select ARCH_HAVE_ELF_PROT
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_HAVE_TRACE_MMIO_ACCESS
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 4545d9b39bd5..1d300b16032d 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -113,6 +113,13 @@
#define DIRECT_MAP_PHYSMEM_END __pa(PAGE_END - 1)
#define MIN_THREAD_SHIFT (14 + KASAN_THREAD_SHIFT)
+#define MIN_KERNEL_STACK_SHIFT (13 + KASAN_THREAD_SHIFT)
+
+#if (MIN_KERNEL_STACK_SHIFT < PAGE_SHIFT)
+#define MIN_KERNEL_STACK_SIZE PAGE_SIZE
+#else
+#define MIN_KERNEL_STACK_SIZE (UL(1) << MIN_KERNEL_STACK_SHIFT)
+#endif
/*
* VMAP'd stacks are allocated at page granularity, so we must ensure that such
@@ -130,6 +137,11 @@
#define THREAD_SIZE (UL(1) << THREAD_SHIFT)
+#ifndef __ASSEMBLY__
+extern unsigned long kernel_stack_pages;
+#define arch_vm_stack_pages() (kernel_stack_pages)
+#endif
+
#define THREAD_ALIGN THREAD_SIZE
#define IRQ_STACK_SIZE THREAD_SIZE
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index ea733b673970..39e68aace0a6 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -76,9 +76,13 @@
msr spsel, #0
mov x1, sp
msr spsel, #1
- sub x1, x1, x0
- cmp x1, #THREAD_SIZE
- b.lo .Lstack_ok\@
+
+ add x0, x0, #THREAD_SIZE
+ sub x1, x0, x1
+ adr_l x0, kernel_stack_pages
+ ldr x0, [x0]
+ cmp x1, x0, lsl #PAGE_SHIFT
+ b.ls .Lstack_ok\@
/* Check whether we are on the IRQ, SDEI or EFI stacks. */
stp x2, x30, [sp, #-16]!
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 29c6100f0c50..34559048dcca 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -278,6 +278,30 @@ u64 cpu_logical_map(unsigned int cpu)
return __cpu_logical_map[cpu];
}
+unsigned long kernel_stack_pages __ro_after_init = THREAD_SIZE / PAGE_SIZE;
+
+static int __init setup_kernel_stack_pages(char *str)
+{
+ unsigned long min = MIN_KERNEL_STACK_SIZE / PAGE_SIZE;
+ unsigned long max = THREAD_SIZE / PAGE_SIZE;
+ unsigned long val;
+
+ if (!str)
+ return -EINVAL;
+
+ if (kstrtoul(str, 10, &val) || val < min || val > max) {
+ pr_err("Invalid kernel_stack_pages=%s, should be between %lu and %lu\n",
+ str, min, max);
+ return 0;
+ }
+
+ kernel_stack_pages = val;
+ pr_info("Kernel stack size set to %lu KiB\n", (val * PAGE_SIZE) / SZ_1K);
+
+ return 0;
+}
+early_param("kernel_stack_pages", setup_kernel_stack_pages);
+
void __init __no_sanitize_address setup_arch(char **cmdline_p)
{
setup_initial_init_mm(_text, _etext, _edata, _end);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread