1use crate::NsidConflict;
7use crate::NvmeController;
8use crate::NvmeControllerCaps;
9use crate::NvmeControllerClient;
10use anyhow::Context;
11use async_trait::async_trait;
12use disk_backend::resolve::ResolveDiskParameters;
13use futures::StreamExt;
14use nvme_resources::NamespaceDefinition;
15use nvme_resources::NvmeControllerHandle;
16use nvme_resources::NvmeControllerRequest;
17use pal_async::task::Spawn;
18use pci_resources::ResolvePciDeviceHandleParams;
19use pci_resources::ResolvedPciDevice;
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::PciDeviceHandleKind;
26use vmcore::vm_task::VmTaskDriverSource;
27
28pub struct NvmeControllerResolver;
30
31declare_static_async_resolver! {
32 NvmeControllerResolver,
33 (PciDeviceHandleKind, NvmeControllerHandle),
34}
35
36#[derive(Debug, Error)]
38#[expect(missing_docs)]
39pub enum Error {
40 #[error("failed to resolve namespace {nsid}")]
41 NamespaceResolve {
42 nsid: u32,
43 #[source]
44 source: ResolveError,
45 },
46 #[error(transparent)]
47 NsidConflict(NsidConflict),
48}
49
50#[async_trait]
51impl AsyncResolveResource<PciDeviceHandleKind, NvmeControllerHandle> for NvmeControllerResolver {
52 type Output = ResolvedPciDevice;
53 type Error = Error;
54
55 async fn resolve(
56 &self,
57 resolver: &ResourceResolver,
58 resource: NvmeControllerHandle,
59 input: ResolvePciDeviceHandleParams<'_>,
60 ) -> Result<Self::Output, Self::Error> {
61 let controller = NvmeController::new(
62 input.driver_source,
63 input.guest_memory.clone(),
64 input.msi_target,
65 input.register_mmio,
66 NvmeControllerCaps {
67 msix_count: resource.msix_count,
68 max_io_queues: resource.max_io_queues,
69 subsystem_id: resource.subsystem_id,
70 },
71 );
72 for NamespaceDefinition {
73 nsid,
74 read_only,
75 disk,
76 } in resource.namespaces
77 {
78 let disk = resolver
79 .resolve(
80 disk,
81 ResolveDiskParameters {
82 read_only,
83 driver_source: input.driver_source,
84 },
85 )
86 .await
87 .map_err(|source| Error::NamespaceResolve { nsid, source })?;
88 controller
89 .client()
90 .add_namespace(nsid, disk.0)
91 .await
92 .map_err(Error::NsidConflict)?;
93 }
94
95 if let Some(requests) = resource.requests {
96 let driver = input.driver_source.simple();
97 driver
98 .spawn(
99 "nvme-requests",
100 handle_requests(
101 input.driver_source.clone(),
102 controller.client(),
103 resolver.clone(),
104 requests,
105 ),
106 )
107 .detach();
108 }
109
110 Ok(controller.into())
111 }
112}
113
114async fn handle_requests(
115 driver_source: VmTaskDriverSource,
116 client: NvmeControllerClient,
117 resolver: ResourceResolver,
118 mut requests: mesh::Receiver<NvmeControllerRequest>,
119) {
120 while let Some(req) = requests.next().await {
121 match req {
122 NvmeControllerRequest::AddNamespace(rpc) => {
123 rpc.handle_failable(
124 async |NamespaceDefinition {
125 nsid,
126 read_only,
127 disk,
128 }| {
129 let disk = resolver
130 .resolve(
131 disk,
132 ResolveDiskParameters {
133 read_only,
134 driver_source: &driver_source,
135 },
136 )
137 .await
138 .context("failed to resolve disk")?;
139
140 client
141 .add_namespace(nsid, disk.0)
142 .await
143 .context("failed to add namespace")?;
144
145 anyhow::Ok(())
146 },
147 )
148 .await
149 }
150 NvmeControllerRequest::RemoveNamespace(rpc) => {
151 rpc.handle_failable(async |nsid| {
152 let removed = client.remove_namespace(nsid).await;
153 if !removed {
154 anyhow::bail!("namespace {nsid} not found");
155 }
156 anyhow::Ok(())
157 })
158 .await
159 }
160 }
161 }
162}