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