Skip to main content

nvme/
resolver.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Resource resolver for the nvme controller.
5
6use 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
28/// Resource resolver for [`NvmeControllerHandle`].
29pub struct NvmeControllerResolver;
30
31declare_static_async_resolver! {
32    NvmeControllerResolver,
33    (PciDeviceHandleKind, NvmeControllerHandle),
34}
35
36/// Error returned by [`NvmeControllerResolver`].
37#[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}