Refactor the block layer by introducing BioSegmentPool

This commit is contained in:
Shaowei Song
2024-11-20 13:51:22 +00:00
committed by Tate, Hongliang Tian
parent d37da228ab
commit ecad132ec9
13 changed files with 608 additions and 348 deletions

View File

@ -5,7 +5,11 @@
use core::{num::NonZeroUsize, ops::Range, sync::atomic::AtomicU64};
use aster_block::{bio::BioWaiter, id::BlockId, BlockDevice};
use aster_block::{
bio::{BioDirection, BioSegment, BioWaiter},
id::BlockId,
BlockDevice,
};
use hashbrown::HashMap;
use lru::LruCache;
use ostd::mm::Frame;
@ -368,9 +372,11 @@ impl PageCacheBackend for ExfatFS {
if self.fs_size() < idx * PAGE_SIZE {
return_errno_with_message!(Errno::EINVAL, "invalid read size")
}
let bio_segment =
BioSegment::new_from_segment(frame.clone().into(), BioDirection::FromDevice);
let waiter = self
.block_device
.read_block_async(BlockId::new(idx as u64), frame)?;
.read_blocks_async(BlockId::new(idx as u64), bio_segment)?;
Ok(waiter)
}
@ -378,9 +384,11 @@ impl PageCacheBackend for ExfatFS {
if self.fs_size() < idx * PAGE_SIZE {
return_errno_with_message!(Errno::EINVAL, "invalid write size")
}
let bio_segment =
BioSegment::new_from_segment(frame.clone().into(), BioDirection::ToDevice);
let waiter = self
.block_device
.write_block_async(BlockId::new(idx as u64), frame)?;
.write_blocks_async(BlockId::new(idx as u64), bio_segment)?;
Ok(waiter)
}

View File

