storvsp/
resolver.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Resolver for a SCSI controller.
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 thiserror::Error;
23use vm_resource::AsyncResolveResource;
24use vm_resource::ResolveError;
25use vm_resource::ResourceResolver;
26use vm_resource::declare_static_async_resolver;
27use vm_resource::kind::VmbusDeviceHandleKind;
28use vmbus_channel::resources::ResolveVmbusDeviceHandleParams;
29use vmbus_channel::resources::ResolvedVmbusDevice;
30use vmcore::vm_task::VmTaskDriverSource;
31
32/// The resolver for [`ScsiControllerHandle`].
33pub struct StorvspResolver;
34
35declare_static_async_resolver! {
36    StorvspResolver,
37    (VmbusDeviceHandleKind, ScsiControllerHandle),
38}
39
40/// An error returned by [`StorvspResolver`].
41#[derive(Debug, Error)]
42pub enum Error {
43    #[error(transparent)]
44    ScsiPathInUse(ScsiPathInUse),
45    #[error("failed to resolve scsi device at {path}")]
46    Device {
47        path: ScsiPath,
48        #[source]
49        source: ResolveError,
50    },
51}
52
53#[async_trait]
54impl AsyncResolveResource<VmbusDeviceHandleKind, ScsiControllerHandle> for StorvspResolver {
55    type Output = ResolvedVmbusDevice;
56    type Error = Error;
57
58    async fn resolve(
59        &self,
60        resolver: &ResourceResolver,
61        resource: ScsiControllerHandle,
62        input: ResolveVmbusDeviceHandleParams<'_>,
63    ) -> Result<Self::Output, Self::Error> {
64        let controller = ScsiController::new();
65        let device = StorageDevice::build_scsi(
66            input.driver_source,
67            &controller,
68            resource.instance_id,
69            resource.max_sub_channel_count,
70            resource.io_queue_depth.unwrap_or(256),
71        );
72
73        for ScsiDeviceAndPath { path, device } in resource.devices {
74            let device = resolver
75                .resolve(
76                    device,
77                    ResolveScsiDeviceHandleParams {
78                        driver_source: input.driver_source,
79                    },
80                )
81                .await
82                .map_err(|err| Error::Device { path, source: err })?;
83
84            controller
85                .attach(path, ScsiControllerDisk { disk: device.0 })
86                .map_err(Error::ScsiPathInUse)?;
87        }
88
89        let driver = input.driver_source.simple();
90        if let Some(requests) = resource.requests {
91            driver
92                .spawn(
93                    "storvsp-requests",
94                    handle_requests(
95                        input.driver_source.clone(),
96                        Arc::downgrade(&controller.state),
97                        resolver.clone(),
98                        requests,
99                    ),
100                )
101                .detach();
102        }
103
104        Ok(device.into())
105    }
106}
107
108async fn handle_requests(
109    driver_source: VmTaskDriverSource,
110    state: Weak<ScsiControllerState>,
111    resolver: ResourceResolver,
112    mut requests: mesh::Receiver<ScsiControllerRequest>,
113) {
114    while let Some(req) = requests.next().await {
115        match req {
116            ScsiControllerRequest::AddDevice(rpc) => {
117                rpc.handle_failable(async |ScsiDeviceAndPath { path, device }| {
118                    let device = resolver
119                        .resolve(
120                            device,
121                            ResolveScsiDeviceHandleParams {
122                                driver_source: &driver_source,
123                            },
124                        )
125                        .await
126                        .context("failed to resolve media")?;
127
128                    if let Some(state) = state.upgrade() {
129                        ScsiController { state }
130                            .attach(path, ScsiControllerDisk::new(device.0))
131                            .context("failed to attach device")?;
132                    }
133                    anyhow::Ok(())
134                })
135                .await
136            }
137            ScsiControllerRequest::RemoveDevice(rpc) => rpc.handle_failable_sync(|path| {
138                if let Some(state) = state.upgrade() {
139                    ScsiController { state }
140                        .remove(path)
141                        .context("failed to remove device")?;
142                }
143                anyhow::Ok(())
144            }),
145        }
146    }
147}