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