diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs index a8b26035a0..6a81bf3ce5 100644 --- a/devices/src/virtio/wl.rs +++ b/devices/src/virtio/wl.rs @@ -59,7 +59,7 @@ use data_model::VolatileMemoryError; use sys_util::{Error, Result, EventFd, Scm, SharedMemory, GuestAddress, GuestMemory, GuestMemoryError, PollContext, PollToken, FileFlags, pipe}; -use vm_control::{VmControlError, VmRequest, VmResponse, MaybeOwnedFd}; +use vm_control::{VmControlError, VmRequest, VmResponse, MaybeOwnedFd, GpuMemoryDesc}; use super::{VirtioDevice, Queue, DescriptorChain, INTERRUPT_STATUS_USED_RING, TYPE_WL}; const VIRTWL_SEND_MAX_ALLOCS: usize = 28; @@ -236,7 +236,7 @@ fn encode_vfd_new_dmabuf(desc_mem: VolatileSlice, flags: u32, pfn: u64, size: u32, - stride: u32) + desc: GpuMemoryDesc) -> WlResult { let ctrl_vfd_new_dmabuf = CtrlVfdNewDmabuf { hdr: CtrlHeader { @@ -250,12 +250,12 @@ fn encode_vfd_new_dmabuf(desc_mem: VolatileSlice, width: Le32::from(0), height: Le32::from(0), format: Le32::from(0), - stride0: Le32::from(stride), - stride1: Le32::from(0), - stride2: Le32::from(0), - offset0: Le32::from(0), - offset1: Le32::from(0), - offset2: Le32::from(0), + stride0: Le32::from(desc.planes[0].stride), + stride1: Le32::from(desc.planes[1].stride), + stride2: Le32::from(desc.planes[2].stride), + offset0: Le32::from(desc.planes[0].offset), + offset1: Le32::from(desc.planes[1].offset), + offset2: Le32::from(desc.planes[2].offset), }; desc_mem.get_ref(0)?.store(ctrl_vfd_new_dmabuf); @@ -322,8 +322,8 @@ fn encode_resp(desc_mem: VolatileSlice, resp: WlResp) -> WlResult { flags, pfn, size, - stride, - } => encode_vfd_new_dmabuf(desc_mem, id, flags, pfn, size, stride), + desc, + } => encode_vfd_new_dmabuf(desc_mem, id, flags, pfn, size, desc), WlResp::VfdRecv { id, data, vfds } => encode_vfd_recv(desc_mem, id, data, vfds), WlResp::VfdHup { id } => encode_vfd_hup(desc_mem, id), r => { @@ -512,7 +512,7 @@ enum WlResp<'a> { flags: u32, pfn: u64, size: u32, - stride: u32, + desc: GpuMemoryDesc, }, VfdRecv { id: u32, @@ -616,20 +616,21 @@ impl WlVfd { } #[cfg(feature = "wl-dmabuf")] - fn dmabuf(vm: VmRequester, width: u32, height: u32, format: u32) -> WlResult<(WlVfd, u32)> { + fn dmabuf(vm: VmRequester, width: u32, height: u32, format: u32) -> + WlResult<(WlVfd, GpuMemoryDesc)> { let allocate_and_register_gpu_memory_response = vm.request(VmRequest::AllocateAndRegisterGpuMemory { width: width, height: height, format: format })?; match allocate_and_register_gpu_memory_response { - VmResponse::AllocateAndRegisterGpuMemory { fd, pfn, slot, stride } => { + VmResponse::AllocateAndRegisterGpuMemory { fd, pfn, slot, desc } => { let mut vfd = WlVfd::default(); // Duplicate FD for shared memory instance. let raw_fd = unsafe { File::from_raw_fd(dup(fd.as_raw_fd())) }; let vfd_shm = SharedMemory::from_raw_fd(raw_fd).map_err(WlError::NewAlloc)?; vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm.into())); vfd.slot = Some((slot, pfn, vm)); - Ok((vfd, stride)) + Ok((vfd, desc)) } _ => Err(WlError::VmBadResponse), } @@ -930,16 +931,16 @@ impl WlState { match self.vfds.entry(id) { Entry::Vacant(entry) => { - let (vfd, stride) = WlVfd::dmabuf(self.vm.clone(), - width, - height, - format)?; + let (vfd, desc) = WlVfd::dmabuf(self.vm.clone(), + width, + height, + format)?; let resp = WlResp::VfdNewDmabuf { id: id, flags: 0, pfn: vfd.pfn().unwrap_or_default(), size: vfd.size().unwrap_or_default() as u32, - stride: stride, + desc, }; entry.insert(vfd); Ok(resp) diff --git a/gpu_buffer/src/lib.rs b/gpu_buffer/src/lib.rs index fb69eeb4cf..449ab53665 100644 --- a/gpu_buffer/src/lib.rs +++ b/gpu_buffer/src/lib.rs @@ -292,6 +292,24 @@ impl Buffer { unsafe { gbm_bo_get_num_planes(self.0) } } + /// Handle as u64 for the given plane. + pub fn plane_handle(&self, plane: usize) -> u64 { + // This is always safe to call with a valid gbm_bo pointer. + unsafe { gbm_bo_get_plane_handle(self.0, plane).u64 } + } + + /// Offset in bytes for the given plane. + pub fn plane_offset(&self, plane: usize) -> u32 { + // This is always safe to call with a valid gbm_bo pointer. + unsafe { gbm_bo_get_plane_offset(self.0, plane) } + } + + /// Length in bytes of one row for the given plane. + pub fn plane_stride(&self, plane: usize) -> u32 { + // This is always safe to call with a valid gbm_bo pointer. + unsafe { gbm_bo_get_plane_stride(self.0, plane) } + } + /// Exports a new dmabuf/prime file descriptor for the given plane. pub fn export_plane_fd(&self, plane: usize) -> Result { // This is always safe to call with a valid gbm_bo pointer. diff --git a/src/linux.rs b/src/linux.rs index 8aae432d8d..eb8565988a 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -31,7 +31,7 @@ use qcow::{self, QcowFile}; use sys_util::*; use sys_util; use vhost; -use vm_control::{VmRequest, GpuMemoryAllocator}; +use vm_control::{VmRequest, GpuMemoryAllocator, GpuMemoryPlaneDesc, GpuMemoryDesc}; #[cfg(feature = "wl-dmabuf")] use gpu_buffer; @@ -558,7 +558,8 @@ struct GpuBufferDevice { #[cfg(feature = "wl-dmabuf")] impl GpuMemoryAllocator for GpuBufferDevice { - fn allocate(&self, width: u32, height: u32, format: u32) -> sys_util::Result<(File, u32)> { + fn allocate(&self, width: u32, height: u32, format: u32) -> + sys_util::Result<(File, GpuMemoryDesc)> { let buffer = match self.device.create_buffer( width, height, @@ -573,15 +574,23 @@ impl GpuMemoryAllocator for GpuBufferDevice { Ok(v) => v, Err(_) => return Err(sys_util::Error::new(EINVAL)), }; - // We only support the first plane. Buffers with more planes are not - // a problem but additional planes will not be registered for access - // from guest. + // We only support one FD. Buffers with multiple planes are supported + // as long as each plane is associated with the same handle. let fd = match buffer.export_plane_fd(0) { Ok(v) => v, Err(e) => return Err(sys_util::Error::new(e)), }; - Ok((fd, buffer.stride())) + let mut desc = GpuMemoryDesc::default(); + for i in 0..buffer.num_planes() { + // Use stride and offset for plane if handle matches first plane. + if buffer.plane_handle(i) == buffer.plane_handle(0) { + desc.planes[i] = GpuMemoryPlaneDesc { stride: buffer.plane_stride(i), + offset: buffer.plane_offset(i) } + } + } + + Ok((fd, desc)) } } diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs index 3837bb45af..f13faeffb3 100644 --- a/vm_control/src/lib.rs +++ b/vm_control/src/lib.rs @@ -130,19 +130,32 @@ fn register_memory(vm: &mut Vm, next_mem_pfn: &mut u64, fd: &AsRawFd, size: usiz Ok((pfn, slot)) } +/// Struct that describes the offset and stride of a plane located in GPU memory. +#[derive(Clone, Copy, Debug, PartialEq, Default)] +pub struct GpuMemoryPlaneDesc { + pub stride: u32, + pub offset: u32, +} + +/// Struct that describes a GPU memory allocation that consists of up to 3 planes. +#[derive(Clone, Copy, Debug, Default)] +pub struct GpuMemoryDesc { + pub planes: [GpuMemoryPlaneDesc; 3], +} + /// Trait that needs to be implemented in order to service GPU memory allocation /// requests. Implementations are expected to support some set of buffer sizes and /// formats but every possible combination is not required. pub trait GpuMemoryAllocator { /// Allocates GPU memory for a buffer of a specific size and format. The memory - /// layout for the returned buffer must be linear. A file handle and the stride - /// for the buffer are returned on success. + /// layout for the returned buffer must be linear. A file handle and the + /// description of the planes for the buffer are returned on success. /// /// # Arguments /// * `width` - Width of buffer. /// * `height` - Height of buffer. /// * `format` - Fourcc format of buffer. - fn allocate(&self, width: u32, height: u32, format: u32) -> Result<(File, u32)>; + fn allocate(&self, width: u32, height: u32, format: u32) -> Result<(File, GpuMemoryDesc)>; } impl VmRequest { @@ -281,7 +294,7 @@ impl VmRequest { Some(v) => v, None => return VmResponse::Err(SysError::new(ENODEV)), }; - let (mut fd, stride) = match allocator.allocate(width, height, format) { + let (mut fd, desc) = match allocator.allocate(width, height, format) { Ok(v) => v, Err(e) => return VmResponse::Err(e), }; @@ -296,7 +309,7 @@ impl VmRequest { fd: MaybeOwnedFd::Owned(fd), pfn, slot, - stride }, + desc }, Err(e) => VmResponse::Err(e), } } @@ -316,15 +329,15 @@ pub enum VmResponse { /// number `pfn` and memory slot number `slot`. RegisterMemory { pfn: u64, slot: u32 }, /// The request to allocate and register GPU memory into guest address space was successfully - /// done at page frame number `pfn` and memory slot number `slot` for buffer with `stride`. - AllocateAndRegisterGpuMemory { fd: MaybeOwnedFd, pfn: u64, slot: u32, stride: u32 }, + /// done at page frame number `pfn` and memory slot number `slot` for buffer with `desc`. + AllocateAndRegisterGpuMemory { fd: MaybeOwnedFd, pfn: u64, slot: u32, desc: GpuMemoryDesc }, } const VM_RESPONSE_TYPE_OK: u32 = 1; const VM_RESPONSE_TYPE_ERR: u32 = 2; const VM_RESPONSE_TYPE_REGISTER_MEMORY: u32 = 3; const VM_RESPONSE_TYPE_ALLOCATE_AND_REGISTER_GPU_MEMORY: u32 = 4; -const VM_RESPONSE_SIZE: usize = 24; +const VM_RESPONSE_SIZE: usize = 48; #[repr(C)] #[derive(Clone, Copy, Default)] @@ -333,7 +346,12 @@ struct VmResponseStruct { errno: Le32, pfn: Le64, slot: Le32, - stride: Le32, + stride0: Le32, + stride1: Le32, + stride2: Le32, + offset0: Le32, + offset1: Le32, + offset2: Le32, } // Safe because it only has data and has no implicit padding. @@ -370,7 +388,14 @@ impl VmResponse { fd: MaybeOwnedFd::Owned(fd), pfn: resp.pfn.into(), slot: resp.slot.into(), - stride: resp.stride.into() + desc: GpuMemoryDesc { + planes: [ GpuMemoryPlaneDesc { stride: resp.stride0.into(), + offset: resp.offset0.into() }, + GpuMemoryPlaneDesc { stride: resp.stride1.into(), + offset: resp.offset1.into() }, + GpuMemoryPlaneDesc { stride: resp.stride2.into(), + offset: resp.offset2.into() } ], + }, }) } _ => Err(VmControlError::InvalidType), @@ -396,13 +421,18 @@ impl VmResponse { resp.pfn = Le64::from(pfn); resp.slot = Le32::from(slot); } - &VmResponse::AllocateAndRegisterGpuMemory {ref fd, pfn, slot, stride } => { + &VmResponse::AllocateAndRegisterGpuMemory {ref fd, pfn, slot, desc } => { fd_buf[0] = fd.as_raw_fd(); fd_len = 1; resp.type_ = Le32::from(VM_RESPONSE_TYPE_ALLOCATE_AND_REGISTER_GPU_MEMORY); resp.pfn = Le64::from(pfn); resp.slot = Le32::from(slot); - resp.stride = Le32::from(stride); + resp.stride0 = Le32::from(desc.planes[0].stride); + resp.stride1 = Le32::from(desc.planes[1].stride); + resp.stride2 = Le32::from(desc.planes[2].stride); + resp.offset0 = Le32::from(desc.planes[0].offset); + resp.offset1 = Le32::from(desc.planes[1].offset); + resp.offset2 = Le32::from(desc.planes[2].offset); } } let mut buf = [0; VM_RESPONSE_SIZE]; @@ -625,19 +655,24 @@ mod tests { shm.set_size(shm_size as u64).unwrap(); let memory_pfn = 55; let memory_slot = 66; - let gpu_stride = 32; + let memory_planes = [ + GpuMemoryPlaneDesc { stride: 32, offset: 84 }, + GpuMemoryPlaneDesc { stride: 48, offset: 96 }, + GpuMemoryPlaneDesc { stride: 64, offset: 112 } + ]; let r1 = VmResponse::AllocateAndRegisterGpuMemory { fd: MaybeOwnedFd::Borrowed(shm.as_raw_fd()), pfn: memory_pfn, slot: memory_slot, - stride: gpu_stride }; + desc: GpuMemoryDesc { planes: memory_planes }, + }; r1.send(&mut scm, &s1).unwrap(); match VmResponse::recv(&mut scm, &s2).unwrap() { - VmResponse::AllocateAndRegisterGpuMemory { fd, pfn, slot, stride } => { + VmResponse::AllocateAndRegisterGpuMemory { fd, pfn, slot, desc } => { assert!(fd.as_raw_fd() >= 0); assert_eq!(pfn, memory_pfn); assert_eq!(slot, memory_slot); - assert_eq!(stride, gpu_stride); + assert_eq!(desc.planes, memory_planes); } _ => panic!("recv wrong response variant"), }