From 759a0be6cd0e732c42b37ee44c10847083ad2698 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 11 Feb 2020 11:24:44 +0000 Subject: [PATCH] vhost_user_backend: Add support for handling exiting of worker thread All backends currently provide their own implementation for triggering the worker thread to exit via an EventFd. Modify the VhostUserBackend trait to allow a common implementation strategy that backends can use to provide an EventFd (and optional id) that can be used to trigger the worker to exit. Signed-off-by: Rob Bradford --- vhost_user_backend/src/lib.rs | 39 +++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/vhost_user_backend/src/lib.rs b/vhost_user_backend/src/lib.rs index c0db6a629..06ddcf7e3 100644 --- a/vhost_user_backend/src/lib.rs +++ b/vhost_user_backend/src/lib.rs @@ -95,6 +95,14 @@ pub trait VhostUserBackend: Send + Sync + 'static { fn set_config(&mut self, _offset: u32, _buf: &[u8]) -> result::Result<(), io::Error> { Ok(()) } + + /// Provide an exit EventFd + /// When this EventFd is written to the worker thread will exit. An optional id may + /// also be provided, if it not provided then the exit event will be first event id + /// after the last queue + fn exit_event(&self) -> Option<(EventFd, Option)> { + None + } } /// This structure is the public API the backend is allowed to interact with @@ -231,6 +239,7 @@ type VringEpollHandlerResult = std::result::Result struct VringEpollHandler { backend: Arc>, vrings: Vec>>, + exit_event_id: Option, } impl VringEpollHandler { @@ -239,6 +248,10 @@ impl VringEpollHandler { device_event: u16, evset: epoll::Events, ) -> VringEpollHandlerResult { + if self.exit_event_id == Some(device_event) { + return Ok(true); + } + let num_queues = self.vrings.len(); if (device_event as usize) < num_queues { if let Some(kick) = &self.vrings[device_event as usize].read().unwrap().kick { @@ -371,6 +384,8 @@ pub enum VhostUserHandlerError { SpawnVringWorker(io::Error), /// Could not find the mapping from memory regions. MissingMemoryMapping, + /// Could not register exit event + RegisterExitEvent(io::Error), } impl std::fmt::Display for VhostUserHandlerError { @@ -381,6 +396,9 @@ impl std::fmt::Display for VhostUserHandlerError { write!(f, "failed spawning the vring worker: {}", e) } VhostUserHandlerError::MissingMemoryMapping => write!(f, "Missing memory mapping"), + VhostUserHandlerError::RegisterExitEvent(e) => { + write!(f, "Failed to register exit event: {}", e) + } } } } @@ -418,12 +436,29 @@ impl VhostUserHandler { // Create the epoll file descriptor let epoll_fd = epoll::create(true).map_err(VhostUserHandlerError::EpollCreateFd)?; + let vring_worker = Arc::new(VringWorker { epoll_fd }); + let worker = vring_worker.clone(); + + let exit_event_id = + if let Some((exit_event_fd, exit_event_id)) = backend.read().unwrap().exit_event() { + let exit_event_id = exit_event_id.unwrap_or(num_queues as u16); + worker + .register_listener( + exit_event_fd.as_raw_fd(), + epoll::Events::EPOLLIN, + u64::from(exit_event_id), + ) + .map_err(VhostUserHandlerError::RegisterExitEvent)?; + Some(exit_event_id) + } else { + None + }; + let vring_handler = VringEpollHandler { backend: backend.clone(), vrings: vrings.clone(), + exit_event_id, }; - let vring_worker = Arc::new(VringWorker { epoll_fd }); - let worker = vring_worker.clone(); let worker_thread = Some( thread::Builder::new()