1 个不稳定版本
0.1.0 | 2021年12月20日 |
---|
#346 在 硬件支持
37 每月下载量
71KB
1K SLoC
vm-device
vm-device
包提供
- 定义在特定总线上的读和写操作的设备特性
- 设备管理器(特定总线的特性和具体实现),用于操作设备和调度I/O
- 定义资源和其约束的抽象(例如特定总线的地址范围、中断号等)
设计
虚拟设备模型围绕四个特性构建,分别是用于程序化I/O(PIO)的DevicePio
和MutDevicePio,以及用于内存映射I/O(MMIO)的
DeviceMmio
和MutDeviceMmio》。这些特性定义了处理读和写操作的方法。不同之处在于,
DevicePio
和DeviceMmio
只需要不可变self借用,而MutDevicePio
和MutDeviceMmio需要可变借用。
设备管理器抽象由IoManager
结构实现。它定义了两个总线,一个用于PIO,一个用于MMIO。对于每个总线,借助PioManager
和MmioManager特性,管理器提供设备注册以及调度读和写请求的方法。管理器将根据访问的地址范围确定哪个设备负责处理I/O请求,并将请求路由到该设备。
PioManager
和MmioManager特性作为接口有用,原因如下。首先,当提供的
IoManager
不足时,允许进行替代实现。其次,允许其他crate依赖于特性而不依赖于实际实现。
以下图表展示了使用IoManager
的虚拟机内核、宿主内核和VMM之间的交互。虚拟机内核中的驱动程序发出一个I/O请求。该请求由宿主内核的虚拟机管理程序(KVM)转换为VMM处理的触发器。触发器可以是vCPU退出或eventfd通知。VMM提取地址信息并确定目标总线。然后,它向IoManager
派发新的请求,该请求检查请求的地址是否已在总线上注册了虚拟设备,并最终将该请求发送到该设备。
用法
设备通常连接到特定的总线,因此需要实现一种类型的特制。例如,x86上的串行端口是PIO设备,而VirtIO设备使用MMIO。设备也可以同时实现这两种。一旦确定了I/O类型,下一步就是选择可变和不可变特制变体。如果需要修改设备内部状态,则需要使用可变变体。
在将任何I/O请求调度到新设备之前,它需要在该总线指定的地址范围内注册到IoManager
实例中。通过调用IoManager::new()
(无需任何配置)来创建一个新的IoManager
很容易。内部管理器将设备作为特制对象存储在Arc
的包装中,因此如果设备实现MutDevicePio
或MutDeviceMmio
,则它必须包装在Mutex
中。该包包含自动实现DevicePio for Mutex<T> where T: MutDevicePio
和DeviceMmio for Mutex<T> where T: MutDeviceMmio
,但仅限于标准库中的Mutex类型。对于任何其他第三方包中的Mutex类型,通用实现必须由用户完成。
从现在起,IoManager将路由已注册地址范围到设备的I/O请求。请求由客户端代码调度,例如在处理VM退出时,使用IoManager
的方法pio_read
、pio_write
、mmio_read
和mmio_write
。
示例
实现简单的日志PIO设备
use std::sync::{Arc, Mutex};
use vm_device::bus::{PioAddress, PioAddressOffset, PioRange};
use vm_device::device_manager::{IoManager, PioManager};
use vm_device::MutDevicePio;
struct LogDevice {}
impl MutDevicePio for LogDevice {
fn pio_read(&mut self, base: PioAddress, offset: PioAddressOffset, _data: &mut [u8]) {
println!("mut pio_read: base {:?}, offset {}", base, offset);
}
fn pio_write(&mut self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) {
println!(
"mut pio_write: base {:?}, offset {}, data {:?}",
base, offset, data
);
}
}
将设备注册到IoManager并执行I/O
fn main() {
let mut manager = IoManager::new();
let device = LogDevice {};
let bus_range = PioRange::new(PioAddress(0), 10).unwrap();
manager
.register_pio(bus_range, Arc::new(Mutex::new(device)))
.unwrap();
manager.pio_write(PioAddress(0), &vec![b'o', b'k']).unwrap();
}
测试
使用单元测试和集成测试测试vm-device
。它利用rust-vmm-ci
进行持续测试。所有测试都在rustvmm/dev
容器中运行。
许可证
此项目受以下任一许可证的许可: