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