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