@ -8,12 +8,12 @@ use core::{cmp::Ordering, time::Duration};
pub(super) use align_ext::AlignExt;
use aster_block::{
bio::BioWaiter,
bio::{BioDirection, BioSegment, BioWaiter},
id::{Bid, BlockId},
BLOCK_SIZE,
};
use aster_rights::Full;
use ostd::mm::{Frame, FrameAllocOptions, VmIo};
use ostd::mm::{Frame, VmIo};
use super::{
constants::*,
@ -141,9 +141,11 @@ impl PageCacheBackend for ExfatInode {
return_errno_with_message!(Errno::EINVAL, "Invalid read size")
}
let sector_id = inner.get_sector_id(idx * PAGE_SIZE / inner.fs().sector_size())?;
let waiter = inner.fs().block_device().read_block_async(
let bio_segment =
BioSegment::new_from_segment(frame.clone().into(), BioDirection::FromDevice);
let waiter = inner.fs().block_device().read_blocks_async(
BlockId::from_offset(sector_id * inner.fs().sector_size()),
frame,
bio_segment,
)?;
Ok(waiter)
}
@ -156,9 +158,11 @@ impl PageCacheBackend for ExfatInode {
// FIXME: We may need to truncate the file if write_page fails.
// To fix this issue, we need to change the interface of the PageCacheBackend trait.
let waiter = inner.fs().block_device().write_block_async(
let bio_segment =
BioSegment::new_from_segment(frame.clone().into(), BioDirection::ToDevice);
let waiter = inner.fs().block_device().write_blocks_async(
BlockId::from_offset(sector_id * inner.fs().sector_size()),
frame,
bio_segment,
)?;
Ok(waiter)
}
@ -1263,10 +1267,7 @@ impl Inode for ExfatInode {
.discard_range(read_off..read_off + read_len);
let mut buf_offset = 0;
let frame = FrameAllocOptions::new(1)
.uninit(true)
.alloc_single()
.unwrap();
let bio_segment = BioSegment::alloc(1, BioDirection::FromDevice);
let start_pos = inner.start_chain.walk_to_cluster_at_offset(read_off)?;
let cluster_size = inner.fs().cluster_size();
@ -1275,8 +1276,11 @@ impl Inode for ExfatInode {
for _ in Bid::from_offset(read_off)..Bid::from_offset(read_off + read_len) {
let physical_bid =
Bid::from_offset(cur_cluster.cluster_id() as usize * cluster_size + cur_offset);
inner.fs().block_device().read_block(physical_bid, &frame)?;
frame.read(0, writer).unwrap();
inner
.fs()
.block_device()
.read_blocks(physical_bid, bio_segment.clone())?;
bio_segment.reader().unwrap().read_fallible(writer)?;
buf_offset += BLOCK_SIZE;
cur_offset += BLOCK_SIZE;
@ -1370,23 +1374,18 @@ impl Inode for ExfatInode {
let inner = self.inner.upread();
let bio_segment = BioSegment::alloc(1, BioDirection::ToDevice);
let start_pos = inner.start_chain.walk_to_cluster_at_offset(offset)?;
let cluster_size = inner.fs().cluster_size();
let mut cur_cluster = start_pos.0.clone();
let mut cur_offset = start_pos.1;
for _ in Bid::from_offset(offset)..Bid::from_offset(end_offset) {
let frame = {
let frame = FrameAllocOptions::new(1)
.uninit(true)
.alloc_single()
.unwrap();
frame.write(0, reader)?;
frame
};
bio_segment.writer().unwrap().write_fallible(reader)?;
let physical_bid =
Bid::from_offset(cur_cluster.cluster_id() as usize * cluster_size + cur_offset);
let fs = inner.fs();
fs.block_device().write_block(physical_bid, &frame)?;
fs.block_device()
.write_blocks(physical_bid, bio_segment.clone())?;
cur_offset += BLOCK_SIZE;
if cur_offset >= cluster_size {

View File

@ -83,6 +83,7 @@ mod test {
for seg in bio.segments() {
let size = match bio.type_() {
BioType::Read => seg
.inner_segment()
.writer()
.write(&mut self.queue.0.reader().skip(cur_device_ofs)),
BioType::Write => self
@ -90,7 +91,7 @@ mod test {
.0
.writer()
.skip(cur_device_ofs)
.write(&mut seg.reader()),
.write(&mut seg.inner_segment().reader()),
_ => 0,
};
cur_device_ofs += size;

View File

@ -28,7 +28,7 @@ struct BlockGroupImpl {
impl BlockGroup {
/// Loads and constructs a block group.
pub fn load(
group_descriptors_segment: &SegmentSlice,
group_descriptors_segment: &Segment,
idx: usize,
block_device: &dyn BlockDevice,
super_block: &SuperBlock,
@ -320,12 +320,22 @@ impl Debug for BlockGroup {
impl PageCacheBackend for BlockGroupImpl {
fn read_page_async(&self, idx: usize, frame: &Frame) -> Result<BioWaiter> {
let bid = self.inode_table_bid + idx as Ext2Bid;
self.fs.upgrade().unwrap().read_block_async(bid, frame)
let bio_segment =
BioSegment::new_from_segment(frame.clone().into(), BioDirection::FromDevice);
self.fs
.upgrade()
.unwrap()
.read_blocks_async(bid, bio_segment)
}
fn write_page_async(&self, idx: usize, frame: &Frame) -> Result<BioWaiter> {
let bid = self.inode_table_bid + idx as Ext2Bid;
self.fs.upgrade().unwrap().write_block_async(bid, frame)
let bio_segment =
BioSegment::new_from_segment(frame.clone().into(), BioDirection::ToDevice);
self.fs
.upgrade()
.unwrap()
.write_blocks_async(bid, bio_segment)
}
fn npages(&self) -> usize {

View File

@ -23,7 +23,7 @@ pub struct Ext2 {
blocks_per_group: Ext2Bid,
inode_size: usize,
block_size: usize,
group_descriptors_segment: SegmentSlice,
group_descriptors_segment: Segment,
self_ref: Weak<Self>,
}
@ -48,9 +48,10 @@ impl Ext2 {
.div_ceil(BLOCK_SIZE);
let segment = FrameAllocOptions::new(npages)
.uninit(true)
.alloc_contiguous()?
.into();
match block_device.read_blocks(super_block.group_descriptors_bid(0), &segment)? {
.alloc_contiguous()?;
let bio_segment =
BioSegment::new_from_segment(segment.clone(), BioDirection::FromDevice);
match block_device.read_blocks(super_block.group_descriptors_bid(0), bio_segment)? {
BioStatus::Complete => (),
err_status => {
return Err(Error::from(err_status));
@ -62,7 +63,7 @@ impl Ext2 {
// Load the block groups information
let load_block_groups = |fs: Weak<Ext2>,
block_device: &dyn BlockDevice,
group_descriptors_segment: &SegmentSlice|
group_descriptors_segment: &Segment|
-> Result<Vec<BlockGroup>> {
let block_groups_count = super_block.block_groups_count() as usize;
let mut block_groups = Vec::with_capacity(block_groups_count);
@ -298,10 +299,10 @@ impl Ext2 {
}
/// Reads contiguous blocks starting from the `bid` synchronously.
pub(super) fn read_blocks(&self, bid: Ext2Bid, segment: &SegmentSlice) -> Result<()> {
pub(super) fn read_blocks(&self, bid: Ext2Bid, bio_segment: BioSegment) -> Result<()> {
let status = self
.block_device
.read_blocks(Bid::new(bid as u64), segment)?;
.read_blocks(Bid::new(bid as u64), bio_segment)?;
match status {
BioStatus::Complete => Ok(()),
err_status => Err(Error::from(err_status)),
@ -312,36 +313,19 @@ impl Ext2 {
pub(super) fn read_blocks_async(
&self,
bid: Ext2Bid,
segment: &SegmentSlice,
bio_segment: BioSegment,
) -> Result<BioWaiter> {
let waiter = self
.block_device
.read_blocks_async(Bid::new(bid as u64), segment)?;
Ok(waiter)
}
/// Reads one block indicated by the `bid` synchronously.
pub(super) fn read_block(&self, bid: Ext2Bid, frame: &Frame) -> Result<()> {
let status = self.block_device.read_block(Bid::new(bid as u64), frame)?;
match status {
BioStatus::Complete => Ok(()),
err_status => Err(Error::from(err_status)),
}
}
/// Reads one block indicated by the `bid` asynchronously.
pub(super) fn read_block_async(&self, bid: Ext2Bid, frame: &Frame) -> Result<BioWaiter> {
let waiter = self
.block_device
.read_block_async(Bid::new(bid as u64), frame)?;
.read_blocks_async(Bid::new(bid as u64), bio_segment)?;
Ok(waiter)
}
/// Writes contiguous blocks starting from the `bid` synchronously.
pub(super) fn write_blocks(&self, bid: Ext2Bid, segment: &SegmentSlice) -> Result<()> {
pub(super) fn write_blocks(&self, bid: Ext2Bid, bio_segment: BioSegment) -> Result<()> {
let status = self
.block_device
.write_blocks(Bid::new(bid as u64), segment)?;
.write_blocks(Bid::new(bid as u64), bio_segment)?;
match status {
BioStatus::Complete => Ok(()),
err_status => Err(Error::from(err_status)),
@ -352,28 +336,11 @@ impl Ext2 {
pub(super) fn write_blocks_async(
&self,
bid: Ext2Bid,
segment: &SegmentSlice,
bio_segment: BioSegment,
) -> Result<BioWaiter> {
let waiter = self
.block_device
.write_blocks_async(Bid::new(bid as u64), segment)?;
Ok(waiter)
}
/// Writes one block indicated by the `bid` synchronously.
pub(super) fn write_block(&self, bid: Ext2Bid, frame: &Frame) -> Result<()> {
let status = self.block_device.write_block(Bid::new(bid as u64), frame)?;
match status {
BioStatus::Complete => Ok(()),
err_status => Err(Error::from(err_status)),
}
}
/// Writes one block indicated by the `bid` asynchronously.
pub(super) fn write_block_async(&self, bid: Ext2Bid, frame: &Frame) -> Result<BioWaiter> {
let waiter = self
.block_device
.write_block_async(Bid::new(bid as u64), frame)?;
.write_blocks_async(Bid::new(bid as u64), bio_segment)?;
Ok(waiter)
}
@ -397,9 +364,13 @@ impl Ext2 {
self.block_device
.write_bytes_async(SUPER_BLOCK_OFFSET, raw_super_block.as_bytes())?,
);
let group_descriptors_bio_segment = BioSegment::new_from_segment(
self.group_descriptors_segment.clone(),
BioDirection::ToDevice,
);
bio_waiter.concat(self.block_device.write_blocks_async(
super_block.group_descriptors_bid(0),
&self.group_descriptors_segment,
group_descriptors_bio_segment.clone(),
)?);
bio_waiter
.wait()
@ -418,7 +389,7 @@ impl Ext2 {
)?);
bio_waiter.concat(self.block_device.write_blocks_async(
super_block.group_descriptors_bid(idx as usize),
&self.group_descriptors_segment,
group_descriptors_bio_segment.clone(),
)?);
bio_waiter.wait().ok_or_else(|| {
Error::with_message(Errno::EIO, "failed to sync backup metadata")

View File

@ -42,7 +42,9 @@ impl IndirectBlockCache {
let fs = self.fs();
let load_block = || -> Result<IndirectBlock> {
let mut block = IndirectBlock::alloc_uninit()?;
fs.read_block(bid, &block.frame)?;
let bio_segment =
BioSegment::new_from_segment(block.frame.clone().into(), BioDirection::FromDevice);
fs.read_blocks(bid, bio_segment)?;
block.state = State::UpToDate;
Ok(block)
};
@ -59,7 +61,9 @@ impl IndirectBlockCache {
let fs = self.fs();
let load_block = || -> Result<IndirectBlock> {
let mut block = IndirectBlock::alloc_uninit()?;
fs.read_block(bid, &block.frame)?;
let bio_segment =
BioSegment::new_from_segment(block.frame.clone().into(), BioDirection::FromDevice);
fs.read_blocks(bid, bio_segment)?;
block.state = State::UpToDate;
Ok(block)
};
@ -104,7 +108,11 @@ impl IndirectBlockCache {
for _ in 0..num {
let (bid, block) = self.cache.pop_lru().unwrap();
if block.is_dirty() {
bio_waiter.concat(self.fs().write_block_async(bid, &block.frame)?);
let bio_segment = BioSegment::new_from_segment(
block.frame.clone().into(),
BioDirection::ToDevice,
);
bio_waiter.concat(self.fs().write_blocks_async(bid, bio_segment)?);
}
}

View File

@ -920,6 +920,7 @@ impl Inner {
}
pub fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
debug_assert!(is_block_aligned(offset) && is_block_aligned(writer.avail()));
let (offset, read_len) = {
let file_size = self.inode_impl.file_size();
let start = file_size.min(offset).align_down(BLOCK_SIZE);
@ -928,17 +929,16 @@ impl Inner {
.align_down(BLOCK_SIZE);
(start, end - start)
};
if read_len == 0 {
return Ok(read_len);
}
self.page_cache.discard_range(offset..offset + read_len);
let start_bid = Bid::from_offset(offset).to_raw() as Ext2Bid;
let buf_nblocks = read_len / BLOCK_SIZE;
let segment = FrameAllocOptions::new(buf_nblocks)
.uninit(true)
.alloc_contiguous()?
.into();
self.inode_impl
.read_blocks(start_bid, buf_nblocks, writer)?;
self.inode_impl.read_blocks(start_bid, &segment)?;
segment.read(0, writer)?;
Ok(read_len)
}
@ -958,6 +958,7 @@ impl Inner {
}
pub fn write_direct_at(&mut self, offset: usize, reader: &mut VmReader) -> Result<usize> {
debug_assert!(is_block_aligned(offset) && is_block_aligned(reader.remain()));
let file_size = self.inode_impl.file_size();
let write_len = reader.remain();
let end_offset = offset + write_len;
@ -972,15 +973,9 @@ impl Inner {
let start_bid = Bid::from_offset(offset).to_raw() as Ext2Bid;
let buf_nblocks = write_len / BLOCK_SIZE;
let segment = {
let segment = FrameAllocOptions::new(buf_nblocks)
.uninit(true)
.alloc_contiguous()?;
segment.write(0, reader)?;
segment.into()
};
self.inode_impl
.write_blocks(start_bid, buf_nblocks, reader)?;
self.inode_impl.write_blocks(start_bid, &segment)?;
Ok(write_len)
}
@ -1114,58 +1109,94 @@ impl InodeImpl_ {
self.inode().fs()
}
pub fn read_blocks_async(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<BioWaiter> {
let nblocks = blocks.nframes();
pub fn read_blocks_async(
&self,
bid: Ext2Bid,
nblocks: usize,
writer: &mut VmWriter,
) -> Result<BioWaiter> {
debug_assert!(nblocks * BLOCK_SIZE <= writer.avail());
let mut bio_waiter = BioWaiter::new();
let mut blocks_offset = 0;
for dev_range in DeviceRangeReader::new(self, bid..bid + nblocks as Ext2Bid)? {
let first_bid = dev_range.start as Ext2Bid;
let range_len = dev_range.len();
let segment = blocks.range(blocks_offset..blocks_offset + range_len);
let start_bid = dev_range.start as Ext2Bid;
let range_nblocks = dev_range.len();
let waiter = self.fs().read_blocks_async(first_bid, &segment)?;
let bio_segment = BioSegment::alloc(range_nblocks, BioDirection::FromDevice);
bio_segment.reader().unwrap().read_fallible(writer)?;
let waiter = self.fs().read_blocks_async(start_bid, bio_segment)?;
bio_waiter.concat(waiter);
blocks_offset += range_len;
}
Ok(bio_waiter)
}
pub fn read_blocks(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<()> {
match self.read_blocks_async(bid, blocks)?.wait() {
pub fn read_blocks(&self, bid: Ext2Bid, nblocks: usize, writer: &mut VmWriter) -> Result<()> {
match self.read_blocks_async(bid, nblocks, writer)?.wait() {
Some(BioStatus::Complete) => Ok(()),
_ => return_errno!(Errno::EIO),
}
}
pub fn write_blocks_async(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<BioWaiter> {
let nblocks = blocks.nframes();
pub fn read_block_async(&self, bid: Ext2Bid, frame: &Frame) -> Result<BioWaiter> {
let mut bio_waiter = BioWaiter::new();
let mut blocks_offset = 0;
for dev_range in DeviceRangeReader::new(self, bid..bid + nblocks as Ext2Bid)? {
let first_bid = dev_range.start as Ext2Bid;
let range_len = dev_range.len();
let segment = blocks.range(blocks_offset..blocks_offset + range_len);
let waiter = self.fs().write_blocks_async(first_bid, &segment)?;
for dev_range in DeviceRangeReader::new(self, bid..bid + 1 as Ext2Bid)? {
let start_bid = dev_range.start as Ext2Bid;
let bio_segment =
BioSegment::new_from_segment(frame.clone().into(), BioDirection::FromDevice);
let waiter = self.fs().read_blocks_async(start_bid, bio_segment)?;
bio_waiter.concat(waiter);
blocks_offset += range_len;
}
Ok(bio_waiter)
}
pub fn write_blocks(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<()> {
match self.write_blocks_async(bid, blocks)?.wait() {
pub fn write_blocks_async(
&self,
bid: Ext2Bid,
nblocks: usize,
reader: &mut VmReader,
) -> Result<BioWaiter> {
debug_assert_eq!(nblocks * BLOCK_SIZE, reader.remain());
let mut bio_waiter = BioWaiter::new();
for dev_range in DeviceRangeReader::new(self, bid..bid + nblocks as Ext2Bid)? {
let start_bid = dev_range.start as Ext2Bid;
let range_nblocks = dev_range.len();
let bio_segment = BioSegment::alloc(range_nblocks, BioDirection::ToDevice);
bio_segment.writer().unwrap().write_fallible(reader)?;
let waiter = self.fs().write_blocks_async(start_bid, bio_segment)?;
bio_waiter.concat(waiter);
}
Ok(bio_waiter)
}
pub fn write_blocks(&self, bid: Ext2Bid, nblocks: usize, reader: &mut VmReader) -> Result<()> {
match self.write_blocks_async(bid, nblocks, reader)?.wait() {
Some(BioStatus::Complete) => Ok(()),
_ => return_errno!(Errno::EIO),
}
}
pub fn write_block_async(&self, bid: Ext2Bid, frame: &Frame) -> Result<BioWaiter> {
let mut bio_waiter = BioWaiter::new();
for dev_range in DeviceRangeReader::new(self, bid..bid + 1 as Ext2Bid)? {
let start_bid = dev_range.start as Ext2Bid;
let bio_segment =
BioSegment::new_from_segment(frame.clone().into(), BioDirection::ToDevice);
let waiter = self.fs().write_blocks_async(start_bid, bio_segment)?;
bio_waiter.concat(waiter);
}
Ok(bio_waiter)
}
pub fn resize(&mut self, new_size: usize) -> Result<()> {
let old_size = self.desc.size;
if new_size > old_size {
@ -1816,21 +1847,39 @@ impl InodeImpl {
}
/// Reads one or multiple blocks to the segment start from `bid` asynchronously.
pub fn read_blocks_async(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<BioWaiter> {
self.0.read().read_blocks_async(bid, blocks)
pub fn read_blocks_async(
&self,
bid: Ext2Bid,
nblocks: usize,
writer: &mut VmWriter,
) -> Result<BioWaiter> {
self.0.read().read_blocks_async(bid, nblocks, writer)
}
pub fn read_blocks(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<()> {
self.0.read().read_blocks(bid, blocks)
pub fn read_blocks(&self, bid: Ext2Bid, nblocks: usize, writer: &mut VmWriter) -> Result<()> {
self.0.read().read_blocks(bid, nblocks, writer)
}
pub fn read_block_async(&self, bid: Ext2Bid, frame: &Frame) -> Result<BioWaiter> {
self.0.read().read_block_async(bid, frame)
}
/// Writes one or multiple blocks from the segment start from `bid` asynchronously.
pub fn write_blocks_async(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<BioWaiter> {
self.0.read().write_blocks_async(bid, blocks)
pub fn write_blocks_async(
&self,
bid: Ext2Bid,
nblocks: usize,
reader: &mut VmReader,
) -> Result<BioWaiter> {
self.0.read().write_blocks_async(bid, nblocks, reader)
}
pub fn write_blocks(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<()> {
self.0.read().write_blocks(bid, blocks)
pub fn write_blocks(&self, bid: Ext2Bid, nblocks: usize, reader: &mut VmReader) -> Result<()> {
self.0.read().write_blocks(bid, nblocks, reader)
}
pub fn write_block_async(&self, bid: Ext2Bid, frame: &Frame) -> Result<BioWaiter> {
self.0.read().write_block_async(bid, frame)
}
pub fn set_device_id(&self, device_id: u64) {
@ -1894,12 +1943,12 @@ impl InodeImpl {
impl PageCacheBackend for InodeImpl {
fn read_page_async(&self, idx: usize, frame: &Frame) -> Result<BioWaiter> {
let bid = idx as Ext2Bid;
self.read_blocks_async(bid, &SegmentSlice::from(frame.clone()))
self.read_block_async(bid, frame)
}
fn write_page_async(&self, idx: usize, frame: &Frame) -> Result<BioWaiter> {
let bid = idx as Ext2Bid;
self.write_blocks_async(bid, &SegmentSlice::from(frame.clone()))
self.write_block_async(bid, frame)
}
fn npages(&self) -> usize {

View File

@ -7,14 +7,13 @@ pub(super) use core::{
pub(super) use align_ext::AlignExt;
pub(super) use aster_block::{
bio::{BioStatus, BioWaiter},
bio::{BioDirection, BioSegment, BioStatus, BioWaiter},
id::Bid,
BlockDevice, BLOCK_SIZE,
};
pub(super) use aster_rights::Full;
pub(super) use aster_util::segment_slice::SegmentSlice;
pub(super) use ostd::{
mm::{Frame, FrameAllocOptions, VmIo},
mm::{Frame, FrameAllocOptions, Segment, VmIo},
sync::{RwMutex, RwMutexReadGuard, RwMutexWriteGuard},
};
pub(super) use static_assertions::const_assert;