Skip to content

Commit 866932e

Browse files
stefano-garzarellajiangliu
authored andcommitted
vhost_kern: add VhostKernVdpa struct
Add a vhost-vdpa in-kernel implementation of VhostVdpa trait. Tests are serialized since the device supports only a single user at a time. Signed-off-by: Stefano Garzarella <[email protected]>
1 parent 8884465 commit 866932e

File tree

3 files changed

+287
-0
lines changed

3 files changed

+287
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ vm-memory = "0.6"
2929
[dev-dependencies]
3030
tempfile = ">=3.2.0"
3131
vm-memory = { version = "0.6", features=["backend-mmap"] }
32+
serial_test = "*"

src/vhost_kern/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ use super::{
2828
pub mod vhost_binding;
2929
use self::vhost_binding::*;
3030

31+
#[cfg(feature = "vhost-vdpa")]
32+
pub mod vdpa;
3133
#[cfg(feature = "vhost-vsock")]
3234
pub mod vsock;
3335

src/vhost_kern/vdpa.rs

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
// Copyright (C) 2021 Red Hat, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
3+
4+
//! Kernel-based vhost-vdpa backend.
5+
6+
use std::fs::{File, OpenOptions};
7+
use std::os::raw::c_int;
8+
use std::os::unix::fs::OpenOptionsExt;
9+
use std::os::unix::io::{AsRawFd, RawFd};
10+
11+
use vm_memory::GuestAddressSpace;
12+
use vmm_sys_util::eventfd::EventFd;
13+
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};
14+
15+
use std::alloc::{alloc, dealloc, Layout};
16+
use std::mem;
17+
18+
use super::vhost_binding::*;
19+
use super::{ioctl_result, Error, Result, VhostKernBackend};
20+
use crate::vdpa::*;
21+
22+
/// Handle for running VHOST_VDPA ioctls.
23+
pub struct VhostKernVdpa<AS: GuestAddressSpace> {
24+
fd: File,
25+
mem: AS,
26+
}
27+
28+
impl<AS: GuestAddressSpace> VhostKernVdpa<AS> {
29+
/// Open a handle to a new VHOST-VDPA instance.
30+
pub fn new(path: &str, mem: AS) -> Result<Self> {
31+
Ok(VhostKernVdpa {
32+
fd: OpenOptions::new()
33+
.read(true)
34+
.write(true)
35+
.custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
36+
.open(path)
37+
.map_err(Error::VhostOpen)?,
38+
mem,
39+
})
40+
}
41+
}
42+
43+
impl<AS: GuestAddressSpace> VhostVdpa for VhostKernVdpa<AS> {
44+
fn get_device_id(&self) -> Result<u32> {
45+
let mut device_id: u32 = 0;
46+
let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_DEVICE_ID(), &mut device_id) };
47+
ioctl_result(ret, device_id)
48+
}
49+
50+
fn get_status(&self) -> Result<u8> {
51+
let mut status: u8 = 0;
52+
let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_STATUS(), &mut status) };
53+
ioctl_result(ret, status)
54+
}
55+
56+
fn set_status(&self, status: u8) -> Result<()> {
57+
let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_STATUS(), &status) };
58+
ioctl_result(ret, ())
59+
}
60+
61+
fn get_config(&self, offset: u32, buffer: &mut [u8]) -> Result<()> {
62+
let buffer_len = buffer.len();
63+
let layout =
64+
Layout::from_size_align(mem::size_of::<vhost_vdpa_config>() + buffer_len, 1).unwrap();
65+
let ret: c_int;
66+
67+
unsafe {
68+
let ptr = alloc(layout);
69+
let config = ptr as *mut vhost_vdpa_config;
70+
(*config).off = offset;
71+
(*config).len = buffer_len as u32;
72+
73+
ret = ioctl_with_ptr(self, VHOST_VDPA_GET_CONFIG(), ptr);
74+
75+
buffer.copy_from_slice((*config).buf.as_slice(buffer_len));
76+
77+
dealloc(ptr, layout);
78+
};
79+
80+
ioctl_result(ret, ())
81+
}
82+
83+
fn set_config(&self, offset: u32, buffer: &[u8]) -> Result<()> {
84+
let buffer_len = buffer.len();
85+
let layout =
86+
Layout::from_size_align(mem::size_of::<vhost_vdpa_config>() + buffer_len, 1).unwrap();
87+
let ret: c_int;
88+
89+
unsafe {
90+
let ptr = alloc(layout);
91+
let config = ptr as *mut vhost_vdpa_config;
92+
(*config).off = offset;
93+
(*config).len = buffer_len as u32;
94+
95+
(*config)
96+
.buf
97+
.as_mut_slice(buffer_len)
98+
.copy_from_slice(&buffer);
99+
100+
ret = ioctl_with_ptr(self, VHOST_VDPA_SET_CONFIG(), ptr);
101+
102+
dealloc(ptr, layout);
103+
};
104+
105+
ioctl_result(ret, ())
106+
}
107+
108+
fn set_vring_enable(&self, queue_index: usize, enabled: bool) -> Result<()> {
109+
let vring_state = vhost_vring_state {
110+
index: queue_index as u32,
111+
num: enabled as u32,
112+
};
113+
114+
let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_VRING_ENABLE(), &vring_state) };
115+
ioctl_result(ret, ())
116+
}
117+
118+
fn get_vring_num(&self) -> Result<u16> {
119+
let mut vring_num: u16 = 0;
120+
let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VRING_NUM(), &mut vring_num) };
121+
ioctl_result(ret, vring_num)
122+
}
123+
124+
fn set_config_call(&self, fd: &EventFd) -> Result<()> {
125+
let event_fd: ::std::os::raw::c_int = fd.as_raw_fd();
126+
let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_CONFIG_CALL(), &event_fd) };
127+
ioctl_result(ret, ())
128+
}
129+
130+
fn get_iova_range(&self) -> Result<VhostVdpaIovaRange> {
131+
let mut low_iova_range = vhost_vdpa_iova_range { first: 0, last: 0 };
132+
133+
let ret =
134+
unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VRING_NUM(), &mut low_iova_range) };
135+
136+
let iova_range = VhostVdpaIovaRange {
137+
first: low_iova_range.first,
138+
last: low_iova_range.last,
139+
};
140+
141+
ioctl_result(ret, iova_range)
142+
}
143+
}
144+
145+
impl<AS: GuestAddressSpace> VhostKernBackend for VhostKernVdpa<AS> {
146+
type AS = AS;
147+
148+
fn mem(&self) -> &Self::AS {
149+
&self.mem
150+
}
151+
}
152+
153+
impl<AS: GuestAddressSpace> AsRawFd for VhostKernVdpa<AS> {
154+
fn as_raw_fd(&self) -> RawFd {
155+
self.fd.as_raw_fd()
156+
}
157+
}
158+
159+
#[cfg(test)]
160+
mod tests {
161+
const VHOST_VDPA_PATH: &str = "/dev/vhost-vdpa-0";
162+
163+
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
164+
use vmm_sys_util::eventfd::EventFd;
165+
166+
use super::*;
167+
use crate::{
168+
VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData,
169+
};
170+
use serial_test::serial;
171+
172+
#[test]
173+
#[serial]
174+
fn test_vdpa_kern_new_device() {
175+
let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
176+
let vdpa = VhostKernVdpa::new(VHOST_VDPA_PATH, &m).unwrap();
177+
178+
assert!(vdpa.as_raw_fd() >= 0);
179+
assert!(vdpa.mem().find_region(GuestAddress(0x100)).is_some());
180+
assert!(vdpa.mem().find_region(GuestAddress(0x10_0000)).is_none());
181+
}
182+
183+
#[test]
184+
#[serial]
185+
fn test_vdpa_kern_is_valid() {
186+
let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
187+
let vdpa = VhostKernVdpa::new(VHOST_VDPA_PATH, &m).unwrap();
188+
189+
let mut config = VringConfigData {
190+
queue_max_size: 32,
191+
queue_size: 32,
192+
flags: 0,
193+
desc_table_addr: 0x1000,
194+
used_ring_addr: 0x2000,
195+
avail_ring_addr: 0x3000,
196+
log_addr: None,
197+
};
198+
assert_eq!(vdpa.is_valid(&config), true);
199+
200+
config.queue_size = 0;
201+
assert_eq!(vdpa.is_valid(&config), false);
202+
config.queue_size = 31;
203+
assert_eq!(vdpa.is_valid(&config), false);
204+
config.queue_size = 33;
205+
assert_eq!(vdpa.is_valid(&config), false);
206+
}
207+
208+
#[test]
209+
#[serial]
210+
fn test_vdpa_kern_ioctls() {
211+
let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
212+
let vdpa = VhostKernVdpa::new(VHOST_VDPA_PATH, &m).unwrap();
213+
214+
let features = vdpa.get_features().unwrap();
215+
// VIRTIO_F_VERSION_1 (bit 32) should be set
216+
assert_ne!(features & (1 << 32), 0);
217+
vdpa.set_features(features).unwrap();
218+
219+
vdpa.set_owner().unwrap();
220+
221+
vdpa.set_mem_table(&[]).unwrap_err();
222+
223+
let region = VhostUserMemoryRegionInfo {
224+
guest_phys_addr: 0x0,
225+
memory_size: 0x10_0000,
226+
userspace_addr: m.get_host_address(GuestAddress(0x0)).unwrap() as u64,
227+
mmap_offset: 0,
228+
mmap_handle: -1,
229+
};
230+
vdpa.set_mem_table(&[region]).unwrap();
231+
232+
assert!(vdpa.get_device_id().unwrap() > 0);
233+
234+
assert_eq!(vdpa.get_status().unwrap(), 0x0);
235+
vdpa.set_status(0x1).unwrap();
236+
assert_eq!(vdpa.get_status().unwrap(), 0x1);
237+
238+
let mut vec = vec![0u8; 8];
239+
vdpa.get_config(0, &mut vec).unwrap();
240+
vdpa.set_config(0, &vec).unwrap();
241+
242+
let eventfd = EventFd::new(0).unwrap();
243+
244+
// set_log_base() and set_log_fd() are not supported by vhost-vdpa
245+
vdpa.set_log_base(
246+
0x4000,
247+
Some(VhostUserDirtyLogRegion {
248+
mmap_size: 0x1000,
249+
mmap_offset: 0x10,
250+
mmap_handle: 1,
251+
}),
252+
)
253+
.unwrap_err();
254+
vdpa.set_log_base(0x4000, None).unwrap_err();
255+
vdpa.set_log_fd(eventfd.as_raw_fd()).unwrap_err();
256+
257+
let max_queues = vdpa.get_vring_num().unwrap();
258+
vdpa.set_vring_num(0, max_queues + 1).unwrap_err();
259+
260+
vdpa.set_vring_num(0, 32).unwrap();
261+
262+
let config = VringConfigData {
263+
queue_max_size: 32,
264+
queue_size: 32,
265+
flags: 0,
266+
desc_table_addr: 0x1000,
267+
used_ring_addr: 0x2000,
268+
avail_ring_addr: 0x3000,
269+
log_addr: None,
270+
};
271+
vdpa.set_vring_addr(0, &config).unwrap();
272+
vdpa.set_vring_base(0, 1).unwrap();
273+
vdpa.set_vring_call(0, &eventfd).unwrap();
274+
vdpa.set_vring_kick(0, &eventfd).unwrap();
275+
vdpa.set_vring_err(0, &eventfd).unwrap();
276+
277+
vdpa.set_config_call(&eventfd).unwrap();
278+
279+
assert_eq!(vdpa.get_vring_base(0).unwrap(), 1);
280+
281+
vdpa.set_vring_enable(0, true).unwrap();
282+
vdpa.set_vring_enable(0, false).unwrap();
283+
}
284+
}

0 commit comments

Comments
 (0)