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