• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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 std::collections::BTreeMap;
6 use std::fs::OpenOptions;
7 use std::os::unix::prelude::OpenOptionsExt;
8 
9 use anyhow::anyhow;
10 use anyhow::Context;
11 use base::error;
12 use base::open_file_or_duplicate;
13 use base::warn;
14 use base::AsRawDescriptor;
15 use base::Event;
16 use base::RawDescriptor;
17 use base::WorkerThread;
18 use data_model::Le64;
19 use serde::Deserialize;
20 use serde::Serialize;
21 use snapshot::AnySnapshot;
22 use vhost::Vhost;
23 use vhost::Vsock as VhostVsockHandle;
24 use vm_memory::GuestMemory;
25 use zerocopy::IntoBytes;
26 
27 use super::worker::VringBase;
28 use super::worker::Worker;
29 use super::Error;
30 use super::Result;
31 use crate::virtio::copy_config;
32 use crate::virtio::device_constants::vsock::NUM_QUEUES;
33 use crate::virtio::vsock::VsockConfig;
34 use crate::virtio::DeviceType;
35 use crate::virtio::Interrupt;
36 use crate::virtio::Queue;
37 use crate::virtio::VirtioDevice;
38 
39 const QUEUE_SIZE: u16 = 256;
40 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
41 
42 pub struct Vsock {
43     worker_thread: Option<WorkerThread<Worker<VhostVsockHandle>>>,
44     vhost_handle: Option<VhostVsockHandle>,
45     cid: u64,
46     interrupts: Option<Vec<Event>>,
47     avail_features: u64,
48     acked_features: u64,
49     // vrings_base states:
50     // None - device was just created or is running.
51     // Some - device was put to sleep after running or was restored.
52     vrings_base: Option<Vec<VringBase>>,
53     // Some iff the device is active and awake.
54     event_queue: Option<Queue>,
55     // If true, we should send a TRANSPORT_RESET event to the guest at the next opportunity.
56     needs_transport_reset: bool,
57 }
58 
59 #[derive(Serialize, Deserialize)]
60 struct VsockSnapshot {
61     cid: u64,
62     avail_features: u64,
63     acked_features: u64,
64     vrings_base: Vec<VringBase>,
65 }
66 
67 impl Vsock {
68     /// Create a new virtio-vsock device with the given VM cid.
new(base_features: u64, vsock_config: &VsockConfig) -> anyhow::Result<Vsock>69     pub fn new(base_features: u64, vsock_config: &VsockConfig) -> anyhow::Result<Vsock> {
70         let device_file = open_file_or_duplicate(
71             &vsock_config.vhost_device,
72             OpenOptions::new()
73                 .read(true)
74                 .write(true)
75                 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK),
76         )
77         .with_context(|| {
78             format!(
79                 "failed to open virtual socket device {}",
80                 vsock_config.vhost_device.display(),
81             )
82         })?;
83 
84         let handle = VhostVsockHandle::new(device_file);
85 
86         let avail_features = base_features;
87 
88         let mut interrupts = Vec::new();
89         for _ in 0..NUM_QUEUES {
90             interrupts.push(Event::new().map_err(Error::VhostIrqCreate)?);
91         }
92 
93         Ok(Vsock {
94             worker_thread: None,
95             vhost_handle: Some(handle),
96             cid: vsock_config.cid,
97             interrupts: Some(interrupts),
98             avail_features,
99             acked_features: 0,
100             vrings_base: None,
101             event_queue: None,
102             needs_transport_reset: false,
103         })
104     }
105 
new_for_testing(cid: u64, features: u64) -> Vsock106     pub fn new_for_testing(cid: u64, features: u64) -> Vsock {
107         Vsock {
108             worker_thread: None,
109             vhost_handle: None,
110             cid,
111             interrupts: None,
112             avail_features: features,
113             acked_features: 0,
114             vrings_base: None,
115             event_queue: None,
116             needs_transport_reset: false,
117         }
118     }
119 
acked_features(&self) -> u64120     pub fn acked_features(&self) -> u64 {
121         self.acked_features
122     }
123 }
124 
125 impl VirtioDevice for Vsock {
keep_rds(&self) -> Vec<RawDescriptor>126     fn keep_rds(&self) -> Vec<RawDescriptor> {
127         let mut keep_rds = Vec::new();
128 
129         if let Some(handle) = &self.vhost_handle {
130             keep_rds.push(handle.as_raw_descriptor());
131         }
132 
133         if let Some(interrupt) = &self.interrupts {
134             for vhost_int in interrupt.iter() {
135                 keep_rds.push(vhost_int.as_raw_descriptor());
136             }
137         }
138 
139         keep_rds
140     }
141 
device_type(&self) -> DeviceType142     fn device_type(&self) -> DeviceType {
143         DeviceType::Vsock
144     }
145 
queue_max_sizes(&self) -> &[u16]146     fn queue_max_sizes(&self) -> &[u16] {
147         QUEUE_SIZES
148     }
149 
features(&self) -> u64150     fn features(&self) -> u64 {
151         self.avail_features
152     }
153 
read_config(&self, offset: u64, data: &mut [u8])154     fn read_config(&self, offset: u64, data: &mut [u8]) {
155         let cid = Le64::from(self.cid);
156         copy_config(data, 0, cid.as_bytes(), offset);
157     }
158 
ack_features(&mut self, value: u64)159     fn ack_features(&mut self, value: u64) {
160         let mut v = value;
161 
162         // Check if the guest is ACK'ing a feature that we didn't claim to have.
163         let unrequested_features = v & !self.avail_features;
164         if unrequested_features != 0 {
165             warn!("vsock: virtio-vsock got unknown feature ack: {:x}", v);
166 
167             // Don't count these features as acked.
168             v &= !unrequested_features;
169         }
170         self.acked_features |= v;
171     }
172 
activate( &mut self, mem: GuestMemory, interrupt: Interrupt, mut queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>173     fn activate(
174         &mut self,
175         mem: GuestMemory,
176         interrupt: Interrupt,
177         mut queues: BTreeMap<usize, Queue>,
178     ) -> anyhow::Result<()> {
179         if queues.len() != NUM_QUEUES {
180             return Err(anyhow!(
181                 "net: expected {} queues, got {}",
182                 NUM_QUEUES,
183                 queues.len()
184             ));
185         }
186 
187         let vhost_handle = self.vhost_handle.take().context("missing vhost_handle")?;
188         let interrupts = self.interrupts.take().context("missing interrupts")?;
189         let acked_features = self.acked_features;
190         let cid = self.cid;
191 
192         // The third vq is an event-only vq that is not handled by the vhost
193         // subsystem (but still needs to exist).  Split it off here.
194         let mut event_queue = queues.remove(&2).unwrap();
195         // Send TRANSPORT_RESET event if needed.
196         if self.needs_transport_reset {
197             self.needs_transport_reset = false;
198 
199             // We assume the event queue is non-empty. This should be OK for existing use cases
200             // because we expect the guest vsock driver to be initialized at the time of snapshot
201             // and this is only the event we ever write to the queue.
202             //
203             // If that assumption becomes invalid, we could integrate this logic into the worker
204             // thread's event loop so that it can wait for space in the queue.
205             let mut avail_desc = event_queue
206                 .pop()
207                 .expect("event queue is empty, can't send transport reset event");
208             let transport_reset = virtio_sys::virtio_vsock::virtio_vsock_event{
209                 id: virtio_sys::virtio_vsock::virtio_vsock_event_id_VIRTIO_VSOCK_EVENT_TRANSPORT_RESET.into(),
210             };
211             avail_desc
212                 .writer
213                 .write_obj(transport_reset)
214                 .expect("failed to write transport reset event");
215             let len = avail_desc.writer.bytes_written() as u32;
216             event_queue.add_used(avail_desc, len);
217             event_queue.trigger_interrupt();
218         }
219         self.event_queue = Some(event_queue);
220 
221         let mut worker = Worker::new(
222             queues,
223             vhost_handle,
224             interrupts,
225             interrupt,
226             acked_features,
227             None,
228         );
229         let activate_vqs = |handle: &VhostVsockHandle| -> Result<()> {
230             handle.set_cid(cid).map_err(Error::VhostVsockSetCid)?;
231             handle.start().map_err(Error::VhostVsockStart)?;
232             Ok(())
233         };
234         worker
235             .init(mem, QUEUE_SIZES, activate_vqs, self.vrings_base.take())
236             .context("vsock worker init exited with error")?;
237 
238         self.worker_thread = Some(WorkerThread::start("vhost_vsock", move |kill_evt| {
239             let cleanup_vqs = |_handle: &VhostVsockHandle| -> Result<()> { Ok(()) };
240             let result = worker.run(cleanup_vqs, kill_evt);
241             if let Err(e) = result {
242                 error!("vsock worker thread exited with error: {:?}", e);
243             }
244             worker
245         }));
246 
247         Ok(())
248     }
249 
reset(&mut self) -> anyhow::Result<()>250     fn reset(&mut self) -> anyhow::Result<()> {
251         if let Some(worker_thread) = self.worker_thread.take() {
252             let worker = worker_thread.stop();
253             worker
254                 .vhost_handle
255                 .stop()
256                 .context("failed to stop vrings")?;
257             // Call get_vring_base to stop the queues.
258             for (pos, _) in worker.queues.iter() {
259                 worker
260                     .vhost_handle
261                     .get_vring_base(*pos)
262                     .context("get_vring_base failed")?;
263             }
264 
265             self.vhost_handle = Some(worker.vhost_handle);
266             self.interrupts = Some(worker.vhost_interrupt);
267         }
268         self.acked_features = 0;
269         self.vrings_base = None;
270         self.event_queue = None;
271         self.needs_transport_reset = false;
272         Ok(())
273     }
274 
on_device_sandboxed(&mut self)275     fn on_device_sandboxed(&mut self) {
276         // ignore the error but to log the error. We don't need to do
277         // anything here because when activate, the other vhost set up
278         // will be failed to stop the activate thread.
279         if let Some(vhost_handle) = &self.vhost_handle {
280             match vhost_handle.set_owner() {
281                 Ok(_) => {}
282                 Err(e) => error!("{}: failed to set owner: {:?}", self.debug_label(), e),
283             }
284         }
285     }
286 
virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>>287     fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
288         if let Some(worker_thread) = self.worker_thread.take() {
289             let worker = worker_thread.stop();
290             self.interrupts = Some(worker.vhost_interrupt);
291             worker
292                 .vhost_handle
293                 .stop()
294                 .context("failed to stop vrings")?;
295             let mut queues: BTreeMap<usize, Queue> = worker.queues;
296             let mut vrings_base = Vec::new();
297             for (pos, _) in queues.iter() {
298                 let vring_base = VringBase {
299                     index: *pos,
300                     base: worker.vhost_handle.get_vring_base(*pos)?,
301                 };
302                 vrings_base.push(vring_base);
303             }
304             self.vrings_base = Some(vrings_base);
305             self.vhost_handle = Some(worker.vhost_handle);
306             queues.insert(
307                 2,
308                 self.event_queue.take().expect("Vsock event queue missing"),
309             );
310             return Ok(Some(BTreeMap::from_iter(queues)));
311         }
312         Ok(None)
313     }
314 
virtio_wake( &mut self, device_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, ) -> anyhow::Result<()>315     fn virtio_wake(
316         &mut self,
317         device_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
318     ) -> anyhow::Result<()> {
319         match device_state {
320             None => Ok(()),
321             Some((mem, interrupt, queues)) => {
322                 // TODO: activate is just what we want at the moment, but we should probably move
323                 // it into a "start workers" function to make it obvious that it isn't strictly
324                 // used for activate events.
325                 self.activate(mem, interrupt, queues)?;
326                 Ok(())
327             }
328         }
329     }
330 
virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot>331     fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
332         let vrings_base = self.vrings_base.clone().unwrap_or_default();
333         AnySnapshot::to_any(VsockSnapshot {
334             // `cid` and `avail_features` are snapshot as a safeguard. Upon restore, validate
335             // cid and avail_features in the current vsock match the previously snapshot vsock.
336             cid: self.cid,
337             avail_features: self.avail_features,
338             acked_features: self.acked_features,
339             vrings_base,
340         })
341         .context("failed to snapshot virtio console")
342     }
343 
virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()>344     fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
345         let deser: VsockSnapshot =
346             AnySnapshot::from_any(data).context("failed to deserialize virtio vsock")?;
347         anyhow::ensure!(
348             self.cid == deser.cid,
349             "Virtio vsock incorrect cid for restore:\n Expected: {}, Actual: {}",
350             self.cid,
351             deser.cid,
352         );
353         anyhow::ensure!(
354             self.avail_features == deser.avail_features,
355             "Virtio vsock incorrect avail features for restore:\n Expected: {}, Actual: {}",
356             self.avail_features,
357             deser.avail_features,
358         );
359         self.acked_features = deser.acked_features;
360         self.vrings_base = Some(deser.vrings_base);
361         // Send the TRANSPORT_RESET on next wake so that the guest knows that its existing vsock
362         // connections are broken.
363         self.needs_transport_reset = true;
364         Ok(())
365     }
366 }
367 
368 #[cfg(test)]
369 mod tests {
370     use std::convert::TryInto;
371 
372     use super::*;
373 
374     #[test]
ack_features()375     fn ack_features() {
376         let cid = 5;
377         let features: u64 = (1 << 20) | (1 << 49) | (1 << 2) | (1 << 19);
378         let mut acked_features: u64 = 0;
379         let mut unavailable_features: u64 = 0;
380 
381         let mut vsock = Vsock::new_for_testing(cid, features);
382         assert_eq!(acked_features, vsock.acked_features());
383 
384         acked_features |= 1 << 2;
385         vsock.ack_features(acked_features);
386         assert_eq!(acked_features, vsock.acked_features());
387 
388         acked_features |= 1 << 49;
389         vsock.ack_features(acked_features);
390         assert_eq!(acked_features, vsock.acked_features());
391 
392         acked_features |= 1 << 60;
393         unavailable_features |= 1 << 60;
394         vsock.ack_features(acked_features);
395         assert_eq!(
396             acked_features & !unavailable_features,
397             vsock.acked_features()
398         );
399 
400         acked_features |= 1 << 1;
401         unavailable_features |= 1 << 1;
402         vsock.ack_features(acked_features);
403         assert_eq!(
404             acked_features & !unavailable_features,
405             vsock.acked_features()
406         );
407     }
408 
409     #[test]
read_config()410     fn read_config() {
411         let cid = 0xfca9a559fdcb9756;
412         let vsock = Vsock::new_for_testing(cid, 0);
413 
414         let mut buf = [0u8; 8];
415         vsock.read_config(0, &mut buf);
416         assert_eq!(cid, u64::from_le_bytes(buf));
417 
418         vsock.read_config(0, &mut buf[..4]);
419         assert_eq!(
420             (cid & 0xffffffff) as u32,
421             u32::from_le_bytes(buf[..4].try_into().unwrap())
422         );
423 
424         vsock.read_config(4, &mut buf[..4]);
425         assert_eq!(
426             (cid >> 32) as u32,
427             u32::from_le_bytes(buf[..4].try_into().unwrap())
428         );
429 
430         let data: [u8; 8] = [8, 226, 5, 46, 159, 59, 89, 77];
431         buf.copy_from_slice(&data);
432 
433         vsock.read_config(12, &mut buf);
434         assert_eq!(&buf, &data);
435     }
436 
437     #[test]
features()438     fn features() {
439         let cid = 5;
440         let features: u64 = 0xfc195ae8db88cff9;
441 
442         let vsock = Vsock::new_for_testing(cid, features);
443         assert_eq!(features, vsock.features());
444     }
445 }
446