Skip to main content

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