This commit is contained in:
Matej Hrica 2025-12-10 18:13:09 +01:00 committed by GitHub
commit 36d8a17ed6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 640 additions and 208 deletions

83
Cargo.lock generated
View File

@ -2006,6 +2006,18 @@ dependencies = [
"vmm-sys-util",
]
[[package]]
name = "vhost"
version = "0.15.0"
source = "git+https://github.com/mtjhrc/vhost.git?branch=shmem#c686ebb25a0af340a7969f637ba0d1b1e160fe6c"
dependencies = [
"bitflags 2.10.0",
"libc 0.2.177",
"uuid",
"vm-memory",
"vmm-sys-util",
]
[[package]]
name = "vhost-device-can"
version = "0.1.0"
@ -2017,8 +2029,8 @@ dependencies = [
"queues",
"socketcan",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2038,8 +2050,8 @@ dependencies = [
"log",
"queues",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2057,8 +2069,8 @@ dependencies = [
"libgpiod",
"log",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2080,8 +2092,8 @@ dependencies = [
"rutabaga_gfx",
"tempfile",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (git+https://github.com/mtjhrc/vhost.git?branch=shmem)",
"vhost-user-backend 0.21.0 (git+https://github.com/mtjhrc/vhost.git?branch=shmem)",
"virglrenderer",
"virtio-bindings",
"virtio-queue",
@ -2099,8 +2111,8 @@ dependencies = [
"libc 0.2.177",
"log",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2122,8 +2134,8 @@ dependencies = [
"rand",
"tempfile",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2143,8 +2155,8 @@ dependencies = [
"rand",
"tempfile",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2161,8 +2173,8 @@ dependencies = [
"itertools 0.14.0",
"log",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2180,8 +2192,8 @@ dependencies = [
"log",
"tempfile",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2205,8 +2217,8 @@ dependencies = [
"rusty-fork",
"tempfile",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2224,8 +2236,8 @@ dependencies = [
"libc 0.2.177",
"log",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2242,8 +2254,8 @@ dependencies = [
"libc 0.2.177",
"log",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2265,8 +2277,8 @@ dependencies = [
"serde",
"tempfile",
"thiserror 2.0.17",
"vhost",
"vhost-user-backend",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vhost-user-backend 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"virtio-vsock",
@ -2283,7 +2295,21 @@ checksum = "783587813a59c42c36519a6e12bb31eb2d7fa517377428252ba4cc2312584243"
dependencies = [
"libc 0.2.177",
"log",
"vhost",
"vhost 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
"vmm-sys-util",
]
[[package]]
name = "vhost-user-backend"
version = "0.21.0"
source = "git+https://github.com/mtjhrc/vhost.git?branch=shmem#c686ebb25a0af340a7969f637ba0d1b1e160fe6c"
dependencies = [
"libc 0.2.177",
"log",
"vhost 0.15.0 (git+https://github.com/mtjhrc/vhost.git?branch=shmem)",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@ -2293,8 +2319,7 @@ dependencies = [
[[package]]
name = "virglrenderer"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b3ceb5f84adcbd531661a6c6c0883c3d6cd83427886d3179675b19268f4450"
source = "git+https://gitlab.freedesktop.org/mtjhrc/virglrenderer-rs.git?branch=map_info#a4b3fb2efe39f06c5ea1d96a0a51c3f66df0f79b"
dependencies = [
"libc 1.0.0-alpha.1",
"log",

View File

@ -28,9 +28,9 @@ log = "0.4"
[target.'cfg(not(target_env = "musl"))'.dependencies]
rutabaga_gfx = "0.1.75"
thiserror = "2.0.17"
virglrenderer = {version = "0.1.2", optional = true }
vhost = { version = "0.15.0", features = ["vhost-user-backend"] }
vhost-user-backend = "0.21"
virglrenderer = { git = "https://gitlab.freedesktop.org/mtjhrc/virglrenderer-rs.git", branch = "map_info", optional = true }
vhost = { git = "https://github.com/mtjhrc/vhost.git", branch = "shmem", features = ["vhost-user-backend"] }
vhost-user-backend = { git = "https://github.com/mtjhrc/vhost.git", branch = "shmem" }
virtio-bindings = "0.2.5"
virtio-queue = "0.17.0"
vm-memory = "0.17.1"

View File

@ -2,12 +2,16 @@
//
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
use std::sync::{Arc, Mutex};
use std::{
os::fd::{AsFd, AsRawFd},
sync::{Arc, Mutex},
};
use log::{debug, error};
use log::{debug, error, trace};
use vhost::vhost_user::{
gpu_message::{VhostUserGpuCursorPos, VhostUserGpuCursorUpdate, VhostUserGpuEdidRequest},
GpuBackend,
message::{VhostUserMMap, VhostUserMMapFlags},
Backend, GpuBackend, VhostUserFrontendReqHandler,
};
use vm_memory::VolatileSlice;
@ -21,6 +25,59 @@ use crate::{
renderer::Renderer,
};
/// Common helper for mapping blob resources.
/// Maps a blob resource using the vhost-user backend's shmem_map.
pub fn common_map_blob(
backend: &Backend,
flags: VhostUserMMapFlags,
handle_fd: &(impl AsFd + AsRawFd),
blob_size: u64,
offset: u64,
resource_id: u32,
) -> Result<(), GpuResponse> {
trace!("Mapping blob resource_id={resource_id} offset={offset} size={blob_size}");
let map_request = VhostUserMMap {
shmid: 0,
padding: Default::default(),
fd_offset: 0,
shm_offset: offset,
len: blob_size,
flags: flags.bits(),
};
backend.shmem_map(&map_request, handle_fd).map_err(|e| {
error!("Failed to mmap by frontend: {e:?}");
ErrUnspec
})?;
Ok(())
}
/// Common helper for unmapping blob resources.
/// Unmaps a blob resource using the vhost-user backend's shmem_unmap.
pub fn common_unmap_blob(
backend: &Backend,
blob_size: u64,
offset: u64,
) -> Result<(), GpuResponse> {
let unmap_request = VhostUserMMap {
shmid: 0,
padding: Default::default(),
fd_offset: 0,
shm_offset: offset,
len: blob_size,
flags: 0,
};
backend.shmem_unmap(&unmap_request).map_err(|e| {
error!("Failed to unmap by frontend: {e:?}");
ErrUnspec
})?;
Ok(())
}
#[derive(Debug, Clone)]
pub struct VirtioGpuScanout {
pub resource_id: u32,

View File

@ -8,7 +8,10 @@ use std::{
cell::RefCell,
collections::BTreeMap,
io::IoSliceMut,
os::{fd::FromRawFd, raw::c_void},
os::{
fd::{AsFd, FromRawFd},
raw::c_void,
},
sync::{Arc, Mutex},
};
@ -16,32 +19,39 @@ use log::{debug, error, warn};
use rutabaga_gfx::{
ResourceCreate3D, Rutabaga, RutabagaBuilder, RutabagaComponentType, RutabagaFence,
RutabagaFenceHandler, RutabagaHandle, RutabagaIntoRawDescriptor, RutabagaIovec, Transfer3D,
RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD, RUTABAGA_MAP_ACCESS_MASK, RUTABAGA_MAP_ACCESS_READ,
RUTABAGA_MAP_ACCESS_RW, RUTABAGA_MAP_ACCESS_WRITE, RUTABAGA_MAP_CACHE_MASK,
};
use vhost::vhost_user::{
gpu_message::{
VhostUserGpuCursorPos, VhostUserGpuEdidRequest, VhostUserGpuScanout, VhostUserGpuUpdate,
},
GpuBackend,
message::VhostUserMMapFlags,
Backend, GpuBackend,
};
use vhost_user_backend::{VringRwLock, VringT};
use virtio_bindings::virtio_gpu::VIRTIO_GPU_BLOB_MEM_HOST3D;
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap, VolatileSlice};
use vmm_sys_util::eventfd::EventFd;
use crate::{
backend::{
common,
common::{common_set_scanout_disable, AssociatedScanouts, CursorConfig, VirtioGpuScanout},
common::{
common_map_blob, common_set_scanout_disable, common_unmap_blob, AssociatedScanouts,
CursorConfig, VirtioGpuScanout,
},
},
device::Error,
gpu_types::{FenceState, ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
gpu_types::{FenceState, ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing},
protocol::{
virtio_gpu_rect, GpuResponse,
GpuResponse::{
ErrInvalidParameter, ErrInvalidResourceId, ErrUnspec, OkCapset, OkCapsetInfo, OkNoData,
OkResourcePlaneInfo,
ErrInvalidParameter, ErrInvalidResourceId, ErrUnspec, OkCapset, OkCapsetInfo,
OkMapInfo, OkNoData, OkResourcePlaneInfo,
},
GpuResponsePlaneInfo, VirtioGpuResult, VIRTIO_GPU_FLAG_INFO_RING_IDX,
VIRTIO_GPU_MAX_SCANOUTS,
GpuResponsePlaneInfo, VirtioGpuResult, VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE,
VIRTIO_GPU_FLAG_INFO_RING_IDX, VIRTIO_GPU_MAX_SCANOUTS,
},
renderer::Renderer,
GpuConfig,
@ -59,6 +69,8 @@ pub struct GfxstreamResource {
scanouts: common::AssociatedScanouts,
pub info_3d: Option<rutabaga_gfx::Resource3DInfo>,
pub handle: Option<Arc<RutabagaHandle>>,
pub blob_size: u64,
pub blob_shmem_offset: Option<u64>,
}
impl GfxstreamResource {
@ -85,6 +97,8 @@ impl GfxstreamResource {
scanouts: AssociatedScanouts::default(),
info_3d: None,
handle: None,
blob_size: 0,
blob_shmem_offset: None,
}
}
}
@ -96,6 +110,8 @@ thread_local! {
}
pub struct GfxstreamAdapter {
#[allow(dead_code)]
backend: Backend,
gpu_backend: GpuBackend,
resources: BTreeMap<u32, GfxstreamResource>,
fence_state: Arc<Mutex<FenceState>>,
@ -103,7 +119,12 @@ pub struct GfxstreamAdapter {
}
impl GfxstreamAdapter {
pub fn new(queue_ctl: &VringRwLock, gpu_config: &GpuConfig, gpu_backend: GpuBackend) -> Self {
pub fn new(
queue_ctl: &VringRwLock,
backend: Backend,
gpu_config: &GpuConfig,
gpu_backend: GpuBackend,
) -> Self {
let fence_state = Arc::new(Mutex::new(FenceState::default()));
let fence = Self::create_fence_handler(queue_ctl.clone(), fence_state.clone());
@ -117,6 +138,7 @@ impl GfxstreamAdapter {
});
Self {
backend,
gpu_backend,
fence_state,
resources: BTreeMap::new(),
@ -281,6 +303,8 @@ impl Renderer for GfxstreamAdapter {
scanouts: AssociatedScanouts::default(),
info_3d: None,
handle: None,
blob_size: 0,
blob_shmem_offset: None,
};
debug_assert!(
!self.resources.contains_key(&resource_id),
@ -653,25 +677,119 @@ impl Renderer for GfxstreamAdapter {
fn resource_create_blob(
&mut self,
_ctx_id: u32,
_resource_id: u32,
_blob_id: u64,
_size: u64,
_blob_mem: u32,
_blob_flags: u32,
ctx_id: u32,
resource_create_blob: ResourceCreateBlob,
vecs: Vec<(vm_memory::GuestAddress, usize)>,
mem: &vm_memory::GuestMemoryMmap,
) -> VirtioGpuResult {
error!("Not implemented: resource_create_blob");
Err(ErrUnspec)
let mut rutabaga_iovecs = None;
if resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE != 0 {
panic!("GUEST_HANDLE unimplemented");
} else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D {
rutabaga_iovecs =
Some(Self::sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?);
}
Self::with_rutabaga(|rutabaga| {
rutabaga.resource_create_blob(
ctx_id,
resource_create_blob.resource_id,
resource_create_blob.into(),
rutabaga_iovecs,
None,
)
})?;
let resource = GfxstreamResource {
id: resource_create_blob.resource_id,
width: 0,
height: 0,
scanouts: AssociatedScanouts::default(),
info_3d: None,
handle: None,
blob_size: resource_create_blob.size,
blob_shmem_offset: None,
};
debug_assert!(
!self
.resources
.contains_key(&resource_create_blob.resource_id),
"Resource ID {} already exists in the resources map.",
resource_create_blob.resource_id
);
// Rely on rutabaga to check for duplicate resource ids.
self.resources
.insert(resource_create_blob.resource_id, resource);
Ok(Self::result_from_query(resource_create_blob.resource_id))
}
fn resource_map_blob(&mut self, _resource_id: u32, _offset: u64) -> VirtioGpuResult {
error!("Not implemented: resource_map_blob");
Err(ErrUnspec)
fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult {
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(ErrInvalidResourceId)?;
let map_info = Self::with_rutabaga(|rutabaga| rutabaga.map_info(resource_id))
.map_err(|_| ErrUnspec)?;
let export = Self::with_rutabaga(|rutabaga| rutabaga.export_blob(resource_id))
.map_err(|_| ErrUnspec)?;
// Check handle type - we don't support OPAQUE_FD mapping
if export.handle_type == RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD {
return Err(ErrUnspec);
}
// Convert map_info access flags to VhostUserMMapFlags
let flags = match map_info & RUTABAGA_MAP_ACCESS_MASK {
RUTABAGA_MAP_ACCESS_READ => VhostUserMMapFlags::default(),
RUTABAGA_MAP_ACCESS_WRITE => VhostUserMMapFlags::WRITABLE,
RUTABAGA_MAP_ACCESS_RW => VhostUserMMapFlags::WRITABLE,
_ => {
error!("Invalid access mask for blob resource, map_info: {map_info}");
return Err(ErrUnspec);
}
};
common_map_blob(
&self.backend,
flags,
&export.os_handle.as_fd(),
resource.blob_size,
offset,
resource_id,
)?;
resource.blob_shmem_offset = Some(offset);
// Return cache flags only (access flags not part of virtio-gpu spec)
Ok(OkMapInfo {
map_info: map_info & RUTABAGA_MAP_CACHE_MASK,
})
}
fn resource_unmap_blob(&mut self, _resource_id: u32) -> VirtioGpuResult {
error!("Not implemented: resource_unmap_blob");
Err(ErrUnspec)
fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult {
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(ErrInvalidResourceId)?;
let Some(offset) = resource.blob_shmem_offset else {
log::warn!(
"Guest tried to unmap blob resource with resource_id={resource_id}, but it is not \
mapped!"
);
return Err(ErrInvalidParameter);
};
common_unmap_blob(&self.backend, resource.blob_size, offset)?;
resource.blob_shmem_offset = None;
Ok(OkNoData)
}
}
@ -732,6 +850,11 @@ mod gfx_fence_tests {
GpuBackend::from_stream(backend)
}
fn dummy_backend() -> Backend {
let (_, backend) = UnixStream::pair().unwrap();
Backend::from_stream(backend)
}
/// Attempts to create a GPU adapter for testing.
/// Returns None if gfxstream initialization fails (e.g., in CI without GPU
/// drivers).
@ -776,6 +899,7 @@ mod gfx_fence_tests {
});
Some(GfxstreamAdapter {
backend: dummy_backend(),
gpu_backend: dummy_gpu_backend(),
resources: BTreeMap::default(),
fence_state,

View File

@ -13,7 +13,7 @@ use vm_memory::{GuestAddress, GuestMemoryMmap, VolatileSlice};
use vmm_sys_util::eventfd::EventFd;
use crate::{
gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
gpu_types::{ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing},
protocol::{virtio_gpu_rect, GpuResponse, VirtioGpuResult},
renderer::Renderer,
GpuConfig,
@ -27,6 +27,7 @@ impl NullAdapter {
pub fn new(
_queue_ctl: &vhost_user_backend::VringRwLock,
_config: &GpuConfig,
_backend: vhost::vhost_user::Backend,
gpu_backend: GpuBackend,
) -> Self {
trace!("NullAdapter created");
@ -224,11 +225,9 @@ impl Renderer for NullAdapter {
fn resource_create_blob(
&mut self,
_ctx_id: u32,
_resource_id: u32,
_blob_id: u64,
_size: u64,
_blob_mem: u32,
_blob_flags: u32,
_resource_create_blob: ResourceCreateBlob,
_vecs: Vec<(vm_memory::GuestAddress, usize)>,
_mem: &vm_memory::GuestMemoryMmap,
) -> VirtioGpuResult {
trace!("NullAdapter::resource_create_blob - no-op");
Ok(GpuResponse::OkNoData)
@ -256,15 +255,16 @@ mod tests {
use crate::{GpuFlags, GpuMode};
fn create_null_adapter() -> NullAdapter {
let (_, backend) = UnixStream::pair().unwrap();
let gpu_backend = GpuBackend::from_stream(backend);
let (stream1, stream2) = UnixStream::pair().unwrap();
let backend = vhost::vhost_user::Backend::from_stream(stream1);
let gpu_backend = GpuBackend::from_stream(stream2);
let mem = GuestMemoryAtomic::new(
GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
);
let vring = VringRwLock::new(mem, 0x100).unwrap();
let config = GpuConfig::new(GpuMode::Null, None, GpuFlags::default()).unwrap();
NullAdapter::new(&vring, &config, gpu_backend)
NullAdapter::new(&vring, &config, backend, gpu_backend)
}
#[test]
@ -430,9 +430,21 @@ mod tests {
#[test]
fn test_null_adapter_blob_operations() {
let mut adapter = create_null_adapter();
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
// Verify blob resource creation succeeds
let result = adapter.resource_create_blob(0, 1, 1, 4096, 0, 0);
let result = adapter.resource_create_blob(
0,
ResourceCreateBlob {
resource_id: 1,
blob_id: 1,
blob_mem: 0,
blob_flags: 0,
size: 4096,
},
vec![],
&mem,
);
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
// Verify mapping blob resource succeeds

View File

@ -4,53 +4,54 @@
//
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
use bitflags::Flags;
use libc::c_void;
use log::{debug, error, trace, warn};
use rutabaga_gfx::RutabagaFence;
use std::{
collections::BTreeMap,
io::IoSliceMut,
os::fd::{AsFd, FromRawFd, IntoRawFd, RawFd},
sync::{Arc, Mutex},
};
use libc::c_void;
use log::{debug, error, trace, warn};
use rutabaga_gfx::RutabagaFence;
use vhost::vhost_user::{
gpu_message::{
VhostUserGpuCursorPos, VhostUserGpuDMABUFScanout, VhostUserGpuDMABUFScanout2,
VhostUserGpuEdidRequest, VhostUserGpuUpdate,
},
GpuBackend,
message::VhostUserMMapFlags,
Backend, GpuBackend,
};
use vhost_user_backend::{VringRwLock, VringT};
use virglrenderer::{
FenceHandler, Iovec, VirglContext, VirglRenderer, VirglRendererFlags, VirglResource,
VIRGL_HANDLE_TYPE_MEM_DMABUF,
FenceHandler, Iovec, VirglRenderer, VirglRendererFlags, VirglResource,
VIRGL_HANDLE_TYPE_MEM_DMABUF, VIRGL_HANDLE_TYPE_MEM_OPAQUE_FD, VIRGL_MAP_CACHE_MASK,
};
use virtio_bindings::virtio_gpu::VIRTIO_GPU_BLOB_MEM_HOST3D;
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap, VolatileSlice};
use vmm_sys_util::eventfd::EventFd;
use crate::{
backend::{
common,
common::{common_set_scanout_disable, AssociatedScanouts, CursorConfig, VirtioGpuScanout},
common::{
common_map_blob, common_set_scanout_disable, common_unmap_blob, AssociatedScanouts,
CursorConfig, VirtioGpuScanout,
},
},
gpu_types::{FenceState, ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
gpu_types::{FenceState, ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing},
protocol::{
virtio_gpu_rect, GpuResponse,
GpuResponse::{
ErrInvalidContextId, ErrInvalidParameter, ErrInvalidResourceId, ErrInvalidScanoutId,
ErrUnspec, OkCapset, OkCapsetInfo, OkNoData,
ErrUnspec, OkCapset, OkCapsetInfo, OkMapInfo, OkNoData,
},
VirtioGpuResult, VIRTIO_GPU_MAX_SCANOUTS,
},
renderer::Renderer,
GpuConfig,
GpuCapset, GpuConfig,
};
const CAPSET_ID_VIRGL: u32 = 1;
const CAPSET_ID_VIRGL2: u32 = 2;
const CAPSET_ID_VENUS: u32 = 4;
#[derive(Clone)]
pub struct GpuResource {
pub virgl_resource: VirglResource,
@ -58,6 +59,8 @@ pub struct GpuResource {
// resource. Resource could be used for multiple scanouts.
pub scanouts: AssociatedScanouts,
pub backing_iovecs: Arc<Mutex<Option<Vec<Iovec>>>>,
pub blob_size: u64,
pub blob_shmem_offset: Option<u64>,
}
fn sglist_to_iovecs(
@ -134,18 +137,30 @@ impl FenceHandler for VirglFenceHandler {
pub struct VirglRendererAdapter {
renderer: VirglRenderer,
capsets: GpuCapset,
#[allow(dead_code)]
backend: Backend,
gpu_backend: GpuBackend,
fence_state: Arc<Mutex<FenceState>>,
resources: BTreeMap<u32, GpuResource>,
contexts: BTreeMap<u32, VirglContext>,
scanouts: [Option<VirtioGpuScanout>; VIRTIO_GPU_MAX_SCANOUTS as usize],
}
impl VirglRendererAdapter {
pub fn new(queue_ctl: &VringRwLock, config: &GpuConfig, gpu_backend: GpuBackend) -> Self {
pub fn new(
queue_ctl: &VringRwLock,
backend: Backend,
config: &GpuConfig,
gpu_backend: GpuBackend,
) -> Self {
let capsets = config.capsets();
let venus_enabled = capsets.contains(GpuCapset::VENUS);
let virgl_enabled = !(capsets & (GpuCapset::VIRGL | GpuCapset::VIRGL2)).is_empty();
let virglrenderer_flags = VirglRendererFlags::new()
.use_virgl(true)
.use_venus(true)
.use_virgl(virgl_enabled)
.use_venus(venus_enabled)
.use_render_server(venus_enabled)
.use_egl(config.flags().use_egl)
.use_gles(config.flags().use_gles)
.use_glx(config.flags().use_glx)
@ -162,11 +177,12 @@ impl VirglRendererAdapter {
let renderer = VirglRenderer::init(virglrenderer_flags, fence_handler, None)
.expect("Failed to initialize virglrenderer");
Self {
capsets,
renderer,
backend,
gpu_backend,
fence_state,
resources: BTreeMap::new(),
contexts: BTreeMap::new(),
scanouts: Default::default(),
}
}
@ -184,6 +200,8 @@ impl Renderer for VirglRendererAdapter {
virgl_resource,
scanouts: AssociatedScanouts::default(),
backing_iovecs: Arc::new(Mutex::new(None)),
blob_size: 0,
blob_shmem_offset: None,
};
self.resources.insert(resource_id, local_resource);
Ok(OkNoData)
@ -320,13 +338,14 @@ impl Renderer for VirglRendererAdapter {
}
fn get_capset_info(&self, index: u32) -> VirtioGpuResult {
debug!("the capset index is {index}");
let capset_id = match index {
0 => CAPSET_ID_VIRGL,
1 => CAPSET_ID_VIRGL2,
3 => CAPSET_ID_VENUS,
_ => return Err(ErrInvalidParameter),
};
debug!("Looking up capset at index {index}");
let capset_id = self
.capsets
.iter()
.nth(index as usize)
.ok_or(ErrInvalidParameter)?
.bits() as u32;
let (version, size) = self.renderer.get_capset_info(index);
Ok(OkCapsetInfo {
capset_id,
@ -346,41 +365,34 @@ impl Renderer for VirglRendererAdapter {
context_init: u32,
context_name: Option<&str>,
) -> VirtioGpuResult {
if self.contexts.contains_key(&ctx_id) {
return Err(ErrUnspec);
}
trace!("Creating context ctx_id={ctx_id}, '{context_name:?}', context_init={context_init}");
// Create the VirglContext using virglrenderer
let ctx = virglrenderer::VirglContext::create_context(ctx_id, context_init, context_name)
// Create the context using virglrenderer (contexts are now managed internally)
self.renderer
.create_context(ctx_id, context_init, context_name)
.map_err(|_| ErrInvalidContextId)?;
// Insert the newly created context into our local BTreeMap.
self.contexts.insert(ctx_id, ctx);
Ok(OkNoData)
}
fn destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult {
self.contexts.remove(&ctx_id).ok_or(ErrInvalidContextId)?;
self.renderer.destroy_context(ctx_id);
Ok(OkNoData)
}
fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
let ctx = self.contexts.get_mut(&ctx_id).ok_or(ErrInvalidContextId)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(ErrInvalidResourceId)?;
ctx.attach(&mut resource.virgl_resource);
if !self.resources.contains_key(&resource_id) {
return Err(ErrInvalidResourceId);
}
self.renderer.ctx_attach_resource(ctx_id, resource_id);
Ok(OkNoData)
}
fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
let ctx = self.contexts.get_mut(&ctx_id).ok_or(ErrInvalidContextId)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(ErrInvalidResourceId)?;
ctx.detach(&resource.virgl_resource);
if !self.resources.contains_key(&resource_id) {
return Err(ErrInvalidResourceId);
}
self.renderer.ctx_detach_resource(ctx_id, resource_id);
Ok(OkNoData)
}
@ -390,9 +402,8 @@ impl Renderer for VirglRendererAdapter {
commands: &mut [u8],
fence_ids: &[u64],
) -> VirtioGpuResult {
let ctx = self.contexts.get_mut(&ctx_id).ok_or(ErrInvalidContextId)?;
ctx.submit_cmd(commands, fence_ids)
self.renderer
.submit_cmd(ctx_id, commands, fence_ids)
.map(|()| OkNoData)
.map_err(|_| ErrUnspec)
}
@ -612,25 +623,112 @@ impl Renderer for VirglRendererAdapter {
fn resource_create_blob(
&mut self,
_ctx_id: u32,
_resource_id: u32,
_blob_id: u64,
_size: u64,
_blob_mem: u32,
_blob_flags: u32,
ctx_id: u32,
resource_create_blob: ResourceCreateBlob,
vecs: Vec<(GuestAddress, usize)>,
mem: &GuestMemoryMmap,
) -> VirtioGpuResult {
error!("Not implemented: resource_create_blob");
Err(ErrUnspec)
let mut virgl_iovecs = None;
if resource_create_blob.blob_flags
& crate::protocol::VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE
!= 0
{
error!("GUEST_HANDLE unimplemented for virgl backend");
return Err(ErrUnspec);
} else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D {
virgl_iovecs = Some(sglist_to_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?);
}
let virgl_resource = self
.renderer
.create_blob(
ctx_id,
0, // width
0, // height
resource_create_blob.resource_id,
resource_create_blob.into(),
virgl_iovecs.as_deref(),
)
.map_err(|_| ErrUnspec)?;
let resource = GpuResource {
virgl_resource,
scanouts: AssociatedScanouts::default(),
backing_iovecs: Arc::new(Mutex::new(virgl_iovecs)),
blob_size: resource_create_blob.size,
blob_shmem_offset: None,
};
debug_assert!(
!self
.resources
.contains_key(&resource_create_blob.resource_id),
"Resource ID {} already exists in the resources map.",
resource_create_blob.resource_id
);
self.resources
.insert(resource_create_blob.resource_id, resource);
Ok(OkNoData)
}
fn resource_map_blob(&mut self, _resource_id: u32, _offset: u64) -> VirtioGpuResult {
error!("Not implemented: resource_map_blob");
Err(ErrUnspec)
fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult {
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(ErrInvalidResourceId)?;
let map_info = resource.virgl_resource.map_info.ok_or(ErrUnspec)?;
let handle = resource.virgl_resource.handle.as_ref().ok_or(ErrUnspec)?;
// Check handle type - we don't support OPAQUE_FD mapping
if handle.handle_type == VIRGL_HANDLE_TYPE_MEM_OPAQUE_FD {
error!("VIRGL_HANDLE_TYPE_MEM_OPAQUE_FD not supported for mapping");
return Err(ErrUnspec);
}
// virgl doesn't provide detailed permissions for mapping, map everything as
// writable
let flags = VhostUserMMapFlags::WRITABLE;
common_map_blob(
&self.backend,
flags,
&handle.os_handle.as_fd(),
resource.blob_size,
offset,
resource_id,
)?;
resource.blob_shmem_offset = Some(offset);
// Return cache flags only (access flags not part of virtio-gpu spec)
Ok(OkMapInfo {
map_info: map_info & VIRGL_MAP_CACHE_MASK,
})
}
fn resource_unmap_blob(&mut self, _resource_id: u32) -> VirtioGpuResult {
error!("Not implemented: resource_unmap_blob");
Err(ErrUnspec)
fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult {
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(ErrInvalidResourceId)?;
let Some(offset) = resource.blob_shmem_offset else {
warn!(
"Guest tried to unmap blob resource with resource_id={resource_id}, but it is not \
mapped!"
);
return Err(ErrInvalidParameter);
};
common_unmap_blob(&self.backend, resource.blob_size, offset)?;
resource.blob_shmem_offset = None;
Ok(OkNoData)
}
}
@ -648,7 +746,10 @@ mod virgl_cov_tests {
use super::*;
use crate::{
gpu_types::{FenceDescriptor, FenceState, ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
gpu_types::{
FenceDescriptor, FenceState, ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc,
VirtioGpuRing,
},
protocol::{virtio_gpu_rect, GpuResponse, VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM},
renderer::Renderer,
testutils::{
@ -672,6 +773,11 @@ mod virgl_cov_tests {
GpuBackend::from_stream(backend)
}
fn dummy_backend() -> Backend {
let (_, backend) = UnixStream::pair().unwrap();
Backend::from_stream(backend)
}
#[test]
fn sglist_to_iovecs_err_on_invalid_slice() {
// Single region: 0x1000..0x2000 (4 KiB)
@ -744,10 +850,12 @@ mod virgl_cov_tests {
}
assert!(call_b.read().is_err(), "no signal when no match");
let capsets = GpuCapset::VIRGL | GpuCapset::VIRGL2;
// Initialize virgl ONCE in this forked process; exercise adapter paths
let cfg = GpuConfig::new(
GpuMode::VirglRenderer,
Some(GpuCapset::VIRGL | GpuCapset::VIRGL2),
Some(capsets),
GpuFlags::default(),
).expect("GpuConfig");
@ -757,8 +865,9 @@ mod virgl_cov_tests {
let (vring, _outs, _call_evt) =
create_vring(&mem, &[] as &[TestingDescChainArgs], GuestAddress(0x2000), GuestAddress(0x4000), 64);
let backend = dummy_gpu_backend();
let mut gpu = VirglRendererAdapter::new(&vring, &cfg, backend);
let backend = dummy_backend();
let gpu_backend = dummy_gpu_backend();
let mut gpu = VirglRendererAdapter::new(&vring, backend, &cfg, gpu_backend);
gpu.event_poll();
let edid_req = VhostUserGpuEdidRequest {
@ -862,22 +971,34 @@ mod virgl_cov_tests {
assert_matches!(gpu.flush_resource(0, dirty), Ok(GpuResponse::OkNoData));
// Test capset queries
for index in [0, 1, 3] {
for index in 0..capsets.num_capsets() {
test_capset_operations(&gpu, index);
}
// Test blob resource functions (all should return ErrUnspec - not implemented)
// Test blob resource functions
assert_matches!(
gpu.resource_create_blob(1, 100, 0, 4096, 0, 0),
gpu.resource_create_blob(
1,
ResourceCreateBlob {
resource_id: 100,
blob_id: 0,
blob_mem: 0,
blob_flags: 0,
size: 4096,
},
vec![],
&gm_back
),
Err(GpuResponse::ErrUnspec)
);
// resource 100 was never created, so these return ErrInvalidResourceId
assert_matches!(
gpu.resource_map_blob(100, 0),
Err(GpuResponse::ErrUnspec)
Err(GpuResponse::ErrInvalidResourceId)
);
assert_matches!(
gpu.resource_unmap_blob(100),
Err(GpuResponse::ErrUnspec)
Err(GpuResponse::ErrInvalidResourceId)
);
// Test resource_assign_uuid (not implemented)

View File

@ -22,9 +22,10 @@ macro_rules! handle_adapter {
Some(renderer) => renderer,
None => {
// Pass $vrings to the call
let (control_vring, gpu_backend) = $self.extract_backend_and_vring($vrings)?;
let (control_vring, backend, gpu_backend) =
$self.extract_backend_and_vring($vrings)?;
let renderer = $new_adapter(control_vring, gpu_backend);
let renderer = $new_adapter(control_vring, backend, gpu_backend);
event_poll_fd = renderer.get_event_poll_fd();
maybe_renderer.insert(renderer)
@ -52,7 +53,7 @@ use thiserror::Error as ThisError;
use vhost::vhost_user::{
gpu_message::{VhostUserGpuCursorPos, VhostUserGpuEdidRequest},
message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures},
GpuBackend,
Backend, GpuBackend,
};
use vhost_user_backend::{VhostUserBackend, VringEpollHandler, VringRwLock, VringT};
use virtio_bindings::{
@ -79,7 +80,7 @@ use crate::backend::gfxstream::GfxstreamAdapter;
use crate::backend::virgl::VirglRendererAdapter;
use crate::{
backend::null::NullAdapter,
gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
gpu_types::{ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing},
protocol::{
virtio_gpu_ctrl_hdr, virtio_gpu_ctx_create, virtio_gpu_get_edid,
virtio_gpu_resource_create_2d, virtio_gpu_resource_create_3d, virtio_gpu_transfer_host_3d,
@ -140,6 +141,7 @@ impl From<Error> for io::Error {
struct VhostUserGpuBackendInner {
virtio_cfg: VirtioGpuConfig,
event_idx_enabled: bool,
backend: Option<Backend>,
gpu_backend: Option<GpuBackend>,
exit_consumer: EventConsumer,
exit_notifier: EventNotifier,
@ -173,6 +175,7 @@ impl VhostUserGpuBackend {
num_capsets: Le32::from(gpu_config.capsets().num_capsets()),
},
event_idx_enabled: false,
backend: None,
gpu_backend: None,
exit_consumer,
exit_notifier,
@ -257,18 +260,27 @@ impl VhostUserGpuBackendInner {
fence_ids,
mut cmd_data,
} => renderer.submit_command(hdr.ctx_id.into(), &mut cmd_data, &fence_ids),
GpuCommand::ResourceCreateBlob(_) => {
panic!("virtio_gpu: GpuCommand::ResourceCreateBlob unimplemented")
}
GpuCommand::ResourceCreateBlob(info, vecs) => renderer.resource_create_blob(
hdr.ctx_id.into(),
ResourceCreateBlob {
resource_id: info.resource_id.into(),
blob_id: info.blob_id.into(),
blob_mem: info.blob_mem.into(),
blob_flags: info.blob_flags.into(),
size: info.size.into(),
},
vecs,
mem,
),
GpuCommand::SetScanoutBlob(_) => {
panic!("virtio_gpu: GpuCommand::SetScanoutBlob unimplemented")
}
GpuCommand::ResourceMapBlob(_) => {
panic!("virtio_gpu: GpuCommand::ResourceMapBlob unimplemented")
GpuCommand::ResourceMapBlob(info) => {
renderer.resource_map_blob(info.resource_id.into(), info.offset.into())
}
GpuCommand::ResourceUnmapBlob(_) => {
panic!("virtio_gpu: GpuCommand::ResourceUnmapBlob unimplemented")
GpuCommand::ResourceUnmapBlob(info) => {
renderer.resource_unmap_blob(info.resource_id.into())
}
}
}
@ -347,11 +359,11 @@ impl VhostUserGpuBackendInner {
hdr: virtio_gpu_ctrl_hdr,
req: virtio_gpu_ctx_create,
) -> VirtioGpuResult {
let context_name: Option<String> = Some(req.get_debug_name());
let context_name = req.get_debug_name();
renderer.create_context(
hdr.ctx_id.into(),
req.context_init.into(),
context_name.as_deref(),
Some(&context_name),
)
}
@ -592,13 +604,17 @@ impl VhostUserGpuBackendInner {
fn extract_backend_and_vring<'a>(
&mut self,
vrings: &'a [VringRwLock],
) -> IoResult<(&'a VringRwLock, GpuBackend)> {
) -> IoResult<(&'a VringRwLock, Backend, GpuBackend)> {
let control_vring = &vrings[CONTROL_QUEUE as usize];
let backend = self
.backend
.take()
.ok_or_else(|| io::Error::other("set_backend_req_fd() not called, Backend missing"))?;
let gpu_backend = self
.gpu_backend
.take()
.ok_or_else(|| io::Error::other("set_gpu_socket() not called, GpuBackend missing"))?;
Ok((control_vring, backend))
Ok((control_vring, backend, gpu_backend))
}
fn lazy_init_and_handle_event(
@ -618,8 +634,8 @@ impl VhostUserGpuBackendInner {
GpuMode::Gfxstream => handle_adapter!(
GfxstreamAdapter,
TLS_GFXSTREAM,
|control_vring, gpu_backend| {
GfxstreamAdapter::new(control_vring, &self.gpu_config, gpu_backend)
|control_vring, backend, gpu_backend| {
GfxstreamAdapter::new(control_vring, backend, &self.gpu_config, gpu_backend)
},
self,
device_event,
@ -630,8 +646,8 @@ impl VhostUserGpuBackendInner {
GpuMode::VirglRenderer => handle_adapter!(
VirglRendererAdapter,
TLS_VIRGL,
|control_vring, gpu_backend| {
VirglRendererAdapter::new(control_vring, &self.gpu_config, gpu_backend)
|control_vring, backend, gpu_backend| {
VirglRendererAdapter::new(control_vring, backend, &self.gpu_config, gpu_backend)
},
self,
device_event,
@ -641,8 +657,8 @@ impl VhostUserGpuBackendInner {
GpuMode::Null => handle_adapter!(
NullAdapter,
TLS_NULL,
|control_vring, gpu_backend| {
NullAdapter::new(control_vring, &self.gpu_config, gpu_backend)
|control_vring, backend, gpu_backend| {
NullAdapter::new(control_vring, &self.gpu_config, backend, gpu_backend)
},
self,
device_event,
@ -695,7 +711,11 @@ impl VhostUserBackend for VhostUserGpuBackend {
fn protocol_features(&self) -> VhostUserProtocolFeatures {
debug!("Protocol features called");
VhostUserProtocolFeatures::CONFIG | VhostUserProtocolFeatures::MQ
VhostUserProtocolFeatures::CONFIG
| VhostUserProtocolFeatures::MQ
| VhostUserProtocolFeatures::BACKEND_REQ
| VhostUserProtocolFeatures::BACKEND_SEND_FD
| VhostUserProtocolFeatures::SHMEM
}
fn set_event_idx(&self, enabled: bool) {
@ -709,6 +729,11 @@ impl VhostUserBackend for VhostUserGpuBackend {
Ok(())
}
fn set_backend_req_fd(&self, backend: Backend) {
trace!("Got set_backend_req_fd");
self.inner.lock().unwrap().backend = Some(backend);
}
fn set_gpu_socket(&self, backend: GpuBackend) -> IoResult<()> {
self.inner.lock().unwrap().gpu_backend = Some(backend);
Ok(())
@ -787,7 +812,7 @@ mod tests {
use super::*;
use crate::{
gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
gpu_types::{ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing},
protocol::{
virtio_gpu_ctrl_hdr, virtio_gpu_ctx_create, virtio_gpu_ctx_destroy,
virtio_gpu_ctx_resource, virtio_gpu_get_capset, virtio_gpu_get_capset_info,
@ -821,11 +846,9 @@ mod tests {
fn resource_create_blob(
&mut self,
ctx_id: u32,
resource_id: u32,
blob_id: u64,
size: u64,
blob_mem: u32,
blob_flags: u32,
resource_create_blob: ResourceCreateBlob,
vecs: Vec<(GuestAddress, usize)>,
mem: &GuestMemoryMmap,
) -> VirtioGpuResult;
fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult;
fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult;
@ -917,6 +940,11 @@ mod tests {
(frontend, backend)
}
fn dummy_backend_request_socket() -> Backend {
let (_frontend, backend) = UnixStream::pair().unwrap();
Backend::from_stream(backend)
}
#[test]
fn test_process_gpu_command() {
let (_, mem) = init();
@ -1398,7 +1426,11 @@ mod tests {
assert_eq!(backend.features(), 0x0101_7100_001B);
assert_eq!(
backend.protocol_features(),
VhostUserProtocolFeatures::CONFIG | VhostUserProtocolFeatures::MQ
VhostUserProtocolFeatures::CONFIG
| VhostUserProtocolFeatures::MQ
| VhostUserProtocolFeatures::BACKEND_REQ
| VhostUserProtocolFeatures::BACKEND_SEND_FD
| VhostUserProtocolFeatures::SHMEM
);
assert_eq!(backend.queues_per_thread(), vec![0xffff_ffff]);
assert_eq!(backend.get_config(0, 0), Vec::<u8>::new());
@ -1420,7 +1452,7 @@ mod tests {
let vring = VringRwLock::new(mem, 0x1000).unwrap();
vring.set_queue_info(0x100, 0x200, 0x300).unwrap();
vring.set_queue_ready(true);
backend.set_backend_req_fd(dummy_backend_request_socket());
assert_eq!(
backend
.handle_event(0, EventSet::OUT, &[vring.clone()], 0)
@ -1439,6 +1471,7 @@ mod tests {
// Hit the loop part
backend.set_event_idx(true);
backend.set_backend_req_fd(dummy_backend_request_socket());
backend
.handle_event(0, EventSet::IN, &[vring.clone()], 0)
.unwrap();
@ -1541,6 +1574,7 @@ mod tests {
.unwrap();
backend.set_gpu_socket(gpu_backend).unwrap();
backend.set_backend_req_fd(dummy_backend_request_socket());
// Unfortunately, there is no way to create a VringEpollHandler directly (the ::new is not public)
// So we create a daemon to create the epoll handler for us here

View File

@ -48,6 +48,22 @@ macro_rules! impl_from_resource_create3d {
};
}
#[cfg(any(feature = "backend-virgl", feature = "backend-gfxstream"))]
macro_rules! impl_from_resource_create_blob {
($target:ty) => {
impl From<ResourceCreateBlob> for $target {
fn from(r: ResourceCreateBlob) -> Self {
Self {
blob_id: r.blob_id,
blob_mem: r.blob_mem,
blob_flags: r.blob_flags,
size: r.size,
}
}
}
};
}
use std::{collections::BTreeMap, os::raw::c_void};
#[cfg(feature = "backend-gfxstream")]
@ -147,6 +163,21 @@ impl_from_resource_create3d!(rutabaga_gfx::ResourceCreate3D);
#[cfg(feature = "backend-virgl")]
impl_from_resource_create3d!(virglrenderer::ResourceCreate3D);
#[cfg(feature = "backend-gfxstream")]
impl_from_resource_create_blob!(rutabaga_gfx::ResourceCreateBlob);
#[cfg(feature = "backend-virgl")]
impl_from_resource_create_blob!(virglrenderer::ResourceCreateBlob);
/// Parameters for creating a blob resource.
#[derive(Debug, Clone, Copy)]
pub struct ResourceCreateBlob {
pub resource_id: u32,
pub blob_id: u64,
pub blob_mem: u32,
pub blob_flags: u32,
pub size: u64,
}
#[derive(Debug, Clone, Copy)]
pub struct ResourceCreate2d {
pub resource_id: u32,

View File

@ -30,7 +30,7 @@ use log::info;
#[cfg(feature = "backend-gfxstream")]
use rutabaga_gfx::{RUTABAGA_CAPSET_GFXSTREAM_GLES, RUTABAGA_CAPSET_GFXSTREAM_VULKAN};
#[cfg(feature = "backend-virgl")]
use rutabaga_gfx::{RUTABAGA_CAPSET_VIRGL, RUTABAGA_CAPSET_VIRGL2};
use rutabaga_gfx::{RUTABAGA_CAPSET_VENUS, RUTABAGA_CAPSET_VIRGL, RUTABAGA_CAPSET_VIRGL2};
use thiserror::Error as ThisError;
use vhost_user_backend::VhostUserDaemon;
use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
@ -68,7 +68,9 @@ bitflags! {
#[cfg(feature = "backend-virgl")]
const VIRGL2 = 1 << RUTABAGA_CAPSET_VIRGL2 as u64;
#[cfg(feature = "backend-virgl")]
const ALL_VIRGLRENDERER_CAPSETS = Self::VIRGL.bits() | Self::VIRGL2.bits();
const VENUS = 1 << RUTABAGA_CAPSET_VENUS as u64;
#[cfg(feature = "backend-virgl")]
const ALL_VIRGLRENDERER_CAPSETS = Self::VIRGL.bits() | Self::VIRGL2.bits() | Self::VENUS.bits();
#[cfg(feature = "backend-gfxstream")]
const GFXSTREAM_VULKAN = 1 << RUTABAGA_CAPSET_GFXSTREAM_VULKAN as u64;
@ -98,6 +100,8 @@ impl Display for GpuCapset {
Self::VIRGL => write!(f, "virgl")?,
#[cfg(feature = "backend-virgl")]
Self::VIRGL2 => write!(f, "virgl2")?,
#[cfg(feature = "backend-virgl")]
Self::VENUS => write!(f, "venus")?,
#[cfg(feature = "backend-gfxstream")]
Self::GFXSTREAM_VULKAN => write!(f, "gfxstream-vulkan")?,
#[cfg(feature = "backend-gfxstream")]
@ -334,7 +338,7 @@ mod tests {
#[test]
fn test_default_num_capsets() {
#[cfg(feature = "backend-virgl")]
assert_eq!(GpuConfig::DEFAULT_VIRGLRENDER_CAPSET_MASK.num_capsets(), 2);
assert_eq!(GpuConfig::DEFAULT_VIRGLRENDER_CAPSET_MASK.num_capsets(), 3);
#[cfg(feature = "backend-gfxstream")]
assert_eq!(GpuConfig::DEFAULT_GFXSTREAM_CAPSET_MASK.num_capsets(), 2);
}

View File

@ -24,6 +24,10 @@ pub enum CapsetName {
#[cfg(feature = "backend-virgl")]
Virgl2 = GpuCapset::VIRGL2.bits(),
/// [virglrenderer] Venus (Vulkan) implementation
#[cfg(feature = "backend-virgl")]
Venus = GpuCapset::VENUS.bits(),
/// [gfxstream] Vulkan implementation (partial support only){n}
/// NOTE: Can only be used for 2D display output for now, there is no
/// hardware acceleration yet

View File

@ -6,9 +6,9 @@
#![allow(non_camel_case_types)]
use std::{
borrow::Cow,
cmp::min,
convert::From,
ffi::CStr,
fmt::{self, Display},
io::{self, Read, Write},
marker::PhantomData,
@ -441,14 +441,9 @@ impl Default for virtio_gpu_ctx_create {
}
impl virtio_gpu_ctx_create {
pub fn get_debug_name(&self) -> String {
CStr::from_bytes_with_nul(
&self.debug_name[..min(64, <Le32 as Into<u32>>::into(self.nlen) as usize)],
)
.map_or_else(
|err| format!("Err({err})"),
|c_str| c_str.to_string_lossy().into_owned(),
)
pub fn get_debug_name(&self) -> Cow<'_, str> {
let len = min(64, <Le32 as Into<u32>>::into(self.nlen) as usize);
String::from_utf8_lossy(&self.debug_name[..len])
}
}
impl fmt::Debug for virtio_gpu_ctx_create {
@ -707,7 +702,7 @@ pub enum GpuCommand {
cmd_data: Vec<u8>,
fence_ids: Vec<u64>,
},
ResourceCreateBlob(virtio_gpu_resource_create_blob),
ResourceCreateBlob(virtio_gpu_resource_create_blob, Vec<(GuestAddress, usize)>),
ResourceMapBlob(virtio_gpu_resource_map_blob),
ResourceUnmapBlob(virtio_gpu_resource_unmap_blob),
UpdateCursor(virtio_gpu_update_cursor),
@ -753,6 +748,23 @@ impl fmt::Debug for GpuCommand {
}
}
fn read_mem_entries(
reader: &mut Reader,
num_entries: u32,
) -> Result<Vec<(GuestAddress, usize)>, GpuCommandDecodeError> {
let mut entries = Vec::with_capacity(num_entries as usize);
for _ in 0..num_entries {
let entry: virtio_gpu_mem_entry =
reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?;
entries.push((
GuestAddress(entry.addr.into()),
entry.length.to_native() as usize,
))
}
Ok(entries)
}
impl GpuCommand {
pub const fn command_name(&self) -> &'static str {
use GpuCommand::*;
@ -777,7 +789,7 @@ impl GpuCommand {
TransferToHost3d(_info) => "TransferToHost3d",
TransferFromHost3d(_info) => "TransferFromHost3d",
CmdSubmit3d { .. } => "CmdSubmit3d",
ResourceCreateBlob(_info) => "ResourceCreateBlob",
ResourceCreateBlob(_info, _) => "ResourceCreateBlob",
ResourceMapBlob(_info) => "ResourceMapBlob",
ResourceUnmapBlob(_info) => "ResourceUnmapBlob",
UpdateCursor(_info) => "UpdateCursor",
@ -824,16 +836,7 @@ impl GpuCommand {
VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => {
let info: virtio_gpu_resource_attach_backing =
reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?;
let mut entries =
Vec::with_capacity(<Le32 as Into<u32>>::into(info.nr_entries) as usize);
for _ in 0..info.nr_entries.into() {
let entry: virtio_gpu_mem_entry =
reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?;
entries.push((
GuestAddress(entry.addr.into()),
<Le32 as Into<u32>>::into(entry.length) as usize,
));
}
let entries = read_mem_entries(reader, info.nr_entries.into())?;
ResourceAttachBacking(info, entries)
}
VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => {
@ -891,7 +894,11 @@ impl GpuCommand {
}
}
VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB => {
ResourceCreateBlob(reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?)
let info: virtio_gpu_resource_create_blob =
reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?;
let entries = read_mem_entries(reader, info.nr_entries.into())?;
ResourceCreateBlob(info, entries)
}
VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB => {
ResourceMapBlob(reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?)
@ -1311,7 +1318,7 @@ mod tests {
"CmdSubmit3d",
),
(
ResourceCreateBlob(virtio_gpu_resource_create_blob::default()),
ResourceCreateBlob(virtio_gpu_resource_create_blob::default(), Vec::new()),
"ResourceCreateBlob",
),
(
@ -1343,8 +1350,9 @@ mod tests {
#[test]
fn test_virtio_gpu_ctx_create_debug() {
let bytes = b"test_debug\0";
let original = virtio_gpu_ctx_create {
// Test without null terminator (typical case)
let bytes = b"test_debug";
let ctx = virtio_gpu_ctx_create {
debug_name: {
let mut debug_name = [0; 64];
debug_name[..bytes.len()].copy_from_slice(bytes);
@ -1353,12 +1361,26 @@ mod tests {
context_init: 0.into(),
nlen: (bytes.len() as u32).into(),
};
let debug_string = format!("{original:?}");
assert_eq!(
debug_string,
format!("{ctx:?}"),
"virtio_gpu_ctx_create { debug_name: \"test_debug\", context_init: Le32(0), .. }"
);
// Test with null terminator included in nlen (edge case - should preserve it)
let bytes_with_null = b"test_debug\0";
let ctx_with_null = virtio_gpu_ctx_create {
debug_name: {
let mut debug_name = [0; 64];
debug_name[..bytes_with_null.len()].copy_from_slice(bytes_with_null);
debug_name
},
context_init: 0.into(),
nlen: (bytes_with_null.len() as u32).into(),
};
assert_eq!(
format!("{ctx_with_null:?}"),
"virtio_gpu_ctx_create { debug_name: \"test_debug\\0\", context_init: Le32(0), .. }"
);
}
#[test]

View File

@ -8,7 +8,7 @@ use vm_memory::{GuestAddress, GuestMemoryMmap, VolatileSlice};
use vmm_sys_util::eventfd::EventFd;
use crate::{
gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
gpu_types::{ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing},
protocol::{virtio_gpu_rect, VirtioGpuResult},
};
@ -91,11 +91,9 @@ pub trait Renderer: Send + Sync {
fn resource_create_blob(
&mut self,
ctx_id: u32,
resource_id: u32,
blob_id: u64,
size: u64,
blob_mem: u32,
blob_flags: u32,
resource_create_blob: ResourceCreateBlob,
vecs: Vec<(vm_memory::GuestAddress, usize)>,
mem: &vm_memory::GuestMemoryMmap,
) -> VirtioGpuResult;
fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult;
fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult;