18个版本 (破坏性)

0.15.0 2024年4月15日
0.14.0 2024年3月15日
0.13.1 2024年1月16日
0.11.0 2023年11月7日
0.1.0 2021年12月22日

205HTTP服务器

Download history 3164/week @ 2024-04-20 3285/week @ 2024-04-27 2931/week @ 2024-05-04 2466/week @ 2024-05-11 3041/week @ 2024-05-18 2785/week @ 2024-05-25 3436/week @ 2024-06-01 3280/week @ 2024-06-08 2774/week @ 2024-06-15 2565/week @ 2024-06-22 3027/week @ 2024-06-29 3624/week @ 2024-07-06 3940/week @ 2024-07-13 3888/week @ 2024-07-20 3339/week @ 2024-07-27 3228/week @ 2024-08-03

每月15,242 次下载
用于 12 crate

Apache-2.0

455KB
9K SLoC

vhost-user-backend

设计

vhost-user-backend crate提供了一个框架来实现vhost-user后端服务,包括以下外部公共API

  • 守护进程控制对象(VhostUserDaemon),用于启动和停止服务守护进程。
  • 处理vhost-user控制消息和virtio消息的vhost-user后端trait(VhostUserBackendMut)。
  • 访问virtio队列的vring访问trait(VringT),以及该trait的三个实现:VringStateVringMutexVringRwLock

用法

vhost-user-backend crate提供了一个框架来实现vhost-user后端服务。由vhost-user-backend库提供的主要接口是struct VhostUserDaemon

pub struct VhostUserDaemon<S, V, B = ()>
where
    S: VhostUserBackend<V, B>,
    V: VringT<GM<B>> + Clone + Send + Sync + 'static,
    B: Bitmap + 'static,
{
    pub fn new(name: String, backend: S, atomic_mem: GuestMemoryAtomic<GuestMemoryMmap<B>>) -> Result<Self>;
    pub fn start(&mut self, listener: Listener) -> Result<()>;
    pub fn wait(&mut self) -> Result<()>;
    pub fn get_epoll_handlers(&self) -> Vec<Arc<VringEpollHandler<S, V, B>>>;
}

创建一个VhostUserDaemon实例

VhostUserDaemon::new()函数创建了一个VhostUserDaemon对象的实例。客户端需要传递一个VhostUserBackend对象,该对象将被用于配置VhostUserDaemon实例、处理来自vhost-user前端的控制消息和处理来自virtio队列的virtio请求。将创建一组工作线程来处理来自已配置virtio队列的virtio请求。

启动VhostUserDaemon

VhostUserDaemon::start()方法等待在listener上来自vhost-user前端的新连接。一旦连接就绪,将创建一个主线程来处理来自vhost-user前端的消息。

停止VhostUserDaemon

VhostUserDaemon::stop()方法等待主线程退出。在等待它退出之前,必须向主线程发送一个退出事件,通过向exit_event EventFd写入。

线程模型

主线程和virtio队列工作线程将并发访问底层virtio队列,因此在多线程模型中,所有virtio队列。但是主线程只访问virtio队列进行配置,因此客户端可以采用锁定策略来优化virtio队列工作线程。

示例

处理来自virtio队列的virtio消息的示例代码

impl VhostUserBackendMut for VhostUserService {
    fn process_queue(&mut self, vring: &VringMutex) -> Result<bool> {
        let mut used_any = false;
        let mem = match &self.mem {
            Some(m) => m.memory(),
            None => return Err(Error::NoMemoryConfigured),
        };

        let mut vring_state = vring.get_mut();

        while let Some(avail_desc) = vring_state
            .get_queue_mut()
            .iter()
            .map_err(|_| Error::IterateQueue)?
            .next()
        {
            // Process the request...

            if self.event_idx {
                if vring_state.add_used(head_index, 0).is_err() {
                    warn!("Couldn't return used descriptors to the ring");
                }

                match vring_state.needs_notification() {
                    Err(_) => {
                        warn!("Couldn't check if queue needs to be notified");
                        vring_state.signal_used_queue().unwrap();
                    }
                    Ok(needs_notification) => {
                        if needs_notification {
                            vring_state.signal_used_queue().unwrap();
                        }
                    }
                }
            } else {
                if vring_state.add_used(head_index, 0).is_err() {
                    warn!("Couldn't return used descriptors to the ring");
                }
                vring_state.signal_used_queue().unwrap();
            }
        }

        Ok(used_any)
    }
}

Postcopy支持

要启用POSTCOPY_*消息支持,有一个postcopy功能。由于Xen处理内存映射的方式,postcopy功能与xen功能不兼容。同时启用两者将导致编译错误。

postcopy功能启用可选的userfaultfd依赖项,以便创建和与userfaultfd对象交互。这需要从后端访问/dev/userfaultfd文件的权限。

Xen支持

支持Xen在映射虚拟机内存时需要特殊处理。vm-memory库通过一个单独的功能xen实现了xen内存映射支持,并且该库使用相同的功能名称来启用Xen支持。

此外,对于xen映射,前端传递的内存区域包含一些额外字段,如vhost-user协议文档所述。

rust-vmm维护者决定保持接口简单,并为标准Unix内存映射或Xen构建库,而不是两者都构建。

许可证

本项目采用

依赖关系

~1.6–3.5MB
~54K SLoC