Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vhost: support for "standalone" vhost-user daemons #174

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion crates/vhost-user-backend/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::io::Result;
use std::ops::Deref;
use std::sync::{Arc, Mutex, RwLock};

use vhost::vhost_user::message::VhostUserProtocolFeatures;
use vhost::vhost_user::message::{VhostUserBackendSpecs, VhostUserProtocolFeatures};
use vhost::vhost_user::Slave;
use vm_memory::bitmap::Bitmap;
use vmm_sys_util::epoll::EventSet;
Expand Down Expand Up @@ -54,6 +54,9 @@ where
/// Get available vhost protocol features.
fn protocol_features(&self) -> VhostUserProtocolFeatures;

/// Get the backends specs
fn specs(&self) -> VhostUserBackendSpecs;

/// Enable or disable the virtio EVENT_IDX feature
fn set_event_idx(&self, enabled: bool);

Expand Down Expand Up @@ -135,6 +138,9 @@ where
/// Get available vhost protocol features.
fn protocol_features(&self) -> VhostUserProtocolFeatures;

/// Get specs
fn specs(&self) -> VhostUserBackendSpecs;

/// Enable or disable the virtio EVENT_IDX feature
fn set_event_idx(&mut self, enabled: bool);

Expand Down Expand Up @@ -220,6 +226,10 @@ where
self.deref().protocol_features()
}

fn specs(&self) -> VhostUserBackendSpecs {
self.deref().specs()
}

fn set_event_idx(&self, enabled: bool) {
self.deref().set_event_idx(enabled)
}
Expand Down Expand Up @@ -285,6 +295,10 @@ where
self.lock().unwrap().protocol_features()
}

fn specs(&self) -> VhostUserBackendSpecs {
self.lock().unwrap().specs()
}

fn set_event_idx(&self, enabled: bool) {
self.lock().unwrap().set_event_idx(enabled)
}
Expand Down Expand Up @@ -351,6 +365,10 @@ where
self.read().unwrap().protocol_features()
}

fn specs(&self) -> VhostUserBackendSpecs {
self.read().unwrap().specs()
}

fn set_event_idx(&self, enabled: bool) {
self.write().unwrap().set_event_idx(enabled)
}
Expand Down Expand Up @@ -436,6 +454,10 @@ pub mod tests {
VhostUserProtocolFeatures::all()
}

fn specs(&self) -> VhostUserBackendSpecs {
VhostUserBackendSpecs::new(2, 32, 4, 8)
}

fn set_event_idx(&mut self, enabled: bool) {
self.event_idx = enabled;
}
Expand Down
16 changes: 16 additions & 0 deletions crates/vhost-user-backend/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ pub struct VhostUserHandler<S, V, B: Bitmap + 'static> {
atomic_mem: GM<B>,
vrings: Vec<V>,
worker_threads: Vec<thread::JoinHandle<VringEpollResult<()>>>,
/// VirtIO Device Status field, when using get/set status
status: u8,
}

// Ensure VhostUserHandler: Clone + Send + Sync + 'static.
Expand Down Expand Up @@ -147,6 +149,7 @@ where
atomic_mem,
vrings,
worker_threads,
status: 0,
})
}
}
Expand Down Expand Up @@ -452,6 +455,10 @@ where
Ok(())
}

fn specs(&self) -> VhostUserResult<vhost::vhost_user::message::VhostUserBackendSpecs> {
Ok(self.backend.specs())
}

fn get_queue_num(&mut self) -> VhostUserResult<u64> {
Ok(self.num_queues as u64)
}
Expand Down Expand Up @@ -586,6 +593,15 @@ where

Ok(())
}

fn get_status(&self) -> VhostUserResult<u8> {
Ok(self.status)
}

fn set_status(&mut self, status: u8) -> VhostUserResult<()> {
self.status = status;
Ok(())
}
}

