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::GuestAddress;
25use vm_memory::GuestMemory;
26use vmm_vhost::connection::Connection;
27use vmm_vhost::message::VhostUserConfigFlags;
28use vmm_vhost::message::VhostUserInflight;
29use vmm_vhost::message::VhostUserMemoryRegion;
30use vmm_vhost::message::VhostUserMigrationPhase;
31use vmm_vhost::message::VhostUserProtocolFeatures;
32use vmm_vhost::message::VhostUserSingleMemoryRegion;
33use vmm_vhost::message::VhostUserTransferDirection;
34use vmm_vhost::message::VhostUserVringAddrFlags;
35use vmm_vhost::message::VhostUserVringState;
36use vmm_vhost::Error;
37use vmm_vhost::Result;
38use vmm_vhost::SharedMemoryRegion;
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 VringConfig {
54 kick_fd: Option<File>,
55 call_fd: Option<File>,
56 err_fd: Option<File>,
57 flags: VhostUserVringAddrFlags,
58 log_addr: Option<GuestAddress>,
59}
60
61impl Default for VringConfig {
62 fn default() -> Self {
63 Self {
64 kick_fd: None,
65 call_fd: None,
66 err_fd: None,
67 flags: VhostUserVringAddrFlags::empty(),
68 log_addr: None,
69 }
70 }
71}
72
73struct VringState {
74 queue: QueueConfig,
75 config: VringConfig,
76}
77
78impl Default for VringState {
79 fn default() -> Self {
80 Self {
81 queue: QueueConfig::new(Queue::MAX_SIZE, 0),
82 config: VringConfig::default(),
83 }
84 }
85}
86
87struct VsockBackend {
88 vrings: [VringState; NUM_QUEUES],
89 vmm_maps: Option<Vec<MappingInfo>>,
90 mem: Option<GuestMemory>,
91
92 handle: Vsock,
93 cid: u64,
94 protocol_features: VhostUserProtocolFeatures,
95}
96
97pub struct VhostUserVsockDevice {
101 cid: u64,
102 handle: Vsock,
103}
104
105impl VhostUserVsockDevice {
106 pub fn new<P: AsRef<Path>>(cid: u64, vhost_device: P) -> anyhow::Result<Self> {
107 let handle = Vsock::new(
108 OpenOptions::new()
109 .read(true)
110 .write(true)
111 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
112 .open(vhost_device.as_ref())
113 .with_context(|| {
114 format!(
115 "failed to open vhost-vsock device {}",
116 vhost_device.as_ref().display()
117 )
118 })?,
119 );
120
121 Ok(Self { cid, handle })
122 }
123}
124
125impl AsRawDescriptor for VhostUserVsockDevice {
126 fn as_raw_descriptor(&self) -> base::RawDescriptor {
127 self.handle.as_raw_descriptor()
128 }
129}
130
131impl VhostUserDeviceBuilder for VhostUserVsockDevice {
132 fn build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>> {
133 let backend = VsockBackend {
134 vrings: Default::default(),
135 vmm_maps: None,
136 mem: None,
137 handle: self.handle,
138 cid: self.cid,
139 protocol_features: VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::CONFIG,
140 };
141
142 Ok(Box::new(backend))
143 }
144}
145
146fn convert_vhost_error(err: vhost::Error) -> Error {
147 use vhost::Error::*;
148 match err {
149 IoctlError(e) => Error::ReqHandlerError(e),
150 _ => Error::BackendInternalError,
151 }
152}
153
154fn program_fd<F>(handle: &Vsock, fd: Option<&File>, index: usize, f: F) -> Result<()>
155where
156 F: FnOnce(&Vsock, usize, &Event) -> std::result::Result<(), vhost::Error>,
157{
158 if let Some(file) = fd {
159 let cloned_file = file.try_clone().map_err(Error::ReqHandlerError)?;
160 let event = Event::from(SafeDescriptor::from(cloned_file));
161 f(handle, index, &event).map_err(convert_vhost_error)?;
162 }
163 Ok(())
164}
165
166impl VsockBackend {
167 fn validate_addresses(
168 mem: &GuestMemory,
169 queue_size: u16,
170 desc_addr: GuestAddress,
171 avail_addr: GuestAddress,
172 used_addr: GuestAddress,
173 log_addr: Option<GuestAddress>,
174 ) -> Result<()> {
175 if queue_size == 0 || !queue_size.is_power_of_two() {
176 return Err(Error::InvalidParam("invalid queue size"));
177 }
178
179 let queue_size = usize::from(queue_size);
180
181 let desc_table_size = 16 * queue_size;
182 mem.get_slice_at_addr(desc_addr, desc_table_size)
183 .map_err(|_| Error::InvalidParam("invalid descriptor table address"))?;
184
185 let used_ring_size = 6 + 8 * queue_size;
186 mem.get_slice_at_addr(used_addr, used_ring_size)
187 .map_err(|_| Error::InvalidParam("invalid used ring address"))?;
188
189 let avail_ring_size = 6 + 2 * queue_size;
190 mem.get_slice_at_addr(avail_addr, avail_ring_size)
191 .map_err(|_| Error::InvalidParam("invalid available ring address"))?;
192
193 if let Some(a) = log_addr {
194 mem.get_host_address(a)
195 .map_err(|_| Error::InvalidParam("invalid log address"))?;
196 }
197
198 Ok(())
199 }
200
201 fn program_queues_to_kernel(&mut self) -> Result<()> {
202 let mem = self
203 .mem
204 .as_ref()
205 .ok_or(Error::InvalidParam("program_queues: guest memory not set"))?;
206
207 for index in 0..EVENT_QUEUE {
208 let vring = &self.vrings[index];
209 let queue = &vring.queue;
210 let config = &vring.config;
211
212 self.handle
213 .set_vring_num(index, queue.size())
214 .map_err(convert_vhost_error)?;
215
216 let flags = config.flags;
217 let log_addr = config.log_addr;
218 self.handle
219 .set_vring_addr(
220 mem,
221 queue.size(),
222 index,
223 flags.bits(),
224 queue.desc_table(),
225 queue.used_ring(),
226 queue.avail_ring(),
227 log_addr,
228 )
229 .map_err(convert_vhost_error)?;
230
231 self.handle
232 .set_vring_base(index, queue.next_avail().0)
233 .map_err(convert_vhost_error)?;
234
235 program_fd(
236 &self.handle,
237 config.kick_fd.as_ref(),
238 index,
239 Vsock::set_vring_kick,
240 )?;
241 program_fd(
242 &self.handle,
243 config.call_fd.as_ref(),
244 index,
245 Vsock::set_vring_call,
246 )?;
247 program_fd(
248 &self.handle,
249 config.err_fd.as_ref(),
250 index,
251 Vsock::set_vring_err,
252 )?;
253 }
254 Ok(())
255 }
256}
257
258impl vmm_vhost::Backend for VsockBackend {
259 fn set_owner(&mut self) -> Result<()> {
260 self.handle.set_owner().map_err(convert_vhost_error)
261 }
262
263 fn reset_owner(&mut self) -> Result<()> {
264 self.handle.reset_owner().map_err(convert_vhost_error)
265 }
266
267 fn get_features(&mut self) -> Result<u64> {
268 let features = self.handle.get_features().map_err(convert_vhost_error)?
270 | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
271 Ok(features)
272 }
273
274 fn set_features(&mut self, features: u64) -> Result<()> {
275 let features = features & !(1 << VHOST_USER_F_PROTOCOL_FEATURES);
278 self.handle
279 .set_features(features)
280 .map_err(convert_vhost_error)
281 }
282
283 fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
284 Ok(self.protocol_features)
285 }
286
287 fn set_protocol_features(&mut self, features: u64) -> Result<()> {
288 let unrequested_features = features & !self.protocol_features.bits();
289 if unrequested_features != 0 {
290 Err(Error::InvalidParam("unsupported protocol feature"))
291 } else {
292 Ok(())
293 }
294 }
295
296 fn set_mem_table(
297 &mut self,
298 contexts: &[VhostUserMemoryRegion],
299 files: Vec<File>,
300 ) -> Result<()> {
301 let (guest_mem, vmm_maps) = VhostUserRegularOps::set_mem_table(contexts, files)?;
302
303 self.handle
304 .set_mem_table(&guest_mem)
305 .map_err(convert_vhost_error)?;
306
307 self.mem = Some(guest_mem);
308 self.vmm_maps = Some(vmm_maps);
309
310 Ok(())
311 }
312
313 fn get_queue_num(&mut self) -> Result<u64> {
314 Ok(NUM_QUEUES as u64)
315 }
316
317 fn set_vring_num(&mut self, index: u32, num: u32) -> Result<()> {
318 if index >= NUM_QUEUES as u32 || num == 0 || num > Queue::MAX_SIZE.into() {
319 return Err(Error::InvalidParam(
320 "set_vring_num: vring index or size out of range",
321 ));
322 }
323
324 let index = index as usize;
325 let num = num as u16;
326
327 let vring = &mut self.vrings[index];
328 if vring.queue.used_ring().0 != 0 {
329 let mem = self.mem.as_ref().ok_or(Error::InvalidParam(
330 "set_vring_num: addresses set but no mem table",
331 ))?;
332 Self::validate_addresses(
333 mem,
334 num,
335 vring.queue.desc_table(),
336 vring.queue.avail_ring(),
337 vring.queue.used_ring(),
338 vring.config.log_addr,
339 )?;
340 }
341
342 vring.queue.set_size(num);
343 Ok(())
344 }
345
346 fn set_vring_addr(
347 &mut self,
348 index: u32,
349 flags: VhostUserVringAddrFlags,
350 descriptor: u64,
351 used: u64,
352 available: u64,
353 log: u64,
354 ) -> Result<()> {
355 if index >= NUM_QUEUES as u32 {
356 return Err(Error::InvalidParam("set_vring_addr: index out of range"));
357 }
358
359 let index = index as usize;
360
361 let mem = self
362 .mem
363 .as_ref()
364 .ok_or(Error::InvalidParam("set_vring_addr: could not get mem"))?;
365 let maps = self.vmm_maps.as_ref().ok_or(Error::InvalidParam(
366 "set_vring_addr: could not get vmm_maps",
367 ))?;
368
369 let desc_gpa = vmm_va_to_gpa(maps, descriptor)?;
370 let avail_gpa = vmm_va_to_gpa(maps, available)?;
371 let used_gpa = vmm_va_to_gpa(maps, used)?;
372 let log_gpa = if flags.contains(VhostUserVringAddrFlags::VHOST_VRING_F_LOG) {
373 vmm_va_to_gpa(maps, log).map(Some)?
374 } else {
375 None
376 };
377
378 let vring = &mut self.vrings[index];
379 let queue_size = vring.queue.size();
380 Self::validate_addresses(mem, queue_size, desc_gpa, avail_gpa, used_gpa, log_gpa)?;
381
382 vring.queue.set_desc_table(desc_gpa);
383 vring.queue.set_avail_ring(avail_gpa);
384 vring.queue.set_used_ring(used_gpa);
385
386 vring.config.flags = flags;
387 vring.config.log_addr = log_gpa;
388
389 Ok(())
390 }
391
392 fn set_vring_base(&mut self, index: u32, base: u32) -> Result<()> {
393 if index >= NUM_QUEUES as u32 {
394 return Err(Error::InvalidParam("set_vring_base: index out of range"));
395 }
396
397 let index = index as usize;
398 let base = base as u16;
399
400 let queue = &mut self.vrings[index].queue;
401 queue.set_next_avail(Wrapping(base));
402 queue.set_next_used(Wrapping(base));
403
404 Ok(())
405 }
406
407 fn get_vring_base(&mut self, index: u32) -> Result<VhostUserVringState> {
408 if index >= NUM_QUEUES as u32 {
409 return Err(Error::InvalidParam("get_vring_base: index out of range"));
410 }
411
412 let index = index as usize;
413 let next_avail = if index == EVENT_QUEUE {
414 self.vrings[index].queue.next_avail().0
415 } else {
416 self.handle
417 .get_vring_base(index)
418 .map_err(convert_vhost_error)?
419 };
420
421 Ok(VhostUserVringState::new(index as u32, next_avail.into()))
422 }
423
424 fn set_vring_kick(&mut self, index: u8, fd: Option<File>) -> Result<()> {
425 if index >= NUM_QUEUES as u8 {
426 return Err(Error::InvalidParam("set_vring_kick: index out of range"));
427 }
428 let file = fd.ok_or(Error::InvalidParam("set_vring_kick: missing fd"))?;
429 let index = usize::from(index);
430 self.vrings[index].config.kick_fd = Some(file);
431 Ok(())
432 }
433
434 fn set_vring_call(&mut self, index: u8, fd: Option<File>) -> Result<()> {
435 if index >= NUM_QUEUES as u8 {
436 return Err(Error::InvalidParam("set_vring_call: index out of range"));
437 }
438 let file = fd.ok_or(Error::InvalidParam("set_vring_call: missing fd"))?;
439 let index = usize::from(index);
440 self.vrings[index].config.call_fd = Some(file);
441 Ok(())
442 }
443
444 fn set_vring_err(&mut self, index: u8, fd: Option<File>) -> Result<()> {
445 if index >= NUM_QUEUES as u8 {
446 return Err(Error::InvalidParam("set_vring_err: index out of range"));
447 }
448 let file = fd.ok_or(Error::InvalidParam("set_vring_err: missing fd"))?;
449 let index = usize::from(index);
450 self.vrings[index].config.err_fd = Some(file);
451 Ok(())
452 }
453
454 fn set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()> {
455 if index >= NUM_QUEUES as u32 {
456 return Err(Error::InvalidParam("vring index out of range"));
457 }
458
459 self.vrings[index as usize].queue.set_ready(enable);
460
461 if index == (EVENT_QUEUE) as u32 {
462 return Ok(());
463 }
464
465 if self.vrings[..EVENT_QUEUE].iter().all(|v| v.queue.ready()) {
466 self.program_queues_to_kernel()?;
468 self.handle.set_cid(self.cid).map_err(convert_vhost_error)?;
469 self.handle.start().map_err(convert_vhost_error)
470 } else if !enable {
471 self.handle.stop().map_err(convert_vhost_error)
473 } else {
474 Ok(())
475 }
476 }
477
478 fn get_config(
479 &mut self,
480 offset: u32,
481 size: u32,
482 _flags: VhostUserConfigFlags,
483 ) -> Result<Vec<u8>> {
484 let start: usize = offset
485 .try_into()
486 .map_err(|_| Error::InvalidParam("offset does not fit in usize"))?;
487 let end: usize = offset
488 .checked_add(size)
489 .and_then(|e| e.try_into().ok())
490 .ok_or(Error::InvalidParam("offset + size does not fit in usize"))?;
491
492 if start >= size_of::<Le64>() || end > size_of::<Le64>() {
493 return Err(Error::InvalidParam(
494 "get_config: offset and/or size out of range",
495 ));
496 }
497
498 Ok(Le64::from(self.cid).as_bytes()[start..end].to_vec())
499 }
500
501 fn set_config(
502 &mut self,
503 _offset: u32,
504 _buf: &[u8],
505 _flags: VhostUserConfigFlags,
506 ) -> Result<()> {
507 Err(Error::InvalidOperation)
508 }
509
510 fn set_backend_req_fd(&mut self, _vu_req: Connection) {
511 unreachable!("unexpected set_backend_req_fd");
513 }
514
515 fn get_inflight_fd(
516 &mut self,
517 _inflight: &VhostUserInflight,
518 ) -> Result<(VhostUserInflight, File)> {
519 Err(Error::InvalidOperation)
520 }
521
522 fn set_inflight_fd(&mut self, _inflight: &VhostUserInflight, _file: File) -> Result<()> {
523 Err(Error::InvalidOperation)
524 }
525
526 fn get_max_mem_slots(&mut self) -> Result<u64> {
527 Err(Error::InvalidOperation)
528 }
529
530 fn add_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion, _fd: File) -> Result<()> {
531 Err(Error::InvalidOperation)
532 }
533
534 fn remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()> {
535 Err(Error::InvalidOperation)
536 }
537
538 fn set_device_state_fd(
539 &mut self,
540 _transfer_direction: VhostUserTransferDirection,
541 _migration_phase: VhostUserMigrationPhase,
542 _fd: File,
543 ) -> Result<Option<File>> {
544 Err(Error::InvalidOperation)
545 }
546
547 fn check_device_state(&mut self) -> Result<()> {
548 Err(Error::InvalidOperation)
549 }
550
551 fn get_shmem_config(&mut self) -> Result<Vec<SharedMemoryRegion>> {
552 Ok(Vec::new())
553 }
554}
555
556#[derive(FromArgs)]
557#[argh(subcommand, name = "vsock")]
558pub struct Options {
560 #[argh(option, arg_name = "PATH", hidden_help)]
561 socket: Option<String>,
563 #[argh(option, arg_name = "PATH")]
564 socket_path: Option<String>,
567 #[argh(option, arg_name = "FD")]
568 fd: Option<RawDescriptor>,
571
572 #[argh(option, arg_name = "INT")]
573 cid: u64,
575 #[argh(
576 option,
577 default = "String::from(\"/dev/vhost-vsock\")",
578 arg_name = "PATH"
579 )]
580 vhost_socket: String,
582}
583
584pub fn run_vsock_device(opts: Options) -> anyhow::Result<()> {
586 let ex = Executor::new().context("failed to create executor")?;
587
588 let conn =
589 BackendConnection::from_opts(opts.socket.as_deref(), opts.socket_path.as_deref(), opts.fd)?;
590
591 let vsock_device = Box::new(VhostUserVsockDevice::new(opts.cid, opts.vhost_socket)?);
592
593 conn.run_device(ex, vsock_device)
594}