summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Reveman <reveman@chromium.org>2018-05-24 06:48:19 -0400
committerchrome-bot <chrome-bot@chromium.org>2018-05-26 10:19:50 -0700
commit5f5e7ec3ba04e29b68244e56f1bce0d87ff6f7d9 (patch)
tree1fddde72ff3e67deed979dc70794cf83fa468512
parent8608eb044b8b00f3309804de3b86758ed8184963 (diff)
downloadcrosvm-5f5e7ec3ba04e29b68244e56f1bce0d87ff6f7d9.tar
crosvm-5f5e7ec3ba04e29b68244e56f1bce0d87ff6f7d9.tar.gz
crosvm-5f5e7ec3ba04e29b68244e56f1bce0d87ff6f7d9.tar.bz2
crosvm-5f5e7ec3ba04e29b68244e56f1bce0d87ff6f7d9.tar.lz
crosvm-5f5e7ec3ba04e29b68244e56f1bce0d87ff6f7d9.tar.xz
crosvm-5f5e7ec3ba04e29b68244e56f1bce0d87ff6f7d9.tar.zst
crosvm-5f5e7ec3ba04e29b68244e56f1bce0d87ff6f7d9.zip
virtwl: better multi-plane DMABuf support
Multi-plane DMABufs are useful for efficient video playback. The
guest can already use this but has to guess the stride and offsets
for the second and third plane as they are not passed by virtwl
to the guest kernel.

This extracts the correct strides and offsets for each buffer and
passes them back to the guest in the allocation response message.

BUG=chromium:837209
TEST=sommelier can use nv12 buffers without guessing stride/offset

Change-Id: I36ae2fad6605293c907802121676296cbc607a57
Reviewed-on: https://chromium-review.googlesource.com/1070799
Commit-Ready: David Reveman <reveman@chromium.org>
Tested-by: David Reveman <reveman@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
-rw-r--r--devices/src/virtio/wl.rs39
-rw-r--r--gpu_buffer/src/lib.rs18
-rw-r--r--src/linux.rs21
-rw-r--r--vm_control/src/lib.rs67
4 files changed, 104 insertions, 41 deletions
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs
index a8b2603..6a81bf3 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<u32> {
     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<u32> {
             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 fb69eeb..449ab53 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<File, i32> {
         // This is always safe to call with a valid gbm_bo pointer.
diff --git a/src/linux.rs b/src/linux.rs
index 8aae432..eb85659 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 3837bb4..f13faef 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"),
         }