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 =
65            ScsiController::new_with_poll_mode_queue_depth(resource.poll_mode_queue_depth);
66        let device = StorageDevice::build_scsi(
67            input.driver_source,
68            &controller,
69            resource.instance_id,
70            resource.max_sub_channel_count,
71            resource.io_queue_depth.unwrap_or(256),
72        );
73
74        for ScsiDeviceAndPath { path, device } in resource.devices {
75            let device = resolver
76                .resolve(
77                    device,
78                    ResolveScsiDeviceHandleParams {
79                        driver_source: input.driver_source,
80                    },
81                )
82                .await
83                .map_err(|err| Error::Device { path, source: err })?;
84
85            controller
86                .attach(path, ScsiControllerDisk { disk: device.0 })
87                .map_err(Error::ScsiPathInUse)?;
88        }
89
90        let driver = input.driver_source.simple();
91        if let Some(requests) = resource.requests {
92            driver
93                .spawn(
94                    "storvsp-requests",
95                    handle_requests(
96                        input.driver_source.clone(),
97                        Arc::downgrade(&controller.state),
98                        resolver.clone(),
99                        requests,
100                    ),
101                )
102                .detach();
103        }
104
105        Ok(device.into())
106    }
107}
108
109async fn handle_requests(
110    driver_source: VmTaskDriverSource,
111    state: Weak<ScsiControllerState>,
112    resolver: ResourceResolver,
113    mut requests: mesh::Receiver<ScsiControllerRequest>,
114) {
115    while let Some(req) = requests.next().await {
116        match req {
117            ScsiControllerRequest::AddDevice(rpc) => {
118                rpc.handle_failable(async |ScsiDeviceAndPath { path, device }| {
119                    let device = resolver
120                        .resolve(
121                            device,
122                            ResolveScsiDeviceHandleParams {
123                                driver_source: &driver_source,
124                            },
125                        )
126                        .await
127                        .context("failed to resolve media")?;
128
129                    if let Some(state) = state.upgrade() {
130                        ScsiController { state }
131                            .attach(path, ScsiControllerDisk::new(device.0))
132                            .context("failed to attach device")?;
133                    }
134                    anyhow::Ok(())
135                })
136                .await
137            }
138            ScsiControllerRequest::RemoveDevice(rpc) => rpc.handle_failable_sync(|path| {
139                if let Some(state) = state.upgrade() {
140                    ScsiController { state }
141                        .remove(path)
142                        .context("failed to remove device")?;
143                }
144                anyhow::Ok(())
145            }),
146        }
147    }
148}