1use std::cell::RefCell;
6use std::collections::BTreeMap;
7use std::collections::BTreeSet;
8use std::io;
9use std::io::Write;
10use std::mem::size_of;
11#[cfg(windows)]
12use std::num::NonZeroU32;
13use std::rc::Rc;
14use std::result;
15use std::sync::atomic::AtomicU64;
16use std::sync::atomic::Ordering;
17use std::sync::Arc;
18use std::time::Duration;
19
20use anyhow::Context;
21use base::debug;
22use base::error;
23use base::info;
24use base::warn;
25use base::AsRawDescriptor;
26use base::Error as SysError;
27use base::Event;
28use base::RawDescriptor;
29use base::Result as SysResult;
30use base::Timer;
31use base::Tube;
32use base::TubeError;
33use base::WorkerThread;
34use cros_async::sync::RwLock as AsyncRwLock;
35use cros_async::AsyncError;
36use cros_async::AsyncTube;
37use cros_async::EventAsync;
38use cros_async::Executor;
39use cros_async::ExecutorKind;
40use cros_async::IoOptions;
41use cros_async::TimerAsync;
42use data_model::Le16;
43use data_model::Le32;
44use data_model::Le64;
45use disk::AsyncDisk;
46use disk::DiskFile;
47use futures::channel::mpsc;
48use futures::channel::oneshot;
49use futures::pin_mut;
50use futures::stream::FuturesUnordered;
51use futures::stream::StreamExt;
52use futures::FutureExt;
53use remain::sorted;
54use snapshot::AnySnapshot;
55use thiserror::Error as ThisError;
56use virtio_sys::virtio_config::VIRTIO_F_RING_PACKED;
57use vm_control::DiskControlCommand;
58use vm_control::DiskControlResult;
59use vm_memory::GuestMemory;
60use zerocopy::IntoBytes;
61
62use crate::virtio::async_utils;
63use crate::virtio::block::sys::*;
64use crate::virtio::block::DiskOption;
65use crate::virtio::copy_config;
66use crate::virtio::device_constants::block::virtio_blk_config;
67use crate::virtio::device_constants::block::virtio_blk_discard_write_zeroes;
68use crate::virtio::device_constants::block::virtio_blk_req_header;
69use crate::virtio::device_constants::block::VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP;
70use crate::virtio::device_constants::block::VIRTIO_BLK_F_BLK_SIZE;
71use crate::virtio::device_constants::block::VIRTIO_BLK_F_DISCARD;
72use crate::virtio::device_constants::block::VIRTIO_BLK_F_FLUSH;
73use crate::virtio::device_constants::block::VIRTIO_BLK_F_MQ;
74use crate::virtio::device_constants::block::VIRTIO_BLK_F_RO;
75use crate::virtio::device_constants::block::VIRTIO_BLK_F_SEG_MAX;
76use crate::virtio::device_constants::block::VIRTIO_BLK_F_WRITE_ZEROES;
77use crate::virtio::device_constants::block::VIRTIO_BLK_S_IOERR;
78use crate::virtio::device_constants::block::VIRTIO_BLK_S_OK;
79use crate::virtio::device_constants::block::VIRTIO_BLK_S_UNSUPP;
80use crate::virtio::device_constants::block::VIRTIO_BLK_T_DISCARD;
81use crate::virtio::device_constants::block::VIRTIO_BLK_T_FLUSH;
82use crate::virtio::device_constants::block::VIRTIO_BLK_T_GET_ID;
83use crate::virtio::device_constants::block::VIRTIO_BLK_T_IN;
84use crate::virtio::device_constants::block::VIRTIO_BLK_T_OUT;
85use crate::virtio::device_constants::block::VIRTIO_BLK_T_WRITE_ZEROES;
86use crate::virtio::DescriptorChain;
87use crate::virtio::DeviceType;
88use crate::virtio::Interrupt;
89use crate::virtio::Queue;
90use crate::virtio::Reader;
91use crate::virtio::VirtioDevice;
92use crate::virtio::Writer;
93use crate::PciAddress;
94
95const DEFAULT_QUEUE_SIZE: u16 = 256;
96const DEFAULT_NUM_QUEUES: u16 = 16;
97
98const SECTOR_SHIFT: u8 = 9;
99const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
100
101const MAX_DISCARD_SECTORS: u32 = u32::MAX;
102const MAX_WRITE_ZEROES_SECTORS: u32 = u32::MAX;
103const MAX_DISCARD_SEG: u32 = 32;
105const MAX_WRITE_ZEROES_SEG: u32 = 32;
106const DISCARD_SECTOR_ALIGNMENT: u32 = 128;
109
110#[sorted]
111#[derive(ThisError, Debug)]
112enum ExecuteError {
113 #[error("failed to copy ID string: {0}")]
114 CopyId(io::Error),
115 #[error("failed to perform discard or write zeroes; sector={sector} num_sectors={num_sectors} flags={flags}; {ioerr:?}")]
116 DiscardWriteZeroes {
117 ioerr: Option<disk::Error>,
118 sector: u64,
119 num_sectors: u32,
120 flags: u32,
121 },
122 #[error("failed to flush: {0}")]
123 Flush(disk::Error),
124 #[error("not enough space in descriptor chain to write status")]
125 MissingStatus,
126 #[error("out of range")]
127 OutOfRange,
128 #[error("failed to read message: {0}")]
129 Read(io::Error),
130 #[error("io error reading {length} bytes from sector {sector}: {desc_error}")]
131 ReadIo {
132 length: usize,
133 sector: u64,
134 desc_error: disk::Error,
135 },
136 #[error("read only; request_type={request_type}")]
137 ReadOnly { request_type: u32 },
138 #[error("failed to recieve command message: {0}")]
139 ReceivingCommand(TubeError),
140 #[error("failed to send command response: {0}")]
141 SendingResponse(TubeError),
142 #[error("couldn't reset the timer: {0}")]
143 TimerReset(base::Error),
144 #[error("too many segments: {0} > {0}")]
145 TooManySegments(usize, usize),
146 #[error("unsupported ({0})")]
147 Unsupported(u32),
148 #[error("io error writing {length} bytes from sector {sector}: {desc_error}")]
149 WriteIo {
150 length: usize,
151 sector: u64,
152 desc_error: disk::Error,
153 },
154 #[error("failed to write request status: {0}")]
155 WriteStatus(io::Error),
156}
157
158enum LogLevel {
159 Debug,
160 Error,
161}
162
163impl ExecuteError {
164 fn status(&self) -> u8 {
165 match self {
166 ExecuteError::CopyId(_) => VIRTIO_BLK_S_IOERR,
167 ExecuteError::DiscardWriteZeroes { .. } => VIRTIO_BLK_S_IOERR,
168 ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
169 ExecuteError::MissingStatus => VIRTIO_BLK_S_IOERR,
170 ExecuteError::OutOfRange => VIRTIO_BLK_S_IOERR,
171 ExecuteError::Read(_) => VIRTIO_BLK_S_IOERR,
172 ExecuteError::ReadIo { .. } => VIRTIO_BLK_S_IOERR,
173 ExecuteError::ReadOnly { .. } => VIRTIO_BLK_S_IOERR,
174 ExecuteError::ReceivingCommand(_) => VIRTIO_BLK_S_IOERR,
175 ExecuteError::SendingResponse(_) => VIRTIO_BLK_S_IOERR,
176 ExecuteError::TimerReset(_) => VIRTIO_BLK_S_IOERR,
177 ExecuteError::TooManySegments(_, _) => VIRTIO_BLK_S_IOERR,
178 ExecuteError::WriteIo { .. } => VIRTIO_BLK_S_IOERR,
179 ExecuteError::WriteStatus(_) => VIRTIO_BLK_S_IOERR,
180 ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
181 }
182 }
183
184 fn log_level(&self) -> LogLevel {
185 match self {
186 ExecuteError::ReadIo { .. }
188 | ExecuteError::WriteIo { .. }
189 | ExecuteError::Flush { .. }
190 | ExecuteError::DiscardWriteZeroes { .. } => LogLevel::Debug,
191 _ => LogLevel::Error,
193 }
194 }
195}
196
197#[sorted]
200#[derive(ThisError, Debug)]
201enum ControlError {
202 #[error("failed to fdatasync the disk: {0}")]
203 FdatasyncDisk(disk::Error),
204 #[error("couldn't get a value from a timer for flushing: {0}")]
205 FlushTimer(AsyncError),
206}
207
208const ID_LEN: usize = 20;
210
211type BlockId = [u8; ID_LEN];
215
216struct DiskState {
218 disk_image: Box<dyn AsyncDisk>,
219 read_only: bool,
220 sparse: bool,
221 id: BlockId,
222 dontcache_read: bool,
223 dontcache_write: bool,
224 worker_shared_state: Arc<AsyncRwLock<WorkerSharedState>>,
227}
228
229struct WorkerSharedState {
231 disk_size: Arc<AtomicU64>,
232}
233
234async fn process_one_request(
235 avail_desc: &mut DescriptorChain,
236 disk_state: &AsyncRwLock<DiskState>,
237 flush_timer: &RefCell<TimerAsync<Timer>>,
238 flush_timer_armed: &RefCell<bool>,
239) -> result::Result<usize, ExecuteError> {
240 let reader = &mut avail_desc.reader;
241 let writer = &mut avail_desc.writer;
242
243 let available_bytes = writer.available_bytes();
247 let status_offset = available_bytes
248 .checked_sub(1)
249 .ok_or(ExecuteError::MissingStatus)?;
250 let mut status_writer = writer.split_at(status_offset);
251
252 let status = match BlockAsync::execute_request(
253 reader,
254 writer,
255 disk_state,
256 flush_timer,
257 flush_timer_armed,
258 )
259 .await
260 {
261 Ok(()) => VIRTIO_BLK_S_OK,
262 Err(e) => {
263 match e.log_level() {
264 LogLevel::Debug => debug!("failed executing disk request: {:#}", e),
265 LogLevel::Error => error!("failed executing disk request: {:#}", e),
266 }
267 e.status()
268 }
269 };
270
271 status_writer
272 .write_all(&[status])
273 .map_err(ExecuteError::WriteStatus)?;
274 Ok(available_bytes)
275}
276
277async fn process_one_chain(
279 queue: &RefCell<Queue>,
280 mut avail_desc: DescriptorChain,
281 disk_state: &AsyncRwLock<DiskState>,
282 flush_timer: &RefCell<TimerAsync<Timer>>,
283 flush_timer_armed: &RefCell<bool>,
284) {
285 let len = match process_one_request(&mut avail_desc, disk_state, flush_timer, flush_timer_armed)
286 .await
287 {
288 Ok(len) => len,
289 Err(e) => {
290 error!("block: failed to handle request: {:#}", e);
291 0
292 }
293 };
294
295 let mut queue = queue.borrow_mut();
296 queue.add_used_with_bytes_written(avail_desc, len as u32);
297 queue.trigger_interrupt();
298}
299
300async fn handle_queue(
304 disk_state: Rc<AsyncRwLock<DiskState>>,
305 queue: Queue,
306 evt: EventAsync,
307 flush_timer: Rc<RefCell<TimerAsync<Timer>>>,
308 flush_timer_armed: Rc<RefCell<bool>>,
309 mut stop_rx: oneshot::Receiver<()>,
310) -> Queue {
311 let queue = RefCell::new(queue);
312 let mut background_tasks = FuturesUnordered::new();
313 let evt_future = evt.next_val().fuse();
314 pin_mut!(evt_future);
315 loop {
316 futures::select! {
323 _ = background_tasks.next() => continue,
324 res = evt_future => {
325 evt_future.set(evt.next_val().fuse());
326 if let Err(e) = res {
327 error!("Failed to read the next queue event: {:#}", e);
328 continue;
329 }
330 }
331 _ = stop_rx => {
332 background_tasks.collect::<()>().await;
335 return queue.into_inner();
336 }
337 };
338 while let Some(descriptor_chain) = queue.borrow_mut().pop() {
339 background_tasks.push(process_one_chain(
340 &queue,
341 descriptor_chain,
342 &disk_state,
343 &flush_timer,
344 &flush_timer_armed,
345 ));
346 }
347 }
348}
349
350async fn handle_command_tube(
351 command_tube: &Option<AsyncTube>,
352 interrupt: &RefCell<Option<Interrupt>>,
353 disk_state: Rc<AsyncRwLock<DiskState>>,
354) -> Result<(), ExecuteError> {
355 let command_tube = match command_tube {
356 Some(c) => c,
357 None => {
358 futures::future::pending::<()>().await;
359 return Ok(());
360 }
361 };
362 loop {
363 match command_tube.next().await {
364 Ok(command) => {
365 let resp = match command {
366 DiskControlCommand::Resize { new_size } => resize(&disk_state, new_size).await,
367 };
368
369 let resp_clone = resp.clone();
370 command_tube
371 .send(resp_clone)
372 .await
373 .map_err(ExecuteError::SendingResponse)?;
374 if let DiskControlResult::Ok = resp {
375 if let Some(interrupt) = &*interrupt.borrow() {
376 interrupt.signal_config_changed();
377 }
378 }
379 }
380 Err(e) => return Err(ExecuteError::ReceivingCommand(e)),
381 }
382 }
383}
384
385async fn resize(disk_state: &AsyncRwLock<DiskState>, new_size: u64) -> DiskControlResult {
386 let disk_state = disk_state.lock().await;
389 let worker_shared_state = Arc::clone(&disk_state.worker_shared_state);
391 let worker_shared_state = worker_shared_state.lock().await;
392
393 if disk_state.read_only {
394 error!("Attempted to resize read-only block device");
395 return DiskControlResult::Err(SysError::new(libc::EROFS));
396 }
397
398 info!("Resizing block device to {} bytes", new_size);
399
400 if let Err(e) = disk_state.disk_image.set_len(new_size) {
401 error!("Resizing disk failed! {:#}", e);
402 return DiskControlResult::Err(SysError::new(libc::EIO));
403 }
404
405 if !disk_state.sparse {
407 if let Err(e) = disk_state.disk_image.allocate(0, new_size) {
408 error!("Allocating disk space after resize failed! {:#}", e);
409 return DiskControlResult::Err(SysError::new(libc::EIO));
410 }
411 }
412
413 if let Ok(new_disk_size) = disk_state.disk_image.get_len() {
414 worker_shared_state
415 .disk_size
416 .store(new_disk_size, Ordering::Release);
417 }
418 DiskControlResult::Ok
419}
420
421async fn flush_disk(
423 disk_state: Rc<AsyncRwLock<DiskState>>,
424 timer: TimerAsync<Timer>,
425 armed: Rc<RefCell<bool>>,
426) -> Result<(), ControlError> {
427 loop {
428 timer.wait().await.map_err(ControlError::FlushTimer)?;
429 if !*armed.borrow() {
430 continue;
431 }
432
433 *armed.borrow_mut() = false;
436
437 disk_state
438 .read_lock()
439 .await
440 .disk_image
441 .fdatasync()
442 .await
443 .map_err(ControlError::FdatasyncDisk)?;
444 }
445}
446
447enum WorkerCmd {
448 StartQueue {
449 index: usize,
450 queue: Queue,
451 },
452 StopQueue {
453 index: usize,
454 response_tx: oneshot::Sender<Option<Queue>>,
457 },
458 AbortQueues {
461 response_tx: oneshot::Sender<()>,
463 },
464}
465
466async fn run_worker(
473 ex: &Executor,
474 disk_state: &Rc<AsyncRwLock<DiskState>>,
475 control_tube: &Option<AsyncTube>,
476 mut worker_rx: mpsc::UnboundedReceiver<WorkerCmd>,
477 kill_evt: Event,
478) -> anyhow::Result<()> {
479 let timer = Timer::new().expect("Failed to create a timer");
481 let flush_timer_armed = Rc::new(RefCell::new(false));
482
483 let control_interrupt = RefCell::new(None);
485 let control = handle_command_tube(control_tube, &control_interrupt, disk_state.clone()).fuse();
486 pin_mut!(control);
487
488 let flush_timer = Rc::new(RefCell::new(
490 TimerAsync::new(
491 timer.try_clone().expect("Failed to clone flush_timer"),
493 ex,
494 )
495 .expect("Failed to create an async timer"),
496 ));
497
498 let flush_timer2 = TimerAsync::new(timer, ex).expect("Failed to create an async timer");
500 let disk_flush = flush_disk(disk_state.clone(), flush_timer2, flush_timer_armed.clone()).fuse();
501 pin_mut!(disk_flush);
502
503 let kill = async_utils::await_and_exit(ex, kill_evt).fuse();
505 pin_mut!(kill);
506
507 let mut queue_handlers = FuturesUnordered::new();
509 let mut queue_handler_stop_fns = std::collections::BTreeMap::new();
511
512 loop {
513 futures::select! {
514 _ = queue_handlers.next() => continue,
515 r = disk_flush => return r.context("failed to flush a disk"),
516 r = control => return r.context("failed to handle a control request"),
517 r = kill => return r.context("failed to wait on the kill event"),
518 worker_cmd = worker_rx.next() => {
519 match worker_cmd {
520 None => anyhow::bail!("worker control channel unexpectedly closed"),
521 Some(WorkerCmd::StartQueue{index, queue}) => {
522 if control_interrupt.borrow().is_none() {
523 *control_interrupt.borrow_mut() = Some(queue.interrupt().clone());
524 }
525
526 let (tx, rx) = oneshot::channel();
527 let kick_evt = queue.event().try_clone().expect("Failed to clone queue event");
528 let (handle_queue_future, remote_handle) = handle_queue(
529 Rc::clone(disk_state),
530 queue,
531 EventAsync::new(kick_evt, ex).expect("Failed to create async event for queue"),
532 Rc::clone(&flush_timer),
533 Rc::clone(&flush_timer_armed),
534 rx,
535 ).remote_handle();
536 let old_stop_fn = queue_handler_stop_fns.insert(index, move || {
537 tx.send(()).unwrap_or_else(|_| panic!("queue handler channel closed early"));
539 remote_handle
541 });
542
543 if let Some(stop_fn) = old_stop_fn {
546 warn!("Starting new queue handler without stopping old handler");
547 let mut fut = stop_fn().fuse();
550 loop {
551 futures::select! {
552 _ = queue_handlers.next() => continue,
553 _queue = fut => break,
554 }
555 }
556 }
557
558 queue_handlers.push(handle_queue_future);
559 }
560 Some(WorkerCmd::StopQueue{index, response_tx}) => {
561 match queue_handler_stop_fns.remove(&index) {
562 Some(stop_fn) => {
563 let mut fut = stop_fn().fuse();
571 let queue = loop {
572 futures::select! {
573 _ = queue_handlers.next() => continue,
574 queue = fut => break queue,
575 }
576 };
577
578 if queue_handlers.is_empty() {
582 *control_interrupt.borrow_mut() = None;
583 }
584
585 let _ = response_tx.send(Some(queue));
586 }
587 None => { let _ = response_tx.send(None); },
588 }
589
590 }
591 Some(WorkerCmd::AbortQueues{response_tx}) => {
592 queue_handlers.clear();
593 queue_handler_stop_fns.clear();
594
595 *control_interrupt.borrow_mut() = None;
596
597 let _ = response_tx.send(());
598 }
599 }
600 }
601 };
602 }
603}
604
605pub struct BlockAsync {
607 boot_index: Option<usize>,
610 disk_image: Option<Box<dyn DiskFile>>,
612 disk_size: Arc<AtomicU64>,
613 avail_features: u64,
614 read_only: bool,
615 sparse: bool,
616 seg_max: u32,
617 block_size: u32,
618 id: BlockId,
619 control_tube: Option<Tube>,
620 queue_sizes: Vec<u16>,
621 pub(super) executor_kind: ExecutorKind,
622 worker_threads: BTreeMap<usize, (WorkerThread<()>, mpsc::UnboundedSender<WorkerCmd>)>,
630 shared_state: Arc<AsyncRwLock<WorkerSharedState>>,
631 worker_per_queue: bool,
633 activated_queues: BTreeSet<usize>,
637 #[cfg(windows)]
638 pub(super) io_concurrency: u32,
639 pci_address: Option<PciAddress>,
640 dontcache_read: bool,
641 dontcache_write: bool,
642}
643
644impl BlockAsync {
645 pub fn new(
647 base_features: u64,
648 disk_image: Box<dyn DiskFile>,
649 disk_option: &DiskOption,
650 control_tube: Option<Tube>,
651 queue_size: Option<u16>,
652 num_queues: Option<u16>,
653 ) -> SysResult<BlockAsync> {
654 let read_only = disk_option.read_only;
655 let sparse = disk_option.sparse;
656 let block_size = disk_option.block_size;
657 let packed_queue = disk_option.packed_queue;
658 let id = disk_option.id.unwrap_or_default();
659 let mut worker_per_queue = disk_option.multiple_workers;
660 if worker_per_queue && disk_image.try_clone().is_err() {
662 base::warn!("multiple workers requested, but not supported by disk image type");
663 worker_per_queue = false;
664 }
665 let executor_kind = disk_option.async_executor.unwrap_or_default();
666 let boot_index = disk_option.bootindex;
667 #[cfg(windows)]
668 let io_concurrency = disk_option.io_concurrency.get();
669
670 if block_size % SECTOR_SIZE as u32 != 0 {
671 error!(
672 "Block size {} is not a multiple of {}.",
673 block_size, SECTOR_SIZE,
674 );
675 return Err(SysError::new(libc::EINVAL));
676 }
677 let disk_size = disk_image.get_len()?;
678 if disk_size % block_size as u64 != 0 {
679 warn!(
680 "Disk size {} is not a multiple of block size {}; \
681 the remainder will not be visible to the guest.",
682 disk_size, block_size,
683 );
684 }
685 let num_queues = num_queues.unwrap_or(DEFAULT_NUM_QUEUES);
686 let multi_queue = match num_queues {
687 0 => panic!("Number of queues cannot be zero for a block device"),
688 1 => false,
689 _ => true,
690 };
691 let q_size = queue_size.unwrap_or(DEFAULT_QUEUE_SIZE);
692 if !q_size.is_power_of_two() {
693 error!("queue size {} is not a power of 2.", q_size);
694 return Err(SysError::new(libc::EINVAL));
695 }
696 let queue_sizes = vec![q_size; num_queues as usize];
697
698 let avail_features =
699 Self::build_avail_features(base_features, read_only, sparse, multi_queue, packed_queue);
700
701 let seg_max = get_seg_max(q_size);
702
703 let disk_size = Arc::new(AtomicU64::new(disk_size));
704 let shared_state = Arc::new(AsyncRwLock::new(WorkerSharedState {
705 disk_size: disk_size.clone(),
706 }));
707
708 let mut dontcache_read = disk_option.dontcache;
709 let mut dontcache_write = disk_option.dontcache;
710 if dontcache_write {
711 let descriptors = disk_image.as_raw_descriptors();
712 if descriptors.is_empty()
713 || !descriptors
714 .iter()
715 .all(|&fd| check_dontcache_support(fd, true ))
716 {
717 base::info!(
718 "dontcache requested, but not supported for writes by backing filesystem; falling
719 back to cached I/O"
720 );
721 dontcache_write = false;
722 }
723 }
724 if dontcache_read {
725 let descriptors = disk_image.as_raw_descriptors();
726 if descriptors.is_empty()
727 || !descriptors
728 .iter()
729 .all(|&fd| check_dontcache_support(fd, false ))
730 {
731 base::info!(
732 "dontcache requested, but not supported for reads by backing filesystem; falling
733 back to cached I/O"
734 );
735 dontcache_read = false;
736 }
737 }
738
739 Ok(BlockAsync {
740 disk_image: Some(disk_image),
741 disk_size,
742 avail_features,
743 read_only,
744 sparse,
745 seg_max,
746 block_size,
747 id,
748 queue_sizes,
749 worker_threads: BTreeMap::new(),
750 shared_state,
751 worker_per_queue,
752 control_tube,
753 executor_kind,
754 activated_queues: BTreeSet::new(),
755 boot_index,
756 #[cfg(windows)]
757 io_concurrency,
758 pci_address: disk_option.pci_address,
759 dontcache_read,
760 dontcache_write,
761 })
762 }
763
764 fn build_avail_features(
766 base_features: u64,
767 read_only: bool,
768 sparse: bool,
769 multi_queue: bool,
770 packed_queue: bool,
771 ) -> u64 {
772 let mut avail_features = base_features;
773 if read_only {
774 avail_features |= 1 << VIRTIO_BLK_F_RO;
775 } else {
776 if sparse {
777 avail_features |= 1 << VIRTIO_BLK_F_DISCARD;
778 }
779 avail_features |= 1 << VIRTIO_BLK_F_FLUSH;
780 avail_features |= 1 << VIRTIO_BLK_F_WRITE_ZEROES;
781 }
782 avail_features |= 1 << VIRTIO_BLK_F_SEG_MAX;
783 avail_features |= 1 << VIRTIO_BLK_F_BLK_SIZE;
784 if multi_queue {
785 avail_features |= 1 << VIRTIO_BLK_F_MQ;
786 }
787 if packed_queue {
788 avail_features |= 1 << VIRTIO_F_RING_PACKED;
789 }
790 avail_features
791 }
792
793 async fn execute_request(
798 reader: &mut Reader,
799 writer: &mut Writer,
800 disk_state: &AsyncRwLock<DiskState>,
801 flush_timer: &RefCell<TimerAsync<Timer>>,
802 flush_timer_armed: &RefCell<bool>,
803 ) -> result::Result<(), ExecuteError> {
804 let disk_state = disk_state.read_lock().await;
806 let worker_shared_state = disk_state.worker_shared_state.read_lock().await;
808
809 let req_header: virtio_blk_req_header = reader.read_obj().map_err(ExecuteError::Read)?;
810
811 let req_type = req_header.req_type.to_native();
812 let sector = req_header.sector.to_native();
813
814 if disk_state.read_only && req_type != VIRTIO_BLK_T_IN && req_type != VIRTIO_BLK_T_GET_ID {
815 return Err(ExecuteError::ReadOnly {
816 request_type: req_type,
817 });
818 }
819
820 fn check_range(
823 io_start: u64,
824 io_length: u64,
825 disk_size: u64,
826 ) -> result::Result<(), ExecuteError> {
827 let io_end = io_start
828 .checked_add(io_length)
829 .ok_or(ExecuteError::OutOfRange)?;
830 if io_end > disk_size {
831 Err(ExecuteError::OutOfRange)
832 } else {
833 Ok(())
834 }
835 }
836
837 let disk_size = worker_shared_state.disk_size.load(Ordering::Relaxed);
838 match req_type {
839 VIRTIO_BLK_T_IN => {
840 let data_len = writer.available_bytes();
841 if data_len == 0 {
842 return Ok(());
843 }
844 let offset = sector
845 .checked_shl(u32::from(SECTOR_SHIFT))
846 .ok_or(ExecuteError::OutOfRange)?;
847 check_range(offset, data_len as u64, disk_size)?;
848 let disk_image = &disk_state.disk_image;
849 writer
850 .write_all_from_at_fut(
851 &**disk_image,
852 data_len,
853 offset,
854 IoOptions {
855 dontcache: disk_state.dontcache_read,
856 },
857 )
858 .await
859 .map_err(|desc_error| ExecuteError::ReadIo {
860 length: data_len,
861 sector,
862 desc_error,
863 })?;
864 }
865 VIRTIO_BLK_T_OUT => {
866 let data_len = reader.available_bytes();
867 if data_len == 0 {
868 return Ok(());
869 }
870 let offset = sector
871 .checked_shl(u32::from(SECTOR_SHIFT))
872 .ok_or(ExecuteError::OutOfRange)?;
873 check_range(offset, data_len as u64, disk_size)?;
874 let disk_image = &disk_state.disk_image;
875 reader
876 .read_exact_to_at_fut(
877 &**disk_image,
878 data_len,
879 offset,
880 IoOptions {
881 dontcache: disk_state.dontcache_write,
882 },
883 )
884 .await
885 .map_err(|desc_error| ExecuteError::WriteIo {
886 length: data_len,
887 sector,
888 desc_error,
889 })?;
890
891 if !*flush_timer_armed.borrow() {
892 *flush_timer_armed.borrow_mut() = true;
893
894 let flush_delay = Duration::from_secs(60);
895 flush_timer
896 .borrow_mut()
897 .reset_oneshot(flush_delay)
898 .map_err(ExecuteError::TimerReset)?;
899 }
900 }
901 VIRTIO_BLK_T_DISCARD | VIRTIO_BLK_T_WRITE_ZEROES => {
902 if req_type == VIRTIO_BLK_T_DISCARD && !disk_state.sparse {
903 return Ok(());
905 }
906
907 let seg_count =
908 reader.available_bytes() / size_of::<virtio_blk_discard_write_zeroes>();
909 let seg_max = if req_type == VIRTIO_BLK_T_DISCARD {
910 MAX_DISCARD_SEG as usize
911 } else {
912 MAX_WRITE_ZEROES_SEG as usize
913 };
914 if seg_count > seg_max {
915 return Err(ExecuteError::TooManySegments(seg_count, seg_max));
916 }
917
918 while reader.available_bytes() >= size_of::<virtio_blk_discard_write_zeroes>() {
919 let seg: virtio_blk_discard_write_zeroes =
920 reader.read_obj().map_err(ExecuteError::Read)?;
921
922 let sector = seg.sector.to_native();
923 let num_sectors = seg.num_sectors.to_native();
924 let flags = seg.flags.to_native();
925
926 let valid_flags = if req_type == VIRTIO_BLK_T_WRITE_ZEROES {
927 VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP
928 } else {
929 0
930 };
931
932 if (flags & !valid_flags) != 0 {
933 return Err(ExecuteError::DiscardWriteZeroes {
934 ioerr: None,
935 sector,
936 num_sectors,
937 flags,
938 });
939 }
940
941 let offset = sector
942 .checked_shl(u32::from(SECTOR_SHIFT))
943 .ok_or(ExecuteError::OutOfRange)?;
944 let length = u64::from(num_sectors)
945 .checked_shl(u32::from(SECTOR_SHIFT))
946 .ok_or(ExecuteError::OutOfRange)?;
947 check_range(offset, length, disk_size)?;
948
949 if req_type == VIRTIO_BLK_T_DISCARD {
950 let _ = disk_state.disk_image.punch_hole(offset, length).await;
953 } else {
954 disk_state
955 .disk_image
956 .write_zeroes_at(offset, length)
957 .await
958 .map_err(|e| ExecuteError::DiscardWriteZeroes {
959 ioerr: Some(e),
960 sector,
961 num_sectors,
962 flags,
963 })?;
964 }
965 }
966 }
967 VIRTIO_BLK_T_FLUSH => {
968 disk_state
969 .disk_image
970 .fdatasync()
971 .await
972 .map_err(ExecuteError::Flush)?;
973
974 if *flush_timer_armed.borrow() {
975 flush_timer
976 .borrow_mut()
977 .clear()
978 .map_err(ExecuteError::TimerReset)?;
979 *flush_timer_armed.borrow_mut() = false;
980 }
981 }
982 VIRTIO_BLK_T_GET_ID => {
983 writer
984 .write_all(&disk_state.id)
985 .map_err(ExecuteError::CopyId)?;
986 }
987 t => return Err(ExecuteError::Unsupported(t)),
988 };
989 Ok(())
990 }
991
992 fn build_config_space(
994 disk_size: u64,
995 seg_max: u32,
996 block_size: u32,
997 num_queues: u16,
998 ) -> virtio_blk_config {
999 virtio_blk_config {
1000 capacity: Le64::from(disk_size >> SECTOR_SHIFT),
1002 seg_max: Le32::from(seg_max),
1003 blk_size: Le32::from(block_size),
1004 num_queues: Le16::from(num_queues),
1005 max_discard_sectors: Le32::from(MAX_DISCARD_SECTORS),
1006 discard_sector_alignment: Le32::from(DISCARD_SECTOR_ALIGNMENT),
1007 max_write_zeroes_sectors: Le32::from(MAX_WRITE_ZEROES_SECTORS),
1008 write_zeroes_may_unmap: 1,
1009 max_discard_seg: Le32::from(MAX_DISCARD_SEG),
1010 max_write_zeroes_seg: Le32::from(MAX_WRITE_ZEROES_SEG),
1011 ..Default::default()
1012 }
1013 }
1014
1015 #[allow(clippy::map_entry)]
1018 fn start_worker(
1019 &mut self,
1020 idx: usize,
1021 ) -> anyhow::Result<&(WorkerThread<()>, mpsc::UnboundedSender<WorkerCmd>)> {
1022 let key = if self.worker_per_queue { idx } else { 0 };
1023 if self.worker_threads.contains_key(&key) {
1024 return Ok(self.worker_threads.get(&key).unwrap());
1025 }
1026
1027 let ex = self.create_executor();
1028 let control_tube = self.control_tube.take();
1029 let dontcache_read = self.dontcache_read;
1030 let dontcache_write = self.dontcache_write;
1031 let disk_image = if self.worker_per_queue {
1032 self.disk_image
1033 .as_ref()
1034 .context("Failed to ref a disk image")?
1035 .try_clone()
1036 .context("Failed to clone a disk image")?
1037 } else {
1038 self.disk_image
1039 .take()
1040 .context("Failed to take a disk image")?
1041 };
1042 let read_only = self.read_only;
1043 let sparse = self.sparse;
1044 let id = self.id;
1045 let worker_shared_state = self.shared_state.clone();
1046
1047 let (worker_tx, worker_rx) = mpsc::unbounded();
1048 let worker_thread = WorkerThread::start("virtio_blk", move |kill_evt| {
1049 let async_control =
1050 control_tube.map(|c| AsyncTube::new(&ex, c).expect("failed to create async tube"));
1051
1052 let async_image = match disk_image.to_async_disk(&ex) {
1053 Ok(d) => d,
1054 Err(e) => panic!("Failed to create async disk {e:#}"),
1055 };
1056
1057 let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1058 disk_image: async_image,
1059 read_only,
1060 sparse,
1061 id,
1062 dontcache_read,
1063 dontcache_write,
1064 worker_shared_state,
1065 }));
1066
1067 if let Err(err_string) = ex
1068 .run_until(async {
1069 let r = run_worker(&ex, &disk_state, &async_control, worker_rx, kill_evt).await;
1070 if let Err(e) = disk_state.lock().await.disk_image.flush().await {
1072 error!("failed to flush disk image when stopping worker: {e:?}");
1073 }
1074 r
1075 })
1076 .expect("run_until failed")
1077 {
1078 error!("{:#}", err_string);
1079 }
1080 });
1081 match self.worker_threads.entry(key) {
1082 std::collections::btree_map::Entry::Occupied(_) => unreachable!(),
1083 std::collections::btree_map::Entry::Vacant(e) => {
1084 Ok(e.insert((worker_thread, worker_tx)))
1085 }
1086 }
1087 }
1088
1089 pub fn start_queue(
1090 &mut self,
1091 idx: usize,
1092 queue: Queue,
1093 _mem: GuestMemory,
1094 ) -> anyhow::Result<()> {
1095 let (_, worker_tx) = self.start_worker(idx)?;
1096 worker_tx
1097 .unbounded_send(WorkerCmd::StartQueue { index: idx, queue })
1098 .expect("worker channel closed early");
1099 self.activated_queues.insert(idx);
1100 Ok(())
1101 }
1102
1103 pub fn stop_queue(&mut self, idx: usize) -> anyhow::Result<Queue> {
1104 let (_, worker_tx) = self
1107 .worker_threads
1108 .get(if self.worker_per_queue { &idx } else { &0 })
1109 .context("worker not found")?;
1110 let (response_tx, response_rx) = oneshot::channel();
1111 worker_tx
1112 .unbounded_send(WorkerCmd::StopQueue {
1113 index: idx,
1114 response_tx,
1115 })
1116 .expect("worker channel closed early");
1117 let queue = cros_async::block_on(async {
1118 response_rx
1119 .await
1120 .expect("response_rx closed early")
1121 .context("queue not found")
1122 })?;
1123 self.activated_queues.remove(&idx);
1124 Ok(queue)
1125 }
1126}
1127
1128impl VirtioDevice for BlockAsync {
1129 fn keep_rds(&self) -> Vec<RawDescriptor> {
1130 let mut keep_rds = Vec::new();
1131
1132 if let Some(disk_image) = &self.disk_image {
1133 keep_rds.extend(disk_image.as_raw_descriptors());
1134 }
1135
1136 if let Some(control_tube) = &self.control_tube {
1137 keep_rds.push(control_tube.as_raw_descriptor());
1138 }
1139
1140 keep_rds
1141 }
1142
1143 fn features(&self) -> u64 {
1144 self.avail_features
1145 }
1146
1147 fn device_type(&self) -> DeviceType {
1148 DeviceType::Block
1149 }
1150
1151 fn queue_max_sizes(&self) -> &[u16] {
1152 &self.queue_sizes
1153 }
1154
1155 fn read_config(&self, offset: u64, data: &mut [u8]) {
1156 let config_space = {
1157 let disk_size = self.disk_size.load(Ordering::Acquire);
1158 Self::build_config_space(
1159 disk_size,
1160 self.seg_max,
1161 self.block_size,
1162 self.queue_sizes.len() as u16,
1163 )
1164 };
1165 copy_config(data, 0, config_space.as_bytes(), offset);
1166 }
1167
1168 fn activate(
1169 &mut self,
1170 mem: GuestMemory,
1171 _interrupt: Interrupt,
1172 queues: BTreeMap<usize, Queue>,
1173 ) -> anyhow::Result<()> {
1174 for (i, q) in queues {
1175 self.start_queue(i, q, mem.clone())?;
1176 }
1177 Ok(())
1178 }
1179
1180 fn reset(&mut self) -> anyhow::Result<()> {
1181 for (_, (_, worker_tx)) in self.worker_threads.iter_mut() {
1182 let (response_tx, response_rx) = oneshot::channel();
1183 worker_tx
1184 .unbounded_send(WorkerCmd::AbortQueues { response_tx })
1185 .expect("worker channel closed early");
1186 cros_async::block_on(async { response_rx.await.expect("response_rx closed early") });
1187 }
1188 self.activated_queues.clear();
1189 Ok(())
1190 }
1191
1192 fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
1193 let mut queues = BTreeMap::new();
1195 for index in self.activated_queues.clone() {
1196 queues.insert(index, self.stop_queue(index)?);
1197 }
1198 if queues.is_empty() {
1199 return Ok(None); }
1201 Ok(Some(queues))
1202 }
1203
1204 fn virtio_wake(
1205 &mut self,
1206 queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
1207 ) -> anyhow::Result<()> {
1208 if let Some((mem, _interrupt, queues)) = queues_state {
1209 for (i, q) in queues {
1210 self.start_queue(i, q, mem.clone())?
1211 }
1212 }
1213 Ok(())
1214 }
1215
1216 fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
1217 AnySnapshot::to_any(())
1220 }
1221
1222 fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
1223 let () = AnySnapshot::from_any(data)?;
1224 Ok(())
1225 }
1226
1227 fn pci_address(&self) -> Option<PciAddress> {
1228 self.pci_address
1229 }
1230
1231 fn bootorder_fw_cfg(&self, pci_slot: u8) -> Option<(Vec<u8>, usize)> {
1232 self.boot_index
1233 .map(|s| (format!("scsi@{pci_slot}/disk@0,0").as_bytes().to_vec(), s))
1234 }
1235}
1236
1237#[cfg(test)]
1238mod tests {
1239 use std::fs::File;
1240 use std::mem::size_of_val;
1241 use std::sync::atomic::AtomicU64;
1242
1243 use data_model::Le32;
1244 use data_model::Le64;
1245 use disk::SingleFileDisk;
1246 use hypervisor::ProtectionType;
1247 use tempfile::tempfile;
1248 use tempfile::TempDir;
1249 use vm_memory::GuestAddress;
1250
1251 use super::*;
1252 use crate::suspendable_virtio_tests;
1253 use crate::virtio::base_features;
1254 use crate::virtio::descriptor_utils::create_descriptor_chain;
1255 use crate::virtio::descriptor_utils::DescriptorType;
1256 use crate::virtio::QueueConfig;
1257
1258 #[test]
1259 fn read_size() {
1260 let f = tempfile().unwrap();
1261 f.set_len(0x1000).unwrap();
1262
1263 let features = base_features(ProtectionType::Unprotected);
1264 let disk_option = DiskOption::default();
1265 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1266 let mut num_sectors = [0u8; 4];
1267 b.read_config(0, &mut num_sectors);
1268 assert_eq!([0x08, 0x00, 0x00, 0x00], num_sectors);
1270 let mut msw_sectors = [0u8; 4];
1271 b.read_config(4, &mut msw_sectors);
1272 assert_eq!([0x00, 0x00, 0x00, 0x00], msw_sectors);
1274 }
1275
1276 #[test]
1277 fn read_block_size() {
1278 let f = tempfile().unwrap();
1279 f.set_len(0x1000).unwrap();
1280
1281 let features = base_features(ProtectionType::Unprotected);
1282 let disk_option = DiskOption {
1283 block_size: 4096,
1284 sparse: false,
1285 ..Default::default()
1286 };
1287 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1288 let mut blk_size = [0u8; 4];
1289 b.read_config(20, &mut blk_size);
1290 assert_eq!([0x00, 0x10, 0x00, 0x00], blk_size);
1292 }
1293
1294 #[test]
1295 fn read_features() {
1296 let tempdir = TempDir::new().unwrap();
1297 let mut path = tempdir.path().to_owned();
1298 path.push("disk_image");
1299
1300 const DEVICE_FEATURE_BITS: u64 = 0xffffff;
1303
1304 {
1306 let f = File::create(&path).unwrap();
1307 let features = base_features(ProtectionType::Unprotected);
1308 let disk_option = DiskOption::default();
1309 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1310 assert_eq!(0x7244, b.features() & DEVICE_FEATURE_BITS);
1314 }
1315
1316 {
1318 let f = File::create(&path).unwrap();
1319 let features = base_features(ProtectionType::Unprotected);
1320 let disk_option = DiskOption {
1321 sparse: false,
1322 ..Default::default()
1323 };
1324 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1325 assert_eq!(0x5244, b.features() & DEVICE_FEATURE_BITS);
1328 }
1329
1330 {
1332 let f = File::create(&path).unwrap();
1333 let features = base_features(ProtectionType::Unprotected);
1334 let disk_option = DiskOption {
1335 read_only: true,
1336 ..Default::default()
1337 };
1338 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1339 assert_eq!(0x1064, b.features() & DEVICE_FEATURE_BITS);
1342 }
1343 }
1344
1345 #[test]
1346 fn check_pci_adress_configurability() {
1347 let f = tempfile().unwrap();
1348
1349 let features = base_features(ProtectionType::Unprotected);
1350 let disk_option = DiskOption {
1351 pci_address: Some(PciAddress {
1352 bus: 0,
1353 dev: 1,
1354 func: 1,
1355 }),
1356 ..Default::default()
1357 };
1358 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1359
1360 assert_eq!(b.pci_address(), disk_option.pci_address);
1361 }
1362
1363 #[test]
1364 fn check_runtime_blk_queue_configurability() {
1365 let tempdir = TempDir::new().unwrap();
1366 let mut path = tempdir.path().to_owned();
1367 path.push("disk_image");
1368 let features = base_features(ProtectionType::Unprotected);
1369
1370 let f = File::create(&path).unwrap();
1372 let disk_option = DiskOption::default();
1373 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1374 assert_eq!(
1375 [DEFAULT_QUEUE_SIZE; DEFAULT_NUM_QUEUES as usize],
1376 b.queue_max_sizes()
1377 );
1378
1379 let f = File::create(&path).unwrap();
1381 let disk_option = DiskOption::default();
1382 let b = BlockAsync::new(
1383 features,
1384 Box::new(f),
1385 &disk_option,
1386 None,
1387 Some(128),
1388 Some(1),
1389 )
1390 .unwrap();
1391 assert_eq!([128; 1], b.queue_max_sizes());
1392 assert_eq!(0, b.features() & (1 << VIRTIO_BLK_F_MQ) as u64);
1394 }
1395
1396 #[test]
1397 fn read_last_sector() {
1398 let ex = Executor::new().expect("creating an executor failed");
1399
1400 let f = tempfile().unwrap();
1401 let disk_size = 0x1000;
1402 f.set_len(disk_size).unwrap();
1403 let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1404
1405 let mem = Rc::new(
1406 GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1407 .expect("Creating guest memory failed."),
1408 );
1409
1410 let req_hdr = virtio_blk_req_header {
1411 req_type: Le32::from(VIRTIO_BLK_T_IN),
1412 reserved: Le32::from(0),
1413 sector: Le64::from(7), };
1415 mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1416 .expect("writing req failed");
1417
1418 let mut avail_desc = create_descriptor_chain(
1419 &mem,
1420 GuestAddress(0x100), GuestAddress(0x1000), vec![
1423 (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1425 (DescriptorType::Writable, 512),
1427 (DescriptorType::Writable, 1),
1429 ],
1430 0,
1431 )
1432 .expect("create_descriptor_chain failed");
1433
1434 let timer = Timer::new().expect("Failed to create a timer");
1435 let flush_timer = Rc::new(RefCell::new(
1436 TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1437 ));
1438 let flush_timer_armed = Rc::new(RefCell::new(false));
1439
1440 let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1441 disk_image: Box::new(af),
1442 read_only: false,
1443 sparse: true,
1444 id: Default::default(),
1445 dontcache_read: false,
1446 dontcache_write: false,
1447 worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1448 disk_size: Arc::new(AtomicU64::new(disk_size)),
1449 })),
1450 }));
1451
1452 let fut = process_one_request(
1453 &mut avail_desc,
1454 &disk_state,
1455 &flush_timer,
1456 &flush_timer_armed,
1457 );
1458
1459 ex.run_until(fut)
1460 .expect("running executor failed")
1461 .expect("execute failed");
1462
1463 let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512) as u64);
1464 let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1465 assert_eq!(status, VIRTIO_BLK_S_OK);
1466 }
1467
1468 #[test]
1469 fn read_beyond_last_sector() {
1470 let f = tempfile().unwrap();
1471 let disk_size = 0x1000;
1472 f.set_len(disk_size).unwrap();
1473 let mem = Rc::new(
1474 GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1475 .expect("Creating guest memory failed."),
1476 );
1477
1478 let req_hdr = virtio_blk_req_header {
1479 req_type: Le32::from(VIRTIO_BLK_T_IN),
1480 reserved: Le32::from(0),
1481 sector: Le64::from(7), };
1483 mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1484 .expect("writing req failed");
1485
1486 let mut avail_desc = create_descriptor_chain(
1487 &mem,
1488 GuestAddress(0x100), GuestAddress(0x1000), vec![
1491 (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1493 (DescriptorType::Writable, 512 * 2),
1495 (DescriptorType::Writable, 1),
1497 ],
1498 0,
1499 )
1500 .expect("create_descriptor_chain failed");
1501
1502 let ex = Executor::new().expect("creating an executor failed");
1503
1504 let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1505 let timer = Timer::new().expect("Failed to create a timer");
1506 let flush_timer = Rc::new(RefCell::new(
1507 TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1508 ));
1509 let flush_timer_armed = Rc::new(RefCell::new(false));
1510 let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1511 disk_image: Box::new(af),
1512 read_only: false,
1513 sparse: true,
1514 id: Default::default(),
1515 dontcache_read: false,
1516 dontcache_write: false,
1517 worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1518 disk_size: Arc::new(AtomicU64::new(disk_size)),
1519 })),
1520 }));
1521
1522 let fut = process_one_request(
1523 &mut avail_desc,
1524 &disk_state,
1525 &flush_timer,
1526 &flush_timer_armed,
1527 );
1528
1529 ex.run_until(fut)
1530 .expect("running executor failed")
1531 .expect("execute failed");
1532
1533 let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512 * 2) as u64);
1534 let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1535 assert_eq!(status, VIRTIO_BLK_S_IOERR);
1536 }
1537
1538 #[test]
1539 fn get_id() {
1540 let ex = Executor::new().expect("creating an executor failed");
1541
1542 let f = tempfile().unwrap();
1543 let disk_size = 0x1000;
1544 f.set_len(disk_size).unwrap();
1545
1546 let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1547 .expect("Creating guest memory failed.");
1548
1549 let req_hdr = virtio_blk_req_header {
1550 req_type: Le32::from(VIRTIO_BLK_T_GET_ID),
1551 reserved: Le32::from(0),
1552 sector: Le64::from(0),
1553 };
1554 mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1555 .expect("writing req failed");
1556
1557 let mut avail_desc = create_descriptor_chain(
1558 &mem,
1559 GuestAddress(0x100), GuestAddress(0x1000), vec![
1562 (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1564 (DescriptorType::Writable, 20),
1566 (DescriptorType::Writable, 1),
1568 ],
1569 0,
1570 )
1571 .expect("create_descriptor_chain failed");
1572
1573 let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1574 let timer = Timer::new().expect("Failed to create a timer");
1575 let flush_timer = Rc::new(RefCell::new(
1576 TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1577 ));
1578 let flush_timer_armed = Rc::new(RefCell::new(false));
1579
1580 let id = b"a20-byteserialnumber";
1581
1582 let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1583 disk_image: Box::new(af),
1584 read_only: false,
1585 sparse: true,
1586 id: *id,
1587 dontcache_read: false,
1588 dontcache_write: false,
1589 worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1590 disk_size: Arc::new(AtomicU64::new(disk_size)),
1591 })),
1592 }));
1593
1594 let fut = process_one_request(
1595 &mut avail_desc,
1596 &disk_state,
1597 &flush_timer,
1598 &flush_timer_armed,
1599 );
1600
1601 ex.run_until(fut)
1602 .expect("running executor failed")
1603 .expect("execute failed");
1604
1605 let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512) as u64);
1606 let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1607 assert_eq!(status, VIRTIO_BLK_S_OK);
1608
1609 let id_offset = GuestAddress(0x1000 + size_of_val(&req_hdr) as u64);
1610 let returned_id = mem.read_obj_from_addr::<[u8; 20]>(id_offset).unwrap();
1611 assert_eq!(returned_id, *id);
1612 }
1613
1614 #[test]
1615 fn reset_and_reactivate_single_worker() {
1616 reset_and_reactivate(false, None);
1617 }
1618
1619 #[test]
1620 fn reset_and_reactivate_multiple_workers() {
1621 reset_and_reactivate(true, None);
1622 }
1623
1624 #[test]
1625 #[cfg(windows)]
1626 fn reset_and_reactivate_overrlapped_io() {
1627 reset_and_reactivate(
1628 false,
1629 Some(
1630 cros_async::sys::windows::ExecutorKindSys::Overlapped { concurrency: None }.into(),
1631 ),
1632 );
1633 }
1634
1635 fn reset_and_reactivate(
1636 enables_multiple_workers: bool,
1637 async_executor: Option<cros_async::ExecutorKind>,
1638 ) {
1639 let f = tempfile::NamedTempFile::new().unwrap();
1641 f.as_file().set_len(0x1000).unwrap();
1642 let path: tempfile::TempPath = f.into_temp_path();
1645
1646 let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1648 .expect("Creating guest memory failed.");
1649
1650 let (_control_tube, control_tube_device) = Tube::pair().unwrap();
1654
1655 let features = base_features(ProtectionType::Unprotected);
1657 let id = b"Block serial number\0";
1658 let disk_option = DiskOption {
1659 path: path.to_path_buf(),
1660 read_only: true,
1661 id: Some(*id),
1662 sparse: false,
1663 multiple_workers: enables_multiple_workers,
1664 async_executor,
1665 ..Default::default()
1666 };
1667 let disk_image = disk_option.open().unwrap();
1668 let mut b = BlockAsync::new(
1669 features,
1670 disk_image,
1671 &disk_option,
1672 Some(control_tube_device),
1673 None,
1674 None,
1675 )
1676 .unwrap();
1677
1678 let interrupt = Interrupt::new_for_test();
1679
1680 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1682 q0.set_ready(true);
1683 let q0 = q0
1684 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1685 .expect("QueueConfig::activate");
1686
1687 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1688 q1.set_ready(true);
1689 let q1 = q1
1690 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1691 .expect("QueueConfig::activate");
1692
1693 b.activate(mem.clone(), interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1694 .expect("activate should succeed");
1695 if !enables_multiple_workers {
1697 assert!(
1698 b.disk_image.is_none(),
1699 "BlockAsync should not have a disk image"
1700 );
1701 }
1702 assert!(
1703 b.control_tube.is_none(),
1704 "BlockAsync should not have a control tube"
1705 );
1706 assert_eq!(
1707 b.worker_threads.len(),
1708 if enables_multiple_workers { 2 } else { 1 }
1709 );
1710
1711 assert!(b.reset().is_ok(), "reset should succeed");
1713 if !enables_multiple_workers {
1714 assert!(
1715 b.disk_image.is_none(),
1716 "BlockAsync should not have a disk image"
1717 );
1718 }
1719 assert!(
1720 b.control_tube.is_none(),
1721 "BlockAsync should not have a control tube"
1722 );
1723 assert_eq!(
1724 b.worker_threads.len(),
1725 if enables_multiple_workers { 2 } else { 1 }
1726 );
1727 assert_eq!(b.id, *b"Block serial number\0");
1728
1729 let interrupt = Interrupt::new_for_test();
1731 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1732 q0.set_ready(true);
1733 let q0 = q0
1734 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1735 .expect("QueueConfig::activate");
1736
1737 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1738 q1.set_ready(true);
1739 let q1 = q1
1740 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1741 .expect("QueueConfig::activate");
1742
1743 b.activate(mem, interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1744 .expect("re-activate should succeed");
1745 }
1746
1747 #[test]
1748 fn resize_with_single_worker() {
1749 resize(false);
1750 }
1751
1752 #[test]
1753 fn resize_with_multiple_workers() {
1754 resize(true);
1756 }
1757
1758 fn resize(enables_multiple_workers: bool) {
1759 let original_size = 0x1000;
1761 let resized_size = 0x2000;
1762
1763 let f = tempfile().unwrap();
1765 f.set_len(original_size).unwrap();
1766 let disk_image: Box<dyn DiskFile> = Box::new(f);
1767 assert_eq!(disk_image.get_len().unwrap(), original_size);
1768
1769 let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1771 .expect("Creating guest memory failed.");
1772
1773 let (control_tube, control_tube_device) = Tube::pair().unwrap();
1775
1776 let features = base_features(ProtectionType::Unprotected);
1778 let disk_option = DiskOption {
1779 multiple_workers: enables_multiple_workers,
1780 ..Default::default()
1781 };
1782 let mut b = BlockAsync::new(
1783 features,
1784 disk_image.try_clone().unwrap(),
1785 &disk_option,
1786 Some(control_tube_device),
1787 None,
1788 None,
1789 )
1790 .unwrap();
1791
1792 let interrupt = Interrupt::new_for_test();
1793
1794 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1796 q0.set_ready(true);
1797 let q0 = q0
1798 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1799 .expect("QueueConfig::activate");
1800
1801 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1802 q1.set_ready(true);
1803 let q1 = q1
1804 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1805 .expect("QueueConfig::activate");
1806
1807 b.activate(mem, interrupt.clone(), BTreeMap::from([(0, q0), (1, q1)]))
1808 .expect("activate should succeed");
1809
1810 assert_eq!(
1812 b.disk_size.load(Ordering::Acquire),
1813 original_size,
1814 "disk_size should be the original size first"
1815 );
1816 let mut capacity = [0u8; 8];
1817 b.read_config(0, &mut capacity);
1818 assert_eq!(
1819 capacity,
1820 [0x8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
1822 "read_config should read the original capacity first"
1823 );
1824
1825 control_tube
1827 .send(&DiskControlCommand::Resize {
1828 new_size: resized_size,
1829 })
1830 .unwrap();
1831 assert_eq!(
1832 control_tube.recv::<DiskControlResult>().unwrap(),
1833 DiskControlResult::Ok,
1834 "resize command should succeed"
1835 );
1836 assert_eq!(
1837 b.disk_size.load(Ordering::Acquire),
1838 resized_size,
1839 "disk_size should be resized to the new size"
1840 );
1841 assert_eq!(
1842 disk_image.get_len().unwrap(),
1843 resized_size,
1844 "underlying disk image should be resized to the new size"
1845 );
1846 let mut capacity = [0u8; 8];
1847 b.read_config(0, &mut capacity);
1848 assert_eq!(
1849 capacity,
1850 [0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
1852 "read_config should read the resized capacity"
1853 );
1854 interrupt
1856 .get_interrupt_evt()
1857 .wait()
1858 .expect("interrupt should be signaled");
1859
1860 assert_eq!(
1861 interrupt.read_interrupt_status(),
1862 crate::virtio::INTERRUPT_STATUS_CONFIG_CHANGED as u8,
1863 "INTERRUPT_STATUS_CONFIG_CHANGED should be signaled"
1864 );
1865 }
1866
1867 #[test]
1868 fn run_worker_threads() {
1869 let f = tempfile().unwrap();
1871 f.set_len(0x1000).unwrap();
1872 let disk_image: Box<dyn DiskFile> = Box::new(f);
1873
1874 let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1876 .expect("Creating guest memory failed.");
1877
1878 let features = base_features(ProtectionType::Unprotected);
1880 let disk_option = DiskOption::default();
1881 let mut b = BlockAsync::new(
1882 features,
1883 disk_image.try_clone().unwrap(),
1884 &disk_option,
1885 None,
1886 None,
1887 None,
1888 )
1889 .unwrap();
1890
1891 let interrupt = Interrupt::new_for_test();
1893 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1894 q0.set_ready(true);
1895 let q0 = q0
1896 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1897 .expect("QueueConfig::activate");
1898
1899 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1900 q1.set_ready(true);
1901 let q1 = q1
1902 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1903 .expect("QueueConfig::activate");
1904
1905 b.activate(mem.clone(), interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1906 .expect("activate should succeed");
1907
1908 assert_eq!(b.worker_threads.len(), 1, "1 threads should be spawned.");
1909 drop(b);
1910
1911 let features = base_features(ProtectionType::Unprotected);
1913 let disk_option = DiskOption {
1914 read_only: true,
1915 sparse: false,
1916 multiple_workers: true,
1917 ..DiskOption::default()
1918 };
1919 let mut b = BlockAsync::new(features, disk_image, &disk_option, None, None, None).unwrap();
1920
1921 let interrupt = Interrupt::new_for_test();
1923 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1924 q0.set_ready(true);
1925 let q0 = q0
1926 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1927 .expect("QueueConfig::activate");
1928
1929 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1930 q1.set_ready(true);
1931 let q1 = q1
1932 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1933 .expect("QueueConfig::activate");
1934
1935 b.activate(mem, interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1936 .expect("activate should succeed");
1937
1938 assert_eq!(b.worker_threads.len(), 2, "2 threads should be spawned.");
1939 }
1940
1941 struct BlockContext {}
1942
1943 fn modify_device(_block_context: &mut BlockContext, b: &mut BlockAsync) {
1944 b.avail_features = !b.avail_features;
1945 }
1946
1947 fn create_device() -> (BlockContext, BlockAsync) {
1948 let f = tempfile().unwrap();
1950 f.set_len(0x1000).unwrap();
1951 let disk_image: Box<dyn DiskFile> = Box::new(f);
1952
1953 let features = base_features(ProtectionType::Unprotected);
1955 let id = b"Block serial number\0";
1956 let disk_option = DiskOption {
1957 read_only: true,
1958 id: Some(*id),
1959 sparse: false,
1960 multiple_workers: true,
1961 ..Default::default()
1962 };
1963 (
1964 BlockContext {},
1965 BlockAsync::new(
1966 features,
1967 disk_image.try_clone().unwrap(),
1968 &disk_option,
1969 None,
1970 None,
1971 None,
1972 )
1973 .unwrap(),
1974 )
1975 }
1976
1977 #[cfg(any(target_os = "android", target_os = "linux"))]
1978 suspendable_virtio_tests!(asyncblock, create_device, 2, modify_device);
1979}