Skip to content

Commit 77ba627

Browse files
committed
Use mio to replace Epoll
Epoll is linux-specific. So we use mio, which is a cross-platform event notification, to replace Epoll. Signed-off-by: Wenyu Huang <[email protected]>
1 parent 220b30b commit 77ba627

File tree

7 files changed

+133
-89
lines changed

7 files changed

+133
-89
lines changed

vhost-user-backend/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Added
66
### Changed
77
- [[#308](https://github.com/rust-vmm/vhost/pull/308)] Replace Eventfd with EventNotifier/EventConsumer.
8+
- [[316]](https://github.com/rust-vmm/vhost/pull/316) Use mio to replace Epoll. Expose event_loop::EventSet.
89

910
### Deprecated
1011
### Fixed

vhost-user-backend/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ postcopy = ["vhost/postcopy", "userfaultfd"]
1616
libc = "0.2.39"
1717
log = "0.4.17"
1818
userfaultfd = { version = "0.9.0", optional = true }
19+
mio = { version = "1.0.4", features = ["os-poll", "os-ext"] }
1920
vhost = { path = "../vhost", version = "0.14.0", features = ["vhost-user-backend"] }
2021
virtio-bindings = { workspace = true }
2122
virtio-queue = { workspace = true }

vhost-user-backend/src/backend.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ use vhost::vhost_user::message::{
2929
};
3030
use vhost::vhost_user::Backend;
3131
use vm_memory::bitmap::Bitmap;
32-
use vmm_sys_util::epoll::EventSet;
3332
use vmm_sys_util::event::{EventConsumer, EventNotifier};
3433

3534
use vhost::vhost_user::GpuBackend;
3635

3736
use super::vring::VringT;
3837
use super::GM;
3938

39+
use crate::EventSet;
40+
4041
/// Trait with interior mutability for vhost user backend servers to implement concrete services.
4142
///
4243
/// To support multi-threading and asynchronous IO, we enforce `Send + Sync` bound.
@@ -798,7 +799,7 @@ pub mod tests {
798799

799800
let vring = VringRwLock::new(mem, 0x1000).unwrap();
800801
backend
801-
.handle_event(0x1, EventSet::IN, &[vring], 0)
802+
.handle_event(0x1, EventSet::Readable, &[vring], 0)
802803
.unwrap();
803804

804805
backend.reset_device();

vhost-user-backend/src/event_loop.rs

Lines changed: 121 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,90 @@
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

6+
use std::collections::HashSet;
67
use std::fmt::{Display, Formatter};
78
use std::io::{self, Result};
89
use std::marker::PhantomData;
910
use std::os::fd::IntoRawFd;
1011
use std::os::unix::io::{AsRawFd, RawFd};
12+
use std::sync::Mutex;
1113

12-
use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet};
14+
use mio::event::Event;
15+
use mio::unix::SourceFd;
16+
use mio::{Events, Interest, Poll, Registry, Token};
1317
use vmm_sys_util::event::EventNotifier;
1418

1519
use super::backend::VhostUserBackend;
1620
use super::vring::VringT;
1721

1822
/// Errors related to vring epoll event handling.
1923
#[derive(Debug)]
20-
pub enum VringEpollError {
24+
pub enum VringPollError {
2125
/// Failed to create epoll file descriptor.
22-
EpollCreateFd(io::Error),
26+
PollerCreate(io::Error),
2327
/// Failed while waiting for events.
24-
EpollWait(io::Error),
28+
PollerWait(io::Error),
2529
/// Could not register exit event
2630
RegisterExitEvent(io::Error),
2731
/// Failed to read the event from kick EventFd.
2832
HandleEventReadKick(io::Error),
2933
/// Failed to handle the event from the backend.
3034
HandleEventBackendHandling(io::Error),
35+
/// Failed to clone registry.
36+
RegistryClone(io::Error),
3137
}
3238

33-
impl Display for VringEpollError {
39+
impl Display for VringPollError {
3440
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
3541
match self {
36-
VringEpollError::EpollCreateFd(e) => write!(f, "cannot create epoll fd: {e}"),
37-
VringEpollError::EpollWait(e) => write!(f, "failed to wait for epoll event: {e}"),
38-
VringEpollError::RegisterExitEvent(e) => write!(f, "cannot register exit event: {e}"),
39-
VringEpollError::HandleEventReadKick(e) => {
42+
VringPollError::PollerCreate(e) => write!(f, "cannot create poller: {e}"),
43+
VringPollError::PollerWait(e) => write!(f, "failed to wait for poller event: {e}"),
44+
VringPollError::RegisterExitEvent(e) => write!(f, "cannot register exit event: {e}"),
45+
VringPollError::HandleEventReadKick(e) => {
4046
write!(f, "cannot read vring kick event: {e}")
4147
}
42-
VringEpollError::HandleEventBackendHandling(e) => {
43-
write!(f, "failed to handle epoll event: {e}")
48+
VringPollError::HandleEventBackendHandling(e) => {
49+
write!(f, "failed to handle poll event: {e}")
4450
}
51+
VringPollError::RegistryClone(e) => write!(f, "cannot clone poller's registry: {e}"),
4552
}
4653
}
4754
}
4855

49-
impl std::error::Error for VringEpollError {}
56+
impl std::error::Error for VringPollError {}
5057

5158
/// Result of vring epoll operations.
52-
pub type VringEpollResult<T> = std::result::Result<T, VringEpollError>;
59+
pub type VringEpollResult<T> = std::result::Result<T, VringPollError>;
60+
61+
#[derive(Debug, Clone, Copy)]
62+
pub enum EventSet {
63+
Readable,
64+
Writable,
65+
All,
66+
}
67+
68+
impl EventSet {
69+
fn to_interest(self) -> Interest {
70+
match self {
71+
EventSet::Readable => Interest::READABLE,
72+
EventSet::Writable => Interest::WRITABLE,
73+
EventSet::All => Interest::READABLE | Interest::WRITABLE,
74+
}
75+
}
76+
}
77+
78+
fn event_to_event_set(evt: &Event) -> Option<EventSet> {
79+
if evt.is_readable() && evt.is_writable() {
80+
return Some(EventSet::All);
81+
}
82+
if evt.is_readable() {
83+
return Some(EventSet::Readable);
84+
}
85+
if evt.is_writable() {
86+
return Some(EventSet::Writable);
87+
}
88+
None
89+
}
5390

5491
/// Epoll event handler to manage and process epoll events for registered file descriptor.
5592
///
@@ -58,7 +95,11 @@ pub type VringEpollResult<T> = std::result::Result<T, VringEpollError>;
5895
/// - remove registered file descriptors from the epoll fd
5996
/// - run the event loop to handle pending events on the epoll fd
6097
pub struct VringEpollHandler<T: VhostUserBackend> {
61-
epoll: Epoll,
98+
poller: Mutex<Poll>,
99+
registry: Registry,
100+
// Record the registered fd.
101+
// Because in mio, consecutive calls to register is unspecified behavior.
102+
fd_set: Mutex<HashSet<RawFd>>,
62103
backend: T,
63104
vrings: Vec<T::Vring>,
64105
thread_id: usize,
@@ -85,25 +126,35 @@ where
85126
vrings: Vec<T::Vring>,
86127
thread_id: usize,
87128
) -> VringEpollResult<Self> {
88-
let epoll = Epoll::new().map_err(VringEpollError::EpollCreateFd)?;
129+
let poller = Poll::new().map_err(VringPollError::PollerCreate)?;
89130
let exit_event_fd = backend.exit_event(thread_id);
131+
let fd_set = Mutex::new(HashSet::new());
90132

133+
let registry = poller
134+
.registry()
135+
.try_clone()
136+
.map_err(VringPollError::RegistryClone)?;
91137
let exit_event_fd = if let Some((consumer, notifier)) = exit_event_fd {
92138
let id = backend.num_queues();
93-
epoll
94-
.ctl(
95-
ControlOperation::Add,
96-
consumer.into_raw_fd(),
97-
EpollEvent::new(EventSet::IN, id as u64),
139+
140+
registry
141+
.register(
142+
&mut SourceFd(&consumer.as_raw_fd()),
143+
Token(id),
144+
Interest::READABLE,
98145
)
99-
.map_err(VringEpollError::RegisterExitEvent)?;
146+
.map_err(VringPollError::RegisterExitEvent)?;
147+
148+
fd_set.lock().unwrap().insert(consumer.into_raw_fd());
100149
Some(notifier)
101150
} else {
102151
None
103152
};
104153

105154
Ok(VringEpollHandler {
106-
epoll,
155+
poller: Mutex::new(poller),
156+
registry,
157+
fd_set,
107158
backend,
108159
vrings,
109160
thread_id,
@@ -129,29 +180,37 @@ where
129180
///
130181
/// If the event is triggered after this function has been called, the event will be silently
131182
/// dropped.
132-
pub fn unregister_listener(&self, fd: RawFd, ev_type: EventSet, data: usize) -> Result<()> {
183+
pub fn unregister_listener(&self, fd: RawFd, data: usize) -> Result<()> {
133184
// `data` range [0...num_queues] is reserved for queues and exit event.
134185
if data <= self.backend.num_queues() {
135186
Err(io::Error::from_raw_os_error(libc::EINVAL))
136187
} else {
137-
self.unregister_event(fd, ev_type, data)
188+
self.unregister_event(fd)
138189
}
139190
}
140191

141192
pub(crate) fn register_event(&self, fd: RawFd, ev_type: EventSet, data: usize) -> Result<()> {
142-
self.epoll.ctl(
143-
ControlOperation::Add,
144-
fd,
145-
EpollEvent::new(ev_type, data as u64),
146-
)
193+
let mut fd_set = self.fd_set.lock().unwrap();
194+
if fd_set.contains(&fd) {
195+
return Err(io::Error::from_raw_os_error(libc::EEXIST));
196+
}
197+
self.registry
198+
.register(&mut SourceFd(&fd), Token(data), ev_type.to_interest())
199+
.map_err(std::io::Error::other)?;
200+
fd_set.insert(fd);
201+
Ok(())
147202
}
148203

149-
pub(crate) fn unregister_event(&self, fd: RawFd, ev_type: EventSet, data: usize) -> Result<()> {
150-
self.epoll.ctl(
151-
ControlOperation::Delete,
152-
fd,
153-
EpollEvent::new(ev_type, data as u64),
154-
)
204+
pub(crate) fn unregister_event(&self, fd: RawFd) -> Result<()> {
205+
let mut fd_set = self.fd_set.lock().unwrap();
206+
if !fd_set.contains(&fd) {
207+
return Err(io::Error::from_raw_os_error(libc::ENOENT));
208+
}
209+
self.registry
210+
.deregister(&mut SourceFd(&fd))
211+
.map_err(|e| std::io::Error::other(format!("Failed to deregister fd {fd}: {e}")))?;
212+
fd_set.remove(&fd);
213+
Ok(())
155214
}
156215

157216
/// Run the event poll loop to handle all pending events on registered fds.
@@ -160,41 +219,22 @@ where
160219
/// associated with the backend.
161220
pub(crate) fn run(&self) -> VringEpollResult<()> {
162221
const EPOLL_EVENTS_LEN: usize = 100;
163-
let mut events = vec![EpollEvent::new(EventSet::empty(), 0); EPOLL_EVENTS_LEN];
164-
165-
'epoll: loop {
166-
let num_events = match self.epoll.wait(-1, &mut events[..]) {
167-
Ok(res) => res,
168-
Err(e) => {
169-
if e.kind() == io::ErrorKind::Interrupted {
170-
// It's well defined from the epoll_wait() syscall
171-
// documentation that the epoll loop can be interrupted
172-
// before any of the requested events occurred or the
173-
// timeout expired. In both those cases, epoll_wait()
174-
// returns an error of type EINTR, but this should not
175-
// be considered as a regular error. Instead it is more
176-
// appropriate to retry, by calling into epoll_wait().
177-
continue;
178-
}
179-
return Err(VringEpollError::EpollWait(e));
180-
}
181-
};
182-
183-
for event in events.iter().take(num_events) {
184-
let evset = match EventSet::from_bits(event.events) {
185-
Some(evset) => evset,
186-
None => {
187-
let evbits = event.events;
188-
println!("epoll: ignoring unknown event set: 0x{evbits:x}");
189-
continue;
190-
}
191-
};
192222

193-
let ev_type = event.data();
223+
let mut events = Events::with_capacity(EPOLL_EVENTS_LEN);
224+
'poll: loop {
225+
self.poller
226+
.lock()
227+
.unwrap()
228+
.poll(&mut events, None)
229+
.map_err(VringPollError::PollerWait)?;
194230

195-
// handle_event() returns true if an event is received from the exit event fd.
196-
if self.handle_event(ev_type as usize, evset)? {
197-
break 'epoll;
231+
for event in &events {
232+
let token = event.token();
233+
234+
if let Some(evt_set) = event_to_event_set(event) {
235+
if self.handle_event(token.0, evt_set)? {
236+
break 'poll;
237+
}
198238
}
199239
}
200240
}
@@ -211,7 +251,7 @@ where
211251
let vring = &self.vrings[device_event];
212252
let enabled = vring
213253
.read_kick()
214-
.map_err(VringEpollError::HandleEventReadKick)?;
254+
.map_err(VringPollError::HandleEventReadKick)?;
215255

216256
// If the vring is not enabled, it should not be processed.
217257
if !enabled {
@@ -221,15 +261,15 @@ where
221261

222262
self.backend
223263
.handle_event(device_event, evset, &self.vrings, self.thread_id)
224-
.map_err(VringEpollError::HandleEventBackendHandling)?;
264+
.map_err(VringPollError::HandleEventBackendHandling)?;
225265

226266
Ok(false)
227267
}
228268
}
229269

230270
impl<T: VhostUserBackend> AsRawFd for VringEpollHandler<T> {
231271
fn as_raw_fd(&self) -> RawFd {
232-
self.epoll.as_raw_fd()
272+
self.poller.lock().unwrap().as_raw_fd()
233273
}
234274
}
235275

@@ -254,29 +294,32 @@ mod tests {
254294

255295
let (consumer, _notifier) = new_event_consumer_and_notifier(EventFlag::empty()).unwrap();
256296
handler
257-
.register_listener(consumer.as_raw_fd(), EventSet::IN, 3)
297+
.register_listener(consumer.as_raw_fd(), EventSet::Readable, 3)
258298
.unwrap();
259299
// Register an already registered fd.
260300
handler
261-
.register_listener(consumer.as_raw_fd(), EventSet::IN, 3)
301+
.register_listener(consumer.as_raw_fd(), EventSet::Readable, 3)
262302
.unwrap_err();
263303
// Register an invalid data.
264304
handler
265-
.register_listener(consumer.as_raw_fd(), EventSet::IN, 1)
305+
.register_listener(consumer.as_raw_fd(), EventSet::Readable, 1)
266306
.unwrap_err();
267307

268308
handler
269-
.unregister_listener(consumer.as_raw_fd(), EventSet::IN, 3)
309+
.unregister_listener(consumer.as_raw_fd(), 3)
270310
.unwrap();
271311
// unregister an already unregistered fd.
272312
handler
273-
.unregister_listener(consumer.as_raw_fd(), EventSet::IN, 3)
313+
.unregister_listener(consumer.as_raw_fd(), 3)
274314
.unwrap_err();
275315
// unregister an invalid data.
276316
handler
277-
.unregister_listener(consumer.as_raw_fd(), EventSet::IN, 1)
317+
.unregister_listener(consumer.as_raw_fd(), 1)
278318
.unwrap_err();
279319
// Check we retrieve the correct file descriptor
280-
assert_eq!(handler.as_raw_fd(), handler.epoll.as_raw_fd());
320+
assert_eq!(
321+
handler.as_raw_fd(),
322+
handler.poller.lock().unwrap().as_raw_fd()
323+
);
281324
}
282325
}

0 commit comments

Comments
 (0)