[RFC 00/10] Reclaimable kernel stacks

David Stevens posted 10 patches 1 month ago
arch/Kconfig                       |  18 +
arch/arm64/Kconfig                 |   1 +
arch/arm64/include/asm/processor.h |   5 +
arch/x86/Kconfig                   |   1 +
arch/x86/include/asm/processor.h   |   5 +
drivers/android/binder/thread.rs   |  14 +
fs/eventpoll.c                     |   3 +
fs/pipe.c                          |  28 +-
fs/select.c                        |   3 +
include/linux/list_lru.h           |   7 +-
include/linux/sched.h              |  44 +-
include/linux/sched/task_stack.h   |  23 +
include/linux/vmalloc.h            |   2 +
kernel/Makefile                    |   2 +
kernel/fork.c                      | 140 +++++-
kernel/futex/waitwake.c            |   3 +
kernel/sched/core.c                |  18 +-
kernel/sched/sched.h               |   3 +
kernel/signal.c                    |  36 +-
kernel/stack_shrinker.c            | 760 +++++++++++++++++++++++++++++
kernel/stack_shrinker.h            |  58 +++
kernel/time/hrtimer.c              |   3 +
mm/vmalloc.c                       |  27 +-
rust/kernel/task.rs                |  16 +
24 files changed, 1175 insertions(+), 45 deletions(-)
create mode 100644 kernel/stack_shrinker.c
create mode 100644 kernel/stack_shrinker.h
[RFC 00/10] Reclaimable kernel stacks
Posted by David Stevens 1 month ago
This RFC is a different approach to reducing kernel stack usage from the
earlier dynamic kernel stack RFC [1]. This patch series aims to reduce
the cost of kernel stacks by partially reclaiming stacks of blocked
tasks when it is safe to do so.

On Android, system processes typically have 2000-3000 threads. App
processes add 1000s more threads on top of this. The number of app
processes varies based on device RAM size, but the end result is that
1-2% of system RAM is consumed by kernel stacks. However, most of these
threads spend extended periods of time blocked. As such, reclaiming
blocked kernel stacks can reduce total kernel stack memory usage by
upwards of 50% in various multi-tasking test cases.

When a task is blocked, we know exactly where the top of its stack is
and can reclaim any pages past that point. Since any accesses to that
portion of the stack are bugs like use-after-return or buffer overflow,
turning those invalid accesses into hard crashes could even be
considered a positive.

Tracking blocked state and when it is safe to reclaim a stack is done
via a series of hooks in the scheduler. The actual reclaim of stacks is
done asynchronously in a shrinker.

Once a task's stack has been reclaimed, it cannot be rescheduled until
its stack is repopulated. Although there can be a repopulation fast path
within the scheduler, reliably allocating memory to repopulate the stack
requires a fallback path that defers the repopulation and wakeup to a
workqueue context that can use GFP_KERNEL.

The primary challenge is avoiding reclaim deadlocks. If a task blocks
while holding a lock used by direct reclaim and then has its stack
reclaimed, using GFP_KERNEL to reallocate its stack risks deadlock. To
avoid this, only tasks which are known not to hold any locks upon which
reclaim depends are considered eligible for stack reclaim. Automatically
inferring this property is not feasible, so instead a new
PF_RECLAIMABLE_STACK task flag is used to annotate blocking locations
that are known safe. While annotating all safe blocking locations is not
feasible, the vast majority of userspace threads block using a fairly
small number of syscalls - futex, epoll, nanosleep, etc. The 10
annotations added in this series cover >95% of userspace threads on
Android based on my testing. Since missing annotations are leaving an
optimization on the table rather than an actual bug, other annotations
can be added later as needed.

Although reclaimable stacks will not cause reclaim to deadlock, it does
introduce a dependency on needing to allocate memory before an OOM
victim can exit, as reclaimed stacks need to be repopulated before their
tasks can run. While the OOM reaper will still be able to immediately
free the victim's mm, the freeing of non-mm memory may be delayed. This
can lead to more OOM kills. While this is not a significant concern on
Android due to the reliance on lmkd over the kernel OOM killer, it may
be a concern on other systems.

This RFC was developed primarily on 6.18 and 7.1 based kernels. I have
done fairly heavy stress testing, but it has not yet been deployed to
any production systems. If initial feedback on the RFC is somewhat
positive, I will work on deploying it to production systems for further
stability and performance testing as well as resolving the handful of
TODOs left in the RFC.

