Coverage Report

Created: 2026-06-30 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/crosvm/devices/src/virtio/scsi/device.rs
Line
Count
Source
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
0
    fn ok() -> Self {
123
0
        VirtioScsiCmdRespHeader {
124
0
            sense_len: 0,
125
0
            resid: 0,
126
0
            status_qualifier: 0,
127
0
            status: GOOD,
128
0
            response: VIRTIO_SCSI_S_OK as u8,
129
0
        }
130
0
    }
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
0
    fn as_resp(&self) -> (VirtioScsiCmdRespHeader, Sense) {
176
0
        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
0
        let sense = match self {
180
            Self::Read(_) | Self::ReadCommand => {
181
                // UNRECOVERED READ ERROR
182
0
                Sense {
183
0
                    key: MEDIUM_ERROR,
184
0
                    asc: 0x11,
185
0
                    ascq: 0x00,
186
0
                }
187
            }
188
            Self::Write(_) => {
189
                // WRITE ERROR
190
0
                Sense {
191
0
                    key: MEDIUM_ERROR,
192
0
                    asc: 0x0c,
193
0
                    ascq: 0x00,
194
0
                }
195
            }
196
            Self::InvalidField => {
197
                // INVALID FIELD IN CDB
198
0
                Sense {
199
0
                    key: ILLEGAL_REQUEST,
200
0
                    asc: 0x24,
201
0
                    ascq: 0x00,
202
0
                }
203
            }
204
            Self::InvalidParamLen => {
205
                // INVALID PARAMETER LENGTH
206
0
                Sense {
207
0
                    key: ILLEGAL_REQUEST,
208
0
                    asc: 0x1a,
209
0
                    ascq: 0x00,
210
0
                }
211
            }
212
            Self::Unsupported(_) => {
213
                // INVALID COMMAND OPERATION CODE
214
0
                Sense {
215
0
                    key: ILLEGAL_REQUEST,
216
0
                    asc: 0x20,
217
0
                    ascq: 0x00,
218
0
                }
219
            }
220
            Self::ReadOnly | Self::LbaOutOfRange { .. } => {
221
                // LOGICAL BLOCK ADDRESS OUT OF RANGE
222
0
                Sense {
223
0
                    key: ILLEGAL_REQUEST,
224
0
                    asc: 0x21,
225
0
                    ascq: 0x00,
226
0
                }
227
            }
228
0
            Self::SavingParamNotSupported => Sense {
229
0
                // SAVING PARAMETERS NOT SUPPORTED
230
0
                key: ILLEGAL_REQUEST,
231
0
                asc: 0x39,
232
0
                ascq: 0x00,
233
0
            },
234
0
            Self::SynchronizationError => Sense {
235
0
                // SYNCHRONIZATION ERROR
236
0
                key: MEDIUM_ERROR,
237
0
                asc: 0x16,
238
0
                ascq: 0x00,
239
0
            },
240
            // Ignore these errors.
241
0
            Self::ReadIo { resid, desc_error } | Self::WriteIo { resid, desc_error } => {
242
0
                warn!("error while performing I/O {}", desc_error);
243
0
                let hdr = VirtioScsiCmdRespHeader {
244
0
                    resid: (*resid).try_into().unwrap_or(u32::MAX).to_be(),
245
0
                    ..resp
246
0
                };
247
0
                return (hdr, Sense::default());
248
            }
249
        };
250
0
        (
251
0
            VirtioScsiCmdRespHeader {
252
0
                sense_len: FIXED_FORMAT_SENSE_SIZE,
253
0
                status: CHECK_CONDITION,
254
0
                ..resp
255
0
            },
256
0
            sense,
257
0
        )
258
0
    }
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
0
    fn write_to(&self, writer: &mut Writer, sense_size: u32) -> Result<(), ExecuteError> {
276
0
        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
0
        sense_data[0] = 0x70;
281
        // sense_data[1]: Obsolete
282
        // Sense key
283
0
        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
0
        sense_data[7] = 10;
287
        // sense_data[8..12]: Command specific information, which we do not support.
288
        // Additional sense code
289
0
        sense_data[12] = self.asc;
290
        // Additional sense code qualifier
291
0
        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
0
        writer.write_all(&sense_data).map_err(ExecuteError::Write)?;
295
0
        writer.consume_bytes(sense_size as usize - sense_data.len());
296
0
        Ok(())
297
0
    }
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
0
    fn make_async(self, ex: &Executor) -> anyhow::Result<AsyncLogicalUnit> {
313
0
        let disk_image = self
314
0
            .disk_image
315
0
            .to_async_disk(ex)
316
0
            .context("Failed to create async disk")?;
317
0
        Ok(AsyncLogicalUnit {
318
0
            last_lba: self.last_lba,
319
0
            block_size: self.block_size,
320
0
            read_only: self.read_only,
321
0
            disk_image,
322
0
        })
323
0
    }
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
0
    fn try_clone(&self) -> io::Result<Self> {
340
0
        let logical_units = self
341
0
            .0
342
0
            .iter()
343
0
            .map(|(id, logical_unit)| {
344
0
                let disk_image = logical_unit.disk_image.try_clone()?;
345
0
                Ok((
346
0
                    *id,
347
0
                    LogicalUnit {
348
0
                        disk_image,
349
0
                        last_lba: logical_unit.last_lba,
350
0
                        block_size: logical_unit.block_size,
351
0
                        read_only: logical_unit.read_only,
352
0
                    },
353
0
                ))
354
0
            })
355
0
            .collect::<io::Result<_>>()?;
356
0
        Ok(Self(logical_units))
357
0
    }
