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