[1] https://lore.kernel.org/linux-mm/20260424191456.2679717-1-stevensd@google.com/

David Stevens (10):
  Add !MEMCG memcg_list_lru_alloc implementation
  mm/vmalloc: Skip vmallocinfo NUMA stats for VM_SPARSE
  fork: refactor vmap stack alloc/free into helpers
  mm: vmalloc: support creating aligned vm areas
  fork: allocate reclaimable stacks with VM_SPARSE
  Reclaim memory from blocked kernel stacks
  Reclaim stacks via a shrinker
  Set PF_RECLAIMABLE_STACK in various places
  x86: Enable reclaimable stacks
  arm64: Enable reclaimable stacks

 arch/Kconfig                       |  18 +
 arch/arm64/Kconfig                 |   1 +
 arch/arm64/include/asm/processor.h |   5 +
 arch/x86/Kconfig                   |   1 +
 arch/x86/include/asm/processor.h   |   5 +
 drivers/android/binder/thread.rs   |  14 +
 fs/eventpoll.c                     |   3 +
 fs/pipe.c                          |  28 +-
 fs/select.c                        |   3 +
 include/linux/list_lru.h           |   7 +-
 include/linux/sched.h              |  44 +-
 include/linux/sched/task_stack.h   |  23 +
 include/linux/vmalloc.h            |   2 +
 kernel/Makefile                    |   2 +
 kernel/fork.c                      | 140 +++++-
 kernel/futex/waitwake.c            |   3 +
 kernel/sched/core.c                |  18 +-
 kernel/sched/sched.h               |   3 +
 kernel/signal.c                    |  36 +-
 kernel/stack_shrinker.c            | 760 +++++++++++++++++++++++++++++
 kernel/stack_shrinker.h            |  58 +++
 kernel/time/hrtimer.c              |   3 +
 mm/vmalloc.c                       |  27 +-
 rust/kernel/task.rs                |  16 +
 24 files changed, 1175 insertions(+), 45 deletions(-)
 create mode 100644 kernel/stack_shrinker.c
 create mode 100644 kernel/stack_shrinker.h


base-commit: 8d3ae59288f1e7d58d76558a6ee96d533bc5019f
-- 
2.55.0.897.gb25b4bd76c-goog
Re: [RFC 00/10] Reclaimable kernel stacks
Posted by Peter Zijlstra 4 weeks, 1 day ago
On Thu, Aug 27, 2026 at 04:29:38PM -0700, David Stevens wrote:

> On Android, system processes typically have 2000-3000 threads. App
> processes add 1000s more threads on top of this.

WTF ?!? Why does that all spawn *that* many threads? Perhaps work on
reducing that some?

> Tracking blocked state and when it is safe to reclaim a stack is done
> via a series of hooks in the scheduler. The actual reclaim of stacks is
> done asynchronously in a shrinker.
> 
> Once a task's stack has been reclaimed, it cannot be rescheduled until
> its stack is repopulated. Although there can be a repopulation fast path
> within the scheduler, reliably allocating memory to repopulate the stack
> requires a fallback path that defers the repopulation and wakeup to a
> workqueue context that can use GFP_KERNEL.

I am really confused. On the one hand you have John working on proxy
execution, with the aim on reducing latencies, and then here you are,
posting something that will introduce basically unbound latencies.
Re: [RFC 00/10] Reclaimable kernel stacks
Posted by David Stevens 4 weeks, 1 day ago
On Fri, Aug 28, 2026 at 5:48 AM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Aug 27, 2026 at 04:29:38PM -0700, David Stevens wrote:
>
> > On Android, system processes typically have 2000-3000 threads. App
> > processes add 1000s more threads on top of this.
>
> WTF ?!? Why does that all spawn *that* many threads? Perhaps work on
> reducing that some?

I agree that the number of threads is rather excessive, and reducing
it in userspace is an ongoing effort. However, given Android's use of
Java and its heavily multi-process architecture, especially once apps
are involved, there are limits to how much the thread count can
realistically be reduced. And even if a 50% reduction in thread count
were somehow achieved, kernel stacks would still consume upwards of 1%
of system RAM on lower spec devices with 4GB of memory.

