impl naive BlockCache

master
WangRunji 6 years ago
parent f3d4811305
commit 8dcd4fc012

@ -4,6 +4,9 @@ version = "0.1.0"
authors = ["WangRunji <wangrunji0408@163.com>", "Ben Pig Chu <benpichu@gmail.com>"] authors = ["WangRunji <wangrunji0408@163.com>", "Ben Pig Chu <benpichu@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies]
spin = "0.5"
[dev-dependencies] [dev-dependencies]
tempfile = "3" tempfile = "3"

@ -0,0 +1,114 @@
//! A naive cache layer for `BlockDevice`
use super::*;
use alloc::{vec, vec::Vec};
use spin::{Mutex, MutexGuard};
pub struct BlockCache<T: BlockDevice> {
device: T,
bufs: Vec<Mutex<Buf>>,
}
struct Buf {
status: BufStatus,
data: Vec<u8>,
}
enum BufStatus {
/// buffer is unused
Unused,
/// buffer has been read from disk
Valid(BlockId),
/// buffer needs to be written to disk
Dirty(BlockId),
}
impl<T: BlockDevice> BlockCache<T> {
pub fn new(device: T, capacity: usize) -> Self {
let mut bufs = Vec::new();
bufs.resize_with(capacity, || {
Mutex::new(Buf {
status: BufStatus::Unused,
data: vec![0; 1 << T::BLOCK_SIZE_LOG2 as usize],
})
});
BlockCache { device, bufs }
}
/// Get a buffer for `block_id` with any status
fn get_buf(&self, block_id: BlockId) -> MutexGuard<Buf> {
for buf in self.bufs.iter() {
if let Some(lock) = buf.try_lock() {
match lock.status {
BufStatus::Valid(id) if id == block_id => return lock,
BufStatus::Dirty(id) if id == block_id => return lock,
_ => {}
}
}
}
self.get_unused()
}
/// Get an unused buffer
fn get_unused(&self) -> MutexGuard<Buf> {
for buf in self.bufs.iter() {
if let Some(lock) = buf.try_lock() {
if let BufStatus::Unused = lock.status {
return lock;
}
}
}
// TODO: choose a victim using LRU
let mut victim = self.bufs[0].lock();
self.write_back(&mut victim).expect("failed to write back");
victim.status = BufStatus::Unused;
victim
}
/// Write back data if buffer is dirty
fn write_back(&self, buf: &mut Buf) -> Result<()> {
if let BufStatus::Dirty(block_id) = buf.status {
self.device.write_at(block_id, &buf.data)?;
buf.status = BufStatus::Valid(block_id);
}
Ok(())
}
}
impl<T: BlockDevice> Drop for BlockCache<T> {
fn drop(&mut self) {
BlockDevice::sync(self).expect("failed to sync");
}
}
impl<T: BlockDevice> BlockDevice for BlockCache<T> {
const BLOCK_SIZE_LOG2: u8 = T::BLOCK_SIZE_LOG2;
fn read_at(&self, block_id: BlockId, buffer: &mut [u8]) -> Result<()> {
let mut buf = self.get_buf(block_id);
match buf.status {
BufStatus::Unused => {
// read from device
self.device.read_at(block_id, &mut buf.data)?;
buf.status = BufStatus::Valid(block_id);
}
_ => {}
}
buffer[..1 << Self::BLOCK_SIZE_LOG2 as usize].copy_from_slice(&buf.data);
Ok(())
}
fn write_at(&self, block_id: BlockId, buffer: &[u8]) -> Result<()> {
let mut buf = self.get_buf(block_id);
buf.status = BufStatus::Dirty(block_id);
buf.data.copy_from_slice(&buffer[..1 << Self::BLOCK_SIZE_LOG2 as usize]);
Ok(())
}
fn sync(&self) -> Result<()> {
for buf in self.bufs.iter() {
self.write_back(&mut buf.lock())?;
}
self.device.sync()?;
Ok(())
}
}

@ -1,6 +1,7 @@
use crate::util::*; use crate::util::*;
use crate::vfs::Timespec; use crate::vfs::Timespec;
pub mod block_cache;
pub mod std_impl; pub mod std_impl;
/// A current time provider /// A current time provider

Loading…
Cancel
Save