impl<S, V, B: Bitmap> Drop for VhostUserHandler<S, V, B> {
Expand Down
7 changes: 6 additions & 1 deletion crates/vhost-user-backend/tests/vhost-user-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use std::sync::{Arc, Barrier, Mutex};
use std::thread;

use vhost::vhost_user::message::{
VhostUserConfigFlags, VhostUserHeaderFlag, VhostUserInflight, VhostUserProtocolFeatures,
VhostUserBackendSpecs, VhostUserConfigFlags, VhostUserHeaderFlag, VhostUserInflight,
VhostUserProtocolFeatures,
};
use vhost::vhost_user::{Listener, Master, Slave, VhostUserMaster};
use vhost::{VhostBackend, VhostUserMemoryRegionInfo, VringConfigData};
Expand Down Expand Up @@ -56,6 +57,10 @@ impl VhostUserBackendMut<VringRwLock, ()> for MockVhostBackend {
VhostUserProtocolFeatures::all()
}

fn specs(&self) -> VhostUserBackendSpecs {
VhostUserBackendSpecs::new(1, 32, 4, 8)
}

fn set_event_idx(&mut self, enabled: bool) {
self.event_idx = enabled;
}
Expand Down
66 changes: 64 additions & 2 deletions crates/vhost/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,30 @@ pub trait VhostBackend: std::marker::Sized {
/// * `queue_index` - Index of the queue to modify.
/// * `fd` - EventFd that will be signaled from guest.
fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()>;

/// Set the status.
/// The status bits follow the same definition of the device
/// status defined in virtio-spec.
///
/// As not all backends can implement this we provide a default
/// implementation that returns an Error.
///
/// # Arguments
/// * `status` - Status bits to set
fn set_status(&self, _status: u8) -> Result<()> {
Err(Error::InvalidOperation)
}

/// Get the status.
///
/// The status bits follow the same definition of the device
/// status defined in virtio-spec.
///
/// As not all backends can implement this we provide a default
/// implementation that returns an Error.
fn get_status(&self) -> Result<u8> {
Err(Error::InvalidOperation)
}
}

/// An interface for setting up vhost-based backend drivers.
Expand Down Expand Up @@ -394,6 +418,17 @@ pub trait VhostBackendMut: std::marker::Sized {
/// * `queue_index` - Index of the queue to modify.
/// * `fd` - EventFd that will be signaled from guest.
fn set_vring_err(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>;

/// Set the status.
/// The status bits follow the same definition of the device status defined in virtio-spec.
///
/// # Arguments
/// * `status` - Status bits to set
fn set_status(&mut self, status: u8) -> Result<()>;

/// Get the status.
/// The status bits follow the same definition of the device status defined in virtio-spec.
fn get_status(&self) -> Result<u8>;
}

impl<T: VhostBackendMut> VhostBackend for RwLock<T> {
Expand Down Expand Up @@ -454,6 +489,14 @@ impl<T: VhostBackendMut> VhostBackend for RwLock<T> {
fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
self.write().unwrap().set_vring_err(queue_index, fd)
}

fn set_status(&self, status: u8) -> Result<()> {
self.write().unwrap().set_status(status)
}

fn get_status(&self) -> Result<u8> {
self.write().unwrap().get_status()
}
}

impl<T: VhostBackendMut> VhostBackend for RefCell<T> {
Expand Down Expand Up @@ -512,6 +555,14 @@ impl<T: VhostBackendMut> VhostBackend for RefCell<T> {
fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
self.borrow_mut().set_vring_err(queue_index, fd)
}

fn set_status(&self, status: u8) -> Result<()> {
self.borrow_mut().set_status(status)
}

fn get_status(&self) -> Result<u8> {
self.borrow_mut().get_status()
}
}

#[cfg(any(test, feature = "test-utils"))]
Expand Down Expand Up @@ -543,7 +594,9 @@ impl VhostUserMemoryRegionInfo {
mod tests {
use super::*;

struct MockBackend {}
struct MockBackend {
status: u8,
}

impl VhostBackendMut for MockBackend {
fn get_features(&mut self) -> Result<u64> {
Expand Down Expand Up @@ -625,11 +678,20 @@ mod tests {
assert_eq!(queue_index, 1);
Ok(())
}

fn set_status(&mut self, status: u8) -> Result<()> {
self.status = status;
Ok(())
}

fn get_status(&self) -> Result<u8> {
Ok(self.status)
}
}

#[test]
fn test_vring_backend_mut() {
let b = RwLock::new(MockBackend {});
let b = RwLock::new(MockBackend { status: 0 });

assert_eq!(b.get_features().unwrap(), 0x1);
b.set_features(0x1).unwrap();
Expand Down
11 changes: 0 additions & 11 deletions crates/vhost/src/vdpa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,6 @@ pub trait VhostVdpa: VhostBackend {
/// The device ids follow the same definition of the device id defined in virtio-spec.
fn get_device_id(&self) -> Result<u32>;

/// Get the status.
/// The status bits follow the same definition of the device status defined in virtio-spec.
fn get_status(&self) -> Result<u8>;

/// Set the status.
/// The status bits follow the same definition of the device status defined in virtio-spec.
///
/// # Arguments
/// * `status` - Status bits to set
fn set_status(&self, status: u8) -> Result<()>;

/// Get the device configuration.
///
/// # Arguments
Expand Down
16 changes: 16 additions & 0 deletions crates/vhost/src/vhost_kern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,22 @@ impl<T: VhostKernBackend> VhostBackend for T {
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ERR(), &vring_file) };
ioctl_result(ret, ())
}

fn get_status(&self) -> Result<u8> {
let mut status: u8 = 0;

// SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its
// return value checked.
let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_STATUS(), &mut status) };
ioctl_result(ret, status)
}

fn set_status(&self, status: u8) -> Result<()> {
// SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its
// return value checked.
let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_STATUS(), &status) };
ioctl_result(ret, ())
}
}

