Path: blob/main/devices/src/virtio/vhost_user_frontend/worker.rs
5394 views
// Copyright 2021 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use anyhow::bail;5use anyhow::Context;6use base::info;7use base::warn;8#[cfg(windows)]9use base::CloseNotifier;10use base::Event;11use base::EventToken;12use base::EventType;13use base::ReadNotifier;14use base::SafeDescriptor;15use base::WaitContext;16use vmm_vhost::Error as VhostError;1718use crate::virtio::vhost_user_frontend::handler::BackendReqHandler;19use crate::virtio::Interrupt;20use crate::virtio::VIRTIO_MSI_NO_VECTOR;2122pub struct Worker {23pub kill_evt: Event,24pub non_msix_evt: Event,25pub backend_req_handler: Option<BackendReqHandler>,26pub backend_client_read_notifier: SafeDescriptor,27#[cfg(target_os = "windows")]28pub backend_client_close_notifier: SafeDescriptor,29}3031impl Worker {32pub fn run(&mut self, interrupt: Interrupt) -> anyhow::Result<()> {33#[derive(EventToken)]34enum Token {35Kill,36NonMsixEvt,37ReqHandlerRead,38#[cfg(target_os = "windows")]39ReqHandlerClose,40// monitor whether backend_client_fd is broken41BackendCloseNotify,42}43let wait_ctx = WaitContext::build_with(&[44(&self.non_msix_evt, Token::NonMsixEvt),45(&self.kill_evt, Token::Kill),46])47.context("failed to build WaitContext")?;4849if let Some(backend_req_handler) = self.backend_req_handler.as_mut() {50wait_ctx51.add(52backend_req_handler.get_read_notifier(),53Token::ReqHandlerRead,54)55.context("failed to add backend req handler to WaitContext")?;5657#[cfg(target_os = "windows")]58wait_ctx59.add(60backend_req_handler.get_close_notifier(),61Token::ReqHandlerClose,62)63.context("failed to add backend req handler close notifier to WaitContext")?;64}6566#[cfg(any(target_os = "android", target_os = "linux"))]67wait_ctx68.add_for_event(69&self.backend_client_read_notifier,70EventType::None,71Token::BackendCloseNotify,72)73.context("failed to add backend client close notifier to WaitContext")?;74#[cfg(target_os = "windows")]75wait_ctx76.add(77&self.backend_client_close_notifier,78Token::BackendCloseNotify,79)80.context("failed to add backend client close notifier to WaitContext")?;8182'wait: loop {83let events = wait_ctx.wait().context("WaitContext::wait() failed")?;84for event in events {85match event.token {86Token::Kill => {87break 'wait;88}89Token::NonMsixEvt => {90// The vhost-user protocol allows the backend to signal events, but for91// non-MSI-X devices, a device must also update the interrupt status mask.92// `non_msix_evt` proxies events from the vhost-user backend to update the93// status mask.94let _ = self.non_msix_evt.wait();9596// The parameter vector of signal_used_queue is used only when msix is97// enabled.98interrupt.signal_used_queue(VIRTIO_MSI_NO_VECTOR);99}100Token::ReqHandlerRead => {101let Some(backend_req_handler) = self.backend_req_handler.as_mut() else {102continue;103};104105match backend_req_handler.handle_request() {106Ok(()) => (),107Err(VhostError::ClientExit) | Err(VhostError::Disconnect) => {108info!("backend req handler connection closed");109// Stop monitoring `backend_req_handler` as the client closed110// the connection.111let _ = wait_ctx.delete(backend_req_handler.get_read_notifier());112#[cfg(target_os = "windows")]113let _ = wait_ctx.delete(backend_req_handler.get_close_notifier());114self.backend_req_handler = None;115}116Err(e) => return Err(e).context("failed to handle vhost-user request"),117}118}119#[cfg(target_os = "windows")]120Token::ReqHandlerClose => {121let Some(backend_req_handler) = self.backend_req_handler.as_mut() else {122continue;123};124125info!("backend req handler connection closed");126let _ = wait_ctx.delete(backend_req_handler.get_read_notifier());127let _ = wait_ctx.delete(backend_req_handler.get_close_notifier());128self.backend_req_handler = None;129}130Token::BackendCloseNotify => {131// For linux domain socket, the close notifier fd is same with read/write132// notifier We need check whether the event is caused by socket broken.133#[cfg(any(target_os = "android", target_os = "linux"))]134if !event.is_hungup {135warn!("event besides hungup should not be notified");136continue;137}138bail!("Backend device disconnected early");139}140}141}142}143144Ok(())145}146}147148149