1use crate::SimpleScsiDisk;
7use crate::scsidvd::SimpleScsiDvd;
8use anyhow::Context;
9use async_trait::async_trait;
10use disk_backend::resolve::ResolveDiskParameters;
11use futures::StreamExt;
12use pal_async::task::Spawn;
13use scsi_core::ResolveScsiDeviceHandleParams;
14use scsi_core::ResolvedScsiDevice;
15use scsidisk_resources::SimpleScsiDiskHandle;
16use scsidisk_resources::SimpleScsiDvdHandle;
17use scsidisk_resources::SimpleScsiDvdRequest;
18use std::sync::Arc;
19use std::sync::Weak;
20use thiserror::Error;
21use vm_resource::AsyncResolveResource;
22use vm_resource::ResolveError;
23use vm_resource::ResourceResolver;
24use vm_resource::declare_static_async_resolver;
25use vm_resource::kind::ScsiDeviceHandleKind;
26use vmcore::vm_task::VmTaskDriverSource;
27
28pub struct SimpleScsiResolver;
30
31declare_static_async_resolver! {
32 SimpleScsiResolver,
33 (ScsiDeviceHandleKind, SimpleScsiDiskHandle),
34 (ScsiDeviceHandleKind, SimpleScsiDvdHandle),
35}
36
37#[derive(Debug, Error)]
38pub enum Error {
39 #[error("failed to resolve backing disk")]
40 Disk(#[source] ResolveError),
41}
42
43#[async_trait]
44impl AsyncResolveResource<ScsiDeviceHandleKind, SimpleScsiDiskHandle> for SimpleScsiResolver {
45 type Output = ResolvedScsiDevice;
46 type Error = Error;
47
48 async fn resolve(
49 &self,
50 resolver: &ResourceResolver,
51 resource: SimpleScsiDiskHandle,
52 input: ResolveScsiDeviceHandleParams<'_>,
53 ) -> Result<Self::Output, Self::Error> {
54 let disk = resolver
55 .resolve(
56 resource.disk,
57 ResolveDiskParameters {
58 read_only: resource.read_only,
59 driver_source: input.driver_source,
60 },
61 )
62 .await
63 .map_err(Error::Disk)?;
64
65 let disk = SimpleScsiDisk::new(disk.0, resource.parameters);
66 Ok(disk.into())
67 }
68}
69
70#[async_trait]
71impl AsyncResolveResource<ScsiDeviceHandleKind, SimpleScsiDvdHandle> for SimpleScsiResolver {
72 type Output = ResolvedScsiDevice;
73 type Error = Error;
74
75 async fn resolve(
76 &self,
77 resolver: &ResourceResolver,
78 resource: SimpleScsiDvdHandle,
79 input: ResolveScsiDeviceHandleParams<'_>,
80 ) -> Result<Self::Output, Self::Error> {
81 let media = if let Some(media) = resource.media {
82 Some(
83 resolver
84 .resolve(
85 media,
86 ResolveDiskParameters {
87 read_only: true,
88 driver_source: input.driver_source,
89 },
90 )
91 .await
92 .map_err(Error::Disk)?
93 .0,
94 )
95 } else {
96 None
97 };
98 let dvd = Arc::new(SimpleScsiDvd::new(media));
99
100 if let Some(requests) = resource.requests {
102 input
103 .driver_source
104 .simple()
105 .spawn(
106 "dvd-requests",
107 handle_dvd_requests(
108 Arc::downgrade(&dvd),
109 resolver.clone(),
110 input.driver_source.clone(),
111 requests,
112 ),
113 )
114 .detach();
115 }
116
117 Ok(ResolvedScsiDevice(dvd))
118 }
119}
120
121async fn handle_dvd_requests(
122 dvd: Weak<SimpleScsiDvd>,
123 resolver: ResourceResolver,
124 driver_source: VmTaskDriverSource,
125 mut requests: mesh::Receiver<SimpleScsiDvdRequest>,
126) {
127 while let Some(req) = requests.next().await {
128 match req {
129 SimpleScsiDvdRequest::ChangeMedia(rpc) => {
130 rpc.handle_failable(async |resource| {
131 let media = if let Some(resource) = resource {
132 Some(
133 resolver
134 .resolve(
135 resource,
136 ResolveDiskParameters {
137 read_only: true,
138 driver_source: &driver_source,
139 },
140 )
141 .await
142 .context("failed to resolve media")?
143 .0,
144 )
145 } else {
146 None
147 };
148 if let Some(dvd) = dvd.upgrade() {
149 dvd.change_media(media);
150 }
151 anyhow::Ok(())
152 })
153 .await
154 }
155 }
156 }
157}