Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/vhost_user_frontend/worker.rs
5394 views
1
// Copyright 2021 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use anyhow::bail;
6
use anyhow::Context;
7
use base::info;
8
use base::warn;
9
#[cfg(windows)]
10
use base::CloseNotifier;
11
use base::Event;
12
use base::EventToken;
13
use base::EventType;
14
use base::ReadNotifier;
15
use base::SafeDescriptor;
16
use base::WaitContext;
17
use vmm_vhost::Error as VhostError;
18
19
use crate::virtio::vhost_user_frontend::handler::BackendReqHandler;
20
use crate::virtio::Interrupt;
21
use crate::virtio::VIRTIO_MSI_NO_VECTOR;
22
23
pub 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
32
impl 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
// monitor whether backend_client_fd is broken
42
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
// The vhost-user protocol allows the backend to signal events, but for
92
// non-MSI-X devices, a device must also update the interrupt status mask.
93
// `non_msix_evt` proxies events from the vhost-user backend to update the
94
// status mask.
95
let _ = self.non_msix_evt.wait();
96
97
// The parameter vector of signal_used_queue is used only when msix is
98
// enabled.
99
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
// Stop monitoring `backend_req_handler` as the client closed
111
// the connection.
112
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
// For linux domain socket, the close notifier fd is same with read/write
133
// notifier We need check whether the event is caused by socket broken.
134
#[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
}
148
149