Skip to content

Commit b482edb

Browse files
aesteve-rhmtjhrc
authored andcommitted
vhost_user: Add support for SHMEM_MAP/UNMAP backend requests
Add request defintions and methods for using the new SHMEM_MAP/UNMAP backend requests. Note that at the time of writing this, these requests are part of this RFC in QEMU: https://mail.gnu.org/archive/html/qemu-devel/2024-06/msg05736.html Co-authored-by: Albert Esteve <[email protected]> Co-authored-by: Matej Hrica <[email protected]> Signed-off-by: Albert Esteve <[email protected]> Signed-off-by: Matej Hrica <[email protected]>
1 parent c14f10b commit b482edb

File tree

4 files changed

+206
-1
lines changed

4 files changed

+206
-1
lines changed

vhost/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
### Added
55
- [[#268]](https://github.com/rust-vmm/vhost/pull/268) Add support for `VHOST_USER_GET_SHARED_OBJECT`
6+
- [[#251]](https://github.com/rust-vmm/vhost/pull/251) Add `SHMEM_MAP` and `SHMEM_UNMAP` support
67

78
### Changed
89

vhost/src/vhost_user/backend_req.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,16 @@ impl VhostUserFrontendReqHandler for Backend {
183183
Some(&[fd.as_raw_fd()]),
184184
)
185185
}
186+
187+
/// Forward vhost-user memory map file request to the frontend.
188+
fn shmem_map(&self, req: &VhostUserMMap, fd: &dyn AsRawFd) -> HandlerResult<u64> {
189+
self.send_message(BackendReq::SHMEM_MAP, req, Some(&[fd.as_raw_fd()]))
190+
}
191+
192+
/// Forward vhost-user memory unmap file request to the frontend.
193+
fn shmem_unmap(&self, req: &VhostUserMMap) -> HandlerResult<u64> {
194+
self.send_message(BackendReq::SHMEM_UNMAP, req, None)
195+
}
186196
}
187197

188198
#[cfg(test)]
@@ -269,4 +279,50 @@ mod tests {
269279
.shared_object_add(&VhostUserSharedMsg::default())
270280
.unwrap();
271281
}
282+
283+
#[test]
284+
fn test_shmem_map() {
285+
let (mut fronted, backend) = frontend_backend_pair();
286+
287+
let (_, some_fd_to_send) = UnixStream::pair().unwrap();
288+
let map_request = VhostUserMMap {
289+
shmid: 0,
290+
padding: Default::default(),
291+
fd_offset: 0,
292+
shm_offset: 1028,
293+
len: 4096,
294+
flags: (VhostUserMMapFlags::MAP_R | VhostUserMMapFlags::MAP_W).bits(),
295+
};
296+
297+
backend.shmem_map(&map_request, &some_fd_to_send).unwrap();
298+
299+
let (hdr, request, fd) = fronted.recv_body::<VhostUserMMap>().unwrap();
300+
assert_eq!(hdr.get_code().unwrap(), BackendReq::SHMEM_MAP);
301+
assert!(fd.is_some());
302+
assert_eq!({ request.shm_offset }, { map_request.shm_offset });
303+
assert_eq!({ request.len }, { map_request.len },);
304+
assert_eq!({ request.flags }, { map_request.flags });
305+
}
306+
307+
#[test]
308+
fn test_shmem_unmap() {
309+
let (mut frontend, backend) = frontend_backend_pair();
310+
311+
let unmap_request = VhostUserMMap {
312+
shmid: 0,
313+
padding: Default::default(),
314+
fd_offset: 0,
315+
shm_offset: 1028,
316+
len: 4096,
317+
flags: 0,
318+
};
319+
320+
backend.shmem_unmap(&unmap_request).unwrap();
321+
322+
let (hdr, request, fd) = frontend.recv_body::<VhostUserMMap>().unwrap();
323+
assert_eq!(hdr.get_code().unwrap(), BackendReq::SHMEM_UNMAP);
324+
assert!(fd.is_none());
325+
assert_eq!({ request.shm_offset }, { unmap_request.shm_offset });
326+
assert_eq!({ request.len }, { unmap_request.len });
327+
}
272328
}

vhost/src/vhost_user/frontend_req_handler.rs

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ pub trait VhostUserFrontendReqHandler {
5252
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
5353
}
5454

55+
/// Handle shared memory region mapping requests.
56+
fn shmem_map(&self, _req: &VhostUserMMap, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
57+
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
58+
}
59+
60+
/// Handle shared memory region unmapping requests.
61+
fn shmem_unmap(&self, _req: &VhostUserMMap) -> HandlerResult<u64> {
62+
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
63+
}
64+
5565
// fn handle_iotlb_msg(&mut self, iotlb: VhostUserIotlb);
5666
// fn handle_vring_host_notifier(&mut self, area: VhostUserVringArea, fd: &dyn AsRawFd);
5767
}
@@ -84,6 +94,16 @@ pub trait VhostUserFrontendReqHandlerMut {
8494
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
8595
}
8696

