Skip to content

Commit be0c86d

Browse files
feat(syscall/mincore):实现了mincore系统调用 (#1258)
* feat(syscall/mincore):实现了mincore系统调用 * 往usebuffer直接写入,在get_page处改为拿一次锁 * 更改原先测试 * 完成can_do_mincore * fmt * fix(mm): 完善mincore系统调用实现 - 添加大页支持,在遇到大页时按4K粒度填充 - 修复地址对齐检查,将assert改为返回EINVAL错误 - 严格验证用户缓冲区映射与写权限 - 修复VMA查找和排序逻辑,确保地址连续性判断正确 - 添加覆盖完整性校验 - 重写mincore测试用例,增加边界条件和文件映射测试 Signed-off-by: longjin <[email protected]> --------- Signed-off-by: longjin <[email protected]> Co-authored-by: longjin <[email protected]>
1 parent bd4d6b8 commit be0c86d

File tree

5 files changed

+365
-8
lines changed

5 files changed

+365
-8
lines changed

kernel/src/mm/mincore.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use crate::arch::MMArch;
2+
use crate::{
3+
arch::mm::PageMapper,
4+
mm::{ucontext::LockedVMA, MemoryManagementArch, VirtAddr},
5+
};
6+
use system_error::SystemError;
7+
8+
impl LockedVMA {
9+
pub fn do_mincore(
10+
&self,
11+
mapper: &PageMapper,
12+
vec: &mut [u8],
13+
start_addr: VirtAddr,
14+
end_addr: VirtAddr,
15+
offset: usize,
16+
) -> Result<(), SystemError> {
17+
let total_pages = (end_addr - start_addr) >> MMArch::PAGE_SHIFT;
18+
if vec.len() < total_pages + offset {
19+
return Err(SystemError::EINVAL);
20+
}
21+
22+
if !self.can_do_mincore() {
23+
let pages = (end_addr - start_addr) >> MMArch::PAGE_SHIFT;
24+
vec[offset..offset + pages].fill(0);
25+
return Ok(());
26+
}
27+
// 支持多级页表遍历;在遇到大页时按4K粒度填充
28+
self.mincore_walk_page_range(mapper, start_addr, end_addr, 3, vec, offset);
29+
Ok(())
30+
}
31+
32+
fn mincore_walk_page_range(
33+
&self,
34+
mapper: &PageMapper,
35+
start_addr: VirtAddr,
36+
end_addr: VirtAddr,
37+
level: usize,
38+
vec: &mut [u8],
39+
vec_offset: usize,
40+
) -> usize {
41+
let mut page_count = 0;
42+
let mut start = start_addr;
43+
while start < end_addr {
44+
let entry_size = MMArch::PAGE_SIZE << (level * MMArch::PAGE_ENTRY_SHIFT);
45+
let next = core::cmp::min(end_addr, start + entry_size);
46+
if let Some(entry) = mapper.get_entry(start, level) {
47+
// 大页处理:当上层条目标记为大页时,按子页数量批量填充
48+
if level > 0 && entry.flags().has_flag(MMArch::ENTRY_FLAG_HUGE_PAGE) {
49+
let sub_pages = (next - start) >> MMArch::PAGE_SHIFT;
50+
let val = if entry.present() { 1 } else { 0 };
51+
vec[vec_offset + page_count..vec_offset + page_count + sub_pages].fill(val);
52+
page_count += sub_pages;
53+
} else if level > 0 {
54+
let sub_pages = self.mincore_walk_page_range(
55+
mapper,
56+
start,
57+
next,
58+
level - 1,
59+
vec,
60+
vec_offset + page_count,
61+
);
62+
page_count += sub_pages;
63+
} else {
64+
vec[vec_offset + page_count] = if entry.present() { 1 } else { 0 };
65+
page_count += 1;
66+
}
67+
} else {
68+
let unmapped_pages =
69+
self.mincore_unmapped_range(start, next, vec, vec_offset + page_count);
70+
page_count += unmapped_pages;
71+
}
72+
start = next;
73+
}
74+
page_count
75+
}
76+
77+
fn mincore_unmapped_range(
78+
&self,
79+
start_addr: VirtAddr,
80+
end_addr: VirtAddr,
81+
vec: &mut [u8],
82+
vec_offset: usize,
83+
) -> usize {
84+
let nr = (end_addr - start_addr) >> MMArch::PAGE_SHIFT;
85+
if self.is_anonymous() {
86+
vec[vec_offset..vec_offset + nr].fill(0);
87+
} else {
88+
let guard = self.lock_irqsave();
89+
let pgoff = ((start_addr - guard.region().start()) >> MMArch::PAGE_SHIFT)
90+
+ guard.file_page_offset().unwrap();
91+
if guard.vm_file().is_none() {
92+
vec[vec_offset..vec_offset + nr].fill(0);
93+
return nr;
94+
}
95+
let page_cache = guard.vm_file().unwrap().inode().page_cache();
96+
match page_cache {
97+
Some(page_cache) => {
98+
let cache_guard = page_cache.lock_irqsave();
99+
for i in 0..nr {
100+
if cache_guard.get_page(pgoff + i).is_some() {
101+
vec[vec_offset + i] = 1;
102+
} else {
103+
vec[vec_offset + i] = 0;
104+
}
105+
}
106+
}
107+
None => {
108+
vec[vec_offset..vec_offset + nr].fill(0);
109+
}
110+
}
111+
}
112+
nr
113+
}
114+
115+
pub fn can_do_mincore(&self) -> bool {
116+
//todo: 没有实现vm_ops,这里只能找到匿名映射和文件映射。对于设备映射和其他特殊映射(对应linux中vm_ops有值,但不是文件映射的vma),返回false
117+
if self.is_accessible() {
118+
return true;
119+
} else {
120+
//todo: 若文件不是当前用户所有,需要当前用户对文件有写权限,否则返回false
121+
return true;
122+
}
123+
}
124+
}

kernel/src/mm/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub mod init;
2727
pub mod kernel_mapper;
2828
pub mod madvise;
2929
pub mod memblock;
30+
pub mod mincore;
3031
pub mod mmio_buddy;
3132
pub mod no_init;
3233
pub mod page;

kernel/src/mm/syscall/sys_mincore.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
use crate::arch::interrupt::TrapFrame;
22
use crate::arch::syscall::nr::SYS_MINCORE;
3+
use crate::arch::MMArch;
4+
use crate::libs::align::page_align_up;
5+
use crate::mm::allocator::page_frame::{PageFrameCount, VirtPageFrame};
6+
use crate::mm::ucontext::AddressSpace;
7+
use crate::mm::{verify_area, MemoryManagementArch};
38
use crate::syscall::table::{FormattedSyscallParam, Syscall};
9+
use crate::syscall::user_access::UserBufferWriter;
410
use system_error::SystemError;
511

12+
use crate::mm::VirtAddr;
613
use alloc::vec::Vec;
714

815
pub struct SysMincoreHandle;
@@ -14,9 +21,39 @@ impl Syscall for SysMincoreHandle {
1421

1522
/// ## mincore系统调用
1623
///
17-
/// todo: 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/mm/mincore.c#232 实现mincore
18-
fn handle(&self, _args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
19-
return Err(SystemError::ENOSYS);
24+
/// ## 参数
25+
///
26+
/// - `start_vaddr`:起始地址(已经对齐到页)
27+
/// - `len`:需要遍历的长度
28+
/// - `vec`:用户空间的vec指针
29+
fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
30+
let start_vaddr = VirtAddr::new(Self::start_vaddr(args));
31+
let len = Self::len(args);
32+
let vec = Self::vec(args);
33+
// 未对齐返回 EINVAL,而不是触发 panic
34+
if !start_vaddr.check_aligned(MMArch::PAGE_SIZE) {
35+
return Err(SystemError::EINVAL);
36+
}
37+
38+
if verify_area(start_vaddr, len).is_err() {
39+
return Err(SystemError::ENOMEM);
40+
}
41+
if len == 0 {
42+
return Err(SystemError::EINVAL);
43+
}
44+
let len = page_align_up(len);
45+
let current_address_space = AddressSpace::current()?;
46+
let start_frame = VirtPageFrame::new(start_vaddr);
47+
let page_count = len >> MMArch::PAGE_SHIFT;
48+
49+
// 严格验证 vec 映射与写权限,失败返回 EFAULT
50+
let mut writer = UserBufferWriter::new_checked(vec as *mut u8, page_count, true)?;
51+
let buf: &mut [u8] = writer.buffer(0)?;
52+
let page_count = PageFrameCount::new(page_count);
53+
current_address_space
54+
.read()
55+
.mincore(start_frame, page_count, buf)?;
56+
return Ok(0);
2057
}
2158

2259
/// Formats the syscall arguments for display/debugging purposes.

kernel/src/mm/ucontext.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ impl InnerAddressSpace {
398398
drop(fd_table_guard);
399399

400400
// offset需要4K对齐
401-
if !offset & (MMArch::PAGE_SIZE - 1) == 0 {
401+
if (offset & (MMArch::PAGE_SIZE - 1)) != 0 {
402402
return Err(SystemError::EINVAL);
403403
}
404404
let pgoff = offset >> MMArch::PAGE_SHIFT;
@@ -435,7 +435,10 @@ impl InnerAddressSpace {
435435
// todo!(impl mmap for other file)
436436
// https://github.com/DragonOS-Community/DragonOS/pull/912#discussion_r1765334272
437437
let file = file.unwrap();
438-
let _ = file.inode().mmap(start_vaddr.data(), len, offset);
438+
// 传入实际映射后的起始虚拟地址,而非用户传入的 hint
439+
let _ = file
440+
.inode()
441+
.mmap(start_page.virt_address().data(), len, offset);
439442
return Ok(start_page);
440443
}
441444

@@ -704,6 +707,45 @@ impl InnerAddressSpace {
704707
return Ok(());
705708
}
706709

710+
pub fn mincore(
711+
&self,
712+
start_page: VirtPageFrame,
713+
page_count: PageFrameCount,
714+
vec: &mut [u8],
715+
) -> Result<(), SystemError> {
716+
let mapper = &self.user_mapper.utable;
717+
718+
if self.mappings.contains(start_page.virt_address()).is_none() {
719+
return Err(SystemError::ENOMEM);
720+
}
721+
722+
let mut last_vaddr = start_page.virt_address();
723+
let region = VirtRegion::new(start_page.virt_address(), page_count.bytes());
724+
let mut vmas = self.mappings.conflicts(region).collect::<Vec<_>>();
725+
// 为保证与地址连续性的判断正确,这里按起始地址升序遍历
726+
vmas.sort_by_key(|v| v.lock_irqsave().region().start().data());
727+
let mut offset = 0;
728+
for v in vmas {
729+
let region = *v.lock_irqsave().region();
730+
// 保证相邻的两个vma连续
731+
if region.start() != last_vaddr && last_vaddr != start_page.virt_address() {
732+
return Err(SystemError::ENOMEM);
733+
}
734+
let start_vaddr = last_vaddr;
735+
let end_vaddr = core::cmp::min(region.end(), start_vaddr + page_count.bytes());
736+
v.do_mincore(mapper, vec, start_vaddr, end_vaddr, offset)?;
737+
let page_count_this_vma = (end_vaddr - start_vaddr) >> MMArch::PAGE_SHIFT;
738+
offset += page_count_this_vma;
739+
last_vaddr = end_vaddr;
740+
}
741+
742+
// 校验覆盖完整性:若末尾未覆盖到请求范围,则返回 ENOMEM
743+
if last_vaddr != region.end() {
744+
return Err(SystemError::ENOMEM);
745+
}
746+
747+
return Ok(());
748+
}
707749
pub fn madvise(
708750
&mut self,
709751
start_page: VirtPageFrame,
@@ -974,9 +1016,10 @@ impl UserMappings {
9741016
if guard.region.contains(vaddr) {
9751017
return Some(v.clone());
9761018
}
977-
if guard.region.start >= vaddr
978-
&& if let Some(ref nearest) = nearest {
979-
guard.region.start < nearest.lock_irqsave().region.start
1019+
// 选择起始地址不大于 vaddr 的 VMA 中,起始地址最大的一个
1020+
if guard.region.start <= vaddr
1021+
&& if let Some(ref current) = nearest {
1022+
guard.region.start > current.lock_irqsave().region.start
9801023
} else {
9811024
true
9821025
}

0 commit comments

Comments
 (0)