ext2: re-enable block cache

This commit is contained in:
Mark Poliakov 2024-12-05 12:43:09 +02:00
parent 93c143fa58
commit dd2c948107
2 changed files with 98 additions and 18 deletions

View File

@ -180,7 +180,7 @@ impl Ext2Fs {
(!unsupported, enabled)
}
async fn create_fs(device: &'static dyn BlockDevice, _cached: bool) -> Result<Self, Error> {
async fn create_fs(device: &'static dyn BlockDevice, cached: bool) -> Result<Self, Error> {
let mut superblock = ExtendedSuperblock::zeroed();
device
.read_exact(
@ -245,12 +245,10 @@ impl Ext2Fs {
log::info!("Inode size: {}", superblock.inode_size());
// TODO block cache produces data corruption
let cache = DeviceMapper::uncached(device, block_size);
// let cache = match cached {
// false => MaybeCache::uncached(device, block_size),
// true => MaybeCache::cached_with_capacity(device, block_size, 512),
// };
let mapper = match cached {
false => DeviceMapper::uncached(device, block_size),
true => DeviceMapper::cached_with_capacity(device, block_size, 512),
};
Ok(Self {
block_size,
@ -263,8 +261,7 @@ impl Ext2Fs {
block_group_inode_count,
block_group_block_count,
// 128 × 8 cache
mapper: cache,
mapper,
inode_cache: OneTimeInit::new(),
state: IrqSafeRwLock::new(State {
superblock,

View File

@ -4,7 +4,7 @@ use core::{
ops::{Deref, DerefMut},
};
use alloc::sync::Arc;
use alloc::{sync::Arc, vec::Vec};
use kernel_arch::mem::PhysicalMemoryAllocator;
use libk_mm::{address::PhysicalAddress, phys::GlobalPhysicalAllocator, PageBox};
use libk_util::{lru_hash_table::LruCache, sync::spin_rwlock::IrqSafeRwLock};
@ -41,7 +41,7 @@ pub struct BlockCache<
> {
device: &'static dyn BlockDevice,
block_size: usize,
_pd: PhantomData<A>,
cache: AsyncMutex<LruCache<u64, Arc<IrqSafeRwLock<CachedBlock<A>>>>>,
}
impl DeviceMapper {
@ -64,9 +64,8 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
block_size: usize,
bucket_capacity: usize,
) -> DeviceMapper<A> {
todo!()
// let cache = BlockCache::<A>::with_capacity_in(device, block_size, bucket_capacity);
// DeviceMapper::<A>::Cached(cache)
let cache = BlockCache::<A>::with_capacity_in(device, block_size, bucket_capacity);
DeviceMapper::<A>::Cached(cache)
}
pub fn uncached_in(device: &'static dyn BlockDevice, block_size: usize) -> DeviceMapper<A> {
@ -84,7 +83,7 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
pub fn device(&self) -> &'static dyn BlockDevice {
match self {
Self::Uncached(uncache) => uncache.device(),
Self::Cached(_cache) => todo!(),
Self::Cached(cache) => cache.device(),
}
}
@ -95,7 +94,7 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
) -> Result<T, Error> {
match self {
Self::Uncached(uncache) => uncache.try_with(pos, mapper).await,
Self::Cached(_cache) => todo!(),
Self::Cached(cache) => cache.try_with(pos, mapper).await,
}
}
@ -106,14 +105,17 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
) -> Result<T, Error> {
match self {
Self::Uncached(uncache) => uncache.try_with_mut(pos, mapper).await,
Self::Cached(_cache) => todo!(),
Self::Cached(cache) => cache.try_with_mut(pos, mapper).await,
}
}
pub async fn flush(&self) -> Result<(), Error> {
match self {
Self::Uncached(_) => Ok(()),
Self::Cached(_cache) => todo!(),
Self::Cached(cache) => {
cache.flush().await;
Ok(())
}
}
}
}
@ -148,6 +150,87 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> UncachedCache<A> {
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
pub fn with_capacity_in(
device: &'static dyn BlockDevice,
block_size: usize,
bucket_capacity: usize,
) -> BlockCache<A> {
if block_size % device.block_size() != 0 {
panic!("Cache block size is not multiple of device block size");
}
BlockCache {
device,
block_size,
cache: AsyncMutex::new(LruCache::with_capacity(bucket_capacity, 8)),
}
}
pub fn device(&self) -> &'static dyn BlockDevice {
self.device
}
async fn evict_block(&self, pos: u64, block: Arc<IrqSafeRwLock<CachedBlock<A>>>) {
let read = block.read();
if read.dirty {
if let Err(err) = self.device.write_aligned(pos, read.data.as_slice()).await {
log::error!("Disk error: flushing block {}: {:?}", pos, err);
}
}
}
async fn fetch_block(&self, pos: u64) -> Result<Arc<IrqSafeRwLock<CachedBlock<A>>>, Error> {
let mut data = PageBox::new_uninit_slice_in(self.block_size)?;
self.device.read_aligned(pos, data.as_slice_mut()).await?;
let data = unsafe { data.assume_init_slice() };
Ok(Arc::new(IrqSafeRwLock::new(CachedBlock {
data,
dirty: false,
})))
}
async fn entry(&self, pos: u64) -> Result<Arc<IrqSafeRwLock<CachedBlock<A>>>, Error> {
let mut lock = self.cache.lock().await;
let (value, evicted) = lock
.try_get_or_insert_with_async(pos, || self.fetch_block(pos))
.await?;
if let Some((pos, block)) = evicted {
self.evict_block(pos, block).await;
}
Ok(value.clone())
}
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
&self,
pos: u64,
mapper: F,
) -> Result<T, Error> {
let block = self.entry(pos).await?;
let result = mapper(&block.read()[..])?;
Ok(result)
}
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self,
pos: u64,
mapper: F,
) -> Result<T, Error> {
let block = self.entry(pos).await?;
let mut block = block.write();
let result = mapper(&mut block[..])?;
block.dirty = true;
Ok(result)
}
pub async fn flush(&self) {
for (pos, block) in self.cache.lock().await.flush() {
self.evict_block(pos, block).await;
}
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> CachedBlock<A> {
pub fn set_dirty(&mut self) {
self.dirty = true;