devices/virtio/scsi/
device.rs

1// Copyright 2023 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
5#![deny(missing_docs)]
6//! A SCSI controller has SCSI target(s), a SCSI target has logical unit(s).
7//! crosvm currently supports only one logical unit in a target (LUN0), therefore a SCSI target is
8//! tied to a logical unit and a disk image belongs to a logical unit in crosvm.
9
10use std::cell::RefCell;
11use std::collections::BTreeMap;
12use std::collections::BTreeSet;
13use std::io;
14use std::io::Read;
15use std::io::Write;
16use std::rc::Rc;
17
18use anyhow::Context;
19use base::error;
20use base::warn;
21use base::Event;
22use base::WorkerThread;
23use cros_async::EventAsync;
24use cros_async::Executor;
25use cros_async::ExecutorKind;
26use disk::AsyncDisk;
27use disk::DiskFile;
28use futures::pin_mut;
29use futures::stream::FuturesUnordered;
30use futures::FutureExt;
31use futures::StreamExt;
32use remain::sorted;
33use thiserror::Error as ThisError;
34use virtio_sys::virtio_scsi::virtio_scsi_config;
35use virtio_sys::virtio_scsi::virtio_scsi_ctrl_an_resp;
36use virtio_sys::virtio_scsi::virtio_scsi_ctrl_tmf_req;
37use virtio_sys::virtio_scsi::virtio_scsi_ctrl_tmf_resp;
38use virtio_sys::virtio_scsi::virtio_scsi_event;
39use virtio_sys::virtio_scsi::VIRTIO_SCSI_CDB_DEFAULT_SIZE;
40use virtio_sys::virtio_scsi::VIRTIO_SCSI_SENSE_DEFAULT_SIZE;
41use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_BAD_TARGET;
42use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_FUNCTION_REJECTED;
43use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
44use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_INCORRECT_LUN;
45use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_OK;
46use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_AN_QUERY;
47use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_AN_SUBSCRIBE;
48use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_TMF;
49use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET;
50use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET;
51use vm_memory::GuestMemory;
52use zerocopy::FromBytes;
53use zerocopy::Immutable;
54use zerocopy::IntoBytes;
55use zerocopy::KnownLayout;
56
57use crate::virtio::async_utils;
58use crate::virtio::block::sys::get_seg_max;
59use crate::virtio::copy_config;
60use crate::virtio::scsi::commands::execute_cdb;
61use crate::virtio::scsi::constants::CHECK_CONDITION;
62use crate::virtio::scsi::constants::GOOD;
63use crate::virtio::scsi::constants::ILLEGAL_REQUEST;
64use crate::virtio::scsi::constants::MEDIUM_ERROR;
65use crate::virtio::DescriptorChain;
66use crate::virtio::DeviceType as VirtioDeviceType;
67use crate::virtio::Interrupt;
68use crate::virtio::Queue;
69use crate::virtio::Reader;
70use crate::virtio::VirtioDevice;
71use crate::virtio::Writer;
72
73// The following values reflects the virtio v1.2 spec:
74// <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-3470004>
75
76// Should have one controlq, one eventq, and at least one request queue.
77const MIN_NUM_QUEUES: usize = 3;
78// The number of queues exposed by the device.
79// First crosvm pass this value through `VirtioDevice::read_config`, and then the driver determines
80// the number of queues which does not exceed the passed value. The determined value eventually
81// shows as the length of `queues` in `VirtioDevice::activate`.
82const MAX_NUM_QUEUES: usize = 16;
83// Max channel should be 0.
84const DEFAULT_MAX_CHANNEL: u16 = 0;
85// Max target should be less than or equal to 255.
86const DEFAULT_MAX_TARGET: u16 = 255;
87// Max lun should be less than or equal to 16383
88const DEFAULT_MAX_LUN: u32 = 16383;
89
90const DEFAULT_QUEUE_SIZE: u16 = 1024;
91
92// The maximum number of linked commands.
93const MAX_CMD_PER_LUN: u32 = 1024;
94// We do not set a limit on the transfer size.
95const MAX_SECTORS: u32 = u32::MAX;
96
97// The length of sense data in fixed format. Details are in SPC-3 t10 revision 23:
98// <https://www.t10.org/cgi-bin/ac.pl?t=f&f=spc3r23.pdf>
99const FIXED_FORMAT_SENSE_SIZE: u32 = 18;
100
101#[repr(C, packed)]
102#[derive(Debug, Default, Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
103struct VirtioScsiCmdReqHeader {
104    lun: [u8; 8usize],
105    tag: u64,
106    task_attr: u8,
107    prio: u8,
108    crn: u8,
109}
110
111#[repr(C, packed)]
112#[derive(Debug, Default, Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
113struct VirtioScsiCmdRespHeader {
114    sense_len: u32,
115    resid: u32,
116    status_qualifier: u16,
117    status: u8,
118    response: u8,
119}
120
121impl VirtioScsiCmdRespHeader {
122    fn ok() -> Self {
123        VirtioScsiCmdRespHeader {
124            sense_len: 0,
125            resid: 0,
126            status_qualifier: 0,
127            status: GOOD,
128            response: VIRTIO_SCSI_S_OK as u8,
129        }
130    }
131}
132
133/// Errors that happen while handling scsi commands.
134#[sorted]
135#[derive(ThisError, Debug)]
136pub enum ExecuteError {
137    #[error("invalid cdb field")]
138    InvalidField,
139    #[error("invalid parameter length")]
140    InvalidParamLen,
141    #[error("{xfer_blocks} blocks from LBA {lba} exceeds end of this device {last_lba}")]
142    LbaOutOfRange {
143        lba: u64,
144        xfer_blocks: usize,
145        last_lba: u64,
146    },
147    #[error("failed to read message: {0}")]
148    Read(io::Error),
149    #[error("failed to read command from cdb")]
150    ReadCommand,
151    #[error("io error {resid} bytes remained to be read: {desc_error}")]
152    ReadIo {
153        resid: usize,
154        desc_error: disk::Error,
155    },
156    #[error("writing to a read only device")]
157    ReadOnly,
158    #[error("saving parameters not supported")]
159    SavingParamNotSupported,
160    #[error("synchronization error")]
161    SynchronizationError,
162    #[error("unsupported scsi command: {0}")]
163    Unsupported(u8),
164    #[error("failed to write message: {0}")]
165    Write(io::Error),
166    #[error("io error {resid} bytes remained to be written: {desc_error}")]
167    WriteIo {
168        resid: usize,
169        desc_error: disk::Error,
170    },
171}
172
173impl ExecuteError {
174    // converts ExecuteError to (VirtioScsiCmdReqHeader, Sense)
175    fn as_resp(&self) -> (VirtioScsiCmdRespHeader, Sense) {
176        let resp = VirtioScsiCmdRespHeader::ok();
177        // The asc and ascq assignments are taken from the t10 SPC spec.
178        // cf) Table 28 of <https://www.t10.org/cgi-bin/ac.pl?t=f&f=spc3r23.pdf>
179        let sense = match self {
180            Self::Read(_) | Self::ReadCommand => {
181                // UNRECOVERED READ ERROR
182                Sense {
183                    key: MEDIUM_ERROR,
184                    asc: 0x11,
185                    ascq: 0x00,
186                }
187            }
188            Self::Write(_) => {
189                // WRITE ERROR
190                Sense {
191                    key: MEDIUM_ERROR,
192                    asc: 0x0c,
193                    ascq: 0x00,
194                }
195            }
196            Self::InvalidField => {
197                // INVALID FIELD IN CDB
198                Sense {
199                    key: ILLEGAL_REQUEST,
200                    asc: 0x24,
201                    ascq: 0x00,
202                }
203            }
204            Self::InvalidParamLen => {
205                // INVALID PARAMETER LENGTH
206                Sense {
207                    key: ILLEGAL_REQUEST,
208                    asc: 0x1a,
209                    ascq: 0x00,
210                }
211            }
212            Self::Unsupported(_) => {
213                // INVALID COMMAND OPERATION CODE
214                Sense {
215                    key: ILLEGAL_REQUEST,
216                    asc: 0x20,
217                    ascq: 0x00,
218                }
219            }
220            Self::ReadOnly | Self::LbaOutOfRange { .. } => {
221                // LOGICAL BLOCK ADDRESS OUT OF RANGE
222                Sense {
223                    key: ILLEGAL_REQUEST,
224                    asc: 0x21,
225                    ascq: 0x00,
226                }
227            }
228            Self::SavingParamNotSupported => Sense {
229                // SAVING PARAMETERS NOT SUPPORTED
230                key: ILLEGAL_REQUEST,
231                asc: 0x39,
232                ascq: 0x00,
233            },
234            Self::SynchronizationError => Sense {
235                // SYNCHRONIZATION ERROR
236                key: MEDIUM_ERROR,
237                asc: 0x16,
238                ascq: 0x00,
239            },
240            // Ignore these errors.
241            Self::ReadIo { resid, desc_error } | Self::WriteIo { resid, desc_error } => {
242                warn!("error while performing I/O {}", desc_error);
243                let hdr = VirtioScsiCmdRespHeader {
244                    resid: (*resid).try_into().unwrap_or(u32::MAX).to_be(),
245                    ..resp
246                };
247                return (hdr, Sense::default());
248            }
249        };
250        (
251            VirtioScsiCmdRespHeader {
252                sense_len: FIXED_FORMAT_SENSE_SIZE,
253                status: CHECK_CONDITION,
254                ..resp
255            },
256            sense,
257        )
258    }
259}
260
261/// Sense code representation
262#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
263pub struct Sense {
264    /// Provides generic information describing an error or exception condition.
265    pub key: u8,
266    /// Additional Sense Code.
267    /// Indicates further information related to the error or exception reported in the key field.
268    pub asc: u8,
269    /// Additional Sense Code Qualifier.
270    /// Indicates further detailed information related to the additional sense code.
271    pub ascq: u8,
272}
273
274impl Sense {
275    fn write_to(&self, writer: &mut Writer, sense_size: u32) -> Result<(), ExecuteError> {
276        let mut sense_data = [0u8; FIXED_FORMAT_SENSE_SIZE as usize];
277        // Fixed format sense data has response code:
278        // 1) 0x70 for current errors
279        // 2) 0x71 for deferred errors
280        sense_data[0] = 0x70;
281        // sense_data[1]: Obsolete
282        // Sense key
283        sense_data[2] = self.key;
284        // sense_data[3..7]: Information field, which we do not support.
285        // Additional length. The data is 18 bytes, and this byte is 8th.
286        sense_data[7] = 10;
287        // sense_data[8..12]: Command specific information, which we do not support.
288        // Additional sense code
289        sense_data[12] = self.asc;
290        // Additional sense code qualifier
291        sense_data[13] = self.ascq;
292        // sense_data[14]: Field replaceable unit code, which we do not support.
293        // sense_data[15..18]: Field replaceable unit code, which we do not support.
294        writer.write_all(&sense_data).map_err(ExecuteError::Write)?;
295        writer.consume_bytes(sense_size as usize - sense_data.len());
296        Ok(())
297    }
298}
299
300/// Describes each SCSI logical unit.
301struct LogicalUnit {
302    /// The logical block address of the last logical block on the target device.
303    last_lba: u64,
304    /// Block size of the target device.
305    block_size: u32,
306    read_only: bool,
307    // Represents the image on disk.
308    disk_image: Box<dyn DiskFile>,
309}
310
311impl LogicalUnit {
312    fn make_async(self, ex: &Executor) -> anyhow::Result<AsyncLogicalUnit> {
313        let disk_image = self
314            .disk_image
315            .to_async_disk(ex)
316            .context("Failed to create async disk")?;
317        Ok(AsyncLogicalUnit {
318            last_lba: self.last_lba,
319            block_size: self.block_size,
320            read_only: self.read_only,
321            disk_image,
322        })
323    }
324}
325
326/// A logical unit with an AsyncDisk as the disk.
327pub struct AsyncLogicalUnit {
328    pub last_lba: u64,
329    pub block_size: u32,
330    pub read_only: bool,
331    // Represents the async image on disk.
332    pub disk_image: Box<dyn AsyncDisk>,
333}
334
335type TargetId = u8;
336struct Targets(BTreeMap<TargetId, LogicalUnit>);
337
338impl Targets {
339    fn try_clone(&self) -> io::Result<Self> {
340        let logical_units = self
341            .0
342            .iter()
343            .map(|(id, logical_unit)| {
344                let disk_image = logical_unit.disk_image.try_clone()?;
345                Ok((
346                    *id,
347                    LogicalUnit {
348                        disk_image,
349                        last_lba: logical_unit.last_lba,
350                        block_size: logical_unit.block_size,
351                        read_only: logical_unit.read_only,
352                    },
353                ))
354            })
355            .collect::<io::Result<_>>()?;
356        Ok(Self(logical_units))
357    }
358
359    fn target_ids(&self) -> BTreeSet<TargetId> {
360        self.0.keys().cloned().collect()
361    }
362}
363
364/// Configuration of each SCSI device.
365pub struct DiskConfig {
366    /// The disk file of the device.
367    pub file: Box<dyn DiskFile>,
368    /// The block size of the SCSI disk.
369    pub block_size: u32,
370    /// Indicates whether the SCSI disk is read only.
371    pub read_only: bool,
372}
373
374/// Vitio device for exposing SCSI command operations on a host file.
375pub struct Controller {
376    // Bitmap of virtio-scsi feature bits.
377    avail_features: u64,
378    // Sizes for the virtqueue.
379    queue_sizes: Vec<u16>,
380    // The maximum number of segments that can be in a command.
381    seg_max: u32,
382    // The size of the sense data.
383    sense_size: u32,
384    // The byte size of the CDB that the driver will write.
385    cdb_size: u32,
386    executor_kind: ExecutorKind,
387    worker_threads: Vec<WorkerThread<()>>,
388    // Stores target devices by its target id. Currently we only support bus id 0.
389    targets: Option<Targets>,
390    // Whether the devices handles requests in multiple request queues.
391    // If true, each virtqueue will be handled in a separate worker thread.
392    multi_queue: bool,
393}
394
395impl Controller {
396    /// Creates a virtio-scsi device.
397    pub fn new(base_features: u64, disks: Vec<DiskConfig>) -> anyhow::Result<Self> {
398        let multi_queue = disks.iter().all(|disk| disk.file.try_clone().is_ok());
399        let num_queues = if multi_queue {
400            MAX_NUM_QUEUES
401        } else {
402            MIN_NUM_QUEUES
403        };
404        let logical_units = disks
405            .into_iter()
406            .enumerate()
407            .map(|(i, disk)| {
408                let num_blocks = disk
409                    .file
410                    .get_len()
411                    .context("Failed to get the length of the disk image")?
412                    / disk.block_size as u64;
413                let last_lba = num_blocks
414                    .checked_sub(1)
415                    .context("Invalid zero-length SCSI LUN")?;
416                let target = LogicalUnit {
417                    last_lba,
418                    block_size: disk.block_size,
419                    read_only: disk.read_only,
420                    disk_image: disk.file,
421                };
422                Ok((i as TargetId, target))
423            })
424            .collect::<anyhow::Result<_>>()?;
425        // b/300560198: Support feature bits in virtio-scsi.
426        Ok(Self {
427            avail_features: base_features,
428            queue_sizes: vec![DEFAULT_QUEUE_SIZE; num_queues],
429            seg_max: get_seg_max(DEFAULT_QUEUE_SIZE),
430            sense_size: VIRTIO_SCSI_SENSE_DEFAULT_SIZE,
431            cdb_size: VIRTIO_SCSI_CDB_DEFAULT_SIZE,
432            executor_kind: ExecutorKind::default(),
433            worker_threads: vec![],
434            targets: Some(Targets(logical_units)),
435            multi_queue,
436        })
437    }
438
439    fn build_config_space(&self) -> virtio_scsi_config {
440        virtio_scsi_config {
441            // num_queues is the number of request queues only so we subtract 2 for the control
442            // queue and the event queue.
443            num_queues: self.queue_sizes.len() as u32 - 2,
444            seg_max: self.seg_max,
445            max_sectors: MAX_SECTORS,
446            cmd_per_lun: MAX_CMD_PER_LUN,
447            event_info_size: std::mem::size_of::<virtio_scsi_event>() as u32,
448            sense_size: self.sense_size,
449            cdb_size: self.cdb_size,
450            max_channel: DEFAULT_MAX_CHANNEL,
451            max_target: DEFAULT_MAX_TARGET,
452            max_lun: DEFAULT_MAX_LUN,
453        }
454    }
455
456    // Executes a request in the controlq.
457    fn execute_control(
458        reader: &mut Reader,
459        writer: &mut Writer,
460        target_ids: &BTreeSet<TargetId>,
461    ) -> Result<(), ExecuteError> {
462        let typ = reader.peek_obj::<u32>().map_err(ExecuteError::Read)?;
463        match typ {
464            VIRTIO_SCSI_T_TMF => {
465                let tmf = reader
466                    .read_obj::<virtio_scsi_ctrl_tmf_req>()
467                    .map_err(ExecuteError::Read)?;
468                let resp = Self::execute_tmf(tmf, target_ids);
469                writer.write_obj(resp).map_err(ExecuteError::Write)?;
470                Ok(())
471            }
472            VIRTIO_SCSI_T_AN_QUERY | VIRTIO_SCSI_T_AN_SUBSCRIBE => {
473                // We do not support any asynchronous notification queries hence `event_actual`
474                // will be 0.
475                let resp = virtio_scsi_ctrl_an_resp {
476                    event_actual: 0,
477                    response: VIRTIO_SCSI_S_OK as u8,
478                };
479                writer.write_obj(resp).map_err(ExecuteError::Write)?;
480                Ok(())
481            }
482            _ => {
483                error!("invalid type of a control request: {typ}");
484                Err(ExecuteError::InvalidField)
485            }
486        }
487    }
488
489    // Executes a TMF (task management function) request.
490    fn execute_tmf(
491        tmf: virtio_scsi_ctrl_tmf_req,
492        target_ids: &BTreeSet<TargetId>,
493    ) -> virtio_scsi_ctrl_tmf_resp {
494        match tmf.subtype {
495            VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET | VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET => {
496                // We only have LUN0.
497                let lun = tmf.lun;
498                let target_id = lun[1];
499                let response = if target_ids.contains(&target_id) {
500                    let is_lun0 = u16::from_be_bytes([lun[2], lun[3]]) & 0x3fff == 0;
501                    if is_lun0 {
502                        VIRTIO_SCSI_S_FUNCTION_SUCCEEDED as u8
503                    } else {
504                        VIRTIO_SCSI_S_INCORRECT_LUN as u8
505                    }
506                } else {
507                    VIRTIO_SCSI_S_BAD_TARGET as u8
508                };
509                virtio_scsi_ctrl_tmf_resp { response }
510            }
511            subtype => {
512                error!("TMF request {subtype} is not supported");
513                virtio_scsi_ctrl_tmf_resp {
514                    response: VIRTIO_SCSI_S_FUNCTION_REJECTED as u8,
515                }
516            }
517        }
518    }
519
520    async fn execute_request(
521        reader: &mut Reader,
522        resp_writer: &mut Writer,
523        data_writer: &mut Writer,
524        targets: &BTreeMap<TargetId, AsyncLogicalUnit>,
525        sense_size: u32,
526        cdb_size: u32,
527    ) -> Result<(), ExecuteError> {
528        let req_header = reader
529            .read_obj::<VirtioScsiCmdReqHeader>()
530            .map_err(ExecuteError::Read)?;
531        match Self::get_logical_unit(req_header.lun, targets) {
532            Some(target) => {
533                let mut cdb = vec![0; cdb_size as usize];
534                reader.read_exact(&mut cdb).map_err(ExecuteError::Read)?;
535                match execute_cdb(&cdb, reader, data_writer, target).await {
536                    Ok(()) => {
537                        let hdr = VirtioScsiCmdRespHeader {
538                            sense_len: 0,
539                            resid: 0,
540                            status_qualifier: 0,
541                            status: GOOD,
542                            response: VIRTIO_SCSI_S_OK as u8,
543                        };
544                        resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
545                        resp_writer.consume_bytes(sense_size as usize);
546                        Ok(())
547                    }
548                    Err(err) => {
549                        error!("error while executing a scsi request: {err}");
550                        let (hdr, sense) = err.as_resp();
551                        resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
552                        sense.write_to(resp_writer, sense_size)
553                    }
554                }
555            }
556            None => {
557                let hdr = VirtioScsiCmdRespHeader {
558                    response: VIRTIO_SCSI_S_BAD_TARGET as u8,
559                    ..Default::default()
560                };
561                resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
562                resp_writer.consume_bytes(sense_size as usize);
563                Ok(())
564            }
565        }
566    }
567
568    fn get_logical_unit(
569        lun: [u8; 8],
570        targets: &BTreeMap<TargetId, AsyncLogicalUnit>,
571    ) -> Option<&AsyncLogicalUnit> {
572        // First byte should be 1.
573        if lun[0] != 1 {
574            return None;
575        }
576        // General search strategy for scsi devices is as follows:
577        // 1) Look for a device which has the same bus id and lun indicated by the given lun. If
578        //    there is one, that is the target device.
579        // 2) If we cannot find such device, then we return the first device that has the same bus
580        //    id.
581        // Since we only support one LUN per target, we only need to use the target id.
582        let target_id = lun[1];
583        targets.get(&target_id)
584    }
585}
586
587impl VirtioDevice for Controller {
588    fn keep_rds(&self) -> Vec<base::RawDescriptor> {
589        match &self.targets {
590            Some(targets) => targets
591                .0
592                .values()
593                .flat_map(|t| t.disk_image.as_raw_descriptors())
594                .collect(),
595            None => vec![],
596        }
597    }
598
599    fn features(&self) -> u64 {
600        self.avail_features
601    }
602
603    fn device_type(&self) -> VirtioDeviceType {
604        VirtioDeviceType::Scsi
605    }
606
607    fn queue_max_sizes(&self) -> &[u16] {
608        &self.queue_sizes
609    }
610
611    fn read_config(&self, offset: u64, data: &mut [u8]) {
612        let config_space = self.build_config_space();
613        copy_config(data, 0, config_space.as_bytes(), offset);
614    }
615
616    fn write_config(&mut self, offset: u64, data: &[u8]) {
617        let mut config = self.build_config_space();
618        copy_config(config.as_mut_bytes(), offset, data, 0);
619        // Only `sense_size` and `cdb_size` are modifiable by the driver.
620        self.sense_size = config.sense_size;
621        self.cdb_size = config.cdb_size;
622    }
623
624    fn activate(
625        &mut self,
626        _mem: GuestMemory,
627        _interrupt: Interrupt,
628        mut queues: BTreeMap<usize, Queue>,
629    ) -> anyhow::Result<()> {
630        let executor_kind = self.executor_kind;
631        // 0th virtqueue is the controlq.
632        let controlq = queues.remove(&0).context("controlq should be present")?;
633        // 1st virtqueue is the eventq.
634        // We do not send any events through eventq.
635        let _eventq = queues.remove(&1).context("eventq should be present")?;
636        let targets = self.targets.take().context("failed to take SCSI targets")?;
637        let target_ids = targets.target_ids();
638        let sense_size = self.sense_size;
639        let cdb_size = self.cdb_size;
640        // The rest of the queues are request queues.
641        let request_queues = if self.multi_queue {
642            queues
643                .into_values()
644                .map(|queue| {
645                    let targets = targets
646                        .try_clone()
647                        .context("Failed to clone a disk image")?;
648                    Ok((queue, targets))
649                })
650                .collect::<anyhow::Result<_>>()?
651        } else {
652            // Handle all virtio requests with one thread.
653            vec![(
654                queues
655                    .remove(&2)
656                    .context("request queue should be present")?,
657                targets,
658            )]
659        };
660
661        let worker_thread = WorkerThread::start("v_scsi_ctrlq", move |kill_evt| {
662            let ex =
663                Executor::with_executor_kind(executor_kind).expect("Failed to create an executor");
664            if let Err(err) = ex
665                .run_until(run_worker(
666                    &ex,
667                    controlq,
668                    kill_evt,
669                    QueueType::Control { target_ids },
670                    sense_size,
671                    cdb_size,
672                ))
673                .expect("run_until failed")
674            {
675                error!("run_worker failed: {err}");
676            }
677        });
678        self.worker_threads.push(worker_thread);
679
680        for (i, (queue, targets)) in request_queues.into_iter().enumerate() {
681            let worker_thread =
682                WorkerThread::start(format!("v_scsi_req_{}", i + 2), move |kill_evt| {
683                    let ex = Executor::with_executor_kind(executor_kind)
684                        .expect("Failed to create an executor");
685                    let async_logical_unit = targets
686                        .0
687                        .into_iter()
688                        .map(|(idx, unit)| match unit.make_async(&ex) {
689                            Ok(async_unit) => (idx, async_unit),
690                            Err(err) => panic!("{err}"),
691                        })
692                        .collect();
693                    if let Err(err) = ex
694                        .run_until(run_worker(
695                            &ex,
696                            queue,
697                            kill_evt,
698                            QueueType::Request(async_logical_unit),
699                            sense_size,
700                            cdb_size,
701                        ))
702                        .expect("run_until failed")
703                    {
704                        error!("run_worker failed: {err}");
705                    }
706                });
707            self.worker_threads.push(worker_thread);
708        }
709        Ok(())
710    }
711}
712
713enum QueueType {
714    Control { target_ids: BTreeSet<TargetId> },
715    Request(BTreeMap<TargetId, AsyncLogicalUnit>),
716}
717
718async fn run_worker(
719    ex: &Executor,
720    queue: Queue,
721    kill_evt: Event,
722    queue_type: QueueType,
723    sense_size: u32,
724    cdb_size: u32,
725) -> anyhow::Result<()> {
726    let kill = async_utils::await_and_exit(ex, kill_evt).fuse();
727    pin_mut!(kill);
728
729    let kick_evt = queue
730        .event()
731        .try_clone()
732        .expect("Failed to clone queue event");
733    let queue_handler = handle_queue(
734        Rc::new(RefCell::new(queue)),
735        EventAsync::new(kick_evt, ex).expect("Failed to create async event for queue"),
736        queue_type,
737        sense_size,
738        cdb_size,
739    )
740    .fuse();
741    pin_mut!(queue_handler);
742
743    futures::select! {
744        _ = queue_handler => anyhow::bail!("queue handler exited unexpectedly"),
745        r = kill => r.context("failed to wait on the kill event"),
746    }
747}
748
749async fn handle_queue(
750    queue: Rc<RefCell<Queue>>,
751    evt: EventAsync,
752    queue_type: QueueType,
753    sense_size: u32,
754    cdb_size: u32,
755) {
756    let mut background_tasks = FuturesUnordered::new();
757    let evt_future = evt.next_val().fuse();
758    pin_mut!(evt_future);
759    loop {
760        futures::select! {
761            _ = background_tasks.next() => continue,
762            res = evt_future => {
763                evt_future.set(evt.next_val().fuse());
764                if let Err(e) = res {
765                    error!("Failed to read the next queue event: {e}");
766                    continue;
767                }
768            }
769        }
770        while let Some(chain) = queue.borrow_mut().pop() {
771            background_tasks.push(process_one_chain(
772                &queue,
773                chain,
774                &queue_type,
775                sense_size,
776                cdb_size,
777            ));
778        }
779    }
780}
781
782async fn process_one_chain(
783    queue: &RefCell<Queue>,
784    mut avail_desc: DescriptorChain,
785    queue_type: &QueueType,
786    sense_size: u32,
787    cdb_size: u32,
788) {
789    let _trace = cros_tracing::trace_event!(VirtioScsi, "process_one_chain");
790    let len = process_one_request(&mut avail_desc, queue_type, sense_size, cdb_size).await;
791    let mut queue = queue.borrow_mut();
792    queue.add_used_with_bytes_written(avail_desc, len as u32);
793    queue.trigger_interrupt();
794}
795
796async fn process_one_request(
797    avail_desc: &mut DescriptorChain,
798    queue_type: &QueueType,
799    sense_size: u32,
800    cdb_size: u32,
801) -> usize {
802    let reader = &mut avail_desc.reader;
803    let resp_writer = &mut avail_desc.writer;
804    match queue_type {
805        QueueType::Control { target_ids } => {
806            if let Err(err) = Controller::execute_control(reader, resp_writer, target_ids) {
807                error!("failed to execute control request: {err}");
808            }
809            resp_writer.bytes_written()
810        }
811        QueueType::Request(async_targets) => {
812            let mut data_writer = resp_writer
813                .split_at(std::mem::size_of::<VirtioScsiCmdRespHeader>() + sense_size as usize);
814            if let Err(err) = Controller::execute_request(
815                reader,
816                resp_writer,
817                &mut data_writer,
818                async_targets,
819                sense_size,
820                cdb_size,
821            )
822            .await
823            {
824                // If the write of the virtio_scsi_cmd_resp fails, there is nothing we can do to
825                // inform the error to the guest driver (we usually propagate errors with sense
826                // field, which is in the struct virtio_scsi_cmd_resp). The guest driver should
827                // have at least sizeof(virtio_scsi_cmd_resp) bytes of device-writable part
828                // regions. For now we simply emit an error message.
829                let (hdr, sense) = err.as_resp();
830                if let Err(e) = resp_writer.write_obj(hdr) {
831                    error!("failed to write VirtioScsiCmdRespHeader: {e}");
832                }
833                if let Err(e) = sense.write_to(resp_writer, sense_size) {
834                    error!("failed to write sense data: {e}");
835                }
836            }
837            resp_writer.bytes_written() + data_writer.bytes_written()
838        }
839    }
840}
841
842#[cfg(test)]
843mod tests {
844    use std::fs::File;
845    use std::mem::size_of;
846    use std::mem::size_of_val;
847    use std::rc::Rc;
848
849    use cros_async::Executor;
850    use disk::SingleFileDisk;
851    use tempfile::tempfile;
852    use virtio_sys::virtio_scsi::virtio_scsi_cmd_req;
853    use virtio_sys::virtio_scsi::virtio_scsi_cmd_resp;
854    use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_OK;
855    use vm_memory::GuestAddress;
856    use vm_memory::GuestMemory;
857
858    use super::*;
859    use crate::virtio::create_descriptor_chain;
860    use crate::virtio::scsi::constants::READ_10;
861    use crate::virtio::DescriptorType;
862
863    fn setup_disk(disk_size: u64) -> (File, Vec<u8>) {
864        let mut file_content = vec![0; disk_size as usize];
865        for i in 0..disk_size {
866            file_content[i as usize] = (i % 10) as u8;
867        }
868        let mut f = tempfile().unwrap();
869        f.set_len(disk_size).unwrap();
870        f.write_all(file_content.as_slice()).unwrap();
871        (f, file_content)
872    }
873
874    fn build_read_req_header(target_id: u8, start_lba: u8, xfer_blocks: u8) -> virtio_scsi_cmd_req {
875        let mut cdb = [0; 32];
876        cdb[0] = READ_10;
877        cdb[5] = start_lba;
878        cdb[8] = xfer_blocks;
879        virtio_scsi_cmd_req {
880            lun: [1, 0, 0, target_id, 0, 0, 0, 0],
881            cdb,
882            ..Default::default()
883        }
884    }
885
886    fn setup_desciptor_chain(
887        target_id: TargetId,
888        start_lba: u8,
889        xfer_blocks: u8,
890        block_size: u32,
891        mem: &Rc<GuestMemory>,
892    ) -> DescriptorChain {
893        let req_hdr = build_read_req_header(target_id, start_lba, xfer_blocks);
894        let xfer_bytes = xfer_blocks as u32 * block_size;
895        create_descriptor_chain(
896            mem,
897            GuestAddress(0x100),  // Place descriptor chain at 0x100.
898            GuestAddress(0x1000), // Describe buffer at 0x1000.
899            vec![
900                // Request header
901                (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
902                // Response header
903                (
904                    DescriptorType::Writable,
905                    size_of::<virtio_scsi_cmd_resp>() as u32,
906                ),
907                (DescriptorType::Writable, xfer_bytes),
908            ],
909            0,
910        )
911        .expect("create_descriptor_chain failed")
912    }
913
914    fn read_blocks(
915        ex: &Executor,
916        file_disks: &[File],
917        target_id: u8,
918        start_lba: u8,
919        xfer_blocks: u8,
920        block_size: u32,
921    ) -> (virtio_scsi_cmd_resp, Vec<u8>) {
922        let xfer_bytes = xfer_blocks as u32 * block_size;
923        let mem = Rc::new(
924            GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
925                .expect("Creating guest memory failed."),
926        );
927        let req_hdr = build_read_req_header(target_id, start_lba, xfer_blocks);
928        mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
929            .expect("writing req failed");
930
931        let mut avail_desc = setup_desciptor_chain(target_id, 0, xfer_blocks, block_size, &mem);
932
933        let targets = file_disks
934            .iter()
935            .enumerate()
936            .map(|(i, file)| {
937                let file = file.try_clone().unwrap();
938                let disk_image = Box::new(SingleFileDisk::new(file, ex).unwrap());
939                let logical_unit = AsyncLogicalUnit {
940                    last_lba: 0xFFF,
941                    block_size,
942                    read_only: false,
943                    disk_image,
944                };
945                (i as TargetId, logical_unit)
946            })
947            .collect();
948        ex.run_until(process_one_request(
949            &mut avail_desc,
950            &QueueType::Request(targets),
951            VIRTIO_SCSI_SENSE_DEFAULT_SIZE,
952            VIRTIO_SCSI_CDB_DEFAULT_SIZE,
953        ))
954        .expect("running executor failed");
955        let resp_offset = GuestAddress((0x1000 + size_of::<virtio_scsi_cmd_resp>()) as u64);
956        let resp = mem
957            .read_obj_from_addr::<virtio_scsi_cmd_resp>(resp_offset)
958            .unwrap();
959        let dataout_offset = GuestAddress(
960            (0x1000 + size_of::<virtio_scsi_cmd_req>() + size_of::<virtio_scsi_cmd_resp>()) as u64,
961        );
962        let dataout_slice = mem
963            .get_slice_at_addr(dataout_offset, xfer_bytes as usize)
964            .unwrap();
965        let mut dataout = vec![0; xfer_bytes as usize];
966        dataout_slice.copy_to(&mut dataout);
967        (resp, dataout)
968    }
969
970    fn test_read_blocks(
971        num_targets: usize,
972        blocks: u8,
973        start_lba: u8,
974        xfer_blocks: u8,
975        block_size: u32,
976    ) {
977        let ex = Executor::new().expect("creating an executor failed");
978        let file_len = blocks as u64 * block_size as u64;
979        let xfer_bytes = xfer_blocks as usize * block_size as usize;
980        let start_off = start_lba as usize * block_size as usize;
981
982        let (files, file_contents): (Vec<_>, Vec<_>) =
983            (0..num_targets).map(|_| setup_disk(file_len)).unzip();
984        for (target_id, file_content) in file_contents.iter().enumerate() {
985            let (resp, dataout) = read_blocks(
986                &ex,
987                &files,
988                target_id as TargetId,
989                start_lba,
990                xfer_blocks,
991                block_size,
992            );
993
994            let sense_len = resp.sense_len;
995            assert_eq!(sense_len, 0);
996            assert_eq!(resp.status, VIRTIO_SCSI_S_OK as u8);
997            assert_eq!(resp.response, GOOD);
998
999            assert_eq!(&dataout, &file_content[start_off..(start_off + xfer_bytes)]);
1000        }
1001    }
1002
1003    #[test]
1004    fn read_first_blocks() {
1005        // Read the first 3 blocks of a 8-block device.
1006        let blocks = 8u8;
1007        let start_lba = 0u8;
1008        let xfer_blocks = 3u8;
1009
1010        test_read_blocks(1, blocks, start_lba, xfer_blocks, 64u32);
1011        test_read_blocks(1, blocks, start_lba, xfer_blocks, 128u32);
1012        test_read_blocks(1, blocks, start_lba, xfer_blocks, 512u32);
1013    }
1014
1015    #[test]
1016    fn read_middle_blocks() {
1017        // Read 3 blocks from the 2nd block in the 8-block device.
1018        let blocks = 8u8;
1019        let start_lba = 1u8;
1020        let xfer_blocks = 3u8;
1021
1022        test_read_blocks(1, blocks, start_lba, xfer_blocks, 64u32);
1023        test_read_blocks(1, blocks, start_lba, xfer_blocks, 128u32);
1024        test_read_blocks(1, blocks, start_lba, xfer_blocks, 512u32);
1025    }
1026
1027    #[test]
1028    fn read_first_blocks_with_multiple_disks() {
1029        // Read the first 3 blocks of a 8-block device.
1030        let blocks = 8u8;
1031        let start_lba = 0u8;
1032        let xfer_blocks = 3u8;
1033
1034        test_read_blocks(3, blocks, start_lba, xfer_blocks, 64u32);
1035        test_read_blocks(3, blocks, start_lba, xfer_blocks, 128u32);
1036        test_read_blocks(3, blocks, start_lba, xfer_blocks, 512u32);
1037    }
1038
1039    #[test]
1040    fn read_middle_blocks_with_multiple_disks() {
1041        // Read 3 blocks from the 2nd block in the 8-block device.
1042        let blocks = 8u8;
1043        let start_lba = 1u8;
1044        let xfer_blocks = 3u8;
1045
1046        test_read_blocks(3, blocks, start_lba, xfer_blocks, 64u32);
1047        test_read_blocks(3, blocks, start_lba, xfer_blocks, 128u32);
1048        test_read_blocks(3, blocks, start_lba, xfer_blocks, 512u32);
1049    }
1050}