devices/virtio/vhost_user_frontend/
worker.rs1use anyhow::bail;
6use anyhow::Context;
7use base::info;
8use base::warn;
9#[cfg(windows)]
10use base::CloseNotifier;
11use base::Event;
12use base::EventToken;
13use base::EventType;
14use base::ReadNotifier;
15use base::SafeDescriptor;
16use base::WaitContext;
17use vmm_vhost::Error as VhostError;
18
19use crate::virtio::vhost_user_frontend::handler::BackendReqHandler;
20use crate::virtio::Interrupt;
21use crate::virtio::VIRTIO_MSI_NO_VECTOR;
22
23pub struct Worker {
24 pub kill_evt: Event,
25 pub non_msix_evt: Event,
26 pub backend_req_handler: Option<BackendReqHandler>,
27 pub backend_client_read_notifier: SafeDescriptor,
28 #[cfg(target_os = "windows")]
29 pub backend_client_close_notifier: SafeDescriptor,
30}
31
32impl Worker {
33 pub fn run(&mut self, interrupt: Interrupt) -> anyhow::Result<()> {
34 #[derive(EventToken)]
35 enum Token {
36 Kill,
37 NonMsixEvt,
38 ReqHandlerRead,
39 #[cfg(target_os = "windows")]
40 ReqHandlerClose,
41 BackendCloseNotify,
43 }
44 let wait_ctx = WaitContext::build_with(&[
45 (&self.non_msix_evt, Token::NonMsixEvt),
46 (&self.kill_evt, Token::Kill),
47 ])
48 .context("failed to build WaitContext")?;
49
50 if let Some(backend_req_handler) = self.backend_req_handler.as_mut() {
51 wait_ctx
52 .add(
53 backend_req_handler.get_read_notifier(),
54 Token::ReqHandlerRead,
55 )
56 .context("failed to add backend req handler to WaitContext")?;
57
58 #[cfg(target_os = "windows")]
59 wait_ctx
60 .add(
61 backend_req_handler.get_close_notifier(),
62 Token::ReqHandlerClose,
63 )
64 .context("failed to add backend req handler close notifier to WaitContext")?;
65 }
66
67 #[cfg(any(target_os = "android", target_os = "linux"))]
68 wait_ctx
69 .add_for_event(
70 &self.backend_client_read_notifier,
71 EventType::None,
72 Token::BackendCloseNotify,
73 )
74 .context("failed to add backend client close notifier to WaitContext")?;
75 #[cfg(target_os = "windows")]
76 wait_ctx
77 .add(
78 &self.backend_client_close_notifier,
79 Token::BackendCloseNotify,
80 )
81 .context("failed to add backend client close notifier to WaitContext")?;
82
83 'wait: loop {
84 let events = wait_ctx.wait().context("WaitContext::wait() failed")?;
85 for event in events {
86 match event.token {
87 Token::Kill => {
88 break 'wait;
89 }
90 Token::NonMsixEvt => {
91 let _ = self.non_msix_evt.wait();
96
97 interrupt.signal_used_queue(VIRTIO_MSI_NO_VECTOR);
100 }
101 Token::ReqHandlerRead => {
102 let Some(backend_req_handler) = self.backend_req_handler.as_mut() else {
103 continue;
104 };
105
106 match backend_req_handler.handle_request() {
107 Ok(_) => (),
108 Err(VhostError::ClientExit) | Err(VhostError::Disconnect) => {
109 info!("backend req handler connection closed");
110 let _ = wait_ctx.delete(backend_req_handler.get_read_notifier());
113 #[cfg(target_os = "windows")]
114 let _ = wait_ctx.delete(backend_req_handler.get_close_notifier());
115 self.backend_req_handler = None;
116 }
117 Err(e) => return Err(e).context("failed to handle vhost-user request"),
118 }
119 }
120 #[cfg(target_os = "windows")]
121 Token::ReqHandlerClose => {
122 let Some(backend_req_handler) = self.backend_req_handler.as_mut() else {
123 continue;
124 };
125
126 info!("backend req handler connection closed");
127 let _ = wait_ctx.delete(backend_req_handler.get_read_notifier());
128 let _ = wait_ctx.delete(backend_req_handler.get_close_notifier());
129 self.backend_req_handler = None;
130 }
131 Token::BackendCloseNotify => {
132 #[cfg(any(target_os = "android", target_os = "linux"))]
135 if !event.is_hungup {
136 warn!("event besides hungup should not be notified");
137 continue;
138 }
139 bail!("Backend device disconnected early");
140 }
141 }
142 }
143 }
144
145 Ok(())
146 }
147}