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.dma_target,
64 input.register_mmio,
65 NvmeControllerCaps {
66 msix_count: resource.msix_count,
67 max_io_queues: resource.max_io_queues,
68 subsystem_id: resource.subsystem_id,
69 },
70 );
71 for NamespaceDefinition {
72 nsid,
73 read_only,
74 disk,
75 } in resource.namespaces
76 {
77 let disk = resolver
78 .resolve(
79 disk,
80 ResolveDiskParameters {
81 read_only,
82 driver_source: input.driver_source,
83 },
84 )
85 .await
86 .map_err(|source| Error::NamespaceResolve { nsid, source })?;
87 controller
88 .client()
89 .add_namespace(nsid, disk.0)
90 .await
91 .map_err(Error::NsidConflict)?;
92 }
93
94 if let Some(requests) = resource.requests {
95 let driver = input.driver_source.simple();
96 driver
97 .spawn(
98 "nvme-requests",
99 handle_requests(
100 input.driver_source.clone(),
101 controller.client(),
102 resolver.clone(),
103 requests,
104 ),
105 )
106 .detach();
107 }
108
109 Ok(controller.into())
110 }
111}
112
113async fn handle_requests(
114 driver_source: VmTaskDriverSource,
115 client: NvmeControllerClient,
116 resolver: ResourceResolver,
117 mut requests: mesh::Receiver<NvmeControllerRequest>,
118) {
119 while let Some(req) = requests.next().await {
120 match req {
121 NvmeControllerRequest::AddNamespace(rpc) => {
122 rpc.handle_failable(
123 async |NamespaceDefinition {
124 nsid,
125 read_only,
126 disk,
127 }| {
128 let disk = resolver
129 .resolve(
130 disk,
131 ResolveDiskParameters {
132 read_only,
133 driver_source: &driver_source,
134 },
135 )
136 .await
137 .context("failed to resolve disk")?;
138
139 client
140 .add_namespace(nsid, disk.0)
141 .await
142 .context("failed to add namespace")?;
143
144 anyhow::Ok(())
145 },
146 )
147 .await
148 }
149 NvmeControllerRequest::RemoveNamespace(rpc) => {
150 rpc.handle_failable(async |nsid| {
151 let removed = client.remove_namespace(nsid).await;
152 if !removed {
153 anyhow::bail!("namespace {nsid} not found");
154 }
155 anyhow::Ok(())
156 })
157 .await
158 }
159 }
160 }
161}