1use 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
33const DEFAULT_IO_QUEUE_DEPTH: u32 = 256;
35
36pub struct StorvspResolver;
38
39declare_static_async_resolver! {
40 StorvspResolver,
41 (VmbusDeviceHandleKind, ScsiControllerHandle),
42}
43
44#[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
154pub struct StorvspIdeResolver;
156
157declare_static_async_resolver! {
158 StorvspIdeResolver,
159 (VmbusDeviceHandleKind, StorvspIdeDeviceHandle),
160}
161
162#[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}