mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-16 17:46:48 +00:00
229 lines
7.8 KiB
Rust
229 lines
7.8 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use ostd::mm::{Frame, FrameAllocOptions, Segment, VmIo};
|
|
|
|
use super::{
|
|
bio::{Bio, BioEnqueueError, BioSegment, BioStatus, BioType, BioWaiter, SubmittedBio},
|
|
id::{Bid, Sid},
|
|
BlockDevice, BLOCK_SIZE, SECTOR_SIZE,
|
|
};
|
|
use crate::prelude::*;
|
|
|
|
/// Implements several commonly used APIs for the block device to conveniently
|
|
/// read and write block(s).
|
|
// TODO: Add API to submit bio with multiple segments in scatter/gather manner.
|
|
impl dyn BlockDevice {
|
|
/// Synchronously reads contiguous blocks starting from the `bid`.
|
|
pub fn read_blocks(&self, bid: Bid, segment: &Segment) -> Result<BioStatus, BioEnqueueError> {
|
|
let bio = create_bio_from_segment(BioType::Read, bid, segment);
|
|
let status = bio.submit_and_wait(self)?;
|
|
Ok(status)
|
|
}
|
|
|
|
/// Asynchronously reads contiguous blocks starting from the `bid`.
|
|
pub fn read_blocks_async(
|
|
&self,
|
|
bid: Bid,
|
|
segment: &Segment,
|
|
) -> Result<BioWaiter, BioEnqueueError> {
|
|
let bio = create_bio_from_segment(BioType::Read, bid, segment);
|
|
bio.submit(self)
|
|
}
|
|
|
|
/// Synchronously reads one block indicated by the `bid`.
|
|
pub fn read_block(&self, bid: Bid, frame: &Frame) -> Result<BioStatus, BioEnqueueError> {
|
|
let bio = create_bio_from_frame(BioType::Read, bid, frame);
|
|
let status = bio.submit_and_wait(self)?;
|
|
Ok(status)
|
|
}
|
|
|
|
/// Asynchronously reads one block indicated by the `bid`.
|
|
pub fn read_block_async(&self, bid: Bid, frame: &Frame) -> Result<BioWaiter, BioEnqueueError> {
|
|
let bio = create_bio_from_frame(BioType::Read, bid, frame);
|
|
bio.submit(self)
|
|
}
|
|
|
|
/// Synchronously writes contiguous blocks starting from the `bid`.
|
|
pub fn write_blocks(&self, bid: Bid, segment: &Segment) -> Result<BioStatus, BioEnqueueError> {
|
|
let bio = create_bio_from_segment(BioType::Write, bid, segment);
|
|
let status = bio.submit_and_wait(self)?;
|
|
Ok(status)
|
|
}
|
|
|
|
/// Asynchronously writes contiguous blocks starting from the `bid`.
|
|
pub fn write_blocks_async(
|
|
&self,
|
|
bid: Bid,
|
|
segment: &Segment,
|
|
) -> Result<BioWaiter, BioEnqueueError> {
|
|
let bio = create_bio_from_segment(BioType::Write, bid, segment);
|
|
bio.submit(self)
|
|
}
|
|
|
|
/// Synchronously writes one block indicated by the `bid`.
|
|
pub fn write_block(&self, bid: Bid, frame: &Frame) -> Result<BioStatus, BioEnqueueError> {
|
|
let bio = create_bio_from_frame(BioType::Write, bid, frame);
|
|
let status = bio.submit_and_wait(self)?;
|
|
Ok(status)
|
|
}
|
|
|
|
/// Asynchronously writes one block indicated by the `bid`.
|
|
pub fn write_block_async(&self, bid: Bid, frame: &Frame) -> Result<BioWaiter, BioEnqueueError> {
|
|
let bio = create_bio_from_frame(BioType::Write, bid, frame);
|
|
bio.submit(self)
|
|
}
|
|
}
|
|
|
|
impl VmIo for dyn BlockDevice {
|
|
/// Reads consecutive bytes of several sectors in size.
|
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> ostd::Result<()> {
|
|
if offset % SECTOR_SIZE != 0 || buf.len() % SECTOR_SIZE != 0 {
|
|
return Err(ostd::Error::InvalidArgs);
|
|
}
|
|
if buf.is_empty() {
|
|
return Ok(());
|
|
}
|
|
|
|
let (bio, bio_segment) = {
|
|
let num_blocks = {
|
|
let first = Bid::from_offset(offset).to_raw();
|
|
let last = Bid::from_offset(offset + buf.len() - 1).to_raw();
|
|
last - first + 1
|
|
};
|
|
let segment = FrameAllocOptions::new(num_blocks as usize)
|
|
.uninit(true)
|
|
.alloc_contiguous()?;
|
|
let bio_segment = BioSegment::from_segment(segment, offset % BLOCK_SIZE, buf.len());
|
|
|
|
(
|
|
Bio::new(
|
|
BioType::Read,
|
|
Sid::from_offset(offset),
|
|
vec![bio_segment.clone()],
|
|
None,
|
|
),
|
|
bio_segment,
|
|
)
|
|
};
|
|
|
|
let status = bio.submit_and_wait(self)?;
|
|
match status {
|
|
BioStatus::Complete => {
|
|
let _ = bio_segment.reader().read(&mut buf.into());
|
|
Ok(())
|
|
}
|
|
_ => Err(ostd::Error::IoError),
|
|
}
|
|
}
|
|
|
|
/// Writes consecutive bytes of several sectors in size.
|
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> ostd::Result<()> {
|
|
if offset % SECTOR_SIZE != 0 || buf.len() % SECTOR_SIZE != 0 {
|
|
return Err(ostd::Error::InvalidArgs);
|
|
}
|
|
if buf.is_empty() {
|
|
return Ok(());
|
|
}
|
|
|
|
let bio = {
|
|
let num_blocks = {
|
|
let first = Bid::from_offset(offset).to_raw();
|
|
let last = Bid::from_offset(offset + buf.len() - 1).to_raw();
|
|
last - first + 1
|
|
};
|
|
let segment = FrameAllocOptions::new(num_blocks as usize)
|
|
.uninit(true)
|
|
.alloc_contiguous()?;
|
|
segment.write_bytes(offset % BLOCK_SIZE, buf)?;
|
|
let len = segment
|
|
.writer()
|
|
.skip(offset % BLOCK_SIZE)
|
|
.write(&mut buf.into());
|
|
let bio_segment = BioSegment::from_segment(segment, offset % BLOCK_SIZE, len);
|
|
Bio::new(
|
|
BioType::Write,
|
|
Sid::from_offset(offset),
|
|
vec![bio_segment],
|
|
None,
|
|
)
|
|
};
|
|
|
|
let status = bio.submit_and_wait(self)?;
|
|
match status {
|
|
BioStatus::Complete => Ok(()),
|
|
_ => Err(ostd::Error::IoError),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl dyn BlockDevice {
|
|
/// Asynchronously writes consecutive bytes of several sectors in size.
|
|
pub fn write_bytes_async(&self, offset: usize, buf: &[u8]) -> ostd::Result<BioWaiter> {
|
|
if offset % SECTOR_SIZE != 0 || buf.len() % SECTOR_SIZE != 0 {
|
|
return Err(ostd::Error::InvalidArgs);
|
|
}
|
|
if buf.is_empty() {
|
|
return Ok(BioWaiter::new());
|
|
}
|
|
|
|
let bio = {
|
|
let num_blocks = {
|
|
let first = Bid::from_offset(offset).to_raw();
|
|
let last = Bid::from_offset(offset + buf.len() - 1).to_raw();
|
|
last - first + 1
|
|
};
|
|
let segment = FrameAllocOptions::new(num_blocks as usize)
|
|
.uninit(true)
|
|
.alloc_contiguous()?;
|
|
segment.write_bytes(offset % BLOCK_SIZE, buf)?;
|
|
let len = segment
|
|
.writer()
|
|
.skip(offset % BLOCK_SIZE)
|
|
.write(&mut buf.into());
|
|
let bio_segment = BioSegment::from_segment(segment, offset % BLOCK_SIZE, len);
|
|
Bio::new(
|
|
BioType::Write,
|
|
Sid::from_offset(offset),
|
|
vec![bio_segment],
|
|
Some(general_complete_fn),
|
|
)
|
|
};
|
|
|
|
let complete = bio.submit(self)?;
|
|
Ok(complete)
|
|
}
|
|
}
|
|
|
|
// TODO: Maybe we should have a builder for `Bio`.
|
|
fn create_bio_from_segment(type_: BioType, bid: Bid, segment: &Segment) -> Bio {
|
|
let bio_segment = BioSegment::from_segment(segment.clone(), 0, segment.nbytes());
|
|
Bio::new(
|
|
type_,
|
|
Sid::from(bid),
|
|
vec![bio_segment],
|
|
Some(general_complete_fn),
|
|
)
|
|
}
|
|
|
|
// TODO: Maybe we should have a builder for `Bio`.
|
|
fn create_bio_from_frame(type_: BioType, bid: Bid, frame: &Frame) -> Bio {
|
|
let bio_segment = BioSegment::from_frame(frame.clone(), 0, BLOCK_SIZE);
|
|
Bio::new(
|
|
type_,
|
|
Sid::from(bid),
|
|
vec![bio_segment],
|
|
Some(general_complete_fn),
|
|
)
|
|
}
|
|
|
|
fn general_complete_fn(bio: &SubmittedBio) {
|
|
match bio.status() {
|
|
BioStatus::Complete => (),
|
|
err_status => log::error!(
|
|
"faild to do {:?} on the device with error status: {:?}",
|
|
bio.type_(),
|
|
err_status
|
|
),
|
|
}
|
|
}
|