use super::{ BlockDevice, BLOCK_SZ, }; use alloc::sync::{Arc, Weak}; use alloc::collections::BTreeMap; use lazy_static::*; use spin::Mutex; pub struct BlockCache { cache: [u8; BLOCK_SZ], block_id: usize, block_device: Arc, } impl BlockCache { pub fn new(block_id: usize, block_device: Arc) -> Self { let mut cache = [0u8; BLOCK_SZ]; block_device.read_block(block_id, &mut cache); Self { cache, block_id, block_device, } } pub fn start_addr(&self, offset: usize) -> usize { &self.cache[offset] as *const _ as usize } } impl Drop for BlockCache { fn drop(&mut self) { // write back self.block_device.write_block(self.block_id, &self.cache); // invalid in block cache manager BLOCK_CACHE_MANAGER.lock().invalid(self.block_id); } } pub struct BlockCacheManager { map: BTreeMap>, } lazy_static! { static ref BLOCK_CACHE_MANAGER: Mutex = Mutex::new( BlockCacheManager::new() ); } impl BlockCacheManager { pub fn new() -> Self { Self { map: BTreeMap::new() } } pub fn get( &mut self, block_id: usize, block_device: Arc ) -> Arc { if let Some(block_cache) = self.map.get(&block_id) { // return cloned block_cache.upgrade().unwrap().clone() } else { // fetch from disk let block_cache = Arc::new(BlockCache::new( block_id, block_device.clone() )); self.map.insert( block_id, Arc::downgrade(&block_cache), ); // return block_cache } } pub fn invalid(&mut self, block_id: usize) { assert!(self.map.remove(&block_id).is_some()); } } pub fn get_block_cache( block_id: usize, block_device: Arc ) -> Arc { BLOCK_CACHE_MANAGER.lock().get(block_id, block_device) }