xref: /aosp_15_r20/external/crosvm/src/sys/windows/irq_wait.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! Handles the main wait loop for IRQs.
6*bb4ee6a4SAndroid Build Coastguard Worker //! Should be started on a background thread.
7*bb4ee6a4SAndroid Build Coastguard Worker 
8*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::HashMap;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::io;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::Arc;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::thread;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::thread::JoinHandle;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
14*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Instant;
15*bb4ee6a4SAndroid Build Coastguard Worker 
16*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
17*bb4ee6a4SAndroid Build Coastguard Worker use arch::IrqChipArch;
18*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
19*bb4ee6a4SAndroid Build Coastguard Worker use base::info;
20*bb4ee6a4SAndroid Build Coastguard Worker use base::warn;
21*bb4ee6a4SAndroid Build Coastguard Worker use base::Event;
22*bb4ee6a4SAndroid Build Coastguard Worker use base::EventToken;
23*bb4ee6a4SAndroid Build Coastguard Worker use base::ReadNotifier;
24*bb4ee6a4SAndroid Build Coastguard Worker use base::Result;
25*bb4ee6a4SAndroid Build Coastguard Worker use base::Tube;
26*bb4ee6a4SAndroid Build Coastguard Worker use base::TubeError;
27*bb4ee6a4SAndroid Build Coastguard Worker use base::WaitContext;
28*bb4ee6a4SAndroid Build Coastguard Worker use base::MAXIMUM_WAIT_OBJECTS;
29*bb4ee6a4SAndroid Build Coastguard Worker use devices::IrqEdgeEvent;
30*bb4ee6a4SAndroid Build Coastguard Worker use devices::IrqEventIndex;
31*bb4ee6a4SAndroid Build Coastguard Worker use devices::IrqEventSource;
32*bb4ee6a4SAndroid Build Coastguard Worker use metrics::log_high_frequency_descriptor_event;
33*bb4ee6a4SAndroid Build Coastguard Worker use metrics::MetricEventType;
34*bb4ee6a4SAndroid Build Coastguard Worker use resources::SystemAllocator;
35*bb4ee6a4SAndroid Build Coastguard Worker use sync::Mutex;
36*bb4ee6a4SAndroid Build Coastguard Worker use vm_control::IrqHandlerRequest;
37*bb4ee6a4SAndroid Build Coastguard Worker use vm_control::IrqHandlerResponse;
38*bb4ee6a4SAndroid Build Coastguard Worker use vm_control::IrqSetup;
39*bb4ee6a4SAndroid Build Coastguard Worker use vm_control::VmIrqRequest;
40*bb4ee6a4SAndroid Build Coastguard Worker 
41*bb4ee6a4SAndroid Build Coastguard Worker pub struct IrqWaitWorker {
42*bb4ee6a4SAndroid Build Coastguard Worker     irq_handler_control: Tube,
43*bb4ee6a4SAndroid Build Coastguard Worker     irq_chip: Box<dyn IrqChipArch>,
44*bb4ee6a4SAndroid Build Coastguard Worker     irq_control_tubes: Vec<Tube>,
45*bb4ee6a4SAndroid Build Coastguard Worker     sys_allocator: Arc<Mutex<SystemAllocator>>,
46*bb4ee6a4SAndroid Build Coastguard Worker }
47*bb4ee6a4SAndroid Build Coastguard Worker 
48*bb4ee6a4SAndroid Build Coastguard Worker #[derive(EventToken)]
49*bb4ee6a4SAndroid Build Coastguard Worker enum Token {
50*bb4ee6a4SAndroid Build Coastguard Worker     VmControl { index: usize },
51*bb4ee6a4SAndroid Build Coastguard Worker     IrqHandlerControl,
52*bb4ee6a4SAndroid Build Coastguard Worker     DelayedIrqEvent,
53*bb4ee6a4SAndroid Build Coastguard Worker }
54*bb4ee6a4SAndroid Build Coastguard Worker 
55*bb4ee6a4SAndroid Build Coastguard Worker impl IrqWaitWorker {
start( irq_handler_control: Tube, irq_chip: Box<dyn IrqChipArch>, irq_control_tubes: Vec<Tube>, sys_allocator: Arc<Mutex<SystemAllocator>>, ) -> JoinHandle<anyhow::Result<()>>56*bb4ee6a4SAndroid Build Coastguard Worker     pub fn start(
57*bb4ee6a4SAndroid Build Coastguard Worker         irq_handler_control: Tube,
58*bb4ee6a4SAndroid Build Coastguard Worker         irq_chip: Box<dyn IrqChipArch>,
59*bb4ee6a4SAndroid Build Coastguard Worker         irq_control_tubes: Vec<Tube>,
60*bb4ee6a4SAndroid Build Coastguard Worker         sys_allocator: Arc<Mutex<SystemAllocator>>,
61*bb4ee6a4SAndroid Build Coastguard Worker     ) -> JoinHandle<anyhow::Result<()>> {
62*bb4ee6a4SAndroid Build Coastguard Worker         let mut irq_worker = IrqWaitWorker {
63*bb4ee6a4SAndroid Build Coastguard Worker             irq_handler_control,
64*bb4ee6a4SAndroid Build Coastguard Worker             irq_chip,
65*bb4ee6a4SAndroid Build Coastguard Worker             irq_control_tubes,
66*bb4ee6a4SAndroid Build Coastguard Worker             sys_allocator,
67*bb4ee6a4SAndroid Build Coastguard Worker         };
68*bb4ee6a4SAndroid Build Coastguard Worker         thread::Builder::new()
69*bb4ee6a4SAndroid Build Coastguard Worker             .name("irq_wait_loop".into())
70*bb4ee6a4SAndroid Build Coastguard Worker             .spawn(move || irq_worker.run())
71*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap()
72*bb4ee6a4SAndroid Build Coastguard Worker     }
73*bb4ee6a4SAndroid Build Coastguard Worker 
add_child( wait_ctx: &WaitContext<Token>, children: &mut Vec<JoinHandle<Result<()>>>, child_control_tubes: &mut Vec<Tube>, irq_chip: Box<dyn IrqChipArch>, irq_frequencies: Arc<Mutex<Vec<u64>>>, ) -> Result<Arc<WaitContext<ChildToken>>>74*bb4ee6a4SAndroid Build Coastguard Worker     fn add_child(
75*bb4ee6a4SAndroid Build Coastguard Worker         wait_ctx: &WaitContext<Token>,
76*bb4ee6a4SAndroid Build Coastguard Worker         children: &mut Vec<JoinHandle<Result<()>>>,
77*bb4ee6a4SAndroid Build Coastguard Worker         child_control_tubes: &mut Vec<Tube>,
78*bb4ee6a4SAndroid Build Coastguard Worker         irq_chip: Box<dyn IrqChipArch>,
79*bb4ee6a4SAndroid Build Coastguard Worker         irq_frequencies: Arc<Mutex<Vec<u64>>>,
80*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<Arc<WaitContext<ChildToken>>> {
81*bb4ee6a4SAndroid Build Coastguard Worker         let (child_control_tube, child_control_tube_for_child) = Tube::pair().map_err(|e| {
82*bb4ee6a4SAndroid Build Coastguard Worker             error!("failed to create IRQ child control tube: {:?}", e);
83*bb4ee6a4SAndroid Build Coastguard Worker             base::Error::from(io::Error::new(io::ErrorKind::Other, e))
84*bb4ee6a4SAndroid Build Coastguard Worker         })?;
85*bb4ee6a4SAndroid Build Coastguard Worker         let (child_wait_ctx, child_join_handle) =
86*bb4ee6a4SAndroid Build Coastguard Worker             IrqWaitWorkerChild::start(child_control_tube_for_child, irq_chip, irq_frequencies)?;
87*bb4ee6a4SAndroid Build Coastguard Worker         children.push(child_join_handle);
88*bb4ee6a4SAndroid Build Coastguard Worker         child_control_tubes.push(child_control_tube);
89*bb4ee6a4SAndroid Build Coastguard Worker         Ok(child_wait_ctx)
90*bb4ee6a4SAndroid Build Coastguard Worker     }
91*bb4ee6a4SAndroid Build Coastguard Worker 
run(&mut self) -> anyhow::Result<()>92*bb4ee6a4SAndroid Build Coastguard Worker     fn run(&mut self) -> anyhow::Result<()> {
93*bb4ee6a4SAndroid Build Coastguard Worker         let wait_ctx = WaitContext::new()?;
94*bb4ee6a4SAndroid Build Coastguard Worker 
95*bb4ee6a4SAndroid Build Coastguard Worker         let mut max_event_index: usize = 0;
96*bb4ee6a4SAndroid Build Coastguard Worker         let mut vm_control_added_irq_events: Vec<Event> = Vec::new();
97*bb4ee6a4SAndroid Build Coastguard Worker         let mut irq_event_sources: HashMap<IrqEventIndex, IrqEventSource> = HashMap::new();
98*bb4ee6a4SAndroid Build Coastguard Worker         // TODO(b/190828888): Move irq logging into the irqchip impls.
99*bb4ee6a4SAndroid Build Coastguard Worker         let irq_frequencies = Arc::new(Mutex::new(vec![0; max_event_index + 1]));
100*bb4ee6a4SAndroid Build Coastguard Worker         let irq_events = self.irq_chip.irq_event_tokens()?;
101*bb4ee6a4SAndroid Build Coastguard Worker         let mut children = vec![];
102*bb4ee6a4SAndroid Build Coastguard Worker         let mut child_control_tubes = vec![];
103*bb4ee6a4SAndroid Build Coastguard Worker 
104*bb4ee6a4SAndroid Build Coastguard Worker         let mut child_wait_ctx = Self::add_child(
105*bb4ee6a4SAndroid Build Coastguard Worker             &wait_ctx,
106*bb4ee6a4SAndroid Build Coastguard Worker             &mut children,
107*bb4ee6a4SAndroid Build Coastguard Worker             &mut child_control_tubes,
108*bb4ee6a4SAndroid Build Coastguard Worker             self.irq_chip.try_box_clone()?,
109*bb4ee6a4SAndroid Build Coastguard Worker             irq_frequencies.clone(),
110*bb4ee6a4SAndroid Build Coastguard Worker         )
111*bb4ee6a4SAndroid Build Coastguard Worker         .context("failed to create IRQ wait child")?;
112*bb4ee6a4SAndroid Build Coastguard Worker 
113*bb4ee6a4SAndroid Build Coastguard Worker         wait_ctx.add(
114*bb4ee6a4SAndroid Build Coastguard Worker             self.irq_handler_control.get_read_notifier(),
115*bb4ee6a4SAndroid Build Coastguard Worker             Token::IrqHandlerControl,
116*bb4ee6a4SAndroid Build Coastguard Worker         )?;
117*bb4ee6a4SAndroid Build Coastguard Worker 
118*bb4ee6a4SAndroid Build Coastguard Worker         for (event_index, source, evt) in irq_events {
119*bb4ee6a4SAndroid Build Coastguard Worker             child_wait_ctx.add(&evt, ChildToken::IrqEvent { event_index })?;
120*bb4ee6a4SAndroid Build Coastguard Worker             max_event_index = std::cmp::max(max_event_index, event_index);
121*bb4ee6a4SAndroid Build Coastguard Worker             irq_event_sources.insert(event_index, source);
122*bb4ee6a4SAndroid Build Coastguard Worker 
123*bb4ee6a4SAndroid Build Coastguard Worker             vm_control_added_irq_events.push(evt);
124*bb4ee6a4SAndroid Build Coastguard Worker         }
125*bb4ee6a4SAndroid Build Coastguard Worker 
126*bb4ee6a4SAndroid Build Coastguard Worker         irq_frequencies.lock().resize(max_event_index + 1, 0);
127*bb4ee6a4SAndroid Build Coastguard Worker 
128*bb4ee6a4SAndroid Build Coastguard Worker         for (index, control_tube) in self.irq_control_tubes.iter().enumerate() {
129*bb4ee6a4SAndroid Build Coastguard Worker             wait_ctx.add(control_tube.get_read_notifier(), Token::VmControl { index })?;
130*bb4ee6a4SAndroid Build Coastguard Worker         }
131*bb4ee6a4SAndroid Build Coastguard Worker 
132*bb4ee6a4SAndroid Build Coastguard Worker         let mut _delayed_event_token: Option<Event> = None;
133*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(delayed_token) = self.irq_chip.irq_delayed_event_token()? {
134*bb4ee6a4SAndroid Build Coastguard Worker             wait_ctx.add(&delayed_token, Token::DelayedIrqEvent)?;
135*bb4ee6a4SAndroid Build Coastguard Worker             // store the token, so that it lasts outside this scope.
136*bb4ee6a4SAndroid Build Coastguard Worker             // We must store the event as try_clone creates a new event. It won't keep
137*bb4ee6a4SAndroid Build Coastguard Worker             // the current event valid that is waited on inside wait_ctx.
138*bb4ee6a4SAndroid Build Coastguard Worker             _delayed_event_token = Some(delayed_token);
139*bb4ee6a4SAndroid Build Coastguard Worker         }
140*bb4ee6a4SAndroid Build Coastguard Worker 
141*bb4ee6a4SAndroid Build Coastguard Worker         let mut intr_stat_sample_time = Instant::now();
142*bb4ee6a4SAndroid Build Coastguard Worker 
143*bb4ee6a4SAndroid Build Coastguard Worker         'poll: loop {
144*bb4ee6a4SAndroid Build Coastguard Worker             let events = {
145*bb4ee6a4SAndroid Build Coastguard Worker                 match wait_ctx.wait() {
146*bb4ee6a4SAndroid Build Coastguard Worker                     Ok(v) => v,
147*bb4ee6a4SAndroid Build Coastguard Worker                     Err(e) => {
148*bb4ee6a4SAndroid Build Coastguard Worker                         error!("failed to wait on irq thread: {}", e);
149*bb4ee6a4SAndroid Build Coastguard Worker                         break 'poll;
150*bb4ee6a4SAndroid Build Coastguard Worker                     }
151*bb4ee6a4SAndroid Build Coastguard Worker                 }
152*bb4ee6a4SAndroid Build Coastguard Worker             };
153*bb4ee6a4SAndroid Build Coastguard Worker 
154*bb4ee6a4SAndroid Build Coastguard Worker             let mut token_count = events.len();
155*bb4ee6a4SAndroid Build Coastguard Worker             let mut notify_control_on_iteration_end = false;
156*bb4ee6a4SAndroid Build Coastguard Worker             let mut vm_control_indices_to_remove = Vec::new();
157*bb4ee6a4SAndroid Build Coastguard Worker             for event in events.iter().filter(|e| e.is_readable) {
158*bb4ee6a4SAndroid Build Coastguard Worker                 match event.token {
159*bb4ee6a4SAndroid Build Coastguard Worker                     Token::IrqHandlerControl => {
160*bb4ee6a4SAndroid Build Coastguard Worker                         match self.irq_handler_control.recv::<IrqHandlerRequest>() {
161*bb4ee6a4SAndroid Build Coastguard Worker                             Ok(request) => match request {
162*bb4ee6a4SAndroid Build Coastguard Worker                                 IrqHandlerRequest::Exit => {
163*bb4ee6a4SAndroid Build Coastguard Worker                                     info!("irq event loop got exit request");
164*bb4ee6a4SAndroid Build Coastguard Worker                                     break 'poll;
165*bb4ee6a4SAndroid Build Coastguard Worker                                 }
166*bb4ee6a4SAndroid Build Coastguard Worker                                 IrqHandlerRequest::AddIrqControlTubes(_tubes) => {
167*bb4ee6a4SAndroid Build Coastguard Worker                                     panic!("CrosVM on Windows does not support adding devices on the fly yet.");
168*bb4ee6a4SAndroid Build Coastguard Worker                                 }
169*bb4ee6a4SAndroid Build Coastguard Worker                                 IrqHandlerRequest::WakeAndNotifyIteration => {
170*bb4ee6a4SAndroid Build Coastguard Worker                                     for child_control_tube in child_control_tubes.iter() {
171*bb4ee6a4SAndroid Build Coastguard Worker                                         child_control_tube
172*bb4ee6a4SAndroid Build Coastguard Worker                                             .send(&IrqHandlerRequest::WakeAndNotifyIteration)
173*bb4ee6a4SAndroid Build Coastguard Worker                                             .context("failed to send flush command to IRQ handler child thread")?;
174*bb4ee6a4SAndroid Build Coastguard Worker                                         let resp = child_control_tube
175*bb4ee6a4SAndroid Build Coastguard Worker                                             .recv()
176*bb4ee6a4SAndroid Build Coastguard Worker                                             .context("failed to recv flush response from IRQ handler child thread")?;
177*bb4ee6a4SAndroid Build Coastguard Worker                                         match resp {
178*bb4ee6a4SAndroid Build Coastguard Worker                                             IrqHandlerResponse::HandlerIterationComplete(
179*bb4ee6a4SAndroid Build Coastguard Worker                                                 tokens_serviced,
180*bb4ee6a4SAndroid Build Coastguard Worker                                             ) => {
181*bb4ee6a4SAndroid Build Coastguard Worker                                                 token_count += tokens_serviced;
182*bb4ee6a4SAndroid Build Coastguard Worker                                             }
183*bb4ee6a4SAndroid Build Coastguard Worker                                             unexpected_resp => panic!(
184*bb4ee6a4SAndroid Build Coastguard Worker                                                 "got unexpected response: {:?}",
185*bb4ee6a4SAndroid Build Coastguard Worker                                                 unexpected_resp
186*bb4ee6a4SAndroid Build Coastguard Worker                                             ),
187*bb4ee6a4SAndroid Build Coastguard Worker                                         }
188*bb4ee6a4SAndroid Build Coastguard Worker                                     }
189*bb4ee6a4SAndroid Build Coastguard Worker                                     notify_control_on_iteration_end = true;
190*bb4ee6a4SAndroid Build Coastguard Worker                                 }
191*bb4ee6a4SAndroid Build Coastguard Worker                                 IrqHandlerRequest::RefreshIrqEventTokens => {
192*bb4ee6a4SAndroid Build Coastguard Worker                                     // TODO(b/282755619): when we want to restore different shaped
193*bb4ee6a4SAndroid Build Coastguard Worker                                     // VMs, we'll have to implement this. For now, we'll just ack
194*bb4ee6a4SAndroid Build Coastguard Worker                                     // the message and not actually do the refresh.
195*bb4ee6a4SAndroid Build Coastguard Worker                                     self.irq_handler_control.send(&IrqHandlerResponse::IrqEventTokenRefreshComplete)
196*bb4ee6a4SAndroid Build Coastguard Worker                                         .context("failed to send reply to irq event token refresh request")?;
197*bb4ee6a4SAndroid Build Coastguard Worker                                 }
198*bb4ee6a4SAndroid Build Coastguard Worker                             },
199*bb4ee6a4SAndroid Build Coastguard Worker                             Err(e) => {
200*bb4ee6a4SAndroid Build Coastguard Worker                                 if let TubeError::Disconnected = e {
201*bb4ee6a4SAndroid Build Coastguard Worker                                     panic!("irq handler control tube disconnected.");
202*bb4ee6a4SAndroid Build Coastguard Worker                                 } else {
203*bb4ee6a4SAndroid Build Coastguard Worker                                     error!("failed to recv IrqHandlerRequest: {}", e);
204*bb4ee6a4SAndroid Build Coastguard Worker                                 }
205*bb4ee6a4SAndroid Build Coastguard Worker                             }
206*bb4ee6a4SAndroid Build Coastguard Worker                         }
207*bb4ee6a4SAndroid Build Coastguard Worker                     }
208*bb4ee6a4SAndroid Build Coastguard Worker                     Token::VmControl { index } => {
209*bb4ee6a4SAndroid Build Coastguard Worker                         if let Some(tube) = self.irq_control_tubes.get(index) {
210*bb4ee6a4SAndroid Build Coastguard Worker                             match tube.recv::<VmIrqRequest>() {
211*bb4ee6a4SAndroid Build Coastguard Worker                                 Ok(request) => {
212*bb4ee6a4SAndroid Build Coastguard Worker                                     let response = {
213*bb4ee6a4SAndroid Build Coastguard Worker                                         let irq_chip = &mut self.irq_chip;
214*bb4ee6a4SAndroid Build Coastguard Worker                                         // TODO(b/229262201): Refactor the closure into a standalone
215*bb4ee6a4SAndroid Build Coastguard Worker                                         // function to reduce indentation.
216*bb4ee6a4SAndroid Build Coastguard Worker                                         request.execute(
217*bb4ee6a4SAndroid Build Coastguard Worker                                             |setup| match setup {
218*bb4ee6a4SAndroid Build Coastguard Worker                                                 IrqSetup::Event(
219*bb4ee6a4SAndroid Build Coastguard Worker                                                     irq,
220*bb4ee6a4SAndroid Build Coastguard Worker                                                     ev,
221*bb4ee6a4SAndroid Build Coastguard Worker                                                     device_id,
222*bb4ee6a4SAndroid Build Coastguard Worker                                                     queue_id,
223*bb4ee6a4SAndroid Build Coastguard Worker                                                     device_name,
224*bb4ee6a4SAndroid Build Coastguard Worker                                                 ) => {
225*bb4ee6a4SAndroid Build Coastguard Worker                                                     let irqevent = IrqEdgeEvent::from_event(
226*bb4ee6a4SAndroid Build Coastguard Worker                                                         ev.try_clone()
227*bb4ee6a4SAndroid Build Coastguard Worker                                                             .expect("Failed to clone irq event."),
228*bb4ee6a4SAndroid Build Coastguard Worker                                                     );
229*bb4ee6a4SAndroid Build Coastguard Worker                                                     let source = IrqEventSource {
230*bb4ee6a4SAndroid Build Coastguard Worker                                                         device_id: device_id.try_into()?,
231*bb4ee6a4SAndroid Build Coastguard Worker                                                         queue_id,
232*bb4ee6a4SAndroid Build Coastguard Worker                                                         device_name,
233*bb4ee6a4SAndroid Build Coastguard Worker                                                     };
234*bb4ee6a4SAndroid Build Coastguard Worker                                                     let event_index = irq_chip
235*bb4ee6a4SAndroid Build Coastguard Worker                                                         .register_edge_irq_event(
236*bb4ee6a4SAndroid Build Coastguard Worker                                                             irq,
237*bb4ee6a4SAndroid Build Coastguard Worker                                                             &irqevent,
238*bb4ee6a4SAndroid Build Coastguard Worker                                                             source.clone(),
239*bb4ee6a4SAndroid Build Coastguard Worker                                                         )?;
240*bb4ee6a4SAndroid Build Coastguard Worker                                                     if let Some(event_index) = event_index {
241*bb4ee6a4SAndroid Build Coastguard Worker                                                         max_event_index = std::cmp::max(
242*bb4ee6a4SAndroid Build Coastguard Worker                                                             event_index,
243*bb4ee6a4SAndroid Build Coastguard Worker                                                             irq as usize,
244*bb4ee6a4SAndroid Build Coastguard Worker                                                         );
245*bb4ee6a4SAndroid Build Coastguard Worker                                                         irq_frequencies
246*bb4ee6a4SAndroid Build Coastguard Worker                                                             .lock()
247*bb4ee6a4SAndroid Build Coastguard Worker                                                             .resize(max_event_index + 1, 0);
248*bb4ee6a4SAndroid Build Coastguard Worker                                                         irq_event_sources
249*bb4ee6a4SAndroid Build Coastguard Worker                                                             .insert(event_index, source);
250*bb4ee6a4SAndroid Build Coastguard Worker                                                         // Make new thread if needed, including buffer space for any
251*bb4ee6a4SAndroid Build Coastguard Worker                                                         // events we didn't explicitly add (exit/reset/etc)
252*bb4ee6a4SAndroid Build Coastguard Worker                                                         if irq_event_sources.len()
253*bb4ee6a4SAndroid Build Coastguard Worker                                                             % (MAXIMUM_WAIT_OBJECTS - 3)
254*bb4ee6a4SAndroid Build Coastguard Worker                                                             == 0
255*bb4ee6a4SAndroid Build Coastguard Worker                                                         {
256*bb4ee6a4SAndroid Build Coastguard Worker                                                             // The child wait thread has reached max capacity, we
257*bb4ee6a4SAndroid Build Coastguard Worker                                                             // need to add another.
258*bb4ee6a4SAndroid Build Coastguard Worker                                                             child_wait_ctx = Self::add_child(
259*bb4ee6a4SAndroid Build Coastguard Worker                                                                 &wait_ctx, &mut children, &mut child_control_tubes,
260*bb4ee6a4SAndroid Build Coastguard Worker                                                                 irq_chip.try_box_clone()?, irq_frequencies.clone())?;
261*bb4ee6a4SAndroid Build Coastguard Worker 
262*bb4ee6a4SAndroid Build Coastguard Worker                                                         }
263*bb4ee6a4SAndroid Build Coastguard Worker                                                         let irqevent =
264*bb4ee6a4SAndroid Build Coastguard Worker                                                             irqevent.get_trigger().try_clone()?;
265*bb4ee6a4SAndroid Build Coastguard Worker                                                         match child_wait_ctx.add(
266*bb4ee6a4SAndroid Build Coastguard Worker                                                             &irqevent,
267*bb4ee6a4SAndroid Build Coastguard Worker                                                             ChildToken::IrqEvent { event_index },
268*bb4ee6a4SAndroid Build Coastguard Worker                                                         ) {
269*bb4ee6a4SAndroid Build Coastguard Worker                                                             Err(e) => {
270*bb4ee6a4SAndroid Build Coastguard Worker                                                                 warn!("failed to add IrqEvent to synchronization context: {}", e);
271*bb4ee6a4SAndroid Build Coastguard Worker                                                                 Err(e)
272*bb4ee6a4SAndroid Build Coastguard Worker                                                             },
273*bb4ee6a4SAndroid Build Coastguard Worker                                                             Ok(_) => {
274*bb4ee6a4SAndroid Build Coastguard Worker                                                                 vm_control_added_irq_events
275*bb4ee6a4SAndroid Build Coastguard Worker                                                                     .push(irqevent);
276*bb4ee6a4SAndroid Build Coastguard Worker                                                                 Ok(())
277*bb4ee6a4SAndroid Build Coastguard Worker                                                             }
278*bb4ee6a4SAndroid Build Coastguard Worker                                                         }
279*bb4ee6a4SAndroid Build Coastguard Worker                                                     } else {
280*bb4ee6a4SAndroid Build Coastguard Worker                                                         Ok(())
281*bb4ee6a4SAndroid Build Coastguard Worker                                                     }
282*bb4ee6a4SAndroid Build Coastguard Worker                                                 }
283*bb4ee6a4SAndroid Build Coastguard Worker                                                 IrqSetup::Route(route) => irq_chip.route_irq(route),
284*bb4ee6a4SAndroid Build Coastguard Worker                                                 IrqSetup::UnRegister(irq, ev) => irq_chip
285*bb4ee6a4SAndroid Build Coastguard Worker                                                     .unregister_edge_irq_event(
286*bb4ee6a4SAndroid Build Coastguard Worker                                                         irq,
287*bb4ee6a4SAndroid Build Coastguard Worker                                                         &IrqEdgeEvent::from_event(ev.try_clone()?),
288*bb4ee6a4SAndroid Build Coastguard Worker                                                     ),
289*bb4ee6a4SAndroid Build Coastguard Worker                                             },
290*bb4ee6a4SAndroid Build Coastguard Worker                                             &mut self.sys_allocator.lock(),
291*bb4ee6a4SAndroid Build Coastguard Worker                                         )
292*bb4ee6a4SAndroid Build Coastguard Worker                                     };
293*bb4ee6a4SAndroid Build Coastguard Worker                                     if let Err(e) = tube.send(&response) {
294*bb4ee6a4SAndroid Build Coastguard Worker                                         error!("failed to send VmIrqResponse: {}", e);
295*bb4ee6a4SAndroid Build Coastguard Worker                                     }
296*bb4ee6a4SAndroid Build Coastguard Worker                                 }
297*bb4ee6a4SAndroid Build Coastguard Worker                                 Err(e) => {
298*bb4ee6a4SAndroid Build Coastguard Worker                                     if let TubeError::Disconnected = e {
299*bb4ee6a4SAndroid Build Coastguard Worker                                         vm_control_indices_to_remove.push(index);
300*bb4ee6a4SAndroid Build Coastguard Worker                                     } else {
301*bb4ee6a4SAndroid Build Coastguard Worker                                         error!("failed to recv VmIrqRequest: {}", e);
302*bb4ee6a4SAndroid Build Coastguard Worker                                     }
303*bb4ee6a4SAndroid Build Coastguard Worker                                 }
304*bb4ee6a4SAndroid Build Coastguard Worker                             }
305*bb4ee6a4SAndroid Build Coastguard Worker                         }
306*bb4ee6a4SAndroid Build Coastguard Worker                     }
307*bb4ee6a4SAndroid Build Coastguard Worker                     Token::DelayedIrqEvent => {
308*bb4ee6a4SAndroid Build Coastguard Worker                         if let Err(e) = self.irq_chip.process_delayed_irq_events() {
309*bb4ee6a4SAndroid Build Coastguard Worker                             warn!("can't deliver delayed irqs: {}", e);
310*bb4ee6a4SAndroid Build Coastguard Worker                         }
311*bb4ee6a4SAndroid Build Coastguard Worker                     }
312*bb4ee6a4SAndroid Build Coastguard Worker                 }
313*bb4ee6a4SAndroid Build Coastguard Worker             }
314*bb4ee6a4SAndroid Build Coastguard Worker 
315*bb4ee6a4SAndroid Build Coastguard Worker             let now = Instant::now();
316*bb4ee6a4SAndroid Build Coastguard Worker             let intr_stat_duration = now.duration_since(intr_stat_sample_time);
317*bb4ee6a4SAndroid Build Coastguard Worker 
318*bb4ee6a4SAndroid Build Coastguard Worker             // include interrupt stats every 10 seconds
319*bb4ee6a4SAndroid Build Coastguard Worker             if intr_stat_duration > Duration::from_secs(10) {
320*bb4ee6a4SAndroid Build Coastguard Worker                 let mut event_indices: Vec<(&usize, &IrqEventSource)> =
321*bb4ee6a4SAndroid Build Coastguard Worker                     irq_event_sources.iter().collect();
322*bb4ee6a4SAndroid Build Coastguard Worker                 // sort the devices by irq_frequency
323*bb4ee6a4SAndroid Build Coastguard Worker                 let mut locked_irq_frequencies = irq_frequencies.lock();
324*bb4ee6a4SAndroid Build Coastguard Worker                 event_indices
325*bb4ee6a4SAndroid Build Coastguard Worker                     .sort_by_key(|(idx, _)| std::cmp::Reverse(locked_irq_frequencies[**idx]));
326*bb4ee6a4SAndroid Build Coastguard Worker                 let rates: Vec<String> = event_indices
327*bb4ee6a4SAndroid Build Coastguard Worker                     .iter()
328*bb4ee6a4SAndroid Build Coastguard Worker                     .filter(|(idx, _)| locked_irq_frequencies[**idx] > 0)
329*bb4ee6a4SAndroid Build Coastguard Worker                     .map(|(idx, source)| {
330*bb4ee6a4SAndroid Build Coastguard Worker                         let rate = locked_irq_frequencies[**idx] / intr_stat_duration.as_secs();
331*bb4ee6a4SAndroid Build Coastguard Worker                         // As the descriptor, use a 64bit int containing two 32bit ids.
332*bb4ee6a4SAndroid Build Coastguard Worker                         // low bits: queue_id, high bits: device_id
333*bb4ee6a4SAndroid Build Coastguard Worker                         let descriptor_bytes: [u8; 8] = {
334*bb4ee6a4SAndroid Build Coastguard Worker                             let mut bytes: [u8; 8] = [0; 8];
335*bb4ee6a4SAndroid Build Coastguard Worker                             for (i, byte) in
336*bb4ee6a4SAndroid Build Coastguard Worker                                 (source.queue_id as u32).to_le_bytes().iter().enumerate()
337*bb4ee6a4SAndroid Build Coastguard Worker                             {
338*bb4ee6a4SAndroid Build Coastguard Worker                                 bytes[i] = *byte
339*bb4ee6a4SAndroid Build Coastguard Worker                             }
340*bb4ee6a4SAndroid Build Coastguard Worker                             let device_id: u32 = source.device_id.into();
341*bb4ee6a4SAndroid Build Coastguard Worker                             for (i, byte) in device_id.to_le_bytes().iter().enumerate() {
342*bb4ee6a4SAndroid Build Coastguard Worker                                 bytes[i + 4] = *byte
343*bb4ee6a4SAndroid Build Coastguard Worker                             }
344*bb4ee6a4SAndroid Build Coastguard Worker                             bytes
345*bb4ee6a4SAndroid Build Coastguard Worker                         };
346*bb4ee6a4SAndroid Build Coastguard Worker                         log_high_frequency_descriptor_event(
347*bb4ee6a4SAndroid Build Coastguard Worker                             MetricEventType::Interrupts,
348*bb4ee6a4SAndroid Build Coastguard Worker                             i64::from_le_bytes(descriptor_bytes),
349*bb4ee6a4SAndroid Build Coastguard Worker                             rate as i64,
350*bb4ee6a4SAndroid Build Coastguard Worker                         );
351*bb4ee6a4SAndroid Build Coastguard Worker                         format!("{}({})->{}/s", source.device_name, source.queue_id, rate,)
352*bb4ee6a4SAndroid Build Coastguard Worker                     })
353*bb4ee6a4SAndroid Build Coastguard Worker                     .collect();
354*bb4ee6a4SAndroid Build Coastguard Worker 
355*bb4ee6a4SAndroid Build Coastguard Worker                 info!("crosvm-interrupt-rates: {}", rates.join(", "));
356*bb4ee6a4SAndroid Build Coastguard Worker 
357*bb4ee6a4SAndroid Build Coastguard Worker                 // reset sample time and counters
358*bb4ee6a4SAndroid Build Coastguard Worker                 intr_stat_sample_time = now;
359*bb4ee6a4SAndroid Build Coastguard Worker                 *locked_irq_frequencies = vec![0; max_event_index + 1];
360*bb4ee6a4SAndroid Build Coastguard Worker             }
361*bb4ee6a4SAndroid Build Coastguard Worker 
362*bb4ee6a4SAndroid Build Coastguard Worker             vm_control_indices_to_remove.dedup();
363*bb4ee6a4SAndroid Build Coastguard Worker             for index in vm_control_indices_to_remove {
364*bb4ee6a4SAndroid Build Coastguard Worker                 self.irq_control_tubes.swap_remove(index);
365*bb4ee6a4SAndroid Build Coastguard Worker             }
366*bb4ee6a4SAndroid Build Coastguard Worker 
367*bb4ee6a4SAndroid Build Coastguard Worker             if notify_control_on_iteration_end {
368*bb4ee6a4SAndroid Build Coastguard Worker                 if let Err(e) =
369*bb4ee6a4SAndroid Build Coastguard Worker                     // We want to send the number of IRQ events processed in
370*bb4ee6a4SAndroid Build Coastguard Worker                     // this iteration. Token count is almost what we want,
371*bb4ee6a4SAndroid Build Coastguard Worker                     // except it counts the IrqHandlerControl token, so we just
372*bb4ee6a4SAndroid Build Coastguard Worker                     // subtract it off to get the desired value.
373*bb4ee6a4SAndroid Build Coastguard Worker                     self.irq_handler_control.send(
374*bb4ee6a4SAndroid Build Coastguard Worker                         &IrqHandlerResponse::HandlerIterationComplete(token_count - 1),
375*bb4ee6a4SAndroid Build Coastguard Worker                     )
376*bb4ee6a4SAndroid Build Coastguard Worker                 {
377*bb4ee6a4SAndroid Build Coastguard Worker                     error!(
378*bb4ee6a4SAndroid Build Coastguard Worker                         "failed to notify on iteration completion (snapshotting may fail): {}",
379*bb4ee6a4SAndroid Build Coastguard Worker                         e
380*bb4ee6a4SAndroid Build Coastguard Worker                     );
381*bb4ee6a4SAndroid Build Coastguard Worker                 }
382*bb4ee6a4SAndroid Build Coastguard Worker             }
383*bb4ee6a4SAndroid Build Coastguard Worker         }
384*bb4ee6a4SAndroid Build Coastguard Worker 
385*bb4ee6a4SAndroid Build Coastguard Worker         // Ensure all children have exited.
386*bb4ee6a4SAndroid Build Coastguard Worker         for child_control_tube in child_control_tubes.iter() {
387*bb4ee6a4SAndroid Build Coastguard Worker             if let Err(e) = child_control_tube.send(&IrqHandlerRequest::Exit) {
388*bb4ee6a4SAndroid Build Coastguard Worker                 warn!("failed to send exit signal to IRQ worker: {}", e);
389*bb4ee6a4SAndroid Build Coastguard Worker             }
390*bb4ee6a4SAndroid Build Coastguard Worker         }
391*bb4ee6a4SAndroid Build Coastguard Worker 
392*bb4ee6a4SAndroid Build Coastguard Worker         // Ensure all children have exited and aren't stalled / stuck.
393*bb4ee6a4SAndroid Build Coastguard Worker         for child in children {
394*bb4ee6a4SAndroid Build Coastguard Worker             match child.join() {
395*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(Err(e)) => warn!("IRQ worker child ended in error: {}", e),
396*bb4ee6a4SAndroid Build Coastguard Worker                 Err(e) => warn!("IRQ worker child panicked with error: {:?}", e),
397*bb4ee6a4SAndroid Build Coastguard Worker                 _ => {}
398*bb4ee6a4SAndroid Build Coastguard Worker             }
399*bb4ee6a4SAndroid Build Coastguard Worker         }
400*bb4ee6a4SAndroid Build Coastguard Worker 
401*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
402*bb4ee6a4SAndroid Build Coastguard Worker     }
403*bb4ee6a4SAndroid Build Coastguard Worker }
404*bb4ee6a4SAndroid Build Coastguard Worker 
405*bb4ee6a4SAndroid Build Coastguard Worker #[derive(EventToken)]
406*bb4ee6a4SAndroid Build Coastguard Worker enum ChildToken {
407*bb4ee6a4SAndroid Build Coastguard Worker     IrqHandlerControl,
408*bb4ee6a4SAndroid Build Coastguard Worker     IrqEvent { event_index: IrqEventIndex },
409*bb4ee6a4SAndroid Build Coastguard Worker }
410*bb4ee6a4SAndroid Build Coastguard Worker /// An arbitrarily expandible worker for waiting on irq events.
411*bb4ee6a4SAndroid Build Coastguard Worker /// This worker is responsible for hadling the irq events, whereas
412*bb4ee6a4SAndroid Build Coastguard Worker /// the parent worker's job is just to handle the irq control tube requests.
413*bb4ee6a4SAndroid Build Coastguard Worker struct IrqWaitWorkerChild {
414*bb4ee6a4SAndroid Build Coastguard Worker     wait_ctx: Arc<WaitContext<ChildToken>>,
415*bb4ee6a4SAndroid Build Coastguard Worker     irq_handler_control: Tube,
416*bb4ee6a4SAndroid Build Coastguard Worker     irq_chip: Box<dyn IrqChipArch>,
417*bb4ee6a4SAndroid Build Coastguard Worker     irq_frequencies: Arc<Mutex<Vec<u64>>>,
418*bb4ee6a4SAndroid Build Coastguard Worker }
419*bb4ee6a4SAndroid Build Coastguard Worker 
420*bb4ee6a4SAndroid Build Coastguard Worker impl IrqWaitWorkerChild {
start( irq_handler_control: Tube, irq_chip: Box<dyn IrqChipArch>, irq_frequencies: Arc<Mutex<Vec<u64>>>, ) -> Result<(Arc<WaitContext<ChildToken>>, JoinHandle<Result<()>>)>421*bb4ee6a4SAndroid Build Coastguard Worker     fn start(
422*bb4ee6a4SAndroid Build Coastguard Worker         irq_handler_control: Tube,
423*bb4ee6a4SAndroid Build Coastguard Worker         irq_chip: Box<dyn IrqChipArch>,
424*bb4ee6a4SAndroid Build Coastguard Worker         irq_frequencies: Arc<Mutex<Vec<u64>>>,
425*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<(Arc<WaitContext<ChildToken>>, JoinHandle<Result<()>>)> {
426*bb4ee6a4SAndroid Build Coastguard Worker         let child_wait_ctx = Arc::new(WaitContext::new()?);
427*bb4ee6a4SAndroid Build Coastguard Worker         let mut child = IrqWaitWorkerChild {
428*bb4ee6a4SAndroid Build Coastguard Worker             wait_ctx: child_wait_ctx.clone(),
429*bb4ee6a4SAndroid Build Coastguard Worker             irq_handler_control,
430*bb4ee6a4SAndroid Build Coastguard Worker             irq_chip,
431*bb4ee6a4SAndroid Build Coastguard Worker             irq_frequencies,
432*bb4ee6a4SAndroid Build Coastguard Worker         };
433*bb4ee6a4SAndroid Build Coastguard Worker         let join_handle = thread::Builder::new()
434*bb4ee6a4SAndroid Build Coastguard Worker             .name("irq_child_wait_loop".into())
435*bb4ee6a4SAndroid Build Coastguard Worker             .spawn(move || child.run())?;
436*bb4ee6a4SAndroid Build Coastguard Worker 
437*bb4ee6a4SAndroid Build Coastguard Worker         Ok((child_wait_ctx, join_handle))
438*bb4ee6a4SAndroid Build Coastguard Worker     }
439*bb4ee6a4SAndroid Build Coastguard Worker 
run(&mut self) -> Result<()>440*bb4ee6a4SAndroid Build Coastguard Worker     fn run(&mut self) -> Result<()> {
441*bb4ee6a4SAndroid Build Coastguard Worker         self.wait_ctx.add(
442*bb4ee6a4SAndroid Build Coastguard Worker             self.irq_handler_control.get_read_notifier(),
443*bb4ee6a4SAndroid Build Coastguard Worker             ChildToken::IrqHandlerControl,
444*bb4ee6a4SAndroid Build Coastguard Worker         )?;
445*bb4ee6a4SAndroid Build Coastguard Worker         'poll: loop {
446*bb4ee6a4SAndroid Build Coastguard Worker             let events = {
447*bb4ee6a4SAndroid Build Coastguard Worker                 match self.wait_ctx.wait() {
448*bb4ee6a4SAndroid Build Coastguard Worker                     Ok(v) => v,
449*bb4ee6a4SAndroid Build Coastguard Worker                     Err(e) => {
450*bb4ee6a4SAndroid Build Coastguard Worker                         error!("failed to wait on irq child thread: {}", e);
451*bb4ee6a4SAndroid Build Coastguard Worker                         break 'poll;
452*bb4ee6a4SAndroid Build Coastguard Worker                     }
453*bb4ee6a4SAndroid Build Coastguard Worker                 }
454*bb4ee6a4SAndroid Build Coastguard Worker             };
455*bb4ee6a4SAndroid Build Coastguard Worker 
456*bb4ee6a4SAndroid Build Coastguard Worker             let token_count = events.len();
457*bb4ee6a4SAndroid Build Coastguard Worker             let mut notify_control_on_iteration_end = false;
458*bb4ee6a4SAndroid Build Coastguard Worker 
459*bb4ee6a4SAndroid Build Coastguard Worker             for event in events.iter().filter(|e| e.is_readable) {
460*bb4ee6a4SAndroid Build Coastguard Worker                 match event.token {
461*bb4ee6a4SAndroid Build Coastguard Worker                     ChildToken::IrqHandlerControl => {
462*bb4ee6a4SAndroid Build Coastguard Worker                         match self.irq_handler_control.recv::<IrqHandlerRequest>() {
463*bb4ee6a4SAndroid Build Coastguard Worker                             Ok(request) => match request {
464*bb4ee6a4SAndroid Build Coastguard Worker                                 IrqHandlerRequest::Exit => {
465*bb4ee6a4SAndroid Build Coastguard Worker                                     info!("irq child event loop got exit event");
466*bb4ee6a4SAndroid Build Coastguard Worker                                     break 'poll;
467*bb4ee6a4SAndroid Build Coastguard Worker                                 }
468*bb4ee6a4SAndroid Build Coastguard Worker                                 IrqHandlerRequest::AddIrqControlTubes(_tubes) => {
469*bb4ee6a4SAndroid Build Coastguard Worker                                     panic!("Windows does not support adding devices on the fly.");
470*bb4ee6a4SAndroid Build Coastguard Worker                                 }
471*bb4ee6a4SAndroid Build Coastguard Worker                                 IrqHandlerRequest::WakeAndNotifyIteration => {
472*bb4ee6a4SAndroid Build Coastguard Worker                                     notify_control_on_iteration_end = true;
473*bb4ee6a4SAndroid Build Coastguard Worker                                 }
474*bb4ee6a4SAndroid Build Coastguard Worker                                 IrqHandlerRequest::RefreshIrqEventTokens => {
475*bb4ee6a4SAndroid Build Coastguard Worker                                     // TODO(b/282755619): when we want to restore different shaped
476*bb4ee6a4SAndroid Build Coastguard Worker                                     // VMs, we'll have to implement this.
477*bb4ee6a4SAndroid Build Coastguard Worker                                     todo!("not implemented yet");
478*bb4ee6a4SAndroid Build Coastguard Worker                                 }
479*bb4ee6a4SAndroid Build Coastguard Worker                             },
480*bb4ee6a4SAndroid Build Coastguard Worker                             Err(e) => {
481*bb4ee6a4SAndroid Build Coastguard Worker                                 if let TubeError::Disconnected = e {
482*bb4ee6a4SAndroid Build Coastguard Worker                                     panic!("irq handler control tube disconnected.");
483*bb4ee6a4SAndroid Build Coastguard Worker                                 } else {
484*bb4ee6a4SAndroid Build Coastguard Worker                                     error!("failed to recv IrqHandlerRequest: {}", e);
485*bb4ee6a4SAndroid Build Coastguard Worker                                 }
486*bb4ee6a4SAndroid Build Coastguard Worker                             }
487*bb4ee6a4SAndroid Build Coastguard Worker                         }
488*bb4ee6a4SAndroid Build Coastguard Worker                     }
489*bb4ee6a4SAndroid Build Coastguard Worker                     ChildToken::IrqEvent { event_index } => {
490*bb4ee6a4SAndroid Build Coastguard Worker                         self.irq_frequencies.lock()[event_index] += 1;
491*bb4ee6a4SAndroid Build Coastguard Worker                         if let Err(e) = self.irq_chip.service_irq_event(event_index) {
492*bb4ee6a4SAndroid Build Coastguard Worker                             error!("failed to signal irq {}: {}", event_index, e);
493*bb4ee6a4SAndroid Build Coastguard Worker                         }
494*bb4ee6a4SAndroid Build Coastguard Worker                     }
495*bb4ee6a4SAndroid Build Coastguard Worker                 }
496*bb4ee6a4SAndroid Build Coastguard Worker             }
497*bb4ee6a4SAndroid Build Coastguard Worker 
498*bb4ee6a4SAndroid Build Coastguard Worker             if notify_control_on_iteration_end {
499*bb4ee6a4SAndroid Build Coastguard Worker                 if let Err(e) =
500*bb4ee6a4SAndroid Build Coastguard Worker                     // We want to send the number of IRQ events processed in
501*bb4ee6a4SAndroid Build Coastguard Worker                     // this iteration. Token count is almost what we want,
502*bb4ee6a4SAndroid Build Coastguard Worker                     // except it counts the IrqHandlerControl token, so we just
503*bb4ee6a4SAndroid Build Coastguard Worker                     // subtract it off to get the desired value.
504*bb4ee6a4SAndroid Build Coastguard Worker                     self.irq_handler_control.send(
505*bb4ee6a4SAndroid Build Coastguard Worker                         &IrqHandlerResponse::HandlerIterationComplete(token_count - 1),
506*bb4ee6a4SAndroid Build Coastguard Worker                     )
507*bb4ee6a4SAndroid Build Coastguard Worker                 {
508*bb4ee6a4SAndroid Build Coastguard Worker                     error!(
509*bb4ee6a4SAndroid Build Coastguard Worker                         "failed to notify on child iteration completion (snapshotting may fail): {}",
510*bb4ee6a4SAndroid Build Coastguard Worker                         e
511*bb4ee6a4SAndroid Build Coastguard Worker                     );
512*bb4ee6a4SAndroid Build Coastguard Worker                 }
513*bb4ee6a4SAndroid Build Coastguard Worker             }
514*bb4ee6a4SAndroid Build Coastguard Worker         }
515*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
516*bb4ee6a4SAndroid Build Coastguard Worker     }
517*bb4ee6a4SAndroid Build Coastguard Worker }
518