blob: 402186823bd3ce927548255a64fa38580466c115 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::io::Write;
use std::os::unix::net::UnixStream;
use std::path::Path;
use base::{AsRawDescriptor, Event, Tube};
use vm_memory::GuestMemory;
use vmm_vhost::message::{
MasterReq, VhostUserConfigFlags, VhostUserProtocolFeatures, VhostUserVirtioFeatures,
};
use vmm_vhost::{
connection::socket::Endpoint as SocketEndpoint, Master, VhostBackend, VhostUserMaster,
VhostUserMemoryRegionInfo, VringConfigData,
};
use crate::virtio::vhost::user::vmm::{Error, Result};
use crate::virtio::{Interrupt, Queue};
type SocketMaster = Master<SocketEndpoint<MasterReq>>;
fn set_features(vu: &mut SocketMaster, avail_features: u64, ack_features: u64) -> Result<u64> {
let features = avail_features & ack_features;
vu.set_features(features).map_err(Error::SetFeatures)?;
Ok(features)
}
pub struct VhostUserHandler {
vu: SocketMaster,
pub avail_features: u64,
acked_features: u64,
protocol_features: VhostUserProtocolFeatures,
}
impl VhostUserHandler {
/// Creates a `VhostUserHandler` instance attached to the provided UDS path
/// with features and protocol features initialized.
pub fn new_from_path<P: AsRef<Path>>(
path: P,
max_queue_num: u64,
allow_features: u64,
init_features: u64,
allow_protocol_features: VhostUserProtocolFeatures,
) -> Result<Self> {
Self::new(
SocketMaster::connect(path, max_queue_num)
.map_err(Error::SocketConnectOnMasterCreate)?,
allow_features,
init_features,
allow_protocol_features,
)
}
/// Creates a `VhostUserHandler` instance attached to the provided
/// UnixStream with features and protocol features initialized.
pub fn new_from_stream(
sock: UnixStream,
max_queue_num: u64,
allow_features: u64,
init_features: u64,
allow_protocol_features: VhostUserProtocolFeatures,
) -> Result<Self> {
Self::new(
SocketMaster::from_stream(sock, max_queue_num),
allow_features,
init_features,
allow_protocol_features,
)
}
/// Creates a `VhostUserHandler` instance with features and protocol features initialized.
fn new(
mut vu: SocketMaster,
allow_features: u64,
init_features: u64,
allow_protocol_features: VhostUserProtocolFeatures,
) -> Result<Self> {
vu.set_owner().map_err(Error::SetOwner)?;
let avail_features = allow_features & vu.get_features().map_err(Error::GetFeatures)?;
let acked_features = set_features(&mut vu, avail_features, init_features)?;
let mut protocol_features = VhostUserProtocolFeatures::empty();
if acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() != 0 {
let avail_protocol_features = vu
.get_protocol_features()
.map_err(Error::GetProtocolFeatures)?;
protocol_features = allow_protocol_features & avail_protocol_features;
vu.set_protocol_features(protocol_features)
.map_err(Error::SetProtocolFeatures)?;
}
Ok(VhostUserHandler {
vu,
avail_features,
acked_features,
protocol_features,
})
}
/// Returns a vector of sizes of each queue.
pub fn queue_sizes(&mut self, queue_size: u16, default_queues_num: usize) -> Result<Vec<u16>> {
let queues_num = if self
.protocol_features
.contains(VhostUserProtocolFeatures::MQ)
{
self.vu.get_queue_num().map_err(Error::GetQueueNum)? as usize
} else {
default_queues_num
};
Ok(vec![queue_size; queues_num])
}
/// Enables a set of features.
pub fn ack_features(&mut self, ack_features: u64) -> Result<()> {
let features = set_features(
&mut self.vu,
self.avail_features,
self.acked_features | ack_features,
)?;
self.acked_features = features;
Ok(())
}
/// Gets the device configuration space at `offset` and writes it into `data`.
pub fn read_config<T>(&mut self, offset: u64, mut data: &mut [u8]) -> Result<()> {
let config_len = std::mem::size_of::<T>() as u64;
let data_len = data.len() as u64;
offset
.checked_add(data_len)
.and_then(|l| if l <= config_len { Some(()) } else { None })
.ok_or(Error::InvalidConfigOffset {
data_len,
offset,
config_len,
})?;
let buf = vec![0u8; config_len as usize];
let (_, config) = self
.vu
.get_config(0, config_len as u32, VhostUserConfigFlags::WRITABLE, &buf)
.map_err(Error::GetConfig)?;
data.write_all(
&config[offset as usize..std::cmp::min(data_len + offset, config_len) as usize],
)
.map_err(Error::CopyConfig)
}
/// Writes `data` into the device configuration space at `offset`.
pub fn write_config<T>(&mut self, offset: u64, data: &[u8]) -> Result<()> {
let config_len = std::mem::size_of::<T>() as u64;
let data_len = data.len() as u64;
offset
.checked_add(data_len)
.and_then(|l| if l <= config_len { Some(()) } else { None })
.ok_or(Error::InvalidConfigOffset {
data_len,
offset,
config_len,
})?;
self.vu
.set_config(offset as u32, VhostUserConfigFlags::empty(), data)
.map_err(Error::SetConfig)
}
/// Sets the channel for device-specific messages.
pub fn set_device_request_channel(&mut self, channel: Tube) -> Result<()> {
self.vu
.set_slave_request_fd(&channel)
.map_err(Error::SetDeviceRequestChannel)
}
/// Sets the memory map regions so it can translate the vring addresses.
pub fn set_mem_table(&mut self, mem: &GuestMemory) -> Result<()> {
let mut regions: Vec<VhostUserMemoryRegionInfo> = Vec::new();
mem.with_regions::<_, ()>(
|_idx, guest_phys_addr, memory_size, userspace_addr, mmap, mmap_offset| {
let region = VhostUserMemoryRegionInfo {
guest_phys_addr: guest_phys_addr.0,
memory_size: memory_size as u64,
userspace_addr: userspace_addr as u64,
mmap_offset,
mmap_handle: mmap.as_raw_descriptor(),
};
regions.push(region);
Ok(())
},
)
.unwrap(); // never fail
self.vu
.set_mem_table(regions.as_slice())
.map_err(Error::SetMemTable)?;
Ok(())
}
/// Activates a vring for the given `queue`.
pub fn activate_vring(
&mut self,
mem: &GuestMemory,
queue_index: usize,
queue: &Queue,
queue_evt: &Event,
irqfd: &Event,
) -> Result<()> {
self.vu
.set_vring_num(queue_index, queue.actual_size())
.map_err(Error::SetVringNum)?;
let config_data = VringConfigData {
queue_max_size: queue.max_size,
queue_size: queue.actual_size(),
flags: 0u32,
desc_table_addr: mem
.get_host_address(queue.desc_table)
.map_err(Error::GetHostAddress)? as u64,
used_ring_addr: mem
.get_host_address(queue.used_ring)
.map_err(Error::GetHostAddress)? as u64,
avail_ring_addr: mem
.get_host_address(queue.avail_ring)
.map_err(Error::GetHostAddress)? as u64,
log_addr: None,
};
self.vu
.set_vring_addr(queue_index, &config_data)
.map_err(Error::SetVringAddr)?;
self.vu
.set_vring_base(queue_index, 0)
.map_err(Error::SetVringBase)?;
self.vu
.set_vring_call(queue_index, irqfd)
.map_err(Error::SetVringCall)?;
self.vu
.set_vring_kick(queue_index, queue_evt)
.map_err(Error::SetVringKick)?;
self.vu
.set_vring_enable(queue_index, true)
.map_err(Error::SetVringEnable)?;
Ok(())
}
/// Activates vrings.
pub fn activate(
&mut self,
mem: &GuestMemory,
interrupt: &Interrupt,
queues: &[Queue],
queue_evts: &[Event],
) -> Result<()> {
self.set_mem_table(mem)?;
let msix_config_opt = interrupt
.get_msix_config()
.as_ref()
.ok_or(Error::MsixConfigUnavailable)?;
let msix_config = msix_config_opt.lock();
for (queue_index, queue) in queues.iter().enumerate() {
let queue_evt = &queue_evts[queue_index];
let irqfd = msix_config
.get_irqfd(queue.vector as usize)
.unwrap_or_else(|| interrupt.get_interrupt_evt());
self.activate_vring(mem, queue_index, queue, queue_evt, irqfd)?;
}
Ok(())
}
/// Deactivates all vrings.
pub fn reset(&mut self, queues_num: usize) -> Result<()> {
for queue_index in 0..queues_num {
self.vu
.set_vring_enable(queue_index, false)
.map_err(Error::SetVringEnable)?;
self.vu
.get_vring_base(queue_index)
.map_err(Error::GetVringBase)?;
}
Ok(())
}
}