use super::{BlockDevice, BLOCK_SZ}; use alloc::collections::VecDeque; use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; use lazy_static::*; use spin::Mutex; pub struct BlockCache { cache: Vec, block_id: usize, block_device: Arc, modified: bool, } impl BlockCache { /// Load a new BlockCache from disk. pub fn new(block_id: usize, block_device: Arc) -> Self { // for alignment and move effciency let mut cache = vec![0u8; BLOCK_SZ]; block_device.read_block(block_id, &mut cache); Self { cache, block_id, block_device, modified: false, } } fn addr_of_offset(&self, offset: usize) -> usize { &self.cache[offset] as *const _ as usize } pub fn get_ref(&self, offset: usize) -> &T where T: Sized, { let type_size = core::mem::size_of::(); assert!(offset + type_size <= BLOCK_SZ); let addr = self.addr_of_offset(offset); unsafe { &*(addr as *const T) } } pub fn get_mut(&mut self, offset: usize) -> &mut T where T: Sized, { let type_size = core::mem::size_of::(); assert!(offset + type_size <= BLOCK_SZ); self.modified = true; let addr = self.addr_of_offset(offset); unsafe { &mut *(addr as *mut T) } } pub fn read(&self, offset: usize, f: impl FnOnce(&T) -> V) -> V { f(self.get_ref(offset)) } pub fn modify(&mut self, offset: usize, f: impl FnOnce(&mut T) -> V) -> V { f(self.get_mut(offset)) } pub fn sync(&mut self) { if self.modified { self.modified = false; self.block_device.write_block(self.block_id, &self.cache); } } } impl Drop for BlockCache { fn drop(&mut self) { self.sync() } } const BLOCK_CACHE_SIZE: usize = 16; pub struct BlockCacheManager { queue: VecDeque<(usize, Arc>)>, } impl BlockCacheManager { pub fn new() -> Self { Self { queue: VecDeque::new(), } } pub fn get_block_cache( &mut self, block_id: usize, block_device: Arc, ) -> Arc> { if let Some(pair) = self.queue.iter().find(|pair| pair.0 == block_id) { Arc::clone(&pair.1) } else { // substitute if self.queue.len() == BLOCK_CACHE_SIZE { // from front to tail if let Some((idx, _)) = self .queue .iter() .enumerate() .find(|(_, pair)| Arc::strong_count(&pair.1) == 1) { self.queue.drain(idx..=idx); } else { panic!("Run out of BlockCache!"); } } // load block into mem and push back let block_cache = Arc::new(Mutex::new(BlockCache::new( block_id, Arc::clone(&block_device), ))); self.queue.push_back((block_id, Arc::clone(&block_cache))); block_cache } } } lazy_static! { pub static ref BLOCK_CACHE_MANAGER: Mutex = Mutex::new(BlockCacheManager::new()); } pub fn get_block_cache( block_id: usize, block_device: Arc, ) -> Arc> { BLOCK_CACHE_MANAGER .lock() .get_block_cache(block_id, block_device) } pub fn block_cache_sync_all() { let manager = BLOCK_CACHE_MANAGER.lock(); for (_, cache) in manager.queue.iter() { cache.lock().sync(); } }