block: use larger segment size for cache prefetch
This commit is contained in:
parent
f79cae5368
commit
3a5f9b6ced
@ -249,7 +249,9 @@ impl Ext2Fs {
|
||||
|
||||
let mapper = match cached {
|
||||
false => DeviceMapper::uncached(device, block_size),
|
||||
true => DeviceMapper::cached_with_capacity(device, block_size, 512),
|
||||
true => {
|
||||
DeviceMapper::cached_with_capacity(device, block_size, block_size * 16, 128, 64)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
|
@ -14,7 +14,7 @@ use crate::task::sync::AsyncMutex;
|
||||
|
||||
use super::BlockDevice;
|
||||
|
||||
pub struct CachedBlock<
|
||||
pub struct CachedSegment<
|
||||
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
|
||||
> {
|
||||
data: PageBox<[u8], A>,
|
||||
@ -41,16 +41,25 @@ pub struct BlockCache<
|
||||
> {
|
||||
device: &'static dyn BlockDevice,
|
||||
block_size: usize,
|
||||
cache: AsyncMutex<LruCache<u64, Arc<IrqSafeRwLock<CachedBlock<A>>>>>,
|
||||
segment_size: usize,
|
||||
cache: AsyncMutex<LruCache<u64, Arc<IrqSafeRwLock<CachedSegment<A>>>>>,
|
||||
}
|
||||
|
||||
impl DeviceMapper {
|
||||
pub fn cached_with_capacity(
|
||||
device: &'static dyn BlockDevice,
|
||||
block_size: usize,
|
||||
segment_size: usize,
|
||||
bucket_capacity: usize,
|
||||
bucket_count: usize,
|
||||
) -> DeviceMapper {
|
||||
DeviceMapper::cached_with_capacity_in(device, block_size, bucket_capacity)
|
||||
DeviceMapper::cached_with_capacity_in(
|
||||
device,
|
||||
block_size,
|
||||
segment_size,
|
||||
bucket_capacity,
|
||||
bucket_count,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn uncached(device: &'static dyn BlockDevice, block_size: usize) -> DeviceMapper {
|
||||
@ -62,9 +71,17 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
|
||||
pub fn cached_with_capacity_in(
|
||||
device: &'static dyn BlockDevice,
|
||||
block_size: usize,
|
||||
segment_size: usize,
|
||||
bucket_capacity: usize,
|
||||
bucket_count: usize,
|
||||
) -> DeviceMapper<A> {
|
||||
let cache = BlockCache::<A>::with_capacity_in(device, block_size, bucket_capacity);
|
||||
let cache = BlockCache::<A>::with_capacity_in(
|
||||
device,
|
||||
block_size,
|
||||
segment_size,
|
||||
bucket_capacity,
|
||||
bucket_count,
|
||||
);
|
||||
DeviceMapper::<A>::Cached(cache)
|
||||
}
|
||||
|
||||
@ -159,15 +176,36 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
|
||||
pub fn with_capacity_in(
|
||||
device: &'static dyn BlockDevice,
|
||||
block_size: usize,
|
||||
segment_size: usize,
|
||||
bucket_capacity: usize,
|
||||
bucket_count: usize,
|
||||
) -> BlockCache<A> {
|
||||
if block_size % device.block_size() != 0 {
|
||||
panic!("Cache block size is not multiple of device block size");
|
||||
}
|
||||
assert_eq!(
|
||||
block_size % device.block_size(),
|
||||
0,
|
||||
"Block size is not a multiple of device block size"
|
||||
);
|
||||
assert_eq!(
|
||||
segment_size % block_size,
|
||||
0,
|
||||
"Segment size is not a multiple of block size"
|
||||
);
|
||||
assert!(
|
||||
segment_size >= block_size,
|
||||
"Segment size is less than block size"
|
||||
);
|
||||
|
||||
log::info!("New block cache: block: {block_size}B, segment: {segment_size}B, geometry: {bucket_capacity}x{bucket_count}");
|
||||
log::info!(
|
||||
"Worst-case memory usage: {}K",
|
||||
(segment_size * bucket_capacity * bucket_count) / 1024
|
||||
);
|
||||
|
||||
BlockCache {
|
||||
device,
|
||||
block_size,
|
||||
cache: AsyncMutex::new(LruCache::with_capacity(bucket_capacity, 8)),
|
||||
segment_size,
|
||||
cache: AsyncMutex::new(LruCache::with_capacity(bucket_capacity, bucket_count)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,28 +213,38 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
|
||||
self.device
|
||||
}
|
||||
|
||||
async fn evict_block(&self, pos: u64, block: Arc<IrqSafeRwLock<CachedBlock<A>>>) {
|
||||
async fn evict_block(
|
||||
&self,
|
||||
segment_position: u64,
|
||||
block: Arc<IrqSafeRwLock<CachedSegment<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);
|
||||
assert_eq!(segment_position % self.segment_size as u64, 0);
|
||||
if let Err(err) = self
|
||||
.device
|
||||
.write_aligned(segment_position, read.data.as_slice())
|
||||
.await
|
||||
{
|
||||
log::error!("Disk error: flushing block {}: {:?}", segment_position, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_block(
|
||||
&self,
|
||||
pos: u64,
|
||||
write_size: Option<usize>,
|
||||
) -> Result<Arc<IrqSafeRwLock<CachedBlock<A>>>, Error> {
|
||||
let need_read = write_size.map(|sz| sz != self.block_size).unwrap_or(true);
|
||||
let mut data = PageBox::new_uninit_slice_in(self.block_size)?;
|
||||
// Don't read a block that's going to be fully rewritten immediately
|
||||
if need_read {
|
||||
self.device.read_aligned(pos, data.as_slice_mut()).await?;
|
||||
}
|
||||
segment_position: u64,
|
||||
) -> Result<Arc<IrqSafeRwLock<CachedSegment<A>>>, Error> {
|
||||
// let need_read = write_size.map(|sz| sz != self.block_size).unwrap_or(true);
|
||||
let mut data = PageBox::new_uninit_slice_in(self.segment_size)?;
|
||||
// // Don't read a block that's going to be fully rewritten immediately
|
||||
// if need_read {
|
||||
self.device
|
||||
.read_aligned(segment_position, data.as_slice_mut())
|
||||
.await?;
|
||||
// }
|
||||
let data = unsafe { data.assume_init_slice() };
|
||||
Ok(Arc::new(IrqSafeRwLock::new(CachedBlock {
|
||||
Ok(Arc::new(IrqSafeRwLock::new(CachedSegment {
|
||||
data,
|
||||
dirty: false,
|
||||
})))
|
||||
@ -204,16 +252,16 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
|
||||
|
||||
async fn entry(
|
||||
&self,
|
||||
pos: u64,
|
||||
write_size: Option<usize>,
|
||||
) -> Result<Arc<IrqSafeRwLock<CachedBlock<A>>>, Error> {
|
||||
segment_position: u64,
|
||||
) -> Result<Arc<IrqSafeRwLock<CachedSegment<A>>>, Error> {
|
||||
assert_eq!(segment_position % self.segment_size as u64, 0);
|
||||
let mut lock = self.cache.lock().await;
|
||||
let (value, evicted) = lock
|
||||
.try_get_or_insert_with_async(pos, || self.fetch_block(pos, write_size))
|
||||
.try_get_or_insert_with_async(segment_position, || self.fetch_block(segment_position))
|
||||
.await?;
|
||||
|
||||
if let Some((pos, block)) = evicted {
|
||||
self.evict_block(pos, block).await;
|
||||
if let Some((segment_position, block)) = evicted {
|
||||
self.evict_block(segment_position, block).await;
|
||||
}
|
||||
|
||||
Ok(value.clone())
|
||||
@ -221,41 +269,46 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
|
||||
|
||||
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
|
||||
&self,
|
||||
pos: u64,
|
||||
block_position: u64,
|
||||
mapper: F,
|
||||
) -> Result<T, Error> {
|
||||
let block = self.entry(pos, None).await?;
|
||||
let result = mapper(&block.read()[..])?;
|
||||
let segment_position = block_position & !(self.segment_size as u64 - 1);
|
||||
let segment_offset = (block_position & (self.segment_size as u64 - 1)) as usize;
|
||||
let block = self.entry(segment_position).await?;
|
||||
let result = mapper(&block.read()[segment_offset..segment_offset + self.block_size])?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
|
||||
&self,
|
||||
pos: u64,
|
||||
block_position: u64,
|
||||
size: usize,
|
||||
mapper: F,
|
||||
) -> Result<T, Error> {
|
||||
let block = self.entry(pos, Some(size)).await?;
|
||||
let segment_position = block_position & !(self.segment_size as u64 - 1);
|
||||
let segment_offset = (block_position & (self.segment_size as u64 - 1)) as usize;
|
||||
let block = self.entry(segment_position).await?;
|
||||
let mut block = block.write();
|
||||
let result = mapper(&mut block[..])?;
|
||||
block.dirty = true;
|
||||
let result = mapper(&mut block[segment_offset..segment_offset + self.block_size])?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn flush(&self) {
|
||||
for (pos, block) in self.cache.lock().await.flush() {
|
||||
self.evict_block(pos, block).await;
|
||||
}
|
||||
todo!()
|
||||
// for (pos, block) in self.cache.lock().await.flush() {
|
||||
// self.evict_block(pos, block).await;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> CachedBlock<A> {
|
||||
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> CachedSegment<A> {
|
||||
pub fn set_dirty(&mut self) {
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> Deref for CachedBlock<A> {
|
||||
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> Deref for CachedSegment<A> {
|
||||
type Target = PageBox<[u8], A>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -263,7 +316,7 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> Deref for CachedBloc
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DerefMut for CachedBlock<A> {
|
||||
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DerefMut for CachedSegment<A> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.dirty = true;
|
||||
&mut self.data
|
||||
@ -407,7 +460,7 @@ mod tests {
|
||||
async fn test_no_modification() {
|
||||
let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
|
||||
let wrapper = NgBlockDeviceWrapper::new(device);
|
||||
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, 64);
|
||||
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 64, 8);
|
||||
|
||||
device.deny_writes.store(true, Ordering::Release);
|
||||
cache
|
||||
@ -433,7 +486,7 @@ mod tests {
|
||||
let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
|
||||
let wrapper = NgBlockDeviceWrapper::new(device);
|
||||
// 8 * 8
|
||||
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, 8);
|
||||
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 8, 8);
|
||||
|
||||
const LBA: u64 = 1;
|
||||
cache
|
||||
@ -513,7 +566,7 @@ mod tests {
|
||||
let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
|
||||
let wrapper = NgBlockDeviceWrapper::new(device);
|
||||
// 8 * 8
|
||||
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, 8);
|
||||
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 8, 8);
|
||||
|
||||
fn mapper(x: u64) -> u8 {
|
||||
(x + 3) as u8
|
||||
|
Loading…
x
Reference in New Issue
Block a user