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 = futures::future::Either::Left(std::future::ready(Ok(0))).fuse();
312 pin_mut!(evt_future);
313 loop {
314 futures::select_biased! {
323 _ = background_tasks.next() => continue,
324 _ = stop_rx => {
325 background_tasks.collect::<()>().await;
328 return queue.into_inner();
329 }
330 res = evt_future => {
331 evt_future.set(futures::future::Either::Right(evt.next_val()).fuse());
332 if let Err(e) = res {
333 error!("Failed to read the next queue event: {:#}", e);
334 continue;
335 }
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 FlushDisk {
466 response_tx: oneshot::Sender<()>,
468 },
469}
470
471async fn run_worker(
478 ex: &Executor,
479 disk_state: &Rc<AsyncRwLock<DiskState>>,
480 control_tube: &Option<AsyncTube>,
481 mut worker_rx: mpsc::UnboundedReceiver<WorkerCmd>,
482 kill_evt: Event,
483) -> anyhow::Result<()> {
484 let timer = Timer::new().expect("Failed to create a timer");
486 let flush_timer_armed = Rc::new(RefCell::new(false));
487
488 let control_interrupt = RefCell::new(None);
490 let control = handle_command_tube(control_tube, &control_interrupt, disk_state.clone()).fuse();
491 pin_mut!(control);
492
493 let flush_timer = Rc::new(RefCell::new(
495 TimerAsync::new(
496 timer.try_clone().expect("Failed to clone flush_timer"),
498 ex,
499 )
500 .expect("Failed to create an async timer"),
501 ));
502
503 let flush_timer2 = TimerAsync::new(timer, ex).expect("Failed to create an async timer");
505 let disk_flush = flush_disk(disk_state.clone(), flush_timer2, flush_timer_armed.clone()).fuse();
506 pin_mut!(disk_flush);
507
508 let kill = async_utils::await_and_exit(ex, kill_evt).fuse();
510 pin_mut!(kill);
511
512 let mut queue_handlers = FuturesUnordered::new();
514 let mut queue_handler_stop_fns = std::collections::BTreeMap::new();
516
517 loop {
518 futures::select! {
519 _ = queue_handlers.next() => continue,
520 r = disk_flush => return r.context("failed to flush a disk"),
521 r = control => return r.context("failed to handle a control request"),
522 r = kill => return r.context("failed to wait on the kill event"),
523 worker_cmd = worker_rx.next() => {
524 match worker_cmd {
525 None => anyhow::bail!("worker control channel unexpectedly closed"),
526 Some(WorkerCmd::StartQueue{index, queue}) => {
527 if control_interrupt.borrow().is_none() {
528 *control_interrupt.borrow_mut() = Some(queue.interrupt().clone());
529 }
530
531 let (tx, rx) = oneshot::channel();
532 let kick_evt = queue.event().try_clone().expect("Failed to clone queue event");
533 let (handle_queue_future, remote_handle) = handle_queue(
534 Rc::clone(disk_state),
535 queue,
536 EventAsync::new(kick_evt, ex).expect("Failed to create async event for queue"),
537 Rc::clone(&flush_timer),
538 Rc::clone(&flush_timer_armed),
539 rx,
540 ).remote_handle();
541 let old_stop_fn = queue_handler_stop_fns.insert(index, move || {
542 tx.send(()).unwrap_or_else(|_| panic!("queue handler channel closed early"));
544 remote_handle
546 });
547
548 if let Some(stop_fn) = old_stop_fn {
551 warn!("Starting new queue handler without stopping old handler");
552 let mut fut = stop_fn().fuse();
555 loop {
556 futures::select! {
557 _ = queue_handlers.next() => continue,
558 _queue = fut => break,
559 }
560 }
561 }
562
563 queue_handlers.push(handle_queue_future);
564 }
565 Some(WorkerCmd::StopQueue{index, response_tx}) => {
566 match queue_handler_stop_fns.remove(&index) {
567 Some(stop_fn) => {
568 let mut fut = stop_fn().fuse();
576 let queue = loop {
577 futures::select! {
578 _ = queue_handlers.next() => continue,
579 queue = fut => break queue,
580 }
581 };
582
583 if queue_handlers.is_empty() {
587 *control_interrupt.borrow_mut() = None;
588 }
589
590 let _ = response_tx.send(Some(queue));
591 }
592 None => { let _ = response_tx.send(None); },
593 }
594
595 }
596 Some(WorkerCmd::AbortQueues{response_tx}) => {
597 queue_handlers.clear();
598 queue_handler_stop_fns.clear();
599
600 *control_interrupt.borrow_mut() = None;
601
602 let _ = response_tx.send(());
603 }
604 Some(WorkerCmd::FlushDisk{response_tx}) => {
605 let disk = disk_state.read_lock().await;
606 if let Err(e) = disk.disk_image.flush().await {
607 error!("failed to flush disk image: {:#}", e);
608 }
609 let _ = response_tx.send(());
610 }
611 }
612 }
613 };
614 }
615}
616
617pub struct BlockAsync {
619 boot_index: Option<usize>,
622 disk_image: Option<Box<dyn DiskFile>>,
624 disk_size: Arc<AtomicU64>,
625 avail_features: u64,
626 read_only: bool,
627 sparse: bool,
628 seg_max: u32,
629 block_size: u32,
630 id: BlockId,
631 control_tube: Option<Tube>,
632 queue_sizes: Vec<u16>,
633 pub(super) executor_kind: ExecutorKind,
634 worker_threads: BTreeMap<usize, (WorkerThread<()>, mpsc::UnboundedSender<WorkerCmd>)>,
642 shared_state: Arc<AsyncRwLock<WorkerSharedState>>,
643 worker_per_queue: bool,
645 activated_queues: BTreeSet<usize>,
649 #[cfg(windows)]
650 pub(super) io_concurrency: u32,
651 pci_address: Option<PciAddress>,
652 dontcache_read: bool,
653 dontcache_write: bool,
654}
655
656impl BlockAsync {
657 pub fn new(
659 base_features: u64,
660 disk_image: Box<dyn DiskFile>,
661 disk_option: &DiskOption,
662 control_tube: Option<Tube>,
663 queue_size: Option<u16>,
664 num_queues: Option<u16>,
665 ) -> SysResult<BlockAsync> {
666 let read_only = disk_option.read_only;
667 let sparse = disk_option.sparse;
668 let block_size = disk_option.block_size;
669 let packed_queue = disk_option.packed_queue;
670 let id = disk_option.id.unwrap_or_default();
671 let mut worker_per_queue = disk_option.multiple_workers;
672 if worker_per_queue && disk_image.try_clone().is_err() {
674 base::warn!("multiple workers requested, but not supported by disk image type");
675 worker_per_queue = false;
676 }
677 let executor_kind = disk_option.async_executor.unwrap_or_default();
678 let boot_index = disk_option.bootindex;
679 #[cfg(windows)]
680 let io_concurrency = disk_option.io_concurrency.get();
681
682 if block_size % SECTOR_SIZE as u32 != 0 {
683 error!(
684 "Block size {} is not a multiple of {}.",
685 block_size, SECTOR_SIZE,
686 );
687 return Err(SysError::new(libc::EINVAL));
688 }
689 let disk_size = disk_image.get_len()?;
690 if disk_size % block_size as u64 != 0 {
691 warn!(
692 "Disk size {} is not a multiple of block size {}; \
693 the remainder will not be visible to the guest.",
694 disk_size, block_size,
695 );
696 }
697 let num_queues = num_queues.unwrap_or(DEFAULT_NUM_QUEUES);
698 let multi_queue = match num_queues {
699 0 => panic!("Number of queues cannot be zero for a block device"),
700 1 => false,
701 _ => true,
702 };
703 let q_size = queue_size.unwrap_or(DEFAULT_QUEUE_SIZE);
704 if !q_size.is_power_of_two() {
705 error!("queue size {} is not a power of 2.", q_size);
706 return Err(SysError::new(libc::EINVAL));
707 }
708 let queue_sizes = vec![q_size; num_queues as usize];
709
710 let avail_features =
711 Self::build_avail_features(base_features, read_only, sparse, multi_queue, packed_queue);
712
713 let seg_max = get_seg_max(q_size);
714
715 let disk_size = Arc::new(AtomicU64::new(disk_size));
716 let shared_state = Arc::new(AsyncRwLock::new(WorkerSharedState {
717 disk_size: disk_size.clone(),
718 }));
719
720 let mut dontcache_read = disk_option.dontcache;
721 let mut dontcache_write = disk_option.dontcache;
722 if dontcache_write {
723 let descriptors = disk_image.as_raw_descriptors();
724 if descriptors.is_empty()
725 || !descriptors
726 .iter()
727 .all(|&fd| check_dontcache_support(fd, true ))
728 {
729 base::info!(
730 "dontcache requested, but not supported for writes by backing filesystem; falling
731 back to cached I/O"
732 );
733 dontcache_write = false;
734 }
735 }
736 if dontcache_read {
737 let descriptors = disk_image.as_raw_descriptors();
738 if descriptors.is_empty()
739 || !descriptors
740 .iter()
741 .all(|&fd| check_dontcache_support(fd, false ))
742 {
743 base::info!(
744 "dontcache requested, but not supported for reads by backing filesystem; falling
745 back to cached I/O"
746 );
747 dontcache_read = false;
748 }
749 }
750
751 Ok(BlockAsync {
752 disk_image: Some(disk_image),
753 disk_size,
754 avail_features,
755 read_only,
756 sparse,
757 seg_max,
758 block_size,
759 id,
760 queue_sizes,
761 worker_threads: BTreeMap::new(),
762 shared_state,
763 worker_per_queue,
764 control_tube,
765 executor_kind,
766 activated_queues: BTreeSet::new(),
767 boot_index,
768 #[cfg(windows)]
769 io_concurrency,
770 pci_address: disk_option.pci_address,
771 dontcache_read,
772 dontcache_write,
773 })
774 }
775
776 fn build_avail_features(
778 base_features: u64,
779 read_only: bool,
780 sparse: bool,
781 multi_queue: bool,
782 packed_queue: bool,
783 ) -> u64 {
784 let mut avail_features = base_features;
785 if read_only {
786 avail_features |= 1 << VIRTIO_BLK_F_RO;
787 } else {
788 if sparse {
789 avail_features |= 1 << VIRTIO_BLK_F_DISCARD;
790 }
791 avail_features |= 1 << VIRTIO_BLK_F_FLUSH;
792 avail_features |= 1 << VIRTIO_BLK_F_WRITE_ZEROES;
793 }
794 avail_features |= 1 << VIRTIO_BLK_F_SEG_MAX;
795 avail_features |= 1 << VIRTIO_BLK_F_BLK_SIZE;
796 if multi_queue {
797 avail_features |= 1 << VIRTIO_BLK_F_MQ;
798 }
799 if packed_queue {
800 avail_features |= 1 << VIRTIO_F_RING_PACKED;
801 }
802 avail_features
803 }
804
805 async fn execute_request(
810 reader: &mut Reader,
811 writer: &mut Writer,
812 disk_state: &AsyncRwLock<DiskState>,
813 flush_timer: &RefCell<TimerAsync<Timer>>,
814 flush_timer_armed: &RefCell<bool>,
815 ) -> result::Result<(), ExecuteError> {
816 let disk_state = disk_state.read_lock().await;
818 let worker_shared_state = disk_state.worker_shared_state.read_lock().await;
820
821 let req_header: virtio_blk_req_header = reader.read_obj().map_err(ExecuteError::Read)?;
822
823 let req_type = req_header.req_type.to_native();
824 let sector = req_header.sector.to_native();
825
826 if disk_state.read_only && req_type != VIRTIO_BLK_T_IN && req_type != VIRTIO_BLK_T_GET_ID {
827 return Err(ExecuteError::ReadOnly {
828 request_type: req_type,
829 });
830 }
831
832 fn check_range(
835 io_start: u64,
836 io_length: u64,
837 disk_size: u64,
838 ) -> result::Result<(), ExecuteError> {
839 let io_end = io_start
840 .checked_add(io_length)
841 .ok_or(ExecuteError::OutOfRange)?;
842 if io_end > disk_size {
843 Err(ExecuteError::OutOfRange)
844 } else {
845 Ok(())
846 }
847 }
848
849 let disk_size = worker_shared_state.disk_size.load(Ordering::Relaxed);
850 match req_type {
851 VIRTIO_BLK_T_IN => {
852 let data_len = writer.available_bytes();
853 if data_len == 0 {
854 return Ok(());
855 }
856 let offset = sector
857 .checked_shl(u32::from(SECTOR_SHIFT))
858 .ok_or(ExecuteError::OutOfRange)?;
859 check_range(offset, data_len as u64, disk_size)?;
860 let disk_image = &disk_state.disk_image;
861 writer
862 .write_all_from_at_fut(
863 &**disk_image,
864 data_len,
865 offset,
866 IoOptions {
867 dontcache: disk_state.dontcache_read,
868 },
869 )
870 .await
871 .map_err(|desc_error| ExecuteError::ReadIo {
872 length: data_len,
873 sector,
874 desc_error,
875 })?;
876 }
877 VIRTIO_BLK_T_OUT => {
878 let data_len = reader.available_bytes();
879 if data_len == 0 {
880 return Ok(());
881 }
882 let offset = sector
883 .checked_shl(u32::from(SECTOR_SHIFT))
884 .ok_or(ExecuteError::OutOfRange)?;
885 check_range(offset, data_len as u64, disk_size)?;
886 let disk_image = &disk_state.disk_image;
887 reader
888 .read_exact_to_at_fut(
889 &**disk_image,
890 data_len,
891 offset,
892 IoOptions {
893 dontcache: disk_state.dontcache_write,
894 },
895 )
896 .await
897 .map_err(|desc_error| ExecuteError::WriteIo {
898 length: data_len,
899 sector,
900 desc_error,
901 })?;
902
903 if !*flush_timer_armed.borrow() {
904 *flush_timer_armed.borrow_mut() = true;
905
906 let flush_delay = Duration::from_secs(60);
907 flush_timer
908 .borrow_mut()
909 .reset_oneshot(flush_delay)
910 .map_err(ExecuteError::TimerReset)?;
911 }
912 }
913 VIRTIO_BLK_T_DISCARD | VIRTIO_BLK_T_WRITE_ZEROES => {
914 if req_type == VIRTIO_BLK_T_DISCARD && !disk_state.sparse {
915 return Ok(());
917 }
918
919 let seg_count =
920 reader.available_bytes() / size_of::<virtio_blk_discard_write_zeroes>();
921 let seg_max = if req_type == VIRTIO_BLK_T_DISCARD {
922 MAX_DISCARD_SEG as usize
923 } else {
924 MAX_WRITE_ZEROES_SEG as usize
925 };
926 if seg_count > seg_max {
927 return Err(ExecuteError::TooManySegments(seg_count, seg_max));
928 }
929
930 while reader.available_bytes() >= size_of::<virtio_blk_discard_write_zeroes>() {
931 let seg: virtio_blk_discard_write_zeroes =
932 reader.read_obj().map_err(ExecuteError::Read)?;
933
934 let sector = seg.sector.to_native();
935 let num_sectors = seg.num_sectors.to_native();
936 let flags = seg.flags.to_native();
937
938 let valid_flags = if req_type == VIRTIO_BLK_T_WRITE_ZEROES {
939 VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP
940 } else {
941 0
942 };
943
944 if (flags & !valid_flags) != 0 {
945 return Err(ExecuteError::DiscardWriteZeroes {
946 ioerr: None,
947 sector,
948 num_sectors,
949 flags,
950 });
951 }
952
953 let offset = sector
954 .checked_shl(u32::from(SECTOR_SHIFT))
955 .ok_or(ExecuteError::OutOfRange)?;
956 let length = u64::from(num_sectors)
957 .checked_shl(u32::from(SECTOR_SHIFT))
958 .ok_or(ExecuteError::OutOfRange)?;
959 check_range(offset, length, disk_size)?;
960
961 if req_type == VIRTIO_BLK_T_DISCARD {
962 let _ = disk_state.disk_image.punch_hole(offset, length).await;
965 } else {
966 disk_state
967 .disk_image
968 .write_zeroes_at(offset, length)
969 .await
970 .map_err(|e| ExecuteError::DiscardWriteZeroes {
971 ioerr: Some(e),
972 sector,
973 num_sectors,
974 flags,
975 })?;
976 }
977 }
978 }
979 VIRTIO_BLK_T_FLUSH => {
980 disk_state
981 .disk_image
982 .fdatasync()
983 .await
984 .map_err(ExecuteError::Flush)?;
985
986 if *flush_timer_armed.borrow() {
987 flush_timer
988 .borrow_mut()
989 .clear()
990 .map_err(ExecuteError::TimerReset)?;
991 *flush_timer_armed.borrow_mut() = false;
992 }
993 }
994 VIRTIO_BLK_T_GET_ID => {
995 writer
996 .write_all(&disk_state.id)
997 .map_err(ExecuteError::CopyId)?;
998 }
999 t => return Err(ExecuteError::Unsupported(t)),
1000 };
1001 Ok(())
1002 }
1003
1004 fn build_config_space(
1006 disk_size: u64,
1007 seg_max: u32,
1008 block_size: u32,
1009 num_queues: u16,
1010 ) -> virtio_blk_config {
1011 virtio_blk_config {
1012 capacity: Le64::from(disk_size >> SECTOR_SHIFT),
1014 seg_max: Le32::from(seg_max),
1015 blk_size: Le32::from(block_size),
1016 num_queues: Le16::from(num_queues),
1017 max_discard_sectors: Le32::from(MAX_DISCARD_SECTORS),
1018 discard_sector_alignment: Le32::from(DISCARD_SECTOR_ALIGNMENT),
1019 max_write_zeroes_sectors: Le32::from(MAX_WRITE_ZEROES_SECTORS),
1020 write_zeroes_may_unmap: 1,
1021 max_discard_seg: Le32::from(MAX_DISCARD_SEG),
1022 max_write_zeroes_seg: Le32::from(MAX_WRITE_ZEROES_SEG),
1023 ..Default::default()
1024 }
1025 }
1026
1027 #[allow(clippy::map_entry)]
1030 fn start_worker(
1031 &mut self,
1032 idx: usize,
1033 ) -> anyhow::Result<&(WorkerThread<()>, mpsc::UnboundedSender<WorkerCmd>)> {
1034 let key = if self.worker_per_queue { idx } else { 0 };
1035 if self.worker_threads.contains_key(&key) {
1036 return Ok(self.worker_threads.get(&key).unwrap());
1037 }
1038
1039 let ex = self.create_executor();
1040 let control_tube = self.control_tube.take();
1041 let dontcache_read = self.dontcache_read;
1042 let dontcache_write = self.dontcache_write;
1043 let disk_image = if self.worker_per_queue {
1044 self.disk_image
1045 .as_ref()
1046 .context("Failed to ref a disk image")?
1047 .try_clone()
1048 .context("Failed to clone a disk image")?
1049 } else {
1050 self.disk_image
1051 .take()
1052 .context("Failed to take a disk image")?
1053 };
1054 let read_only = self.read_only;
1055 let sparse = self.sparse;
1056 let id = self.id;
1057 let worker_shared_state = self.shared_state.clone();
1058
1059 let (worker_tx, worker_rx) = mpsc::unbounded();
1060 let worker_thread = WorkerThread::start("virtio_blk", move |kill_evt| {
1061 let async_control =
1062 control_tube.map(|c| AsyncTube::new(&ex, c).expect("failed to create async tube"));
1063
1064 let async_image = match disk_image.to_async_disk(&ex) {
1065 Ok(d) => d,
1066 Err(e) => panic!("Failed to create async disk {e:#}"),
1067 };
1068
1069 let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1070 disk_image: async_image,
1071 read_only,
1072 sparse,
1073 id,
1074 dontcache_read,
1075 dontcache_write,
1076 worker_shared_state,
1077 }));
1078
1079 if let Err(err_string) = ex
1080 .run_until(async {
1081 let r = run_worker(&ex, &disk_state, &async_control, worker_rx, kill_evt).await;
1082 if let Err(e) = disk_state.lock().await.disk_image.flush().await {
1084 error!("failed to flush disk image when stopping worker: {e:?}");
1085 }
1086 r
1087 })
1088 .expect("run_until failed")
1089 {
1090 error!("{:#}", err_string);
1091 }
1092 });
1093 match self.worker_threads.entry(key) {
1094 std::collections::btree_map::Entry::Occupied(_) => unreachable!(),
1095 std::collections::btree_map::Entry::Vacant(e) => {
1096 Ok(e.insert((worker_thread, worker_tx)))
1097 }
1098 }
1099 }
1100
1101 pub fn start_queue(
1102 &mut self,
1103 idx: usize,
1104 queue: Queue,
1105 _mem: GuestMemory,
1106 ) -> anyhow::Result<()> {
1107 let (_, worker_tx) = self.start_worker(idx)?;
1108 worker_tx
1109 .unbounded_send(WorkerCmd::StartQueue { index: idx, queue })
1110 .expect("worker channel closed early");
1111 self.activated_queues.insert(idx);
1112 Ok(())
1113 }
1114
1115 pub fn stop_queue(&mut self, idx: usize) -> anyhow::Result<Queue> {
1116 let (_, worker_tx) = self
1119 .worker_threads
1120 .get(if self.worker_per_queue { &idx } else { &0 })
1121 .context("worker not found")?;
1122 let (response_tx, response_rx) = oneshot::channel();
1123 worker_tx
1124 .unbounded_send(WorkerCmd::StopQueue {
1125 index: idx,
1126 response_tx,
1127 })
1128 .expect("worker channel closed early");
1129 let queue = cros_async::block_on(async {
1130 response_rx
1131 .await
1132 .expect("response_rx closed early")
1133 .context("queue not found")
1134 })?;
1135 self.activated_queues.remove(&idx);
1136 Ok(queue)
1137 }
1138}
1139
1140impl VirtioDevice for BlockAsync {
1141 fn keep_rds(&self) -> Vec<RawDescriptor> {
1142 let mut keep_rds = Vec::new();
1143
1144 if let Some(disk_image) = &self.disk_image {
1145 keep_rds.extend(disk_image.as_raw_descriptors());
1146 }
1147
1148 if let Some(control_tube) = &self.control_tube {
1149 keep_rds.push(control_tube.as_raw_descriptor());
1150 }
1151
1152 keep_rds
1153 }
1154
1155 fn features(&self) -> u64 {
1156 self.avail_features
1157 }
1158
1159 fn device_type(&self) -> DeviceType {
1160 DeviceType::Block
1161 }
1162
1163 fn queue_max_sizes(&self) -> &[u16] {
1164 &self.queue_sizes
1165 }
1166
1167 fn read_config(&self, offset: u64, data: &mut [u8]) {
1168 let config_space = {
1169 let disk_size = self.disk_size.load(Ordering::Acquire);
1170 Self::build_config_space(
1171 disk_size,
1172 self.seg_max,
1173 self.block_size,
1174 self.queue_sizes.len() as u16,
1175 )
1176 };
1177 copy_config(data, 0, config_space.as_bytes(), offset);
1178 }
1179
1180 fn activate(
1181 &mut self,
1182 mem: GuestMemory,
1183 _interrupt: Interrupt,
1184 queues: BTreeMap<usize, Queue>,
1185 ) -> anyhow::Result<()> {
1186 for (i, q) in queues {
1187 self.start_queue(i, q, mem.clone())?;
1188 }
1189 Ok(())
1190 }
1191
1192 fn reset(&mut self) -> anyhow::Result<()> {
1193 for (_, (_, worker_tx)) in self.worker_threads.iter_mut() {
1194 let (response_tx, response_rx) = oneshot::channel();
1195 worker_tx
1196 .unbounded_send(WorkerCmd::AbortQueues { response_tx })
1197 .expect("worker channel closed early");
1198 cros_async::block_on(async { response_rx.await.expect("response_rx closed early") });
1199 }
1200 self.activated_queues.clear();
1201 Ok(())
1202 }
1203
1204 fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
1205 let mut queues = BTreeMap::new();
1207 for index in self.activated_queues.clone() {
1208 queues.insert(index, self.stop_queue(index)?);
1209 }
1210 if queues.is_empty() {
1211 return Ok(None); }
1213 if let Some((_, (_, worker_tx))) = self.worker_threads.iter().next() {
1214 let (response_tx, response_rx) = oneshot::channel();
1215 worker_tx
1216 .unbounded_send(WorkerCmd::FlushDisk { response_tx })
1217 .expect("worker channel closed early");
1218 cros_async::block_on(async { response_rx.await.expect("response_rx closed early") });
1219 }
1220 Ok(Some(queues))
1221 }
1222
1223 fn virtio_wake(
1224 &mut self,
1225 queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
1226 ) -> anyhow::Result<()> {
1227 if let Some((mem, _interrupt, queues)) = queues_state {
1228 for (i, q) in queues {
1229 self.start_queue(i, q, mem.clone())?
1230 }
1231 }
1232 Ok(())
1233 }
1234
1235 fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
1236 AnySnapshot::to_any(())
1239 }
1240
1241 fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
1242 let () = AnySnapshot::from_any(data)?;
1243 Ok(())
1244 }
1245
1246 fn pci_address(&self) -> Option<PciAddress> {
1247 self.pci_address
1248 }
1249
1250 fn bootorder_fw_cfg(&self, pci_slot: u8) -> Option<(Vec<u8>, usize)> {
1251 self.boot_index
1252 .map(|s| (format!("scsi@{pci_slot}/disk@0,0").as_bytes().to_vec(), s))
1253 }
1254}
1255
1256#[cfg(test)]
1257mod tests {
1258 use std::fs::File;
1259 use std::mem::size_of_val;
1260 use std::sync::atomic::AtomicU64;
1261
1262 use data_model::Le32;
1263 use data_model::Le64;
1264 #[cfg(any(target_os = "android", target_os = "linux"))]
1265 use devices::suspendable_virtio_tests;
1266 use devices::virtio::base_features;
1267 use devices::virtio::create_descriptor_chain;
1268 use devices::virtio::DescriptorType;
1269 use devices::virtio::QueueConfig;
1270 use disk::SingleFileDisk;
1271 use hypervisor::ProtectionType;
1272 use tempfile::tempfile;
1273 use tempfile::TempDir;
1274 use vm_memory::GuestAddress;
1275
1276 use super::*;
1277
1278 #[test]
1279 fn read_size() {
1280 let f = tempfile().unwrap();
1281 f.set_len(0x1000).unwrap();
1282
1283 let features = base_features(ProtectionType::Unprotected);
1284 let disk_option = DiskOption::default();
1285 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1286 let mut num_sectors = [0u8; 4];
1287 b.read_config(0, &mut num_sectors);
1288 assert_eq!([0x08, 0x00, 0x00, 0x00], num_sectors);
1290 let mut msw_sectors = [0u8; 4];
1291 b.read_config(4, &mut msw_sectors);
1292 assert_eq!([0x00, 0x00, 0x00, 0x00], msw_sectors);
1294 }
1295
1296 #[test]
1297 fn read_block_size() {
1298 let f = tempfile().unwrap();
1299 f.set_len(0x1000).unwrap();
1300
1301 let features = base_features(ProtectionType::Unprotected);
1302 let disk_option = DiskOption {
1303 block_size: 4096,
1304 sparse: false,
1305 ..Default::default()
1306 };
1307 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1308 let mut blk_size = [0u8; 4];
1309 b.read_config(20, &mut blk_size);
1310 assert_eq!([0x00, 0x10, 0x00, 0x00], blk_size);
1312 }
1313
1314 #[test]
1315 fn read_features() {
1316 let tempdir = TempDir::new().unwrap();
1317 let mut path = tempdir.path().to_owned();
1318 path.push("disk_image");
1319
1320 const DEVICE_FEATURE_BITS: u64 = 0xffffff;
1323
1324 {
1326 let f = File::create(&path).unwrap();
1327 let features = base_features(ProtectionType::Unprotected);
1328 let disk_option = DiskOption::default();
1329 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1330 assert_eq!(0x7244, b.features() & DEVICE_FEATURE_BITS);
1334 }
1335
1336 {
1338 let f = File::create(&path).unwrap();
1339 let features = base_features(ProtectionType::Unprotected);
1340 let disk_option = DiskOption {
1341 sparse: false,
1342 ..Default::default()
1343 };
1344 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1345 assert_eq!(0x5244, b.features() & DEVICE_FEATURE_BITS);
1348 }
1349
1350 {
1352 let f = File::create(&path).unwrap();
1353 let features = base_features(ProtectionType::Unprotected);
1354 let disk_option = DiskOption {
1355 read_only: true,
1356 ..Default::default()
1357 };
1358 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1359 assert_eq!(0x1064, b.features() & DEVICE_FEATURE_BITS);
1362 }
1363 }
1364
1365 #[test]
1366 fn check_pci_adress_configurability() {
1367 let f = tempfile().unwrap();
1368
1369 let features = base_features(ProtectionType::Unprotected);
1370 let disk_option = DiskOption {
1371 pci_address: Some(PciAddress {
1372 bus: 0,
1373 dev: 1,
1374 func: 1,
1375 }),
1376 ..Default::default()
1377 };
1378 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1379
1380 assert_eq!(b.pci_address(), disk_option.pci_address);
1381 }
1382
1383 #[test]
1384 fn check_runtime_blk_queue_configurability() {
1385 let tempdir = TempDir::new().unwrap();
1386 let mut path = tempdir.path().to_owned();
1387 path.push("disk_image");
1388 let features = base_features(ProtectionType::Unprotected);
1389
1390 let f = File::create(&path).unwrap();
1392 let disk_option = DiskOption::default();
1393 let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1394 assert_eq!(
1395 [DEFAULT_QUEUE_SIZE; DEFAULT_NUM_QUEUES as usize],
1396 b.queue_max_sizes()
1397 );
1398
1399 let f = File::create(&path).unwrap();
1401 let disk_option = DiskOption::default();
1402 let b = BlockAsync::new(
1403 features,
1404 Box::new(f),
1405 &disk_option,
1406 None,
1407 Some(128),
1408 Some(1),
1409 )
1410 .unwrap();
1411 assert_eq!([128; 1], b.queue_max_sizes());
1412 assert_eq!(0, b.features() & (1 << VIRTIO_BLK_F_MQ) as u64);
1414 }
1415
1416 #[test]
1417 fn read_last_sector() {
1418 let ex = Executor::new().expect("creating an executor failed");
1419
1420 let f = tempfile().unwrap();
1421 let disk_size = 0x1000;
1422 f.set_len(disk_size).unwrap();
1423 let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1424
1425 let mem = Rc::new(
1426 GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1427 .expect("Creating guest memory failed."),
1428 );
1429
1430 let req_hdr = virtio_blk_req_header {
1431 req_type: Le32::from(VIRTIO_BLK_T_IN),
1432 reserved: Le32::from(0),
1433 sector: Le64::from(7), };
1435 mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1436 .expect("writing req failed");
1437
1438 let mut avail_desc = create_descriptor_chain(
1439 &mem,
1440 GuestAddress(0x100), GuestAddress(0x1000), vec![
1443 (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1445 (DescriptorType::Writable, 512),
1447 (DescriptorType::Writable, 1),
1449 ],
1450 0,
1451 )
1452 .expect("create_descriptor_chain failed");
1453
1454 let timer = Timer::new().expect("Failed to create a timer");
1455 let flush_timer = Rc::new(RefCell::new(
1456 TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1457 ));
1458 let flush_timer_armed = Rc::new(RefCell::new(false));
1459
1460 let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1461 disk_image: Box::new(af),
1462 read_only: false,
1463 sparse: true,
1464 id: Default::default(),
1465 dontcache_read: false,
1466 dontcache_write: false,
1467 worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1468 disk_size: Arc::new(AtomicU64::new(disk_size)),
1469 })),
1470 }));
1471
1472 let fut = process_one_request(
1473 &mut avail_desc,
1474 &disk_state,
1475 &flush_timer,
1476 &flush_timer_armed,
1477 );
1478
1479 ex.run_until(fut)
1480 .expect("running executor failed")
1481 .expect("execute failed");
1482
1483 let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512) as u64);
1484 let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1485 assert_eq!(status, VIRTIO_BLK_S_OK);
1486 }
1487
1488 #[test]
1489 fn read_beyond_last_sector() {
1490 let f = tempfile().unwrap();
1491 let disk_size = 0x1000;
1492 f.set_len(disk_size).unwrap();
1493 let mem = Rc::new(
1494 GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1495 .expect("Creating guest memory failed."),
1496 );
1497
1498 let req_hdr = virtio_blk_req_header {
1499 req_type: Le32::from(VIRTIO_BLK_T_IN),
1500 reserved: Le32::from(0),
1501 sector: Le64::from(7), };
1503 mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1504 .expect("writing req failed");
1505
1506 let mut avail_desc = create_descriptor_chain(
1507 &mem,
1508 GuestAddress(0x100), GuestAddress(0x1000), vec![
1511 (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1513 (DescriptorType::Writable, 512 * 2),
1515 (DescriptorType::Writable, 1),
1517 ],
1518 0,
1519 )
1520 .expect("create_descriptor_chain failed");
1521
1522 let ex = Executor::new().expect("creating an executor failed");
1523
1524 let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1525 let timer = Timer::new().expect("Failed to create a timer");
1526 let flush_timer = Rc::new(RefCell::new(
1527 TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1528 ));
1529 let flush_timer_armed = Rc::new(RefCell::new(false));
1530 let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1531 disk_image: Box::new(af),
1532 read_only: false,
1533 sparse: true,
1534 id: Default::default(),
1535 dontcache_read: false,
1536 dontcache_write: false,
1537 worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1538 disk_size: Arc::new(AtomicU64::new(disk_size)),
1539 })),
1540 }));
1541
1542 let fut = process_one_request(
1543 &mut avail_desc,
1544 &disk_state,
1545 &flush_timer,
1546 &flush_timer_armed,
1547 );
1548
1549 ex.run_until(fut)
1550 .expect("running executor failed")
1551 .expect("execute failed");
1552
1553 let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512 * 2) as u64);
1554 let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1555 assert_eq!(status, VIRTIO_BLK_S_IOERR);
1556 }
1557
1558 #[test]
1559 fn get_id() {
1560 let ex = Executor::new().expect("creating an executor failed");
1561
1562 let f = tempfile().unwrap();
1563 let disk_size = 0x1000;
1564 f.set_len(disk_size).unwrap();
1565
1566 let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1567 .expect("Creating guest memory failed.");
1568
1569 let req_hdr = virtio_blk_req_header {
1570 req_type: Le32::from(VIRTIO_BLK_T_GET_ID),
1571 reserved: Le32::from(0),
1572 sector: Le64::from(0),
1573 };
1574 mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1575 .expect("writing req failed");
1576
1577 let mut avail_desc = create_descriptor_chain(
1578 &mem,
1579 GuestAddress(0x100), GuestAddress(0x1000), vec![
1582 (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1584 (DescriptorType::Writable, 20),
1586 (DescriptorType::Writable, 1),
1588 ],
1589 0,
1590 )
1591 .expect("create_descriptor_chain failed");
1592
1593 let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1594 let timer = Timer::new().expect("Failed to create a timer");
1595 let flush_timer = Rc::new(RefCell::new(
1596 TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1597 ));
1598 let flush_timer_armed = Rc::new(RefCell::new(false));
1599
1600 let id = b"a20-byteserialnumber";
1601
1602 let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1603 disk_image: Box::new(af),
1604 read_only: false,
1605 sparse: true,
1606 id: *id,
1607 dontcache_read: false,
1608 dontcache_write: false,
1609 worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1610 disk_size: Arc::new(AtomicU64::new(disk_size)),
1611 })),
1612 }));
1613
1614 let fut = process_one_request(
1615 &mut avail_desc,
1616 &disk_state,
1617 &flush_timer,
1618 &flush_timer_armed,
1619 );
1620
1621 ex.run_until(fut)
1622 .expect("running executor failed")
1623 .expect("execute failed");
1624
1625 let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512) as u64);
1626 let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1627 assert_eq!(status, VIRTIO_BLK_S_OK);
1628
1629 let id_offset = GuestAddress(0x1000 + size_of_val(&req_hdr) as u64);
1630 let returned_id = mem.read_obj_from_addr::<[u8; 20]>(id_offset).unwrap();
1631 assert_eq!(returned_id, *id);
1632 }
1633
1634 #[test]
1635 fn reset_and_reactivate_single_worker() {
1636 reset_and_reactivate(false, None);
1637 }
1638
1639 #[test]
1640 fn reset_and_reactivate_multiple_workers() {
1641 reset_and_reactivate(true, None);
1642 }
1643
1644 #[test]
1645 #[cfg(windows)]
1646 fn reset_and_reactivate_overrlapped_io() {
1647 reset_and_reactivate(
1648 false,
1649 Some(
1650 cros_async::sys::windows::ExecutorKindSys::Overlapped { concurrency: None }.into(),
1651 ),
1652 );
1653 }
1654
1655 fn reset_and_reactivate(
1656 enables_multiple_workers: bool,
1657 async_executor: Option<cros_async::ExecutorKind>,
1658 ) {
1659 let f = tempfile::NamedTempFile::new().unwrap();
1661 f.as_file().set_len(0x1000).unwrap();
1662 let path: tempfile::TempPath = f.into_temp_path();
1665
1666 let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1668 .expect("Creating guest memory failed.");
1669
1670 let (_control_tube, control_tube_device) = Tube::pair().unwrap();
1674
1675 let features = base_features(ProtectionType::Unprotected);
1677 let id = b"Block serial number\0";
1678 let disk_option = DiskOption {
1679 path: path.to_path_buf(),
1680 read_only: true,
1681 id: Some(*id),
1682 sparse: false,
1683 multiple_workers: enables_multiple_workers,
1684 async_executor,
1685 ..Default::default()
1686 };
1687 let disk_image = disk_option.open().unwrap();
1688 let mut b = BlockAsync::new(
1689 features,
1690 disk_image,
1691 &disk_option,
1692 Some(control_tube_device),
1693 None,
1694 None,
1695 )
1696 .unwrap();
1697
1698 let interrupt = Interrupt::new_for_test();
1699
1700 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1702 q0.set_ready(true);
1703 let q0 = q0
1704 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1705 .expect("QueueConfig::activate");
1706
1707 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1708 q1.set_ready(true);
1709 let q1 = q1
1710 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1711 .expect("QueueConfig::activate");
1712
1713 b.activate(mem.clone(), interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1714 .expect("activate should succeed");
1715 if !enables_multiple_workers {
1717 assert!(
1718 b.disk_image.is_none(),
1719 "BlockAsync should not have a disk image"
1720 );
1721 }
1722 assert!(
1723 b.control_tube.is_none(),
1724 "BlockAsync should not have a control tube"
1725 );
1726 assert_eq!(
1727 b.worker_threads.len(),
1728 if enables_multiple_workers { 2 } else { 1 }
1729 );
1730
1731 assert!(b.reset().is_ok(), "reset should succeed");
1733 if !enables_multiple_workers {
1734 assert!(
1735 b.disk_image.is_none(),
1736 "BlockAsync should not have a disk image"
1737 );
1738 }
1739 assert!(
1740 b.control_tube.is_none(),
1741 "BlockAsync should not have a control tube"
1742 );
1743 assert_eq!(
1744 b.worker_threads.len(),
1745 if enables_multiple_workers { 2 } else { 1 }
1746 );
1747 assert_eq!(b.id, *b"Block serial number\0");
1748
1749 let interrupt = Interrupt::new_for_test();
1751 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1752 q0.set_ready(true);
1753 let q0 = q0
1754 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1755 .expect("QueueConfig::activate");
1756
1757 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1758 q1.set_ready(true);
1759 let q1 = q1
1760 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1761 .expect("QueueConfig::activate");
1762
1763 b.activate(mem, interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1764 .expect("re-activate should succeed");
1765 }
1766
1767 #[test]
1768 fn resize_with_single_worker() {
1769 resize(false);
1770 }
1771
1772 #[test]
1773 fn resize_with_multiple_workers() {
1774 resize(true);
1776 }
1777
1778 fn resize(enables_multiple_workers: bool) {
1779 let original_size = 0x1000;
1781 let resized_size = 0x2000;
1782
1783 let f = tempfile().unwrap();
1785 f.set_len(original_size).unwrap();
1786 let disk_image: Box<dyn DiskFile> = Box::new(f);
1787 assert_eq!(disk_image.get_len().unwrap(), original_size);
1788
1789 let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1791 .expect("Creating guest memory failed.");
1792
1793 let (control_tube, control_tube_device) = Tube::pair().unwrap();
1795
1796 let features = base_features(ProtectionType::Unprotected);
1798 let disk_option = DiskOption {
1799 multiple_workers: enables_multiple_workers,
1800 ..Default::default()
1801 };
1802 let mut b = BlockAsync::new(
1803 features,
1804 disk_image.try_clone().unwrap(),
1805 &disk_option,
1806 Some(control_tube_device),
1807 None,
1808 None,
1809 )
1810 .unwrap();
1811
1812 let interrupt = Interrupt::new_for_test();
1813
1814 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1816 q0.set_ready(true);
1817 let q0 = q0
1818 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1819 .expect("QueueConfig::activate");
1820
1821 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1822 q1.set_ready(true);
1823 let q1 = q1
1824 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1825 .expect("QueueConfig::activate");
1826
1827 b.activate(mem, interrupt.clone(), BTreeMap::from([(0, q0), (1, q1)]))
1828 .expect("activate should succeed");
1829
1830 assert_eq!(
1832 b.disk_size.load(Ordering::Acquire),
1833 original_size,
1834 "disk_size should be the original size first"
1835 );
1836 let mut capacity = [0u8; 8];
1837 b.read_config(0, &mut capacity);
1838 assert_eq!(
1839 capacity,
1840 [0x8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
1842 "read_config should read the original capacity first"
1843 );
1844
1845 control_tube
1847 .send(&DiskControlCommand::Resize {
1848 new_size: resized_size,
1849 })
1850 .unwrap();
1851 assert_eq!(
1852 control_tube.recv::<DiskControlResult>().unwrap(),
1853 DiskControlResult::Ok,
1854 "resize command should succeed"
1855 );
1856 assert_eq!(
1857 b.disk_size.load(Ordering::Acquire),
1858 resized_size,
1859 "disk_size should be resized to the new size"
1860 );
1861 assert_eq!(
1862 disk_image.get_len().unwrap(),
1863 resized_size,
1864 "underlying disk image should be resized to the new size"
1865 );
1866 let mut capacity = [0u8; 8];
1867 b.read_config(0, &mut capacity);
1868 assert_eq!(
1869 capacity,
1870 [0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
1872 "read_config should read the resized capacity"
1873 );
1874 interrupt
1876 .get_interrupt_evt()
1877 .wait()
1878 .expect("interrupt should be signaled");
1879
1880 assert_eq!(
1881 interrupt.read_interrupt_status(),
1882 devices::virtio::INTERRUPT_STATUS_CONFIG_CHANGED as u8,
1883 "INTERRUPT_STATUS_CONFIG_CHANGED should be signaled"
1884 );
1885 }
1886
1887 #[test]
1888 fn run_worker_threads() {
1889 let f = tempfile().unwrap();
1891 f.set_len(0x1000).unwrap();
1892 let disk_image: Box<dyn DiskFile> = Box::new(f);
1893
1894 let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1896 .expect("Creating guest memory failed.");
1897
1898 let features = base_features(ProtectionType::Unprotected);
1900 let disk_option = DiskOption::default();
1901 let mut b = BlockAsync::new(
1902 features,
1903 disk_image.try_clone().unwrap(),
1904 &disk_option,
1905 None,
1906 None,
1907 None,
1908 )
1909 .unwrap();
1910
1911 let interrupt = Interrupt::new_for_test();
1913 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1914 q0.set_ready(true);
1915 let q0 = q0
1916 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1917 .expect("QueueConfig::activate");
1918
1919 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1920 q1.set_ready(true);
1921 let q1 = q1
1922 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1923 .expect("QueueConfig::activate");
1924
1925 b.activate(mem.clone(), interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1926 .expect("activate should succeed");
1927
1928 assert_eq!(b.worker_threads.len(), 1, "1 threads should be spawned.");
1929 drop(b);
1930
1931 let features = base_features(ProtectionType::Unprotected);
1933 let disk_option = DiskOption {
1934 read_only: true,
1935 sparse: false,
1936 multiple_workers: true,
1937 ..DiskOption::default()
1938 };
1939 let mut b = BlockAsync::new(features, disk_image, &disk_option, None, None, None).unwrap();
1940
1941 let interrupt = Interrupt::new_for_test();
1943 let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1944 q0.set_ready(true);
1945 let q0 = q0
1946 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1947 .expect("QueueConfig::activate");
1948
1949 let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1950 q1.set_ready(true);
1951 let q1 = q1
1952 .activate(&mem, Event::new().unwrap(), interrupt.clone())
1953 .expect("QueueConfig::activate");
1954
1955 b.activate(mem, interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1956 .expect("activate should succeed");
1957
1958 assert_eq!(b.worker_threads.len(), 2, "2 threads should be spawned.");
1959 }
1960
1961 #[cfg(any(target_os = "android", target_os = "linux"))]
1962 struct BlockContext {}
1963
1964 #[cfg(any(target_os = "android", target_os = "linux"))]
1965 fn modify_device(_block_context: &mut BlockContext, b: &mut BlockAsync) {
1966 b.avail_features = !b.avail_features;
1967 }
1968
1969 #[cfg(any(target_os = "android", target_os = "linux"))]
1970 fn create_device() -> (BlockContext, BlockAsync) {
1971 let f = tempfile().unwrap();
1973 f.set_len(0x1000).unwrap();
1974 let disk_image: Box<dyn DiskFile> = Box::new(f);
1975
1976 let features = base_features(ProtectionType::Unprotected);
1978 let id = b"Block serial number\0";
1979 let disk_option = DiskOption {
1980 read_only: true,
1981 id: Some(*id),
1982 sparse: false,
1983 multiple_workers: true,
1984 ..Default::default()
1985 };
1986 (
1987 BlockContext {},
1988 BlockAsync::new(
1989 features,
1990 disk_image.try_clone().unwrap(),
1991 &disk_option,
1992 None,
1993 None,
1994 None,
1995 )
1996 .unwrap(),
1997 )
1998 }
1999
2000 #[cfg(any(target_os = "android", target_os = "linux"))]
2001 suspendable_virtio_tests!(asyncblock, create_device, 2, modify_device);
2002}