> > Tracking blocked state and when it is safe to reclaim a stack is done
> > via a series of hooks in the scheduler. The actual reclaim of stacks is
> > done asynchronously in a shrinker.
> >
> > Once a task's stack has been reclaimed, it cannot be rescheduled until
> > its stack is repopulated. Although there can be a repopulation fast path
> > within the scheduler, reliably allocating memory to repopulate the stack
> > requires a fallback path that defers the repopulation and wakeup to a
> > workqueue context that can use GFP_KERNEL.
>
> I am really confused. On the one hand you have John working on proxy
> execution, with the aim on reducing latencies, and then here you are,
> posting something that will introduce basically unbound latencies.

The two projects aren't contradictory because they deal with different
types of latency.

Proxy execution aims to solve latency introduced by priority inversion
between background tasks and foreground tasks. This can happen at
basically any point if you get unlucky, resulting in unpredictable
latency spikes in high priority tasks.

The latency introduced by reclaimable stacks isn't substantially
different from the latency from refaulting evicted anon or file pages.
It's a tradeoff between the reduced cost of cold memory and the
increased latency when re-accessing that cold memory - clearly a
tradeoff that Linux already makes. In situations where reclaimed
stacks introduce latency, there will almost certainly already be extra
latency incurred from refaulting userspace pages. I don't yet have
production data, but the testing I've done so far doesn't show any
measurable negative impact on user visible latency metrics.

-David
Re: [RFC 00/10] Reclaimable kernel stacks
Posted by Steven Rostedt 4 weeks, 1 day ago
On Fri, 28 Aug 2026 14:47:58 +0200
Peter Zijlstra <peterz@infradead.org> wrote:

> > On Android, system processes typically have 2000-3000 threads. App
> > processes add 1000s more threads on top of this.  
> 
> WTF ?!? Why does that all spawn *that* many threads? Perhaps work on
> reducing that some?

I guess you never ran Java ;-)

-- Steve
Re: [RFC 00/10] Reclaimable kernel stacks
Posted by Peter Zijlstra 4 weeks, 1 day ago
On Fri, Aug 28, 2026 at 10:33:28AM -0400, Steven Rostedt wrote:
> On Fri, 28 Aug 2026 14:47:58 +0200
> Peter Zijlstra <peterz@infradead.org> wrote:
> 
> > > On Android, system processes typically have 2000-3000 threads. App
> > > processes add 1000s more threads on top of this.  
> > 
> > WTF ?!? Why does that all spawn *that* many threads? Perhaps work on
> > reducing that some?
> 
> I guess you never ran Java ;-)

Of course not; why would you want to do something that silly ;-)
Re: [RFC 00/10] Reclaimable kernel stacks
Posted by Peter Zijlstra 4 weeks, 1 day ago
On Fri, Aug 28, 2026 at 04:35:40PM +0200, Peter Zijlstra wrote:
> On Fri, Aug 28, 2026 at 10:33:28AM -0400, Steven Rostedt wrote:
> > On Fri, 28 Aug 2026 14:47:58 +0200
> > Peter Zijlstra <peterz@infradead.org> wrote:
> > 
> > > > On Android, system processes typically have 2000-3000 threads. App
> > > > processes add 1000s more threads on top of this.  
> > > 
> > > WTF ?!? Why does that all spawn *that* many threads? Perhaps work on
> > > reducing that some?
> > 
> > I guess you never ran Java ;-)
> 
> Of course not; why would you want to do something that silly ;-)

I realized I actually have a Java thing, the kids have Minecraft, so I
booted that up and I found all of 108 threads. Which is still kinda
insane, but nowhere near the 1000s per app as claimed.

Reducing the thread count from O(1e3) to O(1e2) would win far more
memory than any thread stack shenanigans would.
Re: [RFC 00/10] Reclaimable kernel stacks
Posted by Steven Rostedt 4 weeks, 1 day ago
On Fri, 28 Aug 2026 16:45:00 +0200
Peter Zijlstra <peterz@infradead.org> wrote:

> I realized I actually have a Java thing, the kids have Minecraft, so I
> booted that up and I found all of 108 threads. Which is still kinda
> insane, but nowhere near the 1000s per app as claimed.

Have you looked at how many threads Chome uses?

 $ ps -eLf|grep chrome | wc -l
 930

Of course I probably have 900 tabs open :-p

-- Steve