asterinas/kernel/src/device/registry/block.rs

145 lines
4.0 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
use aster_block::BlockDevice;
use aster_virtio::device::block::device::BlockDevice as VirtIoBlockDevice;
use device_id::DeviceId;
use ostd::mm::VmIo;
use crate::{
events::IoEvents,
fs::{
device::{add_node, Device, DeviceType},
fs_resolver::FsResolver,
inode_handle::FileIo,
utils::{InodeIo, StatusFlags},
},
prelude::*,
process::signal::{PollHandle, Pollable},
thread::kernel_thread::ThreadOptions,
};
pub(super) fn init_in_first_kthread() {
for device in aster_block::collect_all() {
if device.is_partition() {
continue;
}
let task_fn = move || {
info!("spawn the virt-io-block thread");
let virtio_block_device = device.downcast_ref::<VirtIoBlockDevice>().unwrap();
loop {
virtio_block_device.handle_requests();
}
};
ThreadOptions::new(task_fn).spawn();
}
}
pub(super) fn init_in_first_process(fs_resolver: &FsResolver) -> Result<()> {
for device in aster_block::collect_all() {
let device = Arc::new(BlockFile::new(device));
if let Some(devtmpfs_path) = device.devtmpfs_path() {
add_node(device, &devtmpfs_path, fs_resolver)?;
}
}
Ok(())
}
/// Represents a block device inode in the filesystem.
//
// TODO: This type wraps an `Arc<dyn BlockDevice>` in another `Arc` just to implement the `Device`
// trait. It leads to redundant vtable dispatch, reference counting, and heap allocation. We should
// devise a better strategy to eliminate the unnecessary intermediate `Arc`.
#[derive(Debug)]
struct BlockFile(Arc<dyn BlockDevice>);
impl BlockFile {
fn new(device: Arc<dyn BlockDevice>) -> Self {
Self(device)
}
}
impl Device for BlockFile {
fn type_(&self) -> DeviceType {
DeviceType::Block
}
fn id(&self) -> DeviceId {
self.0.id()
}
fn devtmpfs_path(&self) -> Option<String> {
Some(self.0.name().into())
}
fn open(&self) -> Result<Box<dyn FileIo>> {
Ok(Box::new(OpenBlockFile(self.0.clone())))
}
}
/// Represents an opened block device file ready for I/O operations.
//
// TODO: This type wraps an `Arc<dyn BlockDevice>` in another `Box` just to implement the `FileIo`
// trait. It leads to redundant vtable dispatch and heap allocation. We should devise a better
// strategy to eliminate the unnecessary intermediate `Box`.
struct OpenBlockFile(Arc<dyn BlockDevice>);
impl InodeIo for OpenBlockFile {
fn read_at(
&self,
offset: usize,
writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
let total = writer.avail();
self.0.read(offset, writer)?;
let avail = writer.avail();
Ok(total - avail)
}
fn write_at(
&self,
offset: usize,
reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
let total = reader.remain();
self.0.write(offset, reader)?;
let remain = reader.remain();
Ok(total - remain)
}
}
impl Pollable for OpenBlockFile {
fn poll(&self, mask: IoEvents, _: Option<&mut PollHandle>) -> IoEvents {
let events = IoEvents::IN | IoEvents::OUT;
events & mask
}
}
impl FileIo for OpenBlockFile {
fn check_seekable(&self) -> Result<()> {
Ok(())
}
fn is_offset_aware(&self) -> bool {
true
}
}
pub(super) fn lookup(id: DeviceId) -> Option<Arc<dyn Device>> {
let block_device = aster_block::lookup(id)?;
let mut registry = DEVICE_REGISTRY.lock();
let block_device_file = registry
.entry(id.to_raw())
.or_insert_with(move || Arc::new(BlockFile::new(block_device)))
.clone();
Some(block_device_file)
}
// TODO: Merge the two mapping tables, one is here and the other is in the block component.
// Maintaining two mapping tables is undesirable due to duplication and (potential) inconsistency.
static DEVICE_REGISTRY: Mutex<BTreeMap<u32, Arc<dyn Device>>> = Mutex::new(BTreeMap::new());