vhost_user_backend/
event_loop.rs

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
6use std::fmt::{Display, Formatter};
7use std::io::{self, Result};
8use std::marker::PhantomData;
9use std::os::unix::io::{AsRawFd, RawFd};
10
11use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet};
12use vmm_sys_util::eventfd::EventFd;
13
14use super::backend::VhostUserBackend;
15use super::vring::VringT;
16
17/// Errors related to vring epoll event handling.
18#[derive(Debug)]
19pub enum VringEpollError {
20    /// Failed to create epoll file descriptor.
21    EpollCreateFd(io::Error),
22    /// Failed while waiting for events.
23    EpollWait(io::Error),
24    /// Could not register exit event
25    RegisterExitEvent(io::Error),
26    /// Failed to read the event from kick EventFd.
27    HandleEventReadKick(io::Error),
28    /// Failed to handle the event from the backend.
29    HandleEventBackendHandling(io::Error),
30}
31
32impl Display for VringEpollError {
33    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
34        match self {
35            VringEpollError::EpollCreateFd(e) => write!(f, "cannot create epoll fd: {}", e),
36            VringEpollError::EpollWait(e) => write!(f, "failed to wait for epoll event: {}", e),
37            VringEpollError::RegisterExitEvent(e) => write!(f, "cannot register exit event: {}", e),
38            VringEpollError::HandleEventReadKick(e) => {
39                write!(f, "cannot read vring kick event: {}", e)
40            }
41            VringEpollError::HandleEventBackendHandling(e) => {
42                write!(f, "failed to handle epoll event: {}", e)
43            }
44        }
45    }
46}
47
48impl std::error::Error for VringEpollError {}
49
50/// Result of vring epoll operations.
51pub type VringEpollResult<T> = std::result::Result<T, VringEpollError>;
52
53/// Epoll event handler to manage and process epoll events for registered file descriptor.
54///
55/// The `VringEpollHandler` structure provides interfaces to:
56/// - add file descriptors to be monitored by the epoll fd
57/// - remove registered file descriptors from the epoll fd
58/// - run the event loop to handle pending events on the epoll fd
59pub struct VringEpollHandler<T: VhostUserBackend> {
60    epoll: Epoll,
61    backend: T,
62    vrings: Vec<T::Vring>,
63    thread_id: usize,
64    exit_event_fd: Option<EventFd>,
65    phantom: PhantomData<T::Bitmap>,
66}
67
68impl<T: VhostUserBackend> VringEpollHandler<T> {
69    /// Send `exit event` to break the event loop.
70    pub fn send_exit_event(&self) {
71        if let Some(eventfd) = self.exit_event_fd.as_ref() {
72            let _ = eventfd.write(1);
73        }
74    }
75}
76
77impl<T> VringEpollHandler<T>
78where
79    T: VhostUserBackend,
80{
81    /// Create a `VringEpollHandler` instance.
82    pub(crate) fn new(
83        backend: T,
84        vrings: Vec<T::Vring>,
85        thread_id: usize,
86    ) -> 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.
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.
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
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
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.
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
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        Ok(false)
217    }
218}
219
220impl<T: VhostUserBackend> AsRawFd for VringEpollHandler<T> {
221    fn as_raw_fd(&self) -> RawFd {
222        self.epoll.as_raw_fd()
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use super::super::backend::tests::MockVhostBackend;
229    use super::super::vring::VringRwLock;
230    use super::*;
231    use std::sync::{Arc, Mutex};
232    use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
233    use vmm_sys_util::eventfd::EventFd;
234
235    #[test]
236    fn test_vring_epoll_handler() {
237        let mem = GuestMemoryAtomic::new(
238            GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x100000), 0x10000)]).unwrap(),
239        );
240        let vring = VringRwLock::new(mem, 0x1000).unwrap();
241        let backend = Arc::new(Mutex::new(MockVhostBackend::new()));
242
243        let handler = VringEpollHandler::new(backend, vec![vring], 0x1).unwrap();
244
245        let eventfd = EventFd::new(0).unwrap();
246        handler
247            .register_listener(eventfd.as_raw_fd(), EventSet::IN, 3)
248            .unwrap();
249        // Register an already registered fd.
250        handler
251            .register_listener(eventfd.as_raw_fd(), EventSet::IN, 3)
252            .unwrap_err();
253        // Register an invalid data.
254        handler
255            .register_listener(eventfd.as_raw_fd(), EventSet::IN, 1)
256            .unwrap_err();
257
258        handler
259            .unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 3)
260            .unwrap();
261        // unregister an already unregistered fd.
262        handler
263            .unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 3)
264            .unwrap_err();
265        // unregister an invalid data.
266        handler
267            .unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 1)
268            .unwrap_err();
269        // Check we retrieve the correct file descriptor
270        assert_eq!(handler.as_raw_fd(), handler.epoll.as_raw_fd());
271    }
272}