Skip to content

Commit d44f3fa

Browse files
committed
Recycle used kernel thread stacks
Because new_kernel_stack iterates through the available stack space linearly, it can only provide each stack slot once. This means that there is a limited number of kernel threads that can be created over the lifetime of the kernel. With the kernel stack space defined so currently, and with one-page guard pages for each stack, this would currently give a limit of 255 kernel threads (including the initial thread). To address this issue, we now recycle old kernel stack when their thread exits. When we create a new thread, we check whether there is a dead stack available. If so, we reuse it, rather than allocating another. This means we go from having a limit of 255 kernel threads ever to a limit of 255 simultaneous kernel threads. In practice, this is likely to be sufficient. Signed-off-by: SlyMarbo <[email protected]>
1 parent bce3240 commit d44f3fa

File tree

1 file changed

+30
-2
lines changed
  • kernel/src/multitasking/thread

1 file changed

+30
-2
lines changed

kernel/src/multitasking/thread/mod.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::utils::lazy::Lazy;
77
use crate::utils::once::Once;
88
use alloc::collections::BTreeMap;
99
use alloc::sync::Arc;
10+
use alloc::vec::Vec;
1011
use core::cell::UnsafeCell;
1112
use core::sync::atomic::{AtomicU64, Ordering};
1213
use crossbeam::atomic::AtomicCell;
@@ -55,6 +56,22 @@ pub fn current_thread() -> &'static Arc<Thread> {
5556
unsafe { CURRENT.get() }
5657
}
5758

59+
/// DEAD_STACKS is a free list of kernel stacks that
60+
/// have been released by kernel threads that have
61+
/// exited.
62+
///
63+
/// If there is a stack available in DEAD_STACKS
64+
/// when a new thread is created, it is used instead
65+
/// of allocating a new stack. This mitigates the
66+
/// inability to track unused stacks in new_kernel_stack,
67+
/// which would otherwise limit the number of
68+
/// kernel threads that can be created during the
69+
/// lifetime of the kernel. Instead, we're left
70+
/// with just a limit on the number of simultaneous
71+
/// kernel threads.
72+
///
73+
static DEAD_STACKS: spin::Mutex<Vec<StackBounds>> = spin::Mutex::new(Vec::new());
74+
5875
/// idle_thread implements the idle thread. We
5976
/// fall back to this if the kernel has no other
6077
/// work left to do.
@@ -151,6 +168,9 @@ pub fn exit() -> ! {
151168

152169
current.set_state(ThreadState::Exited);
153170
THREADS.lock().remove(&current.id);
171+
if let Some(bounds) = current.stack_bounds {
172+
DEAD_STACKS.lock().push(bounds);
173+
}
154174

155175
// We've now been unscheduled, so we
156176
// switch to the next thread.
@@ -278,8 +298,16 @@ impl Thread {
278298
///
279299
pub fn new_kernel_thread(entry_point: fn() -> !) {
280300
// Allocate and prepare the stack pointer.
281-
let stack = new_kernel_stack(Thread::DEFAULT_KERNEL_STACK_PAGES)
282-
.expect("failed to allocate stack for new kernel thread");
301+
// Check if we can reuse a dead stack, rather
302+
// than allocating a new one.
303+
let stack = {
304+
let mut stacks = DEAD_STACKS.lock();
305+
match stacks.pop() {
306+
Some(stack) => stack,
307+
None => new_kernel_stack(Thread::DEFAULT_KERNEL_STACK_PAGES)
308+
.expect("failed to allocate stack for new kernel thread"),
309+
}
310+
};
283311

284312
let rsp = unsafe {
285313
let mut rsp: *mut u64 = stack.end().as_mut_ptr();

0 commit comments

Comments
 (0)