• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::FromBytes;
53 use zerocopy::Immutable;
54 use zerocopy::IntoBytes;
55 use zerocopy::KnownLayout;
56 
57 use crate::virtio::async_utils;
58 use crate::virtio::block::sys::get_seg_max;
59 use crate::virtio::copy_config;
60 use crate::virtio::scsi::commands::Command;
61 use crate::virtio::scsi::constants::CHECK_CONDITION;
62 use crate::virtio::scsi::constants::GOOD;
63 use crate::virtio::scsi::constants::ILLEGAL_REQUEST;
64 use crate::virtio::scsi::constants::MEDIUM_ERROR;
65 use crate::virtio::DescriptorChain;
66 use crate::virtio::DeviceType as VirtioDeviceType;
67 use crate::virtio::Interrupt;
68 use crate::virtio::Queue;
69 use crate::virtio::Reader;
70 use crate::virtio::VirtioDevice;
71 use 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.
77 const 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`.
82 const MAX_NUM_QUEUES: usize = 16;
83 // Max channel should be 0.
84 const DEFAULT_MAX_CHANNEL: u16 = 0;
85 // Max target should be less than or equal to 255.
86 const DEFAULT_MAX_TARGET: u16 = 255;
87 // Max lun should be less than or equal to 16383
88 const DEFAULT_MAX_LUN: u32 = 16383;
89 
90 const DEFAULT_QUEUE_SIZE: u16 = 1024;
91 
92 // The maximum number of linked commands.
93 const MAX_CMD_PER_LUN: u32 = 1024;
94 // We do not set a limit on the transfer size.
95 const 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>
99 const FIXED_FORMAT_SENSE_SIZE: u32 = 18;
100 
101 #[repr(C, packed)]
102 #[derive(Debug, Default, Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
103 struct 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)]
113 struct VirtioScsiCmdRespHeader {
114     sense_len: u32,
115     resid: u32,
116     status_qualifier: u16,
117     status: u8,
118     response: u8,
119 }
120 
121 impl VirtioScsiCmdRespHeader {
ok() -> Self122     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)]
136 pub enum ExecuteError {
137     #[error("invalid cdb field")]
138     InvalidField,
139     #[error("invalid parameter length")]
140     InvalidParamLen,
141     #[error("{length} bytes from sector {sector} exceeds end of this device {max_lba}")]
142     LbaOutOfRange {
143         length: usize,
144         sector: u64,
145         max_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 
173 impl ExecuteError {
174     // converts ExecuteError to (VirtioScsiCmdReqHeader, Sense)
as_resp(&self) -> (VirtioScsiCmdRespHeader, 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)]
263 pub 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 
274 impl Sense {
write_to(&self, writer: &mut Writer, sense_size: u32) -> Result<(), ExecuteError>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.
301 struct LogicalUnit {
302     /// The maximum logical block address of the target device.
303     max_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 
311 impl LogicalUnit {
make_async(self, ex: &Executor) -> anyhow::Result<AsyncLogicalUnit>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             max_lba: self.max_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.
327 pub struct AsyncLogicalUnit {
328     pub max_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 
335 type TargetId = u8;
336 struct Targets(BTreeMap<TargetId, LogicalUnit>);
337 
338 impl Targets {
try_clone(&self) -> io::Result<Self>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                         max_lba: logical_unit.max_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 
target_ids(&self) -> BTreeSet<TargetId>359     fn target_ids(&self) -> BTreeSet<TargetId> {
360         self.0.keys().cloned().collect()
361     }
362 }
363 
364 /// Configuration of each SCSI device.
365 pub 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.
375 pub 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 
395 impl Controller {
396     /// Creates a virtio-scsi device.
new(base_features: u64, disks: Vec<DiskConfig>) -> anyhow::Result<Self>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 max_lba = 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 target = LogicalUnit {
414                     max_lba,
415                     block_size: disk.block_size,
416                     read_only: disk.read_only,
417                     disk_image: disk.file,
418                 };
419                 Ok((i as TargetId, target))
420             })
421             .collect::<anyhow::Result<_>>()?;
422         // b/300560198: Support feature bits in virtio-scsi.
423         Ok(Self {
424             avail_features: base_features,
425             queue_sizes: vec![DEFAULT_QUEUE_SIZE; num_queues],
426             seg_max: get_seg_max(DEFAULT_QUEUE_SIZE),
427             sense_size: VIRTIO_SCSI_SENSE_DEFAULT_SIZE,
428             cdb_size: VIRTIO_SCSI_CDB_DEFAULT_SIZE,
429             executor_kind: ExecutorKind::default(),
430             worker_threads: vec![],
431             targets: Some(Targets(logical_units)),
432             multi_queue,
433         })
434     }
435 
build_config_space(&self) -> virtio_scsi_config436     fn build_config_space(&self) -> virtio_scsi_config {
437         virtio_scsi_config {
438             // num_queues is the number of request queues only so we subtract 2 for the control
439             // queue and the event queue.
440             num_queues: self.queue_sizes.len() as u32 - 2,
441             seg_max: self.seg_max,
442             max_sectors: MAX_SECTORS,
443             cmd_per_lun: MAX_CMD_PER_LUN,
444             event_info_size: std::mem::size_of::<virtio_scsi_event>() as u32,
445             sense_size: self.sense_size,
446             cdb_size: self.cdb_size,
447             max_channel: DEFAULT_MAX_CHANNEL,
448             max_target: DEFAULT_MAX_TARGET,
449             max_lun: DEFAULT_MAX_LUN,
450         }
451     }
452 
453     // Executes a request in the controlq.
execute_control( reader: &mut Reader, writer: &mut Writer, target_ids: &BTreeSet<TargetId>, ) -> Result<(), ExecuteError>454     fn execute_control(
455         reader: &mut Reader,
456         writer: &mut Writer,
457         target_ids: &BTreeSet<TargetId>,
458     ) -> Result<(), ExecuteError> {
459         let typ = reader.peek_obj::<u32>().map_err(ExecuteError::Read)?;
460         match typ {
461             VIRTIO_SCSI_T_TMF => {
462                 let tmf = reader
463                     .read_obj::<virtio_scsi_ctrl_tmf_req>()
464                     .map_err(ExecuteError::Read)?;
465                 let resp = Self::execute_tmf(tmf, target_ids);
466                 writer.write_obj(resp).map_err(ExecuteError::Write)?;
467                 Ok(())
468             }
469             VIRTIO_SCSI_T_AN_QUERY | VIRTIO_SCSI_T_AN_SUBSCRIBE => {
470                 // We do not support any asynchronous notification queries hence `event_actual`
471                 // will be 0.
472                 let resp = virtio_scsi_ctrl_an_resp {
473                     event_actual: 0,
474                     response: VIRTIO_SCSI_S_OK as u8,
475                 };
476                 writer.write_obj(resp).map_err(ExecuteError::Write)?;
477                 Ok(())
478             }
479             _ => {
480                 error!("invalid type of a control request: {typ}");
481                 Err(ExecuteError::InvalidField)
482             }
483         }
484     }
485 
486     // Executes a TMF (task management function) request.
execute_tmf( tmf: virtio_scsi_ctrl_tmf_req, target_ids: &BTreeSet<TargetId>, ) -> virtio_scsi_ctrl_tmf_resp487     fn execute_tmf(
488         tmf: virtio_scsi_ctrl_tmf_req,
489         target_ids: &BTreeSet<TargetId>,
490     ) -> virtio_scsi_ctrl_tmf_resp {
491         match tmf.subtype {
492             VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET | VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET => {
493                 // We only have LUN0.
494                 let lun = tmf.lun;
495                 let target_id = lun[1];
496                 let response = if target_ids.contains(&target_id) {
497                     let is_lun0 = u16::from_be_bytes([lun[2], lun[3]]) & 0x3fff == 0;
498                     if is_lun0 {
499                         VIRTIO_SCSI_S_FUNCTION_SUCCEEDED as u8
500                     } else {
501                         VIRTIO_SCSI_S_INCORRECT_LUN as u8
502                     }
503                 } else {
504                     VIRTIO_SCSI_S_BAD_TARGET as u8
505                 };
506                 virtio_scsi_ctrl_tmf_resp { response }
507             }
508             subtype => {
509                 error!("TMF request {subtype} is not supported");
510                 virtio_scsi_ctrl_tmf_resp {
511                     response: VIRTIO_SCSI_S_FUNCTION_REJECTED as u8,
512                 }
513             }
514         }
515     }
516 
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>517     async fn execute_request(
518         reader: &mut Reader,
519         resp_writer: &mut Writer,
520         data_writer: &mut Writer,
521         targets: &BTreeMap<TargetId, AsyncLogicalUnit>,
522         sense_size: u32,
523         cdb_size: u32,
524     ) -> Result<(), ExecuteError> {
525         let req_header = reader
526             .read_obj::<VirtioScsiCmdReqHeader>()
527             .map_err(ExecuteError::Read)?;
528         match Self::get_logical_unit(req_header.lun, targets) {
529             Some(target) => {
530                 let mut cdb = vec![0; cdb_size as usize];
531                 reader.read_exact(&mut cdb).map_err(ExecuteError::Read)?;
532                 let command = Command::new(&cdb)?;
533                 match command.execute(reader, data_writer, target).await {
534                     Ok(()) => {
535                         let hdr = VirtioScsiCmdRespHeader {
536                             sense_len: 0,
537                             resid: 0,
538                             status_qualifier: 0,
539                             status: GOOD,
540                             response: VIRTIO_SCSI_S_OK as u8,
541                         };
542                         resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
543                         resp_writer.consume_bytes(sense_size as usize);
544                         Ok(())
545                     }
546                     Err(err) => {
547                         error!("error while executing a scsi request: {err}");
548                         let (hdr, sense) = err.as_resp();
549                         resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
550                         sense.write_to(resp_writer, sense_size)
551                     }
552                 }
553             }
554             None => {
555                 let hdr = VirtioScsiCmdRespHeader {
556                     response: VIRTIO_SCSI_S_BAD_TARGET as u8,
557                     ..Default::default()
558                 };
559                 resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
560                 resp_writer.consume_bytes(sense_size as usize);
561                 Ok(())
562             }
563         }
564     }
565 
get_logical_unit( lun: [u8; 8], targets: &BTreeMap<TargetId, AsyncLogicalUnit>, ) -> Option<&AsyncLogicalUnit>566     fn get_logical_unit(
567         lun: [u8; 8],
568         targets: &BTreeMap<TargetId, AsyncLogicalUnit>,
569     ) -> Option<&AsyncLogicalUnit> {
570         // First byte should be 1.
571         if lun[0] != 1 {
572             return None;
573         }
574         // General search strategy for scsi devices is as follows:
575         // 1) Look for a device which has the same bus id and lun indicated by the given lun. If
576         //    there is one, that is the target device.
577         // 2) If we cannot find such device, then we return the first device that has the same bus
578         //    id.
579         // Since we only support one LUN per target, we only need to use the target id.
580         let target_id = lun[1];
581         targets.get(&target_id)
582     }
583 }
584 
585 impl VirtioDevice for Controller {
keep_rds(&self) -> Vec<base::RawDescriptor>586     fn keep_rds(&self) -> Vec<base::RawDescriptor> {
587         match &self.targets {
588             Some(targets) => targets
589                 .0
590                 .values()
591                 .flat_map(|t| t.disk_image.as_raw_descriptors())
592                 .collect(),
593             None => vec![],
594         }
595     }
596 
features(&self) -> u64597     fn features(&self) -> u64 {
598         self.avail_features
599     }
600 
device_type(&self) -> VirtioDeviceType601     fn device_type(&self) -> VirtioDeviceType {
602         VirtioDeviceType::Scsi
603     }
604 
queue_max_sizes(&self) -> &[u16]605     fn queue_max_sizes(&self) -> &[u16] {
606         &self.queue_sizes
607     }
608 
read_config(&self, offset: u64, data: &mut [u8])609     fn read_config(&self, offset: u64, data: &mut [u8]) {
610         let config_space = self.build_config_space();
611         copy_config(data, 0, config_space.as_bytes(), offset);
612     }
613 
write_config(&mut self, offset: u64, data: &[u8])614     fn write_config(&mut self, offset: u64, data: &[u8]) {
615         let mut config = self.build_config_space();
616         copy_config(config.as_mut_bytes(), offset, data, 0);
617         // Only `sense_size` and `cdb_size` are modifiable by the driver.
618         self.sense_size = config.sense_size;
619         self.cdb_size = config.cdb_size;
620     }
621 
activate( &mut self, _mem: GuestMemory, _interrupt: Interrupt, mut queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>622     fn activate(
623         &mut self,
624         _mem: GuestMemory,
625         _interrupt: Interrupt,
626         mut queues: BTreeMap<usize, Queue>,
627     ) -> anyhow::Result<()> {
628         let executor_kind = self.executor_kind;
629         // 0th virtqueue is the controlq.
630         let controlq = queues.remove(&0).context("controlq should be present")?;
631         // 1st virtqueue is the eventq.
632         // We do not send any events through eventq.
633         let _eventq = queues.remove(&1).context("eventq should be present")?;
634         let targets = self.targets.take().context("failed to take SCSI targets")?;
635         let target_ids = targets.target_ids();
636         let sense_size = self.sense_size;
637         let cdb_size = self.cdb_size;
638         // The rest of the queues are request queues.
639         let request_queues = if self.multi_queue {
640             queues
641                 .into_values()
642                 .map(|queue| {
643                     let targets = targets
644                         .try_clone()
645                         .context("Failed to clone a disk image")?;
646                     Ok((queue, targets))
647                 })
648                 .collect::<anyhow::Result<_>>()?
649         } else {
650             // Handle all virtio requests with one thread.
651             vec![(
652                 queues
653                     .remove(&2)
654                     .context("request queue should be present")?,
655                 targets,
656             )]
657         };
658 
659         let worker_thread = WorkerThread::start("v_scsi_ctrlq", move |kill_evt| {
660             let ex =
661                 Executor::with_executor_kind(executor_kind).expect("Failed to create an executor");
662             if let Err(err) = ex
663                 .run_until(run_worker(
664                     &ex,
665                     controlq,
666                     kill_evt,
667                     QueueType::Control { target_ids },
668                     sense_size,
669                     cdb_size,
670                 ))
671                 .expect("run_until failed")
672             {
673                 error!("run_worker failed: {err}");
674             }
675         });
676         self.worker_threads.push(worker_thread);
677 
678         for (i, (queue, targets)) in request_queues.into_iter().enumerate() {
679             let worker_thread =
680                 WorkerThread::start(format!("v_scsi_req_{}", i + 2), move |kill_evt| {
681                     let ex = Executor::with_executor_kind(executor_kind)
682                         .expect("Failed to create an executor");
683                     let async_logical_unit = targets
684                         .0
685                         .into_iter()
686                         .map(|(idx, unit)| match unit.make_async(&ex) {
687                             Ok(async_unit) => (idx, async_unit),
688                             Err(err) => panic!("{err}"),
689                         })
690                         .collect();
691                     if let Err(err) = ex
692                         .run_until(run_worker(
693                             &ex,
694                             queue,
695                             kill_evt,
696                             QueueType::Request(async_logical_unit),
697                             sense_size,
698                             cdb_size,
699                         ))
700                         .expect("run_until failed")
701                     {
702                         error!("run_worker failed: {err}");
703                     }
704                 });
705             self.worker_threads.push(worker_thread);
706         }
707         Ok(())
708     }
709 }
710 
711 enum QueueType {
712     Control { target_ids: BTreeSet<TargetId> },
713     Request(BTreeMap<TargetId, AsyncLogicalUnit>),
714 }
715 
run_worker( ex: &Executor, queue: Queue, kill_evt: Event, queue_type: QueueType, sense_size: u32, cdb_size: u32, ) -> anyhow::Result<()>716 async fn run_worker(
717     ex: &Executor,
718     queue: Queue,
719     kill_evt: Event,
720     queue_type: QueueType,
721     sense_size: u32,
722     cdb_size: u32,
723 ) -> anyhow::Result<()> {
724     let kill = async_utils::await_and_exit(ex, kill_evt).fuse();
725     pin_mut!(kill);
726 
727     let kick_evt = queue
728         .event()
729         .try_clone()
730         .expect("Failed to clone queue event");
731     let queue_handler = handle_queue(
732         Rc::new(RefCell::new(queue)),
733         EventAsync::new(kick_evt, ex).expect("Failed to create async event for queue"),
734         queue_type,
735         sense_size,
736         cdb_size,
737     )
738     .fuse();
739     pin_mut!(queue_handler);
740 
741     futures::select! {
742         _ = queue_handler => anyhow::bail!("queue handler exited unexpectedly"),
743         r = kill => r.context("failed to wait on the kill event"),
744     }
745 }
746 
handle_queue( queue: Rc<RefCell<Queue>>, evt: EventAsync, queue_type: QueueType, sense_size: u32, cdb_size: u32, )747 async fn handle_queue(
748     queue: Rc<RefCell<Queue>>,
749     evt: EventAsync,
750     queue_type: QueueType,
751     sense_size: u32,
752     cdb_size: u32,
753 ) {
754     let mut background_tasks = FuturesUnordered::new();
755     let evt_future = evt.next_val().fuse();
756     pin_mut!(evt_future);
757     loop {
758         futures::select! {
759             _ = background_tasks.next() => continue,
760             res = evt_future => {
761                 evt_future.set(evt.next_val().fuse());
762                 if let Err(e) = res {
763                     error!("Failed to read the next queue event: {e}");
764                     continue;
765                 }
766             }
767         }
768         while let Some(chain) = queue.borrow_mut().pop() {
769             background_tasks.push(process_one_chain(
770                 &queue,
771                 chain,
772                 &queue_type,
773                 sense_size,
774                 cdb_size,
775             ));
776         }
777     }
778 }
779 
process_one_chain( queue: &RefCell<Queue>, mut avail_desc: DescriptorChain, queue_type: &QueueType, sense_size: u32, cdb_size: u32, )780 async fn process_one_chain(
781     queue: &RefCell<Queue>,
782     mut avail_desc: DescriptorChain,
783     queue_type: &QueueType,
784     sense_size: u32,
785     cdb_size: u32,
786 ) {
787     let _trace = cros_tracing::trace_event!(VirtioScsi, "process_one_chain");
788     let len = process_one_request(&mut avail_desc, queue_type, sense_size, cdb_size).await;
789     let mut queue = queue.borrow_mut();
790     queue.add_used(avail_desc, len as u32);
791     queue.trigger_interrupt();
792 }
793 
process_one_request( avail_desc: &mut DescriptorChain, queue_type: &QueueType, sense_size: u32, cdb_size: u32, ) -> usize794 async fn process_one_request(
795     avail_desc: &mut DescriptorChain,
796     queue_type: &QueueType,
797     sense_size: u32,
798     cdb_size: u32,
799 ) -> usize {
800     let reader = &mut avail_desc.reader;
801     let resp_writer = &mut avail_desc.writer;
802     match queue_type {
803         QueueType::Control { target_ids } => {
804             if let Err(err) = Controller::execute_control(reader, resp_writer, target_ids) {
805                 error!("failed to execute control request: {err}");
806             }
807             resp_writer.bytes_written()
808         }
809         QueueType::Request(async_targets) => {
810             let mut data_writer = resp_writer
811                 .split_at(std::mem::size_of::<VirtioScsiCmdRespHeader>() + sense_size as usize);
812             if let Err(err) = Controller::execute_request(
813                 reader,
814                 resp_writer,
815                 &mut data_writer,
816                 async_targets,
817                 sense_size,
818                 cdb_size,
819             )
820             .await
821             {
822                 // If the write of the virtio_scsi_cmd_resp fails, there is nothing we can do to
823                 // inform the error to the guest driver (we usually propagate errors with sense
824                 // field, which is in the struct virtio_scsi_cmd_resp). The guest driver should
825                 // have at least sizeof(virtio_scsi_cmd_resp) bytes of device-writable part
826                 // regions. For now we simply emit an error message.
827                 let (hdr, sense) = err.as_resp();
828                 if let Err(e) = resp_writer.write_obj(hdr) {
829                     error!("failed to write VirtioScsiCmdRespHeader: {e}");
830                 }
831                 if let Err(e) = sense.write_to(resp_writer, sense_size) {
832                     error!("failed to write sense data: {e}");
833                 }
834             }
835             resp_writer.bytes_written() + data_writer.bytes_written()
836         }
837     }
838 }
839 
840 #[cfg(test)]
841 mod tests {
842     use std::fs::File;
843     use std::mem::size_of;
844     use std::mem::size_of_val;
845     use std::rc::Rc;
846 
847     use cros_async::Executor;
848     use disk::SingleFileDisk;
849     use tempfile::tempfile;
850     use virtio_sys::virtio_scsi::virtio_scsi_cmd_req;
851     use virtio_sys::virtio_scsi::virtio_scsi_cmd_resp;
852     use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_OK;
853     use vm_memory::GuestAddress;
854     use vm_memory::GuestMemory;
855 
856     use super::*;
857     use crate::virtio::create_descriptor_chain;
858     use crate::virtio::scsi::constants::READ_10;
859     use crate::virtio::DescriptorType;
860 
setup_disk(disk_size: u64) -> (File, Vec<u8>)861     fn setup_disk(disk_size: u64) -> (File, Vec<u8>) {
862         let mut file_content = vec![0; disk_size as usize];
863         for i in 0..disk_size {
864             file_content[i as usize] = (i % 10) as u8;
865         }
866         let mut f = tempfile().unwrap();
867         f.set_len(disk_size).unwrap();
868         f.write_all(file_content.as_slice()).unwrap();
869         (f, file_content)
870     }
871 
build_read_req_header(target_id: u8, start_lba: u8, xfer_blocks: u8) -> virtio_scsi_cmd_req872     fn build_read_req_header(target_id: u8, start_lba: u8, xfer_blocks: u8) -> virtio_scsi_cmd_req {
873         let mut cdb = [0; 32];
874         cdb[0] = READ_10;
875         cdb[5] = start_lba;
876         cdb[8] = xfer_blocks;
877         virtio_scsi_cmd_req {
878             lun: [1, 0, 0, target_id, 0, 0, 0, 0],
879             cdb,
880             ..Default::default()
881         }
882     }
883 
setup_desciptor_chain( target_id: TargetId, start_lba: u8, xfer_blocks: u8, block_size: u32, mem: &Rc<GuestMemory>, ) -> DescriptorChain884     fn setup_desciptor_chain(
885         target_id: TargetId,
886         start_lba: u8,
887         xfer_blocks: u8,
888         block_size: u32,
889         mem: &Rc<GuestMemory>,
890     ) -> DescriptorChain {
891         let req_hdr = build_read_req_header(target_id, start_lba, xfer_blocks);
892         let xfer_bytes = xfer_blocks as u32 * block_size;
893         create_descriptor_chain(
894             mem,
895             GuestAddress(0x100),  // Place descriptor chain at 0x100.
896             GuestAddress(0x1000), // Describe buffer at 0x1000.
897             vec![
898                 // Request header
899                 (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
900                 // Response header
901                 (
902                     DescriptorType::Writable,
903                     size_of::<virtio_scsi_cmd_resp>() as u32,
904                 ),
905                 (DescriptorType::Writable, xfer_bytes),
906             ],
907             0,
908         )
909         .expect("create_descriptor_chain failed")
910     }
911 
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>)912     fn read_blocks(
913         ex: &Executor,
914         file_disks: &[File],
915         target_id: u8,
916         start_lba: u8,
917         xfer_blocks: u8,
918         block_size: u32,
919     ) -> (virtio_scsi_cmd_resp, Vec<u8>) {
920         let xfer_bytes = xfer_blocks as u32 * block_size;
921         let mem = Rc::new(
922             GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
923                 .expect("Creating guest memory failed."),
924         );
925         let req_hdr = build_read_req_header(target_id, start_lba, xfer_blocks);
926         mem.write_obj_at_addr(req_hdr, GuestAddress(0x1000))
927             .expect("writing req failed");
928 
929         let mut avail_desc = setup_desciptor_chain(target_id, 0, xfer_blocks, block_size, &mem);
930 
931         let targets = file_disks
932             .iter()
933             .enumerate()
934             .map(|(i, file)| {
935                 let file = file.try_clone().unwrap();
936                 let disk_image = Box::new(SingleFileDisk::new(file, ex).unwrap());
937                 let logical_unit = AsyncLogicalUnit {
938                     max_lba: 0x1000,
939                     block_size,
940                     read_only: false,
941                     disk_image,
942                 };
943                 (i as TargetId, logical_unit)
944             })
945             .collect();
946         ex.run_until(process_one_request(
947             &mut avail_desc,
948             &QueueType::Request(targets),
949             VIRTIO_SCSI_SENSE_DEFAULT_SIZE,
950             VIRTIO_SCSI_CDB_DEFAULT_SIZE,
951         ))
952         .expect("running executor failed");
953         let resp_offset = GuestAddress((0x1000 + size_of::<virtio_scsi_cmd_resp>()) as u64);
954         let resp = mem
955             .read_obj_from_addr::<virtio_scsi_cmd_resp>(resp_offset)
956             .unwrap();
957         let dataout_offset = GuestAddress(
958             (0x1000 + size_of::<virtio_scsi_cmd_req>() + size_of::<virtio_scsi_cmd_resp>()) as u64,
959         );
960         let dataout_slice = mem
961             .get_slice_at_addr(dataout_offset, xfer_bytes as usize)
962             .unwrap();
963         let mut dataout = vec![0; xfer_bytes as usize];
964         dataout_slice.copy_to(&mut dataout);
965         (resp, dataout)
966     }
967 
test_read_blocks( num_targets: usize, blocks: u8, start_lba: u8, xfer_blocks: u8, block_size: u32, )968     fn test_read_blocks(
969         num_targets: usize,
970         blocks: u8,
971         start_lba: u8,
972         xfer_blocks: u8,
973         block_size: u32,
974     ) {
975         let ex = Executor::new().expect("creating an executor failed");
976         let file_len = blocks as u64 * block_size as u64;
977         let xfer_bytes = xfer_blocks as usize * block_size as usize;
978         let start_off = start_lba as usize * block_size as usize;
979 
980         let (files, file_contents): (Vec<_>, Vec<_>) =
981             (0..num_targets).map(|_| setup_disk(file_len)).unzip();
982         for (target_id, file_content) in file_contents.iter().enumerate() {
983             let (resp, dataout) = read_blocks(
984                 &ex,
985                 &files,
986                 target_id as TargetId,
987                 start_lba,
988                 xfer_blocks,
989                 block_size,
990             );
991 
992             let sense_len = resp.sense_len;
993             assert_eq!(sense_len, 0);
994             assert_eq!(resp.status, VIRTIO_SCSI_S_OK as u8);
995             assert_eq!(resp.response, GOOD);
996 
997             assert_eq!(&dataout, &file_content[start_off..(start_off + xfer_bytes)]);
998         }
999     }
1000 
1001     #[test]
read_first_blocks()1002     fn read_first_blocks() {
1003         // Read the first 3 blocks of a 8-block device.
1004         let blocks = 8u8;
1005         let start_lba = 0u8;
1006         let xfer_blocks = 3u8;
1007 
1008         test_read_blocks(1, blocks, start_lba, xfer_blocks, 64u32);
1009         test_read_blocks(1, blocks, start_lba, xfer_blocks, 128u32);
1010         test_read_blocks(1, blocks, start_lba, xfer_blocks, 512u32);
1011     }
1012 
1013     #[test]
read_middle_blocks()1014     fn read_middle_blocks() {
1015         // Read 3 blocks from the 2nd block in the 8-block device.
1016         let blocks = 8u8;
1017         let start_lba = 1u8;
1018         let xfer_blocks = 3u8;
1019 
1020         test_read_blocks(1, blocks, start_lba, xfer_blocks, 64u32);
1021         test_read_blocks(1, blocks, start_lba, xfer_blocks, 128u32);
1022         test_read_blocks(1, blocks, start_lba, xfer_blocks, 512u32);
1023     }
1024 
1025     #[test]
read_first_blocks_with_multiple_disks()1026     fn read_first_blocks_with_multiple_disks() {
1027         // Read the first 3 blocks of a 8-block device.
1028         let blocks = 8u8;
1029         let start_lba = 0u8;
1030         let xfer_blocks = 3u8;
1031 
1032         test_read_blocks(3, blocks, start_lba, xfer_blocks, 64u32);
1033         test_read_blocks(3, blocks, start_lba, xfer_blocks, 128u32);
1034         test_read_blocks(3, blocks, start_lba, xfer_blocks, 512u32);
1035     }
1036 
1037     #[test]
read_middle_blocks_with_multiple_disks()1038     fn read_middle_blocks_with_multiple_disks() {
1039         // Read 3 blocks from the 2nd block in the 8-block device.
1040         let blocks = 8u8;
1041         let start_lba = 1u8;
1042         let xfer_blocks = 3u8;
1043 
1044         test_read_blocks(3, blocks, start_lba, xfer_blocks, 64u32);
1045         test_read_blocks(3, blocks, start_lba, xfer_blocks, 128u32);
1046         test_read_blocks(3, blocks, start_lba, xfer_blocks, 512u32);
1047     }
1048 }
1049