1#![deny(missing_docs)]
6use std::cell::RefCell;
11use std::collections::BTreeMap;
12use std::collections::BTreeSet;
13use std::io;
14use std::io::Read;
15use std::io::Write;
16use std::rc::Rc;
17
18use anyhow::Context;
19use base::error;
20use base::warn;
21use base::Event;
22use base::WorkerThread;
23use cros_async::EventAsync;
24use cros_async::Executor;
25use cros_async::ExecutorKind;
26use disk::AsyncDisk;
27use disk::DiskFile;
28use futures::pin_mut;
29use futures::stream::FuturesUnordered;
30use futures::FutureExt;
31use futures::StreamExt;
32use remain::sorted;
33use thiserror::Error as ThisError;
34use virtio_sys::virtio_scsi::virtio_scsi_config;
35use virtio_sys::virtio_scsi::virtio_scsi_ctrl_an_resp;
36use virtio_sys::virtio_scsi::virtio_scsi_ctrl_tmf_req;
37use virtio_sys::virtio_scsi::virtio_scsi_ctrl_tmf_resp;
38use virtio_sys::virtio_scsi::virtio_scsi_event;
39use virtio_sys::virtio_scsi::VIRTIO_SCSI_CDB_DEFAULT_SIZE;
40use virtio_sys::virtio_scsi::VIRTIO_SCSI_SENSE_DEFAULT_SIZE;
41use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_BAD_TARGET;
42use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_FUNCTION_REJECTED;
43use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
44use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_INCORRECT_LUN;
45use virtio_sys::virtio_scsi::VIRTIO_SCSI_S_OK;
46use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_AN_QUERY;
47use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_AN_SUBSCRIBE;
48use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_TMF;
49use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET;
50use virtio_sys::virtio_scsi::VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET;
51use vm_memory::GuestMemory;
52use zerocopy::FromBytes;
53use zerocopy::Immutable;
54use zerocopy::IntoBytes;
55use zerocopy::KnownLayout;
56
57use crate::virtio::async_utils;
58use crate::virtio::block::sys::get_seg_max;
59use crate::virtio::copy_config;
60use crate::virtio::scsi::commands::execute_cdb;
61use crate::virtio::scsi::constants::CHECK_CONDITION;
62use crate::virtio::scsi::constants::GOOD;
63use crate::virtio::scsi::constants::ILLEGAL_REQUEST;
64use crate::virtio::scsi::constants::MEDIUM_ERROR;
65use crate::virtio::DescriptorChain;
66use crate::virtio::DeviceType as VirtioDeviceType;
67use crate::virtio::Interrupt;
68use crate::virtio::Queue;
69use crate::virtio::Reader;
70use crate::virtio::VirtioDevice;
71use crate::virtio::Writer;
72
73const MIN_NUM_QUEUES: usize = 3;
78const MAX_NUM_QUEUES: usize = 16;
83const DEFAULT_MAX_CHANNEL: u16 = 0;
85const DEFAULT_MAX_TARGET: u16 = 255;
87const DEFAULT_MAX_LUN: u32 = 16383;
89
90const DEFAULT_QUEUE_SIZE: u16 = 1024;
91
92const MAX_CMD_PER_LUN: u32 = 1024;
94const MAX_SECTORS: u32 = u32::MAX;
96
97const FIXED_FORMAT_SENSE_SIZE: u32 = 18;
100
101#[repr(C, packed)]
102#[derive(Debug, Default, Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
103struct 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)]
113struct VirtioScsiCmdRespHeader {
114 sense_len: u32,
115 resid: u32,
116 status_qualifier: u16,
117 status: u8,
118 response: u8,
119}
120
121impl 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#[sorted]
135#[derive(ThisError, Debug)]
136pub 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
173impl ExecuteError {
174 fn as_resp(&self) -> (VirtioScsiCmdRespHeader, Sense) {
176 let resp = VirtioScsiCmdRespHeader::ok();
177 let sense = match self {
180 Self::Read(_) | Self::ReadCommand => {
181 Sense {
183 key: MEDIUM_ERROR,
184 asc: 0x11,
185 ascq: 0x00,
186 }
187 }
188 Self::Write(_) => {
189 Sense {
191 key: MEDIUM_ERROR,
192 asc: 0x0c,
193 ascq: 0x00,
194 }
195 }
196 Self::InvalidField => {
197 Sense {
199 key: ILLEGAL_REQUEST,
200 asc: 0x24,
201 ascq: 0x00,
202 }
203 }
204 Self::InvalidParamLen => {
205 Sense {
207 key: ILLEGAL_REQUEST,
208 asc: 0x1a,
209 ascq: 0x00,
210 }
211 }
212 Self::Unsupported(_) => {
213 Sense {
215 key: ILLEGAL_REQUEST,
216 asc: 0x20,
217 ascq: 0x00,
218 }
219 }
220 Self::ReadOnly | Self::LbaOutOfRange { .. } => {
221 Sense {
223 key: ILLEGAL_REQUEST,
224 asc: 0x21,
225 ascq: 0x00,
226 }
227 }
228 Self::SavingParamNotSupported => Sense {
229 key: ILLEGAL_REQUEST,
231 asc: 0x39,
232 ascq: 0x00,
233 },
234 Self::SynchronizationError => Sense {
235 key: MEDIUM_ERROR,
237 asc: 0x16,
238 ascq: 0x00,
239 },
240 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#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
263pub struct Sense {
264 pub key: u8,
266 pub asc: u8,
269 pub ascq: u8,
272}
273
274impl 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 sense_data[0] = 0x70;
281 sense_data[2] = self.key;
284 sense_data[7] = 10;
287 sense_data[12] = self.asc;
290 sense_data[13] = self.ascq;
292 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
300struct LogicalUnit {
302 last_lba: u64,
304 block_size: u32,
306 read_only: bool,
307 disk_image: Box<dyn DiskFile>,
309}
310
311impl 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
326pub struct AsyncLogicalUnit {
328 pub last_lba: u64,
329 pub block_size: u32,
330 pub read_only: bool,
331 pub disk_image: Box<dyn AsyncDisk>,
333}
334
335type TargetId = u8;
336struct Targets(BTreeMap<TargetId, LogicalUnit>);
337
338impl 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
364pub struct DiskConfig {
366 pub file: Box<dyn DiskFile>,
368 pub block_size: u32,
370 pub read_only: bool,
372}
373
374pub struct Controller {
376 avail_features: u64,
378 queue_sizes: Vec<u16>,
380 seg_max: u32,
382 sense_size: u32,
384 cdb_size: u32,
386 executor_kind: ExecutorKind,
387 worker_threads: Vec<WorkerThread<()>>,
388 targets: Option<Targets>,
390 multi_queue: bool,
393}
394
395impl Controller {
396 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 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: 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 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 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 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 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 if lun[0] != 1 {
574 return None;
575 }
576 let target_id = lun[1];
583 targets.get(&target_id)
584 }
585}
586
587impl 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 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 let controlq = queues.remove(&0).context("controlq should be present")?;
633 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 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 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
713enum QueueType {
714 Control { target_ids: BTreeSet<TargetId> },
715 Request(BTreeMap<TargetId, AsyncLogicalUnit>),
716}
717
718async 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
749async 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
782async 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
796async 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 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)]
843mod 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), GuestAddress(0x1000), vec![
900 (DescriptorType::Readable, size_of_val(&req_hdr) as u32),
902 (
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 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 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 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 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}