Skip to main content

nvme/workers/
coordinator.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Coordinator between queues and hot add/remove of namespaces.
5
6use super::IoQueueEntrySizes;
7use super::admin::AddNamespaceError;
8use super::admin::AdminConfig;
9use super::admin::AdminHandler;
10use super::admin::AdminState;
11use crate::queue::DoorbellMemory;
12use crate::queue::InvalidDoorbell;
13use disk_backend::Disk;
14use futures::FutureExt;
15use futures::StreamExt;
16use futures_concurrency::future::Race;
17use guestmem::GuestMemory;
18use guid::Guid;
19use inspect::Inspect;
20use inspect::InspectMut;
21use mesh::rpc::PendingRpc;
22use mesh::rpc::Rpc;
23use mesh::rpc::RpcSend;
24use pal_async::task::Spawn;
25use pal_async::task::Task;
26use parking_lot::Mutex;
27use parking_lot::RwLock;
28use std::future::pending;
29use std::sync::Arc;
30use task_control::TaskControl;
31use vmcore::interrupt::Interrupt;
32use vmcore::vm_task::VmTaskDriver;
33use vmcore::vm_task::VmTaskDriverSource;
34
35#[derive(InspectMut)]
36pub struct NvmeWorkers {
37    #[inspect(skip)]
38    _task: Task<()>,
39    #[inspect(flatten, send = "CoordinatorRequest::Inspect")]
40    send: mesh::Sender<CoordinatorRequest>,
41    #[inspect(skip)]
42    doorbells: Arc<RwLock<DoorbellMemory>>,
43    #[inspect(skip)]
44    state: EnableState,
45}
46
47#[derive(Debug)]
48enum EnableState {
49    Disabled,
50    Enabling(PendingRpc<()>),
51    Enabled,
52    Resetting(PendingRpc<()>),
53}
54
55impl NvmeWorkers {
56    pub fn new(
57        driver_source: &VmTaskDriverSource,
58        mem: GuestMemory,
59        interrupts: Vec<Interrupt>,
60        max_sqs: u16,
61        max_cqs: u16,
62        qe_sizes: Arc<Mutex<IoQueueEntrySizes>>,
63        subsystem_id: Guid,
64    ) -> Self {
65        let num_qids = 2 + max_sqs.max(max_cqs) * 2;
66        let doorbells = Arc::new(RwLock::new(DoorbellMemory::new(num_qids)));
67        let driver = driver_source.simple();
68        let handler: AdminHandler = AdminHandler::new(
69            driver.clone(),
70            AdminConfig {
71                driver_source: driver_source.clone(),
72                mem,
73                interrupts,
74                doorbells: doorbells.clone(),
75                subsystem_id,
76                max_sqs,
77                max_cqs,
78                qe_sizes,
79            },
80        );
81        let coordinator = Coordinator {
82            driver: driver.clone(),
83            admin: TaskControl::new(handler),
84            reset: None,
85        };
86        let (send, recv) = mesh::mpsc_channel();
87        let task = driver.spawn("nvme-coord", coordinator.run(recv));
88        Self {
89            _task: task,
90            send,
91            doorbells,
92            state: EnableState::Disabled,
93        }
94    }
95
96    pub fn client(&self) -> NvmeControllerClient {
97        NvmeControllerClient {
98            send: self.send.clone(),
99        }
100    }
101
102    pub fn doorbell(&self, db_id: u16, value: u32) {
103        if let Err(InvalidDoorbell) = self.doorbells.read().try_write(db_id, value) {
104            tracelimit::error_ratelimited!(db_id, "write to invalid doorbell index");
105        }
106    }
107
108    pub fn enable(&mut self, asq: u64, asqs: u16, acq: u64, acqs: u16) {
109        if let EnableState::Disabled = self.state {
110            self.state = EnableState::Enabling(self.send.call(
111                CoordinatorRequest::EnableAdmin,
112                EnableAdminParams {
113                    asq,
114                    asqs,
115                    acq,
116                    acqs,
117                },
118            ));
119        } else {
120            panic!("not disabled: {:?}", self.state);
121        }
122    }
123
124    pub fn poll_enabled(&mut self) -> bool {
125        if let EnableState::Enabling(recv) = &mut self.state {
126            if recv.now_or_never().is_some() {
127                self.state = EnableState::Enabled;
128                true
129            } else {
130                false
131            }
132        } else {
133            panic!("not enabling: {:?}", self.state)
134        }
135    }
136
137    pub fn controller_reset(&mut self) {
138        if let EnableState::Enabled = self.state {
139            self.state =
140                EnableState::Resetting(self.send.call(CoordinatorRequest::ControllerReset, ()));
141        } else {
142            panic!("not enabled: {:?}", self.state);
143        }
144    }
145
146    pub fn poll_controller_reset(&mut self) -> bool {
147        let Self {
148            _task: _,
149            send: _,
150            doorbells,
151            state,
152        } = self;
153        if let EnableState::Resetting(recv) = state {
154            if recv.now_or_never().is_some() {
155                *state = EnableState::Disabled;
156                doorbells.write().reset();
157                true
158            } else {
159                false
160            }
161        } else {
162            panic!("not resetting: {:?}", state)
163        }
164    }
165
166    // Reset the workers from whatever state they are in.
167    pub async fn reset(&mut self) {
168        loop {
169            match &mut self.state {
170                EnableState::Disabled => break,
171                EnableState::Enabling(recv) => {
172                    recv.await.unwrap();
173                    self.state = EnableState::Enabled;
174                }
175                EnableState::Enabled => {
176                    self.controller_reset();
177                }
178                EnableState::Resetting(recv) => {
179                    recv.await.unwrap();
180                    self.state = EnableState::Disabled;
181                }
182            }
183        }
184        self.doorbells.write().reset();
185    }
186}
187
188/// Client for modifying the NVMe controller state at runtime.
189#[derive(Debug)]
190pub struct NvmeControllerClient {
191    send: mesh::Sender<CoordinatorRequest>,
192}
193
194impl NvmeControllerClient {
195    /// Adds a namespace.
196    pub async fn add_namespace(&self, nsid: u32, disk: Disk) -> Result<(), AddNamespaceError> {
197        self.send
198            .call(CoordinatorRequest::AddNamespace, (nsid, disk))
199            .await
200            .unwrap()
201    }
202
203    /// Removes a namespace.
204    pub async fn remove_namespace(&self, nsid: u32) -> bool {
205        self.send
206            .call(CoordinatorRequest::RemoveNamespace, nsid)
207            .await
208            .unwrap()
209    }
210}
211
212#[derive(Inspect)]
213struct Coordinator {
214    driver: VmTaskDriver,
215    #[inspect(flatten)]
216    admin: TaskControl<AdminHandler, AdminState>,
217    #[inspect(with = "Option::is_some")]
218    reset: Option<Rpc<(), ()>>,
219}
220
221enum CoordinatorRequest {
222    EnableAdmin(Rpc<EnableAdminParams, ()>),
223    AddNamespace(Rpc<(u32, Disk), Result<(), AddNamespaceError>>),
224    RemoveNamespace(Rpc<u32, bool>),
225    Inspect(inspect::Deferred),
226    ControllerReset(Rpc<(), ()>),
227}
228
229struct EnableAdminParams {
230    asq: u64,
231    asqs: u16,
232    acq: u64,
233    acqs: u16,
234}
235
236impl Coordinator {
237    async fn run(mut self, mut recv: mesh::Receiver<CoordinatorRequest>) {
238        loop {
239            enum Event {
240                Request(Option<CoordinatorRequest>),
241                ResetComplete,
242            }
243
244            let controller_reset = async {
245                if self.reset.is_some() {
246                    self.admin.stop().await;
247                    if let Some(state) = self.admin.state_mut() {
248                        state.drain().await;
249                        self.admin.remove();
250                    }
251                } else {
252                    pending().await
253                }
254            };
255
256            let event = (
257                recv.next().map(Event::Request),
258                controller_reset.map(|_| Event::ResetComplete),
259            )
260                .race()
261                .await;
262
263            match event {
264                Event::Request(Some(req)) => match req {
265                    CoordinatorRequest::EnableAdmin(rpc) => rpc.handle_sync(
266                        |EnableAdminParams {
267                             asq,
268                             asqs,
269                             acq,
270                             acqs,
271                         }| {
272                            if !self.admin.has_state() {
273                                let state =
274                                    AdminState::new(self.admin.task(), asq, asqs, acq, acqs);
275                                self.admin.insert(&self.driver, "nvme-admin", state);
276                                self.admin.start();
277                            } else {
278                                tracelimit::warn_ratelimited!("duplicate attempt to enable admin");
279                            }
280                        },
281                    ),
282                    CoordinatorRequest::AddNamespace(rpc) => {
283                        rpc.handle(async |(nsid, disk)| {
284                            let running = self.admin.stop().await;
285                            let (admin, state) = self.admin.get_mut();
286                            let r = admin.add_namespace(state, nsid, disk).await;
287                            if running {
288                                self.admin.start();
289                            }
290                            r
291                        })
292                        .await
293                    }
294                    CoordinatorRequest::RemoveNamespace(rpc) => {
295                        rpc.handle(async |nsid| {
296                            let running = self.admin.stop().await;
297                            let (admin, state) = self.admin.get_mut();
298                            let r = admin.remove_namespace(state, nsid).await;
299                            if running {
300                                self.admin.start();
301                            }
302                            r
303                        })
304                        .await
305                    }
306                    CoordinatorRequest::ControllerReset(rpc) => {
307                        assert!(self.reset.is_none());
308                        self.reset = Some(rpc);
309                    }
310                    CoordinatorRequest::Inspect(req) => req.inspect(&self),
311                },
312                Event::Request(None) => break,
313                Event::ResetComplete => {
314                    self.reset.take().unwrap().complete(());
315                }
316            }
317        }
318    }
319}