358
359
0
    fn target_ids(&self) -> BTreeSet<TargetId> {
360
0
        self.0.keys().cloned().collect()
361
0
    }
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
0
    pub fn new(base_features: u64, disks: Vec<DiskConfig>) -> anyhow::Result<Self> {
398
0
        let multi_queue = disks.iter().all(|disk| disk.file.try_clone().is_ok());
399
0
        let num_queues = if multi_queue {
400
0
            MAX_NUM_QUEUES
401
        } else {
402
0
            MIN_NUM_QUEUES
403
        };
404
0
        let logical_units = disks
405
0
            .into_iter()
406
0
            .enumerate()
407
0
            .map(|(i, disk)| {
408
0
                let num_blocks = disk
409
0
                    .file
410
0
                    .get_len()
411
0
                    .context("Failed to get the length of the disk image")?
412
0
                    / disk.block_size as u64;
413
0
                let last_lba = num_blocks
414
0
                    .checked_sub(1)
415
0
                    .context("Invalid zero-length SCSI LUN")?;
416
0
                let target = LogicalUnit {
417
0
                    last_lba,
418
0
                    block_size: disk.block_size,
419
0
                    read_only: disk.read_only,
420
0
                    disk_image: disk.file,
421
0
                };
422
0
                Ok((i as TargetId, target))
423
0
            })
424
0
            .collect::<anyhow::Result<_>>()?;
425
        // b/300560198: Support feature bits in virtio-scsi.
426
0
        Ok(Self {
427
0
            avail_features: base_features,
428
0
            queue_sizes: vec![DEFAULT_QUEUE_SIZE; num_queues],
429
0
            seg_max: get_seg_max(DEFAULT_QUEUE_SIZE),
430
0
            sense_size: VIRTIO_SCSI_SENSE_DEFAULT_SIZE,
431
0
            cdb_size: VIRTIO_SCSI_CDB_DEFAULT_SIZE,
432
0
            executor_kind: ExecutorKind::default(),
433
0
            worker_threads: vec![],
434
0
            targets: Some(Targets(logical_units)),
435
0
            multi_queue,
436
0
        })
437
0
    }
