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