1use 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 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#[derive(Debug)]
193pub struct NvmeFaultControllerClient {
194 send: mesh::Sender<CoordinatorRequest>,
195}
196
197impl NvmeFaultControllerClient {
198 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 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}