From eb49bb993a39964f92494ec3effafed3fb9adfd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E4=BF=8A?= <110876916+ZZJJWarth@users.noreply.github.com> Date: Sun, 7 Apr 2024 14:03:51 +0800 Subject: [PATCH] =?UTF-8?q?BlockCache-read=20cache=E6=94=AF=E6=8C=81=20(#5?= =?UTF-8?q?21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持block cache的读缓存 --- kernel/src/driver/base/block/block_device.rs | 95 +++- kernel/src/driver/base/block/mod.rs | 1 - kernel/src/driver/block/cache/cache_block.rs | 54 +++ kernel/src/driver/block/cache/cache_iter.rs | 102 +++++ .../driver/block/cache/cached_block_device.rs | 422 ++++++++++++++++++ kernel/src/driver/block/cache/mod.rs | 19 + kernel/src/driver/block/mod.rs | 1 + kernel/src/driver/disk/ahci/ahcidisk.rs | 6 +- kernel/src/driver/disk/ahci/mod.rs | 2 + kernel/src/driver/mod.rs | 1 + kernel/src/filesystem/fat/bpb.rs | 2 +- kernel/src/filesystem/fat/fs.rs | 8 +- user/apps/test-blockcache/.cargo/config.toml | 2 + user/apps/test-blockcache/.gitignore | 3 + user/apps/test-blockcache/Cargo.toml | 10 + user/apps/test-blockcache/Makefile | 41 ++ user/apps/test-blockcache/README.md | 8 + user/apps/test-blockcache/src/main.rs | 22 + user/dadk/config/test_blockcache_0_1_0.dadk | 24 + 19 files changed, 797 insertions(+), 26 deletions(-) create mode 100644 kernel/src/driver/block/cache/cache_block.rs create mode 100644 kernel/src/driver/block/cache/cache_iter.rs create mode 100644 kernel/src/driver/block/cache/cached_block_device.rs create mode 100644 kernel/src/driver/block/cache/mod.rs create mode 100644 kernel/src/driver/block/mod.rs create mode 100644 user/apps/test-blockcache/.cargo/config.toml create mode 100644 user/apps/test-blockcache/.gitignore create mode 100644 user/apps/test-blockcache/Cargo.toml create mode 100644 user/apps/test-blockcache/Makefile create mode 100644 user/apps/test-blockcache/README.md create mode 100644 user/apps/test-blockcache/src/main.rs create mode 100644 user/dadk/config/test_blockcache_0_1_0.dadk diff --git a/kernel/src/driver/base/block/block_device.rs b/kernel/src/driver/base/block/block_device.rs index 811b9576..37fd975a 100644 --- a/kernel/src/driver/base/block/block_device.rs +++ b/kernel/src/driver/base/block/block_device.rs @@ -1,14 +1,17 @@ /// 引入Module use crate::{ - driver::base::{ - device::{ - device_number::{DeviceNumber, Major}, - Device, DeviceError, IdTable, BLOCKDEVS, - }, - map::{ - DeviceStruct, DEV_MAJOR_DYN_END, DEV_MAJOR_DYN_EXT_END, DEV_MAJOR_DYN_EXT_START, - DEV_MAJOR_HASH_SIZE, DEV_MAJOR_MAX, + driver::{ + base::{ + device::{ + device_number::{DeviceNumber, Major}, + Device, DeviceError, IdTable, BLOCKDEVS, + }, + map::{ + DeviceStruct, DEV_MAJOR_DYN_END, DEV_MAJOR_DYN_EXT_END, DEV_MAJOR_DYN_EXT_START, + DEV_MAJOR_HASH_SIZE, DEV_MAJOR_MAX, + }, }, + block::cache::{cached_block_device::BlockCache, BlockCacheError, BLOCK_SIZE}, }, kerror, }; @@ -195,7 +198,7 @@ pub trait BlockDevice: Device { /// @return: 如果操作成功,返回 Ok(操作的长度) 其中单位是字节; /// 否则返回Err(错误码),其中错误码为负数; /// 如果操作异常,但是并没有检查出什么错误,将返回Err(已操作的长度) - fn read_at( + fn read_at_sync( &self, lba_id_start: BlockId, count: usize, @@ -209,7 +212,7 @@ pub trait BlockDevice: Device { /// @return: 如果操作成功,返回 Ok(操作的长度) 其中单位是字节; /// 否则返回Err(错误码),其中错误码为负数; /// 如果操作异常,但是并没有检查出什么错误,将返回Err(已操作的长度) - fn write_at( + fn write_at_sync( &self, lba_id_start: BlockId, count: usize, @@ -240,8 +243,72 @@ pub trait BlockDevice: Device { /// @brief 返回当前磁盘上的所有分区的Arc指针数组 fn partitions(&self) -> Vec>; + /// # 函数的功能 + /// 经由Cache对块设备的读操作 + fn read_at( + &self, + lba_id_start: BlockId, + count: usize, + buf: &mut [u8], + ) -> Result { + self.cache_read(lba_id_start, count, buf) + } + + /// # 函数的功能 + /// 经由Cache对块设备的写操作 + fn write_at( + &self, + lba_id_start: BlockId, + count: usize, + buf: &[u8], + ) -> Result { + self.cache_write(lba_id_start, count, buf) + } + + /// # 函数的功能 + /// 其功能对外而言和read_at函数完全一致,但是加入blockcache的功能 + fn cache_read( + &self, + lba_id_start: BlockId, + count: usize, + buf: &mut [u8], + ) -> Result { + let cache_response = BlockCache::read(lba_id_start, count, buf); + if let Err(e) = cache_response { + match e { + BlockCacheError::StaticParameterError => { + BlockCache::init(); + let ans = self.read_at_sync(lba_id_start, count, buf)?; + return Ok(ans); + } + BlockCacheError::BlockFaultError(fail_vec) => { + let ans = self.read_at_sync(lba_id_start, count, buf)?; + let _ = BlockCache::insert(fail_vec, buf); + return Ok(ans); + } + _ => { + let ans = self.read_at_sync(lba_id_start, count, buf)?; + return Ok(ans); + } + } + } else { + return Ok(count * BLOCK_SIZE); + } + } + + /// # 函数功能 + /// 其功能对外而言和write_at函数完全一致,但是加入blockcache的功能 + fn cache_write( + &self, + lba_id_start: BlockId, + count: usize, + buf: &[u8], + ) -> Result { + let _cache_response = BlockCache::immediate_write(lba_id_start, count, buf); + self.write_at_sync(lba_id_start, count, buf) + } + fn write_at_bytes(&self, offset: usize, len: usize, buf: &[u8]) -> Result { - // assert!(len <= buf.len()); if len > buf.len() { return Err(SystemError::E2BIG); } @@ -272,7 +339,6 @@ pub trait BlockDevice: Device { } } return Ok(len); - //self.0.lock().write_at(lba_id_start, count, buf) } fn read_at_bytes( @@ -314,11 +380,6 @@ pub trait BlockDevice: Device { } } return Ok(len); - - // kdebug!( - // "ahci read at {lba_id_start}, count={count}, lock={:?}", - // self.0 - // ); } } diff --git a/kernel/src/driver/base/block/mod.rs b/kernel/src/driver/base/block/mod.rs index 5a092a0b..89bbeb01 100644 --- a/kernel/src/driver/base/block/mod.rs +++ b/kernel/src/driver/base/block/mod.rs @@ -1,6 +1,5 @@ pub mod block_device; pub mod disk_info; - #[derive(Debug)] #[allow(dead_code)] pub enum SeekFrom { diff --git a/kernel/src/driver/block/cache/cache_block.rs b/kernel/src/driver/block/cache/cache_block.rs new file mode 100644 index 00000000..e7b79cce --- /dev/null +++ b/kernel/src/driver/block/cache/cache_block.rs @@ -0,0 +1,54 @@ +use alloc::{boxed::Box, vec::Vec}; + +use crate::driver::base::block::block_device::BlockId; + +use super::{BlockCacheError, BLOCK_SIZE}; + +/// # 枚举功能 +/// 该枚举设计来是用于实现回写法的,但是目前并未使用 +#[allow(dead_code)] +pub enum CacheBlockFlag { + Unused, + Unwrited, + Writed, +} + +pub type CacheBlockAddr = usize; + +/// # 结构功能 +/// 存储数据的最小单位 +pub struct CacheBlock { + data: Box<[u8]>, + _flag: CacheBlockFlag, + lba_id: BlockId, +} + +impl CacheBlock { + pub fn new(data: Box<[u8]>, flag: CacheBlockFlag, lba_id: BlockId) -> Self { + CacheBlock { + data, + _flag: flag, + lba_id, + } + } + + pub fn from_data(lba_id: BlockId, data: Vec) -> Self { + let space_box = data.into_boxed_slice(); + CacheBlock::new(space_box, CacheBlockFlag::Unwrited, lba_id) + } + + pub fn _set_flag(&mut self, _flag: CacheBlockFlag) -> Option<()> { + todo!() + } + pub fn data(&self, buf: &mut [u8]) -> Result { + if buf.len() != BLOCK_SIZE { + return Err(BlockCacheError::BlockSizeError); + } + buf.copy_from_slice(&self.data); + return Ok(BLOCK_SIZE); + } + + pub fn lba_id(&self) -> BlockId { + self.lba_id + } +} diff --git a/kernel/src/driver/block/cache/cache_iter.rs b/kernel/src/driver/block/cache/cache_iter.rs new file mode 100644 index 00000000..18ea9d07 --- /dev/null +++ b/kernel/src/driver/block/cache/cache_iter.rs @@ -0,0 +1,102 @@ +use crate::driver::base::block::block_device::BlockId; + +/// # 结构功能 +/// 一个简单的结构体,是BlockIter的输出 +#[derive(Debug)] +pub struct BlockData { + /// 表示单个块对应的lba_id + lba_id: BlockId, + /// 表示该块在buf中的起始地址,目前并没有作用(例如:若该块是第2个块,那么该数据成员值为2*BLOCK_SIZE) + _data_start_addr: BlockId, + /// 表示该块的大小 + _block_size: usize, +} + +impl BlockData { + pub fn new(lba_id: BlockId, data_start_addr: BlockId, block_size: usize) -> Self { + Self { + lba_id, + _data_start_addr: data_start_addr, + _block_size: block_size, + } + } + #[inline] + pub fn lba_id(&self) -> BlockId { + self.lba_id + } + #[inline] + pub fn _data_start_addr(&self) -> BlockId { + self._data_start_addr + } + #[inline] + pub fn _block_size(&self) -> usize { + self._block_size + } +} + +/// # 结构功能 +/// 块迭代器,它获取需求(起始块,连续块的个数),并将连续的块输出为单一的块(如你需要读取lba_id为10~20的连续块,它就可以输出10,11...,20的BlockData) +#[derive(Copy, Clone)] +pub struct BlockIter { + /// 表示起始块的lba_id + lba_id_start: BlockId, + /// 表示从起始块开始你需要读多少个块 + count: usize, + /// 表示当前遍历到第几个块了 + current: usize, + /// 规定块的大小 + block_size: usize, +} + +impl BlockIter { + pub fn new(lba_id_start: BlockId, count: usize, block_size: usize) -> Self { + Self { + lba_id_start, + count, + block_size, + current: 0, + } + } +} + +impl Iterator for BlockIter { + type Item = BlockData; + + fn next(&mut self) -> Option { + if self.current < self.count { + let ans = BlockData::new( + self.lba_id_start + self.current, + self.current * self.block_size, + self.block_size, + ); + self.current += 1; + Some(ans) + } else { + None + } + } +} + +/// # 结构功能 +/// 表示缺块信息的数据结构,往往在读取的时候发现缺块并产生FailData,在插入的时候使用FailData +pub struct FailData { + /// 表示缺块的lba_id + lba_id: BlockId, + /// 表示缺块在buf中的位置,用于在insert的时候定位缺块数据的位置 + index: usize, +} + +impl FailData { + pub fn new(lba_id: BlockId, index: usize) -> Self { + FailData { lba_id, index } + } + #[inline] + pub fn lba_id(&self) -> BlockId { + self.lba_id + } + /// # 函数的功能 + /// 该函数返回的是缺块在buf中的位置,比如:index=1,那么我们就应该取buf\[512..1024\] + pub fn index(&self) -> usize { + self.index + } +} diff --git a/kernel/src/driver/block/cache/cached_block_device.rs b/kernel/src/driver/block/cache/cached_block_device.rs new file mode 100644 index 00000000..29443042 --- /dev/null +++ b/kernel/src/driver/block/cache/cached_block_device.rs @@ -0,0 +1,422 @@ +use alloc::{boxed::Box, vec::Vec}; +use hashbrown::HashMap; + +use crate::{driver::base::block::block_device::BlockId, libs::rwlock::RwLock}; + +use super::{ + cache_block::{CacheBlock, CacheBlockAddr}, + cache_iter::{BlockIter, FailData}, + BlockCacheError, BLOCK_SIZE, BLOCK_SIZE_LOG, CACHE_THRESHOLD, +}; + +static mut CSPACE: Option = None; +static mut CMAPPER: Option = None; +/// # 结构功能 +/// 该结构体向外提供BlockCache服务 +pub struct BlockCache; + +unsafe fn mapper() -> Result<&'static mut LockedCacheMapper, BlockCacheError> { + unsafe { + match &mut CMAPPER { + Some(x) => return Ok(x), + None => return Err(BlockCacheError::StaticParameterError), + } + }; +} + +unsafe fn space() -> Result<&'static mut LockedCacheSpace, BlockCacheError> { + unsafe { + match &mut CSPACE { + Some(x) => return Ok(x), + None => return Err(BlockCacheError::StaticParameterError), + } + }; +} + +impl BlockCache { + /// # 函数的功能 + /// 初始化BlockCache需要的结构体 + pub fn init() { + unsafe { + CSPACE = Some(LockedCacheSpace::new(CacheSpace::new())); + CMAPPER = Some(LockedCacheMapper::new(CacheMapper::new())); + } + kdebug!("BlockCache Initialized!"); + } + /// # 函数的功能 + /// 使用blockcache进行对块设备进行连续块的读操作 + /// + /// ## 参数: + /// - 'lba_id_start' :连续块的起始块的lba_id + /// - 'count' :从连续块算起需要读多少块 + /// - 'buf' :读取出来的数据存放在buf中 + /// + /// ## 返回值: + /// - Ok(usize) :表示读取块的个数 + /// - Err(BlockCacheError::BlockFaultError) :缺块的情况下,返回读取失败的块的数据,利用该返回值可以帮助blockcache插入读取失败的块值(见insert函数) + /// - Err(BlockCacheError::____) :不缺块的情况往往是初始化或者其他问题,这种异常会在block_device中得到处理 + pub fn read( + lba_id_start: BlockId, + count: usize, + buf: &mut [u8], + ) -> Result { + // 生成一个块迭代器(BlockIter),它可以迭代地给出所有需要块的数据,其中就包括lba_id + let block_iter = BlockIter::new(lba_id_start, count, BLOCK_SIZE); + // 调用检查函数,检查有无缺块,如果没有就可以获得所有块的Cache地址。如果失败了就直接返回FailData向量 + let cache_block_addr = Self::check_able_to_read(block_iter)?; + // 块地址vec的长度应当等于块迭代器的大小 + assert!(cache_block_addr.len() == block_iter.count()); + // 迭代地读取cache并写入到buf中 + for (index, _) in block_iter.enumerate() { + Self::read_one_block(cache_block_addr[index], index, buf)?; + } + return Ok(count); + } + + /// # 函数的功能 + /// 检查cache中是否有缺块的函数 + /// + /// ## 参数: + /// - 'block_iter' :需要检查的块迭代器(因为块迭代器包含了需要读块的信息,所以传入块迭代器) + /// + /// ## 返回值: + /// - Ok(Vec) :如果成功了,那么函数会返回每个块的Cache地址,利用Cache地址就可以访问Cache了 + /// - Err(BlockCacheError::BlockFaultError) :如果发现了缺块,那么我们会返回所有缺块的信息(即FailData) + /// - Err(BlockCacheError::____) :不缺块的情况往往是初始化或者其他问题 + fn check_able_to_read(block_iter: BlockIter) -> Result, BlockCacheError> { + // 存放缺块信息的向量 + let mut fail_ans = vec![]; + // 存放命中块地址的向量 + let mut success_ans = vec![]; + // 获取mapper + let mapper = unsafe { mapper()? }; + for (index, i) in block_iter.enumerate() { + // 在mapper中寻找块的lba_id,判断是否命中 + match mapper.find(i.lba_id()) { + Some(x) => { + success_ans.push(x); + continue; + } + // 缺块就放入fail_ans + None => fail_ans.push(FailData::new(i.lba_id(), index)), + // 缺块不break的原因是,我们需要把所有缺块都找出来,这样才能补上缺块 + } + } + // 只要有缺块就认为cache失败,因为需要补块就需要进行io操作 + if !fail_ans.is_empty() { + return Err(BlockCacheError::BlockFaultError(fail_ans)); + } else { + return Ok(success_ans); + } + } + /// # 函数的功能 + /// 在cache中读取一个块的数据并放置于缓存的指定位置 + /// + /// ## 参数: + /// - 'cache_block_addr' :表示需要读取的cache块的地址 + /// - 'position' :表示该块的数据需要放置在buf的哪个位置,比如position为2,那么读出的数据将放置在buf\[1024..1536\](这里假设块大小是512) + /// - 'buf' :块数据的缓存 + /// + /// ## 返回值: + /// - Ok(usize) :表示读取了多少个字节 + /// - Err(BlockCacheError) :如果输入的cache_block_addr超过了cache的容量,那么将返回Err(由于目前的cache不支持动态变化上限,所以可能出现这种错误;而实际上,由于Cache的地址是由frame_selector给出的,所以正确实现的frame_selector理论上不会出现这种错误) + fn read_one_block( + cache_block_addr: CacheBlockAddr, + position: usize, + buf: &mut [u8], + ) -> Result { + let space = unsafe { space()? }; + space.read(cache_block_addr, position, buf) + } + /// # 函数的功能 + /// 根据缺块的数据和io获得的数据,向cache中补充块数据 + /// + /// ## 参数: + /// - 'f_data_vec' :这里输入的一般是从read函数中返回的缺块数据 + /// - 'data' :经过一次io后获得的数据 + /// + /// ## 返回值: + /// Ok(usize) :表示补上缺页的个数 + /// Err(BlockCacheError) :一般来说不会产生错误,这里产生错误的原因只有插入时还没有初始化(一般也很难发生) + pub fn insert(f_data_vec: Vec, data: &[u8]) -> Result { + 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(), + )?; + } + Ok(count) + } + + /// # 函数的功能 + /// 将一个块数据插入到cache中 + /// + /// ## 参数: + /// - 'lba_id' :表明该块对应的lba_id,用于建立映射 + /// - 'data' :传入的数据 + /// + /// ## 返回值: + /// Ok(()):表示插入成功 + /// Err(BlockCacheError) :一般来说不会产生错误,这里产生错误的原因只有插入时还没有初始化(一般也很难发生) + fn insert_one_block(lba_id: BlockId, data: Vec) -> Result<(), BlockCacheError> { + let space = unsafe { space()? }; + space.insert(lba_id, data) + } + /// # 函数的功能 + /// 立即回写,这里仅仅作为取消映射的方法,并没有真正写入到cache的功能 + /// + /// ## 参数: + /// - 'lba_id_start' :需要读取的连续块的起始块 + /// - 'count' :需要读取块的个数 + /// - '_data' :目前没有写入功能,该参数暂时无用 + /// + /// ## 返回值: + /// Ok(usize) :表示写入了多少个块 + /// Err(BlockCacheError) :这里产生错误的原因只有插入时还没有初始化 + pub fn immediate_write( + lba_id_start: BlockId, + count: usize, + _data: &[u8], + ) -> Result { + let mapper = unsafe { mapper()? }; + let block_iter = BlockIter::new(lba_id_start, count, BLOCK_SIZE); + for i in block_iter { + mapper.remove(i.lba_id()); + } + Ok(count) + } +} + +struct LockedCacheSpace(RwLock); + +impl LockedCacheSpace { + pub fn new(space: CacheSpace) -> Self { + LockedCacheSpace(RwLock::new(space)) + } + + pub fn read( + &self, + addr: CacheBlockAddr, + position: usize, + buf: &mut [u8], + ) -> Result { + self.0.read().read(addr, position, buf) + } + + pub fn _write(&mut self, _addr: CacheBlockAddr, _data: CacheBlock) -> Option<()> { + todo!() + } + + pub fn insert(&mut self, lba_id: BlockId, data: Vec) -> Result<(), BlockCacheError> { + unsafe { self.0.get_mut().insert(lba_id, data) } + } +} + +/// # 结构功能 +/// 管理Cache空间的结构体 +struct CacheSpace { + /// 用于存放CacheBlock,是Cache数据的实际存储空间的向量 + root: Vec, + /// 在块换出换入时,用于选择替换块的结构体 + frame_selector: Box, +} + +impl CacheSpace { + pub fn new() -> Self { + Self { + root: Vec::new(), + // 如果要修改替换算法,可以设计一个结构体实现FrameSelector trait,再在这里替换掉SimpleFrameSelector + frame_selector: Box::new(SimpleFrameSelector::new()), + } + } + /// # 函数的功能 + /// 将一个块的数据写入到buf的指定位置 + /// + /// ## 参数: + /// - 'addr' :请求块在Cache中的地址 + /// - 'position' :表示需要将Cache放入buf中的位置,例如:若position为1,则块的数据放入buf\[512..1024\] + /// - 'buf' :存放数据的buf + /// + /// ## 返回值: + /// Some(usize):表示读取的字节数(这里默认固定为BLOCK_SIZE) + /// Err(BlockCacheError):如果你输入地址大于cache的最大上限,那么就返回InsufficientCacheSpace + pub fn read( + &self, + addr: CacheBlockAddr, + position: usize, + buf: &mut [u8], + ) -> Result { + if addr > self.frame_selector.size() { + return Err(BlockCacheError::InsufficientCacheSpace); + } else { + // CacheBlockAddr就是用于给root寻址的 + return self.root[addr] + .data(&mut buf[position * BLOCK_SIZE..(position + 1) * BLOCK_SIZE]); + } + } + /// # 函数的功能 + /// 向cache空间中写入的函数,目前尚未实现 + pub fn _write(&mut self, _addr: CacheBlockAddr, _data: CacheBlock) -> Option<()> { + todo!() + } + /// # 函数的功能 + /// 向cache中插入一个块并建立lba_id到块之间的映射 + /// + /// ## 参数: + /// - 'lba_id' :表明你插入的块的lba_id,用于建立映射 + /// - 'data' :要插入块的数据 + /// + /// ## 返回值: + /// 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); + let mapper = unsafe { mapper()? }; + // 这里我设计了cache的一个threshold,如果不超过阈值就可以append,否则只能替换 + if self.frame_selector.can_append() { + // 这是append的操作逻辑: + // 从frame_selector获得一个CacheBlockAddr + let index = self.frame_selector.index_append(); + // 直接将块push进去就可以,因为现在是append操作 + self.root.push(data_block); + assert!(index == self.root.len() - 1); + // 建立mapper的映射 + mapper.insert(lba_id, index); + Ok(()) + } else { + // 这是replace的操作逻辑 + // 从frame_selector获得一个CacheBlockAddr,这次是它替换出来的 + let index = self.frame_selector.index_replace(); + // 获取被替换的块的lba_id,待会用于取消映射 + let removed_id = self.root[index].lba_id(); + // 直接替换原本的块,由于被替换的块没有引用了,所以会被drop + self.root[index] = data_block; + // 建立映射插入块的映射 + mapper.insert(lba_id, index); + // 取消被替换块的映射 + mapper.remove(removed_id); + Ok(()) + } + } +} + +struct LockedCacheMapper { + lock: RwLock, +} + +impl LockedCacheMapper { + pub fn new(inner: CacheMapper) -> Self { + Self { + lock: RwLock::new(inner), + } + } + + pub fn insert(&mut self, lba_id: BlockId, caddr: CacheBlockAddr) -> Option<()> { + unsafe { self.lock.get_mut().insert(lba_id, caddr) } + } + + pub fn find(&self, lba_id: BlockId) -> Option { + self.lock.read().find(lba_id) + } + + pub fn remove(&mut self, lba_id: BlockId) { + unsafe { self.lock.get_mut().remove(lba_id) } + } +} + +/// # 结构功能 +/// 该结构体用于建立lba_id到cached块的映射 +struct CacheMapper { + // 执行键值对操作的map + map: HashMap, +} + +impl CacheMapper { + pub fn new() -> Self { + Self { + map: HashMap::new(), + } + } + /// # 函数的功能 + /// 插入操作 + pub fn insert(&mut self, lba_id: BlockId, caddr: CacheBlockAddr) -> Option<()> { + self.map.insert(lba_id, caddr)?; + Some(()) + } + /// # 函数的功能 + /// 查找操作 + #[inline] + pub fn find(&self, lba_id: BlockId) -> Option { + Some(*self.map.get(&lba_id)?) + } + /// # 函数的功能 + /// 去除操作 + pub fn remove(&mut self, lba_id: BlockId) { + self.map.remove(&lba_id); + } +} + +/// # 结构功能 +/// 该trait用于实现块的换入换出算法,需要设计替换算法只需要实现该trait即可 +trait FrameSelector { + /// # 函数的功能 + /// 给出append操作的index(理论上,如果cache没满,就不需要换出块,就可以使用append操作) + fn index_append(&mut self) -> CacheBlockAddr; + /// # 函数的功能 + /// 给出replace操作后的index + fn index_replace(&mut self) -> CacheBlockAddr; + /// # 函数的功能 + /// 判断是否可以append + fn can_append(&self) -> bool; + /// # 函数的功能 + /// 获取size + fn size(&self) -> usize; +} + +/// # 结构功能 +/// 该结构体用于管理块的换入换出过程中,CacheBlockAddr的选择,替换算法在这里实现 +struct SimpleFrameSelector { + // 表示BlockCache的阈值,即最大可以存放多少块,这里目前还不支持动态变化 + threshold: usize, + // 表示使用过的块帧的数量 + size: usize, + // 这里使用从头至尾的替换算法,其替换策略为0,1,2,...,threshold,0,1...以此类推(该算法比FIFO还要简陋,后面可以再实现别的:) + current: usize, +} + +impl SimpleFrameSelector { + pub fn new() -> Self { + Self { + threshold: CACHE_THRESHOLD * (1 << (20 - BLOCK_SIZE_LOG)), + size: 0, + current: 0, + } + } +} + +impl FrameSelector for SimpleFrameSelector { + fn index_append(&mut self) -> CacheBlockAddr { + let ans = self.current; + self.size += 1; + self.current += 1; + self.current %= self.threshold; + return ans; + } + + fn index_replace(&mut self) -> CacheBlockAddr { + let ans = self.current; + self.current += 1; + self.current %= self.threshold; + return ans; + } + + fn can_append(&self) -> bool { + self.size < self.threshold + } + + fn size(&self) -> usize { + self.size + } +} diff --git a/kernel/src/driver/block/cache/mod.rs b/kernel/src/driver/block/cache/mod.rs new file mode 100644 index 00000000..1e2f87fe --- /dev/null +++ b/kernel/src/driver/block/cache/mod.rs @@ -0,0 +1,19 @@ +use alloc::vec::Vec; + +use self::cache_iter::FailData; + +mod cache_block; +mod cache_iter; +pub mod cached_block_device; +pub const BLOCK_SIZE_LOG: usize = 9; +///块大小这里固定为512 +pub const BLOCK_SIZE: usize = 1 << BLOCK_SIZE_LOG; +///这里规定Cache的threshold大小,单位为:MB +pub const CACHE_THRESHOLD: usize = 64; + +pub enum BlockCacheError { + BlockSizeError, + InsufficientCacheSpace, + StaticParameterError, + BlockFaultError(Vec), +} diff --git a/kernel/src/driver/block/mod.rs b/kernel/src/driver/block/mod.rs new file mode 100644 index 00000000..a5c08fdb --- /dev/null +++ b/kernel/src/driver/block/mod.rs @@ -0,0 +1 @@ +pub mod cache; diff --git a/kernel/src/driver/disk/ahci/ahcidisk.rs b/kernel/src/driver/disk/ahci/ahcidisk.rs index abc8e73d..22fdf22e 100644 --- a/kernel/src/driver/disk/ahci/ahcidisk.rs +++ b/kernel/src/driver/disk/ahci/ahcidisk.rs @@ -401,7 +401,7 @@ impl LockedAhciDisk { let mut buf: Vec = vec![0; size_of::()]; buf.resize(size_of::(), 0); - self.read_at(0, 1, &mut buf)?; + self.read_at_sync(0, 1, &mut buf)?; // 创建 Cursor 用于按字节读取 let mut cursor = VecCursor::new(buf); cursor.seek(SeekFrom::SeekCurrent(446))?; @@ -562,7 +562,7 @@ impl BlockDevice for LockedAhciDisk { } #[inline] - fn read_at( + fn read_at_sync( &self, lba_id_start: BlockId, // 起始lba编号 count: usize, // 读取lba的数量 @@ -572,7 +572,7 @@ impl BlockDevice for LockedAhciDisk { } #[inline] - fn write_at( + fn write_at_sync( &self, lba_id_start: BlockId, count: usize, diff --git a/kernel/src/driver/disk/ahci/mod.rs b/kernel/src/driver/disk/ahci/mod.rs index 15b31aad..778fc9a4 100644 --- a/kernel/src/driver/disk/ahci/mod.rs +++ b/kernel/src/driver/disk/ahci/mod.rs @@ -4,6 +4,7 @@ pub mod ahcidisk; pub mod hba; use crate::driver::base::block::disk_info::BLK_GF_AHCI; +use crate::driver::block::cache::cached_block_device::BlockCache; // 依赖的rust工具包 use crate::driver::pci::pci::{ get_pci_device_structure_mut, PciDeviceStructure, PCI_DEVICE_LINKEDLIST, @@ -141,6 +142,7 @@ pub fn ahci_init() -> Result<(), SystemError> { } } } + BlockCache::init(); } compiler_fence(core::sync::atomic::Ordering::SeqCst); diff --git a/kernel/src/driver/mod.rs b/kernel/src/driver/mod.rs index de767936..4309c927 100644 --- a/kernel/src/driver/mod.rs +++ b/kernel/src/driver/mod.rs @@ -1,5 +1,6 @@ pub mod acpi; pub mod base; +pub mod block; pub mod disk; pub mod firmware; pub mod input; diff --git a/kernel/src/filesystem/fat/bpb.rs b/kernel/src/filesystem/fat/bpb.rs index 22e85673..1c7b8cc7 100644 --- a/kernel/src/filesystem/fat/bpb.rs +++ b/kernel/src/filesystem/fat/bpb.rs @@ -225,7 +225,7 @@ impl BiosParameterBlock { // 读取分区的引导扇区 partition .disk() - .read_at(partition.lba_start as usize, 1, &mut v)?; + .read_at_sync(partition.lba_start as usize, 1, &mut v)?; // 获取指针对象 let mut cursor = VecCursor::new(v); diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 6f78b04c..58d92339 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -1001,7 +1001,7 @@ impl FATFileSystem { let mut v: Vec = vec![0; self.lba_per_sector() * LBA_SIZE]; self.partition .disk() - .read_at(lba, self.lba_per_sector(), &mut v)?; + .read_at_sync(lba, self.lba_per_sector(), &mut v)?; let mut cursor: VecCursor = VecCursor::new(v); cursor.seek(SeekFrom::SeekSet(in_block_offset as i64))?; @@ -1032,7 +1032,7 @@ impl FATFileSystem { let mut v: Vec = vec![0; self.lba_per_sector() * LBA_SIZE]; self.partition .disk() - .read_at(lba, self.lba_per_sector(), &mut v)?; + .read_at_sync(lba, self.lba_per_sector(), &mut v)?; let mut cursor: VecCursor = VecCursor::new(v); cursor.seek(SeekFrom::SeekSet(in_block_offset as i64))?; @@ -1078,7 +1078,7 @@ impl FATFileSystem { let lba = self.get_lba_from_offset(self.bytes_to_sector(fat_part_bytes_offset)); let mut v: Vec = vec![0; LBA_SIZE]; - self.partition.disk().read_at(lba, 1, &mut v)?; + self.partition.disk().read_at_sync(lba, 1, &mut v)?; let mut cursor: VecCursor = VecCursor::new(v); cursor.seek(SeekFrom::SeekSet(in_block_offset as i64))?; @@ -1227,7 +1227,7 @@ impl FATFsInfo { // 计算fs_info扇区在磁盘上的字节偏移量,从磁盘读取数据 partition .disk() - .read_at(in_disk_fs_info_offset as usize / LBA_SIZE, 1, &mut v)?; + .read_at_sync(in_disk_fs_info_offset as usize / LBA_SIZE, 1, &mut v)?; let mut cursor = VecCursor::new(v); let mut fsinfo = FATFsInfo { diff --git a/user/apps/test-blockcache/.cargo/config.toml b/user/apps/test-blockcache/.cargo/config.toml new file mode 100644 index 00000000..d67b66e2 --- /dev/null +++ b/user/apps/test-blockcache/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "x86_64-unknown-linux-musl" diff --git a/user/apps/test-blockcache/.gitignore b/user/apps/test-blockcache/.gitignore new file mode 100644 index 00000000..1ac35461 --- /dev/null +++ b/user/apps/test-blockcache/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +/install/ \ No newline at end of file diff --git a/user/apps/test-blockcache/Cargo.toml b/user/apps/test-blockcache/Cargo.toml new file mode 100644 index 00000000..82e2117a --- /dev/null +++ b/user/apps/test-blockcache/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "test-blockcache" +version = "0.1.0" +edition = "2021" +description = "用于测试blockcac小程序" +authors = [ "ZZJJWarth <2678328250@qq.com>" ] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] \ No newline at end of file diff --git a/user/apps/test-blockcache/Makefile b/user/apps/test-blockcache/Makefile new file mode 100644 index 00000000..74ab45f2 --- /dev/null +++ b/user/apps/test-blockcache/Makefile @@ -0,0 +1,41 @@ +# The toolchain we use. +# You can get it by running DragonOS' `tools/bootstrap.sh` +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux_dragonos-gnu" +RUSTFLAGS+="-C target-feature=+crt-static -C link-arg=-no-pie" + +# 如果是在dadk中编译,那么安装到dadk的安装目录中 +INSTALL_DIR?=$(DADK_CURRENT_BUILD_DIR) +# 如果是在本地编译,那么安装到当前目录下的install目录中 +INSTALL_DIR?=./install + + +run: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean + +test: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test + +doc: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc + +run-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --release + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --release + +test-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --release + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test-blockcache/README.md b/user/apps/test-blockcache/README.md new file mode 100644 index 00000000..269e0a63 --- /dev/null +++ b/user/apps/test-blockcache/README.md @@ -0,0 +1,8 @@ +## 程序说明 +用于测试BlockCache的测试程序 +本程序生成一个文件,并开始对这个文件进行单纯的读操作(共读10000次) + +## 使用方法 + +1. 打开系统的/bin目录 +2. 输入指令exec test-blockcache即可开始测试 diff --git a/user/apps/test-blockcache/src/main.rs b/user/apps/test-blockcache/src/main.rs new file mode 100644 index 00000000..ba96f21a --- /dev/null +++ b/user/apps/test-blockcache/src/main.rs @@ -0,0 +1,22 @@ +use std::fs::File; +use std::io::{BufReader, Read, Seek, SeekFrom, Write}; + +fn main() -> std::io::Result<()> { + let file_size_bytes: u64 = 512; + let mut file = File::create("large_file")?; + file.seek(std::io::SeekFrom::Start(file_size_bytes - 1))?; + file.write_all(&[0])?; + let mut file = File::open("large_file")?; + // let mut reader = BufReader::new(file); + let mut buffer = [0; 512]; + let mut count = 0; + loop { + count += 1; + file.seek(SeekFrom::Start(0))?; + let bytes_read = file.read_exact(&mut buffer)?; + if count > 10000 { + break; + } + } + Ok(()) +} diff --git a/user/dadk/config/test_blockcache_0_1_0.dadk b/user/dadk/config/test_blockcache_0_1_0.dadk new file mode 100644 index 00000000..22ff8d4a --- /dev/null +++ b/user/dadk/config/test_blockcache_0_1_0.dadk @@ -0,0 +1,24 @@ +{ + "name": "test-blockcache", + "version": "0.1.0", + "description": "用于测试blockcach小程序", + "rust_target": "x86_64-unknown-dragonos", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test-blockcache" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "make clean" + }, + "envs": [] +} \ No newline at end of file