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