97+
/// Handle shared memory region mapping requests.
98+
fn shmem_map(&mut self, _req: &VhostUserMMap, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
99+
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
100+
}
101+
102+
/// Handle shared memory region unmapping requests.
103+
fn shmem_unmap(&mut self, _req: &VhostUserMMap) -> HandlerResult<u64> {
104+
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
105+
}
106+
87107
// fn handle_iotlb_msg(&mut self, iotlb: VhostUserIotlb);
88108
// fn handle_vring_host_notifier(&mut self, area: VhostUserVringArea, fd: RawFd);
89109
}
@@ -111,6 +131,14 @@ impl<S: VhostUserFrontendReqHandlerMut> VhostUserFrontendReqHandler for Mutex<S>
111131
) -> HandlerResult<u64> {
112132
self.lock().unwrap().shared_object_lookup(uuid, fd)
113133
}
134+
135+
fn shmem_map(&self, req: &VhostUserMMap, fd: &dyn AsRawFd) -> HandlerResult<u64> {
136+
self.lock().unwrap().shmem_map(req, fd)
137+
}
138+
139+
fn shmem_unmap(&self, req: &VhostUserMMap) -> HandlerResult<u64> {
140+
self.lock().unwrap().shmem_unmap(req)
141+
}
114142
}
115143

116144
/// Server to handle service requests from backends from the backend communication channel.
@@ -241,6 +269,18 @@ impl<S: VhostUserFrontendReqHandler> FrontendReqHandler<S> {
241269
.shared_object_lookup(&msg, &files.unwrap()[0])
242270
.map_err(Error::ReqHandlerError)
243271
}
272+
Ok(BackendReq::SHMEM_MAP) => {
273+
let msg = self.extract_msg_body::<VhostUserMMap>(&hdr, size, &buf)?;
274+
self.backend
275+
.shmem_map(&msg, &files.unwrap()[0])
276+
.map_err(Error::ReqHandlerError)
277+
}
278+
Ok(BackendReq::SHMEM_UNMAP) => {
279+
let msg = self.extract_msg_body::<VhostUserMMap>(&hdr, size, &buf)?;
280+
self.backend
281+
.shmem_unmap(&msg)
282+
.map_err(Error::ReqHandlerError)
283+
}
244284
_ => Err(Error::InvalidMessage),
245285
};
246286

@@ -278,7 +318,7 @@ impl<S: VhostUserFrontendReqHandler> FrontendReqHandler<S> {
278318
files: &Option<Vec<File>>,
279319
) -> Result<()> {
280320
match hdr.get_code() {
281-
Ok(BackendReq::SHARED_OBJECT_LOOKUP) => {
321+
Ok(BackendReq::SHARED_OBJECT_LOOKUP | BackendReq::SHMEM_MAP) => {
282322
// Expect a single file is passed.
283323
match files {
284324
Some(files) if files.len() == 1 => Ok(()),
@@ -366,12 +406,14 @@ mod tests {
366406

367407
struct MockFrontendReqHandler {
368408
shared_objects: HashSet<Uuid>,
409+
shmem_mappings: HashSet<(u64, u64)>,
369410
}
370411

371412
impl MockFrontendReqHandler {
372413
fn new() -> Self {
373414
Self {
374415
shared_objects: HashSet::new(),
416+
shmem_mappings: HashSet::new(),
375417
}
376418
}
377419
}
@@ -395,6 +437,16 @@ mod tests {
395437
}
396438
Ok(1)
397439
}
440+
441+
fn shmem_map(&mut self, req: &VhostUserMMap, _fd: &dyn AsRawFd) -> HandlerResult<u64> {
442+
assert_eq!({ req.shmid }, 0);
443+
Ok(!self.shmem_mappings.insert((req.shm_offset, req.len)) as u64)
444+
}
445+
446+
fn shmem_unmap(&mut self, req: &VhostUserMMap) -> HandlerResult<u64> {
447+
assert_eq!({ req.shmid }, 0);
448+
Ok(!self.shmem_mappings.remove(&(req.shm_offset, req.len)) as u64)
449+
}
398450
}
399451

400452
#[test]
@@ -436,6 +488,13 @@ mod tests {
436488
assert_eq!(handler.handle_request().unwrap(), 1);
437489
assert_eq!(handler.handle_request().unwrap(), 0);
438490
assert_eq!(handler.handle_request().unwrap(), 1);
491+
492+
// Testing shmem map/unmap messages.
493+
assert_eq!(handler.handle_request().unwrap(), 0);
494+
assert_eq!(handler.handle_request().unwrap(), 1);
495+
assert_eq!(handler.handle_request().unwrap(), 0);
496+
assert_eq!(handler.handle_request().unwrap(), 0);
497+
assert_eq!(handler.handle_request().unwrap(), 0);
439498
});
440499

441500
backend.set_shared_object_flag(true);
@@ -456,6 +515,24 @@ mod tests {
456515
.is_ok());
457516
assert!(backend.shared_object_remove(&shobj_msg).is_ok());
458517
assert!(backend.shared_object_remove(&shobj_msg).is_ok());
518+
519+
let (_, some_fd_to_map) = UnixStream::pair().unwrap();
520+
let map_request1 = VhostUserMMap {
521+
shm_offset: 0,
522+
len: 4096,
523+
..Default::default()
524+
};
525+
let map_request2 = VhostUserMMap {
526+
shm_offset: 4096,
527+
len: 8192,
528+
..Default::default()
529+
};
530+
backend.shmem_map(&map_request1, &some_fd_to_map).unwrap();
531+
backend.shmem_unmap(&map_request2).unwrap();
532+
backend.shmem_map(&map_request2, &some_fd_to_map).unwrap();
533+
backend.shmem_unmap(&map_request2).unwrap();
534+
backend.shmem_unmap(&map_request1).unwrap();
535+
459536
// Ensure that the handler thread did not panic.
460537
assert!(frontend_handler.join().is_ok());
461538
}
@@ -485,6 +562,13 @@ mod tests {
485562
assert_eq!(handler.handle_request().unwrap(), 1);
486563
assert_eq!(handler.handle_request().unwrap(), 0);
487564
assert_eq!(handler.handle_request().unwrap(), 1);
565+
566+
// Testing shmem map/unmap messages.
567+
assert_eq!(handler.handle_request().unwrap(), 0);
568+
assert_eq!(handler.handle_request().unwrap(), 1);
569+
assert_eq!(handler.handle_request().unwrap(), 0);
570+
assert_eq!(handler.handle_request().unwrap(), 0);
571+
assert_eq!(handler.handle_request().unwrap(), 0);
488572
});
489573

490574
backend.set_reply_ack_flag(true);
@@ -506,6 +590,24 @@ mod tests {
506590
.is_err());
507591
assert!(backend.shared_object_remove(&shobj_msg).is_ok());
508592
assert!(backend.shared_object_remove(&shobj_msg).is_err());
593+
594+
let (_, some_fd_to_map) = UnixStream::pair().unwrap();
595+
let map_request1 = VhostUserMMap {
596+
shm_offset: 0,
597+
len: 4096,
598+
..Default::default()
599+
};
600+
let map_request2 = VhostUserMMap {
601+
shm_offset: 4096,
602+
len: 8192,
603+
..Default::default()
604+
};
605+
backend.shmem_map(&map_request1, &some_fd_to_map).unwrap();
606+
backend.shmem_unmap(&map_request2).unwrap_err();
607+
backend.shmem_map(&map_request2, &some_fd_to_map).unwrap();
608+
backend.shmem_unmap(&map_request2).unwrap();
609+
backend.shmem_unmap(&map_request1).unwrap();
610+
509611
// Ensure that the handler thread did not panic.
510612
assert!(frontend_handler.join().is_ok());
511613
}

