Skip to main content

storvsp/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! VMBus SCSI controller emulator (StorVSP).
5//!
6//! StorVSP implements the Hyper-V synthetic SCSI protocol — a VMBus-based
7//! transport that carries SCSI CDBs between the guest's `storvsc` driver and
8//! the VMM. This is not a standard SCSI transport (like iSCSI or SAS); it's a
9//! Hyper-V-specific wire format defined in [`storvsp_protocol`].
10//!
11//! # Architecture
12//!
13//! The crate uses a multi-worker model. The primary VMBus channel handles
14//! protocol version negotiation (Win6 through Blue); sub-channels process I/O
15//! in parallel. Each worker owns a VMBus ring and processes packets
16//! concurrently via `FuturesUnordered`.
17//!
18//! StorVSP handles the transport (ring buffer management, GPADL setup, packet
19//! framing, sub-channel lifecycle) and a few SCSI control commands directly
20//! (`REPORT_LUNS`, `INQUIRY` for absent targets). All actual I/O is delegated
21//! to [`AsyncScsiDisk`] implementations — StorVSP
22//! never interprets SCSI data CDBs itself.
23//!
24//! For the channel/sub-channel model, CPU affinity, and performance
25//! characteristics, see the
26//! [StorVSP Channels & Subchannels](https://openvmm.dev/reference/devices/vmbus/storvsp_channels.html)
27//! page in the OpenVMM Guide.
28//!
29//! # Key types
30//!
31//! - [`StorageDevice`] — the VMBus device. Implements `VmbusDevice` and
32//!   `SaveRestoreVmbusDevice`.
33//! - [`ScsiController`] — manages attached disks by [`ScsiPath`]. Supports
34//!   runtime attach/remove.
35//! - [`ScsiControllerDisk`] — wraps `Arc<dyn AsyncScsiDisk>`.
36//!
37//! # Performance
38//!
39//! Poll-mode optimization: when pending I/O count exceeds
40//! `poll_mode_queue_depth`, the worker switches from interrupt-driven to
41//! busy-poll for new requests, reducing guest exit frequency. Future storage
42//! for SCSI request processing is pooled to avoid allocation on the hot path.
43
44#![expect(missing_docs)]
45#![forbid(unsafe_code)]
46
47#[cfg(feature = "ioperf")]
48pub mod ioperf;
49
50#[cfg(feature = "test")]
51pub mod test_helpers;
52
53#[cfg(not(feature = "test"))]
54mod test_helpers;
55
56pub mod resolver;
57mod save_restore;
58
59use crate::ring::gparange::MultiPagedRangeBuf;
60use anyhow::Context as _;
61use async_trait::async_trait;
62use fast_select::FastSelect;
63use futures::FutureExt;
64use futures::StreamExt;
65use futures::select_biased;
66use guestmem::AccessError;
67use guestmem::GuestMemory;
68use guestmem::MemoryRead;
69use guestmem::MemoryWrite;
70use guestmem::ranges::PagedRange;
71use guid::Guid;
72use inspect::Inspect;
73use inspect::InspectMut;
74use inspect_counters::Counter;
75use inspect_counters::Histogram;
76use oversized_box::OversizedBox;
77use parking_lot::Mutex;
78use parking_lot::RwLock;
79use ring::OutgoingPacketType;
80use scsi::AdditionalSenseCode;
81use scsi::ScsiOp;
82use scsi::ScsiStatus;
83use scsi::srb::SrbStatus;
84use scsi::srb::SrbStatusAndFlags;
85use scsi_buffers::RequestBuffers;
86use scsi_core::AsyncScsiDisk;
87use scsi_core::Request;
88use scsi_core::ScsiResult;
89use scsi_defs as scsi;
90use scsidisk::illegal_request_sense;
91use slab::Slab;
92use std::collections::hash_map::Entry;
93use std::collections::hash_map::HashMap;
94use std::fmt::Debug;
95use std::future::Future;
96use std::future::poll_fn;
97use std::pin::Pin;
98use std::sync::Arc;
99use std::sync::atomic::AtomicU32;
100use std::sync::atomic::Ordering::Relaxed;
101use std::task::Context;
102use std::task::Poll;
103use storvsp_resources::ScsiPath;
104use task_control::AsyncRun;
105use task_control::InspectTask;
106use task_control::StopTask;
107use task_control::TaskControl;
108use thiserror::Error;
109use tracing_helpers::ErrorValueExt;
110use unicycle::FuturesUnordered;
111use vmbus_async::queue;
112use vmbus_async::queue::ExternalDataError;
113use vmbus_async::queue::IncomingPacket;
114use vmbus_async::queue::OutgoingPacket;
115use vmbus_async::queue::Queue;
116use vmbus_channel::RawAsyncChannel;
117use vmbus_channel::bus::ChannelType;
118use vmbus_channel::bus::OfferParams;
119use vmbus_channel::bus::OpenRequest;
120use vmbus_channel::channel::ChannelControl;
121use vmbus_channel::channel::ChannelOpenError;
122use vmbus_channel::channel::DeviceResources;
123use vmbus_channel::channel::RestoreControl;
124use vmbus_channel::channel::SaveRestoreVmbusDevice;
125use vmbus_channel::channel::VmbusDevice;
126use vmbus_channel::gpadl_ring::GpadlRingMem;
127use vmbus_channel::gpadl_ring::gpadl_channel;
128use vmbus_core::protocol::UserDefinedData;
129use vmbus_ring as ring;
130use vmbus_ring::RingMem;
131use vmcore::save_restore::RestoreError;
132use vmcore::save_restore::SaveError;
133use vmcore::save_restore::SavedStateBlob;
134use vmcore::vm_task::VmTaskDriver;
135use vmcore::vm_task::VmTaskDriverSource;
136use zerocopy::FromBytes;
137use zerocopy::FromZeros;
138use zerocopy::Immutable;
139use zerocopy::IntoBytes;
140use zerocopy::KnownLayout;
141
142/// The IO queue depth at which the controller switches from guest-signal-driven
143/// to poll-mode operation. This optimization reduces the guest exit rate by
144/// relying on (typically-interrupt-driven) IO completions to drive polling for
145/// new IO requests.
146const DEFAULT_POLL_MODE_QUEUE_DEPTH: u32 = 1;
147
148pub struct StorageDevice {
149    instance_id: Guid,
150    ide_path: Option<ScsiPath>,
151    workers: Vec<WorkerAndDriver>,
152    controller: Arc<ScsiControllerState>,
153    resources: DeviceResources,
154    driver_source: VmTaskDriverSource,
155    max_sub_channel_count: u16,
156    protocol: Arc<Protocol>,
157    io_queue_depth: u32,
158}
159
160#[derive(Inspect)]
161struct WorkerAndDriver {
162    #[inspect(flatten)]
163    worker: TaskControl<WorkerState, Worker>,
164    driver: VmTaskDriver,
165}
166
167struct WorkerState;
168
169impl InspectMut for StorageDevice {
170    fn inspect_mut(&mut self, req: inspect::Request<'_>) {
171        let mut resp = req.respond();
172
173        let disks = self.controller.disks.read();
174        for (path, controller_disk) in disks.iter() {
175            resp.child(&format!("disks/{}", path), |req| {
176                controller_disk.disk.inspect(req);
177            });
178        }
179
180        resp.fields(
181            "channels",
182            self.workers
183                .iter()
184                .filter(|task| task.worker.has_state())
185                .enumerate(),
186        )
187        .field(
188            "poll_mode_queue_depth",
189            inspect::AtomicMut(&self.controller.poll_mode_queue_depth),
190        );
191    }
192}
193
194struct Worker<T: RingMem = GpadlRingMem> {
195    inner: WorkerInner,
196    rescan_notification: futures::channel::mpsc::Receiver<()>,
197    fast_select: FastSelect,
198    queue: Queue<T>,
199}
200
201struct Protocol {
202    state: RwLock<ProtocolState>,
203    /// Signaled when `state` transitions to `ProtocolState::Ready`.
204    ready: event_listener::Event,
205}
206
207struct WorkerInner {
208    protocol: Arc<Protocol>,
209    request_size: usize,
210    controller: Arc<ScsiControllerState>,
211    channel_index: u16,
212    scsi_queue: Arc<ScsiCommandQueue>,
213    scsi_requests: FuturesUnordered<ScsiRequest>,
214    scsi_requests_states: Slab<ScsiRequestState>,
215    full_request_pool: Vec<Arc<ScsiRequestAndRange>>,
216    future_pool: Vec<OversizedBox<(), ScsiOpStorage>>,
217    channel_control: ChannelControl,
218    max_io_queue_depth: usize,
219    stats: WorkerStats,
220}
221
222#[derive(Debug, Default, Inspect)]
223struct WorkerStats {
224    ios_submitted: Counter,
225    ios_completed: Counter,
226    wakes: Counter,
227    wakes_spurious: Counter,
228    per_wake_submissions: Histogram<10>,
229    per_wake_completions: Histogram<10>,
230}
231
232#[repr(u16)]
233#[derive(Copy, Clone, Debug, Inspect, PartialEq, Eq, PartialOrd, Ord)]
234enum Version {
235    Win6 = storvsp_protocol::VERSION_WIN6,
236    Win7 = storvsp_protocol::VERSION_WIN7,
237    Win8 = storvsp_protocol::VERSION_WIN8,
238    Blue = storvsp_protocol::VERSION_BLUE,
239}
240
241#[derive(Debug, Error)]
242#[error("protocol version {0:#x} not supported")]
243struct UnsupportedVersion(u16);
244
245impl Version {
246    fn parse(major_minor: u16) -> Result<Self, UnsupportedVersion> {
247        let version = match major_minor {
248            storvsp_protocol::VERSION_WIN6 => Self::Win6,
249            storvsp_protocol::VERSION_WIN7 => Self::Win7,
250            storvsp_protocol::VERSION_WIN8 => Self::Win8,
251            storvsp_protocol::VERSION_BLUE => Self::Blue,
252            version => return Err(UnsupportedVersion(version)),
253        };
254        assert_eq!(version as u16, major_minor);
255        Ok(version)
256    }
257
258    fn max_request_size(&self) -> usize {
259        match self {
260            Version::Win8 | Version::Blue => storvsp_protocol::SCSI_REQUEST_LEN_V2,
261            Version::Win6 | Version::Win7 => storvsp_protocol::SCSI_REQUEST_LEN_V1,
262        }
263    }
264}
265
266#[derive(Copy, Clone)]
267enum ProtocolState {
268    Init(InitState),
269    Ready {
270        version: Version,
271        subchannel_count: u16,
272    },
273}
274
275#[derive(Copy, Clone, Debug)]
276enum InitState {
277    Begin,
278    QueryVersion,
279    QueryProperties {
280        version: Version,
281    },
282    EndInitialization {
283        version: Version,
284        subchannel_count: Option<u16>,
285    },
286}
287
288/// The internal SCSI operation future type.
289///
290/// This is a boxed future of a large pre-determined size. The box is reused
291/// after a SCSI request completes to avoid allocations in the hot path.
292///
293/// An Option type is used so that the future can be efficiently dropped (via
294/// `Pin::set(x, None)`) before it is stashed away for reuse.
295type ScsiOpStorage = [u64; SCSI_REQUEST_STACK_SIZE / 8];
296type ScsiOpFuture = Pin<OversizedBox<dyn Future<Output = ScsiResult> + Send, ScsiOpStorage>>;
297
298/// The amount of space reserved for a ScsiOpFuture.
299///
300/// This was chosen by running `cargo test -p storvsp -- --no-capture` and looking at the required
301/// size that was given in the failure message
302const SCSI_REQUEST_STACK_SIZE: usize = scsi_core::ASYNC_SCSI_DISK_STACK_SIZE + 272;
303
304struct ScsiRequest {
305    request_id: usize,
306    future: Option<ScsiOpFuture>,
307}
308
309impl ScsiRequest {
310    fn new(
311        request_id: usize,
312        future: OversizedBox<dyn Future<Output = ScsiResult> + Send, ScsiOpStorage>,
313    ) -> Self {
314        Self {
315            request_id,
316            future: Some(future.into()),
317        }
318    }
319}
320
321impl Future for ScsiRequest {
322    type Output = (usize, ScsiResult, OversizedBox<(), ScsiOpStorage>);
323
324    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
325        let this = self.get_mut();
326        let future = this.future.as_mut().unwrap().as_mut();
327        let result = std::task::ready!(future.poll(cx));
328        // Return the future so that its storage can be reused.
329        let future = this.future.take().unwrap();
330        Poll::Ready((this.request_id, result, OversizedBox::empty_pinned(future)))
331    }
332}
333
334#[derive(Debug, Error)]
335enum WorkerError {
336    #[error("packet error")]
337    PacketError(#[source] PacketError),
338    #[error("queue error")]
339    Queue(#[source] queue::Error),
340    #[error("queue should have enough space but no longer does")]
341    NotEnoughSpace,
342}
343
344#[derive(Debug, Error)]
345enum PacketError {
346    #[error("Not transactional")]
347    NotTransactional,
348    #[error("Unrecognized operation {0:?}")]
349    UnrecognizedOperation(storvsp_protocol::Operation),
350    #[error("Invalid packet type")]
351    InvalidPacketType,
352    #[error("Invalid data transfer length")]
353    InvalidDataTransferLength,
354    #[error("Access error: {0}")]
355    Access(#[source] AccessError),
356    #[error("Range error")]
357    Range(#[source] ExternalDataError),
358}
359
360#[derive(Debug, Default, Clone)]
361struct Range {
362    len: usize,
363    is_write: bool,
364}
365
366impl Range {
367    fn new(buf: &MultiPagedRangeBuf, request: &storvsp_protocol::ScsiRequest) -> Option<Self> {
368        let len = request.data_transfer_length as usize;
369        let is_write = request.data_in != 0;
370        // Ensure there is exactly one range and it's large enough, or there are
371        // zero ranges and there is no associated SCSI buffer.
372        if buf.range_count() > 1 || (len > 0 && buf.first()?.len() < len) {
373            return None;
374        }
375        Some(Self { len, is_write })
376    }
377
378    fn buffer<'a>(
379        &'a self,
380        buf: &'a MultiPagedRangeBuf,
381        guest_memory: &'a GuestMemory,
382    ) -> RequestBuffers<'a> {
383        let mut range = buf.first().unwrap_or_else(PagedRange::empty);
384        range.truncate(self.len);
385        RequestBuffers::new(guest_memory, range, self.is_write)
386    }
387}
388
389#[derive(Debug)]
390struct Packet {
391    data: PacketData,
392    transaction_id: u64,
393    request_size: usize,
394}
395
396#[derive(Debug)]
397enum PacketData {
398    BeginInitialization,
399    EndInitialization,
400    QueryProtocolVersion(u16),
401    QueryProperties,
402    CreateSubChannels(u16),
403    ExecuteScsi(Arc<ScsiRequestAndRange>),
404    ResetBus,
405    ResetAdapter,
406    ResetLun,
407}
408
409fn parse_packet<T: RingMem>(
410    packet: &IncomingPacket<'_, T>,
411    pool: &mut Vec<Arc<ScsiRequestAndRange>>,
412) -> Result<Packet, PacketError> {
413    let packet = match packet {
414        IncomingPacket::Completion(_) => return Err(PacketError::InvalidPacketType),
415        IncomingPacket::Data(packet) => packet,
416    };
417    let transaction_id = packet
418        .transaction_id()
419        .ok_or(PacketError::NotTransactional)?;
420
421    let mut reader = packet.reader();
422    let header: storvsp_protocol::Packet = reader.read_plain().map_err(PacketError::Access)?;
423    // You would expect that this should be limited to the current protocol
424    // version's maximum packet size, but this is not what Hyper-V does, and
425    // Linux 6.1 relies on this behavior during protocol initialization.
426    let request_size = reader.len().min(storvsp_protocol::SCSI_REQUEST_LEN_MAX);
427    let data = match header.operation {
428        storvsp_protocol::Operation::BEGIN_INITIALIZATION => PacketData::BeginInitialization,
429        storvsp_protocol::Operation::END_INITIALIZATION => PacketData::EndInitialization,
430        storvsp_protocol::Operation::QUERY_PROTOCOL_VERSION => {
431            let mut version = storvsp_protocol::ProtocolVersion::new_zeroed();
432            reader
433                .read(version.as_mut_bytes())
434                .map_err(PacketError::Access)?;
435            PacketData::QueryProtocolVersion(version.major_minor)
436        }
437        storvsp_protocol::Operation::QUERY_PROPERTIES => PacketData::QueryProperties,
438        storvsp_protocol::Operation::EXECUTE_SRB => {
439            let mut full_request = pool.pop().unwrap_or_else(|| {
440                Arc::new(ScsiRequestAndRange {
441                    external_data: Range::default(),
442                    external_data_buf: MultiPagedRangeBuf::new(),
443                    request: storvsp_protocol::ScsiRequest::new_zeroed(),
444                    request_size,
445                })
446            });
447
448            {
449                let full_request = Arc::get_mut(&mut full_request).unwrap();
450                let request_buf = &mut full_request.request.as_mut_bytes()[..request_size];
451                reader.read(request_buf).map_err(PacketError::Access)?;
452
453                full_request.external_data_buf.clear();
454                packet
455                    .read_external_ranges(&mut full_request.external_data_buf)
456                    .map_err(PacketError::Range)?;
457
458                full_request.external_data =
459                    Range::new(&full_request.external_data_buf, &full_request.request)
460                        .ok_or(PacketError::InvalidDataTransferLength)?;
461            }
462
463            PacketData::ExecuteScsi(full_request)
464        }
465        storvsp_protocol::Operation::RESET_LUN => PacketData::ResetLun,
466        storvsp_protocol::Operation::RESET_ADAPTER => PacketData::ResetAdapter,
467        storvsp_protocol::Operation::RESET_BUS => PacketData::ResetBus,
468        storvsp_protocol::Operation::CREATE_SUB_CHANNELS => {
469            let mut sub_channel_count: u16 = 0;
470            reader
471                .read(sub_channel_count.as_mut_bytes())
472                .map_err(PacketError::Access)?;
473            PacketData::CreateSubChannels(sub_channel_count)
474        }
475        _ => return Err(PacketError::UnrecognizedOperation(header.operation)),
476    };
477
478    if let PacketData::ExecuteScsi(_) = data {
479        tracing::trace!(transaction_id, ?data, "parse_packet");
480    } else {
481        tracing::debug!(transaction_id, ?data, "parse_packet");
482    }
483
484    Ok(Packet {
485        data,
486        request_size,
487        transaction_id,
488    })
489}
490
491impl WorkerInner {
492    fn send_vmbus_packet<M: RingMem>(
493        &mut self,
494        writer: &mut queue::WriteBatch<'_, M>,
495        packet_type: OutgoingPacketType<'_>,
496        request_size: usize,
497        transaction_id: u64,
498        operation: storvsp_protocol::Operation,
499        status: storvsp_protocol::NtStatus,
500        payload: &[u8],
501    ) -> Result<(), WorkerError> {
502        let header = storvsp_protocol::Packet {
503            operation,
504            flags: 0,
505            status,
506        };
507
508        let packet_size = size_of_val(&header) + request_size;
509
510        // Zero pad or truncate the payload to the queue's packet size. This is
511        // necessary because Windows guests check that each packet's size is
512        // exactly the largest possible packet size for the negotiated protocol
513        // version.
514        let len = size_of_val(&header) + size_of_val(payload);
515        let padding = [0; storvsp_protocol::SCSI_REQUEST_LEN_MAX];
516        let (payload_bytes, padding_bytes) = if len > packet_size {
517            (&payload[..packet_size - size_of_val(&header)], &[][..])
518        } else {
519            (payload, &padding[..packet_size - len])
520        };
521        assert_eq!(
522            size_of_val(&header) + payload_bytes.len() + padding_bytes.len(),
523            packet_size
524        );
525        writer
526            .try_write(&OutgoingPacket {
527                transaction_id,
528                packet_type,
529                payload: &[header.as_bytes(), payload_bytes, padding_bytes],
530            })
531            .map_err(|err| match err {
532                queue::TryWriteError::Full(_) => WorkerError::NotEnoughSpace,
533                queue::TryWriteError::Queue(err) => WorkerError::Queue(err),
534            })
535    }
536
537    fn send_packet<M: RingMem, P: IntoBytes + Immutable + KnownLayout>(
538        &mut self,
539        writer: &mut queue::WriteHalf<'_, M>,
540        operation: storvsp_protocol::Operation,
541        status: storvsp_protocol::NtStatus,
542        payload: &P,
543    ) -> Result<(), WorkerError> {
544        self.send_vmbus_packet(
545            &mut writer.batched(),
546            OutgoingPacketType::InBandNoCompletion,
547            self.request_size,
548            0,
549            operation,
550            status,
551            payload.as_bytes(),
552        )
553    }
554
555    fn send_completion<M: RingMem, P: IntoBytes + Immutable + KnownLayout>(
556        &mut self,
557        writer: &mut queue::WriteHalf<'_, M>,
558        packet: &Packet,
559        status: storvsp_protocol::NtStatus,
560        payload: &P,
561    ) -> Result<(), WorkerError> {
562        self.send_vmbus_packet(
563            &mut writer.batched(),
564            OutgoingPacketType::Completion,
565            packet.request_size,
566            packet.transaction_id,
567            storvsp_protocol::Operation::COMPLETE_IO,
568            status,
569            payload.as_bytes(),
570        )
571    }
572}
573
574struct ScsiCommandQueue {
575    controller: Arc<ScsiControllerState>,
576    mem: GuestMemory,
577    force_path_id: Option<u8>,
578}
579
580impl ScsiCommandQueue {
581    async fn execute_scsi(&self, full_request: &ScsiRequestAndRange) -> ScsiResult {
582        let request = &full_request.request;
583        let op = ScsiOp(request.payload[0]);
584        let external_data = full_request
585            .external_data
586            .buffer(&full_request.external_data_buf, &self.mem);
587
588        tracing::trace!(
589            path_id = request.path_id,
590            target_id = request.target_id,
591            lun = request.lun,
592            op = ?op,
593            "execute_scsi start...",
594        );
595
596        let path_id = self.force_path_id.unwrap_or(request.path_id);
597
598        let controller_disk = self
599            .controller
600            .disks
601            .read()
602            .get(&ScsiPath {
603                path: path_id,
604                target: request.target_id,
605                lun: request.lun,
606            })
607            .cloned();
608
609        let result = match op {
610            ScsiOp::REPORT_LUNS => {
611                const HEADER_SIZE: usize = size_of::<scsi::LunList>();
612                let mut luns: Vec<u8> = self
613                    .controller
614                    .disks
615                    .read()
616                    .keys()
617                    .flat_map(|path| {
618                        // Use the original path ID and not the forced one to
619                        // match Hyper-V storvsp behavior.
620                        if request.path_id == path.path && request.target_id == path.target {
621                            Some(path.lun)
622                        } else {
623                            None
624                        }
625                    })
626                    .collect();
627                luns.sort_unstable();
628                let mut data: Vec<u64> = vec![0; luns.len() + 1];
629                let header = scsi::LunList {
630                    length: (luns.len() as u32 * 8).into(),
631                    reserved: [0; 4],
632                };
633                data.as_mut_bytes()[..HEADER_SIZE].copy_from_slice(header.as_bytes());
634                for (i, lun) in luns.iter().enumerate() {
635                    data[i + 1].as_mut_bytes()[..2].copy_from_slice(&(*lun as u16).to_be_bytes());
636                }
637                if external_data.len() >= HEADER_SIZE {
638                    let tx = std::cmp::min(external_data.len(), data.as_bytes().len());
639                    external_data.writer().write(&data.as_bytes()[..tx]).map_or(
640                        ScsiResult {
641                            scsi_status: ScsiStatus::CHECK_CONDITION,
642                            srb_status: SrbStatus::INVALID_REQUEST,
643                            tx: 0,
644                            sense_data: Some(illegal_request_sense(
645                                AdditionalSenseCode::INVALID_CDB,
646                            )),
647                        },
648                        |_| ScsiResult {
649                            scsi_status: ScsiStatus::GOOD,
650                            srb_status: SrbStatus::SUCCESS,
651                            tx,
652                            sense_data: None,
653                        },
654                    )
655                } else {
656                    ScsiResult {
657                        scsi_status: ScsiStatus::GOOD,
658                        srb_status: SrbStatus::SUCCESS,
659                        tx: 0,
660                        sense_data: None,
661                    }
662                }
663            }
664            _ if controller_disk.is_some() => {
665                let mut cdb = [0; 16];
666                cdb.copy_from_slice(&request.payload[0..storvsp_protocol::CDB16GENERIC_LENGTH]);
667                controller_disk
668                    .unwrap()
669                    .disk
670                    .execute_scsi(
671                        &external_data,
672                        &Request {
673                            cdb,
674                            srb_flags: request.srb_flags,
675                        },
676                    )
677                    .await
678            }
679            ScsiOp::INQUIRY => {
680                let cdb = scsi::CdbInquiry::ref_from_prefix(&request.payload)
681                    .unwrap()
682                    .0; // TODO: zerocopy: ref-from-prefix: use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
683                if external_data.len() < cdb.allocation_length.get() as usize
684                    || request.data_in != storvsp_protocol::SCSI_IOCTL_DATA_IN
685                    || (cdb.allocation_length.get() as usize) < size_of::<scsi::InquiryDataHeader>()
686                {
687                    ScsiResult {
688                        scsi_status: ScsiStatus::CHECK_CONDITION,
689                        srb_status: SrbStatus::INVALID_REQUEST,
690                        tx: 0,
691                        sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
692                    }
693                } else {
694                    let enable_vpd = cdb.flags.vpd();
695                    if enable_vpd || cdb.page_code != 0 {
696                        // cannot support VPD inquiry for non-existing device (lun).
697                        ScsiResult {
698                            scsi_status: ScsiStatus::CHECK_CONDITION,
699                            srb_status: SrbStatus::INVALID_REQUEST,
700                            tx: 0,
701                            sense_data: Some(illegal_request_sense(
702                                AdditionalSenseCode::INVALID_CDB,
703                            )),
704                        }
705                    } else {
706                        const LOGICAL_UNIT_NOT_PRESENT_DEVICE: u8 = 0x7F;
707                        let mut data = scsidisk::INQUIRY_DATA_TEMPLATE;
708                        data.header.device_type = LOGICAL_UNIT_NOT_PRESENT_DEVICE;
709
710                        if request.lun != 0 {
711                            // Below fields are only set for lun0 inquiry so zero out here.
712                            data.vendor_id = [0; 8];
713                            data.product_id = [0; 16];
714                            data.product_revision_level = [0; 4];
715                        }
716
717                        let datab = data.as_bytes();
718                        let tx = std::cmp::min(
719                            cdb.allocation_length.get() as usize,
720                            size_of::<scsi::InquiryData>(),
721                        );
722                        external_data.writer().write(&datab[..tx]).map_or(
723                            ScsiResult {
724                                scsi_status: ScsiStatus::CHECK_CONDITION,
725                                srb_status: SrbStatus::INVALID_REQUEST,
726                                tx: 0,
727                                sense_data: Some(illegal_request_sense(
728                                    AdditionalSenseCode::INVALID_CDB,
729                                )),
730                            },
731                            |_| ScsiResult {
732                                scsi_status: ScsiStatus::GOOD,
733                                srb_status: SrbStatus::SUCCESS,
734                                tx,
735                                sense_data: None,
736                            },
737                        )
738                    }
739                }
740            }
741            _ => ScsiResult {
742                scsi_status: ScsiStatus::CHECK_CONDITION,
743                srb_status: SrbStatus::INVALID_LUN,
744                tx: 0,
745                sense_data: None,
746            },
747        };
748
749        tracing::trace!(
750            path_id = request.path_id,
751            target_id = request.target_id,
752            lun = request.lun,
753            op = ?op,
754            result = ?result,
755            "execute_scsi completed.",
756        );
757        result
758    }
759}
760
761impl<T: RingMem + 'static> Worker<T> {
762    fn new(
763        controller: Arc<ScsiControllerState>,
764        channel: RawAsyncChannel<T>,
765        channel_index: u16,
766        mem: GuestMemory,
767        channel_control: ChannelControl,
768        io_queue_depth: u32,
769        protocol: Arc<Protocol>,
770        force_path_id: Option<u8>,
771    ) -> anyhow::Result<Self> {
772        let queue = Queue::new(channel)?;
773        #[expect(clippy::disallowed_methods)] // TODO
774        let (source, target) = futures::channel::mpsc::channel(1);
775        controller.add_rescan_notification_source(source);
776
777        let max_io_queue_depth = io_queue_depth.max(1) as usize;
778        Ok(Self {
779            inner: WorkerInner {
780                protocol,
781                request_size: storvsp_protocol::SCSI_REQUEST_LEN_V1,
782                controller: controller.clone(),
783                channel_index,
784                scsi_queue: Arc::new(ScsiCommandQueue {
785                    controller,
786                    mem,
787                    force_path_id,
788                }),
789                scsi_requests: FuturesUnordered::new(),
790                scsi_requests_states: Slab::with_capacity(max_io_queue_depth),
791                channel_control,
792                max_io_queue_depth,
793                future_pool: Vec::new(),
794                full_request_pool: Vec::new(),
795                stats: Default::default(),
796            },
797            queue,
798            rescan_notification: target,
799            fast_select: FastSelect::new(),
800        })
801    }
802
803    async fn wait_for_scsi_requests_complete(&mut self) {
804        tracing::debug!(
805            channel_index = self.inner.channel_index,
806            "wait for IOs completed..."
807        );
808        while let Some((id, _, _)) = self.inner.scsi_requests.next().await {
809            self.inner.scsi_requests_states.remove(id);
810        }
811    }
812}
813
814impl InspectTask<Worker> for WorkerState {
815    fn inspect(&self, req: inspect::Request<'_>, worker: Option<&Worker>) {
816        if let Some(worker) = worker {
817            let mut resp = req.respond();
818            if worker.inner.channel_index == 0 {
819                let (state, version, subchannel_count) = match *worker.inner.protocol.state.read() {
820                    ProtocolState::Init(state) => match state {
821                        InitState::Begin => ("begin_init", None, None),
822                        InitState::QueryVersion => ("query_version", None, None),
823                        InitState::QueryProperties { version } => {
824                            ("query_properties", Some(version), None)
825                        }
826                        InitState::EndInitialization {
827                            version,
828                            subchannel_count,
829                        } => ("end_init", Some(version), subchannel_count),
830                    },
831                    ProtocolState::Ready {
832                        version,
833                        subchannel_count,
834                    } => ("ready", Some(version), Some(subchannel_count)),
835                };
836                resp.field("state", state)
837                    .field("version", version)
838                    .field("subchannel_count", subchannel_count);
839            }
840            resp.field("pending_packets", worker.inner.scsi_requests_states.len())
841                .fields("io", worker.inner.scsi_requests_states.iter())
842                .field("stats", &worker.inner.stats)
843                .field("ring", &worker.queue)
844                .field("max_io_queue_depth", worker.inner.max_io_queue_depth);
845        }
846    }
847}
848
849impl<T: 'static + Send + Sync + RingMem> AsyncRun<Worker<T>> for WorkerState {
850    async fn run(
851        &mut self,
852        stop: &mut StopTask<'_>,
853        worker: &mut Worker<T>,
854    ) -> Result<(), task_control::Cancelled> {
855        let fut = async {
856            if worker.inner.channel_index == 0 {
857                worker.process_primary().await
858            } else {
859                // Wait for initialization to end before processing any
860                // subchannel packets.
861                let protocol_version = loop {
862                    let listener = worker.inner.protocol.ready.listen();
863                    if let ProtocolState::Ready { version, .. } =
864                        *worker.inner.protocol.state.read()
865                    {
866                        break version;
867                    }
868                    tracing::debug!("subchannel waiting for initialization to end");
869                    listener.await
870                };
871                worker
872                    .inner
873                    .process_ready(&mut worker.queue, protocol_version)
874                    .await
875            }
876        };
877
878        match stop.until_stopped(fut).await? {
879            Ok(_) => {}
880            Err(e) => tracing::error!(error = e.as_error(), "process_packets error"),
881        }
882        Ok(())
883    }
884}
885
886impl WorkerInner {
887    /// Awaits the next incoming packet, without checking for any other events (device add/remove notifications or available completions).
888    /// Increments the count of outstanding packets when returning `Ok(Packet)`.
889    async fn next_packet<'a, M: RingMem>(
890        &mut self,
891        reader: &'a mut queue::ReadHalf<'a, M>,
892    ) -> Result<Packet, WorkerError> {
893        let packet = reader.read().await.map_err(WorkerError::Queue)?;
894        let stor_packet =
895            parse_packet(&packet, &mut self.full_request_pool).map_err(WorkerError::PacketError)?;
896        Ok(stor_packet)
897    }
898
899    /// Polls for enough ring space in the outgoing ring to send a packet.
900    ///
901    /// This is used to ensure there is enough space in the ring before
902    /// committing to sending a packet. This avoids the need to save pending
903    /// packets on the side if queue processing is interrupted while the ring is
904    /// full.
905    fn poll_for_ring_space<M: RingMem>(
906        &mut self,
907        cx: &mut Context<'_>,
908        writer: &mut queue::WriteHalf<'_, M>,
909    ) -> Poll<Result<(), WorkerError>> {
910        writer
911            .poll_ready(cx, MAX_VMBUS_PACKET_SIZE)
912            .map_err(WorkerError::Queue)
913    }
914}
915
916const MAX_VMBUS_PACKET_SIZE: usize = ring::PacketSize::in_band(
917    size_of::<storvsp_protocol::Packet>() + storvsp_protocol::SCSI_REQUEST_LEN_MAX,
918);
919
920impl<T: RingMem> Worker<T> {
921    /// Processes the protocol state machine.
922    async fn process_primary(&mut self) -> Result<(), WorkerError> {
923        loop {
924            let current_state = *self.inner.protocol.state.read();
925            match current_state {
926                ProtocolState::Ready { version, .. } => {
927                    break loop {
928                        select_biased! {
929                            r = self.inner.process_ready(&mut self.queue, version).fuse() => break r,
930                            _ = self.fast_select.select((self.rescan_notification.select_next_some(),)).fuse() => {
931                                if version >= Version::Win7
932                                {
933                                    tracing::debug!("rescan notification received, sending ENUMERATE_BUS");
934                                    self.inner.send_packet(&mut self.queue.split().1, storvsp_protocol::Operation::ENUMERATE_BUS, storvsp_protocol::NtStatus::SUCCESS, &())?;
935                                }
936                            }
937                        }
938                    };
939                }
940                ProtocolState::Init(state) => {
941                    let (mut reader, mut writer) = self.queue.split();
942
943                    // Ensure that subsequent calls to `send_completion` won't
944                    // fail due to lack of ring space, to avoid keeping (and saving/restoring) interim states.
945                    poll_fn(|cx| self.inner.poll_for_ring_space(cx, &mut writer)).await?;
946
947                    tracing::debug!(?state, "process_primary");
948                    match state {
949                        InitState::Begin => {
950                            let packet = self.inner.next_packet(&mut reader).await?;
951                            if let PacketData::BeginInitialization = packet.data {
952                                self.inner.send_completion(
953                                    &mut writer,
954                                    &packet,
955                                    storvsp_protocol::NtStatus::SUCCESS,
956                                    &(),
957                                )?;
958                                *self.inner.protocol.state.write() =
959                                    ProtocolState::Init(InitState::QueryVersion);
960                            } else {
961                                tracelimit::warn_ratelimited!(?state, data = ?packet.data, "unexpected packet order");
962                                self.inner.send_completion(
963                                    &mut writer,
964                                    &packet,
965                                    storvsp_protocol::NtStatus::INVALID_DEVICE_STATE,
966                                    &(),
967                                )?;
968                            }
969                        }
970                        InitState::QueryVersion => {
971                            let packet = self.inner.next_packet(&mut reader).await?;
972                            if let PacketData::QueryProtocolVersion(major_minor) = packet.data {
973                                if let Ok(version) = Version::parse(major_minor) {
974                                    self.inner.send_completion(
975                                        &mut writer,
976                                        &packet,
977                                        storvsp_protocol::NtStatus::SUCCESS,
978                                        &storvsp_protocol::ProtocolVersion {
979                                            major_minor,
980                                            reserved: 0,
981                                        },
982                                    )?;
983                                    self.inner.request_size = version.max_request_size();
984                                    *self.inner.protocol.state.write() =
985                                        ProtocolState::Init(InitState::QueryProperties { version });
986
987                                    tracelimit::info_ratelimited!(
988                                        ?version,
989                                        "scsi version negotiated"
990                                    );
991                                } else {
992                                    self.inner.send_completion(
993                                        &mut writer,
994                                        &packet,
995                                        storvsp_protocol::NtStatus::REVISION_MISMATCH,
996                                        &storvsp_protocol::ProtocolVersion {
997                                            major_minor,
998                                            reserved: 0,
999                                        },
1000                                    )?;
1001                                    *self.inner.protocol.state.write() =
1002                                        ProtocolState::Init(InitState::QueryVersion);
1003                                }
1004                            } else {
1005                                tracelimit::warn_ratelimited!(?state, data = ?packet.data, "unexpected packet order");
1006                                self.inner.send_completion(
1007                                    &mut writer,
1008                                    &packet,
1009                                    storvsp_protocol::NtStatus::INVALID_DEVICE_STATE,
1010                                    &(),
1011                                )?;
1012                            }
1013                        }
1014                        InitState::QueryProperties { version } => {
1015                            let packet = self.inner.next_packet(&mut reader).await?;
1016                            if let PacketData::QueryProperties = packet.data {
1017                                let multi_channel_supported = version >= Version::Win8;
1018
1019                                self.inner.send_completion(
1020                                    &mut writer,
1021                                    &packet,
1022                                    storvsp_protocol::NtStatus::SUCCESS,
1023                                    &storvsp_protocol::ChannelProperties {
1024                                        max_transfer_bytes: 0x40000, // 256KB
1025                                        flags: {
1026                                            if multi_channel_supported {
1027                                                storvsp_protocol::STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL
1028                                            } else {
1029                                                0
1030                                            }
1031                                        },
1032                                        maximum_sub_channel_count: if multi_channel_supported {
1033                                            self.inner.channel_control.max_subchannels()
1034                                        } else {
1035                                            0
1036                                        },
1037                                        reserved: 0,
1038                                        reserved2: 0,
1039                                        reserved3: [0, 0],
1040                                    },
1041                                )?;
1042                                *self.inner.protocol.state.write() =
1043                                    ProtocolState::Init(InitState::EndInitialization {
1044                                        version,
1045                                        subchannel_count: if multi_channel_supported {
1046                                            None
1047                                        } else {
1048                                            Some(0)
1049                                        },
1050                                    });
1051                            } else {
1052                                tracelimit::warn_ratelimited!(?state, data = ?packet.data, "unexpected packet order");
1053                                self.inner.send_completion(
1054                                    &mut writer,
1055                                    &packet,
1056                                    storvsp_protocol::NtStatus::INVALID_DEVICE_STATE,
1057                                    &(),
1058                                )?;
1059                            }
1060                        }
1061                        InitState::EndInitialization {
1062                            version,
1063                            subchannel_count,
1064                        } => {
1065                            let packet = self.inner.next_packet(&mut reader).await?;
1066                            match packet.data {
1067                                PacketData::CreateSubChannels(sub_channel_count)
1068                                    if subchannel_count.is_none() =>
1069                                {
1070                                    if let Err(err) = self
1071                                        .inner
1072                                        .channel_control
1073                                        .enable_subchannels(sub_channel_count)
1074                                    {
1075                                        tracelimit::warn_ratelimited!(
1076                                            ?err,
1077                                            "cannot enable subchannels"
1078                                        );
1079                                        self.inner.send_completion(
1080                                            &mut writer,
1081                                            &packet,
1082                                            storvsp_protocol::NtStatus::INVALID_PARAMETER,
1083                                            &(),
1084                                        )?;
1085                                    } else {
1086                                        self.inner.send_completion(
1087                                            &mut writer,
1088                                            &packet,
1089                                            storvsp_protocol::NtStatus::SUCCESS,
1090                                            &(),
1091                                        )?;
1092                                        *self.inner.protocol.state.write() =
1093                                            ProtocolState::Init(InitState::EndInitialization {
1094                                                version,
1095                                                subchannel_count: Some(sub_channel_count),
1096                                            });
1097                                    }
1098                                }
1099                                PacketData::EndInitialization => {
1100                                    self.inner.send_completion(
1101                                        &mut writer,
1102                                        &packet,
1103                                        storvsp_protocol::NtStatus::SUCCESS,
1104                                        &(),
1105                                    )?;
1106                                    // Reset the rescan notification event now, before the guest has a
1107                                    // chance to send any SCSI requests to scan the bus.
1108                                    self.rescan_notification.try_next().ok();
1109                                    *self.inner.protocol.state.write() = ProtocolState::Ready {
1110                                        version,
1111                                        subchannel_count: subchannel_count.unwrap_or(0),
1112                                    };
1113                                    // Wake up subchannels waiting for the
1114                                    // protocol state to become ready.
1115                                    self.inner.protocol.ready.notify(usize::MAX);
1116                                }
1117                                _ => {
1118                                    tracelimit::warn_ratelimited!(?state, data = ?packet.data, "unexpected packet order");
1119                                    self.inner.send_completion(
1120                                        &mut writer,
1121                                        &packet,
1122                                        storvsp_protocol::NtStatus::INVALID_DEVICE_STATE,
1123                                        &(),
1124                                    )?;
1125                                }
1126                            }
1127                        }
1128                    }
1129                }
1130            }
1131        }
1132    }
1133}
1134
1135fn convert_srb_status_to_nt_status(srb_status: SrbStatus) -> storvsp_protocol::NtStatus {
1136    match srb_status {
1137        SrbStatus::BUSY => storvsp_protocol::NtStatus::DEVICE_BUSY,
1138        SrbStatus::SUCCESS => storvsp_protocol::NtStatus::SUCCESS,
1139        SrbStatus::INVALID_LUN
1140        | SrbStatus::INVALID_TARGET_ID
1141        | SrbStatus::NO_DEVICE
1142        | SrbStatus::NO_HBA => storvsp_protocol::NtStatus::DEVICE_DOES_NOT_EXIST,
1143        SrbStatus::COMMAND_TIMEOUT | SrbStatus::TIMEOUT => storvsp_protocol::NtStatus::IO_TIMEOUT,
1144        SrbStatus::SELECTION_TIMEOUT => storvsp_protocol::NtStatus::DEVICE_NOT_CONNECTED,
1145        SrbStatus::BAD_FUNCTION | SrbStatus::BAD_SRB_BLOCK_LENGTH => {
1146            storvsp_protocol::NtStatus::INVALID_DEVICE_REQUEST
1147        }
1148        SrbStatus::DATA_OVERRUN => storvsp_protocol::NtStatus::BUFFER_OVERFLOW,
1149        SrbStatus::REQUEST_FLUSHED => storvsp_protocol::NtStatus::UNSUCCESSFUL,
1150        SrbStatus::ABORTED => storvsp_protocol::NtStatus::CANCELLED,
1151        _ => storvsp_protocol::NtStatus::IO_DEVICE_ERROR,
1152    }
1153}
1154
1155impl WorkerInner {
1156    /// Processes packets and SCSI completions after protocol negotiation has finished.
1157    async fn process_ready<M: RingMem>(
1158        &mut self,
1159        queue: &mut Queue<M>,
1160        protocol_version: Version,
1161    ) -> Result<(), WorkerError> {
1162        self.request_size = protocol_version.max_request_size();
1163        poll_fn(|cx| self.poll_process_ready(cx, queue)).await
1164    }
1165
1166    /// Processes packets and SCSI completions after protocol negotiation has finished.
1167    fn poll_process_ready<M: RingMem>(
1168        &mut self,
1169        cx: &mut Context<'_>,
1170        queue: &mut Queue<M>,
1171    ) -> Poll<Result<(), WorkerError>> {
1172        self.stats.wakes.increment();
1173
1174        let (mut reader, mut writer) = queue.split();
1175        let mut total_completions = 0;
1176        let mut total_submissions = 0;
1177        let poll_mode_queue_depth = self.controller.poll_mode_queue_depth.load(Relaxed) as usize;
1178
1179        loop {
1180            // Drive IOs forward and collect completions.
1181            'outer: while !self.scsi_requests_states.is_empty() {
1182                {
1183                    let mut batch = writer.batched();
1184                    loop {
1185                        // Ensure there is room for the completion before consuming
1186                        // the IO so that we don't have to track completed IOs whose
1187                        // completions haven't been sent.
1188                        if !batch
1189                            .can_write(MAX_VMBUS_PACKET_SIZE)
1190                            .map_err(WorkerError::Queue)?
1191                        {
1192                            // This batch is full but there may still be more completions.
1193                            break;
1194                        }
1195                        if let Poll::Ready(Some((request_id, result, future))) =
1196                            self.scsi_requests.poll_next_unpin(cx)
1197                        {
1198                            self.future_pool.push(future);
1199                            self.handle_completion(&mut batch, request_id, result)?;
1200                            total_completions += 1;
1201                        } else {
1202                            tracing::trace!("out of completions");
1203                            break 'outer;
1204                        }
1205                    }
1206                }
1207
1208                // Wait for enough space for any completion packets.
1209                if self.poll_for_ring_space(cx, &mut writer).is_pending() {
1210                    tracing::trace!("out of ring space");
1211                    break;
1212                }
1213            }
1214
1215            let mut submissions = 0;
1216            // Process new requests.
1217            'outer: loop {
1218                if self.scsi_requests_states.len() >= self.max_io_queue_depth {
1219                    break;
1220                }
1221                let mut batch = if self.scsi_requests_states.len() < poll_mode_queue_depth {
1222                    if let Poll::Ready(batch) = reader.poll_read_batch(cx) {
1223                        batch.map_err(WorkerError::Queue)?
1224                    } else {
1225                        tracing::trace!("out of incoming packets");
1226                        break;
1227                    }
1228                } else {
1229                    match reader.try_read_batch() {
1230                        Ok(batch) => batch,
1231                        Err(queue::TryReadError::Empty) => {
1232                            tracing::trace!(
1233                                pending_io_count = self.scsi_requests_states.len(),
1234                                "out of incoming packets, keeping interrupts masked"
1235                            );
1236                            break;
1237                        }
1238                        Err(queue::TryReadError::Queue(err)) => Err(WorkerError::Queue(err))?,
1239                    }
1240                };
1241
1242                let mut packets = batch.packets();
1243                loop {
1244                    if self.scsi_requests_states.len() >= self.max_io_queue_depth {
1245                        break 'outer;
1246                    }
1247                    // Wait for enough space for any completion packets that
1248                    // `handle_packet` may need to send, so that it isn't necessary
1249                    // to track pending completions.
1250                    if self.poll_for_ring_space(cx, &mut writer).is_pending() {
1251                        tracing::trace!("out of ring space");
1252                        break 'outer;
1253                    }
1254
1255                    let packet = if let Some(packet) = packets.next() {
1256                        packet.map_err(WorkerError::Queue)?
1257                    } else {
1258                        break;
1259                    };
1260
1261                    if self.handle_packet(&mut writer, &packet)? {
1262                        submissions += 1;
1263                    }
1264                }
1265            }
1266
1267            // Loop around to poll the IOs if any new IOs were submitted.
1268            if submissions == 0 {
1269                // No need to poll again.
1270                break;
1271            }
1272            total_submissions += submissions;
1273        }
1274
1275        if total_submissions != 0 || total_completions != 0 {
1276            self.stats.ios_submitted.add(total_submissions);
1277            self.stats
1278                .per_wake_submissions
1279                .add_sample(total_submissions);
1280            self.stats
1281                .per_wake_completions
1282                .add_sample(total_completions);
1283            self.stats.ios_completed.add(total_completions);
1284        } else {
1285            self.stats.wakes_spurious.increment();
1286        }
1287
1288        Poll::Pending
1289    }
1290
1291    fn handle_completion<M: RingMem>(
1292        &mut self,
1293        writer: &mut queue::WriteBatch<'_, M>,
1294        request_id: usize,
1295        result: ScsiResult,
1296    ) -> Result<(), WorkerError> {
1297        let state = self.scsi_requests_states.remove(request_id);
1298        let request_size = state.request.request_size;
1299
1300        // Push the request into the pool to avoid reallocating later.
1301        assert_eq!(
1302            Arc::strong_count(&state.request) + Arc::weak_count(&state.request),
1303            1
1304        );
1305        self.full_request_pool.push(state.request);
1306
1307        let status = convert_srb_status_to_nt_status(result.srb_status);
1308        let mut payload = [0; 0x14];
1309        if let Some(sense) = result.sense_data {
1310            payload[..size_of_val(&sense)].copy_from_slice(sense.as_bytes());
1311            tracing::trace!(sense_info = ?payload, sense_key = payload[2], asc = payload[12], "execute_scsi");
1312        };
1313        let response = storvsp_protocol::ScsiRequest {
1314            length: size_of::<storvsp_protocol::ScsiRequest>() as u16,
1315            scsi_status: result.scsi_status,
1316            srb_status: SrbStatusAndFlags::new()
1317                .with_status(result.srb_status)
1318                .with_autosense_valid(result.sense_data.is_some()),
1319            data_transfer_length: result.tx as u32,
1320            cdb_length: storvsp_protocol::CDB16GENERIC_LENGTH as u8,
1321            sense_info_ex_length: storvsp_protocol::VMSCSI_SENSE_BUFFER_SIZE as u8,
1322            payload,
1323            ..storvsp_protocol::ScsiRequest::new_zeroed()
1324        };
1325        self.send_vmbus_packet(
1326            writer,
1327            OutgoingPacketType::Completion,
1328            request_size,
1329            state.transaction_id,
1330            storvsp_protocol::Operation::COMPLETE_IO,
1331            status,
1332            response.as_bytes(),
1333        )?;
1334        Ok(())
1335    }
1336
1337    fn handle_packet<M: RingMem>(
1338        &mut self,
1339        writer: &mut queue::WriteHalf<'_, M>,
1340        packet: &IncomingPacket<'_, M>,
1341    ) -> Result<bool, WorkerError> {
1342        let packet =
1343            parse_packet(packet, &mut self.full_request_pool).map_err(WorkerError::PacketError)?;
1344        let submitted_io = match packet.data {
1345            PacketData::ExecuteScsi(request) => {
1346                self.push_scsi_request(packet.transaction_id, request);
1347                true
1348            }
1349            PacketData::ResetAdapter | PacketData::ResetBus | PacketData::ResetLun => {
1350                // These operations have always been no-ops.
1351                self.send_completion(writer, &packet, storvsp_protocol::NtStatus::SUCCESS, &())?;
1352                false
1353            }
1354            PacketData::CreateSubChannels(new_subchannel_count) if self.channel_index == 0 => {
1355                if let Err(err) = self
1356                    .channel_control
1357                    .enable_subchannels(new_subchannel_count)
1358                {
1359                    tracelimit::warn_ratelimited!(?err, "cannot create subchannels");
1360                    self.send_completion(
1361                        writer,
1362                        &packet,
1363                        storvsp_protocol::NtStatus::INVALID_PARAMETER,
1364                        &(),
1365                    )?;
1366                    false
1367                } else {
1368                    // Update the subchannel count in the protocol state for save.
1369                    if let ProtocolState::Ready {
1370                        subchannel_count, ..
1371                    } = &mut *self.protocol.state.write()
1372                    {
1373                        *subchannel_count = new_subchannel_count;
1374                    } else {
1375                        unreachable!()
1376                    }
1377
1378                    self.send_completion(
1379                        writer,
1380                        &packet,
1381                        storvsp_protocol::NtStatus::SUCCESS,
1382                        &(),
1383                    )?;
1384                    false
1385                }
1386            }
1387            _ => {
1388                tracelimit::warn_ratelimited!(data = ?packet.data, "unexpected packet on ready");
1389                self.send_completion(
1390                    writer,
1391                    &packet,
1392                    storvsp_protocol::NtStatus::INVALID_DEVICE_STATE,
1393                    &(),
1394                )?;
1395                false
1396            }
1397        };
1398        Ok(submitted_io)
1399    }
1400
1401    fn push_scsi_request(&mut self, transaction_id: u64, full_request: Arc<ScsiRequestAndRange>) {
1402        let scsi_queue = self.scsi_queue.clone();
1403        let scsi_request_state = ScsiRequestState {
1404            transaction_id,
1405            request: full_request.clone(),
1406        };
1407        let request_id = self.scsi_requests_states.insert(scsi_request_state);
1408        let future = self
1409            .future_pool
1410            .pop()
1411            .unwrap_or_else(|| OversizedBox::new(()));
1412        let future = OversizedBox::refill(future, async move {
1413            scsi_queue.execute_scsi(full_request.as_ref()).await
1414        });
1415        let request = ScsiRequest::new(request_id, oversized_box::coerce!(future));
1416        self.scsi_requests.push(request);
1417    }
1418}
1419
1420impl<T: RingMem> Drop for Worker<T> {
1421    fn drop(&mut self) {
1422        self.inner
1423            .controller
1424            .remove_rescan_notification_source(&self.rescan_notification);
1425    }
1426}
1427
1428#[derive(Debug, Error)]
1429#[error("SCSI path {}:{}:{} is already in use", self.0.path, self.0.target, self.0.lun)]
1430pub struct ScsiPathInUse(pub ScsiPath);
1431
1432#[derive(Debug, Error)]
1433#[error("SCSI path {}:{}:{} is not in use", self.0.path, self.0.target, self.0.lun)]
1434pub struct ScsiPathNotInUse(ScsiPath);
1435
1436#[derive(Clone)]
1437struct ScsiRequestState {
1438    transaction_id: u64,
1439    request: Arc<ScsiRequestAndRange>,
1440}
1441
1442#[derive(Debug)]
1443struct ScsiRequestAndRange {
1444    external_data: Range,
1445    external_data_buf: MultiPagedRangeBuf,
1446    request: storvsp_protocol::ScsiRequest,
1447    request_size: usize,
1448}
1449
1450impl Inspect for ScsiRequestState {
1451    fn inspect(&self, req: inspect::Request<'_>) {
1452        req.respond()
1453            .field("transaction_id", self.transaction_id)
1454            .display(
1455                "address",
1456                &ScsiPath {
1457                    path: self.request.request.path_id,
1458                    target: self.request.request.target_id,
1459                    lun: self.request.request.lun,
1460                },
1461            )
1462            .display_debug("operation", &ScsiOp(self.request.request.payload[0]));
1463    }
1464}
1465
1466impl StorageDevice {
1467    /// Returns a new SCSI device.
1468    pub fn build_scsi(
1469        driver_source: &VmTaskDriverSource,
1470        controller: &ScsiController,
1471        instance_id: Guid,
1472        max_sub_channel_count: u16,
1473        io_queue_depth: u32,
1474    ) -> Self {
1475        Self::build_inner(
1476            driver_source,
1477            controller,
1478            instance_id,
1479            None,
1480            max_sub_channel_count,
1481            io_queue_depth,
1482        )
1483    }
1484
1485    /// Returns a new SCSI device for implementing an IDE accelerator channel
1486    /// for IDE device `device_id` on channel `channel_id`.
1487    pub fn build_ide(
1488        driver_source: &VmTaskDriverSource,
1489        channel_id: u8,
1490        device_id: u8,
1491        disk: ScsiControllerDisk,
1492        io_queue_depth: u32,
1493    ) -> Self {
1494        let path = ScsiPath {
1495            path: channel_id,
1496            target: device_id,
1497            lun: 0,
1498        };
1499
1500        let controller = ScsiController::new();
1501        controller.attach(path, disk).unwrap();
1502
1503        // Construct the specific GUID that drivers in the guest expect for this
1504        // IDE device.
1505        let instance_id = Guid {
1506            data1: channel_id.into(),
1507            data2: device_id.into(),
1508            data3: 0x8899,
1509            data4: [0; 8],
1510        };
1511        Self::build_inner(
1512            driver_source,
1513            &controller,
1514            instance_id,
1515            Some(path),
1516            0,
1517            io_queue_depth,
1518        )
1519    }
1520
1521    fn build_inner(
1522        driver_source: &VmTaskDriverSource,
1523        controller: &ScsiController,
1524        instance_id: Guid,
1525        ide_path: Option<ScsiPath>,
1526        max_sub_channel_count: u16,
1527        io_queue_depth: u32,
1528    ) -> Self {
1529        let workers = (0..max_sub_channel_count + 1)
1530            .map(|channel_index| WorkerAndDriver {
1531                worker: TaskControl::new(WorkerState),
1532                driver: driver_source
1533                    .builder()
1534                    .target_vp(0)
1535                    .run_on_target(true)
1536                    .build(format!("storvsp-{}-{}", instance_id, channel_index)),
1537            })
1538            .collect();
1539
1540        Self {
1541            instance_id,
1542            ide_path,
1543            workers,
1544            controller: controller.state.clone(),
1545            resources: Default::default(),
1546            max_sub_channel_count,
1547            driver_source: driver_source.clone(),
1548            protocol: Arc::new(Protocol {
1549                state: RwLock::new(ProtocolState::Init(InitState::Begin)),
1550                ready: Default::default(),
1551            }),
1552            io_queue_depth,
1553        }
1554    }
1555
1556    fn new_worker(
1557        &mut self,
1558        open_request: &OpenRequest,
1559        channel_index: u16,
1560    ) -> anyhow::Result<&mut Worker> {
1561        let controller = self.controller.clone();
1562
1563        // VMBus doesn't provide a target VP if the channel is not using interrupts. Run on VP 0 in
1564        // that case.
1565        let target_vp = open_request.open_data.target_vp.unwrap_or_default();
1566        let driver = self
1567            .driver_source
1568            .builder()
1569            .target_vp(target_vp)
1570            .run_on_target(true)
1571            .build(format!("storvsp-{}-{}", self.instance_id, channel_index));
1572
1573        let channel = gpadl_channel(&driver, &self.resources, open_request, channel_index)
1574            .context("failed to create vmbus channel")?;
1575
1576        let channel_control = self.resources.channel_control.clone();
1577
1578        tracing::debug!(
1579            target_vp = open_request.open_data.target_vp,
1580            channel_index,
1581            "packet processing starting...",
1582        );
1583
1584        // Force the path ID on incoming SCSI requests to match the IDE
1585        // channel ID, since guests do not reliably set the path ID
1586        // correctly.
1587        let force_path_id = self.ide_path.map(|p| p.path);
1588
1589        let worker = Worker::new(
1590            controller,
1591            channel,
1592            channel_index,
1593            self.resources
1594                .offer_resources
1595                .guest_memory(open_request)
1596                .clone(),
1597            channel_control,
1598            self.io_queue_depth,
1599            self.protocol.clone(),
1600            force_path_id,
1601        )
1602        .map_err(RestoreError::Other)?;
1603
1604        self.workers[channel_index as usize]
1605            .driver
1606            .retarget_vp(target_vp);
1607
1608        Ok(self.workers[channel_index as usize].worker.insert(
1609            &driver,
1610            format!("storvsp worker {}-{}", self.instance_id, channel_index),
1611            worker,
1612        ))
1613    }
1614}
1615
1616/// A disk that can be added to a SCSI controller.
1617#[derive(Clone)]
1618pub struct ScsiControllerDisk {
1619    disk: Arc<dyn AsyncScsiDisk>,
1620}
1621
1622impl ScsiControllerDisk {
1623    /// Creates a new controller disk from an async SCSI disk.
1624    pub fn new(disk: Arc<dyn AsyncScsiDisk>) -> Self {
1625        Self { disk }
1626    }
1627}
1628
1629struct ScsiControllerState {
1630    disks: RwLock<HashMap<ScsiPath, ScsiControllerDisk>>,
1631    rescan_notification_source: Mutex<Vec<futures::channel::mpsc::Sender<()>>>,
1632    poll_mode_queue_depth: AtomicU32,
1633}
1634
1635impl ScsiControllerState {
1636    /// Tells every attached device that the initiator it was talking to is gone.
1637    fn clear_nexus_state(&self) {
1638        for disk in self.disks.read().values() {
1639            disk.disk.clear_nexus_state();
1640        }
1641    }
1642}
1643
1644pub struct ScsiController {
1645    state: Arc<ScsiControllerState>,
1646}
1647
1648impl ScsiController {
1649    pub fn new() -> Self {
1650        Self::new_with_poll_mode_queue_depth(None)
1651    }
1652
1653    pub fn new_with_poll_mode_queue_depth(poll_mode_queue_depth: Option<u32>) -> Self {
1654        Self {
1655            state: Arc::new(ScsiControllerState {
1656                disks: Default::default(),
1657                rescan_notification_source: Mutex::new(Vec::new()),
1658                poll_mode_queue_depth: AtomicU32::new(
1659                    poll_mode_queue_depth.unwrap_or(DEFAULT_POLL_MODE_QUEUE_DEPTH),
1660                ),
1661            }),
1662        }
1663    }
1664
1665    pub fn attach(&self, path: ScsiPath, disk: ScsiControllerDisk) -> Result<(), ScsiPathInUse> {
1666        match self.state.disks.write().entry(path) {
1667            Entry::Occupied(_) => return Err(ScsiPathInUse(path)),
1668            Entry::Vacant(entry) => entry.insert(disk),
1669        };
1670        for source in self.state.rescan_notification_source.lock().iter_mut() {
1671            // Ok to ignore errors here. If the channel is full a previous notification has not yet
1672            // been processed by the primary channel worker.
1673            source.try_send(()).ok();
1674        }
1675        Ok(())
1676    }
1677
1678    pub fn remove(&self, path: ScsiPath) -> Result<(), ScsiPathNotInUse> {
1679        match self.state.disks.write().entry(path) {
1680            Entry::Vacant(_) => return Err(ScsiPathNotInUse(path)),
1681            Entry::Occupied(entry) => {
1682                entry.remove();
1683            }
1684        }
1685        for source in self.state.rescan_notification_source.lock().iter_mut() {
1686            // Ok to ignore errors here. If the channel is full a previous notification has not yet
1687            // been processed by the primary channel worker.
1688            source.try_send(()).ok();
1689        }
1690        Ok(())
1691    }
1692}
1693
1694impl ScsiControllerState {
1695    fn add_rescan_notification_source(&self, source: futures::channel::mpsc::Sender<()>) {
1696        self.rescan_notification_source.lock().push(source);
1697    }
1698
1699    fn remove_rescan_notification_source(&self, target: &futures::channel::mpsc::Receiver<()>) {
1700        let mut sources = self.rescan_notification_source.lock();
1701        if let Some(index) = sources
1702            .iter()
1703            .position(|source| source.is_connected_to(target))
1704        {
1705            sources.remove(index);
1706        }
1707    }
1708}
1709
1710#[async_trait]
1711impl VmbusDevice for StorageDevice {
1712    fn offer(&self) -> OfferParams {
1713        if let Some(path) = self.ide_path {
1714            let offer_properties = storvsp_protocol::OfferProperties {
1715                path_id: path.path,
1716                target_id: path.target,
1717                flags: storvsp_protocol::OFFER_PROPERTIES_FLAG_IDE_DEVICE,
1718                ..FromZeros::new_zeroed()
1719            };
1720            let mut user_defined = UserDefinedData::new_zeroed();
1721            offer_properties
1722                .write_to_prefix(&mut user_defined[..])
1723                .unwrap();
1724            OfferParams {
1725                interface_name: "ide-accel".to_owned(),
1726                instance_id: self.instance_id,
1727                interface_id: storvsp_protocol::IDE_ACCELERATOR_INTERFACE_ID,
1728                channel_type: ChannelType::Interface { user_defined },
1729                ..Default::default()
1730            }
1731        } else {
1732            OfferParams {
1733                interface_name: "scsi".to_owned(),
1734                instance_id: self.instance_id,
1735                interface_id: storvsp_protocol::SCSI_INTERFACE_ID,
1736                ..Default::default()
1737            }
1738        }
1739    }
1740
1741    fn max_subchannels(&self) -> u16 {
1742        self.max_sub_channel_count
1743    }
1744
1745    fn install(&mut self, resources: DeviceResources) {
1746        self.resources = resources;
1747    }
1748
1749    async fn open(
1750        &mut self,
1751        channel_index: u16,
1752        open_request: &OpenRequest,
1753    ) -> Result<(), ChannelOpenError> {
1754        tracing::debug!(channel_index, "scsi open channel");
1755        self.new_worker(open_request, channel_index)?;
1756        self.workers[channel_index as usize].worker.start();
1757        Ok(())
1758    }
1759
1760    async fn close(&mut self, channel_index: u16) {
1761        tracing::debug!(channel_index, "scsi close channel");
1762        let worker = &mut self.workers[channel_index as usize].worker;
1763        worker.stop().await;
1764        if worker.state_mut().is_some() {
1765            worker
1766                .state_mut()
1767                .unwrap()
1768                .wait_for_scsi_requests_complete()
1769                .await;
1770            worker.remove();
1771        }
1772        if channel_index == 0 {
1773            *self.protocol.state.write() = ProtocolState::Init(InitState::Begin);
1774            // Closing the primary channel ends the initiator-target nexus, so the
1775            // state scoped to it goes with the protocol state. A machine reset
1776            // arrives here too: the server closes every open channel on the way
1777            // down (vmbus_server's client_release_channel notifies Action::Close),
1778            // and without this the next boot inherits the previous guest's tray
1779            // lock and pending sense.
1780            // Correctness here rests on channel 0 being closed last, which vmbus
1781            // does in both paths: the teardown loop closes subchannels in reverse
1782            // index order, and the close-request path closes 1..len before
1783            // channel 0. Each of those closes stops and drains its worker, so
1784            // none is left to repopulate the state cleared below. `VmbusDevice`
1785            // does not document that order.
1786            self.controller.clear_nexus_state();
1787        }
1788    }
1789
1790    async fn retarget_vp(&mut self, channel_index: u16, target_vp: u32) {
1791        self.workers[channel_index as usize]
1792            .driver
1793            .retarget_vp(target_vp);
1794    }
1795
1796    fn start(&mut self) {
1797        for task in self
1798            .workers
1799            .iter_mut()
1800            .filter(|task| task.worker.has_state() && !task.worker.is_running())
1801        {
1802            task.worker.start();
1803        }
1804    }
1805
1806    async fn stop(&mut self) {
1807        tracing::debug!(instance_id = ?self.instance_id, "StorageDevice stopping...");
1808        for task in self
1809            .workers
1810            .iter_mut()
1811            .filter(|task| task.worker.has_state() && task.worker.is_running())
1812        {
1813            task.worker.stop().await;
1814        }
1815    }
1816
1817    fn supports_save_restore(&mut self) -> Option<&mut dyn SaveRestoreVmbusDevice> {
1818        Some(self)
1819    }
1820}
1821
1822#[async_trait]
1823impl SaveRestoreVmbusDevice for StorageDevice {
1824    async fn save(&mut self) -> Result<SavedStateBlob, SaveError> {
1825        Ok(SavedStateBlob::new(self.save()?))
1826    }
1827
1828    async fn restore(
1829        &mut self,
1830        control: RestoreControl<'_>,
1831        state: SavedStateBlob,
1832    ) -> Result<(), RestoreError> {
1833        self.restore(control, state.parse()?).await
1834    }
1835}
1836
1837#[cfg(test)]
1838mod tests {
1839    use super::*;
1840    use crate::test_helpers::TestWorker;
1841    use crate::test_helpers::parse_guest_completion;
1842    use crate::test_helpers::parse_guest_completion_check_flags_status;
1843    use pal_async::DefaultDriver;
1844    use pal_async::async_test;
1845    use scsi::srb::SrbStatus;
1846    use scsi_buffers::OwnedRequestBuffers;
1847    use test_with_tracing::test;
1848    use vmbus_channel::connected_async_channels;
1849    use vmcore::vm_task::SingleDriverBackend;
1850    use vmcore::vm_task::VmTaskDriverSource;
1851
1852    // Discourage `Clone` for `ScsiController` outside the crate, but it is
1853    // necessary for testing. The fuzzer also uses `TestWorker`, which needs
1854    // a `clone` of the inner state, but is not in this crate.
1855    impl Clone for ScsiController {
1856        fn clone(&self) -> Self {
1857            ScsiController {
1858                state: self.state.clone(),
1859            }
1860        }
1861    }
1862
1863    #[async_test]
1864    async fn test_channel_working(driver: DefaultDriver) {
1865        // set up the channels and worker
1866        let (host, guest) = connected_async_channels(16 * 1024);
1867        let guest_queue = Queue::new(guest).unwrap();
1868
1869        let test_guest_mem = GuestMemory::allocate(16384);
1870        let controller = ScsiController::new();
1871        let disk = scsidisk::SimpleScsiDisk::new(
1872            disklayer_ram::ram_disk(10 * 1024 * 1024, false).unwrap(),
1873            Default::default(),
1874        );
1875        controller
1876            .attach(
1877                ScsiPath {
1878                    path: 0,
1879                    target: 0,
1880                    lun: 0,
1881                },
1882                ScsiControllerDisk::new(Arc::new(disk)),
1883            )
1884            .unwrap();
1885
1886        let test_worker = TestWorker::start(
1887            controller.clone(),
1888            driver.clone(),
1889            test_guest_mem.clone(),
1890            host,
1891            None,
1892        );
1893
1894        let mut guest = test_helpers::TestGuest {
1895            queue: guest_queue,
1896            transaction_id: 0,
1897        };
1898
1899        guest.perform_protocol_negotiation().await;
1900
1901        // Set up the buffer for a write request
1902        const IO_LEN: usize = 4 * 1024;
1903        let write_buf = [7u8; IO_LEN];
1904        let write_gpa = 4 * 1024u64;
1905        test_guest_mem.write_at(write_gpa, &write_buf).unwrap();
1906        guest
1907            .send_write_packet(ScsiPath::default(), write_gpa, 1, IO_LEN)
1908            .await;
1909
1910        guest
1911            .verify_completion(|p| test_helpers::parse_guest_completed_io(p, SrbStatus::SUCCESS))
1912            .await;
1913
1914        let read_gpa = 8 * 1024u64;
1915        guest
1916            .send_read_packet(ScsiPath::default(), read_gpa, 1, IO_LEN)
1917            .await;
1918
1919        guest
1920            .verify_completion(|p| test_helpers::parse_guest_completed_io(p, SrbStatus::SUCCESS))
1921            .await;
1922        let mut read_buf = [0u8; IO_LEN];
1923        test_guest_mem.read_at(read_gpa, &mut read_buf).unwrap();
1924        for (b1, b2) in read_buf.iter().zip(write_buf.iter()) {
1925            assert_eq!(b1, b2);
1926        }
1927
1928        // stop everything
1929        guest.verify_graceful_close(test_worker).await;
1930    }
1931
1932    #[async_test]
1933    async fn test_packet_sizes(driver: DefaultDriver) {
1934        // set up the channels and worker
1935        let (host, guest) = connected_async_channels(16384);
1936        let guest_queue = Queue::new(guest).unwrap();
1937
1938        let test_guest_mem = GuestMemory::allocate(1024);
1939        let controller = ScsiController::new();
1940
1941        let _worker = TestWorker::start(
1942            controller.clone(),
1943            driver.clone(),
1944            test_guest_mem,
1945            host,
1946            None,
1947        );
1948
1949        let mut guest = test_helpers::TestGuest {
1950            queue: guest_queue,
1951            transaction_id: 0,
1952        };
1953
1954        let negotiate_packet = storvsp_protocol::Packet {
1955            operation: storvsp_protocol::Operation::BEGIN_INITIALIZATION,
1956            flags: 0,
1957            status: storvsp_protocol::NtStatus::SUCCESS,
1958        };
1959        guest
1960            .send_data_packet_sync(&[negotiate_packet.as_bytes()])
1961            .await;
1962
1963        guest.verify_completion(parse_guest_completion).await;
1964
1965        let header = storvsp_protocol::Packet {
1966            operation: storvsp_protocol::Operation::QUERY_PROTOCOL_VERSION,
1967            flags: 0,
1968            status: storvsp_protocol::NtStatus::SUCCESS,
1969        };
1970
1971        let mut buf = [0u8; 128];
1972        storvsp_protocol::ProtocolVersion {
1973            major_minor: !0,
1974            reserved: 0,
1975        }
1976        .write_to_prefix(&mut buf[..])
1977        .unwrap(); // PANIC: Infallable since `ProtcolVersion` is less than 128 bytes
1978
1979        for &(len, resp_len) in &[(48, 48), (50, 56), (56, 56), (64, 64), (72, 64)] {
1980            guest
1981                .send_data_packet_sync(&[header.as_bytes(), &buf[..len - size_of_val(&header)]])
1982                .await;
1983
1984            guest
1985                .verify_completion(|packet| {
1986                    let IncomingPacket::Completion(packet) = packet else {
1987                        unreachable!()
1988                    };
1989                    assert_eq!(packet.reader().len(), resp_len);
1990                    assert_eq!(
1991                        packet
1992                            .reader()
1993                            .read_plain::<storvsp_protocol::Packet>()
1994                            .unwrap()
1995                            .status,
1996                        storvsp_protocol::NtStatus::REVISION_MISMATCH
1997                    );
1998                    Ok(())
1999                })
2000                .await;
2001        }
2002    }
2003
2004    #[async_test]
2005    async fn test_wrong_first_packet(driver: DefaultDriver) {
2006        // set up the channels and worker
2007        let (host, guest) = connected_async_channels(16384);
2008        let guest_queue = Queue::new(guest).unwrap();
2009
2010        let test_guest_mem = GuestMemory::allocate(1024);
2011        let controller = ScsiController::new();
2012
2013        let _worker = TestWorker::start(
2014            controller.clone(),
2015            driver.clone(),
2016            test_guest_mem,
2017            host,
2018            None,
2019        );
2020
2021        let mut guest = test_helpers::TestGuest {
2022            queue: guest_queue,
2023            transaction_id: 0,
2024        };
2025
2026        // Protocol negotiation done out of order
2027        let negotiate_packet = storvsp_protocol::Packet {
2028            operation: storvsp_protocol::Operation::END_INITIALIZATION,
2029            flags: 0,
2030            status: storvsp_protocol::NtStatus::SUCCESS,
2031        };
2032        guest
2033            .send_data_packet_sync(&[negotiate_packet.as_bytes()])
2034            .await;
2035
2036        guest
2037            .verify_completion(|packet| {
2038                let IncomingPacket::Completion(packet) = packet else {
2039                    unreachable!()
2040                };
2041                assert_eq!(
2042                    packet
2043                        .reader()
2044                        .read_plain::<storvsp_protocol::Packet>()
2045                        .unwrap()
2046                        .status,
2047                    storvsp_protocol::NtStatus::INVALID_DEVICE_STATE
2048                );
2049                Ok(())
2050            })
2051            .await;
2052    }
2053
2054    #[async_test]
2055    async fn test_unrecognized_operation(driver: DefaultDriver) {
2056        // set up the channels and worker
2057        let (host, guest) = connected_async_channels(16384);
2058        let guest_queue = Queue::new(guest).unwrap();
2059
2060        let test_guest_mem = GuestMemory::allocate(1024);
2061        let controller = ScsiController::new();
2062
2063        let worker = TestWorker::start(
2064            controller.clone(),
2065            driver.clone(),
2066            test_guest_mem,
2067            host,
2068            None,
2069        );
2070
2071        let mut guest = test_helpers::TestGuest {
2072            queue: guest_queue,
2073            transaction_id: 0,
2074        };
2075
2076        // Send packet with unrecognized operation
2077        let negotiate_packet = storvsp_protocol::Packet {
2078            operation: storvsp_protocol::Operation::REMOVE_DEVICE,
2079            flags: 0,
2080            status: storvsp_protocol::NtStatus::SUCCESS,
2081        };
2082        guest
2083            .send_data_packet_sync(&[negotiate_packet.as_bytes()])
2084            .await;
2085
2086        match worker.teardown().await {
2087            Err(WorkerError::PacketError(PacketError::UnrecognizedOperation(
2088                storvsp_protocol::Operation::REMOVE_DEVICE,
2089            ))) => {}
2090            result => panic!("Worker failed with unexpected result {:?}!", result),
2091        }
2092    }
2093
2094    #[async_test]
2095    async fn test_too_many_subchannels(driver: DefaultDriver) {
2096        // set up the channels and worker
2097        let (host, guest) = connected_async_channels(16384);
2098        let guest_queue = Queue::new(guest).unwrap();
2099
2100        let test_guest_mem = GuestMemory::allocate(1024);
2101        let controller = ScsiController::new();
2102
2103        let _worker = TestWorker::start(
2104            controller.clone(),
2105            driver.clone(),
2106            test_guest_mem,
2107            host,
2108            None,
2109        );
2110
2111        let mut guest = test_helpers::TestGuest {
2112            queue: guest_queue,
2113            transaction_id: 0,
2114        };
2115
2116        let negotiate_packet = storvsp_protocol::Packet {
2117            operation: storvsp_protocol::Operation::BEGIN_INITIALIZATION,
2118            flags: 0,
2119            status: storvsp_protocol::NtStatus::SUCCESS,
2120        };
2121        guest
2122            .send_data_packet_sync(&[negotiate_packet.as_bytes()])
2123            .await;
2124        guest.verify_completion(parse_guest_completion).await;
2125
2126        let version_packet = storvsp_protocol::Packet {
2127            operation: storvsp_protocol::Operation::QUERY_PROTOCOL_VERSION,
2128            flags: 0,
2129            status: storvsp_protocol::NtStatus::SUCCESS,
2130        };
2131        let version = storvsp_protocol::ProtocolVersion {
2132            major_minor: storvsp_protocol::VERSION_BLUE,
2133            reserved: 0,
2134        };
2135        guest
2136            .send_data_packet_sync(&[version_packet.as_bytes(), version.as_bytes()])
2137            .await;
2138        guest.verify_completion(parse_guest_completion).await;
2139
2140        let properties_packet = storvsp_protocol::Packet {
2141            operation: storvsp_protocol::Operation::QUERY_PROPERTIES,
2142            flags: 0,
2143            status: storvsp_protocol::NtStatus::SUCCESS,
2144        };
2145        guest
2146            .send_data_packet_sync(&[properties_packet.as_bytes()])
2147            .await;
2148
2149        guest.verify_completion(parse_guest_completion).await;
2150
2151        let negotiate_packet = storvsp_protocol::Packet {
2152            operation: storvsp_protocol::Operation::CREATE_SUB_CHANNELS,
2153            flags: 0,
2154            status: storvsp_protocol::NtStatus::SUCCESS,
2155        };
2156        // Create sub channels more than maximum_sub_channel_count
2157        guest
2158            .send_data_packet_sync(&[negotiate_packet.as_bytes(), 1_u16.as_bytes()])
2159            .await;
2160
2161        guest
2162            .verify_completion(|packet| {
2163                let IncomingPacket::Completion(packet) = packet else {
2164                    unreachable!()
2165                };
2166                assert_eq!(
2167                    packet
2168                        .reader()
2169                        .read_plain::<storvsp_protocol::Packet>()
2170                        .unwrap()
2171                        .status,
2172                    storvsp_protocol::NtStatus::INVALID_PARAMETER
2173                );
2174                Ok(())
2175            })
2176            .await;
2177    }
2178
2179    #[async_test]
2180    async fn test_begin_init_on_ready(driver: DefaultDriver) {
2181        // set up the channels and worker
2182        let (host, guest) = connected_async_channels(16384);
2183        let guest_queue = Queue::new(guest).unwrap();
2184
2185        let test_guest_mem = GuestMemory::allocate(1024);
2186        let controller = ScsiController::new();
2187
2188        let _worker = TestWorker::start(
2189            controller.clone(),
2190            driver.clone(),
2191            test_guest_mem,
2192            host,
2193            None,
2194        );
2195
2196        let mut guest = test_helpers::TestGuest {
2197            queue: guest_queue,
2198            transaction_id: 0,
2199        };
2200
2201        guest.perform_protocol_negotiation().await;
2202
2203        // Protocol negotiation done out of order
2204        let negotiate_packet = storvsp_protocol::Packet {
2205            operation: storvsp_protocol::Operation::BEGIN_INITIALIZATION,
2206            flags: 0,
2207            status: storvsp_protocol::NtStatus::SUCCESS,
2208        };
2209        guest
2210            .send_data_packet_sync(&[negotiate_packet.as_bytes()])
2211            .await;
2212
2213        guest
2214            .verify_completion(|p| {
2215                parse_guest_completion_check_flags_status(
2216                    p,
2217                    0,
2218                    storvsp_protocol::NtStatus::INVALID_DEVICE_STATE,
2219                )
2220            })
2221            .await;
2222    }
2223
2224    #[async_test]
2225    async fn test_hot_add_remove(driver: DefaultDriver) {
2226        // set up channels and worker.
2227        let (host, guest) = connected_async_channels(16 * 1024);
2228        let guest_queue = Queue::new(guest).unwrap();
2229
2230        let test_guest_mem = GuestMemory::allocate(16384);
2231        // create a controller with no disk yet.
2232        let controller = ScsiController::new();
2233
2234        let test_worker = TestWorker::start(
2235            controller.clone(),
2236            driver.clone(),
2237            test_guest_mem.clone(),
2238            host,
2239            None,
2240        );
2241
2242        let mut guest = test_helpers::TestGuest {
2243            queue: guest_queue,
2244            transaction_id: 0,
2245        };
2246
2247        guest.perform_protocol_negotiation().await;
2248
2249        // Verify no LUNs are reported initially.
2250        let mut lun_list_buffer: [u8; 256] = [0; 256];
2251        let mut disk_count = 0;
2252        guest
2253            .send_report_luns_packet(ScsiPath::default(), 0, lun_list_buffer.len())
2254            .await;
2255        guest
2256            .verify_completion(|p| {
2257                test_helpers::parse_guest_completed_io_check_tx_len(p, SrbStatus::SUCCESS, Some(8))
2258            })
2259            .await;
2260        test_guest_mem.read_at(0, &mut lun_list_buffer).unwrap();
2261        let lun_list_size = u32::from_be_bytes(lun_list_buffer[0..4].try_into().unwrap());
2262        assert_eq!(lun_list_size, disk_count as u32 * 8);
2263
2264        // Set up a buffer for writes.
2265        const IO_LEN: usize = 4 * 1024;
2266        let write_buf = [7u8; IO_LEN];
2267        let write_gpa = 4 * 1024u64;
2268        test_guest_mem.write_at(write_gpa, &write_buf).unwrap();
2269
2270        guest
2271            .send_write_packet(ScsiPath::default(), write_gpa, 1, IO_LEN)
2272            .await;
2273        guest
2274            .verify_completion(|p| {
2275                test_helpers::parse_guest_completed_io(p, SrbStatus::INVALID_LUN)
2276            })
2277            .await;
2278
2279        // Add some disks while the guest is running.
2280        for lun in 0..4 {
2281            let disk = scsidisk::SimpleScsiDisk::new(
2282                disklayer_ram::ram_disk(10 * 1024 * 1024, false).unwrap(),
2283                Default::default(),
2284            );
2285            controller
2286                .attach(
2287                    ScsiPath {
2288                        path: 0,
2289                        target: 0,
2290                        lun,
2291                    },
2292                    ScsiControllerDisk::new(Arc::new(disk)),
2293                )
2294                .unwrap();
2295            guest
2296                .verify_completion(test_helpers::parse_guest_enumerate_bus)
2297                .await;
2298
2299            disk_count += 1;
2300            guest
2301                .send_report_luns_packet(ScsiPath::default(), 0, 256)
2302                .await;
2303            guest
2304                .verify_completion(|p| {
2305                    test_helpers::parse_guest_completed_io_check_tx_len(
2306                        p,
2307                        SrbStatus::SUCCESS,
2308                        Some((disk_count + 1) * 8),
2309                    )
2310                })
2311                .await;
2312            test_guest_mem.read_at(0, &mut lun_list_buffer).unwrap();
2313            let lun_list_size = u32::from_be_bytes(lun_list_buffer[0..4].try_into().unwrap());
2314            assert_eq!(lun_list_size, disk_count as u32 * 8);
2315
2316            guest
2317                .send_write_packet(
2318                    ScsiPath {
2319                        path: 0,
2320                        target: 0,
2321                        lun,
2322                    },
2323                    write_gpa,
2324                    1,
2325                    IO_LEN,
2326                )
2327                .await;
2328            guest
2329                .verify_completion(|p| {
2330                    test_helpers::parse_guest_completed_io(p, SrbStatus::SUCCESS)
2331                })
2332                .await;
2333        }
2334
2335        // Remove all disks while the guest is running.
2336        for lun in 0..4 {
2337            controller
2338                .remove(ScsiPath {
2339                    path: 0,
2340                    target: 0,
2341                    lun,
2342                })
2343                .unwrap();
2344            guest
2345                .verify_completion(test_helpers::parse_guest_enumerate_bus)
2346                .await;
2347
2348            disk_count -= 1;
2349            guest
2350                .send_report_luns_packet(ScsiPath::default(), 0, 4096)
2351                .await;
2352            guest
2353                .verify_completion(|p| {
2354                    test_helpers::parse_guest_completed_io_check_tx_len(
2355                        p,
2356                        SrbStatus::SUCCESS,
2357                        Some((disk_count + 1) * 8),
2358                    )
2359                })
2360                .await;
2361            test_guest_mem.read_at(0, &mut lun_list_buffer).unwrap();
2362            let lun_list_size = u32::from_be_bytes(lun_list_buffer[0..4].try_into().unwrap());
2363            assert_eq!(lun_list_size, disk_count as u32 * 8);
2364
2365            guest
2366                .send_write_packet(
2367                    ScsiPath {
2368                        path: 0,
2369                        target: 0,
2370                        lun,
2371                    },
2372                    write_gpa,
2373                    1,
2374                    IO_LEN,
2375                )
2376                .await;
2377            guest
2378                .verify_completion(|p| {
2379                    test_helpers::parse_guest_completed_io(p, SrbStatus::INVALID_LUN)
2380                })
2381                .await;
2382        }
2383
2384        guest.verify_graceful_close(test_worker).await;
2385    }
2386
2387    #[async_test]
2388    async fn test_async_disk(driver: DefaultDriver) {
2389        let device = disklayer_ram::ram_disk(64 * 1024, false).unwrap();
2390        let controller = ScsiController::new();
2391        let disk = ScsiControllerDisk::new(Arc::new(scsidisk::SimpleScsiDisk::new(
2392            device,
2393            Default::default(),
2394        )));
2395        controller
2396            .attach(
2397                ScsiPath {
2398                    path: 0,
2399                    target: 0,
2400                    lun: 0,
2401                },
2402                disk,
2403            )
2404            .unwrap();
2405
2406        let (host, guest) = connected_async_channels(16 * 1024);
2407        let guest_queue = Queue::new(guest).unwrap();
2408
2409        let mut guest = test_helpers::TestGuest {
2410            queue: guest_queue,
2411            transaction_id: 0,
2412        };
2413
2414        let test_guest_mem = GuestMemory::allocate(16384);
2415        let worker = TestWorker::start(
2416            controller.clone(),
2417            &driver,
2418            test_guest_mem.clone(),
2419            host,
2420            None,
2421        );
2422
2423        let negotiate_packet = storvsp_protocol::Packet {
2424            operation: storvsp_protocol::Operation::BEGIN_INITIALIZATION,
2425            flags: 0,
2426            status: storvsp_protocol::NtStatus::SUCCESS,
2427        };
2428        guest
2429            .send_data_packet_sync(&[negotiate_packet.as_bytes()])
2430            .await;
2431        guest.verify_completion(parse_guest_completion).await;
2432
2433        let version_packet = storvsp_protocol::Packet {
2434            operation: storvsp_protocol::Operation::QUERY_PROTOCOL_VERSION,
2435            flags: 0,
2436            status: storvsp_protocol::NtStatus::SUCCESS,
2437        };
2438        let version = storvsp_protocol::ProtocolVersion {
2439            major_minor: storvsp_protocol::VERSION_BLUE,
2440            reserved: 0,
2441        };
2442        guest
2443            .send_data_packet_sync(&[version_packet.as_bytes(), version.as_bytes()])
2444            .await;
2445        guest.verify_completion(parse_guest_completion).await;
2446
2447        let properties_packet = storvsp_protocol::Packet {
2448            operation: storvsp_protocol::Operation::QUERY_PROPERTIES,
2449            flags: 0,
2450            status: storvsp_protocol::NtStatus::SUCCESS,
2451        };
2452        guest
2453            .send_data_packet_sync(&[properties_packet.as_bytes()])
2454            .await;
2455        guest.verify_completion(parse_guest_completion).await;
2456
2457        let negotiate_packet = storvsp_protocol::Packet {
2458            operation: storvsp_protocol::Operation::END_INITIALIZATION,
2459            flags: 0,
2460            status: storvsp_protocol::NtStatus::SUCCESS,
2461        };
2462        guest
2463            .send_data_packet_sync(&[negotiate_packet.as_bytes()])
2464            .await;
2465        guest.verify_completion(parse_guest_completion).await;
2466
2467        const IO_LEN: usize = 4 * 1024;
2468        let write_buf = [7u8; IO_LEN];
2469        let write_gpa = 4 * 1024u64;
2470        test_guest_mem.write_at(write_gpa, &write_buf).unwrap();
2471        guest
2472            .send_write_packet(ScsiPath::default(), write_gpa, 1, IO_LEN)
2473            .await;
2474        guest
2475            .verify_completion(|p| test_helpers::parse_guest_completed_io(p, SrbStatus::SUCCESS))
2476            .await;
2477
2478        let read_gpa = 8 * 1024u64;
2479        guest
2480            .send_read_packet(ScsiPath::default(), read_gpa, 1, IO_LEN)
2481            .await;
2482        guest
2483            .verify_completion(|p| test_helpers::parse_guest_completed_io(p, SrbStatus::SUCCESS))
2484            .await;
2485        let mut read_buf = [0u8; IO_LEN];
2486        test_guest_mem.read_at(read_gpa, &mut read_buf).unwrap();
2487        for (b1, b2) in read_buf.iter().zip(write_buf.iter()) {
2488            assert_eq!(b1, b2);
2489        }
2490
2491        guest.verify_graceful_close(worker).await;
2492    }
2493    fn make_medium_removal_request(prevent: bool) -> Request {
2494        let cdb = scsi::CdbMediaRemoval {
2495            operation_code: ScsiOp::MEDIUM_REMOVAL,
2496            lun: 0,
2497            reserved: [0; 2],
2498            flags: scsi::MediaRemovalFlags::new()
2499                .with_prevent(prevent)
2500                .with_persistent(false),
2501        };
2502        let mut data = [0u8; 16];
2503        data[..size_of::<scsi::CdbMediaRemoval>()].copy_from_slice(cdb.as_bytes());
2504        Request {
2505            cdb: data,
2506            srb_flags: 0,
2507        }
2508    }
2509
2510    fn make_eject_request() -> Request {
2511        let cdb = scsi::StartStop {
2512            operation_code: ScsiOp::START_STOP_UNIT,
2513            immediate: 0,
2514            reserved2: [0; 2],
2515            flag: scsi::StartStopFlags::new()
2516                .with_start(false)
2517                .with_load_eject(true),
2518            control: 0,
2519        };
2520        let mut data = [0u8; 16];
2521        data[..size_of::<scsi::StartStop>()].copy_from_slice(cdb.as_bytes());
2522        Request {
2523            cdb: data,
2524            srb_flags: 0,
2525        }
2526    }
2527
2528    /// Closing the primary channel is what ends the nexus, so it is what has to
2529    /// be tested.
2530    ///
2531    /// The scsidisk tests call `clear_nexus_state` on the device directly, which
2532    /// says the device clears its own state but nothing about the caller. With
2533    /// only those, deleting the `clear_nexus_state` call in `close()` leaves
2534    /// both of them green while the reopened-channel and machine-reset behaviour
2535    /// comes back. This drives the real `VmbusDevice::close` and observes the
2536    /// result the way an initiator would, through a SCSI command, rather than
2537    /// through the device's internals.
2538    #[async_test]
2539    async fn test_close_ends_the_nexus(driver: DefaultDriver) {
2540        let controller = ScsiController::new();
2541        let dvd = Arc::new(scsidisk::scsidvd::SimpleScsiDvd::new(Some(
2542            disklayer_ram::ram_disk(2 * 1024 * 1024, false).unwrap(),
2543        )));
2544        controller
2545            .attach(
2546                ScsiPath {
2547                    path: 0,
2548                    target: 0,
2549                    lun: 0,
2550                },
2551                ScsiControllerDisk::new(dvd.clone()),
2552            )
2553            .unwrap();
2554
2555        let mut device = StorageDevice::build_scsi(
2556            &VmTaskDriverSource::new(SingleDriverBackend::new(driver)),
2557            &controller,
2558            Guid::new_random(),
2559            0,
2560            64,
2561        );
2562
2563        let external_data = OwnedRequestBuffers::new(&[0]);
2564        let guest_mem = GuestMemory::allocate(4096);
2565        let buffers = external_data.buffer(&guest_mem);
2566
2567        // The initiator locks the tray. This is the nexus-scoped state that must
2568        // not reach whoever opens the channel next.
2569        let locked = dvd
2570            .execute_scsi(&buffers, &make_medium_removal_request(true))
2571            .await;
2572        assert_eq!(locked.scsi_status, ScsiStatus::GOOD);
2573
2574        // Control: while the nexus lives the lock holds, so the eject is refused.
2575        // Without this the final assertion would only show that ejecting works.
2576        let refused = dvd.execute_scsi(&buffers, &make_eject_request()).await;
2577        assert_ne!(
2578            refused.scsi_status,
2579            ScsiStatus::GOOD,
2580            "the tray lock did not take, so the rest of this test proves nothing"
2581        );
2582
2583        device.close(0).await;
2584
2585        // Same command, same drive, same media: it succeeds only because closing
2586        // the primary channel dropped the lock.
2587        let allowed = dvd.execute_scsi(&buffers, &make_eject_request()).await;
2588        assert_eq!(
2589            allowed.scsi_status,
2590            ScsiStatus::GOOD,
2591            "the tray lock outlived the nexus"
2592        );
2593    }
2594}