devices/virtio/block/
asynchronous.rs

1// Copyright 2021 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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;
103// Arbitrary limits for number of discard/write zeroes segments.
104const MAX_DISCARD_SEG: u32 = 32;
105const MAX_WRITE_ZEROES_SEG: u32 = 32;
106// Hard-coded to 64 KiB (in 512-byte sectors) for now,
107// but this should probably be based on cluster size for qcow.
108const 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            // Log disk I/O errors at debug level to avoid flooding the logs.
187            ExecuteError::ReadIo { .. }
188            | ExecuteError::WriteIo { .. }
189            | ExecuteError::Flush { .. }
190            | ExecuteError::DiscardWriteZeroes { .. } => LogLevel::Debug,
191            // Log all other failures as errors.
192            _ => LogLevel::Error,
193        }
194    }
195}
196
197/// Errors that happen in block outside of executing a request.
198/// This includes errors during resize and flush operations.
199#[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
208/// Maximum length of the virtio-block ID string field.
209const ID_LEN: usize = 20;
210
211/// Virtio block device identifier.
212/// This is an ASCII string terminated by a \0, unless all 20 bytes are used,
213/// in which case the \0 terminator is omitted.
214type BlockId = [u8; ID_LEN];
215
216/// Tracks the state of an anynchronous disk.
217struct DiskState {
218    disk_image: Box<dyn AsyncDisk>,
219    read_only: bool,
220    sparse: bool,
221    id: BlockId,
222    dontcache_read: bool,
223    dontcache_write: bool,
224    /// A DiskState is owned by each worker's executor and cannot be shared by workers, thus
225    /// `worker_shared_state` holds the state shared by workers in Arc.
226    worker_shared_state: Arc<AsyncRwLock<WorkerSharedState>>,
227}
228
229/// Disk state which can be modified by other worker threads
230struct WorkerSharedState {
231    disk_size: Arc<AtomicU64>,
232}
233
234async fn process_one_request(
235    avail_desc: &mut DescriptorChain,
236    disk_state: &AsyncRwLock<DiskState>,
237    flush_timer: &RefCell<TimerAsync<Timer>>,
238    flush_timer_armed: &RefCell<bool>,
239) -> result::Result<usize, ExecuteError> {
240    let reader = &mut avail_desc.reader;
241    let writer = &mut avail_desc.writer;
242
243    // The last byte of the buffer is virtio_blk_req::status.
244    // Split it into a separate Writer so that status_writer is the final byte and
245    // the original writer is left with just the actual block I/O data.
246    let available_bytes = writer.available_bytes();
247    let status_offset = available_bytes
248        .checked_sub(1)
249        .ok_or(ExecuteError::MissingStatus)?;
250    let mut status_writer = writer.split_at(status_offset);
251
252    let status = match BlockAsync::execute_request(
253        reader,
254        writer,
255        disk_state,
256        flush_timer,
257        flush_timer_armed,
258    )
259    .await
260    {
261        Ok(()) => VIRTIO_BLK_S_OK,
262        Err(e) => {
263            match e.log_level() {
264                LogLevel::Debug => debug!("failed executing disk request: {:#}", e),
265                LogLevel::Error => error!("failed executing disk request: {:#}", e),
266            }
267            e.status()
268        }
269    };
270
271    status_writer
272        .write_all(&[status])
273        .map_err(ExecuteError::WriteStatus)?;
274    Ok(available_bytes)
275}
276
277/// Process one descriptor chain asynchronously.
278async fn process_one_chain(
279    queue: &RefCell<Queue>,
280    mut avail_desc: DescriptorChain,
281    disk_state: &AsyncRwLock<DiskState>,
282    flush_timer: &RefCell<TimerAsync<Timer>>,
283    flush_timer_armed: &RefCell<bool>,
284) {
285    let len = match process_one_request(&mut avail_desc, disk_state, flush_timer, flush_timer_armed)
286        .await
287    {
288        Ok(len) => len,
289        Err(e) => {
290            error!("block: failed to handle request: {:#}", e);
291            0
292        }
293    };
294
295    let mut queue = queue.borrow_mut();
296    queue.add_used_with_bytes_written(avail_desc, len as u32);
297    queue.trigger_interrupt();
298}
299
300// There is one async task running `handle_queue` per virtio queue in use.
301// Receives messages from the guest and queues a task to complete the operations with the async
302// executor.
303async fn handle_queue(
304    disk_state: Rc<AsyncRwLock<DiskState>>,
305    queue: Queue,
306    evt: EventAsync,
307    flush_timer: Rc<RefCell<TimerAsync<Timer>>>,
308    flush_timer_armed: Rc<RefCell<bool>>,
309    mut stop_rx: oneshot::Receiver<()>,
310) -> Queue {
311    let queue = RefCell::new(queue);
312    let mut background_tasks = FuturesUnordered::new();
313    let evt_future = evt.next_val().fuse();
314    pin_mut!(evt_future);
315    loop {
316        // Wait for the next signal from `evt` and process `background_tasks` in the meantime.
317        //
318        // NOTE: We can't call `evt.next_val()` directly in the `select!` expression. That would
319        // create a new future each time, which, in the completion-based async backends like
320        // io_uring, means we'd submit a new syscall each time (i.e. a race condition on the
321        // eventfd).
322        futures::select! {
323            _ = background_tasks.next() => continue,
324            res = evt_future => {
325                evt_future.set(evt.next_val().fuse());
326                if let Err(e) = res {
327                    error!("Failed to read the next queue event: {:#}", e);
328                    continue;
329                }
330            }
331            _ = stop_rx => {
332                // Process all the descriptors we've already popped from the queue so that we leave
333                // the queue in a consistent state.
334                background_tasks.collect::<()>().await;
335                return queue.into_inner();
336            }
337        };
338        while let Some(descriptor_chain) = queue.borrow_mut().pop() {
339            background_tasks.push(process_one_chain(
340                &queue,
341                descriptor_chain,
342                &disk_state,
343                &flush_timer,
344                &flush_timer_armed,
345            ));
346        }
347    }
348}
349
350async fn handle_command_tube(
351    command_tube: &Option<AsyncTube>,
352    interrupt: &RefCell<Option<Interrupt>>,
353    disk_state: Rc<AsyncRwLock<DiskState>>,
354) -> Result<(), ExecuteError> {
355    let command_tube = match command_tube {
356        Some(c) => c,
357        None => {
358            futures::future::pending::<()>().await;
359            return Ok(());
360        }
361    };
362    loop {
363        match command_tube.next().await {
364            Ok(command) => {
365                let resp = match command {
366                    DiskControlCommand::Resize { new_size } => resize(&disk_state, new_size).await,
367                };
368
369                let resp_clone = resp.clone();
370                command_tube
371                    .send(resp_clone)
372                    .await
373                    .map_err(ExecuteError::SendingResponse)?;
374                if let DiskControlResult::Ok = resp {
375                    if let Some(interrupt) = &*interrupt.borrow() {
376                        interrupt.signal_config_changed();
377                    }
378                }
379            }
380            Err(e) => return Err(ExecuteError::ReceivingCommand(e)),
381        }
382    }
383}
384
385async fn resize(disk_state: &AsyncRwLock<DiskState>, new_size: u64) -> DiskControlResult {
386    // Acquire exclusive, mutable access to the state so the virtqueue task won't be able to read
387    // the state while resizing.
388    let disk_state = disk_state.lock().await;
389    // Prevent any other worker threads won't be able to do IO.
390    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    // Allocate new space if the disk image is not sparse.
406    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
421/// Periodically flushes the disk when the given timer fires.
422async 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        // Reset armed before calling fdatasync to guarantee that IO requests that started after we
434        // call fdatasync will be committed eventually.
435        *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        // Once the queue is stopped, it will be sent back over `response_tx`.
455        // `None` indicates that there was no queue at the given index.
456        response_tx: oneshot::Sender<Option<Queue>>,
457    },
458    // Stop all queues without recovering the queues' state and without completing any queued up
459    // work .
460    AbortQueues {
461        // Once the queues are stopped, a `()` value will be sent back over `response_tx`.
462        response_tx: oneshot::Sender<()>,
463    },
464}
465
466// The main worker thread. Initialized the asynchronous worker tasks and passes them to the executor
467// to be processed.
468//
469// `disk_state` is wrapped by `AsyncRwLock`, which provides both shared and exclusive locks. It's
470// because the state can be read from the virtqueue task while the control task is processing a
471// resizing command.
472async fn run_worker(
473    ex: &Executor,
474    disk_state: &Rc<AsyncRwLock<DiskState>>,
475    control_tube: &Option<AsyncTube>,
476    mut worker_rx: mpsc::UnboundedReceiver<WorkerCmd>,
477    kill_evt: Event,
478) -> anyhow::Result<()> {
479    // One flush timer per disk.
480    let timer = Timer::new().expect("Failed to create a timer");
481    let flush_timer_armed = Rc::new(RefCell::new(false));
482
483    // Handles control requests.
484    let control_interrupt = RefCell::new(None);
485    let control = handle_command_tube(control_tube, &control_interrupt, disk_state.clone()).fuse();
486    pin_mut!(control);
487
488    // Handle all the queues in one sub-select call.
489    let flush_timer = Rc::new(RefCell::new(
490        TimerAsync::new(
491            // Call try_clone() to share the same underlying FD with the `flush_disk` task.
492            timer.try_clone().expect("Failed to clone flush_timer"),
493            ex,
494        )
495        .expect("Failed to create an async timer"),
496    ));
497
498    // Flushes the disk periodically.
499    let flush_timer2 = TimerAsync::new(timer, ex).expect("Failed to create an async timer");
500    let disk_flush = flush_disk(disk_state.clone(), flush_timer2, flush_timer_armed.clone()).fuse();
501    pin_mut!(disk_flush);
502
503    // Exit if the kill event is triggered.
504    let kill = async_utils::await_and_exit(ex, kill_evt).fuse();
505    pin_mut!(kill);
506
507    // Running queue handlers.
508    let mut queue_handlers = FuturesUnordered::new();
509    // Async stop functions for queue handlers, by queue index.
510    let mut queue_handler_stop_fns = std::collections::BTreeMap::new();
511
512    loop {
513        futures::select! {
514            _ = queue_handlers.next() => continue,
515            r = disk_flush => return r.context("failed to flush a disk"),
516            r = control => return r.context("failed to handle a control request"),
517            r = kill => return r.context("failed to wait on the kill event"),
518            worker_cmd = worker_rx.next() => {
519                match worker_cmd {
520                    None => anyhow::bail!("worker control channel unexpectedly closed"),
521                    Some(WorkerCmd::StartQueue{index, queue}) => {
522                        if control_interrupt.borrow().is_none() {
523                            *control_interrupt.borrow_mut() = Some(queue.interrupt().clone());
524                        }
525
526                        let (tx, rx) = oneshot::channel();
527                        let kick_evt = queue.event().try_clone().expect("Failed to clone queue event");
528                        let (handle_queue_future, remote_handle) = handle_queue(
529                            Rc::clone(disk_state),
530                            queue,
531                            EventAsync::new(kick_evt, ex).expect("Failed to create async event for queue"),
532                            Rc::clone(&flush_timer),
533                            Rc::clone(&flush_timer_armed),
534                            rx,
535                        ).remote_handle();
536                        let old_stop_fn = queue_handler_stop_fns.insert(index, move || {
537                            // Ask the handler to stop.
538                            tx.send(()).unwrap_or_else(|_| panic!("queue handler channel closed early"));
539                            // Wait for its return value.
540                            remote_handle
541                        });
542
543                        // If there was already a handler for this index, stop it before adding the
544                        // new handler future.
545                        if let Some(stop_fn) = old_stop_fn {
546                            warn!("Starting new queue handler without stopping old handler");
547                            // Unfortunately we can't just do `stop_fn().await` because the actual
548                            // work we are waiting on is in `queue_handlers`. So, run both.
549                            let mut fut = stop_fn().fuse();
550                            loop {
551                                futures::select! {
552                                    _ = queue_handlers.next() => continue,
553                                    _queue = fut => break,
554                                }
555                            }
556                        }
557
558                        queue_handlers.push(handle_queue_future);
559                    }
560                    Some(WorkerCmd::StopQueue{index, response_tx}) => {
561                        match queue_handler_stop_fns.remove(&index) {
562                            Some(stop_fn) => {
563                                // NOTE: This await is blocking the select loop. If we want to
564                                // support stopping queues concurrently, then it needs to be moved.
565                                // For now, keep it simple.
566                                //
567                                // Unfortunately we can't just do `stop_fn().await` because the
568                                // actual work we are waiting on is in `queue_handlers`. So, run
569                                // both.
570                                let mut fut = stop_fn().fuse();
571                                let queue = loop {
572                                    futures::select! {
573                                        _ = queue_handlers.next() => continue,
574                                        queue = fut => break queue,
575                                    }
576                                };
577
578                                // If this is the last queue, drop references to the interrupt so
579                                // that, when queues are started up again, we'll use the new
580                                // interrupt passed with the first queue.
581                                if queue_handlers.is_empty() {
582                                    *control_interrupt.borrow_mut() = None;
583                                }
584
585                                let _ = response_tx.send(Some(queue));
586                            }
587                            None => { let _ = response_tx.send(None); },
588                        }
589
590                    }
591                    Some(WorkerCmd::AbortQueues{response_tx}) => {
592                        queue_handlers.clear();
593                        queue_handler_stop_fns.clear();
594
595                        *control_interrupt.borrow_mut() = None;
596
597                        let _ = response_tx.send(());
598                    }
599                }
600            }
601        };
602    }
603}
604
605/// Virtio device for exposing block level read/write operations on a host file.
606pub struct BlockAsync {
607    // We need to make boot_index public bc the field is used by the main crate to determine boot
608    // order
609    boot_index: Option<usize>,
610    // `None` iff `self.worker_per_queue == false` and the worker thread is running.
611    disk_image: Option<Box<dyn DiskFile>>,
612    disk_size: Arc<AtomicU64>,
613    avail_features: u64,
614    read_only: bool,
615    sparse: bool,
616    seg_max: u32,
617    block_size: u32,
618    id: BlockId,
619    control_tube: Option<Tube>,
620    queue_sizes: Vec<u16>,
621    pub(super) executor_kind: ExecutorKind,
622    // If `worker_per_queue == true`, `worker_threads` contains the worker for each running queue
623    // by index. Otherwise, contains the monolithic worker for all queues at index 0.
624    //
625    // Once a thread is started, we never stop it, except when `BlockAsync` itself is dropped. That
626    // is because we cannot easily convert the `AsyncDisk` back to a `DiskFile` when backed by
627    // Overlapped I/O on Windows because the file becomes permanently associated with the IOCP
628    // instance of the async executor.
629    worker_threads: BTreeMap<usize, (WorkerThread<()>, mpsc::UnboundedSender<WorkerCmd>)>,
630    shared_state: Arc<AsyncRwLock<WorkerSharedState>>,
631    // Whether to run worker threads in parallel for each queue
632    worker_per_queue: bool,
633    // Indices of running queues.
634    // TODO: The worker already tracks this. Only need it here to stop queues on sleep. Maybe add a
635    // worker cmd to stop all at once, then we can delete this field.
636    activated_queues: BTreeSet<usize>,
637    #[cfg(windows)]
638    pub(super) io_concurrency: u32,
639    pci_address: Option<PciAddress>,
640    dontcache_read: bool,
641    dontcache_write: bool,
642}
643
644impl BlockAsync {
645    /// Create a new virtio block device that operates on the given AsyncDisk.
646    pub fn new(
647        base_features: u64,
648        disk_image: Box<dyn DiskFile>,
649        disk_option: &DiskOption,
650        control_tube: Option<Tube>,
651        queue_size: Option<u16>,
652        num_queues: Option<u16>,
653    ) -> SysResult<BlockAsync> {
654        let read_only = disk_option.read_only;
655        let sparse = disk_option.sparse;
656        let block_size = disk_option.block_size;
657        let packed_queue = disk_option.packed_queue;
658        let id = disk_option.id.unwrap_or_default();
659        let mut worker_per_queue = disk_option.multiple_workers;
660        // Automatically disable multiple workers if the disk image can't be cloned.
661        if worker_per_queue && disk_image.try_clone().is_err() {
662            base::warn!("multiple workers requested, but not supported by disk image type");
663            worker_per_queue = false;
664        }
665        let executor_kind = disk_option.async_executor.unwrap_or_default();
666        let boot_index = disk_option.bootindex;
667        #[cfg(windows)]
668        let io_concurrency = disk_option.io_concurrency.get();
669
670        if block_size % SECTOR_SIZE as u32 != 0 {
671            error!(
672                "Block size {} is not a multiple of {}.",
673                block_size, SECTOR_SIZE,
674            );
675            return Err(SysError::new(libc::EINVAL));
676        }
677        let disk_size = disk_image.get_len()?;
678        if disk_size % block_size as u64 != 0 {
679            warn!(
680                "Disk size {} is not a multiple of block size {}; \
681                 the remainder will not be visible to the guest.",
682                disk_size, block_size,
683            );
684        }
685        let num_queues = num_queues.unwrap_or(DEFAULT_NUM_QUEUES);
686        let multi_queue = match num_queues {
687            0 => panic!("Number of queues cannot be zero for a block device"),
688            1 => false,
689            _ => true,
690        };
691        let q_size = queue_size.unwrap_or(DEFAULT_QUEUE_SIZE);
692        if !q_size.is_power_of_two() {
693            error!("queue size {} is not a power of 2.", q_size);
694            return Err(SysError::new(libc::EINVAL));
695        }
696        let queue_sizes = vec![q_size; num_queues as usize];
697
698        let avail_features =
699            Self::build_avail_features(base_features, read_only, sparse, multi_queue, packed_queue);
700
701        let seg_max = get_seg_max(q_size);
702
703        let disk_size = Arc::new(AtomicU64::new(disk_size));
704        let shared_state = Arc::new(AsyncRwLock::new(WorkerSharedState {
705            disk_size: disk_size.clone(),
706        }));
707
708        let mut dontcache_read = disk_option.dontcache;
709        let mut dontcache_write = disk_option.dontcache;
710        if dontcache_write {
711            let descriptors = disk_image.as_raw_descriptors();
712            if descriptors.is_empty()
713                || !descriptors
714                    .iter()
715                    .all(|&fd| check_dontcache_support(fd, true /* write */))
716            {
717                base::info!(
718                    "dontcache requested, but not supported for writes by backing filesystem; falling
719                    back to cached I/O"
720                );
721                dontcache_write = false;
722            }
723        }
724        if dontcache_read {
725            let descriptors = disk_image.as_raw_descriptors();
726            if descriptors.is_empty()
727                || !descriptors
728                    .iter()
729                    .all(|&fd| check_dontcache_support(fd, false /* write */))
730            {
731                base::info!(
732                    "dontcache requested, but not supported for reads by backing filesystem; falling
733                    back to cached I/O"
734                );
735                dontcache_read = false;
736            }
737        }
738
739        Ok(BlockAsync {
740            disk_image: Some(disk_image),
741            disk_size,
742            avail_features,
743            read_only,
744            sparse,
745            seg_max,
746            block_size,
747            id,
748            queue_sizes,
749            worker_threads: BTreeMap::new(),
750            shared_state,
751            worker_per_queue,
752            control_tube,
753            executor_kind,
754            activated_queues: BTreeSet::new(),
755            boot_index,
756            #[cfg(windows)]
757            io_concurrency,
758            pci_address: disk_option.pci_address,
759            dontcache_read,
760            dontcache_write,
761        })
762    }
763
764    /// Returns the feature flags given the specified attributes.
765    fn build_avail_features(
766        base_features: u64,
767        read_only: bool,
768        sparse: bool,
769        multi_queue: bool,
770        packed_queue: bool,
771    ) -> u64 {
772        let mut avail_features = base_features;
773        if read_only {
774            avail_features |= 1 << VIRTIO_BLK_F_RO;
775        } else {
776            if sparse {
777                avail_features |= 1 << VIRTIO_BLK_F_DISCARD;
778            }
779            avail_features |= 1 << VIRTIO_BLK_F_FLUSH;
780            avail_features |= 1 << VIRTIO_BLK_F_WRITE_ZEROES;
781        }
782        avail_features |= 1 << VIRTIO_BLK_F_SEG_MAX;
783        avail_features |= 1 << VIRTIO_BLK_F_BLK_SIZE;
784        if multi_queue {
785            avail_features |= 1 << VIRTIO_BLK_F_MQ;
786        }
787        if packed_queue {
788            avail_features |= 1 << VIRTIO_F_RING_PACKED;
789        }
790        avail_features
791    }
792
793    // Execute a single block device request.
794    // `writer` includes the data region only; the status byte is not included.
795    // It is up to the caller to convert the result of this function into a status byte
796    // and write it to the expected location in guest memory.
797    async fn execute_request(
798        reader: &mut Reader,
799        writer: &mut Writer,
800        disk_state: &AsyncRwLock<DiskState>,
801        flush_timer: &RefCell<TimerAsync<Timer>>,
802        flush_timer_armed: &RefCell<bool>,
803    ) -> result::Result<(), ExecuteError> {
804        // Acquire immutable access to prevent tasks from resizing disk.
805        let disk_state = disk_state.read_lock().await;
806        // Acquire immutable access to prevent other worker threads from resizing disk.
807        let worker_shared_state = disk_state.worker_shared_state.read_lock().await;
808
809        let req_header: virtio_blk_req_header = reader.read_obj().map_err(ExecuteError::Read)?;
810
811        let req_type = req_header.req_type.to_native();
812        let sector = req_header.sector.to_native();
813
814        if disk_state.read_only && req_type != VIRTIO_BLK_T_IN && req_type != VIRTIO_BLK_T_GET_ID {
815            return Err(ExecuteError::ReadOnly {
816                request_type: req_type,
817            });
818        }
819
820        /// Check that a request accesses only data within the disk's current size.
821        /// All parameters are in units of bytes.
822        fn check_range(
823            io_start: u64,
824            io_length: u64,
825            disk_size: u64,
826        ) -> result::Result<(), ExecuteError> {
827            let io_end = io_start
828                .checked_add(io_length)
829                .ok_or(ExecuteError::OutOfRange)?;
830            if io_end > disk_size {
831                Err(ExecuteError::OutOfRange)
832            } else {
833                Ok(())
834            }
835        }
836
837        let disk_size = worker_shared_state.disk_size.load(Ordering::Relaxed);
838        match req_type {
839            VIRTIO_BLK_T_IN => {
840                let data_len = writer.available_bytes();
841                if data_len == 0 {
842                    return Ok(());
843                }
844                let offset = sector
845                    .checked_shl(u32::from(SECTOR_SHIFT))
846                    .ok_or(ExecuteError::OutOfRange)?;
847                check_range(offset, data_len as u64, disk_size)?;
848                let disk_image = &disk_state.disk_image;
849                writer
850                    .write_all_from_at_fut(
851                        &**disk_image,
852                        data_len,
853                        offset,
854                        IoOptions {
855                            dontcache: disk_state.dontcache_read,
856                        },
857                    )
858                    .await
859                    .map_err(|desc_error| ExecuteError::ReadIo {
860                        length: data_len,
861                        sector,
862                        desc_error,
863                    })?;
864            }
865            VIRTIO_BLK_T_OUT => {
866                let data_len = reader.available_bytes();
867                if data_len == 0 {
868                    return Ok(());
869                }
870                let offset = sector
871                    .checked_shl(u32::from(SECTOR_SHIFT))
872                    .ok_or(ExecuteError::OutOfRange)?;
873                check_range(offset, data_len as u64, disk_size)?;
874                let disk_image = &disk_state.disk_image;
875                reader
876                    .read_exact_to_at_fut(
877                        &**disk_image,
878                        data_len,
879                        offset,
880                        IoOptions {
881                            dontcache: disk_state.dontcache_write,
882                        },
883                    )
884                    .await
885                    .map_err(|desc_error| ExecuteError::WriteIo {
886                        length: data_len,
887                        sector,
888                        desc_error,
889                    })?;
890
891                if !*flush_timer_armed.borrow() {
892                    *flush_timer_armed.borrow_mut() = true;
893
894                    let flush_delay = Duration::from_secs(60);
895                    flush_timer
896                        .borrow_mut()
897                        .reset_oneshot(flush_delay)
898                        .map_err(ExecuteError::TimerReset)?;
899                }
900            }
901            VIRTIO_BLK_T_DISCARD | VIRTIO_BLK_T_WRITE_ZEROES => {
902                if req_type == VIRTIO_BLK_T_DISCARD && !disk_state.sparse {
903                    // Discard is a hint; if this is a non-sparse disk, just ignore it.
904                    return Ok(());
905                }
906
907                let seg_count =
908                    reader.available_bytes() / size_of::<virtio_blk_discard_write_zeroes>();
909                let seg_max = if req_type == VIRTIO_BLK_T_DISCARD {
910                    MAX_DISCARD_SEG as usize
911                } else {
912                    MAX_WRITE_ZEROES_SEG as usize
913                };
914                if seg_count > seg_max {
915                    return Err(ExecuteError::TooManySegments(seg_count, seg_max));
916                }
917
918                while reader.available_bytes() >= size_of::<virtio_blk_discard_write_zeroes>() {
919                    let seg: virtio_blk_discard_write_zeroes =
920                        reader.read_obj().map_err(ExecuteError::Read)?;
921
922                    let sector = seg.sector.to_native();
923                    let num_sectors = seg.num_sectors.to_native();
924                    let flags = seg.flags.to_native();
925
926                    let valid_flags = if req_type == VIRTIO_BLK_T_WRITE_ZEROES {
927                        VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP
928                    } else {
929                        0
930                    };
931
932                    if (flags & !valid_flags) != 0 {
933                        return Err(ExecuteError::DiscardWriteZeroes {
934                            ioerr: None,
935                            sector,
936                            num_sectors,
937                            flags,
938                        });
939                    }
940
941                    let offset = sector
942                        .checked_shl(u32::from(SECTOR_SHIFT))
943                        .ok_or(ExecuteError::OutOfRange)?;
944                    let length = u64::from(num_sectors)
945                        .checked_shl(u32::from(SECTOR_SHIFT))
946                        .ok_or(ExecuteError::OutOfRange)?;
947                    check_range(offset, length, disk_size)?;
948
949                    if req_type == VIRTIO_BLK_T_DISCARD {
950                        // Since Discard is just a hint and some filesystems may not implement
951                        // FALLOC_FL_PUNCH_HOLE, ignore punch_hole errors.
952                        let _ = disk_state.disk_image.punch_hole(offset, length).await;
953                    } else {
954                        disk_state
955                            .disk_image
956                            .write_zeroes_at(offset, length)
957                            .await
958                            .map_err(|e| ExecuteError::DiscardWriteZeroes {
959                                ioerr: Some(e),
960                                sector,
961                                num_sectors,
962                                flags,
963                            })?;
964                    }
965                }
966            }
967            VIRTIO_BLK_T_FLUSH => {
968                disk_state
969                    .disk_image
970                    .fdatasync()
971                    .await
972                    .map_err(ExecuteError::Flush)?;
973
974                if *flush_timer_armed.borrow() {
975                    flush_timer
976                        .borrow_mut()
977                        .clear()
978                        .map_err(ExecuteError::TimerReset)?;
979                    *flush_timer_armed.borrow_mut() = false;
980                }
981            }
982            VIRTIO_BLK_T_GET_ID => {
983                writer
984                    .write_all(&disk_state.id)
985                    .map_err(ExecuteError::CopyId)?;
986            }
987            t => return Err(ExecuteError::Unsupported(t)),
988        };
989        Ok(())
990    }
991
992    /// Builds and returns the config structure used to specify block features.
993    fn build_config_space(
994        disk_size: u64,
995        seg_max: u32,
996        block_size: u32,
997        num_queues: u16,
998    ) -> virtio_blk_config {
999        virtio_blk_config {
1000            // If the image is not a multiple of the sector size, the tail bits are not exposed.
1001            capacity: Le64::from(disk_size >> SECTOR_SHIFT),
1002            seg_max: Le32::from(seg_max),
1003            blk_size: Le32::from(block_size),
1004            num_queues: Le16::from(num_queues),
1005            max_discard_sectors: Le32::from(MAX_DISCARD_SECTORS),
1006            discard_sector_alignment: Le32::from(DISCARD_SECTOR_ALIGNMENT),
1007            max_write_zeroes_sectors: Le32::from(MAX_WRITE_ZEROES_SECTORS),
1008            write_zeroes_may_unmap: 1,
1009            max_discard_seg: Le32::from(MAX_DISCARD_SEG),
1010            max_write_zeroes_seg: Le32::from(MAX_WRITE_ZEROES_SEG),
1011            ..Default::default()
1012        }
1013    }
1014
1015    /// Get the worker for a queue, starting it if necessary.
1016    // NOTE: Can't use `BTreeMap::entry` because it requires an exclusive ref for the whole branch.
1017    #[allow(clippy::map_entry)]
1018    fn start_worker(
1019        &mut self,
1020        idx: usize,
1021    ) -> anyhow::Result<&(WorkerThread<()>, mpsc::UnboundedSender<WorkerCmd>)> {
1022        let key = if self.worker_per_queue { idx } else { 0 };
1023        if self.worker_threads.contains_key(&key) {
1024            return Ok(self.worker_threads.get(&key).unwrap());
1025        }
1026
1027        let ex = self.create_executor();
1028        let control_tube = self.control_tube.take();
1029        let dontcache_read = self.dontcache_read;
1030        let dontcache_write = self.dontcache_write;
1031        let disk_image = if self.worker_per_queue {
1032            self.disk_image
1033                .as_ref()
1034                .context("Failed to ref a disk image")?
1035                .try_clone()
1036                .context("Failed to clone a disk image")?
1037        } else {
1038            self.disk_image
1039                .take()
1040                .context("Failed to take a disk image")?
1041        };
1042        let read_only = self.read_only;
1043        let sparse = self.sparse;
1044        let id = self.id;
1045        let worker_shared_state = self.shared_state.clone();
1046
1047        let (worker_tx, worker_rx) = mpsc::unbounded();
1048        let worker_thread = WorkerThread::start("virtio_blk", move |kill_evt| {
1049            let async_control =
1050                control_tube.map(|c| AsyncTube::new(&ex, c).expect("failed to create async tube"));
1051
1052            let async_image = match disk_image.to_async_disk(&ex) {
1053                Ok(d) => d,
1054                Err(e) => panic!("Failed to create async disk {e:#}"),
1055            };
1056
1057            let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1058                disk_image: async_image,
1059                read_only,
1060                sparse,
1061                id,
1062                dontcache_read,
1063                dontcache_write,
1064                worker_shared_state,
1065            }));
1066
1067            if let Err(err_string) = ex
1068                .run_until(async {
1069                    let r = run_worker(&ex, &disk_state, &async_control, worker_rx, kill_evt).await;
1070                    // Flush any in-memory disk image state to file.
1071                    if let Err(e) = disk_state.lock().await.disk_image.flush().await {
1072                        error!("failed to flush disk image when stopping worker: {e:?}");
1073                    }
1074                    r
1075                })
1076                .expect("run_until failed")
1077            {
1078                error!("{:#}", err_string);
1079            }
1080        });
1081        match self.worker_threads.entry(key) {
1082            std::collections::btree_map::Entry::Occupied(_) => unreachable!(),
1083            std::collections::btree_map::Entry::Vacant(e) => {
1084                Ok(e.insert((worker_thread, worker_tx)))
1085            }
1086        }
1087    }
1088
1089    pub fn start_queue(
1090        &mut self,
1091        idx: usize,
1092        queue: Queue,
1093        _mem: GuestMemory,
1094    ) -> anyhow::Result<()> {
1095        let (_, worker_tx) = self.start_worker(idx)?;
1096        worker_tx
1097            .unbounded_send(WorkerCmd::StartQueue { index: idx, queue })
1098            .expect("worker channel closed early");
1099        self.activated_queues.insert(idx);
1100        Ok(())
1101    }
1102
1103    pub fn stop_queue(&mut self, idx: usize) -> anyhow::Result<Queue> {
1104        // TODO: Consider stopping the worker thread if this is the last queue managed by it. Then,
1105        // simplify `virtio_sleep` and/or `reset` methods.
1106        let (_, worker_tx) = self
1107            .worker_threads
1108            .get(if self.worker_per_queue { &idx } else { &0 })
1109            .context("worker not found")?;
1110        let (response_tx, response_rx) = oneshot::channel();
1111        worker_tx
1112            .unbounded_send(WorkerCmd::StopQueue {
1113                index: idx,
1114                response_tx,
1115            })
1116            .expect("worker channel closed early");
1117        let queue = cros_async::block_on(async {
1118            response_rx
1119                .await
1120                .expect("response_rx closed early")
1121                .context("queue not found")
1122        })?;
1123        self.activated_queues.remove(&idx);
1124        Ok(queue)
1125    }
1126}
1127
1128impl VirtioDevice for BlockAsync {
1129    fn keep_rds(&self) -> Vec<RawDescriptor> {
1130        let mut keep_rds = Vec::new();
1131
1132        if let Some(disk_image) = &self.disk_image {
1133            keep_rds.extend(disk_image.as_raw_descriptors());
1134        }
1135
1136        if let Some(control_tube) = &self.control_tube {
1137            keep_rds.push(control_tube.as_raw_descriptor());
1138        }
1139
1140        keep_rds
1141    }
1142
1143    fn features(&self) -> u64 {
1144        self.avail_features
1145    }
1146
1147    fn device_type(&self) -> DeviceType {
1148        DeviceType::Block
1149    }
1150
1151    fn queue_max_sizes(&self) -> &[u16] {
1152        &self.queue_sizes
1153    }
1154
1155    fn read_config(&self, offset: u64, data: &mut [u8]) {
1156        let config_space = {
1157            let disk_size = self.disk_size.load(Ordering::Acquire);
1158            Self::build_config_space(
1159                disk_size,
1160                self.seg_max,
1161                self.block_size,
1162                self.queue_sizes.len() as u16,
1163            )
1164        };
1165        copy_config(data, 0, config_space.as_bytes(), offset);
1166    }
1167
1168    fn activate(
1169        &mut self,
1170        mem: GuestMemory,
1171        _interrupt: Interrupt,
1172        queues: BTreeMap<usize, Queue>,
1173    ) -> anyhow::Result<()> {
1174        for (i, q) in queues {
1175            self.start_queue(i, q, mem.clone())?;
1176        }
1177        Ok(())
1178    }
1179
1180    fn reset(&mut self) -> anyhow::Result<()> {
1181        for (_, (_, worker_tx)) in self.worker_threads.iter_mut() {
1182            let (response_tx, response_rx) = oneshot::channel();
1183            worker_tx
1184                .unbounded_send(WorkerCmd::AbortQueues { response_tx })
1185                .expect("worker channel closed early");
1186            cros_async::block_on(async { response_rx.await.expect("response_rx closed early") });
1187        }
1188        self.activated_queues.clear();
1189        Ok(())
1190    }
1191
1192    fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
1193        // Reclaim the queues from workers.
1194        let mut queues = BTreeMap::new();
1195        for index in self.activated_queues.clone() {
1196            queues.insert(index, self.stop_queue(index)?);
1197        }
1198        if queues.is_empty() {
1199            return Ok(None); // Not activated.
1200        }
1201        Ok(Some(queues))
1202    }
1203
1204    fn virtio_wake(
1205        &mut self,
1206        queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
1207    ) -> anyhow::Result<()> {
1208        if let Some((mem, _interrupt, queues)) = queues_state {
1209            for (i, q) in queues {
1210                self.start_queue(i, q, mem.clone())?
1211            }
1212        }
1213        Ok(())
1214    }
1215
1216    fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
1217        // `virtio_sleep` ensures there is no pending state, except for the `Queue`s, which are
1218        // handled at a higher layer.
1219        AnySnapshot::to_any(())
1220    }
1221
1222    fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
1223        let () = AnySnapshot::from_any(data)?;
1224        Ok(())
1225    }
1226
1227    fn pci_address(&self) -> Option<PciAddress> {
1228        self.pci_address
1229    }
1230
1231    fn bootorder_fw_cfg(&self, pci_slot: u8) -> Option<(Vec<u8>, usize)> {
1232        self.boot_index
1233            .map(|s| (format!("scsi@{pci_slot}/disk@0,0").as_bytes().to_vec(), s))
1234    }
1235}
1236
1237#[cfg(test)]
1238mod tests {
1239    use std::fs::File;
1240    use std::mem::size_of_val;
1241    use std::sync::atomic::AtomicU64;
1242
1243    use data_model::Le32;
1244    use data_model::Le64;
1245    use disk::SingleFileDisk;
1246    use hypervisor::ProtectionType;
1247    use tempfile::tempfile;
1248    use tempfile::TempDir;
1249    use vm_memory::GuestAddress;
1250
1251    use super::*;
1252    use crate::suspendable_virtio_tests;
1253    use crate::virtio::base_features;
1254    use crate::virtio::descriptor_utils::create_descriptor_chain;
1255    use crate::virtio::descriptor_utils::DescriptorType;
1256    use crate::virtio::QueueConfig;
1257
1258    #[test]
1259    fn read_size() {
1260        let f = tempfile().unwrap();
1261        f.set_len(0x1000).unwrap();
1262
1263        let features = base_features(ProtectionType::Unprotected);
1264        let disk_option = DiskOption::default();
1265        let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1266        let mut num_sectors = [0u8; 4];
1267        b.read_config(0, &mut num_sectors);
1268        // size is 0x1000, so num_sectors is 8 (4096/512).
1269        assert_eq!([0x08, 0x00, 0x00, 0x00], num_sectors);
1270        let mut msw_sectors = [0u8; 4];
1271        b.read_config(4, &mut msw_sectors);
1272        // size is 0x1000, so msw_sectors is 0.
1273        assert_eq!([0x00, 0x00, 0x00, 0x00], msw_sectors);
1274    }
1275
1276    #[test]
1277    fn read_block_size() {
1278        let f = tempfile().unwrap();
1279        f.set_len(0x1000).unwrap();
1280
1281        let features = base_features(ProtectionType::Unprotected);
1282        let disk_option = DiskOption {
1283            block_size: 4096,
1284            sparse: false,
1285            ..Default::default()
1286        };
1287        let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1288        let mut blk_size = [0u8; 4];
1289        b.read_config(20, &mut blk_size);
1290        // blk_size should be 4096 (0x1000).
1291        assert_eq!([0x00, 0x10, 0x00, 0x00], blk_size);
1292    }
1293
1294    #[test]
1295    fn read_features() {
1296        let tempdir = TempDir::new().unwrap();
1297        let mut path = tempdir.path().to_owned();
1298        path.push("disk_image");
1299
1300        // Feature bits 0-23 and 50-127 are specific for the device type, but
1301        // at the moment crosvm only supports 64 bits of feature bits.
1302        const DEVICE_FEATURE_BITS: u64 = 0xffffff;
1303
1304        // read-write block device
1305        {
1306            let f = File::create(&path).unwrap();
1307            let features = base_features(ProtectionType::Unprotected);
1308            let disk_option = DiskOption::default();
1309            let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1310            // writable device should set VIRTIO_BLK_F_FLUSH + VIRTIO_BLK_F_DISCARD
1311            // + VIRTIO_BLK_F_WRITE_ZEROES + VIRTIO_BLK_F_BLK_SIZE + VIRTIO_BLK_F_SEG_MAX
1312            // + VIRTIO_BLK_F_MQ
1313            assert_eq!(0x7244, b.features() & DEVICE_FEATURE_BITS);
1314        }
1315
1316        // read-write block device, non-sparse
1317        {
1318            let f = File::create(&path).unwrap();
1319            let features = base_features(ProtectionType::Unprotected);
1320            let disk_option = DiskOption {
1321                sparse: false,
1322                ..Default::default()
1323            };
1324            let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1325            // writable device should set VIRTIO_F_FLUSH + VIRTIO_BLK_F_RO
1326            // + VIRTIO_BLK_F_BLK_SIZE + VIRTIO_BLK_F_SEG_MAX + VIRTIO_BLK_F_MQ
1327            assert_eq!(0x5244, b.features() & DEVICE_FEATURE_BITS);
1328        }
1329
1330        // read-only block device
1331        {
1332            let f = File::create(&path).unwrap();
1333            let features = base_features(ProtectionType::Unprotected);
1334            let disk_option = DiskOption {
1335                read_only: true,
1336                ..Default::default()
1337            };
1338            let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1339            // read-only device should set VIRTIO_BLK_F_RO
1340            // + VIRTIO_BLK_F_BLK_SIZE + VIRTIO_BLK_F_SEG_MAX + VIRTIO_BLK_F_MQ
1341            assert_eq!(0x1064, b.features() & DEVICE_FEATURE_BITS);
1342        }
1343    }
1344
1345    #[test]
1346    fn check_pci_adress_configurability() {
1347        let f = tempfile().unwrap();
1348
1349        let features = base_features(ProtectionType::Unprotected);
1350        let disk_option = DiskOption {
1351            pci_address: Some(PciAddress {
1352                bus: 0,
1353                dev: 1,
1354                func: 1,
1355            }),
1356            ..Default::default()
1357        };
1358        let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1359
1360        assert_eq!(b.pci_address(), disk_option.pci_address);
1361    }
1362
1363    #[test]
1364    fn check_runtime_blk_queue_configurability() {
1365        let tempdir = TempDir::new().unwrap();
1366        let mut path = tempdir.path().to_owned();
1367        path.push("disk_image");
1368        let features = base_features(ProtectionType::Unprotected);
1369
1370        // Default case
1371        let f = File::create(&path).unwrap();
1372        let disk_option = DiskOption::default();
1373        let b = BlockAsync::new(features, Box::new(f), &disk_option, None, None, None).unwrap();
1374        assert_eq!(
1375            [DEFAULT_QUEUE_SIZE; DEFAULT_NUM_QUEUES as usize],
1376            b.queue_max_sizes()
1377        );
1378
1379        // Single queue of size 128
1380        let f = File::create(&path).unwrap();
1381        let disk_option = DiskOption::default();
1382        let b = BlockAsync::new(
1383            features,
1384            Box::new(f),
1385            &disk_option,
1386            None,
1387            Some(128),
1388            Some(1),
1389        )
1390        .unwrap();
1391        assert_eq!([128; 1], b.queue_max_sizes());
1392        // Single queue device should not set VIRTIO_BLK_F_MQ
1393        assert_eq!(0, b.features() & (1 << VIRTIO_BLK_F_MQ) as u64);
1394    }
1395
1396    #[test]
1397    fn read_last_sector() {
1398        let ex = Executor::new().expect("creating an executor failed");
1399
1400        let f = tempfile().unwrap();
1401        let disk_size = 0x1000;
1402        f.set_len(disk_size).unwrap();
1403        let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1404
1405        let mem = Rc::new(
1406            GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1407                .expect("Creating guest memory failed."),
1408        );
1409
1410        let req_hdr = virtio_blk_req_header {
1411            req_type: Le32::from(VIRTIO_BLK_T_IN),
1412            reserved: Le32::from(0),
1413            sector: Le64::from(7), // Disk is 8 sectors long, so this is the last valid sector.
1414        };
1415        mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1416            .expect("writing req failed");
1417
1418        let mut avail_desc = create_descriptor_chain(
1419            &mem,
1420            GuestAddress(0x100),  // Place descriptor chain at 0x100.
1421            GuestAddress(0x1000), // Describe buffer at 0x1000.
1422            vec![
1423                // Request header
1424                (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1425                // I/O buffer (1 sector of data)
1426                (DescriptorType::Writable, 512),
1427                // Request status
1428                (DescriptorType::Writable, 1),
1429            ],
1430            0,
1431        )
1432        .expect("create_descriptor_chain failed");
1433
1434        let timer = Timer::new().expect("Failed to create a timer");
1435        let flush_timer = Rc::new(RefCell::new(
1436            TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1437        ));
1438        let flush_timer_armed = Rc::new(RefCell::new(false));
1439
1440        let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1441            disk_image: Box::new(af),
1442            read_only: false,
1443            sparse: true,
1444            id: Default::default(),
1445            dontcache_read: false,
1446            dontcache_write: false,
1447            worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1448                disk_size: Arc::new(AtomicU64::new(disk_size)),
1449            })),
1450        }));
1451
1452        let fut = process_one_request(
1453            &mut avail_desc,
1454            &disk_state,
1455            &flush_timer,
1456            &flush_timer_armed,
1457        );
1458
1459        ex.run_until(fut)
1460            .expect("running executor failed")
1461            .expect("execute failed");
1462
1463        let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512) as u64);
1464        let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1465        assert_eq!(status, VIRTIO_BLK_S_OK);
1466    }
1467
1468    #[test]
1469    fn read_beyond_last_sector() {
1470        let f = tempfile().unwrap();
1471        let disk_size = 0x1000;
1472        f.set_len(disk_size).unwrap();
1473        let mem = Rc::new(
1474            GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1475                .expect("Creating guest memory failed."),
1476        );
1477
1478        let req_hdr = virtio_blk_req_header {
1479            req_type: Le32::from(VIRTIO_BLK_T_IN),
1480            reserved: Le32::from(0),
1481            sector: Le64::from(7), // Disk is 8 sectors long, so this is the last valid sector.
1482        };
1483        mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1484            .expect("writing req failed");
1485
1486        let mut avail_desc = create_descriptor_chain(
1487            &mem,
1488            GuestAddress(0x100),  // Place descriptor chain at 0x100.
1489            GuestAddress(0x1000), // Describe buffer at 0x1000.
1490            vec![
1491                // Request header
1492                (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1493                // I/O buffer (2 sectors of data - overlap the end of the disk).
1494                (DescriptorType::Writable, 512 * 2),
1495                // Request status
1496                (DescriptorType::Writable, 1),
1497            ],
1498            0,
1499        )
1500        .expect("create_descriptor_chain failed");
1501
1502        let ex = Executor::new().expect("creating an executor failed");
1503
1504        let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1505        let timer = Timer::new().expect("Failed to create a timer");
1506        let flush_timer = Rc::new(RefCell::new(
1507            TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1508        ));
1509        let flush_timer_armed = Rc::new(RefCell::new(false));
1510        let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1511            disk_image: Box::new(af),
1512            read_only: false,
1513            sparse: true,
1514            id: Default::default(),
1515            dontcache_read: false,
1516            dontcache_write: false,
1517            worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1518                disk_size: Arc::new(AtomicU64::new(disk_size)),
1519            })),
1520        }));
1521
1522        let fut = process_one_request(
1523            &mut avail_desc,
1524            &disk_state,
1525            &flush_timer,
1526            &flush_timer_armed,
1527        );
1528
1529        ex.run_until(fut)
1530            .expect("running executor failed")
1531            .expect("execute failed");
1532
1533        let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512 * 2) as u64);
1534        let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1535        assert_eq!(status, VIRTIO_BLK_S_IOERR);
1536    }
1537
1538    #[test]
1539    fn get_id() {
1540        let ex = Executor::new().expect("creating an executor failed");
1541
1542        let f = tempfile().unwrap();
1543        let disk_size = 0x1000;
1544        f.set_len(disk_size).unwrap();
1545
1546        let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1547            .expect("Creating guest memory failed.");
1548
1549        let req_hdr = virtio_blk_req_header {
1550            req_type: Le32::from(VIRTIO_BLK_T_GET_ID),
1551            reserved: Le32::from(0),
1552            sector: Le64::from(0),
1553        };
1554        mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
1555            .expect("writing req failed");
1556
1557        let mut avail_desc = create_descriptor_chain(
1558            &mem,
1559            GuestAddress(0x100),  // Place descriptor chain at 0x100.
1560            GuestAddress(0x1000), // Describe buffer at 0x1000.
1561            vec![
1562                // Request header
1563                (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
1564                // I/O buffer (20 bytes for serial)
1565                (DescriptorType::Writable, 20),
1566                // Request status
1567                (DescriptorType::Writable, 1),
1568            ],
1569            0,
1570        )
1571        .expect("create_descriptor_chain failed");
1572
1573        let af = SingleFileDisk::new(f, &ex).expect("Failed to create SFD");
1574        let timer = Timer::new().expect("Failed to create a timer");
1575        let flush_timer = Rc::new(RefCell::new(
1576            TimerAsync::new(timer, &ex).expect("Failed to create an async timer"),
1577        ));
1578        let flush_timer_armed = Rc::new(RefCell::new(false));
1579
1580        let id = b"a20-byteserialnumber";
1581
1582        let disk_state = Rc::new(AsyncRwLock::new(DiskState {
1583            disk_image: Box::new(af),
1584            read_only: false,
1585            sparse: true,
1586            id: *id,
1587            dontcache_read: false,
1588            dontcache_write: false,
1589            worker_shared_state: Arc::new(AsyncRwLock::new(WorkerSharedState {
1590                disk_size: Arc::new(AtomicU64::new(disk_size)),
1591            })),
1592        }));
1593
1594        let fut = process_one_request(
1595            &mut avail_desc,
1596            &disk_state,
1597            &flush_timer,
1598            &flush_timer_armed,
1599        );
1600
1601        ex.run_until(fut)
1602            .expect("running executor failed")
1603            .expect("execute failed");
1604
1605        let status_offset = GuestAddress((0x1000 + size_of_val(&req_hdr) + 512) as u64);
1606        let status = mem.read_obj_from_addr::<u8>(status_offset).unwrap();
1607        assert_eq!(status, VIRTIO_BLK_S_OK);
1608
1609        let id_offset = GuestAddress(0x1000 + size_of_val(&req_hdr) as u64);
1610        let returned_id = mem.read_obj_from_addr::<[u8; 20]>(id_offset).unwrap();
1611        assert_eq!(returned_id, *id);
1612    }
1613
1614    #[test]
1615    fn reset_and_reactivate_single_worker() {
1616        reset_and_reactivate(false, None);
1617    }
1618
1619    #[test]
1620    fn reset_and_reactivate_multiple_workers() {
1621        reset_and_reactivate(true, None);
1622    }
1623
1624    #[test]
1625    #[cfg(windows)]
1626    fn reset_and_reactivate_overrlapped_io() {
1627        reset_and_reactivate(
1628            false,
1629            Some(
1630                cros_async::sys::windows::ExecutorKindSys::Overlapped { concurrency: None }.into(),
1631            ),
1632        );
1633    }
1634
1635    fn reset_and_reactivate(
1636        enables_multiple_workers: bool,
1637        async_executor: Option<cros_async::ExecutorKind>,
1638    ) {
1639        // Create an empty disk image
1640        let f = tempfile::NamedTempFile::new().unwrap();
1641        f.as_file().set_len(0x1000).unwrap();
1642        // Close the file so that it is possible for the disk implementation to take exclusive
1643        // access when opening it.
1644        let path: tempfile::TempPath = f.into_temp_path();
1645
1646        // Create an empty guest memory
1647        let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1648            .expect("Creating guest memory failed.");
1649
1650        // Create a control tube.
1651        // NOTE: We don't want to drop the vmm half of the tube. That would cause the worker thread
1652        // will immediately fail, which isn't what we want to test in this case.
1653        let (_control_tube, control_tube_device) = Tube::pair().unwrap();
1654
1655        // Create a BlockAsync to test
1656        let features = base_features(ProtectionType::Unprotected);
1657        let id = b"Block serial number\0";
1658        let disk_option = DiskOption {
1659            path: path.to_path_buf(),
1660            read_only: true,
1661            id: Some(*id),
1662            sparse: false,
1663            multiple_workers: enables_multiple_workers,
1664            async_executor,
1665            ..Default::default()
1666        };
1667        let disk_image = disk_option.open().unwrap();
1668        let mut b = BlockAsync::new(
1669            features,
1670            disk_image,
1671            &disk_option,
1672            Some(control_tube_device),
1673            None,
1674            None,
1675        )
1676        .unwrap();
1677
1678        let interrupt = Interrupt::new_for_test();
1679
1680        // activate with queues of an arbitrary size.
1681        let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1682        q0.set_ready(true);
1683        let q0 = q0
1684            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1685            .expect("QueueConfig::activate");
1686
1687        let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1688        q1.set_ready(true);
1689        let q1 = q1
1690            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1691            .expect("QueueConfig::activate");
1692
1693        b.activate(mem.clone(), interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1694            .expect("activate should succeed");
1695        // assert resources are consumed
1696        if !enables_multiple_workers {
1697            assert!(
1698                b.disk_image.is_none(),
1699                "BlockAsync should not have a disk image"
1700            );
1701        }
1702        assert!(
1703            b.control_tube.is_none(),
1704            "BlockAsync should not have a control tube"
1705        );
1706        assert_eq!(
1707            b.worker_threads.len(),
1708            if enables_multiple_workers { 2 } else { 1 }
1709        );
1710
1711        // reset and assert resources are still not back (should be in the worker thread)
1712        assert!(b.reset().is_ok(), "reset should succeed");
1713        if !enables_multiple_workers {
1714            assert!(
1715                b.disk_image.is_none(),
1716                "BlockAsync should not have a disk image"
1717            );
1718        }
1719        assert!(
1720            b.control_tube.is_none(),
1721            "BlockAsync should not have a control tube"
1722        );
1723        assert_eq!(
1724            b.worker_threads.len(),
1725            if enables_multiple_workers { 2 } else { 1 }
1726        );
1727        assert_eq!(b.id, *b"Block serial number\0");
1728
1729        // re-activate should succeed
1730        let interrupt = Interrupt::new_for_test();
1731        let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1732        q0.set_ready(true);
1733        let q0 = q0
1734            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1735            .expect("QueueConfig::activate");
1736
1737        let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1738        q1.set_ready(true);
1739        let q1 = q1
1740            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1741            .expect("QueueConfig::activate");
1742
1743        b.activate(mem, interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1744            .expect("re-activate should succeed");
1745    }
1746
1747    #[test]
1748    fn resize_with_single_worker() {
1749        resize(false);
1750    }
1751
1752    #[test]
1753    fn resize_with_multiple_workers() {
1754        // Test resize handled by one worker affect the whole state
1755        resize(true);
1756    }
1757
1758    fn resize(enables_multiple_workers: bool) {
1759        // disk image size constants
1760        let original_size = 0x1000;
1761        let resized_size = 0x2000;
1762
1763        // Create an empty disk image
1764        let f = tempfile().unwrap();
1765        f.set_len(original_size).unwrap();
1766        let disk_image: Box<dyn DiskFile> = Box::new(f);
1767        assert_eq!(disk_image.get_len().unwrap(), original_size);
1768
1769        // Create an empty guest memory
1770        let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1771            .expect("Creating guest memory failed.");
1772
1773        // Create a control tube
1774        let (control_tube, control_tube_device) = Tube::pair().unwrap();
1775
1776        // Create a BlockAsync to test
1777        let features = base_features(ProtectionType::Unprotected);
1778        let disk_option = DiskOption {
1779            multiple_workers: enables_multiple_workers,
1780            ..Default::default()
1781        };
1782        let mut b = BlockAsync::new(
1783            features,
1784            disk_image.try_clone().unwrap(),
1785            &disk_option,
1786            Some(control_tube_device),
1787            None,
1788            None,
1789        )
1790        .unwrap();
1791
1792        let interrupt = Interrupt::new_for_test();
1793
1794        // activate with queues of an arbitrary size.
1795        let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1796        q0.set_ready(true);
1797        let q0 = q0
1798            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1799            .expect("QueueConfig::activate");
1800
1801        let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1802        q1.set_ready(true);
1803        let q1 = q1
1804            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1805            .expect("QueueConfig::activate");
1806
1807        b.activate(mem, interrupt.clone(), BTreeMap::from([(0, q0), (1, q1)]))
1808            .expect("activate should succeed");
1809
1810        // assert the original size first
1811        assert_eq!(
1812            b.disk_size.load(Ordering::Acquire),
1813            original_size,
1814            "disk_size should be the original size first"
1815        );
1816        let mut capacity = [0u8; 8];
1817        b.read_config(0, &mut capacity);
1818        assert_eq!(
1819            capacity,
1820            // original_size (0x1000) >> SECTOR_SHIFT (9) = 0x8
1821            [0x8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
1822            "read_config should read the original capacity first"
1823        );
1824
1825        // assert resize works
1826        control_tube
1827            .send(&DiskControlCommand::Resize {
1828                new_size: resized_size,
1829            })
1830            .unwrap();
1831        assert_eq!(
1832            control_tube.recv::<DiskControlResult>().unwrap(),
1833            DiskControlResult::Ok,
1834            "resize command should succeed"
1835        );
1836        assert_eq!(
1837            b.disk_size.load(Ordering::Acquire),
1838            resized_size,
1839            "disk_size should be resized to the new size"
1840        );
1841        assert_eq!(
1842            disk_image.get_len().unwrap(),
1843            resized_size,
1844            "underlying disk image should be resized to the new size"
1845        );
1846        let mut capacity = [0u8; 8];
1847        b.read_config(0, &mut capacity);
1848        assert_eq!(
1849            capacity,
1850            // resized_size (0x2000) >> SECTOR_SHIFT (9) = 0x10
1851            [0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
1852            "read_config should read the resized capacity"
1853        );
1854        // Wait until the blk signals the interrupt
1855        interrupt
1856            .get_interrupt_evt()
1857            .wait()
1858            .expect("interrupt should be signaled");
1859
1860        assert_eq!(
1861            interrupt.read_interrupt_status(),
1862            crate::virtio::INTERRUPT_STATUS_CONFIG_CHANGED as u8,
1863            "INTERRUPT_STATUS_CONFIG_CHANGED should be signaled"
1864        );
1865    }
1866
1867    #[test]
1868    fn run_worker_threads() {
1869        // Create an empty duplicable disk image
1870        let f = tempfile().unwrap();
1871        f.set_len(0x1000).unwrap();
1872        let disk_image: Box<dyn DiskFile> = Box::new(f);
1873
1874        // Create an empty guest memory
1875        let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
1876            .expect("Creating guest memory failed.");
1877
1878        // Create a BlockAsync to test with single worker thread
1879        let features = base_features(ProtectionType::Unprotected);
1880        let disk_option = DiskOption::default();
1881        let mut b = BlockAsync::new(
1882            features,
1883            disk_image.try_clone().unwrap(),
1884            &disk_option,
1885            None,
1886            None,
1887            None,
1888        )
1889        .unwrap();
1890
1891        // activate with queues of an arbitrary size.
1892        let interrupt = Interrupt::new_for_test();
1893        let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1894        q0.set_ready(true);
1895        let q0 = q0
1896            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1897            .expect("QueueConfig::activate");
1898
1899        let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1900        q1.set_ready(true);
1901        let q1 = q1
1902            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1903            .expect("QueueConfig::activate");
1904
1905        b.activate(mem.clone(), interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1906            .expect("activate should succeed");
1907
1908        assert_eq!(b.worker_threads.len(), 1, "1 threads should be spawned.");
1909        drop(b);
1910
1911        // Create a BlockAsync to test with multiple worker threads
1912        let features = base_features(ProtectionType::Unprotected);
1913        let disk_option = DiskOption {
1914            read_only: true,
1915            sparse: false,
1916            multiple_workers: true,
1917            ..DiskOption::default()
1918        };
1919        let mut b = BlockAsync::new(features, disk_image, &disk_option, None, None, None).unwrap();
1920
1921        // activate should succeed
1922        let interrupt = Interrupt::new_for_test();
1923        let mut q0 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1924        q0.set_ready(true);
1925        let q0 = q0
1926            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1927            .expect("QueueConfig::activate");
1928
1929        let mut q1 = QueueConfig::new(DEFAULT_QUEUE_SIZE, 0);
1930        q1.set_ready(true);
1931        let q1 = q1
1932            .activate(&mem, Event::new().unwrap(), interrupt.clone())
1933            .expect("QueueConfig::activate");
1934
1935        b.activate(mem, interrupt, BTreeMap::from([(0, q0), (1, q1)]))
1936            .expect("activate should succeed");
1937
1938        assert_eq!(b.worker_threads.len(), 2, "2 threads should be spawned.");
1939    }
1940
1941    struct BlockContext {}
1942
1943    fn modify_device(_block_context: &mut BlockContext, b: &mut BlockAsync) {
1944        b.avail_features = !b.avail_features;
1945    }
1946
1947    fn create_device() -> (BlockContext, BlockAsync) {
1948        // Create an empty disk image
1949        let f = tempfile().unwrap();
1950        f.set_len(0x1000).unwrap();
1951        let disk_image: Box<dyn DiskFile> = Box::new(f);
1952
1953        // Create a BlockAsync to test
1954        let features = base_features(ProtectionType::Unprotected);
1955        let id = b"Block serial number\0";
1956        let disk_option = DiskOption {
1957            read_only: true,
1958            id: Some(*id),
1959            sparse: false,
1960            multiple_workers: true,
1961            ..Default::default()
1962        };
1963        (
1964            BlockContext {},
1965            BlockAsync::new(
1966                features,
1967                disk_image.try_clone().unwrap(),
1968                &disk_option,
1969                None,
1970                None,
1971                None,
1972            )
1973            .unwrap(),
1974        )
1975    }
1976
1977    #[cfg(any(target_os = "android", target_os = "linux"))]
1978    suspendable_virtio_tests!(asyncblock, create_device, 2, modify_device);
1979}