vhost_user_backend: Give access to the EpollVringHandler

By letting the consumer of this crate getting access to the vring
handler, we will be able to let it perform several actions without
producing a deadlock.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2019-09-23 12:23:28 -07:00
parent cfc8c39446
commit f14ab872ec

View File

@ -105,7 +105,6 @@ pub struct VhostUserDaemon<S: VhostUserBackend> {
name: String, name: String,
sock_path: String, sock_path: String,
handler: Arc<Mutex<VhostUserHandler<S>>>, handler: Arc<Mutex<VhostUserHandler<S>>>,
vring_handler: Arc<RwLock<VringEpollHandler<S>>>,
main_thread: Option<thread::JoinHandle<Result<()>>>, main_thread: Option<thread::JoinHandle<Result<()>>>,
} }
@ -120,13 +119,11 @@ impl<S: VhostUserBackend> VhostUserDaemon<S> {
let handler = Arc::new(Mutex::new( let handler = Arc::new(Mutex::new(
VhostUserHandler::new(backend).map_err(Error::NewVhostUserHandler)?, VhostUserHandler::new(backend).map_err(Error::NewVhostUserHandler)?,
)); ));
let vring_handler = handler.lock().unwrap().get_vring_handler();
Ok(VhostUserDaemon { Ok(VhostUserDaemon {
name, name,
sock_path, sock_path,
handler, handler,
vring_handler,
main_thread: None, main_thread: None,
}) })
} }
@ -166,47 +163,11 @@ impl<S: VhostUserBackend> VhostUserDaemon<S> {
Ok(()) Ok(())
} }
/// Register a custom event only meaningful to the caller. When this event /// Retrieve the vring handler. This is necessary to perform further
/// is later triggered, and because only the caller knows what to do about /// actions like registering and unregistering some extra event file
/// it, the backend implementation of `handle_event` will be called. /// descriptors, as well as forcing some vring to be processed.
/// This lets entire control to the caller about what needs to be done for pub fn get_vring_handler(&self) -> Arc<RwLock<VringEpollHandler<S>>> {
/// this special event, without forcing it to run its own dedicated epoll self.handler.lock().unwrap().get_vring_handler()
/// loop for it.
pub fn register_listener(&self, fd: RawFd, ev_type: epoll::Events, data: u64) -> Result<()> {
self.vring_handler
.read()
.unwrap()
.register_listener(fd, ev_type, data)
.map_err(Error::RegisterListener)
}
/// Unregister a custom event. If the custom event is triggered after this
/// function has been called, nothing will happen as it will be removed
/// from the list of file descriptors the epoll loop is listening to.
pub fn unregister_listener(&self, fd: RawFd, ev_type: epoll::Events, data: u64) -> Result<()> {
self.vring_handler
.read()
.unwrap()
.unregister_listener(fd, ev_type, data)
.map_err(Error::RegisterListener)
}
/// Trigger the processing of a virtqueue. This function is meant to be
/// used by the caller whenever it might need some available queues to
/// send data back to the guest.
/// A concrete example is a backend registering one extra listener for
/// data that needs to be sent to the guest. When the associated event
/// is triggered, the backend will be invoked through its `handle_event`
/// implementation. And in this case, the way to handle the event is to
/// call into `process_queue` to let it invoke the backend implementation
/// of `process_queue`. With this twisted trick, all common parts related
/// to the virtqueues can remain part of the library.
pub fn process_queue(&self, q_idx: u16) -> Result<()> {
self.vring_handler
.write()
.unwrap()
.process_queue(q_idx)
.map_err(Error::ProcessQueue)
} }
} }
@ -286,7 +247,7 @@ impl error::Error for VringEpollHandlerError {}
/// Result of vring epoll handler operations. /// Result of vring epoll handler operations.
type VringEpollHandlerResult<T> = std::result::Result<T, VringEpollHandlerError>; type VringEpollHandlerResult<T> = std::result::Result<T, VringEpollHandlerError>;
struct VringEpollHandler<S: VhostUserBackend> { pub struct VringEpollHandler<S: VhostUserBackend> {
backend: Arc<RwLock<S>>, backend: Arc<RwLock<S>>,
vrings: Vec<Arc<RwLock<Vring>>>, vrings: Vec<Arc<RwLock<Vring>>>,
mem: Option<GuestMemoryMmap>, mem: Option<GuestMemoryMmap>,
@ -298,7 +259,17 @@ impl<S: VhostUserBackend> VringEpollHandler<S> {
self.mem = mem; self.mem = mem;
} }
fn process_queue(&mut self, q_idx: u16) -> VringEpollHandlerResult<()> { /// Trigger the processing of a virtqueue. This function is meant to be
/// used by the caller whenever it might need some available queues to
/// send data back to the guest.
/// A concrete example is a backend registering one extra listener for
/// data that needs to be sent to the guest. When the associated event
/// is triggered, the backend will be invoked through its `handle_event`
/// implementation. And in this case, the way to handle the event is to
/// call into `process_queue` to let it invoke the backend implementation
/// of `process_queue`. With this twisted trick, all common parts related
/// to the virtqueues can remain part of the library.
pub fn process_queue(&mut self, q_idx: u16) -> VringEpollHandlerResult<()> {
let vring = &mut self.vrings[q_idx as usize].write().unwrap(); let vring = &mut self.vrings[q_idx as usize].write().unwrap();
let mut used_desc_heads = vec![(0, 0); vring.queue.size as usize]; let mut used_desc_heads = vec![(0, 0); vring.queue.size as usize];
let mut used_count = 0; let mut used_count = 0;
@ -379,7 +350,13 @@ impl<S: VhostUserBackend> VringEpollHandler<S> {
} }
} }
fn register_listener( /// Register a custom event only meaningful to the caller. When this event
/// is later triggered, and because only the caller knows what to do about
/// it, the backend implementation of `handle_event` will be called.
/// This lets entire control to the caller about what needs to be done for
/// this special event, without forcing it to run its own dedicated epoll
/// loop for it.
pub fn register_listener(
&self, &self,
fd: RawFd, fd: RawFd,
ev_type: epoll::Events, ev_type: epoll::Events,
@ -393,7 +370,10 @@ impl<S: VhostUserBackend> VringEpollHandler<S> {
) )
} }
fn unregister_listener( /// Unregister a custom event. If the custom event is triggered after this
/// function has been called, nothing will happen as it will be removed
/// from the list of file descriptors the epoll loop is listening to.
pub fn unregister_listener(
&self, &self,
fd: RawFd, fd: RawFd,
ev_type: epoll::Events, ev_type: epoll::Events,