refactor(page_cache): 引入PageCacheBackend抽象层并重构相关文件系统 (#1686)

- 新增PageCacheBackend trait和SyncPageCacheBackend实现,用于抽象页面缓存后端操作
- 修改PageCache构造函数,支持传入自定义后端
- 重构ext4、fat、tmpfs文件系统使用新的PageCache后端机制
- 优化页面回收和预读逻辑,使用后端接口进行读写操作
- 修复页面错误处理逻辑,移除直接文件读取改用page_cache.read_pages()

Signed-off-by: longjin <longjin@DragonOS.org>
This commit is contained in:
LoGin 2026-01-20 02:19:53 +08:00 committed by GitHub
parent 56b8297ac5
commit d252e73f6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 219 additions and 60 deletions

View File

@ -1,6 +1,6 @@
use crate::{
filesystem::{
page_cache::PageCache,
page_cache::{PageCache, SyncPageCacheBackend},
vfs::{
self, utils::DName, vcore::generate_inode_id, FilePrivateData, IndexNode, InodeFlags,
InodeId, InodeMode,
@ -523,7 +523,13 @@ impl LockedExt4Inode {
// 设置self_ref
guard.self_ref = Arc::downgrade(&inode);
let page_cache = PageCache::new(Some(Arc::downgrade(&inode) as Weak<dyn IndexNode>));
let backend = Arc::new(SyncPageCacheBackend::new(
Arc::downgrade(&inode) as Weak<dyn IndexNode>
));
let page_cache = PageCache::new(
Some(Arc::downgrade(&inode) as Weak<dyn IndexNode>),
Some(backend),
);
guard.page_cache = Some(page_cache);
drop(guard);

View File

@ -19,7 +19,7 @@ use system_error::SystemError;
use crate::driver::base::block::gendisk::GenDisk;
use crate::driver::base::device::device_number::DeviceNumber;
use crate::filesystem::page_cache::PageCache;
use crate::filesystem::page_cache::{PageCache, SyncPageCacheBackend};
use crate::filesystem::vfs::utils::DName;
use crate::filesystem::vfs::{Magic, SpecialNodeData, SuperBlock};
use crate::ipc::pipe::LockedPipeInode;
@ -267,7 +267,13 @@ impl LockedFATInode {
})));
if !inode.0.lock().inode_type.is_dir() {
let page_cache = PageCache::new(Some(Arc::downgrade(&inode) as Weak<dyn IndexNode>));
let backend = Arc::new(SyncPageCacheBackend::new(
Arc::downgrade(&inode) as Weak<dyn IndexNode>
));
let page_cache = PageCache::new(
Some(Arc::downgrade(&inode) as Weak<dyn IndexNode>),
Some(backend),
);
inode.0.lock().page_cache = Some(page_cache);
}

View File

@ -25,12 +25,61 @@ use crate::{
use crate::{libs::align::page_align_up, mm::page::PageType};
static PAGE_CACHE_ID: AtomicUsize = AtomicUsize::new(0);
pub trait PageCacheBackend: Send + Sync + core::fmt::Debug {
fn read_page(&self, index: usize, buf: &mut [u8]) -> Result<usize, SystemError>;
fn write_page(&self, index: usize, buf: &[u8]) -> Result<usize, SystemError>;
fn npages(&self) -> usize;
}
#[derive(Debug)]
pub struct SyncPageCacheBackend {
inode: Weak<dyn IndexNode>,
}
impl SyncPageCacheBackend {
pub fn new(inode: Weak<dyn IndexNode>) -> Self {
Self { inode }
}
}
impl PageCacheBackend for SyncPageCacheBackend {
fn read_page(&self, index: usize, buf: &mut [u8]) -> Result<usize, SystemError> {
let inode = self.inode.upgrade().ok_or(SystemError::EIO)?;
inode.read_sync(index * MMArch::PAGE_SIZE, buf)
}
fn write_page(&self, index: usize, buf: &[u8]) -> Result<usize, SystemError> {
let inode = self.inode.upgrade().ok_or(SystemError::EIO)?;
inode.write_sync(index * MMArch::PAGE_SIZE, buf)
}
fn npages(&self) -> usize {
let inode = match self.inode.upgrade() {
Some(inode) => inode,
None => return 0,
};
match inode.metadata() {
Ok(metadata) => {
let size = metadata.size.max(0) as usize;
if size == 0 {
0
} else {
(size + MMArch::PAGE_SIZE - 1) >> MMArch::PAGE_SHIFT
}
}
Err(_) => 0,
}
}
}
/// 页面缓存
#[derive(Debug)]
pub struct PageCache {
id: usize,
inner: Mutex<InnerPageCache>,
inode: Lazy<Weak<dyn IndexNode>>,
backend: Lazy<Arc<dyn PageCacheBackend>>,
unevictable: AtomicBool,
}
@ -122,6 +171,48 @@ impl InnerPageCache {
Ok(())
}
fn read_pages_from_backend(
&mut self,
start_page_index: usize,
page_num: usize,
) -> Result<(), SystemError> {
if page_num == 0 {
return Ok(());
}
let mut page_buf = vec![0u8; MMArch::PAGE_SIZE * page_num];
let backend = self
.page_cache_ref
.upgrade()
.and_then(|page_cache| page_cache.backend());
if let Some(backend) = backend {
for i in 0..page_num {
let buf_offset = i * MMArch::PAGE_SIZE;
let read_len = backend.read_page(
start_page_index + i,
&mut page_buf[buf_offset..buf_offset + MMArch::PAGE_SIZE],
)?;
if read_len < MMArch::PAGE_SIZE {
page_buf[buf_offset + read_len..buf_offset + MMArch::PAGE_SIZE].fill(0);
}
}
} else {
let inode: Arc<dyn IndexNode> = self
.page_cache_ref
.upgrade()
.unwrap()
.inode
.upgrade()
.unwrap();
inode.read_sync(start_page_index * MMArch::PAGE_SIZE, page_buf.as_mut())?;
}
self.create_pages(start_page_index, page_buf.as_mut())?;
Ok(())
}
/// 创建若干个“零页”并加入 PageCache。
///
/// 与 `create_pages()` 的区别:
@ -257,12 +348,7 @@ impl InnerPageCache {
}
for (page_index, count) in not_exist {
// TODO 这里使用buffer避免多次读取磁盘将来引入异步IO直接写入页面减少内存开销和拷贝
let mut page_buf = vec![0u8; MMArch::PAGE_SIZE * count];
inode.read_sync(page_index * MMArch::PAGE_SIZE, page_buf.as_mut())?;
self.create_pages(page_index, page_buf.as_mut())?;
self.read_pages_from_backend(page_index, count)?;
// 实际要拷贝的内容在文件中的偏移量
let copy_offset = core::cmp::max(page_index * MMArch::PAGE_SIZE, offset);
@ -460,7 +546,10 @@ impl Drop for InnerPageCache {
}
impl PageCache {
pub fn new(inode: Option<Weak<dyn IndexNode>>) -> Arc<PageCache> {
pub fn new(
inode: Option<Weak<dyn IndexNode>>,
backend: Option<Arc<dyn PageCacheBackend>>,
) -> Arc<PageCache> {
let id = PAGE_CACHE_ID.fetch_add(1, Ordering::SeqCst);
Arc::new_cyclic(|weak| Self {
id,
@ -472,6 +561,13 @@ impl PageCache {
}
v
},
backend: {
let v: Lazy<Arc<dyn PageCacheBackend>> = Lazy::new();
if let Some(backend) = backend {
v.init(backend);
}
v
},
unevictable: AtomicBool::new(false),
})
}
@ -495,6 +591,18 @@ impl PageCache {
Ok(())
}
pub fn set_backend(&self, backend: Arc<dyn PageCacheBackend>) -> Result<(), SystemError> {
if self.backend.initialized() {
return Err(SystemError::EINVAL);
}
self.backend.init(backend);
Ok(())
}
pub fn backend(&self) -> Option<Arc<dyn PageCacheBackend>> {
self.backend.try_get().cloned()
}
pub fn lock(&self) -> MutexGuard<'_, InnerPageCache> {
self.inner.lock()
}
@ -505,6 +613,11 @@ impl PageCache {
self.unevictable.store(unevictable, Ordering::Relaxed);
}
pub fn read_pages(&self, start_page_index: usize, page_num: usize) -> Result<(), SystemError> {
let mut guard = self.inner.lock();
guard.read_pages_from_backend(start_page_index, page_num)
}
/// 两阶段读取:持锁收集拷贝项,解锁后拷贝到目标缓冲区,避免用户缺页导致自锁
pub fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, SystemError> {
let (copies, ret) = {

View File

@ -2,7 +2,7 @@ use core::any::Any;
use core::intrinsics::unlikely;
use core::sync::atomic::{AtomicU64, Ordering};
use crate::filesystem::page_cache::PageCache;
use crate::filesystem::page_cache::{PageCache, PageCacheBackend};
use crate::filesystem::vfs::syscall::RenameFlags;
use crate::filesystem::vfs::{FileSystemMakerData, FSMAKER};
use crate::libs::rwsem::RwSem;
@ -46,6 +46,45 @@ const TMPFS_BLOCK_SIZE: u64 = 4096;
const TMPFS_DEFAULT_MIN_SIZE_BYTES: usize = 16 * 1024 * 1024; // 16MiB
const TMPFS_DEFAULT_MAX_SIZE_BYTES: usize = 4 * 1024 * 1024 * 1024; // 4GiB
#[derive(Debug)]
struct TmpfsPageCacheBackend {
inode: Weak<dyn IndexNode>,
}
impl TmpfsPageCacheBackend {
fn new(inode: Weak<dyn IndexNode>) -> Self {
Self { inode }
}
}
impl PageCacheBackend for TmpfsPageCacheBackend {
fn read_page(&self, _index: usize, _buf: &mut [u8]) -> Result<usize, SystemError> {
Ok(0)
}
fn write_page(&self, _index: usize, buf: &[u8]) -> Result<usize, SystemError> {
Ok(buf.len())
}
fn npages(&self) -> usize {
let inode = match self.inode.upgrade() {
Some(inode) => inode,
None => return 0,
};
match inode.metadata() {
Ok(metadata) => {
let size = metadata.size.max(0) as usize;
if size == 0 {
0
} else {
(size + MMArch::PAGE_SIZE - 1) >> MMArch::PAGE_SHIFT
}
}
Err(_) => 0,
}
}
}
fn tmpfs_move_entry_between_dirs(
src_dir: &mut TmpfsInode,
dst_dir: &mut TmpfsInode,
@ -761,7 +800,13 @@ impl IndexNode for LockedTmpfsInode {
// 目前 VFS 使用 read_at/write_at 来读写 symlink 内容readlink/symlink 语义),
// 因此 symlink 也必须有 page_cache 后端,否则会在 write_at/read_at 返回 EIO。
if file_type == FileType::File || file_type == FileType::SymLink {
let pc = PageCache::new(Some(Arc::downgrade(&result) as Weak<dyn IndexNode>));
let backend = Arc::new(TmpfsPageCacheBackend::new(
Arc::downgrade(&result) as Weak<dyn IndexNode>
));
let pc = PageCache::new(
Some(Arc::downgrade(&result) as Weak<dyn IndexNode>),
Some(backend),
);
pc.set_unevictable(true);
result.0.lock().page_cache = Some(pc);
}

View File

@ -699,31 +699,17 @@ impl PageFaultHandler {
// 直接将PageCache中的页面作为要映射的页面
pfm.page = Some(page.clone());
} else {
// TODO 同步预读
// 涉及磁盘IO返回标志为VM_FAULT_MAJOR
ret = VmFaultReason::VM_FAULT_MAJOR;
let mut buffer = vec![0u8; MMArch::PAGE_SIZE];
match file.pread(
backing_pgoff * MMArch::PAGE_SIZE,
MMArch::PAGE_SIZE,
buffer.as_mut_slice(),
) {
Ok(read_len) => {
// 超出文件末尾返回SIGBUS而不是panic
if read_len == 0 {
return VmFaultReason::VM_FAULT_SIGBUS;
}
}
Err(e) => {
log::warn!(
"filemap_fault: pread failed at pgoff {}, err {:?}",
backing_pgoff,
e
);
if let Ok(md) = file.inode().metadata() {
let size = md.size.max(0) as usize;
if size == 0 || backing_pgoff.saturating_mul(MMArch::PAGE_SIZE) >= size {
return VmFaultReason::VM_FAULT_SIGBUS;
}
}
drop(buffer);
ret = VmFaultReason::VM_FAULT_MAJOR;
if page_cache.read_pages(backing_pgoff, 1).is_err() {
return VmFaultReason::VM_FAULT_SIGBUS;
}
let page = page_cache.lock().get_page(backing_pgoff);
if let Some(page) = page {

View File

@ -398,6 +398,7 @@ impl PageReclaimer {
};
let paddr = guard.phys_address();
let inode = page_cache.inode().clone().unwrap().upgrade().unwrap();
let backend = page_cache.backend();
for vma in guard.vma_set() {
let address_space = vma.lock().address_space().and_then(|x| x.upgrade());
@ -437,17 +438,22 @@ impl PageReclaimer {
};
if len > 0 {
let r = inode.write_direct(
page_start,
len,
unsafe {
core::slice::from_raw_parts(
MMArch::phys_2_virt(paddr).unwrap().data() as *const u8,
len,
)
},
Mutex::new(FilePrivateData::Unused).lock(),
);
let data = unsafe {
core::slice::from_raw_parts(
MMArch::phys_2_virt(paddr).unwrap().data() as *const u8,
len,
)
};
let r = if let Some(backend) = backend {
backend.write_page(page_index, data)
} else {
inode.write_direct(
page_start,
len,
data,
Mutex::new(FilePrivateData::Unused).lock(),
)
};
if let Err(e) = r {
log::error!(
"page writeback failed: offset={}, len={}, err={:?}",

View File

@ -119,23 +119,20 @@ impl<'a> ReadaheadControl<'a> {
let ranges = merge_ranges(&missing_pages);
let mut total_read = 0;
let file_size = self.inode.metadata()?.size.max(0) as usize;
if file_size == 0 {
return Ok(0);
}
for (page_index, count) in ranges {
let mut page_buf = alloc::vec![0u8; MMArch::PAGE_SIZE * count];
let offset = page_index << MMArch::PAGE_SHIFT;
let read_len = self.inode.read_sync(offset, &mut page_buf)?;
if read_len == 0 {
let start_offset = page_index * MMArch::PAGE_SIZE;
let end_offset = core::cmp::min((page_index + count) * MMArch::PAGE_SIZE, file_size);
if end_offset <= start_offset {
continue;
}
page_buf.truncate(read_len);
let actual_page_count = (read_len + MMArch::PAGE_SIZE - 1) >> MMArch::PAGE_SHIFT;
let mut page_cache_guard = page_cache.lock();
page_cache_guard.create_pages(page_index, &page_buf)?;
drop(page_cache_guard);
let actual_page_count =
(end_offset - start_offset + MMArch::PAGE_SIZE - 1) >> MMArch::PAGE_SHIFT;
page_cache.read_pages(page_index, actual_page_count)?;
total_read += actual_page_count;
}

View File

@ -235,7 +235,7 @@ impl BpfPerfEvent {
data: SpinLock::new(BpfPerfEventData {
enabled: false,
mmap_page: RingPage::empty(),
page_cache: PageCache::new(None),
page_cache: PageCache::new(None, None),
offset: 0,
}),
}