1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 // 3 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE-BSD-3-Clause file. 6 // 7 // Copyright © 2020 Intel Corporation 8 // 9 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 10 11 use std::fs::File; 12 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; 13 use std::sync::atomic::{AtomicBool, Ordering}; 14 use std::sync::{Arc, Barrier}; 15 use std::thread; 16 use vmm_sys_util::eventfd::EventFd; 17 18 pub struct EpollHelper { 19 pause_evt: EventFd, 20 epoll_file: File, 21 } 22 23 #[derive(Debug)] 24 pub enum EpollHelperError { 25 CreateFd(std::io::Error), 26 Ctl(std::io::Error), 27 IoError(std::io::Error), 28 Wait(std::io::Error), 29 } 30 31 pub const EPOLL_HELPER_EVENT_PAUSE: u16 = 0; 32 pub const EPOLL_HELPER_EVENT_KILL: u16 = 1; 33 pub const EPOLL_HELPER_EVENT_LAST: u16 = 15; 34 35 pub trait EpollHelperHandler { 36 // Return true if execution of the loop should be stopped 37 fn handle_event(&mut self, helper: &mut EpollHelper, event: &epoll::Event) -> bool; 38 } 39 40 impl EpollHelper { 41 pub fn new( 42 kill_evt: &EventFd, 43 pause_evt: &EventFd, 44 ) -> std::result::Result<Self, EpollHelperError> { 45 // Create the epoll file descriptor 46 let epoll_fd = epoll::create(true).map_err(EpollHelperError::CreateFd)?; 47 // Use 'File' to enforce closing on 'epoll_fd' 48 let epoll_file = unsafe { File::from_raw_fd(epoll_fd) }; 49 50 let mut helper = Self { 51 pause_evt: pause_evt.try_clone().unwrap(), 52 epoll_file, 53 }; 54 55 helper.add_event(kill_evt.as_raw_fd(), EPOLL_HELPER_EVENT_KILL)?; 56 helper.add_event(pause_evt.as_raw_fd(), EPOLL_HELPER_EVENT_PAUSE)?; 57 Ok(helper) 58 } 59 60 pub fn add_event(&mut self, fd: RawFd, id: u16) -> std::result::Result<(), EpollHelperError> { 61 self.add_event_custom(fd, id, epoll::Events::EPOLLIN) 62 } 63 64 pub fn add_event_custom( 65 &mut self, 66 fd: RawFd, 67 id: u16, 68 evts: epoll::Events, 69 ) -> std::result::Result<(), EpollHelperError> { 70 epoll::ctl( 71 self.epoll_file.as_raw_fd(), 72 epoll::ControlOptions::EPOLL_CTL_ADD, 73 fd, 74 epoll::Event::new(evts, id.into()), 75 ) 76 .map_err(EpollHelperError::Ctl) 77 } 78 79 pub fn del_event_custom( 80 &mut self, 81 fd: RawFd, 82 id: u16, 83 evts: epoll::Events, 84 ) -> std::result::Result<(), EpollHelperError> { 85 epoll::ctl( 86 self.epoll_file.as_raw_fd(), 87 epoll::ControlOptions::EPOLL_CTL_DEL, 88 fd, 89 epoll::Event::new(evts, id.into()), 90 ) 91 .map_err(EpollHelperError::Ctl) 92 } 93 94 pub fn run( 95 &mut self, 96 paused: Arc<AtomicBool>, 97 paused_sync: Arc<Barrier>, 98 handler: &mut dyn EpollHelperHandler, 99 ) -> std::result::Result<(), EpollHelperError> { 100 const EPOLL_EVENTS_LEN: usize = 100; 101 let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; 102 103 // Before jumping into the epoll loop, check if the device is expected 104 // to be in a paused state. This is helpful for the restore code path 105 // as the device thread should not start processing anything before the 106 // device has been resumed. 107 while paused.load(Ordering::SeqCst) { 108 thread::park(); 109 } 110 111 loop { 112 let num_events = match epoll::wait(self.epoll_file.as_raw_fd(), -1, &mut events[..]) { 113 Ok(res) => res, 114 Err(e) => { 115 if e.kind() == std::io::ErrorKind::Interrupted { 116 // It's well defined from the epoll_wait() syscall 117 // documentation that the epoll loop can be interrupted 118 // before any of the requested events occurred or the 119 // timeout expired. In both those cases, epoll_wait() 120 // returns an error of type EINTR, but this should not 121 // be considered as a regular error. Instead it is more 122 // appropriate to retry, by calling into epoll_wait(). 123 continue; 124 } 125 return Err(EpollHelperError::Wait(e)); 126 } 127 }; 128 129 for event in events.iter().take(num_events) { 130 let ev_type = event.data as u16; 131 132 match ev_type { 133 EPOLL_HELPER_EVENT_KILL => { 134 debug!("KILL_EVENT received, stopping epoll loop"); 135 return Ok(()); 136 } 137 EPOLL_HELPER_EVENT_PAUSE => { 138 debug!("PAUSE_EVENT received, pausing epoll loop"); 139 140 // Acknowledge the pause is effective by using the 141 // paused_sync barrier. 142 paused_sync.wait(); 143 144 // We loop here to handle spurious park() returns. 145 // Until we have not resumed, the paused boolean will 146 // be true. 147 while paused.load(Ordering::SeqCst) { 148 thread::park(); 149 } 150 151 // Drain pause event after the device has been resumed. 152 // This ensures the pause event has been seen by each 153 // and every thread related to this virtio device. 154 let _ = self.pause_evt.read(); 155 } 156 _ => { 157 if handler.handle_event(self, event) { 158 return Ok(()); 159 } 160 } 161 } 162 } 163 } 164 } 165 } 166 167 impl AsRawFd for EpollHelper { 168 fn as_raw_fd(&self) -> RawFd { 169 self.epoll_file.as_raw_fd() 170 } 171 } 172