devices/virtio/vhost_user_backend/
vsock.rs1use std::convert::TryInto;
6use std::fs::File;
7use std::fs::OpenOptions;
8use std::mem::size_of;
9use std::num::Wrapping;
10use std::os::unix::fs::OpenOptionsExt;
11use std::path::Path;
12use std::str;
13
14use anyhow::Context;
15use argh::FromArgs;
16use base::AsRawDescriptor;
17use base::Event;
18use base::RawDescriptor;
19use base::SafeDescriptor;
20use cros_async::Executor;
21use data_model::Le64;
22use vhost::Vhost;
23use vhost::Vsock;
24use vm_memory::GuestMemory;
25use vmm_vhost::connection::Connection;
26use vmm_vhost::message::BackendReq;
27use vmm_vhost::message::VhostSharedMemoryRegion;
28use vmm_vhost::message::VhostUserConfigFlags;
29use vmm_vhost::message::VhostUserInflight;
30use vmm_vhost::message::VhostUserMemoryRegion;
31use vmm_vhost::message::VhostUserMigrationPhase;
32use vmm_vhost::message::VhostUserProtocolFeatures;
33use vmm_vhost::message::VhostUserSingleMemoryRegion;
34use vmm_vhost::message::VhostUserTransferDirection;
35use vmm_vhost::message::VhostUserVringAddrFlags;
36use vmm_vhost::message::VhostUserVringState;
37use vmm_vhost::Error;
38use vmm_vhost::Result;
39use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;
40use zerocopy::IntoBytes;
41
42use super::BackendConnection;
43use crate::virtio::device_constants::vsock::NUM_QUEUES;
44use crate::virtio::vhost_user_backend::handler::vmm_va_to_gpa;
45use crate::virtio::vhost_user_backend::handler::MappingInfo;
46use crate::virtio::vhost_user_backend::handler::VhostUserRegularOps;
47use crate::virtio::vhost_user_backend::VhostUserDeviceBuilder;
48use crate::virtio::Queue;
49use crate::virtio::QueueConfig;
50
51const EVENT_QUEUE: usize = NUM_QUEUES - 1;
52
53struct VsockBackend {
54 queues: [QueueConfig; NUM_QUEUES],
55 vmm_maps: Option<Vec<MappingInfo>>,
56 mem: Option<GuestMemory>,
57
58 handle: Vsock,
59 cid: u64,
60 protocol_features: VhostUserProtocolFeatures,
61}
62
63pub struct VhostUserVsockDevice {
67 cid: u64,
68 handle: Vsock,
69}
70
71impl VhostUserVsockDevice {
72 pub fn new<P: AsRef<Path>>(cid: u64, vhost_device: P) -> anyhow::Result<Self> {
73 let handle = Vsock::new(
74 OpenOptions::new()
75 .read(true)
76 .write(true)
77 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
78 .open(vhost_device.as_ref())
79 .with_context(|| {
80 format!(
81 "failed to open vhost-vsock device {}",
82 vhost_device.as_ref().display()
83 )
84 })?,
85 );
86
87 Ok(Self { cid, handle })
88 }
89}
90
91impl AsRawDescriptor for VhostUserVsockDevice {
92 fn as_raw_descriptor(&self) -> base::RawDescriptor {
93 self.handle.as_raw_descriptor()
94 }
95}
96
97impl VhostUserDeviceBuilder for VhostUserVsockDevice {
98 fn build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>> {
99 let backend = VsockBackend {
100 queues: [
101 QueueConfig::new(Queue::MAX_SIZE, 0),
102 QueueConfig::new(Queue::MAX_SIZE, 0),
103 QueueConfig::new(Queue::MAX_SIZE, 0),
104 ],
105 vmm_maps: None,
106 mem: None,
107 handle: self.handle,
108 cid: self.cid,
109 protocol_features: VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::CONFIG,
110 };
111
112 Ok(Box::new(backend))
113 }
114}
115
116fn convert_vhost_error(err: vhost::Error) -> Error {
117 use vhost::Error::*;
118 match err {
119 IoctlError(e) => Error::ReqHandlerError(e),
120 _ => Error::BackendInternalError,
121 }
122}
123
124impl vmm_vhost::Backend for VsockBackend {
125 fn set_owner(&mut self) -> Result<()> {
126 self.handle.set_owner().map_err(convert_vhost_error)
127 }
128
129 fn reset_owner(&mut self) -> Result<()> {
130 self.handle.reset_owner().map_err(convert_vhost_error)
131 }
132
133 fn get_features(&mut self) -> Result<u64> {
134 let features = self.handle.get_features().map_err(convert_vhost_error)?
136 | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
137 Ok(features)
138 }
139
140 fn set_features(&mut self, features: u64) -> Result<()> {
141 let features = features & !(1 << VHOST_USER_F_PROTOCOL_FEATURES);
144 self.handle
145 .set_features(features)
146 .map_err(convert_vhost_error)
147 }
148
149 fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
150 Ok(self.protocol_features)
151 }
152
153 fn set_protocol_features(&mut self, features: u64) -> Result<()> {
154 let unrequested_features = features & !self.protocol_features.bits();
155 if unrequested_features != 0 {
156 Err(Error::InvalidParam("unsupported protocol feature"))
157 } else {
158 Ok(())
159 }
160 }
161
162 fn set_mem_table(
163 &mut self,
164 contexts: &[VhostUserMemoryRegion],
165 files: Vec<File>,
166 ) -> Result<()> {
167 let (guest_mem, vmm_maps) = VhostUserRegularOps::set_mem_table(contexts, files)?;
168
169 self.handle
170 .set_mem_table(&guest_mem)
171 .map_err(convert_vhost_error)?;
172
173 self.mem = Some(guest_mem);
174 self.vmm_maps = Some(vmm_maps);
175
176 Ok(())
177 }
178
179 fn get_queue_num(&mut self) -> Result<u64> {
180 Ok(NUM_QUEUES as u64)
181 }
182
183 fn set_vring_num(&mut self, index: u32, num: u32) -> Result<()> {
184 if index >= NUM_QUEUES as u32 || num == 0 || num > Queue::MAX_SIZE.into() {
185 return Err(Error::InvalidParam(
186 "set_vring_num: vring index or size out of range",
187 ));
188 }
189
190 let index = index as usize;
192 let num = num as u16;
193 self.queues[index].set_size(num);
194
195 if index == EVENT_QUEUE {
197 return Ok(());
198 }
199
200 self.handle
201 .set_vring_num(index, num)
202 .map_err(convert_vhost_error)
203 }
204
205 fn set_vring_addr(
206 &mut self,
207 index: u32,
208 flags: VhostUserVringAddrFlags,
209 descriptor: u64,
210 used: u64,
211 available: u64,
212 log: u64,
213 ) -> Result<()> {
214 if index >= NUM_QUEUES as u32 {
215 return Err(Error::InvalidParam("set_vring_addr: index out of range"));
216 }
217
218 let index = index as usize;
219
220 let mem = self
221 .mem
222 .as_ref()
223 .ok_or(Error::InvalidParam("set_vring_addr: could not get mem"))?;
224 let maps = self.vmm_maps.as_ref().ok_or(Error::InvalidParam(
225 "set_vring_addr: could not get vmm_maps",
226 ))?;
227
228 let queue = &mut self.queues[index];
229 queue.set_desc_table(vmm_va_to_gpa(maps, descriptor)?);
230 queue.set_avail_ring(vmm_va_to_gpa(maps, available)?);
231 queue.set_used_ring(vmm_va_to_gpa(maps, used)?);
232 let log_addr = if flags.contains(VhostUserVringAddrFlags::VHOST_VRING_F_LOG) {
233 vmm_va_to_gpa(maps, log).map(Some)?
234 } else {
235 None
236 };
237
238 if index == EVENT_QUEUE {
239 return Ok(());
240 }
241
242 self.handle
243 .set_vring_addr(
244 mem,
245 queue.size(),
246 index,
247 flags.bits(),
248 queue.desc_table(),
249 queue.used_ring(),
250 queue.avail_ring(),
251 log_addr,
252 )
253 .map_err(convert_vhost_error)
254 }
255
256 fn set_vring_base(&mut self, index: u32, base: u32) -> Result<()> {
257 if index >= NUM_QUEUES as u32 {
258 return Err(Error::InvalidParam("set_vring_base: index out of range"));
259 }
260
261 let index = index as usize;
262 let base = base as u16;
263
264 let queue = &mut self.queues[index];
265 queue.set_next_avail(Wrapping(base));
266 queue.set_next_used(Wrapping(base));
267
268 if index == EVENT_QUEUE {
269 return Ok(());
270 }
271
272 self.handle
273 .set_vring_base(index, base)
274 .map_err(convert_vhost_error)
275 }
276
277 fn get_vring_base(&mut self, index: u32) -> Result<VhostUserVringState> {
278 if index >= NUM_QUEUES as u32 {
279 return Err(Error::InvalidParam("get_vring_base: index out of range"));
280 }
281
282 let index = index as usize;
283 let next_avail = if index == EVENT_QUEUE {
284 self.queues[index].next_avail().0
285 } else {
286 self.handle
287 .get_vring_base(index)
288 .map_err(convert_vhost_error)?
289 };
290
291 Ok(VhostUserVringState::new(index as u32, next_avail.into()))
292 }
293
294 fn set_vring_kick(&mut self, index: u8, fd: Option<File>) -> Result<()> {
295 if index >= NUM_QUEUES as u8 {
296 return Err(Error::InvalidParam("set_vring_kick: index out of range"));
297 }
298
299 let file = fd.ok_or(Error::InvalidParam("set_vring_kick: missing fd"))?;
300 let event = Event::from(SafeDescriptor::from(file));
301 let index = usize::from(index);
302 if index != EVENT_QUEUE {
303 self.handle
304 .set_vring_kick(index, &event)
305 .map_err(convert_vhost_error)?;
306 }
307
308 Ok(())
309 }
310
311 fn set_vring_call(&mut self, index: u8, fd: Option<File>) -> Result<()> {
312 if index >= NUM_QUEUES as u8 {
313 return Err(Error::InvalidParam("set_vring_call: index out of range"));
314 }
315
316 let file = fd.ok_or(Error::InvalidParam("set_vring_call: missing fd"))?;
317 let event = Event::from(SafeDescriptor::from(file));
318 let index = usize::from(index);
319 if index != EVENT_QUEUE {
320 self.handle
321 .set_vring_call(index, &event)
322 .map_err(convert_vhost_error)?;
323 }
324
325 Ok(())
326 }
327
328 fn set_vring_err(&mut self, index: u8, fd: Option<File>) -> Result<()> {
329 if index >= NUM_QUEUES as u8 {
330 return Err(Error::InvalidParam("set_vring_err: index out of range"));
331 }
332
333 let index = usize::from(index);
334 let file = fd.ok_or(Error::InvalidParam("set_vring_err: missing fd"))?;
335
336 let event = Event::from(SafeDescriptor::from(file));
337
338 if index == EVENT_QUEUE {
339 return Ok(());
340 }
341
342 self.handle
343 .set_vring_err(index, &event)
344 .map_err(convert_vhost_error)
345 }
346
347 fn set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()> {
348 if index >= NUM_QUEUES as u32 {
349 return Err(Error::InvalidParam("vring index out of range"));
350 }
351
352 self.queues[index as usize].set_ready(enable);
353
354 if index == (EVENT_QUEUE) as u32 {
355 return Ok(());
356 }
357
358 if self.queues[..EVENT_QUEUE].iter().all(|q| q.ready()) {
359 self.handle.set_cid(self.cid).map_err(convert_vhost_error)?;
361 self.handle.start().map_err(convert_vhost_error)
362 } else if !enable {
363 self.handle.stop().map_err(convert_vhost_error)
365 } else {
366 Ok(())
367 }
368 }
369
370 fn get_config(
371 &mut self,
372 offset: u32,
373 size: u32,
374 _flags: VhostUserConfigFlags,
375 ) -> Result<Vec<u8>> {
376 let start: usize = offset
377 .try_into()
378 .map_err(|_| Error::InvalidParam("offset does not fit in usize"))?;
379 let end: usize = offset
380 .checked_add(size)
381 .and_then(|e| e.try_into().ok())
382 .ok_or(Error::InvalidParam("offset + size does not fit in usize"))?;
383
384 if start >= size_of::<Le64>() || end > size_of::<Le64>() {
385 return Err(Error::InvalidParam(
386 "get_config: offset and/or size out of range",
387 ));
388 }
389
390 Ok(Le64::from(self.cid).as_bytes()[start..end].to_vec())
391 }
392
393 fn set_config(
394 &mut self,
395 _offset: u32,
396 _buf: &[u8],
397 _flags: VhostUserConfigFlags,
398 ) -> Result<()> {
399 Err(Error::InvalidOperation)
400 }
401
402 fn set_backend_req_fd(&mut self, _vu_req: Connection<BackendReq>) {
403 unreachable!("unexpected set_backend_req_fd");
405 }
406
407 fn get_inflight_fd(
408 &mut self,
409 _inflight: &VhostUserInflight,
410 ) -> Result<(VhostUserInflight, File)> {
411 Err(Error::InvalidOperation)
412 }
413
414 fn set_inflight_fd(&mut self, _inflight: &VhostUserInflight, _file: File) -> Result<()> {
415 Err(Error::InvalidOperation)
416 }
417
418 fn get_max_mem_slots(&mut self) -> Result<u64> {
419 Err(Error::InvalidOperation)
420 }
421
422 fn add_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion, _fd: File) -> Result<()> {
423 Err(Error::InvalidOperation)
424 }
425
426 fn remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()> {
427 Err(Error::InvalidOperation)
428 }
429
430 fn set_device_state_fd(
431 &mut self,
432 _transfer_direction: VhostUserTransferDirection,
433 _migration_phase: VhostUserMigrationPhase,
434 _fd: File,
435 ) -> Result<Option<File>> {
436 Err(Error::InvalidOperation)
437 }
438
439 fn check_device_state(&mut self) -> Result<()> {
440 Err(Error::InvalidOperation)
441 }
442
443 fn get_shared_memory_regions(&mut self) -> Result<Vec<VhostSharedMemoryRegion>> {
444 Ok(vec![])
445 }
446}
447
448#[derive(FromArgs)]
449#[argh(subcommand, name = "vsock")]
450pub struct Options {
452 #[argh(option, arg_name = "PATH", hidden_help)]
453 socket: Option<String>,
455 #[argh(option, arg_name = "PATH")]
456 socket_path: Option<String>,
459 #[argh(option, arg_name = "FD")]
460 fd: Option<RawDescriptor>,
463
464 #[argh(option, arg_name = "INT")]
465 cid: u64,
467 #[argh(
468 option,
469 default = "String::from(\"/dev/vhost-vsock\")",
470 arg_name = "PATH"
471 )]
472 vhost_socket: String,
474}
475
476pub fn run_vsock_device(opts: Options) -> anyhow::Result<()> {
478 let ex = Executor::new().context("failed to create executor")?;
479
480 let conn =
481 BackendConnection::from_opts(opts.socket.as_deref(), opts.socket_path.as_deref(), opts.fd)?;
482
483 let vsock_device = Box::new(VhostUserVsockDevice::new(opts.cid, opts.vhost_socket)?);
484
485 conn.run_device(ex, vsock_device)
486}