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