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