Skip to main content

storvsp/
resolver.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Resolvers for storvsp SCSI and IDE accelerator devices.
5
6use super::StorageDevice;
7use crate::ScsiController;
8use crate::ScsiControllerDisk;
9use crate::ScsiControllerState;
10use crate::ScsiPathInUse;
11use anyhow::Context;
12use async_trait::async_trait;
13use futures::StreamExt;
14use pal_async::task::Spawn;
15use scsi_core::ResolveScsiDeviceHandleParams;
16use std::sync::Arc;
17use std::sync::Weak;
18use storvsp_resources::ScsiControllerHandle;
19use storvsp_resources::ScsiControllerRequest;
20use storvsp_resources::ScsiDeviceAndPath;
21use storvsp_resources::ScsiPath;
22use storvsp_resources::StorvspIdeDeviceHandle;
23use thiserror::Error;
24use vm_resource::AsyncResolveResource;
25use vm_resource::ResolveError;
26use vm_resource::ResourceResolver;
27use vm_resource::declare_static_async_resolver;
28use vm_resource::kind::VmbusDeviceHandleKind;
29use vmbus_channel::resources::ResolveVmbusDeviceHandleParams;
30use vmbus_channel::resources::ResolvedVmbusDevice;
31use vmcore::vm_task::VmTaskDriverSource;
32
33/// Default I/O queue depth when not explicitly configured.
34const DEFAULT_IO_QUEUE_DEPTH: u32 = 256;
35
36/// The resolver for [`ScsiControllerHandle`].
37pub struct StorvspResolver;
38
39declare_static_async_resolver! {
40    StorvspResolver,
41    (VmbusDeviceHandleKind, ScsiControllerHandle),
42}
43
44/// An error returned by [`StorvspResolver`].
45#[derive(Debug, Error)]
46pub enum Error {
47    #[error(transparent)]
48    ScsiPathInUse(ScsiPathInUse),
49    #[error("failed to resolve scsi device at {path}")]
50    Device {
51        path: ScsiPath,
52        #[source]
53        source: ResolveError,
54    },
55}
56
57#[async_trait]
58impl AsyncResolveResource<VmbusDeviceHandleKind, ScsiControllerHandle> for StorvspResolver {
59    type Output = ResolvedVmbusDevice;
60    type Error = Error;
61
62    async fn resolve(
63        &self,
64        resolver: &ResourceResolver,
65        resource: ScsiControllerHandle,
66        input: ResolveVmbusDeviceHandleParams<'_>,
67    ) -> Result<Self::Output, Self::Error> {
68        let controller =
69            ScsiController::new_with_poll_mode_queue_depth(resource.poll_mode_queue_depth);
70        let device = StorageDevice::build_scsi(
71            input.driver_source,
72            &controller,
73            resource.instance_id,
74            resource.max_sub_channel_count,
75            resource.io_queue_depth.unwrap_or(DEFAULT_IO_QUEUE_DEPTH),
76        );
77
78        for ScsiDeviceAndPath { path, device } in resource.devices {
79            let device = resolver
80                .resolve(
81                    device,
82                    ResolveScsiDeviceHandleParams {
83                        driver_source: input.driver_source,
84                    },
85                )
86                .await
87                .map_err(|err| Error::Device { path, source: err })?;
88
89            controller
90                .attach(path, ScsiControllerDisk { disk: device.0 })
91                .map_err(Error::ScsiPathInUse)?;
92        }
93
94        let driver = input.driver_source.simple();
95        if let Some(requests) = resource.requests {
96            driver
97                .spawn(
98                    "storvsp-requests",
99                    handle_requests(
100                        input.driver_source.clone(),
101                        Arc::downgrade(&controller.state),
102                        resolver.clone(),
103                        requests,
104                    ),
105                )
106                .detach();
107        }
108
109        Ok(device.into())
110    }
111}
112
113async fn handle_requests(
114    driver_source: VmTaskDriverSource,
115    state: Weak<ScsiControllerState>,
116    resolver: ResourceResolver,
117    mut requests: mesh::Receiver<ScsiControllerRequest>,
118) {
119    while let Some(req) = requests.next().await {
120        match req {
121            ScsiControllerRequest::AddDevice(rpc) => {
122                rpc.handle_failable(async |ScsiDeviceAndPath { path, device }| {
123                    let device = resolver
124                        .resolve(
125                            device,
126                            ResolveScsiDeviceHandleParams {
127                                driver_source: &driver_source,
128                            },
129                        )
130                        .await
131                        .context("failed to resolve media")?;
132
133                    if let Some(state) = state.upgrade() {
134                        ScsiController { state }
135                            .attach(path, ScsiControllerDisk::new(device.0))
136                            .context("failed to attach device")?;
137                    }
138                    anyhow::Ok(())
139                })
140                .await
141            }
142            ScsiControllerRequest::RemoveDevice(rpc) => rpc.handle_failable_sync(|path| {
143                if let Some(state) = state.upgrade() {
144                    ScsiController { state }
145                        .remove(path)
146                        .context("failed to remove device")?;
147                }
148                anyhow::Ok(())
149            }),
150        }
151    }
152}
153
154/// The resolver for [`StorvspIdeDeviceHandle`].
155pub struct StorvspIdeResolver;
156
157declare_static_async_resolver! {
158    StorvspIdeResolver,
159    (VmbusDeviceHandleKind, StorvspIdeDeviceHandle),
160}
161
162/// An error returned by [`StorvspIdeResolver`].
163#[derive(Debug, Error)]
164pub enum IdeError {
165    #[error("invalid IDE channel {0} (must be 0 or 1)")]
166    InvalidChannel(u8),
167    #[error("invalid IDE device {0} (must be 0 or 1)")]
168    InvalidDevice(u8),
169    #[error("failed to resolve IDE disk at channel {0} device {1}")]
170    ResolveDisk(u8, u8, #[source] ResolveError),
171}
172
173#[async_trait]
174impl AsyncResolveResource<VmbusDeviceHandleKind, StorvspIdeDeviceHandle> for StorvspIdeResolver {
175    type Output = ResolvedVmbusDevice;
176    type Error = IdeError;
177
178    async fn resolve(
179        &self,
180        resolver: &ResourceResolver,
181        resource: StorvspIdeDeviceHandle,
182        input: ResolveVmbusDeviceHandleParams<'_>,
183    ) -> Result<Self::Output, Self::Error> {
184        if resource.channel_id > 1 {
185            return Err(IdeError::InvalidChannel(resource.channel_id));
186        }
187        if resource.device_id > 1 {
188            return Err(IdeError::InvalidDevice(resource.device_id));
189        }
190
191        let disk = resolver
192            .resolve(
193                resource.disk,
194                ResolveScsiDeviceHandleParams {
195                    driver_source: input.driver_source,
196                },
197            )
198            .await
199            .map_err(|e| IdeError::ResolveDisk(resource.channel_id, resource.device_id, e))?;
200
201        let device = StorageDevice::build_ide(
202            input.driver_source,
203            resource.channel_id,
204            resource.device_id,
205            ScsiControllerDisk::new(disk.0),
206            resource.io_queue_depth.unwrap_or(DEFAULT_IO_QUEUE_DEPTH),
207        );
208
209        Ok(device.into())
210    }
211}