Skip to content

Commit 868e706

Browse files
committed
fix: 直接存储地址范围和释放函数以避免悬垂指针
Signed-off-by: YdrMaster <ydrml@hotmail.com>
1 parent b69c4fc commit 868e706

File tree

4 files changed

+40
-121
lines changed

4 files changed

+40
-121
lines changed

README.md

Lines changed: 8 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88

99
> **感谢:2022 系统能力赛,哈工大(深圳),[FTL-OS](https://gitlab.eduxiji.net/DarkAngelEX/oskernel2022-ftlos) 提供灵感**
1010
11-
| 注意 | 这个项目有重大 bug,设计上也需要调整,正在重构中,参见[重构设计](#重构设计)
12-
|-|-
13-
1411
## 目录
1512

1613
- [概念](#概念)
@@ -22,10 +19,10 @@
2219
- [切换现场](#切换现场)
2320
- [任务兼容性](#任务兼容性)
2421
- [陷入服务程序的一致抽象](#陷入服务程序的一致抽象)
22+
- [设计解释](#设计解释)
2523
- [使用说明](#使用说明)
2624
- [功能测试](#功能测试)
2725
- [性能测试](#性能测试)
28-
- [重构设计](#重构设计)
2926

3027
## 概念
3128

@@ -313,6 +310,10 @@ trap Lv.2 (trap3)-->{o} (tr
313310

314311
服务阶段指的是初始化阶段以后的整个生命周期。这个阶段完全是陷入驱动的,每次发生陷入就进入准备好的陷入栈执行处理流程,平时则完全静默。
315312

313+
## 设计解释
314+
315+
- [为什么陷入必须换栈](https://github.com/YdrMaster/notebook/blob/main/%E9%9A%8F%E7%AC%94/20230130-trap-to-a-new-stack.md)
316+
316317
## 使用说明
317318

318319
### 功能测试
@@ -341,17 +342,9 @@ trap Lv.2 (trap3)-->{o} (tr
341342
[ WARN] LOG TEST >> Hello, world!
342343
[ERROR] LOG TEST >> Hello, world!
343344

344-
[TRACE] new TrapStack(0x80205000..0x80206000)
345-
[TRACE] delete TrapStack(0x80205000..0x80206000)
346-
[ INFO] Stack Dropped!
347-
[TRACE] new TrapStack(0x80205000..0x80206000)
348-
[TRACE] load TrapStack(0x80205000..0x80206000)
349-
[TRACE] unload TrapStack(0x80205000..0x80206000)
350-
[TRACE] delete TrapStack(0x80205000..0x80206000)
351-
[ INFO] Stack Dropped!
352-
[TRACE] new TrapStack(0x80205000..0x80206000)
353-
[TRACE] load TrapStack(0x80205000..0x80206000)
354-
[DEBUG] sscratch: 0x80205fd8
345+
[DEBUG] fast trap: Exception(Unknown)(25)
346+
[ WARN] call fast-trap inline!
347+
[DEBUG] mscratch: 0x80008fd0
355348
[DEBUG] fast trap: Exception(Unknown)(24)
356349
[DEBUG] fast trap: Exception(IllegalInstruction)(2)
357350
[ INFO] Test pass
@@ -450,41 +443,3 @@ cargo qemu --kernel bench --smp 1
450443
| main | 107700360 | 107756601 | 123088023 | 48.5%
451444
| vectored | 212014619 | 212212320 | 273411079 | 100.0%
452445
| slow | 210191584 | 210286668 | 456087262 | 125.6%
453-
454-
## 重构设计
455-
456-
将陷入分为 2 类:
457-
458-
- 陷入到新栈:陷入之后将栈指针指向一个新的栈,意味着启动了新的线程;
459-
- 陷入到当前栈:发生陷入时栈指针不动,直接继续使用,因此陷入时栈指针必须有效;
460-
461-
必须将陷入处理和“线程”的概念耦合。在这个库的框架下,产生新线程的唯一方式就是陷入到新栈。
462-
463-
陷入到新栈时一定会创建一个新线程,但是这个线程的生命周期可能很短。任务的切换只能发生在陷入线程的末尾,这类似于 CORTEX 架构的 PendSV 可挂起中断的设计。
464-
465-
一般来说,跨权限等级的陷入必须陷入到新栈,因为低权限代码可能没有正确使用栈指针,甚至就是因为栈溢出才发生陷入。但同权限的陷入,尤其是在同一个软件内部发生的陷入(比如内核开中断),通常来说栈指针是保证有效的,此时就可以使用同栈陷入的方式。
466-
467-
陷入之后,上下文可以直接压在栈上。下表显示了一个典型的线程栈结构:
468-
469-
| 栈底(高地址)
470-
| -
471-
| 低权限上下文
472-
| `0` 级陷入栈
473-
| `1` 级嵌套上下文
474-
| `1` 级陷入栈
475-
| ……
476-
| `n` 级嵌套上下文
477-
478-
这个线程栈由一次低优先级发生的陷入创建,在栈上压入控制流上下文后开始处理,此时使用的栈空间称为 `0` 级陷入栈。若处理过程中再次发生陷入,栈指针不动,当前上下文直接压到栈上后开始处理,此时使用的栈空间称为 `1` 级陷入栈。以此类推,若发生 `n` 次嵌套陷入后,无法处理,需要挂起等待,则 `n` 级嵌套处理必须退出,栈顶存放用于恢复 `n-1` 级陷入处理的 `n` 级上下文,然后一步恢复另一个线程的最后一个上下文。此时,刚刚创建的那个线程上,共有 `n` 个内核陷入和一个低权限陷入不能处理,处于挂起状态。只有原来的 `n` 级陷入处理完成才能依次恢复。
479-
480-
这种逐级压到同一个栈的方式可以节省栈空间的开销,也会减少同权限陷入不得不换栈导致的缓存失效问题。但也会压住之前到来的陷入无法处理,必须跟着挂起。尤其是新到来的同权限陷入通常和被压住的陷入没什么关系,很可能带来死锁。所以,如果第 `n` 级上下文无法立即处理,可以选择创建一个挂起状态的新线程,然后恢复处理 `n-1` 级陷入。只需分配一个新的栈空间,拷贝 `n` 级陷入栈的数据到新栈上,然后构造一个可恢复这个陷入栈的上下文即可。
481-
482-
这种从挂起创建的线程栈具有这样的结构:
483-
484-
| 栈底(高地址)
485-
| -
486-
| 无效上下文
487-
| `n` 级陷入栈
488-
| `n+1` 级嵌套上下文
489-
490-
从这个线程恢复时,恢复 `n+1` 级嵌套上下文将继续执行 `n` 级陷入栈的处理流程,完成后识别到下一个要恢复的是一个无效的上下文,可以执行一个调度流程,这个栈空间则可回收。

fast-trap/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,3 @@ authors = ["YdrMaster <ydrml@hotmail.com>"]
99
[features]
1010
riscv-s = []
1111
riscv-m = []
12-
13-
[dependencies]
14-
log = "0.4.17"

fast-trap/src/lib.rs

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@ use core::{
1717
marker::PhantomPinned,
1818
mem::{align_of, forget, MaybeUninit},
1919
ops::Range,
20-
ptr::{drop_in_place, NonNull},
20+
ptr::NonNull,
2121
};
2222

23-
const TARGET: &str = "fast-trap";
24-
2523
/// 游离的陷入栈。
2624
pub struct FreeTrapStack(NonNull<TrapHandler>);
2725

@@ -35,22 +33,22 @@ pub struct IllegalStack;
3533
impl FreeTrapStack {
3634
/// 在内存块上构造游离的陷入栈。
3735
pub fn new(
38-
block: impl TrapStackBlock,
36+
range: Range<usize>,
37+
drop: fn(Range<usize>),
38+
3939
context_ptr: NonNull<FlowContext>,
4040
fast_handler: FastHandler,
4141
) -> Result<Self, IllegalStack> {
4242
const LAYOUT: Layout = Layout::new::<TrapHandler>();
43-
let range = block.as_ref().as_ptr_range();
44-
let bottom = range.start as usize;
45-
let top = range.end as usize;
43+
let bottom = range.start;
44+
let top = range.end;
4645
let ptr = (top - LAYOUT.size()) & !(LAYOUT.align() - 1);
4746
if ptr >= bottom {
4847
let handler = unsafe { &mut *(ptr as *mut TrapHandler) };
48+
handler.range = range;
49+
handler.drop = drop;
4950
handler.context = context_ptr;
5051
handler.fast_handler = fast_handler;
51-
handler.block = NonNull::from(&block);
52-
forget(block);
53-
log::trace!(target: TARGET, "new TrapStack({:?})", range);
5452
Ok(Self(unsafe { NonNull::new_unchecked(handler) }))
5553
} else {
5654
Err(IllegalStack)
@@ -60,7 +58,6 @@ impl FreeTrapStack {
6058
/// 将这个陷入栈加载为预备陷入栈。
6159
#[inline]
6260
pub fn load(self) -> LoadedTrapStack {
63-
log::trace!("load TrapStack({:#x?})", unsafe { self.0.as_ref().range() });
6461
let scratch = exchange_scratch(self.0.as_ptr() as _);
6562
forget(self);
6663
LoadedTrapStack(scratch)
@@ -70,10 +67,10 @@ impl FreeTrapStack {
7067
impl Drop for FreeTrapStack {
7168
#[inline]
7269
fn drop(&mut self) {
73-
log::trace!("delete TrapStack({:#x?})", unsafe {
74-
self.0.as_ref().range()
75-
});
76-
unsafe { drop_in_place(self.0.as_ref().block.as_ptr()) }
70+
unsafe {
71+
let handler = self.0.as_ref();
72+
(handler.drop)(handler.range.clone());
73+
}
7774
}
7875
}
7976

@@ -101,9 +98,6 @@ impl LoadedTrapStack {
10198
unsafe fn unload_unchecked(&self) -> FreeTrapStack {
10299
let ptr = exchange_scratch(self.0) as *mut TrapHandler;
103100
let handler = unsafe { NonNull::new_unchecked(ptr) };
104-
log::trace!("unload TrapStack({:#x?})", unsafe {
105-
handler.as_ref().range()
106-
});
107101
FreeTrapStack(handler)
108102
}
109103
}
@@ -115,13 +109,6 @@ impl Drop for LoadedTrapStack {
115109
}
116110
}
117111

118-
/// 陷入栈内存块。
119-
///
120-
/// # TODO
121-
///
122-
/// 需要给 `Vec`、`Box<[u8]>` 之类的东西实现。
123-
pub trait TrapStackBlock: 'static + AsRef<[u8]> + AsMut<[u8]> {}
124-
125112
/// 陷入处理器上下文。
126113
#[repr(C)]
127114
struct TrapHandler {
@@ -145,30 +132,23 @@ struct TrapHandler {
145132
/// - 在快速路径开始时暂存 a0。
146133
/// - 在快速路径结束时保存完整路径函数。
147134
scratch: usize,
148-
/// 上下文所在的内存块。
149-
///
150-
/// 保存它以提供内存块的范围,同时用于控制内存块的生命周期。
151-
block: NonNull<dyn TrapStackBlock>,
135+
136+
range: Range<usize>,
137+
drop: fn(Range<usize>),
138+
152139
/// 禁止移动标记。
153140
///
154141
/// `TrapHandler` 是放在其内部定义的 `block` 块里的,这是一种自引用结构,不能移动。
155142
pinned: PhantomPinned,
156143
}
157144

158145
impl TrapHandler {
159-
/// 内存块地址范围。
160-
#[inline]
161-
fn range(&self) -> Range<usize> {
162-
let block = unsafe { self.block.as_ref().as_ref().as_ptr_range() };
163-
block.start as _..block.end as _
164-
}
165-
166146
/// 如果从快速路径向完整路径转移,可以把一个对象放在栈底。
167147
/// 用这个方法找到栈底的一个对齐的位置。
168148
#[inline]
169149
fn locate_fast_mail<T>(&mut self) -> *mut MaybeUninit<T> {
170-
let bottom = unsafe { self.block.as_mut() }.as_mut().as_mut_ptr();
171-
let offset = bottom.align_offset(align_of::<T>());
172-
unsafe { &mut *bottom.add(offset).cast() }
150+
let top = self.range.end as *mut u8;
151+
let offset = top.align_offset(align_of::<T>());
152+
unsafe { &mut *top.add(offset).cast() }
173153
}
174154
}

test-app/src/main.rs

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use core::{
1212
use dtb_walker::{Dtb, DtbObj, HeaderError, Str, WalkOperation};
1313
use fast_trap::{
1414
load_direct_trap_entry, reuse_stack_for_trap, soft_trap, trap_entry, FastContext, FastResult,
15-
FlowContext, FreeTrapStack, TrapStackBlock,
15+
FlowContext, FreeTrapStack,
1616
};
1717
use rcore_console::log;
1818
use riscv::register::*;
@@ -102,7 +102,8 @@ extern "C" fn rust_main(_hartid: usize, dtb: *const u8) {
102102

103103
// 测试构造和释放
104104
let _ = FreeTrapStack::new(
105-
StackRef(unsafe { &mut ROOT_STACK }),
105+
unsafe { ROOT_STACK.range() },
106+
|_| {},
106107
context_ptr,
107108
fast_handler,
108109
)
@@ -114,7 +115,8 @@ extern "C" fn rust_main(_hartid: usize, dtb: *const u8) {
114115

115116
// 测试加载和卸载
116117
let _ = FreeTrapStack::new(
117-
StackRef(unsafe { &mut ROOT_STACK }),
118+
unsafe { ROOT_STACK.range() },
119+
|_| {},
118120
context_ptr,
119121
fast_handler,
120122
)
@@ -127,7 +129,8 @@ extern "C" fn rust_main(_hartid: usize, dtb: *const u8) {
127129

128130
// 加载一个新的陷入栈
129131
let loaded = FreeTrapStack::new(
130-
StackRef(unsafe { &mut ROOT_STACK }),
132+
unsafe { ROOT_STACK.range() },
133+
|_| {},
131134
context_ptr,
132135
fast_handler,
133136
)
@@ -137,7 +140,8 @@ extern "C" fn rust_main(_hartid: usize, dtb: *const u8) {
137140
{
138141
// 叠加一个陷入栈用于临时保护
139142
let _loaded = FreeTrapStack::new(
140-
StackRef(unsafe { &mut FREE_STACK }),
143+
unsafe { FREE_STACK.range() },
144+
|_| {},
141145
context_ptr,
142146
fast_handler,
143147
)
@@ -239,27 +243,10 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
239243
#[repr(C, align(4096))]
240244
struct Stack([u8; 4096]);
241245

242-
struct StackRef(&'static mut Stack);
243-
244-
impl AsRef<[u8]> for StackRef {
245-
#[inline]
246-
fn as_ref(&self) -> &[u8] {
247-
&self.0 .0
248-
}
249-
}
250-
251-
impl AsMut<[u8]> for StackRef {
252-
#[inline]
253-
fn as_mut(&mut self) -> &mut [u8] {
254-
&mut self.0 .0
255-
}
256-
}
257-
258-
impl TrapStackBlock for StackRef {}
259-
260-
impl Drop for StackRef {
261-
fn drop(&mut self) {
262-
log::info!("Stack Dropped!")
246+
impl Stack {
247+
fn range(&self) -> core::ops::Range<usize> {
248+
let ans = self.0.as_ptr_range();
249+
ans.start as usize..ans.end as usize
263250
}
264251
}
265252

0 commit comments

Comments
 (0)