vhost_vsock.rs0.00%
1
// Copyright 2024 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};16
use std::path::Path;17
use std::sync::Arc;18
use std::sync::atomic::Ordering;19
use std::sync::mpsc::Receiver;20
use std::thread::JoinHandle;21
22
use libc::{EFD_CLOEXEC, EFD_NONBLOCK, eventfd};23
use mio::event::Event;24
use mio::unix::SourceFd;25
use mio::{Interest, Registry, Token};26
use serde::Deserialize;27
use serde_aco::Help;28
29
use crate::ffi;30
use crate::hv::IoeventFd;31
use crate::mem::LayoutUpdated;32
use crate::mem::mapped::RamBus;33
use crate::sync::notifier::Notifier;34
use crate::sys::vhost::{VHOST_FILE_UNBIND, VirtqAddr, VirtqFile, VirtqState};35
use crate::virtio::dev::vsock::{VsockConfig, VsockFeature};36
use crate::virtio::dev::{DevParam, DeviceId, Virtio, WakeEvent};37
use crate::virtio::queue::{QueueReg, VirtQueue};38
use crate::virtio::vhost::{UpdateVsockMem, VhostDev, error};39
use crate::virtio::worker::mio::{ActiveMio, Mio, VirtioMio};40
use crate::virtio::{IrqSender, Result, VirtioFeature};41
42
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Help)]43
pub struct VhostVsockParam {44
/// Vsock context id.45
pub cid: u32,46
/// Path to the host device file. [default: /dev/vhost-vsock]47
pub dev: Option<Box<Path>>,48
}49
50
impl DevParam for VhostVsockParam {51
type Device = VhostVsock;52
53
fn build(self, name: impl Into<Arc<str>>) -> Result<Self::Device> {54
VhostVsock::new(self, name)55
}56
}57
58
#[derive(Debug)]59
pub struct VhostVsock {60
name: Arc<str>,61
vhost_dev: Arc<VhostDev>,62
config: VsockConfig,63
features: u64,64
error_fds: [Option<OwnedFd>; 2],65
}66
67
impl VhostVsock {68
pub fn new(param: VhostVsockParam, name: impl Into<Arc<str>>) -> Result<VhostVsock> {69
let name = name.into();70
let vhost_dev = match param.dev {71
Some(dev) => VhostDev::new(dev),72
None => VhostDev::new("/dev/vhost-vsock"),73
}?;74
vhost_dev.set_owner()?;75
vhost_dev.vsock_set_guest_cid(param.cid as _)?;76
if let Ok(backend_feature) = vhost_dev.get_backend_features() {77
log::debug!("{name}: vhost-vsock backend feature: {backend_feature:x?}");78
vhost_dev.set_backend_features(&backend_feature)?;79
}80
let dev_feat = vhost_dev.get_features()? as u128;81
let known_feat = VirtioFeature::from_bits_truncate(dev_feat).bits()82
| VsockFeature::from_bits_truncate(dev_feat).bits();83
if !VirtioFeature::from_bits_retain(known_feat).contains(VirtioFeature::VERSION_1) {84
return error::VhostMissingDeviceFeature {85
feature: VirtioFeature::VERSION_1.bits(),86
}87
.fail()?;88
}89
Ok(VhostVsock {90
name,91
vhost_dev: Arc::new(vhost_dev),92
config: VsockConfig {93
guest_cid: param.cid,94
..Default::default()95
},96
features: known_feat as u64,97
error_fds: [None, None],98
})99
}100
}101
102
impl Virtio for VhostVsock {103
type Config = VsockConfig;104
type Feature = VsockFeature;105
106
fn id(&self) -> DeviceId {107
DeviceId::SOCKET108
}109
110
fn name(&self) -> &str {111
&self.name112
}113
114
fn num_queues(&self) -> u16 {115
3116
}117
118
fn config(&self) -> Arc<VsockConfig> {119
Arc::new(self.config)120
}121
122
fn feature(&self) -> u128 {123
self.features as u128124
}125
126
fn ioeventfd_offloaded(&self, q_index: u16) -> Result<bool> {127
match q_index {128
0 | 1 => Ok(true),129
_ => Ok(false),130
}131
}132
133
fn mem_update_callback(&self) -> Option<Box<dyn LayoutUpdated>> {134
Some(Box::new(UpdateVsockMem {135
dev: self.vhost_dev.clone(),136
}))137
}138
139
fn spawn_worker<S, E>(140
self,141
event_rx: Receiver<WakeEvent<S, E>>,142
memory: Arc<RamBus>,143
queue_regs: Arc<[QueueReg]>,144
) -> Result<(JoinHandle<()>, Arc<Notifier>)>145
where146
S: IrqSender,147
E: IoeventFd,148
{149
Mio::spawn_worker(self, event_rx, memory, queue_regs)150
}151
}152
153
impl VirtioMio for VhostVsock {154
fn activate<'m, Q, S, E>(155
&mut self,156
feature: u128,157
active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,158
) -> Result<()>159
where160
Q: VirtQueue<'m>,161
S: IrqSender,162
E: IoeventFd,163
{164
self.vhost_dev.set_features(&(feature as u64))?;165
for (index, fd) in active_mio.ioeventfds.iter().take(2).enumerate() {166
let kick = VirtqFile {167
index: index as u32,168
fd: fd.as_fd().as_raw_fd(),169
};170
self.vhost_dev.set_virtq_kick(&kick)?;171
}172
for (index, queue) in active_mio.queues.iter().take(2).enumerate() {173
let Some(queue) = queue else {174
continue;175
};176
let reg = queue.reg();177
let index = index as u32;178
active_mio.irq_sender.queue_irqfd(index as _, |fd| {179
self.vhost_dev.set_virtq_call(&VirtqFile {180
index,181
fd: fd.as_raw_fd(),182
})?;183
Ok(())184
})?;185
186
self.vhost_dev.set_virtq_num(&VirtqState {187
index,188
val: reg.size.load(Ordering::Acquire) as _,189
})?;190
self.vhost_dev191
.set_virtq_base(&VirtqState { index, val: 0 })?;192
let mem = active_mio.mem;193
let virtq_addr = VirtqAddr {194
index,195
flags: 0,196
desc_hva: mem.translate(reg.desc.load(Ordering::Acquire))? as _,197
used_hva: mem.translate(reg.device.load(Ordering::Acquire))? as _,198
avail_hva: mem.translate(reg.driver.load(Ordering::Acquire))? as _,199
log_guest_addr: 0,200
};201
self.vhost_dev.set_virtq_addr(&virtq_addr)?;202
}203
for (index, fd) in self.error_fds.iter_mut().enumerate() {204
let err_fd =205
unsafe { OwnedFd::from_raw_fd(ffi!(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK))?) };206
self.vhost_dev.set_virtq_err(&VirtqFile {207
index: index as u32,208
fd: err_fd.as_raw_fd(),209
})?;210
active_mio.poll.registry().register(211
&mut SourceFd(&err_fd.as_raw_fd()),212
Token(index as _),213
Interest::READABLE,214
)?;215
*fd = Some(err_fd);216
}217
self.vhost_dev.vsock_set_running(true)?;218
Ok(())219
}220
221
fn reset(&mut self, registry: &Registry) {222
self.vhost_dev.vsock_set_running(false).unwrap();223
for (index, error_fd) in self.error_fds.iter_mut().enumerate() {224
let Some(err_fd) = error_fd else {225
continue;226
};227
self.vhost_dev228
.set_virtq_err(&VirtqFile {229
index: index as _,230
fd: VHOST_FILE_UNBIND,231
})232
.unwrap();233
registry234
.deregister(&mut SourceFd(&err_fd.as_raw_fd()))235
.unwrap();236
*error_fd = None;237
}238
}239
240
fn handle_event<'a, 'm, Q, S, E>(241
&mut self,242
event: &Event,243
_active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,244
) -> Result<()>245
where246
Q: VirtQueue<'m>,247
S: IrqSender,248
E: IoeventFd,249
{250
let q_index = event.token();251
error::VhostQueueErr {252
dev: "vsock",253
index: q_index.0 as u16,254
}255
.fail()?;256
Ok(())257
}258
259
fn handle_queue<'m, Q, S, E>(260
&mut self,261
index: u16,262
_active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,263
) -> Result<()>264
where265
Q: VirtQueue<'m>,266
S: IrqSender,267
E: IoeventFd,268
{269
match index {270
0 | 1 => unreachable!("{}: queue 0 and 1 are offloaded to kernel", self.name),271
2 => log::info!("{}: event queue buffer available", self.name),272
_ => unreachable!(),273
}274
Ok(())275
}276
}277
278
impl Drop for VhostVsock {279
fn drop(&mut self) {280
let ret = self.vhost_dev.vsock_set_running(false);281
if let Err(e) = ret {282
log::error!("{}: {e}", self.name)283
}284
}285
}286