• 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::fs::OpenOptions;
6 use std::os::unix::prelude::OpenOptionsExt;
7 
8 use anyhow::anyhow;
9 use anyhow::Context;
10 use base::error;
11 use base::open_file;
12 use base::warn;
13 use base::AsRawDescriptor;
14 use base::Event;
15 use base::RawDescriptor;
16 use base::WorkerThread;
17 use data_model::Le64;
18 use vhost::Vhost;
19 use vhost::Vsock as VhostVsockHandle;
20 use vm_memory::GuestMemory;
21 use zerocopy::AsBytes;
22 
23 use super::worker::Worker;
24 use super::Error;
25 use super::Result;
26 use crate::virtio::copy_config;
27 use crate::virtio::device_constants::vsock::NUM_QUEUES;
28 use crate::virtio::device_constants::vsock::QUEUE_SIZES;
29 use crate::virtio::vsock::VsockConfig;
30 use crate::virtio::DeviceType;
31 use crate::virtio::Interrupt;
32 use crate::virtio::Queue;
33 use crate::virtio::VirtioDevice;
34 use crate::Suspendable;
35 
36 pub struct Vsock {
37     worker_thread: Option<WorkerThread<()>>,
38     vhost_handle: Option<VhostVsockHandle>,
39     cid: u64,
40     interrupts: Option<Vec<Event>>,
41     avail_features: u64,
42     acked_features: u64,
43 }
44 
45 impl Vsock {
46     /// Create a new virtio-vsock device with the given VM cid.
new(base_features: u64, vsock_config: &VsockConfig) -> anyhow::Result<Vsock>47     pub fn new(base_features: u64, vsock_config: &VsockConfig) -> anyhow::Result<Vsock> {
48         let device_file = open_file(
49             &vsock_config.vhost_device,
50             OpenOptions::new()
51                 .read(true)
52                 .write(true)
53                 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK),
54         )
55         .with_context(|| {
56             format!(
57                 "failed to open virtual socket device {}",
58                 vsock_config.vhost_device.display(),
59             )
60         })?;
61 
62         let handle = VhostVsockHandle::new(device_file);
63 
64         let avail_features = base_features;
65 
66         let mut interrupts = Vec::new();
67         for _ in 0..NUM_QUEUES {
68             interrupts.push(Event::new().map_err(Error::VhostIrqCreate)?);
69         }
70 
71         Ok(Vsock {
72             worker_thread: None,
73             vhost_handle: Some(handle),
74             cid: vsock_config.cid,
75             interrupts: Some(interrupts),
76             avail_features,
77             acked_features: 0,
78         })
79     }
80 
new_for_testing(cid: u64, features: u64) -> Vsock81     pub fn new_for_testing(cid: u64, features: u64) -> Vsock {
82         Vsock {
83             worker_thread: None,
84             vhost_handle: None,
85             cid,
86             interrupts: None,
87             avail_features: features,
88             acked_features: 0,
89         }
90     }
91 
acked_features(&self) -> u6492     pub fn acked_features(&self) -> u64 {
93         self.acked_features
94     }
95 }
96 
97 impl VirtioDevice for Vsock {
keep_rds(&self) -> Vec<RawDescriptor>98     fn keep_rds(&self) -> Vec<RawDescriptor> {
99         let mut keep_rds = Vec::new();
100 
101         if let Some(handle) = &self.vhost_handle {
102             keep_rds.push(handle.as_raw_descriptor());
103         }
104 
105         if let Some(interrupt) = &self.interrupts {
106             for vhost_int in interrupt.iter() {
107                 keep_rds.push(vhost_int.as_raw_descriptor());
108             }
109         }
110 
111         keep_rds
112     }
113 
device_type(&self) -> DeviceType114     fn device_type(&self) -> DeviceType {
115         DeviceType::Vsock
116     }
117 
queue_max_sizes(&self) -> &[u16]118     fn queue_max_sizes(&self) -> &[u16] {
119         QUEUE_SIZES
120     }
121 
features(&self) -> u64122     fn features(&self) -> u64 {
123         self.avail_features
124     }
125 
read_config(&self, offset: u64, data: &mut [u8])126     fn read_config(&self, offset: u64, data: &mut [u8]) {
127         let cid = Le64::from(self.cid);
128         copy_config(data, 0, cid.as_bytes(), offset);
129     }
130 
ack_features(&mut self, value: u64)131     fn ack_features(&mut self, value: u64) {
132         let mut v = value;
133 
134         // Check if the guest is ACK'ing a feature that we didn't claim to have.
135         let unrequested_features = v & !self.avail_features;
136         if unrequested_features != 0 {
137             warn!("vsock: virtio-vsock got unknown feature ack: {:x}", v);
138 
139             // Don't count these features as acked.
140             v &= !unrequested_features;
141         }
142         self.acked_features |= v;
143     }
144 
activate( &mut self, mem: GuestMemory, interrupt: Interrupt, mut queues: Vec<(Queue, Event)>, ) -> anyhow::Result<()>145     fn activate(
146         &mut self,
147         mem: GuestMemory,
148         interrupt: Interrupt,
149         mut queues: Vec<(Queue, Event)>,
150     ) -> anyhow::Result<()> {
151         if queues.len() != NUM_QUEUES {
152             return Err(anyhow!(
153                 "net: expected {} queues, got {}",
154                 NUM_QUEUES,
155                 queues.len()
156             ));
157         }
158 
159         let vhost_handle = self.vhost_handle.take().context("missing vhost_handle")?;
160         let interrupts = self.interrupts.take().context("missing interrupts")?;
161         let acked_features = self.acked_features;
162         let cid = self.cid;
163         // The third vq is an event-only vq that is not handled by the vhost
164         // subsystem (but still needs to exist).  Split it off here.
165         let _event_queue = queues.remove(2);
166         let mut worker = Worker::new(
167             queues,
168             vhost_handle,
169             interrupts,
170             interrupt,
171             acked_features,
172             None,
173             self.supports_iommu(),
174         );
175         let activate_vqs = |handle: &VhostVsockHandle| -> Result<()> {
176             handle.set_cid(cid).map_err(Error::VhostVsockSetCid)?;
177             handle.start().map_err(Error::VhostVsockStart)?;
178             Ok(())
179         };
180         worker
181             .init(mem, QUEUE_SIZES, activate_vqs)
182             .context("vsock worker init exited with error")?;
183 
184         self.worker_thread = Some(WorkerThread::start("vhost_vsock", move |kill_evt| {
185             let cleanup_vqs = |_handle: &VhostVsockHandle| -> Result<()> { Ok(()) };
186             let result = worker.run(cleanup_vqs, kill_evt);
187             if let Err(e) = result {
188                 error!("vsock worker thread exited with error: {:?}", e);
189             }
190         }));
191 
192         Ok(())
193     }
194 
on_device_sandboxed(&mut self)195     fn on_device_sandboxed(&mut self) {
196         // ignore the error but to log the error. We don't need to do
197         // anything here because when activate, the other vhost set up
198         // will be failed to stop the activate thread.
199         if let Some(vhost_handle) = &self.vhost_handle {
200             match vhost_handle.set_owner() {
201                 Ok(_) => {}
202                 Err(e) => error!("{}: failed to set owner: {:?}", self.debug_label(), e),
203             }
204         }
205     }
206 }
207 
208 impl Suspendable for Vsock {}
209 
210 #[cfg(test)]
211 mod tests {
212     use std::convert::TryInto;
213 
214     use super::*;
215 
216     #[test]
ack_features()217     fn ack_features() {
218         let cid = 5;
219         let features: u64 = (1 << 20) | (1 << 49) | (1 << 2) | (1 << 19);
220         let mut acked_features: u64 = 0;
221         let mut unavailable_features: u64 = 0;
222 
223         let mut vsock = Vsock::new_for_testing(cid, features);
224         assert_eq!(acked_features, vsock.acked_features());
225 
226         acked_features |= 1 << 2;
227         vsock.ack_features(acked_features);
228         assert_eq!(acked_features, vsock.acked_features());
229 
230         acked_features |= 1 << 49;
231         vsock.ack_features(acked_features);
232         assert_eq!(acked_features, vsock.acked_features());
233 
234         acked_features |= 1 << 60;
235         unavailable_features |= 1 << 60;
236         vsock.ack_features(acked_features);
237         assert_eq!(
238             acked_features & !unavailable_features,
239             vsock.acked_features()
240         );
241 
242         acked_features |= 1 << 1;
243         unavailable_features |= 1 << 1;
244         vsock.ack_features(acked_features);
245         assert_eq!(
246             acked_features & !unavailable_features,
247             vsock.acked_features()
248         );
249     }
250 
251     #[test]
read_config()252     fn read_config() {
253         let cid = 0xfca9a559fdcb9756;
254         let vsock = Vsock::new_for_testing(cid, 0);
255 
256         let mut buf = [0u8; 8];
257         vsock.read_config(0, &mut buf);
258         assert_eq!(cid, u64::from_le_bytes(buf));
259 
260         vsock.read_config(0, &mut buf[..4]);
261         assert_eq!(
262             (cid & 0xffffffff) as u32,
263             u32::from_le_bytes(buf[..4].try_into().unwrap())
264         );
265 
266         vsock.read_config(4, &mut buf[..4]);
267         assert_eq!(
268             (cid >> 32) as u32,
269             u32::from_le_bytes(buf[..4].try_into().unwrap())
270         );
271 
272         let data: [u8; 8] = [8, 226, 5, 46, 159, 59, 89, 77];
273         buf.copy_from_slice(&data);
274 
275         vsock.read_config(12, &mut buf);
276         assert_eq!(&buf, &data);
277     }
278 
279     #[test]
features()280     fn features() {
281         let cid = 5;
282         let features: u64 = 0xfc195ae8db88cff9;
283 
284         let vsock = Vsock::new_for_testing(cid, features);
285         assert_eq!(features, vsock.features());
286     }
287 }
288