diff --git a/Cargo.lock b/Cargo.lock index 683d6aa3..8cd16140 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2176,6 +2176,7 @@ dependencies = [ "rutabaga_gfx", "tempfile", "thiserror 2.0.18", + "uuid", "vhost", "vhost-user-backend", "virglrenderer", diff --git a/vhost-device-gpu/CHANGELOG.md b/vhost-device-gpu/CHANGELOG.md index 38e3b98a..bb31fa93 100644 --- a/vhost-device-gpu/CHANGELOG.md +++ b/vhost-device-gpu/CHANGELOG.md @@ -3,6 +3,7 @@ ### Added +- [[#945]] (https://github.com/rust-vmm/vhost-device/pull/945) Implement RESOURCE_ASSIGN_UUID command - [[#932]] (https://github.com/rust-vmm/vhost-device/pull/932) vhost-device-gpu: Add support for GPU device path - [[#927]] (https://github.com/rust-vmm/vhost-device/pull/927) vhost-device-gpu: Introduce headless mode diff --git a/vhost-device-gpu/Cargo.toml b/vhost-device-gpu/Cargo.toml index 4ae7e7f7..534cec10 100644 --- a/vhost-device-gpu/Cargo.toml +++ b/vhost-device-gpu/Cargo.toml @@ -36,6 +36,7 @@ virtio-queue = "0.17.0" vm-memory = "0.17.1" vmm-sys-util = "0.15.0" bitflags = "2.11.0" +uuid = { version = "1.19", features = ["v4"] } [dev-dependencies] assert_matches = "1.5" diff --git a/vhost-device-gpu/src/backend/gfxstream.rs b/vhost-device-gpu/src/backend/gfxstream.rs index 95a18143..31082623 100644 --- a/vhost-device-gpu/src/backend/gfxstream.rs +++ b/vhost-device-gpu/src/backend/gfxstream.rs @@ -17,6 +17,7 @@ use rutabaga_gfx::{ ResourceCreate3D, Rutabaga, RutabagaBuilder, RutabagaComponentType, RutabagaFence, RutabagaFenceHandler, RutabagaHandle, RutabagaIntoRawDescriptor, RutabagaIovec, Transfer3D, }; +use uuid::Uuid; use vhost::vhost_user::{ gpu_message::{ VhostUserGpuCursorPos, VhostUserGpuEdidRequest, VhostUserGpuScanout, VhostUserGpuUpdate, @@ -51,7 +52,7 @@ use crate::{ const READ_RESOURCE_BYTES_PER_PIXEL: u32 = 4; // A local resource struct for the Gfxstream backend -#[derive(Default, Clone)] +#[derive(Clone)] pub struct GfxstreamResource { pub id: u32, pub width: u32, @@ -59,6 +60,7 @@ pub struct GfxstreamResource { scanouts: common::AssociatedScanouts, pub info_3d: Option, pub handle: Option>, + pub uuid: Uuid, } impl GfxstreamResource { @@ -85,6 +87,7 @@ impl GfxstreamResource { scanouts: AssociatedScanouts::default(), info_3d: None, handle: None, + uuid: Uuid::new_v4(), } } } @@ -285,6 +288,7 @@ impl Renderer for GfxstreamAdapter { scanouts: AssociatedScanouts::default(), info_3d: None, handle: None, + uuid: Uuid::new_v4(), }; debug_assert!( !self.resources.contains_key(&resource_id), @@ -425,9 +429,15 @@ impl Renderer for GfxstreamAdapter { }) } - fn resource_assign_uuid(&self, _resource_id: u32) -> VirtioGpuResult { - error!("Not implemented: resource_assign_uuid"); - Err(ErrUnspec) + fn resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult { + debug!("resource_assign_uuid for resource {}", resource_id); + let resource = self + .resources + .get(&resource_id) + .ok_or(ErrInvalidResourceId)?; + Ok(GpuResponse::OkResourceUuid { + uuid: *resource.uuid.as_bytes(), + }) } fn get_capset_info(&self, index: u32) -> VirtioGpuResult { @@ -1124,7 +1134,10 @@ mod gfx_fence_tests { id: 1, width: 64, height: 64, - ..Default::default() + scanouts: common::AssociatedScanouts::default(), + info_3d: None, + handle: None, + uuid: Uuid::new_v4(), }; // 64 * 64 * 4 BPP = 16384 assert_eq!( @@ -1140,7 +1153,10 @@ mod gfx_fence_tests { id: 1, width: u32::MAX, height: u32::MAX, - ..Default::default() + scanouts: common::AssociatedScanouts::default(), + info_3d: None, + handle: None, + uuid: Uuid::new_v4(), }; r.calculate_size().unwrap_err(); } @@ -1153,7 +1169,10 @@ mod gfx_fence_tests { id: 1, width: big as u32, height: 1, - ..Default::default() + scanouts: common::AssociatedScanouts::default(), + info_3d: None, + handle: None, + uuid: Uuid::new_v4(), }; // On 64-bit this should error; if it happens to fit on 32-bit, the guard still // holds elsewhere. diff --git a/vhost-device-gpu/src/backend/null.rs b/vhost-device-gpu/src/backend/null.rs index 178299b7..f7bbf938 100644 --- a/vhost-device-gpu/src/backend/null.rs +++ b/vhost-device-gpu/src/backend/null.rs @@ -118,8 +118,8 @@ impl Renderer for NullAdapter { } fn resource_assign_uuid(&self, _resource_id: u32) -> VirtioGpuResult { - trace!("NullAdapter::resource_assign_uuid - no-op"); - Ok(GpuResponse::OkNoData) + trace!("NullAdapter::resource_assign_uuid - returning zero UUID"); + Ok(GpuResponse::OkResourceUuid { uuid: [0u8; 16] }) } fn get_capset_info(&self, _capset_index: u32) -> VirtioGpuResult { @@ -452,9 +452,14 @@ mod tests { fn test_null_adapter_misc_operations() { let adapter = create_null_adapter(); - // Verify assigning UUID to resource succeeds + // Verify assigning UUID to resource succeeds with zero UUID let result = adapter.resource_assign_uuid(1); - assert!(matches!(result, Ok(GpuResponse::OkNoData))); + match result { + Ok(GpuResponse::OkResourceUuid { uuid }) => { + assert_eq!(uuid, [0u8; 16], "Null adapter should return zero UUID"); + } + _ => panic!("Expected OkResourceUuid"), + } // Verify no event poll fd is provided (null backend has no events) let event_fd = adapter.get_event_poll_fd(); diff --git a/vhost-device-gpu/src/backend/virgl.rs b/vhost-device-gpu/src/backend/virgl.rs index f9db92be..80c32bac 100644 --- a/vhost-device-gpu/src/backend/virgl.rs +++ b/vhost-device-gpu/src/backend/virgl.rs @@ -15,6 +15,7 @@ use libc::c_void; use log::{debug, error, trace, warn}; use rutabaga_gfx::RutabagaFence; use thiserror::Error as ThisError; +use uuid::Uuid; use vhost::vhost_user::{ gpu_message::{ VhostUserGpuCursorPos, VhostUserGpuDMABUFScanout, VhostUserGpuDMABUFScanout2, @@ -73,6 +74,7 @@ pub struct GpuResource { // resource. Resource could be used for multiple scanouts. pub scanouts: AssociatedScanouts, pub backing_iovecs: Arc>>>, + pub uuid: Uuid, } fn sglist_to_iovecs( @@ -211,6 +213,7 @@ impl Renderer for VirglRendererAdapter { virgl_resource, scanouts: AssociatedScanouts::default(), backing_iovecs: Arc::new(Mutex::new(None)), + uuid: Uuid::new_v4(), }; self.resources.insert(resource_id, local_resource); Ok(OkNoData) @@ -357,9 +360,15 @@ impl Renderer for VirglRendererAdapter { }) } - fn resource_assign_uuid(&self, _resource_id: u32) -> VirtioGpuResult { - error!("Not implemented: resource_assign_uuid"); - Err(ErrUnspec) + fn resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult { + debug!("resource_assign_uuid for resource {}", resource_id); + let resource = self + .resources + .get(&resource_id) + .ok_or(ErrInvalidResourceId)?; + Ok(GpuResponse::OkResourceUuid { + uuid: *resource.uuid.as_bytes(), + }) } fn get_capset_info(&self, index: u32) -> VirtioGpuResult { @@ -947,10 +956,10 @@ mod virgl_cov_tests { Err(GpuResponse::ErrUnspec) ); - // Test resource_assign_uuid (not implemented) + // Test resource_assign_uuid with invalid resource assert_matches!( - gpu.resource_assign_uuid(1), - Err(GpuResponse::ErrUnspec) + gpu.resource_assign_uuid(999), + Err(GpuResponse::ErrInvalidResourceId) ); // Test display_info (should fail without frontend) diff --git a/vhost-device-gpu/src/device.rs b/vhost-device-gpu/src/device.rs index ccd04eff..a7a47451 100644 --- a/vhost-device-gpu/src/device.rs +++ b/vhost-device-gpu/src/device.rs @@ -62,7 +62,7 @@ use virtio_bindings::{ }, virtio_gpu::{ VIRTIO_GPU_F_CONTEXT_INIT, VIRTIO_GPU_F_EDID, VIRTIO_GPU_F_RESOURCE_BLOB, - VIRTIO_GPU_F_VIRGL, + VIRTIO_GPU_F_RESOURCE_UUID, VIRTIO_GPU_F_VIRGL, }, }; use virtio_queue::{QueueOwnedT, Reader, Writer}; @@ -231,8 +231,8 @@ impl VhostUserGpuBackendInner { } GpuCommand::UpdateCursor(req) => Self::handle_update_cursor(renderer, req), GpuCommand::MoveCursor(req) => Self::handle_move_cursor(renderer, req), - GpuCommand::ResourceAssignUuid(_) => { - panic!("virtio_gpu: GpuCommand::ResourceAssignUuid unimplemented") + GpuCommand::ResourceAssignUuid(req) => { + renderer.resource_assign_uuid(req.resource_id.into()) } GpuCommand::GetCapsetInfo(req) => renderer.get_capset_info(req.capset_index.into()), GpuCommand::GetCapset(req) => { @@ -700,6 +700,7 @@ impl VhostUserBackend for VhostUserGpuBackend { | (1 << VIRTIO_RING_F_EVENT_IDX) | (1 << VIRTIO_GPU_F_VIRGL) | (1 << VIRTIO_GPU_F_RESOURCE_BLOB) + | (1 << VIRTIO_GPU_F_RESOURCE_UUID) | (1 << VIRTIO_GPU_F_CONTEXT_INIT) | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(); @@ -805,22 +806,24 @@ mod tests { use super::*; use crate::{ + backend::virgl::VirglRendererAdapter, gpu_types::{ResourceCreate3d, 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, - virtio_gpu_mem_entry, virtio_gpu_rect, virtio_gpu_resource_attach_backing, + virtio_gpu_mem_entry, virtio_gpu_rect, virtio_gpu_resource_assign_uuid, + virtio_gpu_resource_attach_backing, virtio_gpu_resource_create_2d, virtio_gpu_resource_detach_backing, virtio_gpu_resource_flush, virtio_gpu_resource_unref, virtio_gpu_set_scanout, - GpuResponse::{OkCapset, OkCapsetInfo, OkDisplayInfo, OkEdid, OkNoData}, - VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_CREATE, - VIRTIO_GPU_CMD_CTX_DESTROY, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE, - VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, - VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, - VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D, - VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, - VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM, VIRTIO_GPU_RESP_ERR_UNSPEC, - VIRTIO_GPU_RESP_OK_NODATA, + GpuResponse::{self, OkCapset, OkCapsetInfo, OkDisplayInfo, OkEdid, OkNoData}, + VIRTIO_GPU_BIND_RENDER_TARGET, VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, + VIRTIO_GPU_CMD_CTX_CREATE, VIRTIO_GPU_CMD_CTX_DESTROY, + VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM, + VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_GPU_TEXTURE_2D, }, renderer::Renderer, testutils::{create_vring, TestingDescChainArgs}, @@ -1114,6 +1117,16 @@ mod tests { .return_once(|_, _, _| Ok(OkNoData)); }); assert_matches!(result, Ok(OkNoData)); + + let cmd = GpuCommand::ResourceAssignUuid(virtio_gpu_resource_assign_uuid::default()); + let result = test_cmd(cmd, |g| { + g.expect_resource_assign_uuid().return_once(|_| { + Ok(GpuResponse::OkResourceUuid { + uuid: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + }) + }); + }); + assert_matches!(result, Ok(GpuResponse::OkResourceUuid { .. })); } fn create_control_vring( @@ -1421,6 +1434,43 @@ mod tests { assert_eq!(backend.features() & (1 << VIRTIO_GPU_F_EDID), 0); } + #[cfg(feature = "backend-virgl")] + #[test] + fn test_resource_uuid_assignment() { + let (_, mem) = init(); + let config = GpuConfigBuilder::default() + .set_gpu_mode(GpuMode::VirglRenderer) + .set_capset(GpuCapset::VIRGL) + .set_flags(GpuFlags::default()) + .build() + .unwrap(); + + let (control_vring, _, _) = create_control_vring(&mem, &[]); + let mut adapter = VirglRendererAdapter::new(&control_vring, &config, None).unwrap(); + + let resource_id = 42; + let create_req = ResourceCreate3d { + target: VIRTIO_GPU_TEXTURE_2D, + format: VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM, + bind: VIRTIO_GPU_BIND_RENDER_TARGET, + width: 1024, + height: 768, + depth: 1, + array_size: 0, + last_level: 0, + nr_samples: 0, + flags: 0, + }; + adapter.resource_create_3d(resource_id, create_req).unwrap(); + + let uuid = match adapter.resource_assign_uuid(resource_id).unwrap() { + GpuResponse::OkResourceUuid { uuid } => uuid, + other => panic!("Expected OkResourceUuid, got {:?}", other), + }; + + assert_ne!(uuid, [0u8; 16], "UUID should not be all zeros"); + } + rusty_fork_test! { #[test] fn test_verify_backend() { @@ -1433,7 +1483,7 @@ mod tests { assert_eq!(backend.num_queues(), NUM_QUEUES); assert_eq!(backend.max_queue_size(), QUEUE_SIZE); - assert_eq!(backend.features(), 0x0101_7100_001B); + assert_eq!(backend.features(), 0x0101_7100_001F); assert_eq!( backend.protocol_features(), VhostUserProtocolFeatures::CONFIG | VhostUserProtocolFeatures::MQ