438
439
0
    fn build_config_space(&self) -> virtio_scsi_config {
440
0
        virtio_scsi_config {
441
0
            // num_queues is the number of request queues only so we subtract 2 for the control
442
0
            // queue and the event queue.
443
0
            num_queues: self.queue_sizes.len() as u32 - 2,
444
0
            seg_max: self.seg_max,
445
0
            max_sectors: MAX_SECTORS,
446
0
            cmd_per_lun: MAX_CMD_PER_LUN,
447
0
            event_info_size: std::mem::size_of::<virtio_scsi_event>() as u32,
448
0
            sense_size: self.sense_size,
449
0
            cdb_size: self.cdb_size,
450
0
            max_channel: DEFAULT_MAX_CHANNEL,
451
0
            max_target: DEFAULT_MAX_TARGET,
452
0
            max_lun: DEFAULT_MAX_LUN,
453
0
        }
454
0
    }
455
456
    // Executes a request in the controlq.
457
0
    fn execute_control(
458
0
        reader: &mut Reader,
459
0
        writer: &mut Writer,
460
0
        target_ids: &BTreeSet<TargetId>,
461
0
    ) -> Result<(), ExecuteError> {
462
0
        let typ = reader.peek_obj::<u32>().map_err(ExecuteError::Read)?;
463
0
        match typ {
464
            VIRTIO_SCSI_T_TMF => {
465
0
                let tmf = reader
466
0
                    .read_obj::<virtio_scsi_ctrl_tmf_req>()
467
0
                    .map_err(ExecuteError::Read)?;
468
0
                let resp = Self::execute_tmf(tmf, target_ids);
469
0
                writer.write_obj(resp).map_err(ExecuteError::Write)?;
470
0
                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
0
                let resp = virtio_scsi_ctrl_an_resp {
476
0
                    event_actual: 0,
477
0
                    response: VIRTIO_SCSI_S_OK as u8,
478
0
                };
479
0
                writer.write_obj(resp).map_err(ExecuteError::Write)?;
480
0
                Ok(())
481
            }
482
            _ => {
483
0
                error!("invalid type of a control request: {typ}");
484
0
                Err(ExecuteError::InvalidField)
485
            }
486
        }
487
0
    }
488
489
    // Executes a TMF (task management function) request.
490
0
    fn execute_tmf(
491
0
        tmf: virtio_scsi_ctrl_tmf_req,
492
0
        target_ids: &BTreeSet<TargetId>,
493
0
    ) -> virtio_scsi_ctrl_tmf_resp {
494
0
        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
0
                let lun = tmf.lun;
498
0
                let target_id = lun[1];
499
0
                let response = if target_ids.contains(&target_id) {
500
0
                    let is_lun0 = u16::from_be_bytes([lun[2], lun[3]]) & 0x3fff == 0;
501
0
                    if is_lun0 {
502
0
                        VIRTIO_SCSI_S_FUNCTION_SUCCEEDED as u8
503
                    } else {
504
0
                        VIRTIO_SCSI_S_INCORRECT_LUN as u8
505
                    }
506
                } else {
507
0
                    VIRTIO_SCSI_S_BAD_TARGET as u8
508
                };
509
0
                virtio_scsi_ctrl_tmf_resp { response }
510
            }
511
0
            subtype => {
512
0
                error!("TMF request {subtype} is not supported");
513
0
                virtio_scsi_ctrl_tmf_resp {
514
0
                    response: VIRTIO_SCSI_S_FUNCTION_REJECTED as u8,
515
0
                }
516
            }
517
        }
518
0
    }
