1 // Copyright 2019 Intel Corporation. All Rights Reserved. 2 // Copyright 2019-2021 Alibaba Cloud. All rights reserved. 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 6 use std::fmt::{Display, Formatter}; 7 use std::io::{self, Result}; 8 use std::marker::PhantomData; 9 use std::os::unix::io::{AsRawFd, RawFd}; 10 11 use vm_memory::bitmap::Bitmap; 12 use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; 13 use vmm_sys_util::eventfd::EventFd; 14 15 use super::backend::VhostUserBackend; 16 use super::vring::VringT; 17 use super::GM; 18 19 /// Errors related to vring epoll event handling. 20 #[derive(Debug)] 21 pub enum VringEpollError { 22 /// Failed to create epoll file descriptor. 23 EpollCreateFd(io::Error), 24 /// Failed while waiting for events. 25 EpollWait(io::Error), 26 /// Could not register exit event 27 RegisterExitEvent(io::Error), 28 /// Failed to read the event from kick EventFd. 29 HandleEventReadKick(io::Error), 30 /// Failed to handle the event from the backend. 31 HandleEventBackendHandling(io::Error), 32 } 33 34 impl Display for VringEpollError { fmt(&self, f: &mut Formatter) -> std::fmt::Result35 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 36 match self { 37 VringEpollError::EpollCreateFd(e) => write!(f, "cannot create epoll fd: {}", e), 38 VringEpollError::EpollWait(e) => write!(f, "failed to wait for epoll event: {}", e), 39 VringEpollError::RegisterExitEvent(e) => write!(f, "cannot register exit event: {}", e), 40 VringEpollError::HandleEventReadKick(e) => { 41 write!(f, "cannot read vring kick event: {}", e) 42 } 43 VringEpollError::HandleEventBackendHandling(e) => { 44 write!(f, "failed to handle epoll event: {}", e) 45 } 46 } 47 } 48 } 49 50 impl std::error::Error for VringEpollError {} 51 52 /// Result of vring epoll operations. 53 pub type VringEpollResult<T> = std::result::Result<T, VringEpollError>; 54 55 /// Epoll event handler to manage and process epoll events for registered file descriptor. 56 /// 57 /// The `VringEpollHandler` structure provides interfaces to: 58 /// - add file descriptors to be monitored by the epoll fd 59 /// - remove registered file descriptors from the epoll fd 60 /// - run the event loop to handle pending events on the epoll fd 61 pub struct VringEpollHandler<S, V, B> { 62 epoll: Epoll, 63 backend: S, 64 vrings: Vec<V>, 65 thread_id: usize, 66 exit_event_fd: Option<EventFd>, 67 phantom: PhantomData<B>, 68 } 69 70 impl<S, V, B> VringEpollHandler<S, V, B> { 71 /// Send `exit event` to break the event loop. send_exit_event(&self)72 pub fn send_exit_event(&self) { 73 if let Some(eventfd) = self.exit_event_fd.as_ref() { 74 let _ = eventfd.write(1); 75 } 76 } 77 } 78 79 impl<S, V, B> VringEpollHandler<S, V, B> 80 where 81 S: VhostUserBackend<V, B>, 82 V: VringT<GM<B>>, 83 B: Bitmap + 'static, 84 { 85 /// Create a `VringEpollHandler` instance. new(backend: S, vrings: Vec<V>, thread_id: usize) -> VringEpollResult<Self>86 pub(crate) fn new(backend: S, vrings: Vec<V>, thread_id: usize) -> VringEpollResult<Self> { 87 let epoll = Epoll::new().map_err(VringEpollError::EpollCreateFd)?; 88 let exit_event_fd = backend.exit_event(thread_id); 89 90 if let Some(exit_event_fd) = &exit_event_fd { 91 let id = backend.num_queues(); 92 epoll 93 .ctl( 94 ControlOperation::Add, 95 exit_event_fd.as_raw_fd(), 96 EpollEvent::new(EventSet::IN, id as u64), 97 ) 98 .map_err(VringEpollError::RegisterExitEvent)?; 99 } 100 101 Ok(VringEpollHandler { 102 epoll, 103 backend, 104 vrings, 105 thread_id, 106 exit_event_fd, 107 phantom: PhantomData, 108 }) 109 } 110 111 /// Register an event into the epoll fd. 112 /// 113 /// When this event is later triggered, the backend implementation of `handle_event` will be 114 /// called. register_listener(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()>115 pub fn register_listener(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()> { 116 // `data` range [0...num_queues] is reserved for queues and exit event. 117 if data <= self.backend.num_queues() as u64 { 118 Err(io::Error::from_raw_os_error(libc::EINVAL)) 119 } else { 120 self.register_event(fd, ev_type, data) 121 } 122 } 123 124 /// Unregister an event from the epoll fd. 125 /// 126 /// If the event is triggered after this function has been called, the event will be silently 127 /// dropped. unregister_listener(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()>128 pub fn unregister_listener(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()> { 129 // `data` range [0...num_queues] is reserved for queues and exit event. 130 if data <= self.backend.num_queues() as u64 { 131 Err(io::Error::from_raw_os_error(libc::EINVAL)) 132 } else { 133 self.unregister_event(fd, ev_type, data) 134 } 135 } 136 register_event(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()>137 pub(crate) fn register_event(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()> { 138 self.epoll 139 .ctl(ControlOperation::Add, fd, EpollEvent::new(ev_type, data)) 140 } 141 unregister_event(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()>142 pub(crate) fn unregister_event(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()> { 143 self.epoll 144 .ctl(ControlOperation::Delete, fd, EpollEvent::new(ev_type, data)) 145 } 146 147 /// Run the event poll loop to handle all pending events on registered fds. 148 /// 149 /// The event loop will be terminated once an event is received from the `exit event fd` 150 /// associated with the backend. run(&self) -> VringEpollResult<()>151 pub(crate) fn run(&self) -> VringEpollResult<()> { 152 const EPOLL_EVENTS_LEN: usize = 100; 153 let mut events = vec![EpollEvent::new(EventSet::empty(), 0); EPOLL_EVENTS_LEN]; 154 155 'epoll: loop { 156 let num_events = match self.epoll.wait(-1, &mut events[..]) { 157 Ok(res) => res, 158 Err(e) => { 159 if e.kind() == io::ErrorKind::Interrupted { 160 // It's well defined from the epoll_wait() syscall 161 // documentation that the epoll loop can be interrupted 162 // before any of the requested events occurred or the 163 // timeout expired. In both those cases, epoll_wait() 164 // returns an error of type EINTR, but this should not 165 // be considered as a regular error. Instead it is more 166 // appropriate to retry, by calling into epoll_wait(). 167 continue; 168 } 169 return Err(VringEpollError::EpollWait(e)); 170 } 171 }; 172 173 for event in events.iter().take(num_events) { 174 let evset = match EventSet::from_bits(event.events) { 175 Some(evset) => evset, 176 None => { 177 let evbits = event.events; 178 println!("epoll: ignoring unknown event set: 0x{:x}", evbits); 179 continue; 180 } 181 }; 182 183 let ev_type = event.data() as u16; 184 185 // handle_event() returns true if an event is received from the exit event fd. 186 if self.handle_event(ev_type, evset)? { 187 break 'epoll; 188 } 189 } 190 } 191 192 Ok(()) 193 } 194 handle_event(&self, device_event: u16, evset: EventSet) -> VringEpollResult<bool>195 fn handle_event(&self, device_event: u16, evset: EventSet) -> VringEpollResult<bool> { 196 if self.exit_event_fd.is_some() && device_event as usize == self.backend.num_queues() { 197 return Ok(true); 198 } 199 200 if (device_event as usize) < self.vrings.len() { 201 let vring = &self.vrings[device_event as usize]; 202 let enabled = vring 203 .read_kick() 204 .map_err(VringEpollError::HandleEventReadKick)?; 205 206 // If the vring is not enabled, it should not be processed. 207 if !enabled { 208 return Ok(false); 209 } 210 } 211 212 self.backend 213 .handle_event(device_event, evset, &self.vrings, self.thread_id) 214 .map_err(VringEpollError::HandleEventBackendHandling) 215 } 216 } 217 218 impl<S, V, B> AsRawFd for VringEpollHandler<S, V, B> { as_raw_fd(&self) -> RawFd219 fn as_raw_fd(&self) -> RawFd { 220 self.epoll.as_raw_fd() 221 } 222 } 223 224 #[cfg(test)] 225 mod tests { 226 use super::super::backend::tests::MockVhostBackend; 227 use super::super::vring::VringRwLock; 228 use super::*; 229 use std::sync::{Arc, Mutex}; 230 use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap}; 231 use vmm_sys_util::eventfd::EventFd; 232 233 #[test] test_vring_epoll_handler()234 fn test_vring_epoll_handler() { 235 let mem = GuestMemoryAtomic::new( 236 GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x100000), 0x10000)]).unwrap(), 237 ); 238 let vring = VringRwLock::new(mem, 0x1000).unwrap(); 239 let backend = Arc::new(Mutex::new(MockVhostBackend::new())); 240 241 let handler = VringEpollHandler::new(backend, vec![vring], 0x1).unwrap(); 242 243 let eventfd = EventFd::new(0).unwrap(); 244 handler 245 .register_listener(eventfd.as_raw_fd(), EventSet::IN, 3) 246 .unwrap(); 247 // Register an already registered fd. 248 handler 249 .register_listener(eventfd.as_raw_fd(), EventSet::IN, 3) 250 .unwrap_err(); 251 // Register an invalid data. 252 handler 253 .register_listener(eventfd.as_raw_fd(), EventSet::IN, 1) 254 .unwrap_err(); 255 256 handler 257 .unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 3) 258 .unwrap(); 259 // unregister an already unregistered fd. 260 handler 261 .unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 3) 262 .unwrap_err(); 263 // unregister an invalid data. 264 handler 265 .unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 1) 266 .unwrap_err(); 267 // Check we retrieve the correct file descriptor 268 assert_eq!(handler.as_raw_fd(), handler.epoll.as_raw_fd()); 269 } 270 } 271