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::io::Error as IOError; 8 use std::os::raw::{c_uchar, c_uint}; 9 use std::os::unix::fs::OpenOptionsExt; 10 use std::os::unix::io::{AsRawFd, RawFd}; 11 12 use vm_memory::GuestAddressSpace; 13 use vmm_sys_util::eventfd::EventFd; 14 use vmm_sys_util::fam::*; 15 use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; 16 17 use super::vhost_binding::*; 18 use super::{ioctl_result, Error, Result, VhostKernBackend, VhostKernFeatures}; 19 use crate::vdpa::*; 20 use crate::{VhostAccess, VhostIotlbBackend, VhostIotlbMsg, VhostIotlbType, VringConfigData}; 21 22 // Implement the FamStruct trait for vhost_vdpa_config 23 generate_fam_struct_impl!( 24 vhost_vdpa_config, 25 c_uchar, 26 buf, 27 c_uint, 28 len, 29 c_uint::MAX as usize 30 ); 31 32 type VhostVdpaConfig = FamStructWrapper<vhost_vdpa_config>; 33 34 /// Handle for running VHOST_VDPA ioctls. 35 pub struct VhostKernVdpa<AS: GuestAddressSpace> { 36 fd: File, 37 mem: AS, 38 backend_features_acked: u64, 39 } 40 41 impl<AS: GuestAddressSpace> VhostKernVdpa<AS> { 42 /// Open a handle to a new VHOST-VDPA instance. new(path: &str, mem: AS) -> Result<Self>43 pub fn new(path: &str, mem: AS) -> Result<Self> { 44 Ok(VhostKernVdpa { 45 fd: OpenOptions::new() 46 .read(true) 47 .write(true) 48 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK) 49 .open(path) 50 .map_err(Error::VhostOpen)?, 51 mem, 52 backend_features_acked: 0, 53 }) 54 } 55 56 /// Create a `VhostKernVdpa` object with given content. with(fd: File, mem: AS, backend_features_acked: u64) -> Self57 pub fn with(fd: File, mem: AS, backend_features_acked: u64) -> Self { 58 VhostKernVdpa { 59 fd, 60 mem, 61 backend_features_acked, 62 } 63 } 64 65 /// Set the addresses for a given vring. 66 /// 67 /// # Arguments 68 /// * `queue_index` - Index of the queue to set addresses for. 69 /// * `config_data` - Vring config data, addresses of desc_table, avail_ring 70 /// and used_ring are in the guest address space. set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()>71 pub fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> { 72 if !self.is_valid(config_data) { 73 return Err(Error::InvalidQueue); 74 } 75 76 // vDPA backends expect IOVA (that can be mapped 1:1 with 77 // GPA when no IOMMU is involved). 78 let vring_addr = vhost_vring_addr { 79 index: queue_index as u32, 80 flags: config_data.flags, 81 desc_user_addr: config_data.desc_table_addr, 82 used_user_addr: config_data.used_ring_addr, 83 avail_user_addr: config_data.avail_ring_addr, 84 log_guest_addr: config_data.get_log_addr(), 85 }; 86 87 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 88 // return value checked. 89 let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ADDR(), &vring_addr) }; 90 ioctl_result(ret, ()) 91 } 92 } 93 94 impl<AS: GuestAddressSpace> VhostVdpa for VhostKernVdpa<AS> { get_device_id(&self) -> Result<u32>95 fn get_device_id(&self) -> Result<u32> { 96 let mut device_id: u32 = 0; 97 98 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 99 // return value checked. 100 let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_DEVICE_ID(), &mut device_id) }; 101 ioctl_result(ret, device_id) 102 } 103 get_status(&self) -> Result<u8>104 fn get_status(&self) -> Result<u8> { 105 let mut status: u8 = 0; 106 107 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 108 // return value checked. 109 let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_STATUS(), &mut status) }; 110 ioctl_result(ret, status) 111 } 112 set_status(&self, status: u8) -> Result<()>113 fn set_status(&self, status: u8) -> Result<()> { 114 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 115 // return value checked. 116 let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_STATUS(), &status) }; 117 ioctl_result(ret, ()) 118 } 119 get_config(&self, offset: u32, buffer: &mut [u8]) -> Result<()>120 fn get_config(&self, offset: u32, buffer: &mut [u8]) -> Result<()> { 121 let mut config = VhostVdpaConfig::new(buffer.len()) 122 .map_err(|_| Error::IoctlError(IOError::from_raw_os_error(libc::ENOMEM)))?; 123 124 // SAFETY: We are not modifying the `len` field of the vhost-vdpa fam-struct 125 unsafe { 126 config.as_mut_fam_struct().off = offset; 127 } 128 129 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 130 // return value checked. 131 let ret = unsafe { 132 ioctl_with_ptr( 133 self, 134 VHOST_VDPA_GET_CONFIG(), 135 config.as_mut_fam_struct_ptr(), 136 ) 137 }; 138 139 buffer.copy_from_slice(config.as_slice()); 140 141 ioctl_result(ret, ()) 142 } 143 set_config(&self, offset: u32, buffer: &[u8]) -> Result<()>144 fn set_config(&self, offset: u32, buffer: &[u8]) -> Result<()> { 145 let mut config = VhostVdpaConfig::new(buffer.len()) 146 .map_err(|_| Error::IoctlError(IOError::from_raw_os_error(libc::ENOMEM)))?; 147 148 // SAFETY: We are not modifying the `len` field of the vhost-vdpa fam-struct 149 unsafe { 150 config.as_mut_fam_struct().off = offset; 151 } 152 config.as_mut_slice().copy_from_slice(buffer); 153 154 let ret = 155 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 156 // return value checked. 157 unsafe { ioctl_with_ptr(self, VHOST_VDPA_SET_CONFIG(), config.as_fam_struct_ptr()) }; 158 ioctl_result(ret, ()) 159 } 160 set_vring_enable(&self, queue_index: usize, enabled: bool) -> Result<()>161 fn set_vring_enable(&self, queue_index: usize, enabled: bool) -> Result<()> { 162 let vring_state = vhost_vring_state { 163 index: queue_index as u32, 164 num: enabled as u32, 165 }; 166 167 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 168 // return value checked. 169 let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_VRING_ENABLE(), &vring_state) }; 170 ioctl_result(ret, ()) 171 } 172 get_vring_num(&self) -> Result<u16>173 fn get_vring_num(&self) -> Result<u16> { 174 let mut vring_num: u16 = 0; 175 176 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 177 // return value checked. 178 let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VRING_NUM(), &mut vring_num) }; 179 ioctl_result(ret, vring_num) 180 } 181 set_config_call(&self, fd: &EventFd) -> Result<()>182 fn set_config_call(&self, fd: &EventFd) -> Result<()> { 183 let event_fd: ::std::os::raw::c_int = fd.as_raw_fd(); 184 185 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 186 // return value checked. 187 let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_CONFIG_CALL(), &event_fd) }; 188 ioctl_result(ret, ()) 189 } 190 get_iova_range(&self) -> Result<VhostVdpaIovaRange>191 fn get_iova_range(&self) -> Result<VhostVdpaIovaRange> { 192 let mut low_iova_range = vhost_vdpa_iova_range { first: 0, last: 0 }; 193 194 let ret = 195 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 196 // return value checked. 197 unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_IOVA_RANGE(), &mut low_iova_range) }; 198 199 let iova_range = VhostVdpaIovaRange { 200 first: low_iova_range.first, 201 last: low_iova_range.last, 202 }; 203 204 ioctl_result(ret, iova_range) 205 } 206 get_config_size(&self) -> Result<u32>207 fn get_config_size(&self) -> Result<u32> { 208 let mut config_size: u32 = 0; 209 210 let ret = 211 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 212 // return value checked. 213 unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_CONFIG_SIZE(), &mut config_size) }; 214 ioctl_result(ret, config_size) 215 } 216 get_vqs_count(&self) -> Result<u32>217 fn get_vqs_count(&self) -> Result<u32> { 218 let mut vqs_count: u32 = 0; 219 220 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 221 // return value checked. 222 let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VQS_COUNT(), &mut vqs_count) }; 223 ioctl_result(ret, vqs_count) 224 } 225 get_group_num(&self) -> Result<u32>226 fn get_group_num(&self) -> Result<u32> { 227 let mut group_num: u32 = 0; 228 229 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 230 // return value checked. 231 let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_GROUP_NUM(), &mut group_num) }; 232 ioctl_result(ret, group_num) 233 } 234 get_as_num(&self) -> Result<u32>235 fn get_as_num(&self) -> Result<u32> { 236 let mut as_num: u32 = 0; 237 238 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 239 // return value checked. 240 let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_AS_NUM(), &mut as_num) }; 241 ioctl_result(ret, as_num) 242 } 243 get_vring_group(&self, queue_index: u32) -> Result<u32>244 fn get_vring_group(&self, queue_index: u32) -> Result<u32> { 245 let mut vring_state = vhost_vring_state { 246 index: queue_index, 247 ..Default::default() 248 }; 249 250 let ret = 251 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 252 // return value checked. 253 unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VRING_GROUP(), &mut vring_state) }; 254 ioctl_result(ret, vring_state.num) 255 } 256 set_group_asid(&self, group_index: u32, asid: u32) -> Result<()>257 fn set_group_asid(&self, group_index: u32, asid: u32) -> Result<()> { 258 let vring_state = vhost_vring_state { 259 index: group_index, 260 num: asid, 261 }; 262 263 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 264 // return value checked. 265 let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_GET_VRING_GROUP(), &vring_state) }; 266 ioctl_result(ret, ()) 267 } 268 suspend(&self) -> Result<()>269 fn suspend(&self) -> Result<()> { 270 // SAFETY: This ioctl is called on a valid vhost-vdpa fd and has its 271 // return value checked. 272 let ret = unsafe { ioctl(self, VHOST_VDPA_SUSPEND()) }; 273 ioctl_result(ret, ()) 274 } 275 dma_map(&self, iova: u64, size: u64, vaddr: *const u8, readonly: bool) -> Result<()>276 fn dma_map(&self, iova: u64, size: u64, vaddr: *const u8, readonly: bool) -> Result<()> { 277 let iotlb = VhostIotlbMsg { 278 iova, 279 size, 280 userspace_addr: vaddr as u64, 281 perm: match readonly { 282 true => VhostAccess::ReadOnly, 283 false => VhostAccess::ReadWrite, 284 }, 285 msg_type: VhostIotlbType::Update, 286 }; 287 288 self.send_iotlb_msg(&iotlb) 289 } 290 dma_unmap(&self, iova: u64, size: u64) -> Result<()>291 fn dma_unmap(&self, iova: u64, size: u64) -> Result<()> { 292 let iotlb = VhostIotlbMsg { 293 iova, 294 size, 295 msg_type: VhostIotlbType::Invalidate, 296 ..Default::default() 297 }; 298 299 self.send_iotlb_msg(&iotlb) 300 } 301 } 302 303 impl<AS: GuestAddressSpace> VhostKernBackend for VhostKernVdpa<AS> { 304 type AS = AS; 305 mem(&self) -> &Self::AS306 fn mem(&self) -> &Self::AS { 307 &self.mem 308 } 309 310 /// Check whether the ring configuration is valid. is_valid(&self, config_data: &VringConfigData) -> bool311 fn is_valid(&self, config_data: &VringConfigData) -> bool { 312 let queue_size = config_data.queue_size; 313 if queue_size > config_data.queue_max_size 314 || queue_size == 0 315 || (queue_size & (queue_size - 1)) != 0 316 { 317 return false; 318 } 319 320 // Since vDPA could be dealing with IOVAs corresponding to GVAs, it 321 // wouldn't make sense to go through the validation of the descriptor 322 // table address, available ring address and used ring address against 323 // the guest memory representation we have access to. 324 325 config_data.is_log_addr_valid() 326 } 327 } 328 329 impl<AS: GuestAddressSpace> AsRawFd for VhostKernVdpa<AS> { as_raw_fd(&self) -> RawFd330 fn as_raw_fd(&self) -> RawFd { 331 self.fd.as_raw_fd() 332 } 333 } 334 335 impl<AS: GuestAddressSpace> VhostKernFeatures for VhostKernVdpa<AS> { get_backend_features_acked(&self) -> u64336 fn get_backend_features_acked(&self) -> u64 { 337 self.backend_features_acked 338 } 339 set_backend_features_acked(&mut self, features: u64)340 fn set_backend_features_acked(&mut self, features: u64) { 341 self.backend_features_acked = features; 342 } 343 } 344 345 #[cfg(test)] 346 mod tests { 347 const VHOST_VDPA_PATH: &str = "/dev/vhost-vdpa-0"; 348 349 use std::alloc::{alloc, dealloc, Layout}; 350 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap}; 351 use vmm_sys_util::eventfd::EventFd; 352 353 use super::*; 354 use crate::{ 355 VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData, 356 }; 357 use serial_test::serial; 358 use std::io::ErrorKind; 359 360 /// macro to skip test if vhost-vdpa device path is not found. 361 /// 362 /// vDPA simulators are available since Linux 5.7, but the CI may have 363 /// an older kernel, so for now we skip the test if we don't find 364 /// the device. 365 macro_rules! unwrap_not_found { 366 ( $e:expr ) => { 367 match $e { 368 Ok(v) => v, 369 Err(error) => match error { 370 Error::VhostOpen(ref e) if e.kind() == ErrorKind::NotFound => { 371 println!("Err: {:?} SKIPPED", e); 372 return; 373 } 374 e => panic!("Err: {:?}", e), 375 }, 376 } 377 }; 378 } 379 380 macro_rules! validate_ioctl { 381 ( $e:expr, $ref_value:expr ) => { 382 match $e { 383 Ok(v) => assert_eq!(v, $ref_value), 384 Err(error) => match error { 385 Error::IoctlError(e) if e.raw_os_error().unwrap() == libc::ENOTTY => { 386 println!("Err: {:?} SKIPPED", e); 387 } 388 e => panic!("Err: {:?}", e), 389 }, 390 } 391 }; 392 } 393 394 #[test] 395 #[serial] test_vdpa_kern_new_device()396 fn test_vdpa_kern_new_device() { 397 let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); 398 let vdpa = unwrap_not_found!(VhostKernVdpa::new(VHOST_VDPA_PATH, &m)); 399 400 assert!(vdpa.as_raw_fd() >= 0); 401 assert!(vdpa.mem().find_region(GuestAddress(0x100)).is_some()); 402 assert!(vdpa.mem().find_region(GuestAddress(0x10_0000)).is_none()); 403 } 404 405 #[test] 406 #[serial] test_vdpa_kern_is_valid()407 fn test_vdpa_kern_is_valid() { 408 let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); 409 let vdpa = unwrap_not_found!(VhostKernVdpa::new(VHOST_VDPA_PATH, &m)); 410 411 let mut config = VringConfigData { 412 queue_max_size: 32, 413 queue_size: 32, 414 flags: 0, 415 desc_table_addr: 0x1000, 416 used_ring_addr: 0x2000, 417 avail_ring_addr: 0x3000, 418 log_addr: None, 419 }; 420 assert!(vdpa.is_valid(&config)); 421 422 config.queue_size = 0; 423 assert!(!vdpa.is_valid(&config)); 424 config.queue_size = 31; 425 assert!(!vdpa.is_valid(&config)); 426 config.queue_size = 33; 427 assert!(!vdpa.is_valid(&config)); 428 } 429 430 #[test] 431 #[serial] test_vdpa_kern_ioctls()432 fn test_vdpa_kern_ioctls() { 433 let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); 434 let vdpa = unwrap_not_found!(VhostKernVdpa::new(VHOST_VDPA_PATH, &m)); 435 436 let features = vdpa.get_features().unwrap(); 437 // VIRTIO_F_VERSION_1 (bit 32) should be set 438 assert_ne!(features & (1 << 32), 0); 439 vdpa.set_features(features).unwrap(); 440 441 vdpa.set_owner().unwrap(); 442 443 vdpa.set_mem_table(&[]).unwrap_err(); 444 445 let region = VhostUserMemoryRegionInfo::new( 446 0x0, 447 0x10_0000, 448 m.get_host_address(GuestAddress(0x0)).unwrap() as u64, 449 0, 450 -1, 451 ); 452 vdpa.set_mem_table(&[region]).unwrap(); 453 454 let device_id = vdpa.get_device_id().unwrap(); 455 assert!(device_id > 0); 456 457 assert_eq!(vdpa.get_status().unwrap(), 0x0); 458 vdpa.set_status(0x1).unwrap(); 459 assert_eq!(vdpa.get_status().unwrap(), 0x1); 460 461 let mut vec = vec![0u8; 8]; 462 vdpa.get_config(0, &mut vec).unwrap(); 463 vdpa.set_config(0, &vec).unwrap(); 464 465 let eventfd = EventFd::new(0).unwrap(); 466 467 // set_log_base() and set_log_fd() are not supported by vhost-vdpa 468 vdpa.set_log_base( 469 0x4000, 470 Some(VhostUserDirtyLogRegion { 471 mmap_size: 0x1000, 472 mmap_offset: 0x10, 473 mmap_handle: 1, 474 }), 475 ) 476 .unwrap_err(); 477 vdpa.set_log_base(0x4000, None).unwrap_err(); 478 vdpa.set_log_fd(eventfd.as_raw_fd()).unwrap_err(); 479 480 let max_queues = vdpa.get_vring_num().unwrap(); 481 vdpa.set_vring_num(0, max_queues + 1).unwrap_err(); 482 483 vdpa.set_vring_num(0, 32).unwrap(); 484 485 let config = VringConfigData { 486 queue_max_size: 32, 487 queue_size: 32, 488 flags: 0, 489 desc_table_addr: 0x1000, 490 used_ring_addr: 0x2000, 491 avail_ring_addr: 0x3000, 492 log_addr: None, 493 }; 494 vdpa.set_vring_addr(0, &config).unwrap(); 495 vdpa.set_vring_base(0, 1).unwrap(); 496 vdpa.set_vring_call(0, &eventfd).unwrap(); 497 vdpa.set_vring_kick(0, &eventfd).unwrap(); 498 vdpa.set_vring_err(0, &eventfd).unwrap(); 499 500 vdpa.set_config_call(&eventfd).unwrap(); 501 502 let iova_range = vdpa.get_iova_range().unwrap(); 503 // vDPA-block simulator returns [0, u64::MAX] range 504 assert_eq!(iova_range.first, 0); 505 assert_eq!(iova_range.last, u64::MAX); 506 507 let (config_size, vqs_count, group_num, as_num, vring_group) = if device_id == 1 { 508 (24, 3, 2, 2, 0) 509 } else if device_id == 2 { 510 (60, 1, 1, 1, 0) 511 } else { 512 panic!("Unexpected device id {}", device_id) 513 }; 514 515 validate_ioctl!(vdpa.get_config_size(), config_size); 516 validate_ioctl!(vdpa.get_vqs_count(), vqs_count); 517 validate_ioctl!(vdpa.get_group_num(), group_num); 518 validate_ioctl!(vdpa.get_as_num(), as_num); 519 validate_ioctl!(vdpa.get_vring_group(0), vring_group); 520 validate_ioctl!(vdpa.set_group_asid(0, 12345), ()); 521 522 if vdpa.get_backend_features().unwrap() & (1 << VHOST_BACKEND_F_SUSPEND) 523 == (1 << VHOST_BACKEND_F_SUSPEND) 524 { 525 validate_ioctl!(vdpa.suspend(), ()); 526 } 527 528 assert_eq!(vdpa.get_vring_base(0).unwrap(), 1); 529 530 vdpa.set_vring_enable(0, true).unwrap(); 531 vdpa.set_vring_enable(0, false).unwrap(); 532 } 533 534 #[test] 535 #[serial] test_vdpa_kern_dma()536 fn test_vdpa_kern_dma() { 537 let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); 538 let mut vdpa = unwrap_not_found!(VhostKernVdpa::new(VHOST_VDPA_PATH, &m)); 539 540 let features = vdpa.get_features().unwrap(); 541 // VIRTIO_F_VERSION_1 (bit 32) should be set 542 assert_ne!(features & (1 << 32), 0); 543 vdpa.set_features(features).unwrap(); 544 545 let backend_features = vdpa.get_backend_features().unwrap(); 546 assert_ne!(backend_features & (1 << VHOST_BACKEND_F_IOTLB_MSG_V2), 0); 547 vdpa.set_backend_features(backend_features).unwrap(); 548 549 vdpa.set_owner().unwrap(); 550 551 vdpa.dma_map(0xFFFF_0000, 0xFFFF, std::ptr::null::<u8>(), false) 552 .unwrap_err(); 553 554 let layout = Layout::from_size_align(0xFFFF, 1).unwrap(); 555 556 // SAFETY: Safe because layout has non-zero size. 557 let ptr = unsafe { alloc(layout) }; 558 559 vdpa.dma_map(0xFFFF_0000, 0xFFFF, ptr, false).unwrap(); 560 vdpa.dma_unmap(0xFFFF_0000, 0xFFFF).unwrap(); 561 562 // SAFETY: Safe because `ptr` is allocated with the same allocator 563 // using the same `layout`. 564 unsafe { dealloc(ptr, layout) }; 565 } 566 } 567