Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions kernel/src/filesystem/page_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,13 @@ impl InnerPageCache {
self.pages.remove(&offset)
}

fn create_pages(&mut self, start_page_index: usize, buf: &[u8]) -> Result<(), SystemError> {
assert!(buf.len().is_multiple_of(MMArch::PAGE_SIZE));

let page_num = buf.len() / MMArch::PAGE_SIZE;

let len = buf.len();
if len == 0 {
pub fn create_pages(&mut self, start_page_index: usize, buf: &[u8]) -> Result<(), SystemError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么不再需要 assert!(buf.len().is_multiple_of(MMArch::PAGE_SIZE));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文件大小不一定是页面大小的整数倍,
let page_len = core::cmp::min(MMArch::PAGE_SIZE, buf.len() - buf_offset);
后面
dst[..page_len].copy_from_slice(&buf[buf_offset..buf_offset + page_len]);
这样写就能保证比如最后一页这种不满一页的数据也能写入到页面中

if buf.is_empty() {
return Ok(());
}
Comment on lines +65 to 68
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The old implementation had an assertion assert!(buf.len().is_multiple_of(MMArch::PAGE_SIZE)) which enforced that the buffer length must be a multiple of page size. The new implementation removes this assertion and handles partial pages. While this is more flexible, if callers were relying on this assertion to catch bugs, they may now silently get unexpected behavior. Consider documenting this behavior change or adding a debug assertion to help catch potential misuse during development.

Copilot uses AI. Check for mistakes.

let page_num = ((buf.len() - 1) >> MMArch::PAGE_SHIFT) + 1;

let mut page_manager_guard = page_manager_lock_irqsave();

for i in 0..page_num {
Expand All @@ -90,12 +87,15 @@ impl InnerPageCache {
&mut LockedFrameAllocator,
)?;

let page_len = core::cmp::min(MMArch::PAGE_SIZE, buf.len() - buf_offset);

let mut page_guard = page.write_irqsave();
unsafe {
page_guard.copy_from_slice(&buf[buf_offset..buf_offset + MMArch::PAGE_SIZE]);
let dst = page_guard.as_slice_mut();
dst[..page_len].copy_from_slice(&buf[buf_offset..buf_offset + page_len]);
}

self.add_page(page_index, &page);
self.add_page(start_page_index + i, &page);
}

Ok(())
Expand Down
72 changes: 71 additions & 1 deletion kernel/src/filesystem/vfs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use system_error::SystemError;
use super::{FileType, IndexNode, InodeId, Metadata, SpecialNodeData};
use crate::process::pid::PidPrivateData;
use crate::{
arch::MMArch,
driver::{
base::{block::SeekFrom, device::DevicePrivateData},
tty::tty_device::TtyFilePrivateData,
Expand All @@ -18,6 +19,11 @@ use crate::{
},
ipc::{kill::kill_process, pipe::PipeFsPrivateData},
libs::{rwlock::RwLock, spinlock::SpinLock},
mm::{
page::PageFlags,
readahead::{page_cache_async_readahead, page_cache_sync_readahead, FileReadaheadState},
MemoryManagementArch,
},
process::{cred::Cred, resource::RLimitID, ProcessControlBlock, ProcessManager, RawPid},
};

Expand Down Expand Up @@ -151,6 +157,8 @@ pub struct File {
close_on_exec: AtomicBool,
/// owner
pid: SpinLock<Option<Arc<ProcessControlBlock>>>,
/// 预读状态
ra_state: SpinLock<FileReadaheadState>,
}

impl File {
Expand Down Expand Up @@ -182,6 +190,7 @@ impl File {
cred: ProcessManager::current_pcb().cred(),
close_on_exec: AtomicBool::new(close_on_exec),
pid: SpinLock::new(None),
ra_state: SpinLock::new(FileReadaheadState::new()),
};

return Ok(f);
Expand Down Expand Up @@ -251,6 +260,58 @@ impl File {
self.do_write(offset, len, buf, false)
}

fn file_readahead(&self, offset: usize, len: usize) -> Result<(), SystemError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果该inode的page cache已经存在(比如当前文件已经预读过,或者当前inode对应的其他file已经预读过),那还会预读,创建新的Page cache吗?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

page_cache是inode的,预读前先检查页面是否已经存在。逻辑上都是从第一个缺页的地方开始实际的读

let page_cache = match self.inode.page_cache() {
Some(page_cahce) => page_cahce,
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: page_cahce should be page_cache.

Suggested change
Some(page_cahce) => page_cahce,
Some(page_cache) => page_cache,

Copilot uses AI. Check for mistakes.
None => return Ok(()),
};

let start_page = offset >> MMArch::PAGE_SHIFT;
let end_page = (offset + len - 1) >> MMArch::PAGE_SHIFT;

let (async_trigger_page, missing_page) = {
let page_cache_guard = page_cache.lock_irqsave();
let mut async_trigger_page = None;
let mut missing_page = None;

for index in start_page..=end_page {
match page_cache_guard.get_page(index) {
Some(page)
if page
.read_irqsave()
.flags()
.contains(PageFlags::PG_READAHEAD) =>
{
async_trigger_page = Some((index, page.clone()));
break;
}
None => {
missing_page = Some(index);
break;
}
_ => {}
}
}
(async_trigger_page, missing_page)
};

if let Some((index, page)) = async_trigger_page {
let mut ra_state = self.ra_state.lock().clone();
let req_pages = end_page - index + 1;
page.write_irqsave().remove_flags(PageFlags::PG_READAHEAD);

page_cache_async_readahead(&page_cache, &self.inode, &mut ra_state, index, req_pages)?;
*self.ra_state.lock() = ra_state;
} else if let Some(index) = missing_page {
let mut ra_state = self.ra_state.lock().clone();
let req_pages = end_page - index + 1;

page_cache_sync_readahead(&page_cache, &self.inode, &mut ra_state, index, req_pages)?;
*self.ra_state.lock() = ra_state;
}
Ok(())
}

pub fn do_read(
&self,
offset: usize,
Expand All @@ -264,6 +325,10 @@ impl File {
return Err(SystemError::ENOBUFS);
}

if self.file_type == FileType::File && !self.mode().contains(FileMode::O_DIRECT) {
self.file_readahead(offset, len)?;
}

let len = if self.mode().contains(FileMode::O_DIRECT) {
self.inode
.read_direct(offset, len, buf, self.private_data.lock())
Expand All @@ -272,11 +337,15 @@ impl File {
.read_at(offset, len, buf, self.private_data.lock())
}?;

if len > 0 {
let last_page_readed = (offset + len - 1) >> MMArch::PAGE_SHIFT;
self.ra_state.lock().prev_index = last_page_readed as i64;
}

if update_offset {
self.offset
.fetch_add(len, core::sync::atomic::Ordering::SeqCst);
}

Ok(len)
}

Expand Down Expand Up @@ -521,6 +590,7 @@ impl File {
cred: self.cred.clone(),
close_on_exec: AtomicBool::new(self.close_on_exec.load(Ordering::SeqCst)),
pid: SpinLock::new(None),
ra_state: SpinLock::new(self.ra_state.lock().clone()),
};
// 调用inode的open方法,让inode知道有新的文件打开了这个inode
// TODO: reopen is not a good idea for some inodes, need a better design
Expand Down
1 change: 1 addition & 0 deletions kernel/src/libs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ pub mod name;
pub mod decompress;

pub mod pod;
pub mod ranges;
120 changes: 120 additions & 0 deletions kernel/src/libs/ranges.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use crate::alloc::vec::Vec;

/// 合并连续的整数为范围
///
/// ## 参数
/// - `set`: 整数集合,必须已排序
///
/// ## 返回值
/// - `Vec<(start, count)>`: 范围列表
///
/// ## 示例
/// ```
/// merge_ranges(&[1, 2, 3, 5, 6, 8])
/// // 返回 [(1, 3), (5, 2), (8, 1)]
/// ```
pub fn merge_ranges(set: &[usize]) -> Vec<(usize, usize)> {
if set.is_empty() {
return Vec::new();
}

let mut ranges = Vec::new();
let mut start = set[0];
let mut count = 1;

for &page_index in &set[1..] {
if page_index == start + count {
count += 1; // 连续,扩展范围
} else {
ranges.push((start, count)); // 保存当前范围
start = page_index;
count = 1;
}
}

ranges.push((start, count)); // 最后一个范围
ranges
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_empty_input() {
// 边界情况:空输入
let result = merge_ranges(&[]);
assert_eq!(result, vec![]);
}

#[test]
fn test_single_page() {
// 边界情况:单个页面
let result = merge_ranges(&[5]);
assert_eq!(result, vec![(5, 1)]);
}

#[test]
fn test_fully_consecutive() {
// 完全连续的页面
let result = merge_ranges(&[1, 2, 3, 4, 5]);
assert_eq!(result, vec![(1, 5)]);
}

#[test]
fn test_no_consecutive() {
// 完全不连续的页面
let result = merge_ranges(&[1, 3, 5, 7, 9]);
assert_eq!(result, vec![(1, 1), (3, 1), (5, 1), (7, 1), (9, 1)]);
}

#[test]
fn test_mixed_ranges() {
// 文档示例:混合连续和不连续
let result = merge_ranges(&[1, 2, 3, 5, 6, 8]);
assert_eq!(result, vec![(1, 3), (5, 2), (8, 1)]);
}

#[test]
fn test_two_consecutive_pairs() {
// 两对连续的范围
let result = merge_ranges(&[0, 1, 10, 11]);
assert_eq!(result, vec![(0, 2), (10, 2)]);
}

#[test]
fn test_start_from_zero() {
// 从 0 开始的连续范围
let result = merge_ranges(&[0, 1, 2]);
assert_eq!(result, vec![(0, 3)]);
}

#[test]
fn test_large_gap() {
// 大间隔的不连续页面
let result = merge_ranges(&[0, 100, 200]);
assert_eq!(result, vec![(0, 1), (100, 1), (200, 1)]);
}

#[test]
fn test_long_consecutive_sequence() {
// 长连续序列(模拟大文件预读)
let input: Vec<usize> = (0..128).collect();
let result = merge_ranges(&input);
assert_eq!(result, vec![(0, 128)]);
}

#[test]
fn test_alternating_pattern() {
// 交替的连续和间断模式
let result = merge_ranges(&[1, 2, 4, 5, 7, 8, 10]);
assert_eq!(result, vec![(1, 2), (4, 2), (7, 2), (10, 1)]);
}

#[test]
fn test_real_world_sparse() {
// 真实场景:稀疏的页面缓存(部分页面已存在)
let result = merge_ranges(&[0, 5, 6, 7, 12, 20, 21]);
assert_eq!(result, vec![(0, 1), (5, 3), (12, 1), (20, 2)]);
}
}
5 changes: 3 additions & 2 deletions kernel/src/mm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub mod mmio_buddy;
pub mod no_init;
pub mod page;
pub mod percpu;
pub mod readahead;
pub mod syscall;
pub mod sysfs;
pub mod truncate;
Expand Down Expand Up @@ -554,13 +555,13 @@ pub trait MemoryManagementArch: Clone + Copy + Debug {

/// @brief 读取指定虚拟地址的值,并假设它是类型T的指针
#[inline(always)]
unsafe fn read<T>(address: VirtAddr) -> T {
unsafe fn read<T: Sized>(address: VirtAddr) -> T {
return ptr::read(address.data() as *const T);
}

/// @brief 将value写入到指定的虚拟地址
#[inline(always)]
unsafe fn write<T>(address: VirtAddr, value: T) {
unsafe fn write<T: Sized>(address: VirtAddr, value: T) {
ptr::write(address.data() as *mut T, value);
}

Expand Down
1 change: 1 addition & 0 deletions kernel/src/mm/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ bitflags! {
const PG_RECLAIM = 1 << 18;
const PG_SWAPBACKED = 1 << 19;
const PG_UNEVICTABLE = 1 << 20;
const PG_READAHEAD = 1 << 21;
}
}

Expand Down
Loading