From 20dca42288776d184f36c3915edeac38eef0da98 Mon Sep 17 00:00:00 2001 From: longjin Date: Mon, 15 Dec 2025 01:43:44 +0800 Subject: [PATCH] feat(block_cache): Enhance CacheBlock and BlockCache functionality - Added `from_slice` method to `CacheBlock` for creating instances directly from slices, avoiding unnecessary allocations. - Introduced `write_data` method in `CacheBlock` to allow in-place updates of block data. - Updated `insert_one_block` and `immediate_write` methods in `BlockCache` to accept slices instead of vectors, improving performance and memory usage. - Implemented error handling for block size validation in multiple locations to ensure data integrity. - Change `FileMapInfo::page_cache` to `Weak` to fix a memory leak caused by reference cycles. Signed-off-by: longjin --- kernel/src/driver/block/cache/cache_block.rs | 19 ++++++ .../driver/block/cache/cached_block_device.rs | 60 +++++++++++++++---- kernel/src/filesystem/page_cache.rs | 5 +- kernel/src/mm/page.rs | 23 +++++-- 4 files changed, 84 insertions(+), 23 deletions(-) diff --git a/kernel/src/driver/block/cache/cache_block.rs b/kernel/src/driver/block/cache/cache_block.rs index e7b79cce2..9a1512797 100644 --- a/kernel/src/driver/block/cache/cache_block.rs +++ b/kernel/src/driver/block/cache/cache_block.rs @@ -37,6 +37,25 @@ impl CacheBlock { CacheBlock::new(space_box, CacheBlockFlag::Unwrited, lba_id) } + /// 从 slice 创建一个 CacheBlock(避免先构造 Vec 再 into_boxed_slice 的额外分配/拷贝步骤)。 + pub fn from_slice(lba_id: BlockId, data: &[u8]) -> Result { + if data.len() != BLOCK_SIZE { + return Err(BlockCacheError::BlockSizeError); + } + let mut v = Vec::with_capacity(BLOCK_SIZE); + v.extend_from_slice(data); + Ok(Self::from_data(lba_id, v)) + } + + /// 用新数据覆盖当前 cache block(用于 write-through 更新)。 + pub fn write_data(&mut self, data: &[u8]) -> Result<(), BlockCacheError> { + if data.len() != BLOCK_SIZE { + return Err(BlockCacheError::BlockSizeError); + } + self.data.copy_from_slice(data); + Ok(()) + } + pub fn _set_flag(&mut self, _flag: CacheBlockFlag) -> Option<()> { todo!() } diff --git a/kernel/src/driver/block/cache/cached_block_device.rs b/kernel/src/driver/block/cache/cached_block_device.rs index 9d7f2020f..35eb35597 100644 --- a/kernel/src/driver/block/cache/cached_block_device.rs +++ b/kernel/src/driver/block/cache/cached_block_device.rs @@ -145,10 +145,12 @@ impl BlockCache { let count = f_data_vec.len(); for i in f_data_vec { let index = i.index(); - Self::insert_one_block( - i.lba_id(), - data[index * BLOCK_SIZE..(index + 1) * BLOCK_SIZE].to_vec(), - )?; + let start = index * BLOCK_SIZE; + let end = (index + 1) * BLOCK_SIZE; + if end > data.len() { + return Err(BlockCacheError::BlockSizeError); + } + Self::insert_one_block(i.lba_id(), &data[start..end])?; } Ok(count) } @@ -163,7 +165,7 @@ impl BlockCache { /// ## 返回值: /// Ok(()):表示插入成功 /// Err(BlockCacheError) :一般来说不会产生错误,这里产生错误的原因只有插入时还没有初始化(一般也很难发生) - fn insert_one_block(lba_id: BlockId, data: Vec) -> Result<(), BlockCacheError> { + fn insert_one_block(lba_id: BlockId, data: &[u8]) -> Result<(), BlockCacheError> { let space = unsafe { space()? }; space.insert(lba_id, data) } @@ -181,12 +183,28 @@ impl BlockCache { pub fn immediate_write( lba_id_start: BlockId, count: usize, - _data: &[u8], + data: &[u8], ) -> Result { + if data.len() < count * BLOCK_SIZE { + return Err(BlockCacheError::BlockSizeError); + } + let mapper = unsafe { mapper()? }; + let space = unsafe { space()? }; let block_iter = BlockIter::new(lba_id_start, count, BLOCK_SIZE); - for i in block_iter { - mapper.remove(i.lba_id()); + for (idx, i) in block_iter.enumerate() { + let lba_id = i.lba_id(); + let start = idx * BLOCK_SIZE; + let end = (idx + 1) * BLOCK_SIZE; + let block_data = &data[start..end]; + + // write-through:如果 cache 中已有该块,更新其内容;否则插入以保持 cache 热度 + if let Some(addr) = mapper.find(lba_id) { + space.write(addr, block_data)?; + } else { + // 不要 remove 映射(会导致 cache 空间和 slab 使用量单调增长) + space.insert(lba_id, block_data)?; + } } Ok(count) } @@ -212,9 +230,14 @@ impl LockedCacheSpace { todo!() } - pub fn insert(&mut self, lba_id: BlockId, data: Vec) -> Result<(), BlockCacheError> { + pub fn insert(&mut self, lba_id: BlockId, data: &[u8]) -> Result<(), BlockCacheError> { unsafe { self.0.get_mut().insert(lba_id, data) } } + + /// 写入已存在的 cache block(write-through 更新) + pub fn write(&mut self, addr: CacheBlockAddr, data: &[u8]) -> Result<(), BlockCacheError> { + unsafe { self.0.get_mut().write(addr, data) } + } } /// # 结构功能 @@ -273,9 +296,9 @@ impl CacheSpace { /// /// ## 返回值: /// Ok(()) - pub fn insert(&mut self, lba_id: BlockId, data: Vec) -> Result<(), BlockCacheError> { - // CacheBlock是cached block的基本单位,这里使用data生成一个CacheBlock用于向Cache空间中插入块 - let data_block = CacheBlock::from_data(lba_id, data); + pub fn insert(&mut self, lba_id: BlockId, data: &[u8]) -> Result<(), BlockCacheError> { + // CacheBlock是cached block的基本单位 + let data_block = CacheBlock::from_slice(lba_id, data)?; let mapper = unsafe { mapper()? }; // 这里我设计了cache的一个threshold,如果不超过阈值就可以append,否则只能替换 if self.frame_selector.can_append() { @@ -303,6 +326,16 @@ impl CacheSpace { Ok(()) } } + + pub fn write(&mut self, addr: CacheBlockAddr, data: &[u8]) -> Result<(), BlockCacheError> { + if addr > self.frame_selector.size() { + return Err(BlockCacheError::InsufficientCacheSpace); + } + if addr >= self.root.len() { + return Err(BlockCacheError::InsufficientCacheSpace); + } + self.root[addr].write_data(data) + } } struct LockedCacheMapper { @@ -345,7 +378,8 @@ impl CacheMapper { /// # 函数的功能 /// 插入操作 pub fn insert(&mut self, lba_id: BlockId, caddr: CacheBlockAddr) -> Option<()> { - self.map.insert(lba_id, caddr)?; + // HashMap::insert 返回的是旧值(新 key 时为 None),不能用 `?` 否则新插入永远失败 + self.map.insert(lba_id, caddr); Some(()) } /// # 函数的功能 diff --git a/kernel/src/filesystem/page_cache.rs b/kernel/src/filesystem/page_cache.rs index 67e635d7c..330de215a 100644 --- a/kernel/src/filesystem/page_cache.rs +++ b/kernel/src/filesystem/page_cache.rs @@ -84,10 +84,7 @@ impl InnerPageCache { let page = page_manager_guard.create_one_page( PageType::File(FileMapInfo { - page_cache: self - .page_cache_ref - .upgrade() - .expect("failed to get self_arc of pagecache"), + page_cache: self.page_cache_ref.clone(), index: page_index, }), PageFlags::PG_LRU, diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 82ef8f7f0..ce5215442 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -9,7 +9,7 @@ use core::{ use system_error::SystemError; use unified_init::macros::unified_init; -use alloc::sync::Arc; +use alloc::sync::{Arc, Weak}; use hashbrown::{HashMap, HashSet}; use log::{error, info}; use lru::LruCache; @@ -332,7 +332,6 @@ impl PageReclaimer { for page in victims { let mut guard = page.write_irqsave(); if let PageType::File(info) = guard.page_type().clone() { - let page_cache = info.page_cache; let page_index = info.index; let paddr = guard.phys_address(); @@ -342,7 +341,12 @@ impl PageReclaimer { } // 删除页面:顺序为 page_cache -> page_manager,避免原有的 reclaimer 锁参与死锁 - page_cache.lock_irqsave().remove_page(page_index); + // + // FileMapInfo 内保存 Weak 以避免 PageCache <-> Page 的强引用环。 + // 如果此时 PageCache 已被释放(upgrade 失败),说明其 pages 映射也已销毁,无需再 remove。 + if let Some(page_cache) = info.page_cache.upgrade() { + page_cache.lock_irqsave().remove_page(page_index); + } page_manager_lock_irqsave().remove_page(&paddr); } } @@ -366,7 +370,13 @@ impl PageReclaimer { // log::debug!("page writeback: {:?}", guard.phys_addr); let (page_cache, page_index) = match guard.page_type() { - PageType::File(info) => (info.page_cache.clone(), info.index), + PageType::File(info) => match info.page_cache.upgrade() { + Some(page_cache) => (page_cache, info.index), + None => { + log::warn!("try to writeback a file page but page_cache already dropped"); + return; + } + }, _ => { log::warn!("try to writeback a non-file page"); return; @@ -584,7 +594,7 @@ impl InnerPage { pub fn page_cache(&self) -> Option> { match &self.page_type { - PageType::File(info) => Some(info.page_cache.clone()), + PageType::File(info) => info.page_cache.upgrade(), _ => None, } } @@ -702,7 +712,8 @@ pub enum PageType { #[derive(Debug, Clone)] pub struct FileMapInfo { - pub page_cache: Arc, + /// 指向页缓存的弱引用,避免 PageCache -> Page -> PageCache 的强引用环导致页面无法释放。 + pub page_cache: Weak, /// 在pagecache中的偏移 pub index: usize, }