/// Interface to handle in-kernel backend features.
Expand Down
16 changes: 0 additions & 16 deletions crates/vhost/src/vhost_kern/vdpa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,22 +101,6 @@ impl<AS: GuestAddressSpace> VhostVdpa for VhostKernVdpa<AS> {
ioctl_result(ret, device_id)
}

fn get_status(&self) -> Result<u8> {
let mut status: u8 = 0;

// SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its
// return value checked.
let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_STATUS(), &mut status) };
ioctl_result(ret, status)
}

fn set_status(&self, status: u8) -> Result<()> {
// SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its
// return value checked.
let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_STATUS(), &status) };
ioctl_result(ret, ())
}

fn get_config(&self, offset: u32, buffer: &mut [u8]) -> Result<()> {
let mut config = VhostVdpaConfig::new(buffer.len())
.map_err(|_| Error::IoctlError(IOError::from_raw_os_error(libc::ENOMEM)))?;
Expand Down
14 changes: 14 additions & 0 deletions crates/vhost/src/vhost_user/dummy_slave.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct DummySlaveReqHandler {
pub features_acked: bool,
pub acked_features: u64,
pub acked_protocol_features: u64,
pub status: u8,
pub queue_num: usize,
pub vring_num: [u32; MAX_QUEUE_NUM],
pub vring_base: [u32; MAX_QUEUE_NUM],
Expand Down Expand Up @@ -203,6 +204,10 @@ impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler {
Ok(())
}

fn specs(&self) -> Result<VhostUserBackendSpecs> {
Ok(VhostUserBackendSpecs::new(1, 2, 3, 4))
}

fn get_queue_num(&mut self) -> Result<u64> {
Ok(MAX_QUEUE_NUM as u64)
}
Expand Down Expand Up @@ -291,4 +296,13 @@ impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler {
fn remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()> {
Ok(())
}

fn get_status(&self) -> Result<u8> {
Ok(self.status)
}

fn set_status(&mut self, status: u8) -> Result<()> {
self.status = status;
Ok(())
}
}
21 changes: 21 additions & 0 deletions crates/vhost/src/vhost_user/master.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

//! Traits and Struct for vhost-user master.

use std::convert::TryFrom;
use std::fs::File;
use std::mem;
use std::os::unix::io::{AsRawFd, RawFd};
Expand Down Expand Up @@ -330,6 +331,26 @@ impl VhostBackend for Master {
let hdr = node.send_fd_for_vring(MasterReq::SET_VRING_ERR, queue_index, fd.as_raw_fd())?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}

/// Set the status at the remote end (if supported)
fn set_status(&self, status: u8) -> Result<()> {
let mut node = self.node();
// depends on VhostUserProtocolFeatures::STATUS
node.check_proto_feature(VhostUserProtocolFeatures::STATUS)?;
let val = VhostUserU64::new(status.into());
node.send_request_with_body(MasterReq::SET_STATUS, &val, None)?;
Ok(())
}

/// Get the status from the remote end (if supported)
fn get_status(&self) -> Result<u8> {
let mut node = self.node();
// depends on VhostUserProtocolFeatures::STATUS
node.check_proto_feature(VhostUserProtocolFeatures::STATUS)?;
let hdr = node.send_request_header(MasterReq::GET_STATUS, None)?;
let reply = node.recv_reply::<VhostUserU64>(&hdr)?;
u8::try_from(reply.value).or(error_code(VhostUserError::InvalidParam))
}
}

impl VhostUserMaster for Master {
Expand Down
Loading