Skip to content

Commit 3d462af

Browse files
authored
✨ feat: 添加 per-CPU 预分配支持 (#62)
* ✨ feat: 添加 per-CPU 预分配支持 * ✨ feat: 优化内存布局计算,添加对对齐的调试断言 * chore(workflows): 更新 checkout action 至 v6
1 parent 78aa73b commit 3d462af

6 files changed

Lines changed: 326 additions & 118 deletions

File tree

.github/workflows/test-real.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
[ -d .git ] && git clean -ffdx || true
3636
3737
- name: Checkout
38-
uses: actions/checkout@v4
38+
uses: actions/checkout@v6
3939
with:
4040
clean: true
4141

crates/someboot/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ version = "0.1.10"
1313
efi = []
1414
hv = []
1515
mmu = []
16+
percpu-prealloc = []
1617
uspace = ["mmu"]
1718

1819
[dependencies]

crates/someboot/src/smp/legacy.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use crate::mem::{__kimage_va, __percpu, phys_to_virt, stack_size};
2+
3+
use super::{
4+
__cpu_id_list, PerCpuMeta, align_up_pow2, alloc_percpu_region, cpu_count, meta_align,
5+
percpu_data_range, percpu_link_range, percpu_region_align, set_percpu_range,
6+
};
7+
8+
fn meta_offset() -> usize {
9+
let link_size = percpu_link_range().len();
10+
let offset = align_up_pow2(link_size, meta_align());
11+
debug_assert_eq!(offset % meta_align(), 0);
12+
offset
13+
}
14+
15+
fn stack_offset() -> usize {
16+
let meta_offset = meta_offset();
17+
let meta_size = core::mem::size_of::<PerCpuMeta>();
18+
align_up_pow2(meta_offset + meta_size, crate::mem::page_size())
19+
}
20+
21+
fn percpu_data_size() -> usize {
22+
align_up_pow2(stack_offset() + stack_size(), percpu_region_align())
23+
}
24+
25+
/// Per-CPU data layout:
26+
///
27+
/// | Linker percpu data | align to 0x8 | PerCpuMeta | align padding to page size | Stack |
28+
pub fn alloc_percpu() {
29+
println!("Initializing per-CPU data");
30+
let cpu_num = cpu_count();
31+
let link_range = percpu_link_range();
32+
let link_size = link_range.len();
33+
34+
let percpu_size = percpu_data_size();
35+
println!("Per-CPU data one cpu size: {:#x} bytes", percpu_size);
36+
let percpu_all_secondary_size = percpu_size * cpu_num;
37+
38+
println!(
39+
"Total per-CPU data size for secondary CPUs: {:#x} bytes ({} CPUs)",
40+
percpu_all_secondary_size, cpu_num
41+
);
42+
43+
let percpu_data = alloc_percpu_region(percpu_all_secondary_size);
44+
45+
set_percpu_range(percpu_data, percpu_data + percpu_all_secondary_size);
46+
47+
unsafe {
48+
core::ptr::write_bytes(phys_to_virt(percpu_data), 0, percpu_all_secondary_size);
49+
}
50+
51+
println!(
52+
"Per-CPU data allocated at {:#x} - {:#x}",
53+
percpu_data_range().start,
54+
percpu_data_range().end
55+
);
56+
57+
let entry_virt = __kimage_va(super::super::entry::secondary_entry as *const () as usize);
58+
59+
for (idx, hard_id) in __cpu_id_list().enumerate() {
60+
let cpu_percpu_start = percpu_data_range().start + idx * percpu_size;
61+
println!(
62+
"Initializing per-CPU RAM for CPU{idx} - hard id {hard_id:#x} @ {cpu_percpu_start:#x}"
63+
);
64+
unsafe {
65+
core::ptr::copy_nonoverlapping(
66+
phys_to_virt(link_range.start) as *const u8,
67+
phys_to_virt(cpu_percpu_start),
68+
link_size,
69+
);
70+
}
71+
let meta_start = cpu_percpu_start + meta_offset();
72+
let meta_va = phys_to_virt(meta_start);
73+
debug_assert_eq!(meta_start % meta_align(), 0);
74+
debug_assert_eq!((meta_va as usize) % meta_align(), 0);
75+
76+
let stack_top = cpu_percpu_start + stack_offset() + stack_size();
77+
let stack_top_virt = __percpu(stack_top);
78+
79+
let meta = PerCpuMeta {
80+
stack_top,
81+
cpu_id: hard_id,
82+
cpu_idx: idx,
83+
stack_top_virt: stack_top_virt as _,
84+
entry_virt: entry_virt as _,
85+
boot_table_paddr: 0,
86+
primary_table_paddr: 0,
87+
};
88+
unsafe {
89+
*meta_va.cast::<PerCpuMeta>() = meta;
90+
}
91+
}
92+
93+
for meta in super::cpu_meta_list() {
94+
println!(
95+
"CPU{} - hard id {:#x}, stack top @{:#x}, stack top virt @{:#x}, entry virt @{:#x}",
96+
meta.cpu_idx, meta.cpu_id, meta.stack_top, meta.stack_top_virt, meta.entry_virt
97+
);
98+
}
99+
}
100+
101+
pub(crate) fn cpu_meta_addr(idx: usize) -> Option<usize> {
102+
let base = percpu_data_range().start + idx * percpu_data_size();
103+
if base >= percpu_data_range().end {
104+
return None;
105+
}
106+
Some(base + meta_offset())
107+
}
108+
109+
pub(crate) fn percpu_data_ptr(idx: usize) -> Option<*mut u8> {
110+
let base = percpu_data_range().start + idx * percpu_data_size();
111+
if base >= percpu_data_range().end {
112+
return None;
113+
}
114+
Some(phys_to_virt(base))
115+
}

crates/someboot/src/smp/mod.rs

Lines changed: 36 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
use core::alloc::Layout;
2-
31
use kernutil::memory::MemoryType;
42

53
use crate::{
64
ArchTrait, DCacheOp,
75
arch::Arch,
86
kernel_page_table_paddr,
9-
mem::{__kimage_va, __percpu, dcache_range, page_size, phys_to_virt, stack_size},
7+
mem::{__percpu, dcache_range, page_size, phys_to_virt},
108
};
119

1210
mod cpu_iter;
11+
#[cfg(not(feature = "percpu-prealloc"))]
12+
mod legacy;
13+
#[cfg(feature = "percpu-prealloc")]
14+
mod prealloc;
15+
16+
#[cfg(not(feature = "percpu-prealloc"))]
17+
use legacy as layout;
18+
#[cfg(feature = "percpu-prealloc")]
19+
use prealloc as layout;
1320

1421
static mut PERCPU_START: usize = 0;
1522
static mut PERCPU_END: usize = 0;
@@ -31,97 +38,8 @@ fn percpu_region_align() -> usize {
3138
page_size().max(meta_align())
3239
}
3340

34-
fn meta_offset() -> usize {
35-
let link_size = percpu_link_range().len();
36-
let offset = align_up_pow2(link_size, meta_align());
37-
debug_assert_eq!(offset % meta_align(), 0);
38-
offset
39-
}
40-
41-
/// Per-CPU data layout:
42-
///
43-
///
44-
/// | Linker percpu data | align to 0x8 | PerCpuMeta | align padding to page size | Stack |
4541
pub fn alloc_percpu() {
46-
println!("Initializing per-CPU data");
47-
let cpu_num = __cpu_id_list().count();
48-
let link_range = percpu_link_range();
49-
let link_size = link_range.len();
50-
51-
let percpu_size = percpu_data_size();
52-
println!("Per-CPU data one cpu size: {:#x} bytes", percpu_size);
53-
let percpu_all_secondary_size = percpu_size * cpu_num;
54-
55-
println!(
56-
"Total per-CPU data size for secondary CPUs: {:#x} bytes ({} CPUs)",
57-
percpu_all_secondary_size, cpu_num
58-
);
59-
60-
unsafe { crate::mem::ram::flush_to_memory_map(MemoryType::Reserved) };
61-
62-
let percpu_data = unsafe {
63-
crate::mem::ram::alloc_and_flush_to_memory_map(
64-
Layout::from_size_align(percpu_all_secondary_size, percpu_region_align()).unwrap(),
65-
MemoryType::PerCpuData,
66-
)
67-
.unwrap()
68-
};
69-
70-
unsafe {
71-
PERCPU_START = percpu_data;
72-
PERCPU_END = PERCPU_START + percpu_all_secondary_size;
73-
74-
core::ptr::write_bytes(phys_to_virt(percpu_data), 0, percpu_all_secondary_size);
75-
}
76-
77-
println!(
78-
"Per-CPU data allocated at {:#x} - {:#x}",
79-
unsafe { PERCPU_START },
80-
unsafe { PERCPU_END }
81-
);
82-
83-
let entry_virt = __kimage_va(super::entry::secondary_entry as *const () as usize);
84-
85-
for (idx, hard_id) in __cpu_id_list().enumerate() {
86-
let cpu_percpu_start = percpu_data_range().start + idx * percpu_size;
87-
println!(
88-
"Initializing per-CPU RAM for CPU{idx} - hard id {hard_id:#x} @ {cpu_percpu_start:#x}"
89-
);
90-
unsafe {
91-
core::ptr::copy_nonoverlapping(
92-
phys_to_virt(link_range.start) as *const u8,
93-
phys_to_virt(cpu_percpu_start),
94-
link_size,
95-
);
96-
}
97-
let meta_start = cpu_percpu_start + meta_offset();
98-
let meta_va = phys_to_virt(meta_start);
99-
debug_assert_eq!(meta_start % meta_align(), 0);
100-
debug_assert_eq!((meta_va as usize) % meta_align(), 0);
101-
102-
let stack_top = cpu_percpu_start + stack_offset() + stack_size();
103-
let stack_top_virt = __percpu(stack_top);
104-
105-
let meta = PerCpuMeta {
106-
stack_top,
107-
cpu_id: hard_id,
108-
cpu_idx: idx,
109-
stack_top_virt: stack_top_virt as _,
110-
entry_virt: entry_virt as _,
111-
boot_table_paddr: 0,
112-
primary_table_paddr: 0,
113-
};
114-
unsafe {
115-
*meta_va.cast::<PerCpuMeta>() = meta;
116-
}
117-
}
118-
119-
for meta in cpu_meta_list() {
120-
println!(
121-
"CPU{} - hard id {:#x}, stack top @{:#x}, stack top virt @{:#x}, entry virt @{:#x}",
122-
meta.cpu_idx, meta.cpu_id, meta.stack_top, meta.stack_top_virt, meta.entry_virt
123-
);
124-
}
42+
layout::alloc_percpu();
12543
}
12644

12745
pub(crate) fn init_percpu() {
@@ -153,16 +71,6 @@ pub struct PerCpuMeta {
15371
pub primary_table_paddr: usize,
15472
}
15573

156-
fn stack_offset() -> usize {
157-
let meta_offset = meta_offset();
158-
let meta_size = core::mem::size_of::<PerCpuMeta>();
159-
align_up_pow2(meta_offset + meta_size, page_size())
160-
}
161-
162-
fn percpu_data_size() -> usize {
163-
align_up_pow2(stack_offset() + stack_size(), percpu_region_align())
164-
}
165-
16674
#[allow(dead_code)]
16775
/// Physical RAM allocated for per-CPU data should be mapped to this virtual address range in the kernel
16876
pub(crate) fn percpu_range() -> core::ops::Range<usize> {
@@ -189,19 +97,11 @@ pub fn cpu_meta(idx: usize) -> Option<PerCpuMeta> {
18997

19098
/// Physical address of cpu meta
19199
pub(crate) fn cpu_meta_addr(idx: usize) -> Option<usize> {
192-
let base = percpu_data_range().start + idx * percpu_data_size();
193-
if base >= percpu_data_range().end {
194-
return None;
195-
}
196-
Some(base + meta_offset())
100+
layout::cpu_meta_addr(idx)
197101
}
198102

199103
pub fn percpu_data_ptr(idx: usize) -> Option<*mut u8> {
200-
let base = percpu_data_range().start + idx * percpu_data_size();
201-
if base >= percpu_data_range().end {
202-
return None;
203-
}
204-
Some(phys_to_virt(base))
104+
layout::percpu_data_ptr(idx)
205105
}
206106

207107
pub fn cpu_hart_id() -> usize {
@@ -270,10 +170,6 @@ impl Iterator for CpuMetaIterMutable {
270170
}
271171
}
272172

273-
fn percpu_data_range() -> core::ops::Range<usize> {
274-
unsafe { PERCPU_START..PERCPU_END }
275-
}
276-
277173
fn percpu_link_range() -> core::ops::Range<usize> {
278174
unsafe extern "C" {
279175
fn __percpu_start();
@@ -283,3 +179,26 @@ fn percpu_link_range() -> core::ops::Range<usize> {
283179
let end = __percpu_end as *const () as usize;
284180
start..end
285181
}
182+
183+
fn set_percpu_range(start: usize, end: usize) {
184+
unsafe {
185+
PERCPU_START = start;
186+
PERCPU_END = end;
187+
}
188+
}
189+
190+
fn percpu_data_range() -> core::ops::Range<usize> {
191+
unsafe { PERCPU_START..PERCPU_END }
192+
}
193+
194+
fn alloc_percpu_region(size: usize) -> usize {
195+
unsafe { crate::mem::ram::flush_to_memory_map(MemoryType::Reserved) };
196+
197+
unsafe {
198+
crate::mem::ram::alloc_and_flush_to_memory_map(
199+
core::alloc::Layout::from_size_align(size, percpu_region_align()).unwrap(),
200+
MemoryType::PerCpuData,
201+
)
202+
.unwrap()
203+
}
204+
}

0 commit comments

Comments
 (0)