519
520
0
    async fn execute_request(
521
0
        reader: &mut Reader,
522
0
        resp_writer: &mut Writer,
523
0
        data_writer: &mut Writer,
524
0
        targets: &BTreeMap<TargetId, AsyncLogicalUnit>,
525
0
        sense_size: u32,
526
0
        cdb_size: u32,
527
0
    ) -> Result<(), ExecuteError> {
528
0
        let req_header = reader
529
0
            .read_obj::<VirtioScsiCmdReqHeader>()
530
0
            .map_err(ExecuteError::Read)?;
531
0
        match Self::get_logical_unit(req_header.lun, targets) {
532
0
            Some(target) => {
533
0
                let mut cdb = vec![0; cdb_size as usize];
534
0
                reader.read_exact(&mut cdb).map_err(ExecuteError::Read)?;
535
0
                match execute_cdb(&cdb, reader, data_writer, target).await {
536
                    Ok(()) => {
537
0
                        let hdr = VirtioScsiCmdRespHeader {
538
0
                            sense_len: 0,
539
0
                            resid: 0,
540
0
                            status_qualifier: 0,
541
0
                            status: GOOD,
542
0
                            response: VIRTIO_SCSI_S_OK as u8,
543
0
                        };
544
0
                        resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
545
0
                        resp_writer.consume_bytes(sense_size as usize);
546
0
                        Ok(())
547
                    }
548
0
                    Err(err) => {
549
0
                        error!("error while executing a scsi request: {err}");
550
0
                        let (hdr, sense) = err.as_resp();
551
0
                        resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
552
0
                        sense.write_to(resp_writer, sense_size)
553
                    }
554
                }
555
            }
556
            None => {
557
0
                let hdr = VirtioScsiCmdRespHeader {
558
0
                    response: VIRTIO_SCSI_S_BAD_TARGET as u8,
559
0
                    ..Default::default()
560
0
                };
561
0
                resp_writer.write_obj(hdr).map_err(ExecuteError::Write)?;
562
0
                resp_writer.consume_bytes(sense_size as usize);
563
0
                Ok(())
564
            }
565
        }
566
0
    }
567
568
0
    fn get_logical_unit(
569
0
        lun: [u8; 8],
570
0
        targets: &BTreeMap<TargetId, AsyncLogicalUnit>,
571
0
    ) -> Option<&AsyncLogicalUnit> {
572
        // First byte should be 1.
573
0
        if lun[0] != 1 {
574
0
            return None;
575
0
        }
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
0
        let target_id = lun[1];
583
0
        targets.get(&target_id)
584
0
    }
