mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 01:13:23 +00:00
Add mlsdisk as a component
Co-authored-by: Shaowei Song <songshaowei.ssw@antgroup.com>
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
6e691d5838
commit
56a137dc56
227
kernel/comps/mlsdisk/src/layers/0-bio/block_set.rs
Normal file
227
kernel/comps/mlsdisk/src/layers/0-bio/block_set.rs
Normal file
@ -0,0 +1,227 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::ops::Range;
|
||||
|
||||
use inherit_methods_macro::inherit_methods;
|
||||
|
||||
use super::{Buf, BufMut, BufRef};
|
||||
use crate::{error::Errno, os::Mutex, prelude::*};
|
||||
|
||||
/// A fixed set of data blocks that can support random reads and writes.
|
||||
///
|
||||
/// # Thread safety
|
||||
///
|
||||
/// `BlockSet` is a data structure of interior mutability.
|
||||
/// It is ok to perform I/O on a `BlockSet` concurrently in multiple threads.
|
||||
/// `BlockSet` promises the atomicity of reading and writing individual blocks.
|
||||
pub trait BlockSet: Sync + Send {
|
||||
/// Read one or multiple blocks at a specified position.
|
||||
fn read(&self, pos: BlockId, buf: BufMut) -> Result<()>;
|
||||
|
||||
/// Read a slice of bytes at a specified byte offset.
|
||||
fn read_slice(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||
let start_pos = offset / BLOCK_SIZE;
|
||||
let end_pos = (offset + buf.len()).div_ceil(BLOCK_SIZE);
|
||||
if end_pos > self.nblocks() {
|
||||
return_errno_with_msg!(Errno::InvalidArgs, "read_slice position is out of range");
|
||||
}
|
||||
|
||||
let nblocks = end_pos - start_pos;
|
||||
let mut blocks = Buf::alloc(nblocks)?;
|
||||
self.read(start_pos, blocks.as_mut())?;
|
||||
|
||||
let offset = offset % BLOCK_SIZE;
|
||||
buf.copy_from_slice(&blocks.as_slice()[offset..offset + buf.len()]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write one or multiple blocks at a specified position.
|
||||
fn write(&self, pos: BlockId, buf: BufRef) -> Result<()>;
|
||||
|
||||
/// Write a slice of bytes at a specified byte offset.
|
||||
fn write_slice(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||
let start_pos = offset / BLOCK_SIZE;
|
||||
let end_pos = (offset + buf.len()).div_ceil(BLOCK_SIZE);
|
||||
if end_pos > self.nblocks() {
|
||||
return_errno_with_msg!(Errno::InvalidArgs, "write_slice position is out of range");
|
||||
}
|
||||
let nblocks = end_pos - start_pos;
|
||||
let mut blocks = Buf::alloc(nblocks)?;
|
||||
|
||||
// Maybe we should read the first block partially.
|
||||
let start_offset = offset % BLOCK_SIZE;
|
||||
if start_offset != 0 {
|
||||
let mut start_block = Buf::alloc(1)?;
|
||||
self.read(start_pos, start_block.as_mut())?;
|
||||
blocks.as_mut_slice()[..start_offset]
|
||||
.copy_from_slice(&start_block.as_slice()[..start_offset]);
|
||||
}
|
||||
|
||||
// Copy the input buffer to the write buffer.
|
||||
let end_offset = start_offset + buf.len();
|
||||
blocks.as_mut_slice()[start_offset..end_offset].copy_from_slice(buf);
|
||||
|
||||
// Maybe we should read the last block partially.
|
||||
if end_offset % BLOCK_SIZE != 0 {
|
||||
let mut end_block = Buf::alloc(1)?;
|
||||
self.read(end_pos, end_block.as_mut())?;
|
||||
blocks.as_mut_slice()[end_offset..]
|
||||
.copy_from_slice(&end_block.as_slice()[end_offset % BLOCK_SIZE..]);
|
||||
}
|
||||
|
||||
// Write blocks.
|
||||
self.write(start_pos, blocks.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get a subset of the blocks in the block set.
|
||||
fn subset(&self, range: Range<BlockId>) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Ensure that blocks are persisted to the disk.
|
||||
fn flush(&self) -> Result<()>;
|
||||
|
||||
/// Returns the number of blocks.
|
||||
fn nblocks(&self) -> usize;
|
||||
}
|
||||
|
||||
macro_rules! impl_blockset_for {
|
||||
($typ:ty,$from:tt,$subset_fn:expr) => {
|
||||
#[inherit_methods(from = $from)]
|
||||
impl<T: BlockSet> BlockSet for $typ {
|
||||
fn read(&self, pos: BlockId, buf: BufMut) -> Result<()>;
|
||||
fn read_slice(&self, offset: usize, buf: &mut [u8]) -> Result<()>;
|
||||
fn write(&self, pos: BlockId, buf: BufRef) -> Result<()>;
|
||||
fn write_slice(&self, offset: usize, buf: &[u8]) -> Result<()>;
|
||||
fn flush(&self) -> Result<()>;
|
||||
fn nblocks(&self) -> usize;
|
||||
fn subset(&self, range: Range<BlockId>) -> Result<Self> {
|
||||
let closure = $subset_fn;
|
||||
closure(self, range)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_blockset_for!(&T, "(**self)", |_this, _range| {
|
||||
return_errno_with_msg!(Errno::NotFound, "cannot return `Self` by `subset` of `&T`");
|
||||
});
|
||||
|
||||
impl_blockset_for!(&mut T, "(**self)", |_this, _range| {
|
||||
return_errno_with_msg!(
|
||||
Errno::NotFound,
|
||||
"cannot return `Self` by `subset` of `&mut T`"
|
||||
);
|
||||
});
|
||||
|
||||
impl_blockset_for!(Box<T>, "(**self)", |this: &T, range| {
|
||||
this.subset(range).map(|v| Box::new(v))
|
||||
});
|
||||
|
||||
impl_blockset_for!(Arc<T>, "(**self)", |this: &Arc<T>, range| {
|
||||
(**this).subset(range).map(|v| Arc::new(v))
|
||||
});
|
||||
|
||||
/// A disk that impl `BlockSet`.
|
||||
///
|
||||
/// The `region` is the accessible subset.
|
||||
#[derive(Clone)]
|
||||
pub struct MemDisk {
|
||||
disk: Arc<Mutex<Buf>>,
|
||||
region: Range<BlockId>,
|
||||
}
|
||||
|
||||
impl MemDisk {
|
||||
/// Create a `MemDisk` with the number of blocks.
|
||||
pub fn create(num_blocks: usize) -> Result<Self> {
|
||||
let blocks = Buf::alloc(num_blocks)?;
|
||||
Ok(Self {
|
||||
disk: Arc::new(Mutex::new(blocks)),
|
||||
region: Range {
|
||||
start: 0,
|
||||
end: num_blocks,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockSet for MemDisk {
|
||||
fn read(&self, pos: BlockId, mut buf: BufMut) -> Result<()> {
|
||||
if pos + buf.nblocks() > self.region.end {
|
||||
return_errno_with_msg!(Errno::InvalidArgs, "read position is out of range");
|
||||
}
|
||||
let offset = (self.region.start + pos) * BLOCK_SIZE;
|
||||
let buf_len = buf.as_slice().len();
|
||||
|
||||
let disk = self.disk.lock();
|
||||
buf.as_mut_slice()
|
||||
.copy_from_slice(&disk.as_slice()[offset..offset + buf_len]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, pos: BlockId, buf: BufRef) -> Result<()> {
|
||||
if pos + buf.nblocks() > self.region.end {
|
||||
return_errno_with_msg!(Errno::InvalidArgs, "write position is out of range");
|
||||
}
|
||||
let offset = (self.region.start + pos) * BLOCK_SIZE;
|
||||
let buf_len = buf.as_slice().len();
|
||||
|
||||
let mut disk = self.disk.lock();
|
||||
disk.as_mut_slice()[offset..offset + buf_len].copy_from_slice(buf.as_slice());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn subset(&self, range: Range<BlockId>) -> Result<Self> {
|
||||
if self.region.start + range.end > self.region.end {
|
||||
return_errno_with_msg!(Errno::InvalidArgs, "subset is out of range");
|
||||
}
|
||||
|
||||
Ok(MemDisk {
|
||||
disk: self.disk.clone(),
|
||||
region: Range {
|
||||
start: self.region.start + range.start,
|
||||
end: self.region.start + range.end,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn nblocks(&self) -> usize {
|
||||
self.region.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ops::Range;
|
||||
|
||||
use crate::layers::bio::{BlockSet, Buf, MemDisk};
|
||||
|
||||
#[test]
|
||||
fn mem_disk() {
|
||||
let num_blocks = 64;
|
||||
let disk = MemDisk::create(num_blocks).unwrap();
|
||||
assert_eq!(disk.nblocks(), 64);
|
||||
|
||||
let mut buf = Buf::alloc(1).unwrap();
|
||||
buf.as_mut_slice().fill(1);
|
||||
disk.write(32, buf.as_ref()).unwrap();
|
||||
|
||||
let range = Range { start: 32, end: 64 };
|
||||
let subset = disk.subset(range).unwrap();
|
||||
assert_eq!(subset.nblocks(), 32);
|
||||
|
||||
buf.as_mut_slice().fill(0);
|
||||
subset.read(0, buf.as_mut()).unwrap();
|
||||
assert_eq!(buf.as_ref().as_slice(), [1u8; 4096]);
|
||||
|
||||
subset.write_slice(4096 - 4, &[2u8; 8]).unwrap();
|
||||
let mut buf = [0u8; 16];
|
||||
subset.read_slice(4096 - 8, &mut buf).unwrap();
|
||||
assert_eq!(buf, [1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user