Alioth Code Coverage

vhost_vsock.rs0.00%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
16use std::path::Path;
17use std::sync::Arc;
18use std::sync::atomic::Ordering;
19use std::sync::mpsc::Receiver;
20use std::thread::JoinHandle;
21
22use libc::{EFD_CLOEXEC, EFD_NONBLOCK, eventfd};
23use mio::event::Event;
24use mio::unix::SourceFd;
25use mio::{Interest, Registry, Token};
26use serde::Deserialize;
27use serde_aco::Help;
28
29use crate::ffi;
30use crate::hv::IoeventFd;
31use crate::mem::LayoutUpdated;
32use crate::mem::mapped::RamBus;
33use crate::sync::notifier::Notifier;
34use crate::sys::vhost::{VHOST_FILE_UNBIND, VirtqAddr, VirtqFile, VirtqState};
35use crate::virtio::dev::vsock::{VsockConfig, VsockFeature};
36use crate::virtio::dev::{DevParam, DeviceId, Virtio, WakeEvent};
37use crate::virtio::queue::{QueueReg, VirtQueue};
38use crate::virtio::vhost::{UpdateVsockMem, VhostDev, error};
39use crate::virtio::worker::mio::{ActiveMio, Mio, VirtioMio};
40use crate::virtio::{IrqSender, Result, VirtioFeature};
41
42#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Help)]
43pub struct VhostVsockParam {
44 /// Vsock context id.
45 pub cid: u32,
46 /// Path to the host device file. [default: /dev/vhost-vsock]
47 pub dev: Option<Box<Path>>,
48}
49
50impl DevParam for VhostVsockParam {
51 type Device = VhostVsock;
52
53 fn build(self, name: impl Into<Arc<str>>) -> Result<Self::Device> {
54 VhostVsock::new(self, name)
55 }
56}
57
58#[derive(Debug)]
59pub struct VhostVsock {
60 name: Arc<str>,
61 vhost_dev: Arc<VhostDev>,
62 config: VsockConfig,
63 features: u64,
64 error_fds: [Option<OwnedFd>; 2],
65}
66
67impl VhostVsock {
68 pub fn new(param: VhostVsockParam, name: impl Into<Arc<str>>) -> Result<VhostVsock> {
69 let name = name.into();
70 let vhost_dev = match param.dev {
71 Some(dev) => VhostDev::new(dev),
72 None => VhostDev::new("/dev/vhost-vsock"),
73 }?;
74 vhost_dev.set_owner()?;
75 vhost_dev.vsock_set_guest_cid(param.cid as _)?;
76 if let Ok(backend_feature) = vhost_dev.get_backend_features() {
77 log::debug!("{name}: vhost-vsock backend feature: {backend_feature:x?}");
78 vhost_dev.set_backend_features(&backend_feature)?;
79 }
80 let dev_feat = vhost_dev.get_features()? as u128;
81 let known_feat = VirtioFeature::from_bits_truncate(dev_feat).bits()
82 | VsockFeature::from_bits_truncate(dev_feat).bits();
83 if !VirtioFeature::from_bits_retain(known_feat).contains(VirtioFeature::VERSION_1) {
84 return error::VhostMissingDeviceFeature {
85 feature: VirtioFeature::VERSION_1.bits(),
86 }
87 .fail()?;
88 }
89 Ok(VhostVsock {
90 name,
91 vhost_dev: Arc::new(vhost_dev),
92 config: VsockConfig {
93 guest_cid: param.cid,
94 ..Default::default()
95 },
96 features: known_feat as u64,
97 error_fds: [None, None],
98 })
99 }
100}
101
102impl Virtio for VhostVsock {
103 type Config = VsockConfig;
104 type Feature = VsockFeature;
105
106 fn id(&self) -> DeviceId {
107 DeviceId::SOCKET
108 }
109
110 fn name(&self) -> &str {
111 &self.name
112 }
113
114 fn num_queues(&self) -> u16 {
115 3
116 }
117
118 fn config(&self) -> Arc<VsockConfig> {
119 Arc::new(self.config)
120 }
121
122 fn feature(&self) -> u128 {
123 self.features as u128
124 }
125
126 fn ioeventfd_offloaded(&self, q_index: u16) -> Result<bool> {
127 match q_index {
128 0 | 1 => Ok(true),
129 _ => Ok(false),
130 }
131 }
132
133 fn mem_update_callback(&self) -> Option<Box<dyn LayoutUpdated>> {
134 Some(Box::new(UpdateVsockMem {
135 dev: self.vhost_dev.clone(),
136 }))
137 }
138
139 fn spawn_worker<S, E>(
140 self,
141 event_rx: Receiver<WakeEvent<S, E>>,
142 memory: Arc<RamBus>,
143 queue_regs: Arc<[QueueReg]>,
144 ) -> Result<(JoinHandle<()>, Arc<Notifier>)>
145 where
146 S: IrqSender,
147 E: IoeventFd,
148 {
149 Mio::spawn_worker(self, event_rx, memory, queue_regs)
150 }
151}
152
153impl VirtioMio for VhostVsock {
154 fn activate<'m, Q, S, E>(
155 &mut self,
156 feature: u128,
157 active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,
158 ) -> Result<()>
159 where
160 Q: VirtQueue<'m>,
161 S: IrqSender,
162 E: IoeventFd,
163 {
164 self.vhost_dev.set_features(&(feature as u64))?;
165 for (index, fd) in active_mio.ioeventfds.iter().take(2).enumerate() {
166 let kick = VirtqFile {
167 index: index as u32,
168 fd: fd.as_fd().as_raw_fd(),
169 };
170 self.vhost_dev.set_virtq_kick(&kick)?;
171 }
172 for (index, queue) in active_mio.queues.iter().take(2).enumerate() {
173 let Some(queue) = queue else {
174 continue;
175 };
176 let reg = queue.reg();
177 let index = index as u32;
178 active_mio.irq_sender.queue_irqfd(index as _, |fd| {
179 self.vhost_dev.set_virtq_call(&VirtqFile {
180 index,
181 fd: fd.as_raw_fd(),
182 })?;
183 Ok(())
184 })?;
185
186 self.vhost_dev.set_virtq_num(&VirtqState {
187 index,
188 val: reg.size.load(Ordering::Acquire) as _,
189 })?;
190 self.vhost_dev
191 .set_virtq_base(&VirtqState { index, val: 0 })?;
192 let mem = active_mio.mem;
193 let virtq_addr = VirtqAddr {
194 index,
195 flags: 0,
196 desc_hva: mem.translate(reg.desc.load(Ordering::Acquire))? as _,
197 used_hva: mem.translate(reg.device.load(Ordering::Acquire))? as _,
198 avail_hva: mem.translate(reg.driver.load(Ordering::Acquire))? as _,
199 log_guest_addr: 0,
200 };
201 self.vhost_dev.set_virtq_addr(&virtq_addr)?;
202 }
203 for (index, fd) in self.error_fds.iter_mut().enumerate() {
204 let err_fd =
205 unsafe { OwnedFd::from_raw_fd(ffi!(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK))?) };
206 self.vhost_dev.set_virtq_err(&VirtqFile {
207 index: index as u32,
208 fd: err_fd.as_raw_fd(),
209 })?;
210 active_mio.poll.registry().register(
211 &mut SourceFd(&err_fd.as_raw_fd()),
212 Token(index as _),
213 Interest::READABLE,
214 )?;
215 *fd = Some(err_fd);
216 }
217 self.vhost_dev.vsock_set_running(true)?;
218 Ok(())
219 }
220
221 fn reset(&mut self, registry: &Registry) {
222 self.vhost_dev.vsock_set_running(false).unwrap();
223 for (index, error_fd) in self.error_fds.iter_mut().enumerate() {
224 let Some(err_fd) = error_fd else {
225 continue;
226 };
227 self.vhost_dev
228 .set_virtq_err(&VirtqFile {
229 index: index as _,
230 fd: VHOST_FILE_UNBIND,
231 })
232 .unwrap();
233 registry
234 .deregister(&mut SourceFd(&err_fd.as_raw_fd()))
235 .unwrap();
236 *error_fd = None;
237 }
238 }
239
240 fn handle_event<'a, 'm, Q, S, E>(
241 &mut self,
242 event: &Event,
243 _active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,
244 ) -> Result<()>
245 where
246 Q: VirtQueue<'m>,
247 S: IrqSender,
248 E: IoeventFd,
249 {
250 let q_index = event.token();
251 error::VhostQueueErr {
252 dev: "vsock",
253 index: q_index.0 as u16,
254 }
255 .fail()?;
256 Ok(())
257 }
258
259 fn handle_queue<'m, Q, S, E>(
260 &mut self,
261 index: u16,
262 _active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,
263 ) -> Result<()>
264 where
265 Q: VirtQueue<'m>,
266 S: IrqSender,
267 E: IoeventFd,
268 {
269 match index {
270 0 | 1 => unreachable!("{}: queue 0 and 1 are offloaded to kernel", self.name),
271 2 => log::info!("{}: event queue buffer available", self.name),
272 _ => unreachable!(),
273 }
274 Ok(())
275 }
276}
277
278impl Drop for VhostVsock {
279 fn drop(&mut self) {
280 let ret = self.vhost_dev.vsock_set_running(false);
281 if let Err(e) = ret {
282 log::error!("{}: {e}", self.name)
283 }
284 }
285}
286