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:
parent
5c9bef626c
commit
4db81adcd2
19 changed files with 828 additions and 811 deletions
|
|
@ -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"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
### Added
|
||||
|
||||
### Changed
|
||||
- Change uses of master/slave for frontend/backend in the codebase.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;">MasterReqHandler</pre>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
|
||||
<mxCell id="2" value="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;">FrontendReqHandler</pre>" 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="<pre style="font-size: 16.5pt; font-weight: 700; font-family: &quot;jetbrains mono&quot;, monospace;">VhostUserMasterReqHandler</pre>" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
|
||||
<mxCell id="4" value="<pre style="font-size: 16.5pt; font-weight: 700; font-family: &quot;jetbrains mono&quot;, monospace;">VhostUserFrontendReqHandler</pre>" 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="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">Slave</pre></pre>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" parent="1" vertex="1">
|
||||
<mxCell id="5" value="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">Backend</pre></pre>" 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="<pre style="font-size: 16.5pt; font-weight: 700; font-family: &quot;jetbrains mono&quot;, monospace;">VhostUserMasterReqHandlerMut</pre>" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
|
||||
<mxCell id="7" value="<pre style="font-size: 16.5pt; font-weight: 700; font-family: &quot;jetbrains mono&quot;, monospace;">VhostUserFrontendReqHandlerMut</pre>" 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="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">SlaveListener</pre></pre>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
|
||||
<mxCell id="10" value="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">BackendListener</pre></pre>" 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="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">SlaveReqHandler</pre></pre></pre>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
|
||||
<mxCell id="11" value="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">BackendReqHandler</pre></pre></pre>" 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="<pre style="font-size: 16.5pt; font-weight: 700; font-family: &quot;jetbrains mono&quot;, monospace;"><pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;">VhostUserSlaveReqHandler</pre></pre>" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
|
||||
<mxCell id="14" value="<pre style="font-size: 16.5pt; font-weight: 700; font-family: &quot;jetbrains mono&quot;, monospace;"><pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;">VhostUserBackendReqHandler</pre></pre>" 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="<pre style="font-family: &quot;jetbrains mono&quot;, monospace; font-size: 16.5pt;">VhostVdpaBackend</pre>" 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="<pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">Master</pre>" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=1;labelBackgroundColor=none;fontColor=#FF00FF;strokeColor=#FF00FF;" vertex="1" parent="1">
|
||||
<mxCell id="19" value="<pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">Frontend</pre>" 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="<pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">VhostSoftBackend</pre>" 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="<pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">Message</pre></pre>" 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="<pre style="font-size: 16.5pt ; font-weight: 700 ; font-family: &quot;jetbrains mono&quot; , monospace"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">VhostUserMaster</pre></pre>" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;strokeColor=#FF33FF;fontColor=#FF00FF;" vertex="1" parent="1">
|
||||
<mxCell id="37" value="<pre style="font-size: 16.5pt ; font-weight: 700 ; font-family: &quot;jetbrains mono&quot; , monospace"><pre style="font-family: &quot;jetbrains mono&quot; , monospace ; font-size: 16.5pt">VhostUserFrontend</pre></pre>" 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">
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
86
crates/vhost/src/vhost_user/backend.rs
Normal file
86
crates/vhost/src/vhost_user/backend.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(®ion).unwrap();
|
||||
frontend.add_mem_region(®ion).unwrap();
|
||||
|
||||
master.remove_mem_region(®ion).unwrap();
|
||||
frontend.remove_mem_region(®ion).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!(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue