vhost: Adopt new backend naming

Following vhost-user specification, replace
all uses of master/slave with backend/frontend
in the vhost crate.

Signed-off-by: Albert Esteve <aesteve@redhat.com>
This commit is contained in:
Albert Esteve 2023-09-08 16:14:12 +02:00 committed by Stefano Garzarella
parent 5c9bef626c
commit 4db81adcd2
19 changed files with 828 additions and 811 deletions

View file

@ -8,15 +8,15 @@
]
},
{
"test_name": "build-vhost-user-master",
"command": "cargo build --features=vhost-user-master",
"test_name": "build-vhost-user-frontend",
"command": "cargo build --features=vhost-user-frontend",
"platform": [
"x86_64"
]
},
{
"test_name": "build-vhost-user-slave",
"command": "cargo build --features=vhost-user-slave",
"test_name": "build-vhost-user-backend",
"command": "cargo build --features=vhost-user-backend",
"platform": [
"x86_64"
]

View file

@ -1,5 +1,5 @@
{
"coverage_score": 39.8,
"exclude_path": "",
"crate_features": "vhost/vhost-vsock,vhost/vhost-kern,vhost/vhost-user-master,vhost/vhost-user-slave"
"crate_features": "vhost/vhost-vsock,vhost/vhost-kern,vhost/vhost-user-frontend,vhost/vhost-user-backend"
}

View file

@ -1,5 +1,5 @@
{
"coverage_score": 84.0,
"exclude_path": "vhost/src/vhost_kern/",
"crate_features": "vhost/vhost-user-master,vhost/vhost-user-slave"
"crate_features": "vhost/vhost-user-frontend,vhost/vhost-user-backend"
}

View file

@ -14,7 +14,7 @@ xen = ["vm-memory/xen", "vhost/xen"]
[dependencies]
libc = "0.2.39"
log = "0.4.17"
vhost = { path = "../vhost", version = "0.8", features = ["vhost-user-slave"] }
vhost = { path = "../vhost", version = "0.8", features = ["vhost-user-backend"] }
virtio-bindings = "0.2.1"
virtio-queue = "0.9.0"
vm-memory = { version = "0.12.0", features = ["backend-mmap", "backend-atomic"] }
@ -22,6 +22,6 @@ vmm-sys-util = "0.11.0"
[dev-dependencies]
nix = "0.26"
vhost = { path = "../vhost", version = "0.8", features = ["test-utils", "vhost-user-master", "vhost-user-slave"] }
vhost = { path = "../vhost", version = "0.8", features = ["test-utils", "vhost-user-frontend", "vhost-user-backend"] }
vm-memory = { version = "0.12.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
tempfile = "3.2.0"

View file

@ -4,6 +4,7 @@
### Added
### Changed
- Change uses of master/slave for frontend/backend in the codebase.
### Fixed

View file

@ -21,8 +21,8 @@ vhost-kern = []
vhost-vdpa = ["vhost-kern"]
vhost-net = ["vhost-kern"]
vhost-user = []
vhost-user-master = ["vhost-user"]
vhost-user-slave = ["vhost-user"]
vhost-user-frontend = ["vhost-user"]
vhost-user-backend = ["vhost-user"]
xen = ["vm-memory/xen"]
[dependencies]

View file

@ -26,9 +26,9 @@ to establish virtqueue sharing with a user space process on the same host.
It uses communication over a Unix domain socket to share file descriptors in
the ancillary data of the message.
The protocol defines two sides of the communication, master and slave.
Master is the application that shares its virtqueues, slave is the consumer
of the virtqueues. Master and slave can be either a client (i.e. connecting)
The protocol defines two sides of the communication, frontend and backend.
Frontend is the application that shares its virtqueues, backend is the consumer
of the virtqueues. Frontend and backend can be either a client (i.e. connecting)
or server (listening) in the socket communication.
## Xen support

View file

@ -13,10 +13,10 @@
<mxCell id="44" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;sketch=0;fontSize=25;fontColor=#FF00FF;fillColor=none;strokeColor=#4D4D4D;strokeWidth=5;" vertex="1" parent="1">
<mxGeometry x="-10" y="37" width="1250" height="670" as="geometry"/>
</mxCell>
<mxCell id="2" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;MasterReqHandler&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
<mxCell id="2" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;FrontendReqHandler&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
<mxGeometry x="830" y="477" width="220" height="50" as="geometry"/>
</mxCell>
<mxCell id="4" value="&lt;pre style=&quot;font-size: 16.5pt; font-weight: 700; font-family: &amp;quot;jetbrains mono&amp;quot;, monospace;&quot;&gt;VhostUserMasterReqHandler&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
<mxCell id="4" value="&lt;pre style=&quot;font-size: 16.5pt; font-weight: 700; font-family: &amp;quot;jetbrains mono&amp;quot;, monospace;&quot;&gt;VhostUserFrontendReqHandler&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
<mxGeometry x="840" y="597" width="360" height="60" as="geometry"/>
</mxCell>
<mxCell id="6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" edge="1" parent="1" source="5" target="2">
@ -27,10 +27,10 @@
</Array>
</mxGeometry>
</mxCell>
<mxCell id="5" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;Slave&lt;/pre&gt;&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
<mxCell id="5" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;Backend&lt;/pre&gt;&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
<mxGeometry x="1715" y="767" width="220" height="50" as="geometry"/>
</mxCell>
<mxCell id="7" value="&lt;pre style=&quot;font-size: 16.5pt; font-weight: 700; font-family: &amp;quot;jetbrains mono&amp;quot;, monospace;&quot;&gt;VhostUserMasterReqHandlerMut&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxCell id="7" value="&lt;pre style=&quot;font-size: 16.5pt; font-weight: 700; font-family: &amp;quot;jetbrains mono&amp;quot;, monospace;&quot;&gt;VhostUserFrontendReqHandlerMut&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxGeometry x="1630" y="657" width="390" height="60" as="geometry"/>
</mxCell>
<mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" edge="1" parent="1" source="2" target="4">
@ -39,13 +39,13 @@
<mxPoint x="680" y="717" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="10" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;SlaveListener&lt;/pre&gt;&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxCell id="10" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;BackendListener&lt;/pre&gt;&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxGeometry x="1360" y="472" width="190" height="50" as="geometry"/>
</mxCell>
<mxCell id="11" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;SlaveReqHandler&lt;/pre&gt;&lt;/pre&gt;&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxCell id="11" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;BackendReqHandler&lt;/pre&gt;&lt;/pre&gt;&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxGeometry x="1712" y="387" width="210" height="50" as="geometry"/>
</mxCell>
<mxCell id="14" value="&lt;pre style=&quot;font-size: 16.5pt; font-weight: 700; font-family: &amp;quot;jetbrains mono&amp;quot;, monospace;&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;VhostUserSlaveReqHandler&lt;/pre&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxCell id="14" value="&lt;pre style=&quot;font-size: 16.5pt; font-weight: 700; font-family: &amp;quot;jetbrains mono&amp;quot;, monospace;&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;VhostUserBackendReqHandler&lt;/pre&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxGeometry x="1652" y="537" width="330" height="60" as="geometry"/>
</mxCell>
<mxCell id="15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" edge="1" parent="1" source="11" target="14">
@ -63,7 +63,7 @@
<mxCell id="18" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot;, monospace; font-size: 16.5pt;&quot;&gt;VhostVdpaBackend&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#808080;strokeColor=#808080;" vertex="1" parent="1">
<mxGeometry x="270" y="387" width="220" height="50" as="geometry"/>
</mxCell>
<mxCell id="19" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;Master&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxCell id="19" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;Frontend&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxGeometry x="820" y="387" width="220" height="50" as="geometry"/>
</mxCell>
<mxCell id="20" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;VhostSoftBackend&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#808080;strokeColor=#808080;" vertex="1" parent="1">
@ -126,7 +126,7 @@
<mxCell id="36" value="&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;Message&lt;/pre&gt;&lt;/pre&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
<mxGeometry x="1360" y="632" width="190" height="50" as="geometry"/>
</mxCell>
<mxCell id="37" value="&lt;pre style=&quot;font-size: 16.5pt ; font-weight: 700 ; font-family: &amp;quot;jetbrains mono&amp;quot; , monospace&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;VhostUserMaster&lt;/pre&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;strokeColor=#FF33FF;fontColor=#FF00FF;" vertex="1" parent="1">
<mxCell id="37" value="&lt;pre style=&quot;font-size: 16.5pt ; font-weight: 700 ; font-family: &amp;quot;jetbrains mono&amp;quot; , monospace&quot;&gt;&lt;pre style=&quot;font-family: &amp;quot;jetbrains mono&amp;quot; , monospace ; font-size: 16.5pt&quot;&gt;VhostUserFrontend&lt;/pre&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;strokeColor=#FF33FF;fontColor=#FF00FF;" vertex="1" parent="1">
<mxGeometry x="980" y="257" width="230" height="60" as="geometry"/>
</mxCell>
<mxCell id="38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;labelBackgroundColor=none;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeColor=#00994D;" edge="1" parent="1" source="19" target="16">

View file

@ -26,8 +26,8 @@
//! vhost implementation in the Linux kernel. It implements the control plane needed to establish
//! virtqueues sharing with a user space process on the same host. It uses communication over a
//! Unix domain socket to share file descriptors in the ancillary data of the message.
//! The protocol defines 2 sides of the communication, master and slave. Master is the application
//! that shares its virtqueues. Slave is the consumer of the virtqueues. Master and slave can be
//! The protocol defines 2 sides of the communication, frontend and backend. Frontend is the application
//! that shares its virtqueues. Backend is the consumer of the virtqueues. Frontend and backend can be
//! either a client (i.e. connecting) or server (listening) in the socket communication.
#![deny(missing_docs)]

View file

@ -0,0 +1,86 @@
// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! Traits and Structs for vhost-user backend.
use std::sync::Arc;
use super::connection::{Endpoint, Listener};
use super::message::*;
use super::{BackendReqHandler, Result, VhostUserBackendReqHandler};
/// Vhost-user backend side connection listener.
pub struct BackendListener<S: VhostUserBackendReqHandler> {
listener: Listener,
backend: Option<Arc<S>>,
}
/// Sets up a listener for incoming frontend connections, and handles construction
/// of a Backend on success.
impl<S: VhostUserBackendReqHandler> BackendListener<S> {
/// Create a unix domain socket for incoming frontend connections.
pub fn new(listener: Listener, backend: Arc<S>) -> Result<Self> {
Ok(BackendListener {
listener,
backend: Some(backend),
})
}
/// Accept an incoming connection from the frontend, returning Some(Backend) on
/// success, or None if the socket is nonblocking and no incoming connection
/// was detected
pub fn accept(&mut self) -> Result<Option<BackendReqHandler<S>>> {
if let Some(fd) = self.listener.accept()? {
return Ok(Some(BackendReqHandler::new(
Endpoint::<FrontendReq>::from_stream(fd),
self.backend.take().unwrap(),
)));
}
Ok(None)
}
/// Change blocking status on the listener.
pub fn set_nonblocking(&self, block: bool) -> Result<()> {
self.listener.set_nonblocking(block)
}
}
#[cfg(test)]
mod tests {
use std::sync::Mutex;
use super::*;
use crate::vhost_user::dummy_backend::DummyBackendReqHandler;
#[test]
fn test_backend_listener_set_nonblocking() {
let backend = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
let listener =
Listener::new("/tmp/vhost_user_lib_unit_test_backend_nonblocking", true).unwrap();
let backend_listener = BackendListener::new(listener, backend).unwrap();
backend_listener.set_nonblocking(true).unwrap();
backend_listener.set_nonblocking(false).unwrap();
backend_listener.set_nonblocking(false).unwrap();
backend_listener.set_nonblocking(true).unwrap();
backend_listener.set_nonblocking(true).unwrap();
}
#[cfg(feature = "vhost-user-frontend")]
#[test]
fn test_backend_listener_accept() {
use super::super::Frontend;
let path = "/tmp/vhost_user_lib_unit_test_backend_accept";
let backend = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
let listener = Listener::new(path, true).unwrap();
let mut backend_listener = BackendListener::new(listener, backend).unwrap();
backend_listener.set_nonblocking(true).unwrap();
assert!(backend_listener.accept().unwrap().is_none());
assert!(backend_listener.accept().unwrap().is_none());
let _frontend = Frontend::connect(path, 1).unwrap();
let _backend = backend_listener.accept().unwrap().unwrap();
}
}

View file

@ -9,12 +9,12 @@ use std::sync::{Arc, Mutex, MutexGuard};
use super::connection::Endpoint;
use super::message::*;
use super::{Error, HandlerResult, Result, VhostUserMasterReqHandler};
use super::{Error, HandlerResult, Result, VhostUserFrontendReqHandler};
use vm_memory::ByteValued;
struct SlaveInternal {
sock: Endpoint<SlaveReq>,
struct BackendInternal {
sock: Endpoint<BackendReq>,
// Protocol feature VHOST_USER_PROTOCOL_F_REPLY_ACK has been negotiated.
reply_ack_negotiated: bool,
@ -23,7 +23,7 @@ struct SlaveInternal {
error: Option<i32>,
}
impl SlaveInternal {
impl BackendInternal {
fn check_state(&self) -> Result<u64> {
match self.error {
Some(e) => Err(Error::SocketBroken(std::io::Error::from_raw_os_error(e))),
@ -33,7 +33,7 @@ impl SlaveInternal {
fn send_message<T: ByteValued>(
&mut self,
request: SlaveReq,
request: BackendReq,
body: &T,
fds: Option<&[RawFd]>,
) -> Result<u64> {
@ -49,7 +49,7 @@ impl SlaveInternal {
self.wait_for_ack(&hdr)
}
fn wait_for_ack(&mut self, hdr: &VhostUserMsgHeader<SlaveReq>) -> Result<u64> {
fn wait_for_ack(&mut self, hdr: &VhostUserMsgHeader<BackendReq>) -> Result<u64> {
self.check_state()?;
if !self.reply_ack_negotiated {
return Ok(0);
@ -60,32 +60,32 @@ impl SlaveInternal {
return Err(Error::InvalidMessage);
}
if body.value != 0 {
return Err(Error::MasterInternalError);
return Err(Error::FrontendInternalError);
}
Ok(body.value)
}
}
/// Request proxy to send vhost-user slave requests to the master through the slave
/// Request proxy to send vhost-user backend requests to the frontend through the backend
/// communication channel.
///
/// The [Slave] acts as a message proxy to forward vhost-user slave requests to the
/// master through the vhost-user slave communication channel. The forwarded messages will be
/// handled by the [MasterReqHandler] server.
/// The [Backend] acts as a message proxy to forward vhost-user backend requests to the
/// frontend through the vhost-user backend communication channel. The forwarded messages will be
/// handled by the [FrontendReqHandler] server.
///
/// [Slave]: struct.Slave.html
/// [MasterReqHandler]: struct.MasterReqHandler.html
/// [Backend]: struct.Backend.html
/// [FrontendReqHandler]: struct.FrontendReqHandler.html
#[derive(Clone)]
pub struct Slave {
pub struct Backend {
// underlying Unix domain socket for communication
node: Arc<Mutex<SlaveInternal>>,
node: Arc<Mutex<BackendInternal>>,
}
impl Slave {
fn new(ep: Endpoint<SlaveReq>) -> Self {
Slave {
node: Arc::new(Mutex::new(SlaveInternal {
impl Backend {
fn new(ep: Endpoint<BackendReq>) -> Self {
Backend {
node: Arc::new(Mutex::new(BackendInternal {
sock: ep,
reply_ack_negotiated: false,
error: None,
@ -93,13 +93,13 @@ impl Slave {
}
}
fn node(&self) -> MutexGuard<SlaveInternal> {
fn node(&self) -> MutexGuard<BackendInternal> {
self.node.lock().unwrap()
}
fn send_message<T: ByteValued>(
&self,
request: SlaveReq,
request: BackendReq,
body: &T,
fds: Option<&[RawFd]>,
) -> io::Result<u64> {
@ -110,13 +110,13 @@ impl Slave {
/// Create a new instance from a `UnixStream` object.
pub fn from_stream(sock: UnixStream) -> Self {
Self::new(Endpoint::<SlaveReq>::from_stream(sock))
Self::new(Endpoint::<BackendReq>::from_stream(sock))
}
/// Set the negotiation state of the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature.
///
/// When the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature has been negotiated,
/// the "REPLY_ACK" flag will be set in the message header for every slave to master request
/// the "REPLY_ACK" flag will be set in the message header for every backend to frontend request
/// message.
pub fn set_reply_ack_flag(&self, enable: bool) {
self.node().reply_ack_negotiated = enable;
@ -128,15 +128,15 @@ impl Slave {
}
}
impl VhostUserMasterReqHandler for Slave {
/// Forward vhost-user-fs map file requests to the slave.
fn fs_slave_map(&self, fs: &VhostUserFSSlaveMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> {
self.send_message(SlaveReq::FS_MAP, fs, Some(&[fd.as_raw_fd()]))
impl VhostUserFrontendReqHandler for Backend {
/// Forward vhost-user-fs map file requests to the backend.
fn fs_backend_map(&self, fs: &VhostUserFSBackendMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> {
self.send_message(BackendReq::FS_MAP, fs, Some(&[fd.as_raw_fd()]))
}
/// Forward vhost-user-fs unmap file requests to the master.
fn fs_slave_unmap(&self, fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
self.send_message(SlaveReq::FS_UNMAP, fs, None)
/// Forward vhost-user-fs unmap file requests to the frontend.
fn fs_backend_unmap(&self, fs: &VhostUserFSBackendMsg) -> HandlerResult<u64> {
self.send_message(BackendReq::FS_UNMAP, fs, None)
}
}
@ -147,73 +147,73 @@ mod tests {
use super::*;
#[test]
fn test_slave_req_set_failed() {
fn test_backend_req_set_failed() {
let (p1, _p2) = UnixStream::pair().unwrap();
let slave = Slave::from_stream(p1);
let backend = Backend::from_stream(p1);
assert!(slave.node().error.is_none());
slave.set_failed(libc::EAGAIN);
assert_eq!(slave.node().error, Some(libc::EAGAIN));
assert!(backend.node().error.is_none());
backend.set_failed(libc::EAGAIN);
assert_eq!(backend.node().error, Some(libc::EAGAIN));
}
#[test]
fn test_slave_req_send_failure() {
fn test_backend_req_send_failure() {
let (p1, p2) = UnixStream::pair().unwrap();
let slave = Slave::from_stream(p1);
let backend = Backend::from_stream(p1);
slave.set_failed(libc::ECONNRESET);
slave
.fs_slave_map(&VhostUserFSSlaveMsg::default(), &p2)
backend.set_failed(libc::ECONNRESET);
backend
.fs_backend_map(&VhostUserFSBackendMsg::default(), &p2)
.unwrap_err();
slave
.fs_slave_unmap(&VhostUserFSSlaveMsg::default())
backend
.fs_backend_unmap(&VhostUserFSBackendMsg::default())
.unwrap_err();
slave.node().error = None;
backend.node().error = None;
}
#[test]
fn test_slave_req_recv_negative() {
fn test_backend_req_recv_negative() {
let (p1, p2) = UnixStream::pair().unwrap();
let slave = Slave::from_stream(p1);
let mut master = Endpoint::<SlaveReq>::from_stream(p2);
let backend = Backend::from_stream(p1);
let mut frontend = Endpoint::<BackendReq>::from_stream(p2);
let len = mem::size_of::<VhostUserFSSlaveMsg>();
let len = mem::size_of::<VhostUserFSBackendMsg>();
let mut hdr = VhostUserMsgHeader::new(
SlaveReq::FS_MAP,
BackendReq::FS_MAP,
VhostUserHeaderFlag::REPLY.bits(),
len as u32,
);
let body = VhostUserU64::new(0);
master
.send_message(&hdr, &body, Some(&[master.as_raw_fd()]))
frontend
.send_message(&hdr, &body, Some(&[frontend.as_raw_fd()]))
.unwrap();
slave
.fs_slave_map(&VhostUserFSSlaveMsg::default(), &master)
backend
.fs_backend_map(&VhostUserFSBackendMsg::default(), &frontend)
.unwrap();
slave.set_reply_ack_flag(true);
slave
.fs_slave_map(&VhostUserFSSlaveMsg::default(), &master)
backend.set_reply_ack_flag(true);
backend
.fs_backend_map(&VhostUserFSBackendMsg::default(), &frontend)
.unwrap_err();
hdr.set_code(SlaveReq::FS_UNMAP);
master.send_message(&hdr, &body, None).unwrap();
slave
.fs_slave_map(&VhostUserFSSlaveMsg::default(), &master)
hdr.set_code(BackendReq::FS_UNMAP);
frontend.send_message(&hdr, &body, None).unwrap();
backend
.fs_backend_map(&VhostUserFSBackendMsg::default(), &frontend)
.unwrap_err();
hdr.set_code(SlaveReq::FS_MAP);
hdr.set_code(BackendReq::FS_MAP);
let body = VhostUserU64::new(1);
master.send_message(&hdr, &body, None).unwrap();
slave
.fs_slave_map(&VhostUserFSSlaveMsg::default(), &master)
frontend.send_message(&hdr, &body, None).unwrap();
backend
.fs_backend_map(&VhostUserFSBackendMsg::default(), &frontend)
.unwrap_err();
let body = VhostUserU64::new(0);
master.send_message(&hdr, &body, None).unwrap();
slave
.fs_slave_map(&VhostUserFSSlaveMsg::default(), &master)
frontend.send_message(&hdr, &body, None).unwrap();
backend
.fs_backend_map(&VhostUserFSBackendMsg::default(), &frontend)
.unwrap();
}
}

View file

@ -10,33 +10,33 @@ use std::sync::{Arc, Mutex};
use vm_memory::ByteValued;
use super::backend_req::Backend;
use super::connection::Endpoint;
use super::message::*;
use super::slave_req::Slave;
use super::{take_single_file, Error, Result};
/// Services provided to the master by the slave with interior mutability.
/// Services provided to the frontend by the backend with interior mutability.
///
/// The [VhostUserSlaveReqHandler] trait defines the services provided to the master by the slave.
/// And the [VhostUserSlaveReqHandlerMut] trait is a helper mirroring [VhostUserSlaveReqHandler],
/// The [VhostUserBackendReqHandler] trait defines the services provided to the frontend by the backend.
/// And the [VhostUserBackendReqHandlerMut] trait is a helper mirroring [VhostUserBackendReqHandler],
/// but without interior mutability.
/// The vhost-user specification defines a master communication channel, by which masters could
/// request services from slaves. The [VhostUserSlaveReqHandler] trait defines services provided by
/// slaves, and it's used both on the master side and slave side.
/// The vhost-user specification defines a frontend communication channel, by which frontends could
/// request services from backends. The [VhostUserBackendReqHandler] trait defines services provided by
/// backends, and it's used both on the frontend side and backend side.
///
/// - on the master side, a stub forwarder implementing [VhostUserSlaveReqHandler] will proxy
/// service requests to slaves.
/// - on the slave side, the [SlaveReqHandler] will forward service requests to a handler
/// implementing [VhostUserSlaveReqHandler].
/// - on the frontend side, a stub forwarder implementing [VhostUserBackendReqHandler] will proxy
/// service requests to backends.
/// - on the backend side, the [BackendReqHandler] will forward service requests to a handler
/// implementing [VhostUserBackendReqHandler].
///
/// The [VhostUserSlaveReqHandler] trait is design with interior mutability to improve performance
/// The [VhostUserBackendReqHandler] trait is design with interior mutability to improve performance
/// for multi-threading.
///
/// [VhostUserSlaveReqHandler]: trait.VhostUserSlaveReqHandler.html
/// [VhostUserSlaveReqHandlerMut]: trait.VhostUserSlaveReqHandlerMut.html
/// [SlaveReqHandler]: struct.SlaveReqHandler.html
/// [VhostUserBackendReqHandler]: trait.VhostUserBackendReqHandler.html
/// [VhostUserBackendReqHandlerMut]: trait.VhostUserBackendReqHandlerMut.html
/// [BackendReqHandler]: struct.BackendReqHandler.html
#[allow(missing_docs)]
pub trait VhostUserSlaveReqHandler {
pub trait VhostUserBackendReqHandler {
fn set_owner(&self) -> Result<()>;
fn reset_owner(&self) -> Result<()>;
fn get_features(&self) -> Result<u64>;
@ -64,7 +64,7 @@ pub trait VhostUserSlaveReqHandler {
fn set_vring_enable(&self, index: u32, enable: bool) -> Result<()>;
fn get_config(&self, offset: u32, size: u32, flags: VhostUserConfigFlags) -> Result<Vec<u8>>;
fn set_config(&self, offset: u32, buf: &[u8], flags: VhostUserConfigFlags) -> Result<()>;
fn set_slave_req_fd(&self, _slave: Slave) {}
fn set_backend_req_fd(&self, _backend: Backend) {}
fn get_inflight_fd(&self, inflight: &VhostUserInflight) -> Result<(VhostUserInflight, File)>;
fn set_inflight_fd(&self, inflight: &VhostUserInflight, file: File) -> Result<()>;
fn get_max_mem_slots(&self) -> Result<u64>;
@ -72,11 +72,11 @@ pub trait VhostUserSlaveReqHandler {
fn remove_mem_region(&self, region: &VhostUserSingleMemoryRegion) -> Result<()>;
}
/// Services provided to the master by the slave without interior mutability.
/// Services provided to the frontend by the backend without interior mutability.
///
/// This is a helper trait mirroring the [VhostUserSlaveReqHandler] trait.
/// This is a helper trait mirroring the [VhostUserBackendReqHandler] trait.
#[allow(missing_docs)]
pub trait VhostUserSlaveReqHandlerMut {
pub trait VhostUserBackendReqHandlerMut {
fn set_owner(&mut self) -> Result<()>;
fn reset_owner(&mut self) -> Result<()>;
fn get_features(&mut self) -> Result<u64>;
@ -109,7 +109,7 @@ pub trait VhostUserSlaveReqHandlerMut {
flags: VhostUserConfigFlags,
) -> Result<Vec<u8>>;
fn set_config(&mut self, offset: u32, buf: &[u8], flags: VhostUserConfigFlags) -> Result<()>;
fn set_slave_req_fd(&mut self, _slave: Slave) {}
fn set_backend_req_fd(&mut self, _backend: Backend) {}
fn get_inflight_fd(
&mut self,
inflight: &VhostUserInflight,
@ -120,7 +120,7 @@ pub trait VhostUserSlaveReqHandlerMut {
fn remove_mem_region(&mut self, region: &VhostUserSingleMemoryRegion) -> Result<()>;
}
impl<T: VhostUserSlaveReqHandlerMut> VhostUserSlaveReqHandler for Mutex<T> {
impl<T: VhostUserBackendReqHandlerMut> VhostUserBackendReqHandler for Mutex<T> {
fn set_owner(&self) -> Result<()> {
self.lock().unwrap().set_owner()
}
@ -203,8 +203,8 @@ impl<T: VhostUserSlaveReqHandlerMut> VhostUserSlaveReqHandler for Mutex<T> {
self.lock().unwrap().set_config(offset, buf, flags)
}
fn set_slave_req_fd(&self, slave: Slave) {
self.lock().unwrap().set_slave_req_fd(slave)
fn set_backend_req_fd(&self, backend: Backend) {
self.lock().unwrap().set_backend_req_fd(backend)
}
fn get_inflight_fd(&self, inflight: &VhostUserInflight) -> Result<(VhostUserInflight, File)> {
@ -228,20 +228,20 @@ impl<T: VhostUserSlaveReqHandlerMut> VhostUserSlaveReqHandler for Mutex<T> {
}
}
/// Server to handle service requests from masters from the master communication channel.
/// Server to handle service requests from frontends from the frontend communication channel.
///
/// The [SlaveReqHandler] acts as a server on the slave side, to handle service requests from
/// masters on the master communication channel. It's actually a proxy invoking the registered
/// handler implementing [VhostUserSlaveReqHandler] to do the real work.
/// The [BackendReqHandler] acts as a server on the backend side, to handle service requests from
/// frontends on the frontend communication channel. It's actually a proxy invoking the registered
/// handler implementing [VhostUserBackendReqHandler] to do the real work.
///
/// The lifetime of the SlaveReqHandler object should be the same as the underline Unix Domain
/// The lifetime of the BackendReqHandler object should be the same as the underline Unix Domain
/// Socket, so it gets simpler to recover from disconnect.
///
/// [VhostUserSlaveReqHandler]: trait.VhostUserSlaveReqHandler.html
/// [SlaveReqHandler]: struct.SlaveReqHandler.html
pub struct SlaveReqHandler<S: VhostUserSlaveReqHandler> {
/// [VhostUserBackendReqHandler]: trait.VhostUserBackendReqHandler.html
/// [BackendReqHandler]: struct.BackendReqHandler.html
pub struct BackendReqHandler<S: VhostUserBackendReqHandler> {
// underlying Unix domain socket for communication
main_sock: Endpoint<MasterReq>,
main_sock: Endpoint<FrontendReq>,
// the vhost-user backend device object
backend: Arc<S>,
@ -256,10 +256,10 @@ pub struct SlaveReqHandler<S: VhostUserSlaveReqHandler> {
error: Option<i32>,
}
impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
/// Create a vhost-user slave endpoint.
pub(super) fn new(main_sock: Endpoint<MasterReq>, backend: Arc<S>) -> Self {
SlaveReqHandler {
impl<S: VhostUserBackendReqHandler> BackendReqHandler<S> {
/// Create a vhost-user backend endpoint.
pub(super) fn new(main_sock: Endpoint<FrontendReq>, backend: Arc<S>) -> Self {
BackendReqHandler {
main_sock,
backend,
virtio_features: 0,
@ -287,18 +287,18 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
}
}
/// Create a vhost-user slave endpoint from a connected socket.
/// Create a vhost-user backend endpoint from a connected socket.
pub fn from_stream(socket: UnixStream, backend: Arc<S>) -> Self {
Self::new(Endpoint::from_stream(socket), backend)
}
/// Create a new vhost-user slave endpoint.
/// Create a new vhost-user backend endpoint.
///
/// # Arguments
/// * - `path` - path of Unix domain socket listener to connect to
/// * - `backend` - handler for requests from the master to the slave
/// * - `backend` - handler for requests from the frontend to the backend
pub fn connect(path: &str, backend: Arc<S>) -> Result<Self> {
Ok(Self::new(Endpoint::<MasterReq>::connect(path)?, backend))
Ok(Self::new(Endpoint::<FrontendReq>::connect(path)?, backend))
}
/// Mark endpoint as failed with specified error code.
@ -306,9 +306,9 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
self.error = Some(error);
}
/// Main entrance to server slave request from the slave communication channel.
/// Main entrance to server backend request from the backend communication channel.
///
/// Receive and handle one incoming request message from the master. The caller needs to:
/// Receive and handle one incoming request message from the frontend. The caller needs to:
/// - serialize calls to this function
/// - decide what to do when error happens
/// - optional recover from failure
@ -340,17 +340,17 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
};
match hdr.get_code() {
Ok(MasterReq::SET_OWNER) => {
Ok(FrontendReq::SET_OWNER) => {
self.check_request_size(&hdr, size, 0)?;
let res = self.backend.set_owner();
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::RESET_OWNER) => {
Ok(FrontendReq::RESET_OWNER) => {
self.check_request_size(&hdr, size, 0)?;
let res = self.backend.reset_owner();
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::GET_FEATURES) => {
Ok(FrontendReq::GET_FEATURES) => {
self.check_request_size(&hdr, size, 0)?;
let features = self.backend.get_features()?;
let msg = VhostUserU64::new(features);
@ -358,23 +358,23 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
self.virtio_features = features;
self.update_reply_ack_flag();
}
Ok(MasterReq::SET_FEATURES) => {
Ok(FrontendReq::SET_FEATURES) => {
let msg = self.extract_request_body::<VhostUserU64>(&hdr, size, &buf)?;
let res = self.backend.set_features(msg.value);
self.acked_virtio_features = msg.value;
self.update_reply_ack_flag();
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::SET_MEM_TABLE) => {
Ok(FrontendReq::SET_MEM_TABLE) => {
let res = self.set_mem_table(&hdr, size, &buf, files);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::SET_VRING_NUM) => {
Ok(FrontendReq::SET_VRING_NUM) => {
let msg = self.extract_request_body::<VhostUserVringState>(&hdr, size, &buf)?;
let res = self.backend.set_vring_num(msg.index, msg.num);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::SET_VRING_ADDR) => {
Ok(FrontendReq::SET_VRING_ADDR) => {
let msg = self.extract_request_body::<VhostUserVringAddr>(&hdr, size, &buf)?;
let flags = match VhostUserVringAddrFlags::from_bits(msg.flags) {
Some(val) => val,
@ -390,35 +390,35 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::SET_VRING_BASE) => {
Ok(FrontendReq::SET_VRING_BASE) => {
let msg = self.extract_request_body::<VhostUserVringState>(&hdr, size, &buf)?;
let res = self.backend.set_vring_base(msg.index, msg.num);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::GET_VRING_BASE) => {
Ok(FrontendReq::GET_VRING_BASE) => {
let msg = self.extract_request_body::<VhostUserVringState>(&hdr, size, &buf)?;
let reply = self.backend.get_vring_base(msg.index)?;
self.send_reply_message(&hdr, &reply)?;
}
Ok(MasterReq::SET_VRING_CALL) => {
Ok(FrontendReq::SET_VRING_CALL) => {
self.check_request_size(&hdr, size, mem::size_of::<VhostUserU64>())?;
let (index, file) = self.handle_vring_fd_request(&buf, files)?;
let res = self.backend.set_vring_call(index, file);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::SET_VRING_KICK) => {
Ok(FrontendReq::SET_VRING_KICK) => {
self.check_request_size(&hdr, size, mem::size_of::<VhostUserU64>())?;
let (index, file) = self.handle_vring_fd_request(&buf, files)?;
let res = self.backend.set_vring_kick(index, file);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::SET_VRING_ERR) => {
Ok(FrontendReq::SET_VRING_ERR) => {
self.check_request_size(&hdr, size, mem::size_of::<VhostUserU64>())?;
let (index, file) = self.handle_vring_fd_request(&buf, files)?;
let res = self.backend.set_vring_err(index, file);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::GET_PROTOCOL_FEATURES) => {
Ok(FrontendReq::GET_PROTOCOL_FEATURES) => {
self.check_request_size(&hdr, size, 0)?;
let features = self.backend.get_protocol_features()?;
@ -431,7 +431,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
self.protocol_features = features;
self.update_reply_ack_flag();
}
Ok(MasterReq::SET_PROTOCOL_FEATURES) => {
Ok(FrontendReq::SET_PROTOCOL_FEATURES) => {
let msg = self.extract_request_body::<VhostUserU64>(&hdr, size, &buf)?;
let res = self.backend.set_protocol_features(msg.value);
self.acked_protocol_features = msg.value;
@ -441,14 +441,14 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
#[cfg(feature = "xen")]
self.check_proto_feature(VhostUserProtocolFeatures::XEN_MMAP)?;
}
Ok(MasterReq::GET_QUEUE_NUM) => {
Ok(FrontendReq::GET_QUEUE_NUM) => {
self.check_proto_feature(VhostUserProtocolFeatures::MQ)?;
self.check_request_size(&hdr, size, 0)?;
let num = self.backend.get_queue_num()?;
let msg = VhostUserU64::new(num);
self.send_reply_message(&hdr, &msg)?;
}
Ok(MasterReq::SET_VRING_ENABLE) => {
Ok(FrontendReq::SET_VRING_ENABLE) => {
let msg = self.extract_request_body::<VhostUserVringState>(&hdr, size, &buf)?;
self.check_feature(VhostUserVirtioFeatures::PROTOCOL_FEATURES)?;
let enable = match msg.num {
@ -460,24 +460,24 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
let res = self.backend.set_vring_enable(msg.index, enable);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::GET_CONFIG) => {
Ok(FrontendReq::GET_CONFIG) => {
self.check_proto_feature(VhostUserProtocolFeatures::CONFIG)?;
self.check_request_size(&hdr, size, hdr.get_size() as usize)?;
self.get_config(&hdr, &buf)?;
}
Ok(MasterReq::SET_CONFIG) => {
Ok(FrontendReq::SET_CONFIG) => {
self.check_proto_feature(VhostUserProtocolFeatures::CONFIG)?;
self.check_request_size(&hdr, size, hdr.get_size() as usize)?;
let res = self.set_config(size, &buf);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::SET_SLAVE_REQ_FD) => {
self.check_proto_feature(VhostUserProtocolFeatures::SLAVE_REQ)?;
Ok(FrontendReq::SET_BACKEND_REQ_FD) => {
self.check_proto_feature(VhostUserProtocolFeatures::BACKEND_REQ)?;
self.check_request_size(&hdr, size, hdr.get_size() as usize)?;
let res = self.set_slave_req_fd(files);
let res = self.set_backend_req_fd(files);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::GET_INFLIGHT_FD) => {
Ok(FrontendReq::GET_INFLIGHT_FD) => {
self.check_proto_feature(VhostUserProtocolFeatures::INFLIGHT_SHMFD)?;
let msg = self.extract_request_body::<VhostUserInflight>(&hdr, size, &buf)?;
@ -486,21 +486,21 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
self.main_sock
.send_message(&reply_hdr, &inflight, Some(&[file.as_raw_fd()]))?;
}
Ok(MasterReq::SET_INFLIGHT_FD) => {
Ok(FrontendReq::SET_INFLIGHT_FD) => {
self.check_proto_feature(VhostUserProtocolFeatures::INFLIGHT_SHMFD)?;
let file = take_single_file(files).ok_or(Error::IncorrectFds)?;
let msg = self.extract_request_body::<VhostUserInflight>(&hdr, size, &buf)?;
let res = self.backend.set_inflight_fd(&msg, file);
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::GET_MAX_MEM_SLOTS) => {
Ok(FrontendReq::GET_MAX_MEM_SLOTS) => {
self.check_proto_feature(VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS)?;
self.check_request_size(&hdr, size, 0)?;
let num = self.backend.get_max_mem_slots()?;
let msg = VhostUserU64::new(num);
self.send_reply_message(&hdr, &msg)?;
}
Ok(MasterReq::ADD_MEM_REG) => {
Ok(FrontendReq::ADD_MEM_REG) => {
self.check_proto_feature(VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS)?;
let mut files = files.ok_or(Error::InvalidParam)?;
if files.len() != 1 {
@ -511,7 +511,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
let res = self.backend.add_mem_region(&msg, files.swap_remove(0));
self.send_ack_message(&hdr, res)?;
}
Ok(MasterReq::REM_MEM_REG) => {
Ok(FrontendReq::REM_MEM_REG) => {
self.check_proto_feature(VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS)?;
let msg =
@ -528,7 +528,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
fn set_mem_table(
&mut self,
hdr: &VhostUserMsgHeader<MasterReq>,
hdr: &VhostUserMsgHeader<FrontendReq>,
size: usize,
buf: &[u8],
files: Option<Vec<File>>,
@ -575,7 +575,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
self.backend.set_mem_table(regions, files)
}
fn get_config(&mut self, hdr: &VhostUserMsgHeader<MasterReq>, buf: &[u8]) -> Result<()> {
fn get_config(&mut self, hdr: &VhostUserMsgHeader<FrontendReq>, buf: &[u8]) -> Result<()> {
let payload_offset = mem::size_of::<VhostUserConfig>();
if buf.len() > MAX_MSG_SIZE || buf.len() < payload_offset {
return Err(Error::InvalidMessage);
@ -594,9 +594,9 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
};
let res = self.backend.get_config(msg.offset, msg.size, flags);
// vhost-user slave's payload size MUST match master's request
// vhost-user backend's payload size MUST match frontend's request
// on success, uses zero length of payload to indicate an error
// to vhost-user master.
// to vhost-user frontend.
match res {
Ok(ref buf) if buf.len() == msg.size as usize => {
let reply = VhostUserConfig::new(msg.offset, buf.len() as u32, flags);
@ -632,14 +632,14 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
.set_config(msg.offset, &buf[mem::size_of::<VhostUserConfig>()..], flags)
}
fn set_slave_req_fd(&mut self, files: Option<Vec<File>>) -> Result<()> {
fn set_backend_req_fd(&mut self, files: Option<Vec<File>>) -> Result<()> {
let file = take_single_file(files).ok_or(Error::InvalidMessage)?;
// SAFETY: Safe because we have ownership of the files that were
// checked when received. We have to trust that they are Unix sockets
// since we have no way to check this. If not, it will fail later.
let sock = unsafe { UnixStream::from_raw_fd(file.into_raw_fd()) };
let slave = Slave::from_stream(sock);
self.backend.set_slave_req_fd(slave);
let backend = Backend::from_stream(sock);
self.backend.set_backend_req_fd(backend);
Ok(())
}
@ -682,7 +682,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
fn check_request_size(
&self,
hdr: &VhostUserMsgHeader<MasterReq>,
hdr: &VhostUserMsgHeader<FrontendReq>,
size: usize,
expected: usize,
) -> Result<()> {
@ -698,20 +698,20 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
fn check_attached_files(
&self,
hdr: &VhostUserMsgHeader<MasterReq>,
hdr: &VhostUserMsgHeader<FrontendReq>,
files: &Option<Vec<File>>,
) -> Result<()> {
match hdr.get_code() {
Ok(
MasterReq::SET_MEM_TABLE
| MasterReq::SET_VRING_CALL
| MasterReq::SET_VRING_KICK
| MasterReq::SET_VRING_ERR
| MasterReq::SET_LOG_BASE
| MasterReq::SET_LOG_FD
| MasterReq::SET_SLAVE_REQ_FD
| MasterReq::SET_INFLIGHT_FD
| MasterReq::ADD_MEM_REG,
FrontendReq::SET_MEM_TABLE
| FrontendReq::SET_VRING_CALL
| FrontendReq::SET_VRING_KICK
| FrontendReq::SET_VRING_ERR
| FrontendReq::SET_LOG_BASE
| FrontendReq::SET_LOG_FD
| FrontendReq::SET_BACKEND_REQ_FD
| FrontendReq::SET_INFLIGHT_FD
| FrontendReq::ADD_MEM_REG,
) => Ok(()),
_ if files.is_some() => Err(Error::InvalidMessage),
_ => Ok(()),
@ -720,7 +720,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
fn extract_request_body<T: Sized + VhostUserMsgValidator>(
&self,
hdr: &VhostUserMsgHeader<MasterReq>,
hdr: &VhostUserMsgHeader<FrontendReq>,
size: usize,
buf: &[u8],
) -> Result<T> {
@ -748,9 +748,9 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
fn new_reply_header<T: Sized>(
&self,
req: &VhostUserMsgHeader<MasterReq>,
req: &VhostUserMsgHeader<FrontendReq>,
payload_size: usize,
) -> Result<VhostUserMsgHeader<MasterReq>> {
) -> Result<VhostUserMsgHeader<FrontendReq>> {
if mem::size_of::<T>() > MAX_MSG_SIZE
|| payload_size > MAX_MSG_SIZE
|| mem::size_of::<T>() + payload_size > MAX_MSG_SIZE
@ -767,7 +767,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
fn send_ack_message(
&mut self,
req: &VhostUserMsgHeader<MasterReq>,
req: &VhostUserMsgHeader<FrontendReq>,
res: Result<()>,
) -> Result<()> {
if self.reply_ack_enabled && req.is_need_reply() {
@ -784,7 +784,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
fn send_reply_message<T: ByteValued>(
&mut self,
req: &VhostUserMsgHeader<MasterReq>,
req: &VhostUserMsgHeader<FrontendReq>,
msg: &T,
) -> Result<()> {
let hdr = self.new_reply_header::<T>(req, 0)?;
@ -794,7 +794,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
fn send_reply_with_payload<T: ByteValued>(
&mut self,
req: &VhostUserMsgHeader<MasterReq>,
req: &VhostUserMsgHeader<FrontendReq>,
msg: &T,
payload: &[u8],
) -> Result<()> {
@ -805,7 +805,7 @@ impl<S: VhostUserSlaveReqHandler> SlaveReqHandler<S> {
}
}
impl<S: VhostUserSlaveReqHandler> AsRawFd for SlaveReqHandler<S> {
impl<S: VhostUserBackendReqHandler> AsRawFd for BackendReqHandler<S> {
fn as_raw_fd(&self) -> RawFd {
self.main_sock.as_raw_fd()
}
@ -816,14 +816,14 @@ mod tests {
use std::os::unix::io::AsRawFd;
use super::*;
use crate::vhost_user::dummy_slave::DummySlaveReqHandler;
use crate::vhost_user::dummy_backend::DummyBackendReqHandler;
#[test]
fn test_slave_req_handler_new() {
fn test_backend_req_handler_new() {
let (p1, _p2) = UnixStream::pair().unwrap();
let endpoint = Endpoint::<MasterReq>::from_stream(p1);
let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
let mut handler = SlaveReqHandler::new(endpoint, backend);
let endpoint = Endpoint::<FrontendReq>::from_stream(p1);
let backend = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
let mut handler = BackendReqHandler::new(endpoint, backend);
handler.check_state().unwrap();
handler.set_failed(libc::EAGAIN);

View file

@ -669,23 +669,23 @@ mod tests {
let path = temp_path();
let listener = Listener::new(&path, true).unwrap();
listener.set_nonblocking(true).unwrap();
let mut master = Endpoint::<MasterReq>::connect(&path).unwrap();
let mut frontend = Endpoint::<FrontendReq>::connect(&path).unwrap();
let sock = listener.accept().unwrap().unwrap();
let mut slave = Endpoint::<MasterReq>::from_stream(sock);
let mut backend = Endpoint::<FrontendReq>::from_stream(sock);
let buf1 = vec![0x1, 0x2, 0x3, 0x4];
let mut len = master.send_slice(&buf1[..], None).unwrap();
let mut len = frontend.send_slice(&buf1[..], None).unwrap();
assert_eq!(len, 4);
let (bytes, buf2, _) = slave.recv_into_buf(0x1000).unwrap();
let (bytes, buf2, _) = backend.recv_into_buf(0x1000).unwrap();
assert_eq!(bytes, 4);
assert_eq!(&buf1[..], &buf2[..bytes]);
len = master.send_slice(&buf1[..], None).unwrap();
len = frontend.send_slice(&buf1[..], None).unwrap();
assert_eq!(len, 4);
let (bytes, buf2, _) = slave.recv_into_buf(0x2).unwrap();
let (bytes, buf2, _) = backend.recv_into_buf(0x2).unwrap();
assert_eq!(bytes, 2);
assert_eq!(&buf1[..2], &buf2[..]);
let (bytes, buf2, _) = slave.recv_into_buf(0x2).unwrap();
let (bytes, buf2, _) = backend.recv_into_buf(0x2).unwrap();
assert_eq!(bytes, 2);
assert_eq!(&buf1[2..], &buf2[..]);
}
@ -695,21 +695,21 @@ mod tests {
let path = temp_path();
let listener = Listener::new(&path, true).unwrap();
listener.set_nonblocking(true).unwrap();
let mut master = Endpoint::<MasterReq>::connect(&path).unwrap();
let mut frontend = Endpoint::<FrontendReq>::connect(&path).unwrap();
let sock = listener.accept().unwrap().unwrap();
let mut slave = Endpoint::<MasterReq>::from_stream(sock);
let mut backend = Endpoint::<FrontendReq>::from_stream(sock);
let mut fd = TempFile::new().unwrap().into_file();
write!(fd, "test").unwrap();
// Normal case for sending/receiving file descriptors
let buf1 = vec![0x1, 0x2, 0x3, 0x4];
let len = master
let len = frontend
.send_slice(&buf1[..], Some(&[fd.as_raw_fd()]))
.unwrap();
assert_eq!(len, 4);
let (bytes, buf2, files) = slave.recv_into_buf(4).unwrap();
let (bytes, buf2, files) = backend.recv_into_buf(4).unwrap();
assert_eq!(bytes, 4);
assert_eq!(&buf1[..], &buf2[..]);
assert!(files.is_some());
@ -726,7 +726,7 @@ mod tests {
// Following communication pattern should work:
// Sending side: data(header, body) with fds
// Receiving side: data(header) with fds, data(body)
let len = master
let len = frontend
.send_slice(
&buf1[..],
Some(&[fd.as_raw_fd(), fd.as_raw_fd(), fd.as_raw_fd()]),
@ -734,7 +734,7 @@ mod tests {
.unwrap();
assert_eq!(len, 4);
let (bytes, buf2, files) = slave.recv_into_buf(0x2).unwrap();
let (bytes, buf2, files) = backend.recv_into_buf(0x2).unwrap();
assert_eq!(bytes, 2);
assert_eq!(&buf1[..2], &buf2[..]);
assert!(files.is_some());
@ -747,7 +747,7 @@ mod tests {
file.read_to_string(&mut content).unwrap();
assert_eq!(content, "test");
}
let (bytes, buf2, files) = slave.recv_into_buf(0x2).unwrap();
let (bytes, buf2, files) = backend.recv_into_buf(0x2).unwrap();
assert_eq!(bytes, 2);
assert_eq!(&buf1[2..], &buf2[..]);
assert!(files.is_none());
@ -755,7 +755,7 @@ mod tests {
// Following communication pattern should not work:
// Sending side: data(header, body) with fds
// Receiving side: data(header), data(body) with fds
let len = master
let len = frontend
.send_slice(
&buf1[..],
Some(&[fd.as_raw_fd(), fd.as_raw_fd(), fd.as_raw_fd()]),
@ -763,10 +763,10 @@ mod tests {
.unwrap();
assert_eq!(len, 4);
let (bytes, buf4) = slave.recv_data(2).unwrap();
let (bytes, buf4) = backend.recv_data(2).unwrap();
assert_eq!(bytes, 2);
assert_eq!(&buf1[..2], &buf4[..]);
let (bytes, buf2, files) = slave.recv_into_buf(0x2).unwrap();
let (bytes, buf2, files) = backend.recv_into_buf(0x2).unwrap();
assert_eq!(bytes, 2);
assert_eq!(&buf1[2..], &buf2[..]);
assert!(files.is_none());
@ -774,9 +774,9 @@ mod tests {
// Following communication pattern should work:
// Sending side: data, data with fds
// Receiving side: data, data with fds
let len = master.send_slice(&buf1[..], None).unwrap();
let len = frontend.send_slice(&buf1[..], None).unwrap();
assert_eq!(len, 4);
let len = master
let len = frontend
.send_slice(
&buf1[..],
Some(&[fd.as_raw_fd(), fd.as_raw_fd(), fd.as_raw_fd()]),
@ -784,12 +784,12 @@ mod tests {
.unwrap();
assert_eq!(len, 4);
let (bytes, buf2, files) = slave.recv_into_buf(0x4).unwrap();
let (bytes, buf2, files) = backend.recv_into_buf(0x4).unwrap();
assert_eq!(bytes, 4);
assert_eq!(&buf1[..], &buf2[..]);
assert!(files.is_none());
let (bytes, buf2, files) = slave.recv_into_buf(0x2).unwrap();
let (bytes, buf2, files) = backend.recv_into_buf(0x2).unwrap();
assert_eq!(bytes, 2);
assert_eq!(&buf1[..2], &buf2[..]);
assert!(files.is_some());
@ -802,7 +802,7 @@ mod tests {
file.read_to_string(&mut content).unwrap();
assert_eq!(content, "test");
}
let (bytes, buf2, files) = slave.recv_into_buf(0x2).unwrap();
let (bytes, buf2, files) = backend.recv_into_buf(0x2).unwrap();
assert_eq!(bytes, 2);
assert_eq!(&buf1[2..], &buf2[..]);
assert!(files.is_none());
@ -810,9 +810,9 @@ mod tests {
// Following communication pattern should not work:
// Sending side: data1, data2 with fds
// Receiving side: data + partial of data2, left of data2 with fds
let len = master.send_slice(&buf1[..], None).unwrap();
let len = frontend.send_slice(&buf1[..], None).unwrap();
assert_eq!(len, 4);
let len = master
let len = frontend
.send_slice(
&buf1[..],
Some(&[fd.as_raw_fd(), fd.as_raw_fd(), fd.as_raw_fd()]),
@ -820,15 +820,15 @@ mod tests {
.unwrap();
assert_eq!(len, 4);
let (bytes, _) = slave.recv_data(5).unwrap();
let (bytes, _) = backend.recv_data(5).unwrap();
assert_eq!(bytes, 5);
let (bytes, _, files) = slave.recv_into_buf(0x4).unwrap();
let (bytes, _, files) = backend.recv_into_buf(0x4).unwrap();
assert_eq!(bytes, 3);
assert!(files.is_none());
// If the target fd array is too small, extra file descriptors will get lost.
let len = master
let len = frontend
.send_slice(
&buf1[..],
Some(&[fd.as_raw_fd(), fd.as_raw_fd(), fd.as_raw_fd()]),
@ -836,7 +836,7 @@ mod tests {
.unwrap();
assert_eq!(len, 4);
let (bytes, _, files) = slave.recv_into_buf(0x4).unwrap();
let (bytes, _, files) = backend.recv_into_buf(0x4).unwrap();
assert_eq!(bytes, 4);
assert!(files.is_some());
}
@ -846,15 +846,15 @@ mod tests {
let path = temp_path();
let listener = Listener::new(&path, true).unwrap();
listener.set_nonblocking(true).unwrap();
let mut master = Endpoint::<MasterReq>::connect(&path).unwrap();
let mut frontend = Endpoint::<FrontendReq>::connect(&path).unwrap();
let sock = listener.accept().unwrap().unwrap();
let mut slave = Endpoint::<MasterReq>::from_stream(sock);
let mut backend = Endpoint::<FrontendReq>::from_stream(sock);
let mut hdr1 =
VhostUserMsgHeader::new(MasterReq::GET_FEATURES, 0, mem::size_of::<u64>() as u32);
VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0, mem::size_of::<u64>() as u32);
hdr1.set_need_reply(true);
let features1 = 0x1u64;
master.send_message(&hdr1, &features1, None).unwrap();
frontend.send_message(&hdr1, &features1, None).unwrap();
let mut features2 = 0u64;
@ -865,14 +865,14 @@ mod tests {
mem::size_of::<u64>(),
)
};
let (hdr2, bytes, files) = slave.recv_body_into_buf(slice).unwrap();
let (hdr2, bytes, files) = backend.recv_body_into_buf(slice).unwrap();
assert_eq!(hdr1, hdr2);
assert_eq!(bytes, 8);
assert_eq!(features1, features2);
assert!(files.is_none());
master.send_header(&hdr1, None).unwrap();
let (hdr2, files) = slave.recv_header().unwrap();
frontend.send_header(&hdr1, None).unwrap();
let (hdr2, files) = backend.recv_header().unwrap();
assert_eq!(hdr1, hdr2);
assert!(files.is_none());
}
@ -881,13 +881,13 @@ mod tests {
fn partial_message() {
let path = temp_path();
let listener = Listener::new(&path, true).unwrap();
let mut master = UnixStream::connect(&path).unwrap();
let mut frontend = UnixStream::connect(&path).unwrap();
let sock = listener.accept().unwrap().unwrap();
let mut slave = Endpoint::<MasterReq>::from_stream(sock);
let mut backend = Endpoint::<FrontendReq>::from_stream(sock);
write!(master, "a").unwrap();
drop(master);
assert!(matches!(slave.recv_header(), Err(Error::PartialMessage)));
write!(frontend, "a").unwrap();
drop(frontend);
assert!(matches!(backend.recv_header(), Err(Error::PartialMessage)));
}
#[test]
@ -896,8 +896,8 @@ mod tests {
let listener = Listener::new(&path, true).unwrap();
let _ = UnixStream::connect(&path).unwrap();
let sock = listener.accept().unwrap().unwrap();
let mut slave = Endpoint::<MasterReq>::from_stream(sock);
let mut backend = Endpoint::<FrontendReq>::from_stream(sock);
assert!(matches!(slave.recv_header(), Err(Error::Disconnected)));
assert!(matches!(backend.recv_header(), Err(Error::Disconnected)));
}
}

View file

@ -12,7 +12,7 @@ pub const MAX_MEM_SLOTS: usize = 32;
pub const VIRTIO_FEATURES: u64 = 0x40000003;
#[derive(Default)]
pub struct DummySlaveReqHandler {
pub struct DummyBackendReqHandler {
pub owned: bool,
pub features_acked: bool,
pub acked_features: u64,
@ -28,9 +28,9 @@ pub struct DummySlaveReqHandler {
pub inflight_file: Option<File>,
}
impl DummySlaveReqHandler {
impl DummyBackendReqHandler {
pub fn new() -> Self {
DummySlaveReqHandler {
DummyBackendReqHandler {
queue_num: MAX_QUEUE_NUM,
..Default::default()
}
@ -55,7 +55,7 @@ impl DummySlaveReqHandler {
}
}
impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler {
impl VhostUserBackendReqHandlerMut for DummyBackendReqHandler {
fn set_owner(&mut self) -> Result<()> {
if self.owned {
return Err(Error::InvalidOperation("already claimed"));
@ -193,10 +193,10 @@ impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler {
}
fn set_protocol_features(&mut self, features: u64) -> Result<()> {
// Note: slave that reported VHOST_USER_F_PROTOCOL_FEATURES must
// Note: backend that reported VHOST_USER_F_PROTOCOL_FEATURES must
// support this message even before VHOST_USER_SET_FEATURES was
// called.
// What happens if the master calls set_features() with
// What happens if the frontend calls set_features() with
// VHOST_USER_F_PROTOCOL_FEATURES cleared after calling this
// interface?
self.acked_protocol_features = features;
@ -216,7 +216,7 @@ impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler {
return Err(Error::InvalidParam);
}
// Slave must not pass data to/from the backend until ring is
// Backend must not pass data to/from the backend until ring is
// enabled by VHOST_USER_SET_VRING_ENABLE with parameter 1,
// or after it has been disabled by VHOST_USER_SET_VRING_ENABLE
// with parameter 0.

View file

@ -1,7 +1,7 @@
// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! Traits and Struct for vhost-user master.
//! Traits and Struct for vhost-user frontend.
use std::fs::File;
use std::mem;
@ -21,8 +21,8 @@ use crate::backend::{
};
use crate::{Error, Result};
/// Trait for vhost-user master to provide extra methods not covered by the VhostBackend yet.
pub trait VhostUserMaster: VhostBackend {
/// Trait for vhost-user frontend to provide extra methods not covered by the VhostBackend yet.
pub trait VhostUserFrontend: VhostBackend {
/// Get the protocol feature bitmask from the underlying vhost implementation.
fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures>;
@ -32,9 +32,9 @@ pub trait VhostUserMaster: VhostBackend {
/// Query how many queues the backend supports.
fn get_queue_num(&mut self) -> Result<u64>;
/// Signal slave to enable or disable corresponding vring.
/// Signal backend to enable or disable corresponding vring.
///
/// Slave must not pass data to/from the backend until ring is enabled by
/// Backend must not pass data to/from the backend until ring is enabled by
/// VHOST_USER_SET_VRING_ENABLE with parameter 1, or after it has been
/// disabled by VHOST_USER_SET_VRING_ENABLE with parameter 0.
fn set_vring_enable(&mut self, queue_index: usize, enable: bool) -> Result<()>;
@ -52,8 +52,8 @@ pub trait VhostUserMaster: VhostBackend {
/// destination host to set readonly configuration space fields.
fn set_config(&mut self, offset: u32, flags: VhostUserConfigFlags, buf: &[u8]) -> Result<()>;
/// Setup slave communication channel.
fn set_slave_request_fd(&mut self, fd: &dyn AsRawFd) -> Result<()>;
/// Setup backend communication channel.
fn set_backend_request_fd(&mut self, fd: &dyn AsRawFd) -> Result<()>;
/// Retrieve shared buffer for inflight I/O tracking.
fn get_inflight_fd(
@ -78,17 +78,17 @@ fn error_code<T>(err: VhostUserError) -> Result<T> {
Err(Error::VhostUserProtocol(err))
}
/// Struct for the vhost-user master endpoint.
/// Struct for the vhost-user frontend endpoint.
#[derive(Clone)]
pub struct Master {
node: Arc<Mutex<MasterInternal>>,
pub struct Frontend {
node: Arc<Mutex<FrontendInternal>>,
}
impl Master {
impl Frontend {
/// Create a new instance.
fn new(ep: Endpoint<MasterReq>, max_queue_num: u64) -> Self {
Master {
node: Arc::new(Mutex::new(MasterInternal {
fn new(ep: Endpoint<FrontendReq>, max_queue_num: u64) -> Self {
Frontend {
node: Arc::new(Mutex::new(FrontendInternal {
main_sock: ep,
virtio_features: 0,
acked_virtio_features: 0,
@ -102,16 +102,16 @@ impl Master {
}
}
fn node(&self) -> MutexGuard<MasterInternal> {
fn node(&self) -> MutexGuard<FrontendInternal> {
self.node.lock().unwrap()
}
/// Create a new instance from a Unix stream socket.
pub fn from_stream(sock: UnixStream, max_queue_num: u64) -> Self {
Self::new(Endpoint::<MasterReq>::from_stream(sock), max_queue_num)
Self::new(Endpoint::<FrontendReq>::from_stream(sock), max_queue_num)
}
/// Create a new vhost-user master endpoint.
/// Create a new vhost-user frontend endpoint.
///
/// Will retry as the backend may not be ready to accept the connection.
///
@ -120,7 +120,7 @@ impl Master {
pub fn connect<P: AsRef<Path>>(path: P, max_queue_num: u64) -> Result<Self> {
let mut retry_count = 5;
let endpoint = loop {
match Endpoint::<MasterReq>::connect(&path) {
match Endpoint::<FrontendReq>::connect(&path) {
Ok(endpoint) => break Ok(endpoint),
Err(e) => match &e {
VhostUserError::SocketConnect(why) => {
@ -147,11 +147,11 @@ impl Master {
}
}
impl VhostBackend for Master {
impl VhostBackend for Frontend {
/// Get from the underlying vhost implementation the feature bitmask.
fn get_features(&self) -> Result<u64> {
let mut node = self.node();
let hdr = node.send_request_header(MasterReq::GET_FEATURES, None)?;
let hdr = node.send_request_header(FrontendReq::GET_FEATURES, None)?;
let val = node.recv_reply::<VhostUserU64>(&hdr)?;
node.virtio_features = val.value;
Ok(node.virtio_features)
@ -161,27 +161,27 @@ impl VhostBackend for Master {
fn set_features(&self, features: u64) -> Result<()> {
let mut node = self.node();
let val = VhostUserU64::new(features);
let hdr = node.send_request_with_body(MasterReq::SET_FEATURES, &val, None)?;
let hdr = node.send_request_with_body(FrontendReq::SET_FEATURES, &val, None)?;
node.acked_virtio_features = features & node.virtio_features;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
/// Set the current Master as an owner of the session.
/// Set the current Frontend as an owner of the session.
fn set_owner(&self) -> Result<()> {
// We unwrap() the return value to assert that we are not expecting threads to ever fail
// while holding the lock.
let mut node = self.node();
let hdr = node.send_request_header(MasterReq::SET_OWNER, None)?;
let hdr = node.send_request_header(FrontendReq::SET_OWNER, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
fn reset_owner(&self) -> Result<()> {
let mut node = self.node();
let hdr = node.send_request_header(MasterReq::RESET_OWNER, None)?;
let hdr = node.send_request_header(FrontendReq::RESET_OWNER, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
/// Set the memory map regions on the slave so it can translate the vring
/// Set the memory map regions on the backend so it can translate the vring
/// addresses. In the ancillary data there is an array of file descriptors
fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
if regions.is_empty() || regions.len() > MAX_ATTACHED_FD_ENTRIES {
@ -202,7 +202,7 @@ impl VhostBackend for Master {
// SAFETY: Safe because ctx.regions is a valid Vec() at this point.
let (_, payload, _) = unsafe { ctx.regions.align_to::<u8>() };
let hdr = node.send_request_with_payload(
MasterReq::SET_MEM_TABLE,
FrontendReq::SET_MEM_TABLE,
&body,
payload,
Some(ctx.fds.as_slice()),
@ -225,13 +225,13 @@ impl VhostBackend for Master {
mmap_offset: region.mmap_offset,
};
let hdr = node.send_request_with_body(
MasterReq::SET_LOG_BASE,
FrontendReq::SET_LOG_BASE,
&log,
Some(&[region.mmap_handle]),
)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
} else {
let _ = node.send_request_with_body(MasterReq::SET_LOG_BASE, &val, None)?;
let _ = node.send_request_with_body(FrontendReq::SET_LOG_BASE, &val, None)?;
Ok(())
}
}
@ -239,7 +239,7 @@ impl VhostBackend for Master {
fn set_log_fd(&self, fd: RawFd) -> Result<()> {
let mut node = self.node();
let fds = [fd];
let hdr = node.send_request_header(MasterReq::SET_LOG_FD, Some(&fds))?;
let hdr = node.send_request_header(FrontendReq::SET_LOG_FD, Some(&fds))?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -251,7 +251,7 @@ impl VhostBackend for Master {
}
let val = VhostUserVringState::new(queue_index as u32, num.into());
let hdr = node.send_request_with_body(MasterReq::SET_VRING_NUM, &val, None)?;
let hdr = node.send_request_with_body(FrontendReq::SET_VRING_NUM, &val, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -265,7 +265,7 @@ impl VhostBackend for Master {
}
let val = VhostUserVringAddr::from_config_data(queue_index as u32, config_data);
let hdr = node.send_request_with_body(MasterReq::SET_VRING_ADDR, &val, None)?;
let hdr = node.send_request_with_body(FrontendReq::SET_VRING_ADDR, &val, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -277,7 +277,7 @@ impl VhostBackend for Master {
}
let val = VhostUserVringState::new(queue_index as u32, base.into());
let hdr = node.send_request_with_body(MasterReq::SET_VRING_BASE, &val, None)?;
let hdr = node.send_request_with_body(FrontendReq::SET_VRING_BASE, &val, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -288,7 +288,7 @@ impl VhostBackend for Master {
}
let req = VhostUserVringState::new(queue_index as u32, 0);
let hdr = node.send_request_with_body(MasterReq::GET_VRING_BASE, &req, None)?;
let hdr = node.send_request_with_body(FrontendReq::GET_VRING_BASE, &req, None)?;
let reply = node.recv_reply::<VhostUserVringState>(&hdr)?;
Ok(reply.num)
}
@ -302,7 +302,8 @@ impl VhostBackend for Master {
if queue_index as u64 >= node.max_queue_num {
return error_code(VhostUserError::InvalidParam);
}
let hdr = node.send_fd_for_vring(MasterReq::SET_VRING_CALL, queue_index, fd.as_raw_fd())?;
let hdr =
node.send_fd_for_vring(FrontendReq::SET_VRING_CALL, queue_index, fd.as_raw_fd())?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -315,7 +316,8 @@ impl VhostBackend for Master {
if queue_index as u64 >= node.max_queue_num {
return error_code(VhostUserError::InvalidParam);
}
let hdr = node.send_fd_for_vring(MasterReq::SET_VRING_KICK, queue_index, fd.as_raw_fd())?;
let hdr =
node.send_fd_for_vring(FrontendReq::SET_VRING_KICK, queue_index, fd.as_raw_fd())?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -327,16 +329,17 @@ impl VhostBackend for Master {
if queue_index as u64 >= node.max_queue_num {
return error_code(VhostUserError::InvalidParam);
}
let hdr = node.send_fd_for_vring(MasterReq::SET_VRING_ERR, queue_index, fd.as_raw_fd())?;
let hdr =
node.send_fd_for_vring(FrontendReq::SET_VRING_ERR, queue_index, fd.as_raw_fd())?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
}
impl VhostUserMaster for Master {
impl VhostUserFrontend for Frontend {
fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
let mut node = self.node();
node.check_feature(VhostUserVirtioFeatures::PROTOCOL_FEATURES)?;
let hdr = node.send_request_header(MasterReq::GET_PROTOCOL_FEATURES, None)?;
let hdr = node.send_request_header(FrontendReq::GET_PROTOCOL_FEATURES, None)?;
let val = node.recv_reply::<VhostUserU64>(&hdr)?;
node.protocol_features = val.value;
// Should we support forward compatibility?
@ -351,7 +354,7 @@ impl VhostUserMaster for Master {
let mut node = self.node();
node.check_feature(VhostUserVirtioFeatures::PROTOCOL_FEATURES)?;
let val = VhostUserU64::new(features.bits());
let hdr = node.send_request_with_body(MasterReq::SET_PROTOCOL_FEATURES, &val, None)?;
let hdr = node.send_request_with_body(FrontendReq::SET_PROTOCOL_FEATURES, &val, None)?;
// Don't wait for ACK here because the protocol feature negotiation process hasn't been
// completed yet.
node.acked_protocol_features = features.bits();
@ -363,7 +366,7 @@ impl VhostUserMaster for Master {
let mut node = self.node();
node.check_proto_feature(VhostUserProtocolFeatures::MQ)?;
let hdr = node.send_request_header(MasterReq::GET_QUEUE_NUM, None)?;
let hdr = node.send_request_header(FrontendReq::GET_QUEUE_NUM, None)?;
let val = node.recv_reply::<VhostUserU64>(&hdr)?;
if val.value > VHOST_USER_MAX_VRINGS {
return error_code(VhostUserError::InvalidMessage);
@ -385,7 +388,7 @@ impl VhostUserMaster for Master {
let flag = enable.into();
let val = VhostUserVringState::new(queue_index as u32, flag);
let hdr = node.send_request_with_body(MasterReq::SET_VRING_ENABLE, &val, None)?;
let hdr = node.send_request_with_body(FrontendReq::SET_VRING_ENABLE, &val, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -406,15 +409,15 @@ impl VhostUserMaster for Master {
node.check_proto_feature(VhostUserProtocolFeatures::CONFIG)?;
// vhost-user spec states that:
// "Master payload: virtio device config space"
// "Slave payload: virtio device config space"
let hdr = node.send_request_with_payload(MasterReq::GET_CONFIG, &body, buf, None)?;
// "Frontend payload: virtio device config space"
// "Backend payload: virtio device config space"
let hdr = node.send_request_with_payload(FrontendReq::GET_CONFIG, &body, buf, None)?;
let (body_reply, buf_reply, rfds) =
node.recv_reply_with_payload::<VhostUserConfig>(&hdr)?;
if rfds.is_some() {
return error_code(VhostUserError::InvalidMessage);
} else if body_reply.size == 0 {
return error_code(VhostUserError::SlaveInternalError);
return error_code(VhostUserError::BackendInternalError);
} else if body_reply.size != body.size
|| body_reply.size as usize != buf.len()
|| body_reply.offset != body.offset
@ -438,15 +441,15 @@ impl VhostUserMaster for Master {
// depends on VhostUserProtocolFeatures::CONFIG
node.check_proto_feature(VhostUserProtocolFeatures::CONFIG)?;
let hdr = node.send_request_with_payload(MasterReq::SET_CONFIG, &body, buf, None)?;
let hdr = node.send_request_with_payload(FrontendReq::SET_CONFIG, &body, buf, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
fn set_slave_request_fd(&mut self, fd: &dyn AsRawFd) -> Result<()> {
fn set_backend_request_fd(&mut self, fd: &dyn AsRawFd) -> Result<()> {
let mut node = self.node();
node.check_proto_feature(VhostUserProtocolFeatures::SLAVE_REQ)?;
node.check_proto_feature(VhostUserProtocolFeatures::BACKEND_REQ)?;
let fds = [fd.as_raw_fd()];
let hdr = node.send_request_header(MasterReq::SET_SLAVE_REQ_FD, Some(&fds))?;
let hdr = node.send_request_header(FrontendReq::SET_BACKEND_REQ_FD, Some(&fds))?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -457,7 +460,7 @@ impl VhostUserMaster for Master {
let mut node = self.node();
node.check_proto_feature(VhostUserProtocolFeatures::INFLIGHT_SHMFD)?;
let hdr = node.send_request_with_body(MasterReq::GET_INFLIGHT_FD, inflight, None)?;
let hdr = node.send_request_with_body(FrontendReq::GET_INFLIGHT_FD, inflight, None)?;
let (inflight, files) = node.recv_reply_with_files::<VhostUserInflight>(&hdr)?;
match take_single_file(files) {
@ -475,7 +478,8 @@ impl VhostUserMaster for Master {
return error_code(VhostUserError::InvalidParam);
}
let hdr = node.send_request_with_body(MasterReq::SET_INFLIGHT_FD, inflight, Some(&[fd]))?;
let hdr =
node.send_request_with_body(FrontendReq::SET_INFLIGHT_FD, inflight, Some(&[fd]))?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -483,7 +487,7 @@ impl VhostUserMaster for Master {
let mut node = self.node();
node.check_proto_feature(VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS)?;
let hdr = node.send_request_header(MasterReq::GET_MAX_MEM_SLOTS, None)?;
let hdr = node.send_request_header(FrontendReq::GET_MAX_MEM_SLOTS, None)?;
let val = node.recv_reply::<VhostUserU64>(&hdr)?;
Ok(val.value)
@ -498,7 +502,7 @@ impl VhostUserMaster for Master {
let body = region.to_single_region();
let fds = [region.mmap_handle];
let hdr = node.send_request_with_body(MasterReq::ADD_MEM_REG, &body, Some(&fds))?;
let hdr = node.send_request_with_body(FrontendReq::ADD_MEM_REG, &body, Some(&fds))?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
@ -510,19 +514,19 @@ impl VhostUserMaster for Master {
}
let body = region.to_single_region();
let hdr = node.send_request_with_body(MasterReq::REM_MEM_REG, &body, None)?;
let hdr = node.send_request_with_body(FrontendReq::REM_MEM_REG, &body, None)?;
node.wait_for_ack(&hdr).map_err(|e| e.into())
}
}
impl AsRawFd for Master {
impl AsRawFd for Frontend {
fn as_raw_fd(&self) -> RawFd {
let node = self.node();
node.main_sock.as_raw_fd()
}
}
/// Context object to pass guest memory configuration to VhostUserMaster::set_mem_table().
/// Context object to pass guest memory configuration to VhostUserFrontend::set_mem_table().
struct VhostUserMemoryContext {
regions: VhostUserMemoryPayload,
fds: Vec<RawFd>,
@ -544,20 +548,20 @@ impl VhostUserMemoryContext {
}
}
struct MasterInternal {
// Used to send requests to the slave.
main_sock: Endpoint<MasterReq>,
// Cached virtio features from the slave.
struct FrontendInternal {
// Used to send requests to the backend.
main_sock: Endpoint<FrontendReq>,
// Cached virtio features from the backend.
virtio_features: u64,
// Cached acked virtio features from the driver.
acked_virtio_features: u64,
// Cached vhost-user protocol features from the slave.
// Cached vhost-user protocol features from the backend.
protocol_features: u64,
// Cached vhost-user protocol features.
acked_protocol_features: u64,
// Cached vhost-user protocol features are ready to use.
protocol_features_ready: bool,
// Cached maxinum number of queues supported from the slave.
// Cached maxinum number of queues supported from the backend.
max_queue_num: u64,
// Internal flag to mark failure state.
error: Option<i32>,
@ -565,12 +569,12 @@ struct MasterInternal {
hdr_flags: VhostUserHeaderFlag,
}
impl MasterInternal {
impl FrontendInternal {
fn send_request_header(
&mut self,
code: MasterReq,
code: FrontendReq,
fds: Option<&[RawFd]>,
) -> VhostUserResult<VhostUserMsgHeader<MasterReq>> {
) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>> {
self.check_state()?;
let hdr = self.new_request_header(code, 0);
self.main_sock.send_header(&hdr, fds)?;
@ -579,10 +583,10 @@ impl MasterInternal {
fn send_request_with_body<T: ByteValued>(
&mut self,
code: MasterReq,
code: FrontendReq,
msg: &T,
fds: Option<&[RawFd]>,
) -> VhostUserResult<VhostUserMsgHeader<MasterReq>> {
) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>> {
if mem::size_of::<T>() > MAX_MSG_SIZE {
return Err(VhostUserError::InvalidParam);
}
@ -595,11 +599,11 @@ impl MasterInternal {
fn send_request_with_payload<T: ByteValued>(
&mut self,
code: MasterReq,
code: FrontendReq,
msg: &T,
payload: &[u8],
fds: Option<&[RawFd]>,
) -> VhostUserResult<VhostUserMsgHeader<MasterReq>> {
) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>> {
let len = mem::size_of::<T>() + payload.len();
if len > MAX_MSG_SIZE {
return Err(VhostUserError::InvalidParam);
@ -619,10 +623,10 @@ impl MasterInternal {
fn send_fd_for_vring(
&mut self,
code: MasterReq,
code: FrontendReq,
queue_index: usize,
fd: RawFd,
) -> VhostUserResult<VhostUserMsgHeader<MasterReq>> {
) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>> {
if queue_index as u64 >= self.max_queue_num {
return Err(VhostUserError::InvalidParam);
}
@ -639,7 +643,7 @@ impl MasterInternal {
fn recv_reply<T: ByteValued + Sized + VhostUserMsgValidator>(
&mut self,
hdr: &VhostUserMsgHeader<MasterReq>,
hdr: &VhostUserMsgHeader<FrontendReq>,
) -> VhostUserResult<T> {
if mem::size_of::<T>() > MAX_MSG_SIZE || hdr.is_reply() {
return Err(VhostUserError::InvalidParam);
@ -655,7 +659,7 @@ impl MasterInternal {
fn recv_reply_with_files<T: ByteValued + Sized + VhostUserMsgValidator>(
&mut self,
hdr: &VhostUserMsgHeader<MasterReq>,
hdr: &VhostUserMsgHeader<FrontendReq>,
) -> VhostUserResult<(T, Option<Vec<File>>)> {
if mem::size_of::<T>() > MAX_MSG_SIZE || hdr.is_reply() {
return Err(VhostUserError::InvalidParam);
@ -671,7 +675,7 @@ impl MasterInternal {
fn recv_reply_with_payload<T: ByteValued + Sized + VhostUserMsgValidator>(
&mut self,
hdr: &VhostUserMsgHeader<MasterReq>,
hdr: &VhostUserMsgHeader<FrontendReq>,
) -> VhostUserResult<(T, Vec<u8>, Option<Vec<File>>)> {
if mem::size_of::<T>() > MAX_MSG_SIZE
|| hdr.get_size() as usize <= mem::size_of::<T>()
@ -696,7 +700,7 @@ impl MasterInternal {
Ok((body, buf, files))
}
fn wait_for_ack(&mut self, hdr: &VhostUserMsgHeader<MasterReq>) -> VhostUserResult<()> {
fn wait_for_ack(&mut self, hdr: &VhostUserMsgHeader<FrontendReq>) -> VhostUserResult<()> {
if self.acked_protocol_features & VhostUserProtocolFeatures::REPLY_ACK.bits() == 0
|| !hdr.is_need_reply()
{
@ -709,7 +713,7 @@ impl MasterInternal {
return Err(VhostUserError::InvalidMessage);
}
if body.value != 0 {
return Err(VhostUserError::SlaveInternalError);
return Err(VhostUserError::BackendInternalError);
}
Ok(())
}
@ -740,7 +744,11 @@ impl MasterInternal {
}
#[inline]
fn new_request_header(&self, request: MasterReq, size: u32) -> VhostUserMsgHeader<MasterReq> {
fn new_request_header(
&self,
request: FrontendReq,
size: u32,
) -> VhostUserMsgHeader<FrontendReq> {
VhostUserMsgHeader::new(request, self.hdr_flags.bits() | 0x1, size)
}
}
@ -760,36 +768,36 @@ mod tests {
))
}
fn create_pair<P: AsRef<Path>>(path: P) -> (Master, Endpoint<MasterReq>) {
fn create_pair<P: AsRef<Path>>(path: P) -> (Frontend, Endpoint<FrontendReq>) {
let listener = Listener::new(&path, true).unwrap();
listener.set_nonblocking(true).unwrap();
let master = Master::connect(path, 2).unwrap();
let slave = listener.accept().unwrap().unwrap();
(master, Endpoint::from_stream(slave))
let frontend = Frontend::connect(path, 2).unwrap();
let backend = listener.accept().unwrap().unwrap();
(frontend, Endpoint::from_stream(backend))
}
#[test]
fn create_master() {
fn create_frontend() {
let path = temp_path();
let listener = Listener::new(&path, true).unwrap();
listener.set_nonblocking(true).unwrap();
let master = Master::connect(&path, 1).unwrap();
let mut slave = Endpoint::<MasterReq>::from_stream(listener.accept().unwrap().unwrap());
let frontend = Frontend::connect(&path, 1).unwrap();
let mut backend = Endpoint::<FrontendReq>::from_stream(listener.accept().unwrap().unwrap());
assert!(master.as_raw_fd() > 0);
assert!(frontend.as_raw_fd() > 0);
// Send two messages continuously
master.set_owner().unwrap();
master.reset_owner().unwrap();
frontend.set_owner().unwrap();
frontend.reset_owner().unwrap();
let (hdr, rfds) = slave.recv_header().unwrap();
assert_eq!(hdr.get_code().unwrap(), MasterReq::SET_OWNER);
let (hdr, rfds) = backend.recv_header().unwrap();
assert_eq!(hdr.get_code().unwrap(), FrontendReq::SET_OWNER);
assert_eq!(hdr.get_size(), 0);
assert_eq!(hdr.get_version(), 0x1);
assert!(rfds.is_none());
let (hdr, rfds) = slave.recv_header().unwrap();
assert_eq!(hdr.get_code().unwrap(), MasterReq::RESET_OWNER);
let (hdr, rfds) = backend.recv_header().unwrap();
assert_eq!(hdr.get_code().unwrap(), FrontendReq::RESET_OWNER);
assert_eq!(hdr.get_size(), 0);
assert_eq!(hdr.get_version(), 0x1);
assert!(rfds.is_none());
@ -800,134 +808,134 @@ mod tests {
let path = temp_path();
let _ = Listener::new(&path, true).unwrap();
let _ = Listener::new(&path, false).is_err();
assert!(Master::connect(&path, 1).is_err());
assert!(Frontend::connect(&path, 1).is_err());
let listener = Listener::new(&path, true).unwrap();
assert!(Listener::new(&path, false).is_err());
listener.set_nonblocking(true).unwrap();
let _master = Master::connect(&path, 1).unwrap();
let _slave = listener.accept().unwrap().unwrap();
let _frontend = Frontend::connect(&path, 1).unwrap();
let _backend = listener.accept().unwrap().unwrap();
}
#[test]
fn test_features() {
let path = temp_path();
let (master, mut peer) = create_pair(path);
let (frontend, mut peer) = create_pair(path);
master.set_owner().unwrap();
frontend.set_owner().unwrap();
let (hdr, rfds) = peer.recv_header().unwrap();
assert_eq!(hdr.get_code().unwrap(), MasterReq::SET_OWNER);
assert_eq!(hdr.get_code().unwrap(), FrontendReq::SET_OWNER);
assert_eq!(hdr.get_size(), 0);
assert_eq!(hdr.get_version(), 0x1);
assert!(rfds.is_none());
let hdr = VhostUserMsgHeader::new(MasterReq::GET_FEATURES, 0x4, 8);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0x4, 8);
let msg = VhostUserU64::new(0x15);
peer.send_message(&hdr, &msg, None).unwrap();
let features = master.get_features().unwrap();
let features = frontend.get_features().unwrap();
assert_eq!(features, 0x15u64);
let (_hdr, rfds) = peer.recv_header().unwrap();
assert!(rfds.is_none());
let hdr = VhostUserMsgHeader::new(MasterReq::SET_FEATURES, 0x4, 8);
let hdr = VhostUserMsgHeader::new(FrontendReq::SET_FEATURES, 0x4, 8);
let msg = VhostUserU64::new(0x15);
peer.send_message(&hdr, &msg, None).unwrap();
master.set_features(0x15).unwrap();
frontend.set_features(0x15).unwrap();
let (_hdr, msg, rfds) = peer.recv_body::<VhostUserU64>().unwrap();
assert!(rfds.is_none());
let val = msg.value;
assert_eq!(val, 0x15);
let hdr = VhostUserMsgHeader::new(MasterReq::GET_FEATURES, 0x4, 8);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0x4, 8);
let msg = 0x15u32;
peer.send_message(&hdr, &msg, None).unwrap();
assert!(master.get_features().is_err());
assert!(frontend.get_features().is_err());
}
#[test]
fn test_protocol_features() {
let path = temp_path();
let (mut master, mut peer) = create_pair(path);
let (mut frontend, mut peer) = create_pair(path);
master.set_owner().unwrap();
frontend.set_owner().unwrap();
let (hdr, rfds) = peer.recv_header().unwrap();
assert_eq!(hdr.get_code().unwrap(), MasterReq::SET_OWNER);
assert_eq!(hdr.get_code().unwrap(), FrontendReq::SET_OWNER);
assert!(rfds.is_none());
assert!(master.get_protocol_features().is_err());
assert!(master
assert!(frontend.get_protocol_features().is_err());
assert!(frontend
.set_protocol_features(VhostUserProtocolFeatures::all())
.is_err());
let vfeatures = 0x15 | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
let hdr = VhostUserMsgHeader::new(MasterReq::GET_FEATURES, 0x4, 8);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0x4, 8);
let msg = VhostUserU64::new(vfeatures);
peer.send_message(&hdr, &msg, None).unwrap();
let features = master.get_features().unwrap();
let features = frontend.get_features().unwrap();
assert_eq!(features, vfeatures);
let (_hdr, rfds) = peer.recv_header().unwrap();
assert!(rfds.is_none());
master.set_features(vfeatures).unwrap();
frontend.set_features(vfeatures).unwrap();
let (_hdr, msg, rfds) = peer.recv_body::<VhostUserU64>().unwrap();
assert!(rfds.is_none());
let val = msg.value;
assert_eq!(val, vfeatures);
let pfeatures = VhostUserProtocolFeatures::all();
let hdr = VhostUserMsgHeader::new(MasterReq::GET_PROTOCOL_FEATURES, 0x4, 8);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_PROTOCOL_FEATURES, 0x4, 8);
let msg = VhostUserU64::new(pfeatures.bits());
peer.send_message(&hdr, &msg, None).unwrap();
let features = master.get_protocol_features().unwrap();
let features = frontend.get_protocol_features().unwrap();
assert_eq!(features, pfeatures);
let (_hdr, rfds) = peer.recv_header().unwrap();
assert!(rfds.is_none());
master.set_protocol_features(pfeatures).unwrap();
frontend.set_protocol_features(pfeatures).unwrap();
let (_hdr, msg, rfds) = peer.recv_body::<VhostUserU64>().unwrap();
assert!(rfds.is_none());
let val = msg.value;
assert_eq!(val, pfeatures.bits());
let hdr = VhostUserMsgHeader::new(MasterReq::SET_PROTOCOL_FEATURES, 0x4, 8);
let hdr = VhostUserMsgHeader::new(FrontendReq::SET_PROTOCOL_FEATURES, 0x4, 8);
let msg = VhostUserU64::new(pfeatures.bits());
peer.send_message(&hdr, &msg, None).unwrap();
assert!(master.get_protocol_features().is_err());
assert!(frontend.get_protocol_features().is_err());
}
#[test]
fn test_master_set_config_negative() {
fn test_frontend_set_config_negative() {
let path = temp_path();
let (mut master, _peer) = create_pair(path);
let (mut frontend, _peer) = create_pair(path);
let buf = vec![0x0; MAX_MSG_SIZE + 1];
master
frontend
.set_config(0x100, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.unwrap_err();
{
let mut node = master.node();
let mut node = frontend.node();
node.virtio_features = 0xffff_ffff;
node.acked_virtio_features = 0xffff_ffff;
node.protocol_features = 0xffff_ffff;
node.acked_protocol_features = 0xffff_ffff;
}
master
frontend
.set_config(0, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.unwrap();
master
frontend
.set_config(
VHOST_USER_CONFIG_SIZE,
VhostUserConfigFlags::WRITABLE,
&buf[0..4],
)
.unwrap_err();
master
frontend
.set_config(0x1000, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.unwrap_err();
master
frontend
.set_config(
0x100,
// SAFETY: This is a negative test, so we are setting unexpected flags.
@ -935,176 +943,176 @@ mod tests {
&buf[0..4],
)
.unwrap_err();
master
frontend
.set_config(VHOST_USER_CONFIG_SIZE, VhostUserConfigFlags::WRITABLE, &buf)
.unwrap_err();
master
frontend
.set_config(VHOST_USER_CONFIG_SIZE, VhostUserConfigFlags::WRITABLE, &[])
.unwrap_err();
}
fn create_pair2() -> (Master, Endpoint<MasterReq>) {
fn create_pair2() -> (Frontend, Endpoint<FrontendReq>) {
let path = temp_path();
let (master, peer) = create_pair(path);
let (frontend, peer) = create_pair(path);
{
let mut node = master.node();
let mut node = frontend.node();
node.virtio_features = 0xffff_ffff;
node.acked_virtio_features = 0xffff_ffff;
node.protocol_features = 0xffff_ffff;
node.acked_protocol_features = 0xffff_ffff;
}
(master, peer)
(frontend, peer)
}
#[test]
fn test_master_get_config_negative0() {
let (mut master, mut peer) = create_pair2();
fn test_frontend_get_config_negative0() {
let (mut frontend, mut peer) = create_pair2();
let buf = vec![0x0; MAX_MSG_SIZE + 1];
let mut hdr = VhostUserMsgHeader::new(MasterReq::GET_CONFIG, 0x4, 16);
let mut hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_ok());
hdr.set_code(MasterReq::GET_FEATURES);
hdr.set_code(FrontendReq::GET_FEATURES);
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_err());
hdr.set_code(MasterReq::GET_CONFIG);
hdr.set_code(FrontendReq::GET_CONFIG);
}
#[test]
fn test_master_get_config_negative1() {
let (mut master, mut peer) = create_pair2();
fn test_frontend_get_config_negative1() {
let (mut frontend, mut peer) = create_pair2();
let buf = vec![0x0; MAX_MSG_SIZE + 1];
let mut hdr = VhostUserMsgHeader::new(MasterReq::GET_CONFIG, 0x4, 16);
let mut hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_ok());
hdr.set_reply(false);
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_err());
}
#[test]
fn test_master_get_config_negative2() {
let (mut master, mut peer) = create_pair2();
fn test_frontend_get_config_negative2() {
let (mut frontend, mut peer) = create_pair2();
let buf = vec![0x0; MAX_MSG_SIZE + 1];
let hdr = VhostUserMsgHeader::new(MasterReq::GET_CONFIG, 0x4, 16);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_ok());
}
#[test]
fn test_master_get_config_negative3() {
let (mut master, mut peer) = create_pair2();
fn test_frontend_get_config_negative3() {
let (mut frontend, mut peer) = create_pair2();
let buf = vec![0x0; MAX_MSG_SIZE + 1];
let hdr = VhostUserMsgHeader::new(MasterReq::GET_CONFIG, 0x4, 16);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_ok());
msg.offset = 0;
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_err());
}
#[test]
fn test_master_get_config_negative4() {
let (mut master, mut peer) = create_pair2();
fn test_frontend_get_config_negative4() {
let (mut frontend, mut peer) = create_pair2();
let buf = vec![0x0; MAX_MSG_SIZE + 1];
let hdr = VhostUserMsgHeader::new(MasterReq::GET_CONFIG, 0x4, 16);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_ok());
msg.offset = 0x101;
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_err());
}
#[test]
fn test_master_get_config_negative5() {
let (mut master, mut peer) = create_pair2();
fn test_frontend_get_config_negative5() {
let (mut frontend, mut peer) = create_pair2();
let buf = vec![0x0; MAX_MSG_SIZE + 1];
let hdr = VhostUserMsgHeader::new(MasterReq::GET_CONFIG, 0x4, 16);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_ok());
msg.offset = (MAX_MSG_SIZE + 1) as u32;
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_err());
}
#[test]
fn test_master_get_config_negative6() {
let (mut master, mut peer) = create_pair2();
fn test_frontend_get_config_negative6() {
let (mut frontend, mut peer) = create_pair2();
let buf = vec![0x0; MAX_MSG_SIZE + 1];
let hdr = VhostUserMsgHeader::new(MasterReq::GET_CONFIG, 0x4, 16);
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_ok());
msg.size = 6;
peer.send_message_with_payload(&hdr, &msg, &buf[0..6], None)
.unwrap();
assert!(master
assert!(frontend
.get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
.is_err());
}
#[test]
fn test_maset_set_mem_table_failure() {
let (master, _peer) = create_pair2();
let (frontend, _peer) = create_pair2();
master.set_mem_table(&[]).unwrap_err();
frontend.set_mem_table(&[]).unwrap_err();
let tables = vec![VhostUserMemoryRegionInfo::default(); MAX_ATTACHED_FD_ENTRIES + 1];
master.set_mem_table(&tables).unwrap_err();
frontend.set_mem_table(&tables).unwrap_err();
}
}

View file

@ -11,45 +11,45 @@ use super::connection::Endpoint;
use super::message::*;
use super::{Error, HandlerResult, Result};
/// Define services provided by masters for the slave communication channel.
/// Define services provided by frontends for the backend communication channel.
///
/// The vhost-user specification defines a slave communication channel, by which slaves could
/// request services from masters. The [VhostUserMasterReqHandler] trait defines services provided
/// by masters, and it's used both on the master side and slave side.
/// - on the slave side, a stub forwarder implementing [VhostUserMasterReqHandler] will proxy
/// service requests to masters. The [Slave] is an example stub forwarder.
/// - on the master side, the [MasterReqHandler] will forward service requests to a handler
/// implementing [VhostUserMasterReqHandler].
/// The vhost-user specification defines a backend communication channel, by which backends could
/// request services from frontends. The [VhostUserFrontendReqHandler] trait defines services provided
/// by frontends, and it's used both on the frontend side and backend side.
/// - on the backend side, a stub forwarder implementing [VhostUserFrontendReqHandler] will proxy
/// service requests to frontends. The [Backend] is an example stub forwarder.
/// - on the frontend side, the [FrontendReqHandler] will forward service requests to a handler
/// implementing [VhostUserFrontendReqHandler].
///
/// The [VhostUserMasterReqHandler] trait is design with interior mutability to improve performance
/// The [VhostUserFrontendReqHandler] trait is design with interior mutability to improve performance
/// for multi-threading.
///
/// [VhostUserMasterReqHandler]: trait.VhostUserMasterReqHandler.html
/// [MasterReqHandler]: struct.MasterReqHandler.html
/// [Slave]: struct.Slave.html
pub trait VhostUserMasterReqHandler {
/// [VhostUserFrontendReqHandler]: trait.VhostUserFrontendReqHandler.html
/// [FrontendReqHandler]: struct.FrontendReqHandler.html
/// [Backend]: struct.Backend.html
pub trait VhostUserFrontendReqHandler {
/// Handle device configuration change notifications.
fn handle_config_change(&self) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
/// Handle virtio-fs map file requests.
fn fs_slave_map(&self, _fs: &VhostUserFSSlaveMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
fn fs_backend_map(&self, _fs: &VhostUserFSBackendMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
/// Handle virtio-fs unmap file requests.
fn fs_slave_unmap(&self, _fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
fn fs_backend_unmap(&self, _fs: &VhostUserFSBackendMsg) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
/// Handle virtio-fs sync file requests.
fn fs_slave_sync(&self, _fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
fn fs_backend_sync(&self, _fs: &VhostUserFSBackendMsg) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
/// Handle virtio-fs file IO requests.
fn fs_slave_io(&self, _fs: &VhostUserFSSlaveMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
fn fs_backend_io(&self, _fs: &VhostUserFSBackendMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
@ -57,32 +57,40 @@ pub trait VhostUserMasterReqHandler {
// fn handle_vring_host_notifier(&mut self, area: VhostUserVringArea, fd: &dyn AsRawFd);
}
/// A helper trait mirroring [VhostUserMasterReqHandler] but without interior mutability.
/// A helper trait mirroring [VhostUserFrontendReqHandler] but without interior mutability.
///
/// [VhostUserMasterReqHandler]: trait.VhostUserMasterReqHandler.html
pub trait VhostUserMasterReqHandlerMut {
/// [VhostUserFrontendReqHandler]: trait.VhostUserFrontendReqHandler.html
pub trait VhostUserFrontendReqHandlerMut {
/// Handle device configuration change notifications.
fn handle_config_change(&mut self) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
/// Handle virtio-fs map file requests.
fn fs_slave_map(&mut self, _fs: &VhostUserFSSlaveMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
fn fs_backend_map(
&mut self,
_fs: &VhostUserFSBackendMsg,
_fd: &dyn AsRawFd,
) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
/// Handle virtio-fs unmap file requests.
fn fs_slave_unmap(&mut self, _fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
fn fs_backend_unmap(&mut self, _fs: &VhostUserFSBackendMsg) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
/// Handle virtio-fs sync file requests.
fn fs_slave_sync(&mut self, _fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
fn fs_backend_sync(&mut self, _fs: &VhostUserFSBackendMsg) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
/// Handle virtio-fs file IO requests.
fn fs_slave_io(&mut self, _fs: &VhostUserFSSlaveMsg, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
fn fs_backend_io(
&mut self,
_fs: &VhostUserFSBackendMsg,
_fd: &dyn AsRawFd,
) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
@ -90,39 +98,39 @@ pub trait VhostUserMasterReqHandlerMut {
// fn handle_vring_host_notifier(&mut self, area: VhostUserVringArea, fd: RawFd);
}
impl<S: VhostUserMasterReqHandlerMut> VhostUserMasterReqHandler for Mutex<S> {
impl<S: VhostUserFrontendReqHandlerMut> VhostUserFrontendReqHandler for Mutex<S> {
fn handle_config_change(&self) -> HandlerResult<u64> {
self.lock().unwrap().handle_config_change()
}
fn fs_slave_map(&self, fs: &VhostUserFSSlaveMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> {
self.lock().unwrap().fs_slave_map(fs, fd)
fn fs_backend_map(&self, fs: &VhostUserFSBackendMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> {
self.lock().unwrap().fs_backend_map(fs, fd)
}
fn fs_slave_unmap(&self, fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
self.lock().unwrap().fs_slave_unmap(fs)
fn fs_backend_unmap(&self, fs: &VhostUserFSBackendMsg) -> HandlerResult<u64> {
self.lock().unwrap().fs_backend_unmap(fs)
}
fn fs_slave_sync(&self, fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
self.lock().unwrap().fs_slave_sync(fs)
fn fs_backend_sync(&self, fs: &VhostUserFSBackendMsg) -> HandlerResult<u64> {
self.lock().unwrap().fs_backend_sync(fs)
}
fn fs_slave_io(&self, fs: &VhostUserFSSlaveMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> {
self.lock().unwrap().fs_slave_io(fs, fd)
fn fs_backend_io(&self, fs: &VhostUserFSBackendMsg, fd: &dyn AsRawFd) -> HandlerResult<u64> {
self.lock().unwrap().fs_backend_io(fs, fd)
}
}
/// Server to handle service requests from slaves from the slave communication channel.
/// Server to handle service requests from backends from the backend communication channel.
///
/// The [MasterReqHandler] acts as a server on the master side, to handle service requests from
/// slaves on the slave communication channel. It's actually a proxy invoking the registered
/// handler implementing [VhostUserMasterReqHandler] to do the real work.
/// The [FrontendReqHandler] acts as a server on the frontend side, to handle service requests from
/// backends on the backend communication channel. It's actually a proxy invoking the registered
/// handler implementing [VhostUserFrontendReqHandler] to do the real work.
///
/// [MasterReqHandler]: struct.MasterReqHandler.html
/// [VhostUserMasterReqHandler]: trait.VhostUserMasterReqHandler.html
pub struct MasterReqHandler<S: VhostUserMasterReqHandler> {
/// [FrontendReqHandler]: struct.FrontendReqHandler.html
/// [VhostUserFrontendReqHandler]: trait.VhostUserFrontendReqHandler.html
pub struct FrontendReqHandler<S: VhostUserFrontendReqHandler> {
// underlying Unix domain socket for communication
sub_sock: Endpoint<SlaveReq>,
sub_sock: Endpoint<BackendReq>,
tx_sock: UnixStream,
// Protocol feature VHOST_USER_PROTOCOL_F_REPLY_ACK has been negotiated.
reply_ack_negotiated: bool,
@ -132,20 +140,20 @@ pub struct MasterReqHandler<S: VhostUserMasterReqHandler> {
error: Option<i32>,
}
impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
/// Create a server to handle service requests from slaves on the slave communication channel.
impl<S: VhostUserFrontendReqHandler> FrontendReqHandler<S> {
/// Create a server to handle service requests from backends on the backend communication channel.
///
/// This opens a pair of connected anonymous sockets to form the slave communication channel.
/// The socket fd returned by [Self::get_tx_raw_fd()] should be sent to the slave by
/// [VhostUserMaster::set_slave_request_fd()].
/// This opens a pair of connected anonymous sockets to form the backend communication channel.
/// The socket fd returned by [Self::get_tx_raw_fd()] should be sent to the backend by
/// [VhostUserFrontend::set_backend_request_fd()].
///
/// [Self::get_tx_raw_fd()]: struct.MasterReqHandler.html#method.get_tx_raw_fd
/// [VhostUserMaster::set_slave_request_fd()]: trait.VhostUserMaster.html#tymethod.set_slave_request_fd
/// [Self::get_tx_raw_fd()]: struct.FrontendReqHandler.html#method.get_tx_raw_fd
/// [VhostUserFrontend::set_backend_request_fd()]: trait.VhostUserFrontend.html#tymethod.set_backend_request_fd
pub fn new(backend: Arc<S>) -> Result<Self> {
let (tx, rx) = UnixStream::pair().map_err(Error::SocketError)?;
Ok(MasterReqHandler {
sub_sock: Endpoint::<SlaveReq>::from_stream(rx),
Ok(FrontendReqHandler {
sub_sock: Endpoint::<BackendReq>::from_stream(rx),
tx_sock: tx,
reply_ack_negotiated: false,
backend,
@ -153,11 +161,11 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
})
}
/// Get the socket fd for the slave to communication with the master.
/// Get the socket fd for the backend to communication with the frontend.
///
/// The returned fd should be sent to the slave by [VhostUserMaster::set_slave_request_fd()].
/// The returned fd should be sent to the backend by [VhostUserFrontend::set_backend_request_fd()].
///
/// [VhostUserMaster::set_slave_request_fd()]: trait.VhostUserMaster.html#tymethod.set_slave_request_fd
/// [VhostUserFrontend::set_backend_request_fd()]: trait.VhostUserFrontend.html#tymethod.set_backend_request_fd
pub fn get_tx_raw_fd(&self) -> RawFd {
self.tx_sock.as_raw_fd()
}
@ -165,7 +173,7 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
/// Set the negotiation state of the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature.
///
/// When the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature has been negotiated,
/// the "REPLY_ACK" flag will be set in the message header for every slave to master request
/// the "REPLY_ACK" flag will be set in the message header for every backend to frontend request
/// message.
pub fn set_reply_ack_flag(&mut self, enable: bool) {
self.reply_ack_negotiated = enable;
@ -180,7 +188,7 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
}
}
/// Main entrance to server slave request from the slave communication channel.
/// Main entrance to server backend request from the backend communication channel.
///
/// The caller needs to:
/// - serialize calls to this function
@ -216,36 +224,36 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
};
let res = match hdr.get_code() {
Ok(SlaveReq::CONFIG_CHANGE_MSG) => {
Ok(BackendReq::CONFIG_CHANGE_MSG) => {
self.check_msg_size(&hdr, size, 0)?;
self.backend
.handle_config_change()
.map_err(Error::ReqHandlerError)
}
Ok(SlaveReq::FS_MAP) => {
let msg = self.extract_msg_body::<VhostUserFSSlaveMsg>(&hdr, size, &buf)?;
Ok(BackendReq::FS_MAP) => {
let msg = self.extract_msg_body::<VhostUserFSBackendMsg>(&hdr, size, &buf)?;
// check_attached_files() has validated files
self.backend
.fs_slave_map(&msg, &files.unwrap()[0])
.fs_backend_map(&msg, &files.unwrap()[0])
.map_err(Error::ReqHandlerError)
}
Ok(SlaveReq::FS_UNMAP) => {
let msg = self.extract_msg_body::<VhostUserFSSlaveMsg>(&hdr, size, &buf)?;
Ok(BackendReq::FS_UNMAP) => {
let msg = self.extract_msg_body::<VhostUserFSBackendMsg>(&hdr, size, &buf)?;
self.backend
.fs_slave_unmap(&msg)
.fs_backend_unmap(&msg)
.map_err(Error::ReqHandlerError)
}
Ok(SlaveReq::FS_SYNC) => {
let msg = self.extract_msg_body::<VhostUserFSSlaveMsg>(&hdr, size, &buf)?;
Ok(BackendReq::FS_SYNC) => {
let msg = self.extract_msg_body::<VhostUserFSBackendMsg>(&hdr, size, &buf)?;
self.backend
.fs_slave_sync(&msg)
.fs_backend_sync(&msg)
.map_err(Error::ReqHandlerError)
}
Ok(SlaveReq::FS_IO) => {
let msg = self.extract_msg_body::<VhostUserFSSlaveMsg>(&hdr, size, &buf)?;
Ok(BackendReq::FS_IO) => {
let msg = self.extract_msg_body::<VhostUserFSBackendMsg>(&hdr, size, &buf)?;
// check_attached_files() has validated files
self.backend
.fs_slave_io(&msg, &files.unwrap()[0])
.fs_backend_io(&msg, &files.unwrap()[0])
.map_err(Error::ReqHandlerError)
}
_ => Err(Error::InvalidMessage),
@ -265,7 +273,7 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
fn check_msg_size(
&self,
hdr: &VhostUserMsgHeader<SlaveReq>,
hdr: &VhostUserMsgHeader<BackendReq>,
size: usize,
expected: usize,
) -> Result<()> {
@ -281,11 +289,11 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
fn check_attached_files(
&self,
hdr: &VhostUserMsgHeader<SlaveReq>,
hdr: &VhostUserMsgHeader<BackendReq>,
files: &Option<Vec<File>>,
) -> Result<()> {
match hdr.get_code() {
Ok(SlaveReq::FS_MAP | SlaveReq::FS_IO) => {
Ok(BackendReq::FS_MAP | BackendReq::FS_IO) => {
// Expect a single file is passed.
match files {
Some(files) if files.len() == 1 => Ok(()),
@ -299,7 +307,7 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
fn extract_msg_body<T: Sized + VhostUserMsgValidator>(
&self,
hdr: &VhostUserMsgHeader<SlaveReq>,
hdr: &VhostUserMsgHeader<BackendReq>,
size: usize,
buf: &[u8],
) -> Result<T> {
@ -314,8 +322,8 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
fn new_reply_header<T: Sized>(
&self,
req: &VhostUserMsgHeader<SlaveReq>,
) -> Result<VhostUserMsgHeader<SlaveReq>> {
req: &VhostUserMsgHeader<BackendReq>,
) -> Result<VhostUserMsgHeader<BackendReq>> {
if mem::size_of::<T>() > MAX_MSG_SIZE {
return Err(Error::InvalidParam);
}
@ -329,7 +337,7 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
fn send_ack_message(
&mut self,
req: &VhostUserMsgHeader<SlaveReq>,
req: &VhostUserMsgHeader<BackendReq>,
res: &Result<u64>,
) -> Result<()> {
if self.reply_ack_negotiated && req.is_need_reply() {
@ -352,7 +360,7 @@ impl<S: VhostUserMasterReqHandler> MasterReqHandler<S> {
}
}
impl<S: VhostUserMasterReqHandler> AsRawFd for MasterReqHandler<S> {
impl<S: VhostUserFrontendReqHandler> AsRawFd for FrontendReqHandler<S> {
fn as_raw_fd(&self) -> RawFd {
self.sub_sock.as_raw_fd()
}
@ -362,33 +370,33 @@ impl<S: VhostUserMasterReqHandler> AsRawFd for MasterReqHandler<S> {
mod tests {
use super::*;
#[cfg(feature = "vhost-user-slave")]
use crate::vhost_user::Slave;
#[cfg(feature = "vhost-user-slave")]
#[cfg(feature = "vhost-user-backend")]
use crate::vhost_user::Backend;
#[cfg(feature = "vhost-user-backend")]
use std::os::unix::io::FromRawFd;
struct MockMasterReqHandler {}
struct MockFrontendReqHandler {}
impl VhostUserMasterReqHandlerMut for MockMasterReqHandler {
/// Handle virtio-fs map file requests from the slave.
fn fs_slave_map(
impl VhostUserFrontendReqHandlerMut for MockFrontendReqHandler {
/// Handle virtio-fs map file requests from the backend.
fn fs_backend_map(
&mut self,
_fs: &VhostUserFSSlaveMsg,
_fs: &VhostUserFSBackendMsg,
_fd: &dyn AsRawFd,
) -> HandlerResult<u64> {
Ok(0)
}
/// Handle virtio-fs unmap file requests from the slave.
fn fs_slave_unmap(&mut self, _fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
/// Handle virtio-fs unmap file requests from the backend.
fn fs_backend_unmap(&mut self, _fs: &VhostUserFSBackendMsg) -> HandlerResult<u64> {
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
}
}
#[test]
fn test_new_master_req_handler() {
let backend = Arc::new(Mutex::new(MockMasterReqHandler {}));
let mut handler = MasterReqHandler::new(backend).unwrap();
fn test_new_frontend_req_handler() {
let backend = Arc::new(Mutex::new(MockFrontendReqHandler {}));
let mut handler = FrontendReqHandler::new(backend).unwrap();
assert!(handler.get_tx_raw_fd() >= 0);
assert!(handler.as_raw_fd() >= 0);
@ -400,11 +408,11 @@ mod tests {
handler.check_state().unwrap_err();
}
#[cfg(feature = "vhost-user-slave")]
#[cfg(feature = "vhost-user-backend")]
#[test]
fn test_master_slave_req_handler() {
let backend = Arc::new(Mutex::new(MockMasterReqHandler {}));
let mut handler = MasterReqHandler::new(backend).unwrap();
fn test_frontend_backend_req_handler() {
let backend = Arc::new(Mutex::new(MockFrontendReqHandler {}));
let mut handler = FrontendReqHandler::new(backend).unwrap();
// SAFETY: Safe because `handler` contains valid fds, and we are
// checking if `dup` returns a valid fd.
@ -414,7 +422,7 @@ mod tests {
}
// SAFETY: Safe because we checked if fd is valid.
let stream = unsafe { UnixStream::from_raw_fd(fd) };
let slave = Slave::from_stream(stream);
let backend = Backend::from_stream(stream);
std::thread::spawn(move || {
let res = handler.handle_request().unwrap();
@ -422,21 +430,21 @@ mod tests {
handler.handle_request().unwrap_err();
});
slave
.fs_slave_map(&VhostUserFSSlaveMsg::default(), &fd)
backend
.fs_backend_map(&VhostUserFSBackendMsg::default(), &fd)
.unwrap();
// When REPLY_ACK has not been negotiated, the master has no way to detect failure from
// slave side.
slave
.fs_slave_unmap(&VhostUserFSSlaveMsg::default())
// When REPLY_ACK has not been negotiated, the frontend has no way to detect failure from
// backend side.
backend
.fs_backend_unmap(&VhostUserFSBackendMsg::default())
.unwrap();
}
#[cfg(feature = "vhost-user-slave")]
#[cfg(feature = "vhost-user-backend")]
#[test]
fn test_master_slave_req_handler_with_ack() {
let backend = Arc::new(Mutex::new(MockMasterReqHandler {}));
let mut handler = MasterReqHandler::new(backend).unwrap();
fn test_frontend_backend_req_handler_with_ack() {
let backend = Arc::new(Mutex::new(MockFrontendReqHandler {}));
let mut handler = FrontendReqHandler::new(backend).unwrap();
handler.set_reply_ack_flag(true);
// SAFETY: Safe because `handler` contains valid fds, and we are
@ -447,7 +455,7 @@ mod tests {
}
// SAFETY: Safe because we checked if fd is valid.
let stream = unsafe { UnixStream::from_raw_fd(fd) };
let slave = Slave::from_stream(stream);
let backend = Backend::from_stream(stream);
std::thread::spawn(move || {
let res = handler.handle_request().unwrap();
@ -455,12 +463,12 @@ mod tests {
handler.handle_request().unwrap_err();
});
slave.set_reply_ack_flag(true);
slave
.fs_slave_map(&VhostUserFSSlaveMsg::default(), &fd)
backend.set_reply_ack_flag(true);
backend
.fs_backend_map(&VhostUserFSBackendMsg::default(), &fd)
.unwrap();
slave
.fs_slave_unmap(&VhostUserFSSlaveMsg::default())
backend
.fs_backend_unmap(&VhostUserFSBackendMsg::default())
.unwrap_err();
}
}

View file

@ -55,21 +55,21 @@ pub(super) trait Req:
fn is_valid(value: u32) -> bool;
}
/// Type of requests sending from masters to slaves.
/// Type of requests sending from frontends to backends.
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum MasterReq {
pub enum FrontendReq {
/// Null operation.
NOOP = 0,
/// Get from the underlying vhost implementation the features bit mask.
GET_FEATURES = 1,
/// Enable features in the underlying vhost implementation using a bit mask.
SET_FEATURES = 2,
/// Set the current Master as an owner of the session.
/// Set the current Frontend as an owner of the session.
SET_OWNER = 3,
/// No longer used.
RESET_OWNER = 4,
/// Set the memory map regions on the slave so it can translate the vring addresses.
/// Set the memory map regions on the backend so it can translate the vring addresses.
SET_MEM_TABLE = 5,
/// Set logging shared memory space.
SET_LOG_BASE = 6,
@ -95,15 +95,15 @@ pub enum MasterReq {
SET_PROTOCOL_FEATURES = 16,
/// Query how many queues the backend supports.
GET_QUEUE_NUM = 17,
/// Signal slave to enable or disable corresponding vring.
/// Signal backend to enable or disable corresponding vring.
SET_VRING_ENABLE = 18,
/// Ask vhost user backend to broadcast a fake RARP to notify the migration is terminated
/// for guest that does not support GUEST_ANNOUNCE.
SEND_RARP = 19,
/// Set host MTU value exposed to the guest.
NET_SET_MTU = 20,
/// Set the socket file descriptor for slave initiated requests.
SET_SLAVE_REQ_FD = 21,
/// Set the socket file descriptor for backend initiated requests.
SET_BACKEND_REQ_FD = 21,
/// Send IOTLB messages with struct vhost_iotlb_msg as payload.
IOTLB_MSG = 22,
/// Set the endianness of a VQ for legacy devices.
@ -116,15 +116,15 @@ pub enum MasterReq {
CREATE_CRYPTO_SESSION = 26,
/// Close a session for crypto operation.
CLOSE_CRYPTO_SESSION = 27,
/// Advise slave that a migration with postcopy enabled is underway.
/// Advise backend that a migration with postcopy enabled is underway.
POSTCOPY_ADVISE = 28,
/// Advise slave that a transition to postcopy mode has happened.
/// Advise backend that a transition to postcopy mode has happened.
POSTCOPY_LISTEN = 29,
/// Advise that postcopy migration has now completed.
POSTCOPY_END = 30,
/// Get a shared buffer from slave.
/// Get a shared buffer from backend.
GET_INFLIGHT_FD = 31,
/// Send the shared inflight buffer back to slave.
/// Send the shared inflight buffer back to backend.
SET_INFLIGHT_FD = 32,
/// Sets the GPU protocol socket file descriptor.
GPU_SET_SOCKET = 33,
@ -150,22 +150,22 @@ pub enum MasterReq {
MAX_CMD = 41,
}
impl From<MasterReq> for u32 {
fn from(req: MasterReq) -> u32 {
impl From<FrontendReq> for u32 {
fn from(req: FrontendReq) -> u32 {
req as u32
}
}
impl Req for MasterReq {
impl Req for FrontendReq {
fn is_valid(value: u32) -> bool {
(value > MasterReq::NOOP as u32) && (value < MasterReq::MAX_CMD as u32)
(value > FrontendReq::NOOP as u32) && (value < FrontendReq::MAX_CMD as u32)
}
}
/// Type of requests sending from slaves to masters.
/// Type of requests sending from backends to frontends.
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SlaveReq {
pub enum BackendReq {
/// Null operation.
NOOP = 0,
/// Send IOTLB messages with struct vhost_iotlb_msg as payload.
@ -190,15 +190,15 @@ pub enum SlaveReq {
MAX_CMD = 10,
}
impl From<SlaveReq> for u32 {
fn from(req: SlaveReq) -> u32 {
impl From<BackendReq> for u32 {
fn from(req: BackendReq) -> u32 {
req as u32
}
}
impl Req for SlaveReq {
impl Req for BackendReq {
fn is_valid(value: u32) -> bool {
(value > SlaveReq::NOOP as u32) && (value < SlaveReq::MAX_CMD as u32)
(value > BackendReq::NOOP as u32) && (value < BackendReq::MAX_CMD as u32)
}
}
@ -403,19 +403,19 @@ bitflags! {
const REPLY_ACK = 0x0000_0008;
/// Support setting MTU for virtio-net devices.
const MTU = 0x0000_0010;
/// Allow the slave to send requests to the master by an optional communication channel.
const SLAVE_REQ = 0x0000_0020;
/// Support setting slave endian by SET_VRING_ENDIAN.
/// Allow the backend to send requests to the frontend by an optional communication channel.
const BACKEND_REQ = 0x0000_0020;
/// Support setting backend endian by SET_VRING_ENDIAN.
const CROSS_ENDIAN = 0x0000_0040;
/// Support crypto operations.
const CRYPTO_SESSION = 0x0000_0080;
/// Support sending userfault_fd from slaves to masters.
/// Support sending userfault_fd from backends to frontends.
const PAGEFAULT = 0x0000_0100;
/// Support Virtio device configuration.
const CONFIG = 0x0000_0200;
/// Allow the slave to send fds (at most 8 descriptors in each message) to the master.
const SLAVE_SEND_FD = 0x0000_0400;
/// Allow the slave to register a host notifier.
/// Allow the backend to send fds (at most 8 descriptors in each message) to the frontend.
const BACKEND_SEND_FD = 0x0000_0400;
/// Allow the backend to register a host notifier.
const HOST_NOTIFIER = 0x0000_0800;
/// Support inflight shmfd.
const INFLIGHT_SHMFD = 0x0000_1000;
@ -778,9 +778,9 @@ impl VhostUserMsgValidator for VhostUserVringAddr {
bitflags! {
/// Flags for the device configuration message.
pub struct VhostUserConfigFlags: u32 {
/// Vhost master messages used for writeable fields.
/// Vhost frontend messages used for writeable fields.
const WRITABLE = 0x1;
/// Vhost master messages used for live migration.
/// Vhost frontend messages used for live migration.
const LIVE_MIGRATION = 0x2;
}
}
@ -927,11 +927,11 @@ pub struct VhostUserIotlb {
}
*/
// Bit mask for flags in virtio-fs slave messages
// Bit mask for flags in virtio-fs backend messages
bitflags! {
#[derive(Default)]
/// Flags for virtio-fs slave messages.
pub struct VhostUserFSSlaveMsgFlags: u64 {
/// Flags for virtio-fs backend messages.
pub struct VhostUserFSBackendMsgFlags: u64 {
/// Empty permission.
const EMPTY = 0x0;
/// Read permission.
@ -941,30 +941,30 @@ bitflags! {
}
}
/// Max entries in one virtio-fs slave request.
pub const VHOST_USER_FS_SLAVE_ENTRIES: usize = 8;
/// Max entries in one virtio-fs backend request.
pub const VHOST_USER_FS_BACKEND_ENTRIES: usize = 8;
/// Slave request message to update the MMIO window.
/// Backend request message to update the MMIO window.
#[repr(packed)]
#[derive(Copy, Clone, Default)]
pub struct VhostUserFSSlaveMsg {
pub struct VhostUserFSBackendMsg {
/// File offset.
pub fd_offset: [u64; VHOST_USER_FS_SLAVE_ENTRIES],
pub fd_offset: [u64; VHOST_USER_FS_BACKEND_ENTRIES],
/// Offset into the DAX window.
pub cache_offset: [u64; VHOST_USER_FS_SLAVE_ENTRIES],
pub cache_offset: [u64; VHOST_USER_FS_BACKEND_ENTRIES],
/// Size of region to map.
pub len: [u64; VHOST_USER_FS_SLAVE_ENTRIES],
pub len: [u64; VHOST_USER_FS_BACKEND_ENTRIES],
/// Flags for the mmap operation
pub flags: [VhostUserFSSlaveMsgFlags; VHOST_USER_FS_SLAVE_ENTRIES],
pub flags: [VhostUserFSBackendMsgFlags; VHOST_USER_FS_BACKEND_ENTRIES],
}
// SAFETY: Safe because all fields of VhostUserFSSlaveMsg are POD.
unsafe impl ByteValued for VhostUserFSSlaveMsg {}
// SAFETY: Safe because all fields of VhostUserFSBackendMsg are POD.
unsafe impl ByteValued for VhostUserFSBackendMsg {}
impl VhostUserMsgValidator for VhostUserFSSlaveMsg {
impl VhostUserMsgValidator for VhostUserFSBackendMsg {
fn is_valid(&self) -> bool {
for i in 0..VHOST_USER_FS_SLAVE_ENTRIES {
if ({ self.flags[i] }.bits() & !VhostUserFSSlaveMsgFlags::all().bits()) != 0
for i in 0..VHOST_USER_FS_BACKEND_ENTRIES {
if ({ self.flags[i] }.bits() & !VhostUserFSBackendMsgFlags::all().bits()) != 0
|| self.fd_offset[i].checked_add(self.len[i]).is_none()
|| self.cache_offset[i].checked_add(self.len[i]).is_none()
{
@ -1126,33 +1126,33 @@ mod tests {
}
#[test]
fn check_master_request_code() {
assert!(!MasterReq::is_valid(MasterReq::NOOP as _));
assert!(!MasterReq::is_valid(MasterReq::MAX_CMD as _));
assert!(MasterReq::MAX_CMD > MasterReq::NOOP);
let code = MasterReq::GET_FEATURES;
assert!(MasterReq::is_valid(code as _));
fn check_frontend_request_code() {
assert!(!FrontendReq::is_valid(FrontendReq::NOOP as _));
assert!(!FrontendReq::is_valid(FrontendReq::MAX_CMD as _));
assert!(FrontendReq::MAX_CMD > FrontendReq::NOOP);
let code = FrontendReq::GET_FEATURES;
assert!(FrontendReq::is_valid(code as _));
assert_eq!(code, code.clone());
assert!(!MasterReq::is_valid(10000));
assert!(!FrontendReq::is_valid(10000));
}
#[test]
fn check_slave_request_code() {
assert!(!SlaveReq::is_valid(SlaveReq::NOOP as _));
assert!(!SlaveReq::is_valid(SlaveReq::MAX_CMD as _));
assert!(SlaveReq::MAX_CMD > SlaveReq::NOOP);
let code = SlaveReq::CONFIG_CHANGE_MSG;
assert!(SlaveReq::is_valid(code as _));
fn check_backend_request_code() {
assert!(!BackendReq::is_valid(BackendReq::NOOP as _));
assert!(!BackendReq::is_valid(BackendReq::MAX_CMD as _));
assert!(BackendReq::MAX_CMD > BackendReq::NOOP);
let code = BackendReq::CONFIG_CHANGE_MSG;
assert!(BackendReq::is_valid(code as _));
assert_eq!(code, code.clone());
assert!(!SlaveReq::is_valid(10000));
assert!(!BackendReq::is_valid(10000));
}
#[test]
fn msg_header_ops() {
let mut hdr = VhostUserMsgHeader::new(MasterReq::GET_FEATURES, 0, 0x100);
assert_eq!(hdr.get_code().unwrap(), MasterReq::GET_FEATURES);
hdr.set_code(MasterReq::SET_FEATURES);
assert_eq!(hdr.get_code().unwrap(), MasterReq::SET_FEATURES);
let mut hdr = VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0, 0x100);
assert_eq!(hdr.get_code().unwrap(), FrontendReq::GET_FEATURES);
hdr.set_code(FrontendReq::SET_FEATURES);
assert_eq!(hdr.get_code().unwrap(), FrontendReq::SET_FEATURES);
assert_eq!(hdr.get_version(), 0x1);
@ -1181,7 +1181,7 @@ mod tests {
hdr.set_size(0x100);
assert_eq!(hdr.get_size(), 0x100);
assert!(hdr.is_valid());
hdr.set_size((MAX_MSG_SIZE - mem::size_of::<VhostUserMsgHeader<MasterReq>>()) as u32);
hdr.set_size((MAX_MSG_SIZE - mem::size_of::<VhostUserMsgHeader<FrontendReq>>()) as u32);
assert!(hdr.is_valid());
hdr.set_size(0x0);
assert!(hdr.is_valid());
@ -1388,19 +1388,19 @@ mod tests {
}
#[test]
fn test_vhost_user_fs_slave() {
let mut fs_slave = VhostUserFSSlaveMsg::default();
fn test_vhost_user_fs_backend() {
let mut fs_backend = VhostUserFSBackendMsg::default();
assert!(fs_slave.is_valid());
assert!(fs_backend.is_valid());
fs_slave.fd_offset[0] = 0xffff_ffff_ffff_ffff;
fs_slave.len[0] = 0x1;
assert!(!fs_slave.is_valid());
fs_backend.fd_offset[0] = 0xffff_ffff_ffff_ffff;
fs_backend.len[0] = 0x1;
assert!(!fs_backend.is_valid());
assert_ne!(
VhostUserFSSlaveMsgFlags::MAP_R,
VhostUserFSSlaveMsgFlags::MAP_W
VhostUserFSBackendMsgFlags::MAP_R,
VhostUserFSBackendMsgFlags::MAP_W
);
assert_eq!(VhostUserFSSlaveMsgFlags::EMPTY.bits(), 0);
assert_eq!(VhostUserFSBackendMsgFlags::EMPTY.bits(), 0);
}
}

View file

@ -2,18 +2,18 @@
// SPDX-License-Identifier: Apache-2.0
//! The protocol for vhost-user is based on the existing implementation of vhost for the Linux
//! Kernel. The protocol defines two sides of the communication, master and slave. Master is
//! the application that shares its virtqueues. Slave is the consumer of the virtqueues.
//! Kernel. The protocol defines two sides of the communication, frontend and backend. Frontend is
//! the application that shares its virtqueues. Backend is the consumer of the virtqueues.
//!
//! The communication channel between the master and the slave includes two sub channels. One is
//! used to send requests from the master to the slave and optional replies from the slave to the
//! master. This sub channel is created on master startup by connecting to the slave service
//! endpoint. The other is used to send requests from the slave to the master and optional replies
//! from the master to the slave. This sub channel is created by the master issuing a
//! VHOST_USER_SET_SLAVE_REQ_FD request to the slave with an auxiliary file descriptor.
//! The communication channel between the frontend and the backend includes two sub channels. One is
//! used to send requests from the frontend to the backend and optional replies from the backend to the
//! frontend. This sub channel is created on frontend startup by connecting to the backend service
//! endpoint. The other is used to send requests from the backend to the frontend and optional replies
//! from the frontend to the backend. This sub channel is created by the frontend issuing a
//! VHOST_USER_SET_BACKEND_REQ_FD request to the backend with an auxiliary file descriptor.
//!
//! Unix domain socket is used as the underlying communication channel because the master needs to
//! send file descriptors to the slave.
//! Unix domain socket is used as the underlying communication channel because the frontend needs to
//! send file descriptors to the backend.
//!
//! Most messages that can be sent via the Unix domain socket implementing vhost-user have an
//! equivalent ioctl to the kernel implementation.
@ -27,31 +27,31 @@ pub use self::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
mod connection;
pub use self::connection::Listener;
#[cfg(feature = "vhost-user-master")]
mod master;
#[cfg(feature = "vhost-user-master")]
pub use self::master::{Master, VhostUserMaster};
#[cfg(feature = "vhost-user-frontend")]
mod frontend;
#[cfg(feature = "vhost-user-frontend")]
pub use self::frontend::{Frontend, VhostUserFrontend};
#[cfg(feature = "vhost-user")]
mod master_req_handler;
mod frontend_req_handler;
#[cfg(feature = "vhost-user")]
pub use self::master_req_handler::{
MasterReqHandler, VhostUserMasterReqHandler, VhostUserMasterReqHandlerMut,
pub use self::frontend_req_handler::{
FrontendReqHandler, VhostUserFrontendReqHandler, VhostUserFrontendReqHandlerMut,
};
#[cfg(feature = "vhost-user-slave")]
mod slave;
#[cfg(feature = "vhost-user-slave")]
pub use self::slave::SlaveListener;
#[cfg(feature = "vhost-user-slave")]
mod slave_req_handler;
#[cfg(feature = "vhost-user-slave")]
pub use self::slave_req_handler::{
SlaveReqHandler, VhostUserSlaveReqHandler, VhostUserSlaveReqHandlerMut,
#[cfg(feature = "vhost-user-backend")]
mod backend;
#[cfg(feature = "vhost-user-backend")]
pub use self::backend::BackendListener;
#[cfg(feature = "vhost-user-backend")]
mod backend_req_handler;
#[cfg(feature = "vhost-user-backend")]
pub use self::backend_req_handler::{
BackendReqHandler, VhostUserBackendReqHandler, VhostUserBackendReqHandlerMut,
};
#[cfg(feature = "vhost-user-slave")]
mod slave_req;
#[cfg(feature = "vhost-user-slave")]
pub use self::slave_req::Slave;
#[cfg(feature = "vhost-user-backend")]
mod backend_req;
#[cfg(feature = "vhost-user-backend")]
pub use self::backend_req::Backend;
/// Errors for vhost-user operations
#[derive(Debug)]
@ -82,10 +82,10 @@ pub enum Error {
SocketBroken(std::io::Error),
/// Should retry the socket operation again.
SocketRetry(std::io::Error),
/// Failure from the slave side.
SlaveInternalError,
/// Failure from the master side.
MasterInternalError,
/// Failure from the backend side.
BackendInternalError,
/// Failure from the frontend side.
FrontendInternalError,
/// Virtio/protocol features mismatch.
FeatureMismatch,
/// Error from request handler
@ -116,8 +116,8 @@ impl std::fmt::Display for Error {
Error::SocketConnect(e) => write!(f, "can't connect to peer: {}", e),
Error::SocketBroken(e) => write!(f, "socket is broken: {}", e),
Error::SocketRetry(e) => write!(f, "temporary socket error: {}", e),
Error::SlaveInternalError => write!(f, "slave internal error"),
Error::MasterInternalError => write!(f, "Master internal error"),
Error::BackendInternalError => write!(f, "backend internal error"),
Error::FrontendInternalError => write!(f, "Frontend internal error"),
Error::FeatureMismatch => write!(f, "virtio/protocol features mismatch"),
Error::ReqHandlerError(e) => write!(f, "handler failed to handle request: {}", e),
Error::MemFdCreateError => {
@ -144,10 +144,10 @@ impl Error {
Error::PartialMessage => true,
// Should reconnect because the underline socket is broken.
Error::SocketBroken(_) => true,
// Slave internal error, hope it recovers on reconnect.
Error::SlaveInternalError => true,
// Master internal error, hope it recovers on reconnect.
Error::MasterInternalError => true,
// Backend internal error, hope it recovers on reconnect.
Error::BackendInternalError => true,
// Frontend internal error, hope it recovers on reconnect.
Error::FrontendInternalError => true,
// Should just retry the IO operation instead of rebuilding the underline connection.
Error::SocketRetry(_) => false,
// Looks like the peer deliberately disconnected the socket.
@ -218,10 +218,10 @@ pub(crate) fn take_single_file(files: Option<Vec<File>>) -> Option<File> {
Some(files.swap_remove(0))
}
#[cfg(all(test, feature = "vhost-user-slave"))]
mod dummy_slave;
#[cfg(all(test, feature = "vhost-user-backend"))]
mod dummy_backend;
#[cfg(all(test, feature = "vhost-user-master", feature = "vhost-user-slave"))]
#[cfg(all(test, feature = "vhost-user-frontend", feature = "vhost-user-backend"))]
mod tests {
use std::fs::File;
use std::os::unix::io::AsRawFd;
@ -231,7 +231,7 @@ mod tests {
use vmm_sys_util::rand::rand_alphanumerics;
use vmm_sys_util::tempfile::TempFile;
use super::dummy_slave::{DummySlaveReqHandler, VIRTIO_FEATURES};
use super::dummy_backend::{DummyBackendReqHandler, VIRTIO_FEATURES};
use super::message::*;
use super::*;
use crate::backend::VhostBackend;
@ -244,38 +244,38 @@ mod tests {
))
}
fn create_slave<P, S>(path: P, backend: Arc<S>) -> (Master, SlaveReqHandler<S>)
fn create_backend<P, S>(path: P, backend: Arc<S>) -> (Frontend, BackendReqHandler<S>)
where
P: AsRef<Path>,
S: VhostUserSlaveReqHandler,
S: VhostUserBackendReqHandler,
{
let listener = Listener::new(&path, true).unwrap();
let mut slave_listener = SlaveListener::new(listener, backend).unwrap();
let master = Master::connect(&path, 1).unwrap();
(master, slave_listener.accept().unwrap().unwrap())
let mut backend_listener = BackendListener::new(listener, backend).unwrap();
let frontend = Frontend::connect(&path, 1).unwrap();
(frontend, backend_listener.accept().unwrap().unwrap())
}
#[test]
fn create_dummy_slave() {
let slave = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
fn create_dummy_backend() {
let backend = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
slave.set_owner().unwrap();
assert!(slave.set_owner().is_err());
backend.set_owner().unwrap();
assert!(backend.set_owner().is_err());
}
#[test]
fn test_set_owner() {
let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
let backend_be = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
let path = temp_path();
let (master, mut slave) = create_slave(path, slave_be.clone());
let (frontend, mut backend) = create_backend(path, backend_be.clone());
assert!(!slave_be.lock().unwrap().owned);
master.set_owner().unwrap();
slave.handle_request().unwrap();
assert!(slave_be.lock().unwrap().owned);
master.set_owner().unwrap();
assert!(slave.handle_request().is_err());
assert!(slave_be.lock().unwrap().owned);
assert!(!backend_be.lock().unwrap().owned);
frontend.set_owner().unwrap();
backend.handle_request().unwrap();
assert!(backend_be.lock().unwrap().owned);
frontend.set_owner().unwrap();
assert!(backend.handle_request().is_err());
assert!(backend_be.lock().unwrap().owned);
}
#[test]
@ -283,68 +283,68 @@ mod tests {
let mbar = Arc::new(Barrier::new(2));
let sbar = mbar.clone();
let path = temp_path();
let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
let (mut master, mut slave) = create_slave(path, slave_be.clone());
let backend_be = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
let (mut frontend, mut backend) = create_backend(path, backend_be.clone());
thread::spawn(move || {
slave.handle_request().unwrap();
assert!(slave_be.lock().unwrap().owned);
backend.handle_request().unwrap();
assert!(backend_be.lock().unwrap().owned);
slave.handle_request().unwrap();
slave.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
assert_eq!(
slave_be.lock().unwrap().acked_features,
backend_be.lock().unwrap().acked_features,
VIRTIO_FEATURES & !0x1
);
slave.handle_request().unwrap();
slave.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
assert_eq!(
slave_be.lock().unwrap().acked_protocol_features,
backend_be.lock().unwrap().acked_protocol_features,
VhostUserProtocolFeatures::all().bits()
);
sbar.wait();
});
master.set_owner().unwrap();
frontend.set_owner().unwrap();
// set virtio features
let features = master.get_features().unwrap();
let features = frontend.get_features().unwrap();
assert_eq!(features, VIRTIO_FEATURES);
master.set_features(VIRTIO_FEATURES & !0x1).unwrap();
frontend.set_features(VIRTIO_FEATURES & !0x1).unwrap();
// set vhost protocol features
let features = master.get_protocol_features().unwrap();
let features = frontend.get_protocol_features().unwrap();
assert_eq!(features.bits(), VhostUserProtocolFeatures::all().bits());
master.set_protocol_features(features).unwrap();
frontend.set_protocol_features(features).unwrap();
mbar.wait();
}
#[test]
fn test_master_slave_process() {
fn test_frontend_backend_process() {
let mbar = Arc::new(Barrier::new(2));
let sbar = mbar.clone();
let path = temp_path();
let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
let (mut master, mut slave) = create_slave(path, slave_be.clone());
let backend_be = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
let (mut frontend, mut backend) = create_backend(path, backend_be.clone());
thread::spawn(move || {
// set_own()
slave.handle_request().unwrap();
assert!(slave_be.lock().unwrap().owned);
backend.handle_request().unwrap();
assert!(backend_be.lock().unwrap().owned);
// get/set_features()
slave.handle_request().unwrap();
slave.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
assert_eq!(
slave_be.lock().unwrap().acked_features,
backend_be.lock().unwrap().acked_features,
VIRTIO_FEATURES & !0x1
);
slave.handle_request().unwrap();
slave.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
let mut features = VhostUserProtocolFeatures::all();
@ -354,64 +354,64 @@ mod tests {
}
assert_eq!(
slave_be.lock().unwrap().acked_protocol_features,
backend_be.lock().unwrap().acked_protocol_features,
features.bits()
);
// get_inflight_fd()
slave.handle_request().unwrap();
backend.handle_request().unwrap();
// set_inflight_fd()
slave.handle_request().unwrap();
backend.handle_request().unwrap();
// get_queue_num()
slave.handle_request().unwrap();
backend.handle_request().unwrap();
// set_mem_table()
slave.handle_request().unwrap();
backend.handle_request().unwrap();
// get/set_config()
slave.handle_request().unwrap();
slave.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
// set_slave_request_fd
slave.handle_request().unwrap();
// set_backend_request_fd
backend.handle_request().unwrap();
// set_vring_enable
slave.handle_request().unwrap();
backend.handle_request().unwrap();
// set_log_base,set_log_fd()
slave.handle_request().unwrap_err();
slave.handle_request().unwrap_err();
backend.handle_request().unwrap_err();
backend.handle_request().unwrap_err();
// set_vring_xxx
slave.handle_request().unwrap();
slave.handle_request().unwrap();
slave.handle_request().unwrap();
slave.handle_request().unwrap();
slave.handle_request().unwrap();
slave.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
backend.handle_request().unwrap();
// get_max_mem_slots()
slave.handle_request().unwrap();
backend.handle_request().unwrap();
// add_mem_region()
slave.handle_request().unwrap();
backend.handle_request().unwrap();
// remove_mem_region()
slave.handle_request().unwrap();
backend.handle_request().unwrap();
sbar.wait();
});
master.set_owner().unwrap();
frontend.set_owner().unwrap();
// set virtio features
let features = master.get_features().unwrap();
let features = frontend.get_features().unwrap();
assert_eq!(features, VIRTIO_FEATURES);
master.set_features(VIRTIO_FEATURES & !0x1).unwrap();
frontend.set_features(VIRTIO_FEATURES & !0x1).unwrap();
// set vhost protocol features
let mut features = master.get_protocol_features().unwrap();
let mut features = frontend.get_protocol_features().unwrap();
assert_eq!(features.bits(), VhostUserProtocolFeatures::all().bits());
// Disable Xen mmap feature.
@ -419,10 +419,10 @@ mod tests {
features.remove(VhostUserProtocolFeatures::XEN_MMAP);
}
master.set_protocol_features(features).unwrap();
frontend.set_protocol_features(features).unwrap();
// Retrieve inflight I/O tracking information
let (inflight_info, inflight_file) = master
let (inflight_info, inflight_file) = frontend
.get_inflight_fd(&VhostUserInflight {
num_queues: 2,
queue_size: 256,
@ -430,11 +430,11 @@ mod tests {
})
.unwrap();
// Set the buffer back to the backend
master
frontend
.set_inflight_fd(&inflight_info, inflight_file.as_raw_fd())
.unwrap();
let num = master.get_queue_num().unwrap();
let num = frontend.get_queue_num().unwrap();
assert_eq!(num, 2);
let eventfd = vmm_sys_util::eventfd::EventFd::new(0).unwrap();
@ -445,23 +445,23 @@ mod tests {
0,
eventfd.as_raw_fd(),
)];
master.set_mem_table(&mem).unwrap();
frontend.set_mem_table(&mem).unwrap();
master
frontend
.set_config(0x100, VhostUserConfigFlags::WRITABLE, &[0xa5u8; 4])
.unwrap();
let buf = [0x0u8; 4];
let (reply_body, reply_payload) = master
let (reply_body, reply_payload) = frontend
.get_config(0x100, 4, VhostUserConfigFlags::empty(), &buf)
.unwrap();
let offset = reply_body.offset;
assert_eq!(offset, 0x100);
assert_eq!(&reply_payload, &[0xa5; 4]);
master.set_slave_request_fd(&eventfd).unwrap();
master.set_vring_enable(0, true).unwrap();
frontend.set_backend_request_fd(&eventfd).unwrap();
frontend.set_vring_enable(0, true).unwrap();
master
frontend
.set_log_base(
0,
Some(VhostUserDirtyLogRegion {
@ -471,10 +471,10 @@ mod tests {
}),
)
.unwrap();
master.set_log_fd(eventfd.as_raw_fd()).unwrap();
frontend.set_log_fd(eventfd.as_raw_fd()).unwrap();
master.set_vring_num(0, 256).unwrap();
master.set_vring_base(0, 0).unwrap();
frontend.set_vring_num(0, 256).unwrap();
frontend.set_vring_base(0, 0).unwrap();
let config = VringConfigData {
queue_max_size: 256,
queue_size: 128,
@ -484,20 +484,20 @@ mod tests {
avail_ring_addr: 0x3000,
log_addr: Some(0x4000),
};
master.set_vring_addr(0, &config).unwrap();
master.set_vring_call(0, &eventfd).unwrap();
master.set_vring_kick(0, &eventfd).unwrap();
master.set_vring_err(0, &eventfd).unwrap();
frontend.set_vring_addr(0, &config).unwrap();
frontend.set_vring_call(0, &eventfd).unwrap();
frontend.set_vring_kick(0, &eventfd).unwrap();
frontend.set_vring_err(0, &eventfd).unwrap();
let max_mem_slots = master.get_max_mem_slots().unwrap();
let max_mem_slots = frontend.get_max_mem_slots().unwrap();
assert_eq!(max_mem_slots, 32);
let region_file: File = TempFile::new().unwrap().into_file();
let region =
VhostUserMemoryRegionInfo::new(0x10_0000, 0x10_0000, 0, 0, region_file.as_raw_fd());
master.add_mem_region(&region).unwrap();
frontend.add_mem_region(&region).unwrap();
master.remove_mem_region(&region).unwrap();
frontend.remove_mem_region(&region).unwrap();
mbar.wait();
}
@ -514,8 +514,8 @@ mod tests {
#[test]
fn test_should_reconnect() {
assert!(Error::PartialMessage.should_reconnect());
assert!(Error::SlaveInternalError.should_reconnect());
assert!(Error::MasterInternalError.should_reconnect());
assert!(Error::BackendInternalError.should_reconnect());
assert!(Error::FrontendInternalError.should_reconnect());
assert!(!Error::InvalidParam.should_reconnect());
assert!(!Error::InvalidOperation("reason").should_reconnect());
assert!(

View file

@ -1,86 +0,0 @@
// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! Traits and Structs for vhost-user slave.
use std::sync::Arc;
use super::connection::{Endpoint, Listener};
use super::message::*;
use super::{Result, SlaveReqHandler, VhostUserSlaveReqHandler};
/// Vhost-user slave side connection listener.
pub struct SlaveListener<S: VhostUserSlaveReqHandler> {
listener: Listener,
backend: Option<Arc<S>>,
}
/// Sets up a listener for incoming master connections, and handles construction
/// of a Slave on success.
impl<S: VhostUserSlaveReqHandler> SlaveListener<S> {
/// Create a unix domain socket for incoming master connections.
pub fn new(listener: Listener, backend: Arc<S>) -> Result<Self> {
Ok(SlaveListener {
listener,
backend: Some(backend),
})
}
/// Accept an incoming connection from the master, returning Some(Slave) on
/// success, or None if the socket is nonblocking and no incoming connection
/// was detected
pub fn accept(&mut self) -> Result<Option<SlaveReqHandler<S>>> {
if let Some(fd) = self.listener.accept()? {
return Ok(Some(SlaveReqHandler::new(
Endpoint::<MasterReq>::from_stream(fd),
self.backend.take().unwrap(),
)));
}
Ok(None)
}
/// Change blocking status on the listener.
pub fn set_nonblocking(&self, block: bool) -> Result<()> {
self.listener.set_nonblocking(block)
}
}
#[cfg(test)]
mod tests {
use std::sync::Mutex;
use super::*;
use crate::vhost_user::dummy_slave::DummySlaveReqHandler;
#[test]
fn test_slave_listener_set_nonblocking() {
let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
let listener =
Listener::new("/tmp/vhost_user_lib_unit_test_slave_nonblocking", true).unwrap();
let slave_listener = SlaveListener::new(listener, backend).unwrap();
slave_listener.set_nonblocking(true).unwrap();
slave_listener.set_nonblocking(false).unwrap();
slave_listener.set_nonblocking(false).unwrap();
slave_listener.set_nonblocking(true).unwrap();
slave_listener.set_nonblocking(true).unwrap();
}
#[cfg(feature = "vhost-user-master")]
#[test]
fn test_slave_listener_accept() {
use super::super::Master;
let path = "/tmp/vhost_user_lib_unit_test_slave_accept";
let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
let listener = Listener::new(path, true).unwrap();
let mut slave_listener = SlaveListener::new(listener, backend).unwrap();
slave_listener.set_nonblocking(true).unwrap();
assert!(slave_listener.accept().unwrap().is_none());
assert!(slave_listener.accept().unwrap().is_none());
let _master = Master::connect(path, 1).unwrap();
let _slave = slave_listener.accept().unwrap().unwrap();
}
}