// SPDX-License-Identifier: MPL-2.0 use aster_block::{BlockDevice, SECTOR_SIZE}; use aster_virtio::device::block::device::BlockDevice as VirtIoBlockDevice; use device_id::DeviceId; use ostd::mm::VmIo; use crate::{ events::IoEvents, fs::{ device::{Device, DeviceType, add_node}, inode_handle::FileIo, path::PathResolver, utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, thread::kernel_thread::ThreadOptions, util::ioctl::{RawIoctl, dispatch_ioctl}, }; 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::().unwrap(); loop { virtio_block_device.handle_requests(); } }; ThreadOptions::new(task_fn).spawn(); } } pub(super) fn init_in_first_process(path_resolver: &PathResolver) -> Result<()> { for device in aster_block::collect_all() { let device = Arc::new(BlockFile::new(device)); if let Some(devtmpfs_path) = device.devtmpfs_path() { let dev_id = device.id().as_encoded_u64(); add_node(DeviceType::Block, dev_id, &devtmpfs_path, path_resolver)?; } } Ok(()) } mod ioctl_defs { use crate::util::ioctl::{OutData, ioc}; // Reference: pub(super) type BlkGetSize64 = ioc!(BLKGETSIZE64, 0x12, 114, OutData); } /// Represents a block device inode in the filesystem. // // TODO: This type wraps an `Arc` 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); impl BlockFile { fn new(device: Arc) -> 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 { Some(self.0.name().into()) } fn open(&self) -> Result> { Ok(Box::new(OpenBlockFile(self.0.clone()))) } } /// Represents an opened block device file ready for I/O operations. // // TODO: This type wraps an `Arc` 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); impl InodeIo for OpenBlockFile { fn read_at( &self, offset: usize, writer: &mut VmWriter, _status_flags: StatusFlags, ) -> Result { 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 { 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 } fn ioctl(&self, raw_ioctl: RawIoctl) -> Result { use ioctl_defs::*; dispatch_ioctl!(match raw_ioctl { cmd @ BlkGetSize64 => { let size = (self.0.metadata().nr_sectors * SECTOR_SIZE) as u64; cmd.write(&size)?; Ok(0) } _ => return_errno_with_message!( Errno::ENOTTY, "the ioctl command is not supported by block devices" ), }) } } pub(super) fn lookup(id: DeviceId) -> Option> { 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>> = Mutex::new(BTreeMap::new());