aboutsummaryrefslogtreecommitdiff
path: root/src/vhost_user/dummy_slave.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/vhost_user/dummy_slave.rs')
-rw-r--r--src/vhost_user/dummy_slave.rs294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/vhost_user/dummy_slave.rs b/src/vhost_user/dummy_slave.rs
new file mode 100644
index 0000000..ae728a0
--- /dev/null
+++ b/src/vhost_user/dummy_slave.rs
@@ -0,0 +1,294 @@
+// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+use std::fs::File;
+
+use super::message::*;
+use super::*;
+
+pub const MAX_QUEUE_NUM: usize = 2;
+pub const MAX_VRING_NUM: usize = 256;
+pub const MAX_MEM_SLOTS: usize = 32;
+pub const VIRTIO_FEATURES: u64 = 0x40000003;
+
+#[derive(Default)]
+pub struct DummySlaveReqHandler {
+ pub owned: bool,
+ pub features_acked: bool,
+ pub acked_features: u64,
+ pub acked_protocol_features: u64,
+ pub queue_num: usize,
+ pub vring_num: [u32; MAX_QUEUE_NUM],
+ pub vring_base: [u32; MAX_QUEUE_NUM],
+ pub call_fd: [Option<File>; MAX_QUEUE_NUM],
+ pub kick_fd: [Option<File>; MAX_QUEUE_NUM],
+ pub err_fd: [Option<File>; MAX_QUEUE_NUM],
+ pub vring_started: [bool; MAX_QUEUE_NUM],
+ pub vring_enabled: [bool; MAX_QUEUE_NUM],
+ pub inflight_file: Option<File>,
+}
+
+impl DummySlaveReqHandler {
+ pub fn new() -> Self {
+ DummySlaveReqHandler {
+ queue_num: MAX_QUEUE_NUM,
+ ..Default::default()
+ }
+ }
+
+ /// Helper to check if VirtioFeature enabled
+ fn check_feature(&self, feat: VhostUserVirtioFeatures) -> Result<()> {
+ if self.acked_features & feat.bits() != 0 {
+ Ok(())
+ } else {
+ Err(Error::InactiveFeature(feat))
+ }
+ }
+
+ /// Helper to check is VhostUserProtocolFeatures enabled
+ fn check_proto_feature(&self, feat: VhostUserProtocolFeatures) -> Result<()> {
+ if self.acked_protocol_features & feat.bits() != 0 {
+ Ok(())
+ } else {
+ Err(Error::InactiveOperation(feat))
+ }
+ }
+}
+
+impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler {
+ fn set_owner(&mut self) -> Result<()> {
+ if self.owned {
+ return Err(Error::InvalidOperation("already claimed"));
+ }
+ self.owned = true;
+ Ok(())
+ }
+
+ fn reset_owner(&mut self) -> Result<()> {
+ self.owned = false;
+ self.features_acked = false;
+ self.acked_features = 0;
+ self.acked_protocol_features = 0;
+ Ok(())
+ }
+
+ fn get_features(&mut self) -> Result<u64> {
+ Ok(VIRTIO_FEATURES)
+ }
+
+ fn set_features(&mut self, features: u64) -> Result<()> {
+ if !self.owned {
+ return Err(Error::InvalidOperation("not owned"));
+ } else if self.features_acked {
+ return Err(Error::InvalidOperation("features already set"));
+ } else if (features & !VIRTIO_FEATURES) != 0 {
+ return Err(Error::InvalidParam);
+ }
+
+ self.acked_features = features;
+ self.features_acked = true;
+
+ // If VHOST_USER_F_PROTOCOL_FEATURES has not been negotiated,
+ // the ring is initialized in an enabled state.
+ // If VHOST_USER_F_PROTOCOL_FEATURES has been negotiated,
+ // the ring is initialized in a disabled state. Client 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.
+ let vring_enabled =
+ self.acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() == 0;
+ for enabled in &mut self.vring_enabled {
+ *enabled = vring_enabled;
+ }
+
+ Ok(())
+ }
+
+ fn set_mem_table(&mut self, _ctx: &[VhostUserMemoryRegion], _files: Vec<File>) -> Result<()> {
+ Ok(())
+ }
+
+ fn set_vring_num(&mut self, index: u32, num: u32) -> Result<()> {
+ if index as usize >= self.queue_num || num == 0 || num as usize > MAX_VRING_NUM {
+ return Err(Error::InvalidParam);
+ }
+ self.vring_num[index as usize] = num;
+ Ok(())
+ }
+
+ fn set_vring_addr(
+ &mut self,
+ index: u32,
+ _flags: VhostUserVringAddrFlags,
+ _descriptor: u64,
+ _used: u64,
+ _available: u64,
+ _log: u64,
+ ) -> Result<()> {
+ if index as usize >= self.queue_num {
+ return Err(Error::InvalidParam);
+ }
+ Ok(())
+ }
+
+ fn set_vring_base(&mut self, index: u32, base: u32) -> Result<()> {
+ if index as usize >= self.queue_num || base as usize >= MAX_VRING_NUM {
+ return Err(Error::InvalidParam);
+ }
+ self.vring_base[index as usize] = base;
+ Ok(())
+ }
+
+ fn get_vring_base(&mut self, index: u32) -> Result<VhostUserVringState> {
+ if index as usize >= self.queue_num {
+ return Err(Error::InvalidParam);
+ }
+ // Quotation from vhost-user spec:
+ // Client must start ring upon receiving a kick (that is, detecting
+ // that file descriptor is readable) on the descriptor specified by
+ // VHOST_USER_SET_VRING_KICK, and stop ring upon receiving
+ // VHOST_USER_GET_VRING_BASE.
+ self.vring_started[index as usize] = false;
+ Ok(VhostUserVringState::new(
+ index,
+ self.vring_base[index as usize],
+ ))
+ }
+
+ fn set_vring_kick(&mut self, index: u8, fd: Option<File>) -> Result<()> {
+ if index as usize >= self.queue_num || index as usize > self.queue_num {
+ return Err(Error::InvalidParam);
+ }
+ self.kick_fd[index as usize] = fd;
+
+ // Quotation from vhost-user spec:
+ // Client must start ring upon receiving a kick (that is, detecting
+ // that file descriptor is readable) on the descriptor specified by
+ // VHOST_USER_SET_VRING_KICK, and stop ring upon receiving
+ // VHOST_USER_GET_VRING_BASE.
+ //
+ // So we should add fd to event monitor(select, poll, epoll) here.
+ self.vring_started[index as usize] = true;
+ Ok(())
+ }
+
+ fn set_vring_call(&mut self, index: u8, fd: Option<File>) -> Result<()> {
+ if index as usize >= self.queue_num || index as usize > self.queue_num {
+ return Err(Error::InvalidParam);
+ }
+ self.call_fd[index as usize] = fd;
+ Ok(())
+ }
+
+ fn set_vring_err(&mut self, index: u8, fd: Option<File>) -> Result<()> {
+ if index as usize >= self.queue_num || index as usize > self.queue_num {
+ return Err(Error::InvalidParam);
+ }
+ self.err_fd[index as usize] = fd;
+ Ok(())
+ }
+
+ fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
+ Ok(VhostUserProtocolFeatures::all())
+ }
+
+ fn set_protocol_features(&mut self, features: u64) -> Result<()> {
+ // Note: slave 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
+ // VHOST_USER_F_PROTOCOL_FEATURES cleared after calling this
+ // interface?
+ self.acked_protocol_features = features;
+ Ok(())
+ }
+
+ fn get_queue_num(&mut self) -> Result<u64> {
+ Ok(MAX_QUEUE_NUM as u64)
+ }
+
+ fn set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()> {
+ // This request should be handled only when VHOST_USER_F_PROTOCOL_FEATURES
+ // has been negotiated.
+ self.check_feature(VhostUserVirtioFeatures::PROTOCOL_FEATURES)?;
+
+ if index as usize >= self.queue_num || index as usize > self.queue_num {
+ return Err(Error::InvalidParam);
+ }
+
+ // Slave 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.
+ self.vring_enabled[index as usize] = enable;
+ Ok(())
+ }
+
+ fn get_config(
+ &mut self,
+ offset: u32,
+ size: u32,
+ _flags: VhostUserConfigFlags,
+ ) -> Result<Vec<u8>> {
+ self.check_proto_feature(VhostUserProtocolFeatures::CONFIG)?;
+
+ if !(VHOST_USER_CONFIG_OFFSET..VHOST_USER_CONFIG_SIZE).contains(&offset)
+ || size > VHOST_USER_CONFIG_SIZE - VHOST_USER_CONFIG_OFFSET
+ || size + offset > VHOST_USER_CONFIG_SIZE
+ {
+ return Err(Error::InvalidParam);
+ }
+ assert_eq!(offset, 0x100);
+ assert_eq!(size, 4);
+ Ok(vec![0xa5; size as usize])
+ }
+
+ fn set_config(&mut self, offset: u32, buf: &[u8], _flags: VhostUserConfigFlags) -> Result<()> {
+ let size = buf.len() as u32;
+ self.check_proto_feature(VhostUserProtocolFeatures::CONFIG)?;
+
+ if !(VHOST_USER_CONFIG_OFFSET..VHOST_USER_CONFIG_SIZE).contains(&offset)
+ || size > VHOST_USER_CONFIG_SIZE - VHOST_USER_CONFIG_OFFSET
+ || size + offset > VHOST_USER_CONFIG_SIZE
+ {
+ return Err(Error::InvalidParam);
+ }
+ assert_eq!(offset, 0x100);
+ assert_eq!(buf.len(), 4);
+ assert_eq!(buf, &[0xa5; 4]);
+ Ok(())
+ }
+
+ fn get_inflight_fd(
+ &mut self,
+ inflight: &VhostUserInflight,
+ ) -> Result<(VhostUserInflight, File)> {
+ let file = tempfile::tempfile().unwrap();
+ self.inflight_file = Some(file.try_clone().unwrap());
+ Ok((
+ VhostUserInflight {
+ mmap_size: 0x1000,
+ mmap_offset: 0,
+ num_queues: inflight.num_queues,
+ queue_size: inflight.queue_size,
+ },
+ file,
+ ))
+ }
+
+ fn set_inflight_fd(&mut self, _inflight: &VhostUserInflight, _file: File) -> Result<()> {
+ Ok(())
+ }
+
+ fn get_max_mem_slots(&mut self) -> Result<u64> {
+ Ok(MAX_MEM_SLOTS as u64)
+ }
+
+ fn add_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion, _fd: File) -> Result<()> {
+ Ok(())
+ }
+
+ fn remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()> {
+ Ok(())
+ }
+}