vhost/src/vhost_user/message.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ enum_value! {
194194
SHARED_OBJECT_REMOVE = 7,
195195
/// Lookup for a virtio shared object.
196196
SHARED_OBJECT_LOOKUP = 8,
197+
/// Map memory into guest address space
198+
SHMEM_MAP = 9,
199+
/// Unmap memory from guest address space
200+
SHMEM_UNMAP = 10,
197201
}
198202
}
199203

@@ -990,6 +994,48 @@ impl VhostUserMsgValidator for VhostUserTransferDeviceState {
990994
}
991995
}
992996

997+
// Bit mask for flags in VhostUserMMap struct
998+
bitflags! {
999+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
1000+
/// Flags specifying access permissions of memory mapping of a file
1001+
pub struct VhostUserMMapFlags: u64 {
1002+
/// Empty permission.
1003+
const EMPTY = 0x0;
1004+
/// Read permission.
1005+
const MAP_R = 0x1;
1006+
/// Write permission.
1007+
const MAP_W = 0x2;
1008+
}
1009+
}
1010+
1011+
/// Backend request to mmap a file-backed buffer into guest memory
1012+
#[repr(C, packed)]
1013+
#[derive(Debug, Copy, Clone, Default)]
1014+
pub struct VhostUserMMap {
1015+
/// Shared memory region ID.
1016+
pub shmid: u8,
1017+
/// Struct padding.
1018+
pub padding: [u8; 7],
1019+
/// File offset.
1020+
pub fd_offset: u64,
1021+
/// Offset into the shared memory region.
1022+
pub shm_offset: u64,
1023+
/// Size of region to map.
1024+
pub len: u64,
1025+
/// Flags for the mmap operation
1026+
pub flags: u64,
1027+
}
1028+
1029+
// SAFETY: Safe because all fields of VhostUserBackendMapMsg are POD.
1030+
unsafe impl ByteValued for VhostUserMMap {}
1031+
1032+
impl VhostUserMsgValidator for VhostUserMMap {
1033+
fn is_valid(&self) -> bool {
1034+
(self.flags & !VhostUserMMapFlags::all().bits()) == 0
1035+
&& self.fd_offset.checked_add(self.len).is_some()
1036+
&& self.shm_offset.checked_add(self.len).is_some()
1037+
}
1038+
}
9931039
/// Inflight I/O descriptor state for split virtqueues
9941040
#[repr(C, packed)]
9951041
#[derive(Clone, Copy, Default)]

0 commit comments

Comments
 (0)