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