585
}
586
587
impl VirtioDevice for Controller {
588
0
    fn keep_rds(&self) -> Vec<base::RawDescriptor> {
589
0
        match &self.targets {
590
0
            Some(targets) => targets
591
0
                .0
592
0
                .values()
593
0
                .flat_map(|t| t.disk_image.as_raw_descriptors())
594
0
                .collect(),
595
0
            None => vec![],
596
        }
597
0
    }
598
599
0
    fn features(&self) -> u64 {
600
0
        self.avail_features
601
0
    }
602
603
0
    fn device_type(&self) -> VirtioDeviceType {
604
0
        VirtioDeviceType::Scsi
605
0
    }
606
607
0
    fn queue_max_sizes(&self) -> &[u16] {
608
0
        &self.queue_sizes
609
0
    }
610
611
0
    fn read_config(&self, offset: u64, data: &mut [u8]) {
612
0
        let config_space = self.build_config_space();
613
0
        copy_config(data, 0, config_space.as_bytes(), offset);
614
0
    }
615
616
0
    fn write_config(&mut self, offset: u64, data: &[u8]) {
617
0
        let mut config = self.build_config_space();
618
0
        copy_config(config.as_mut_bytes(), offset, data, 0);
619
        // Only `sense_size` and `cdb_size` are modifiable by the driver.
620
0
        self.sense_size = config.sense_size;
621
0
        self.cdb_size = config.cdb_size;
622
0
    }
623
624
0
    fn activate(
625
0
        &mut self,
626
0
        _mem: GuestMemory,
627
0
        _interrupt: Interrupt,
628
0
        mut queues: BTreeMap<usize, Queue>,
629
0
    ) -> anyhow::Result<()> {
630
0
        let executor_kind = self.executor_kind;
631
        // 0th virtqueue is the controlq.
632
0
        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
0
        let _eventq = queues.remove(&1).context("eventq should be present")?;
636
0
        let targets = self.targets.take().context("failed to take SCSI targets")?;
637
0
        let target_ids = targets.target_ids();
638
0
        let sense_size = self.sense_size;
639
0
        let cdb_size = self.cdb_size;
640
        // The rest of the queues are request queues.
641
0
        let request_queues = if self.multi_queue {
642
0
            queues
643
0
                .into_values()
644
0
                .map(|queue| {
645
0
                    let targets = targets
646
0
                        .try_clone()
647
0
                        .context("Failed to clone a disk image")?;
648
0
                    Ok((queue, targets))
649
0
                })
650
0
                .collect::<anyhow::Result<_>>()?
651
        } else {
652
            // Handle all virtio requests with one thread.
653
0
            vec![(
654
0
                queues
655
0
                    .remove(&2)
656
0
                    .context("request queue should be present")?,
657
0
                targets,
658
            )]
659
        };
660
661
0
        let worker_thread = WorkerThread::start("v_scsi_ctrlq", move |kill_evt| {
662
0
            let ex =
663
0
                Executor::with_executor_kind(executor_kind).expect("Failed to create an executor");
664
0
            if let Err(err) = ex
665
0
                .run_until(run_worker(
666
0
                    &ex,
667
0
                    controlq,
668
0
                    kill_evt,
669
0
                    QueueType::Control { target_ids },
670
0
                    sense_size,
671
0
                    cdb_size,
672
0
                ))
673
0
                .expect("run_until failed")
674
            {
675
0
                error!("run_worker failed: {err}");
676
0
            }
677
0
        });
678
0
        self.worker_threads.push(worker_thread);
679
680
0
        for (i, (queue, targets)) in request_queues.into_iter().enumerate() {
681
0
            let worker_thread =
682
0
                WorkerThread::start(format!("v_scsi_req_{}", i + 2), move |kill_evt| {
683
0
                    let ex = Executor::with_executor_kind(executor_kind)
684
0
                        .expect("Failed to create an executor");
685
0
                    let async_logical_unit = targets
686
0
                        .0
687
0
                        .into_iter()
688
0
                        .map(|(idx, unit)| match unit.make_async(&ex) {
689
0
                            Ok(async_unit) => (idx, async_unit),
690
0
                            Err(err) => panic!("{err}"),
691
0
                        })
692
0
                        .collect();
693
0
                    if let Err(err) = ex
694
0
                        .run_until(run_worker(
695
0
                            &ex,
696
0
                            queue,
697
0
                            kill_evt,
698
0
                            QueueType::Request(async_logical_unit),
699
0
                            sense_size,
700
0
                            cdb_size,
701
0
                        ))
702
0
                        .expect("run_until failed")
703
                    {
704
0
                        error!("run_worker failed: {err}");
705
0
                    }
706
0
                });
707
0
            self.worker_threads.push(worker_thread);
708
        }
709
0
        Ok(())
710
0
    }
711
}
712
713
enum QueueType {
714
    Control { target_ids: BTreeSet<TargetId> },
715
    Request(BTreeMap<TargetId, AsyncLogicalUnit>),
716
}
717
718
0
async fn run_worker(
719
0
    ex: &Executor,
720
0
    queue: Queue,
721
0
    kill_evt: Event,
722
0
    queue_type: QueueType,
723
0
    sense_size: u32,
724
0
    cdb_size: u32,
725
0
) -> anyhow::Result<()> {
726
0
    let kill = async_utils::await_and_exit(ex, kill_evt).fuse();
727
0
    pin_mut!(kill);
728
729
0
    let kick_evt = queue
730
0
        .event()
731
0
        .try_clone()
732
0
        .expect("Failed to clone queue event");
733
0
    let queue_handler = handle_queue(
734
0
        Rc::new(RefCell::new(queue)),
735
0
        EventAsync::new(kick_evt, ex).expect("Failed to create async event for queue"),
736
0
        queue_type,
737
0
        sense_size,
738
0
        cdb_size,
739
    )
740
0
    .fuse();
741
0
    pin_mut!(queue_handler);
742
743
0
    futures::select! {
744
0
        _ = queue_handler => anyhow::bail!("queue handler exited unexpectedly"),
745
0
        r = kill => r.context("failed to wait on the kill event"),
746
    }
747
0
}
748
749
0
async fn handle_queue(
750
0
    queue: Rc<RefCell<Queue>>,
751
0
    evt: EventAsync,
752
0
    queue_type: QueueType,
753
0
    sense_size: u32,
754
0
    cdb_size: u32,
755
0
) {
756
0
    let mut background_tasks = FuturesUnordered::new();
757
0
    let evt_future = evt.next_val().fuse();
758
0
    pin_mut!(evt_future);
759
    loop {
760
0
        futures::select! {
761
0
            _ = background_tasks.next() => continue,
762
0
            res = evt_future => {
763
0
                evt_future.set(evt.next_val().fuse());
764
0
                if let Err(e) = res {
765
0
                    error!("Failed to read the next queue event: {e}");
766
0
                    continue;
767
0
                }
768
            }
769
        }
770
0
        while let Some(chain) = queue.borrow_mut().pop() {
771
0
            background_tasks.push(process_one_chain(
772
0
                &queue,
773
0
                chain,
774
0
                &queue_type,
775
0
                sense_size,
776
0
                cdb_size,
777
0
            ));
778
0
        }
779
    }
780
}
781
782
0
async fn process_one_chain(
783
0
    queue: &RefCell<Queue>,
784
0
    mut avail_desc: DescriptorChain,
785
0
    queue_type: &QueueType,
786
0
    sense_size: u32,
787
0
    cdb_size: u32,
788
0
) {
789
0
    let _trace = cros_tracing::trace_event!(VirtioScsi, "process_one_chain");
790
0
    let len = process_one_request(&mut avail_desc, queue_type, sense_size, cdb_size).await;
791
0
    let mut queue = queue.borrow_mut();
792
0
    queue.add_used_with_bytes_written(avail_desc, len as u32);
793
0
    queue.trigger_interrupt();
794
0
}
795
796
0
async fn process_one_request(
797
0
    avail_desc: &mut DescriptorChain,
798
0
    queue_type: &QueueType,
799
0
    sense_size: u32,
800
0
    cdb_size: u32,
801
0
) -> usize {
802
0
    let reader = &mut avail_desc.reader;
803
0
    let resp_writer = &mut avail_desc.writer;
804
0
    match queue_type {
805
0
        QueueType::Control { target_ids } => {
806
0
            if let Err(err) = Controller::execute_control(reader, resp_writer, target_ids) {
807
0
                error!("failed to execute control request: {err}");
808
0
            }
809
0
            resp_writer.bytes_written()
810
        }
811
0
        QueueType::Request(async_targets) => {
812
0
            let mut data_writer = resp_writer
813
0
                .split_at(std::mem::size_of::<VirtioScsiCmdRespHeader>() + sense_size as usize);
814
0
            if let Err(err) = Controller::execute_request(
815
0
                reader,
816
0
                resp_writer,
817
0
                &mut data_writer,
818
0
                async_targets,
819
0
                sense_size,
820
0
                cdb_size,
821
            )
822
0
            .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
0
                let (hdr, sense) = err.as_resp();
830
0
                if let Err(e) = resp_writer.write_obj(hdr) {
831
0
                    error!("failed to write VirtioScsiCmdRespHeader: {e}");
832
0
                }
833
0
                if let Err(e) = sense.write_to(resp_writer, sense_size) {
834
0
                    error!("failed to write sense data: {e}");
835
0
                }
836
0
            }
837
0
            resp_writer.bytes_written() + data_writer.bytes_written()
838
        }
839
    }
840
0
}
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
}