crosvm/devices/src/virtio/gpu/udmabuf.rs

249 lines
7.6 KiB
Rust
Raw Normal View History

devices: gpu: complete investigation of udmabuf driver Hello everyone ..! After 2.5 years of "on the side" inquiries, I have finally completed my investigations [1] of the udmabuf!! udmabuf is a kernel driver that turns memfd pages into dmabufs. The original hope was it would reduce texture upload costs for virgl, which it did for larger textures [2]. But no measurable improvements where seen with real games. In addition, a more advanced "gfx-streaming" model has since come into the horizon[3][4], which is more performant, conformant, secure and simpler than virgl. As such, building more on virgl does not seem to be best option, but that's another story for another day. Where does that leave udmabuf, then?!? The investigation was able to turn up two possible use cases: 1) Intel integrated + dGPU PCI-passthrough resource sharing When the dGPU is passthroughed into the guest, the dGPU's memory is not available to the host. Ideally, for zero-copy, we would like to get the render target out of the guest somehow and then send to the display. Many approaches have been proposed, such as Vivek Kasireddy's Vdmabuf driver [5]. The current thinking is virtgpu guest blobs can be exported, and then imported into the dGPU -- Vivek is looking into this approach right now ..!! Sommelier or virtgpu KMS can then share the guest blob with the host. It's a quite complex use case and requires changes to guest Mesa GBM to get (such as metadata query) to get the right modifier. Indeed, some would even say you need a virtgpu context type optimized for sharing across domain boundaries. minigbm already supports this for Android upstream's Virtual Graphics Interface (VGI) initiative. 2) Guest VRAM dedicated heap created udmabufs This use case, proposed by automative virtualization expert Dmitry Sepp [6], is primarily for automotive hypervisors (such COQOS). It's typically not easy for such hypervisors to get zero-copy via BLOB_MEM_HOST3D, and these hypervisors have had their homebrew versions of udmabuf for many years. It's important to upstream the workarounds that are currently done for such hypervisors. To increase security and isolation, a guest dedicated heap is preferred over guest system memory. We might even need dedicated queues, who knows. crosvm seems like the most likely upstream target due to it's world class blob support and open-source nature. As such, this CL adds basic udmabuf capabilites so these use cases can be developed further via crosvm. [1] https://www.youtube.com/watch?v=lp6z3s1Gig0 [2] crrev.com/c/1325515 [3] goto.google.com/address-space-graphics [4] https://drive.google.com/file/d/19K_6M8QUeOn-x7HVYvoNfnuC6G5vkR8f/view [5] https://lists.freedesktop.org/archives/dri-devel/2021-February/296177.html [6] https://gitlab.freedesktop.org/virgl/virglrenderer/-/issues/159 BUG=chromium:892806, b:173630595 TEST=Create a bunch of udmabufs from the guest, with the subsequent patches Change-Id: Ia8083c0aa065f303f660ec6875ff5fb76f5d7b4f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2786290 Tested-by: Gurchetan Singh <gurchetansingh@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Gurchetan Singh <gurchetansingh@chromium.org> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
2018-10-22 16:16:45 +00:00
// Copyright 2021 The Chromium OS Authors. All rights reservsize.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![allow(dead_code)]
use std::fs::{File, OpenOptions};
use std::os::raw::c_uint;
use std::path::Path;
use std::{fmt, io};
use base::{
ioctl_iow_nr, ioctl_with_ptr, pagesize, AsRawDescriptor, FromRawDescriptor, MappedRegion,
SafeDescriptor,
};
use data_model::{flexible_array_impl, FlexibleArray, FlexibleArrayWrapper};
devices: gpu: complete investigation of udmabuf driver Hello everyone ..! After 2.5 years of "on the side" inquiries, I have finally completed my investigations [1] of the udmabuf!! udmabuf is a kernel driver that turns memfd pages into dmabufs. The original hope was it would reduce texture upload costs for virgl, which it did for larger textures [2]. But no measurable improvements where seen with real games. In addition, a more advanced "gfx-streaming" model has since come into the horizon[3][4], which is more performant, conformant, secure and simpler than virgl. As such, building more on virgl does not seem to be best option, but that's another story for another day. Where does that leave udmabuf, then?!? The investigation was able to turn up two possible use cases: 1) Intel integrated + dGPU PCI-passthrough resource sharing When the dGPU is passthroughed into the guest, the dGPU's memory is not available to the host. Ideally, for zero-copy, we would like to get the render target out of the guest somehow and then send to the display. Many approaches have been proposed, such as Vivek Kasireddy's Vdmabuf driver [5]. The current thinking is virtgpu guest blobs can be exported, and then imported into the dGPU -- Vivek is looking into this approach right now ..!! Sommelier or virtgpu KMS can then share the guest blob with the host. It's a quite complex use case and requires changes to guest Mesa GBM to get (such as metadata query) to get the right modifier. Indeed, some would even say you need a virtgpu context type optimized for sharing across domain boundaries. minigbm already supports this for Android upstream's Virtual Graphics Interface (VGI) initiative. 2) Guest VRAM dedicated heap created udmabufs This use case, proposed by automative virtualization expert Dmitry Sepp [6], is primarily for automotive hypervisors (such COQOS). It's typically not easy for such hypervisors to get zero-copy via BLOB_MEM_HOST3D, and these hypervisors have had their homebrew versions of udmabuf for many years. It's important to upstream the workarounds that are currently done for such hypervisors. To increase security and isolation, a guest dedicated heap is preferred over guest system memory. We might even need dedicated queues, who knows. crosvm seems like the most likely upstream target due to it's world class blob support and open-source nature. As such, this CL adds basic udmabuf capabilites so these use cases can be developed further via crosvm. [1] https://www.youtube.com/watch?v=lp6z3s1Gig0 [2] crrev.com/c/1325515 [3] goto.google.com/address-space-graphics [4] https://drive.google.com/file/d/19K_6M8QUeOn-x7HVYvoNfnuC6G5vkR8f/view [5] https://lists.freedesktop.org/archives/dri-devel/2021-February/296177.html [6] https://gitlab.freedesktop.org/virgl/virglrenderer/-/issues/159 BUG=chromium:892806, b:173630595 TEST=Create a bunch of udmabufs from the guest, with the subsequent patches Change-Id: Ia8083c0aa065f303f660ec6875ff5fb76f5d7b4f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2786290 Tested-by: Gurchetan Singh <gurchetansingh@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Gurchetan Singh <gurchetansingh@chromium.org> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
2018-10-22 16:16:45 +00:00
use rutabaga_gfx::{RutabagaHandle, RUTABAGA_MEM_HANDLE_TYPE_DMABUF};
use super::udmabuf_bindings::*;
devices: gpu: complete investigation of udmabuf driver Hello everyone ..! After 2.5 years of "on the side" inquiries, I have finally completed my investigations [1] of the udmabuf!! udmabuf is a kernel driver that turns memfd pages into dmabufs. The original hope was it would reduce texture upload costs for virgl, which it did for larger textures [2]. But no measurable improvements where seen with real games. In addition, a more advanced "gfx-streaming" model has since come into the horizon[3][4], which is more performant, conformant, secure and simpler than virgl. As such, building more on virgl does not seem to be best option, but that's another story for another day. Where does that leave udmabuf, then?!? The investigation was able to turn up two possible use cases: 1) Intel integrated + dGPU PCI-passthrough resource sharing When the dGPU is passthroughed into the guest, the dGPU's memory is not available to the host. Ideally, for zero-copy, we would like to get the render target out of the guest somehow and then send to the display. Many approaches have been proposed, such as Vivek Kasireddy's Vdmabuf driver [5]. The current thinking is virtgpu guest blobs can be exported, and then imported into the dGPU -- Vivek is looking into this approach right now ..!! Sommelier or virtgpu KMS can then share the guest blob with the host. It's a quite complex use case and requires changes to guest Mesa GBM to get (such as metadata query) to get the right modifier. Indeed, some would even say you need a virtgpu context type optimized for sharing across domain boundaries. minigbm already supports this for Android upstream's Virtual Graphics Interface (VGI) initiative. 2) Guest VRAM dedicated heap created udmabufs This use case, proposed by automative virtualization expert Dmitry Sepp [6], is primarily for automotive hypervisors (such COQOS). It's typically not easy for such hypervisors to get zero-copy via BLOB_MEM_HOST3D, and these hypervisors have had their homebrew versions of udmabuf for many years. It's important to upstream the workarounds that are currently done for such hypervisors. To increase security and isolation, a guest dedicated heap is preferred over guest system memory. We might even need dedicated queues, who knows. crosvm seems like the most likely upstream target due to it's world class blob support and open-source nature. As such, this CL adds basic udmabuf capabilites so these use cases can be developed further via crosvm. [1] https://www.youtube.com/watch?v=lp6z3s1Gig0 [2] crrev.com/c/1325515 [3] goto.google.com/address-space-graphics [4] https://drive.google.com/file/d/19K_6M8QUeOn-x7HVYvoNfnuC6G5vkR8f/view [5] https://lists.freedesktop.org/archives/dri-devel/2021-February/296177.html [6] https://gitlab.freedesktop.org/virgl/virglrenderer/-/issues/159 BUG=chromium:892806, b:173630595 TEST=Create a bunch of udmabufs from the guest, with the subsequent patches Change-Id: Ia8083c0aa065f303f660ec6875ff5fb76f5d7b4f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2786290 Tested-by: Gurchetan Singh <gurchetansingh@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Gurchetan Singh <gurchetansingh@chromium.org> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
2018-10-22 16:16:45 +00:00
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
const UDMABUF_IOCTL_BASE: c_uint = 0x75;
ioctl_iow_nr!(UDMABUF_CREATE, UDMABUF_IOCTL_BASE, 0x42, udmabuf_create);
ioctl_iow_nr!(
UDMABUF_CREATE_LIST,
UDMABUF_IOCTL_BASE,
0x43,
udmabuf_create_list
);
flexible_array_impl!(udmabuf_create_list, udmabuf_create_item, count, list);
devices: gpu: complete investigation of udmabuf driver Hello everyone ..! After 2.5 years of "on the side" inquiries, I have finally completed my investigations [1] of the udmabuf!! udmabuf is a kernel driver that turns memfd pages into dmabufs. The original hope was it would reduce texture upload costs for virgl, which it did for larger textures [2]. But no measurable improvements where seen with real games. In addition, a more advanced "gfx-streaming" model has since come into the horizon[3][4], which is more performant, conformant, secure and simpler than virgl. As such, building more on virgl does not seem to be best option, but that's another story for another day. Where does that leave udmabuf, then?!? The investigation was able to turn up two possible use cases: 1) Intel integrated + dGPU PCI-passthrough resource sharing When the dGPU is passthroughed into the guest, the dGPU's memory is not available to the host. Ideally, for zero-copy, we would like to get the render target out of the guest somehow and then send to the display. Many approaches have been proposed, such as Vivek Kasireddy's Vdmabuf driver [5]. The current thinking is virtgpu guest blobs can be exported, and then imported into the dGPU -- Vivek is looking into this approach right now ..!! Sommelier or virtgpu KMS can then share the guest blob with the host. It's a quite complex use case and requires changes to guest Mesa GBM to get (such as metadata query) to get the right modifier. Indeed, some would even say you need a virtgpu context type optimized for sharing across domain boundaries. minigbm already supports this for Android upstream's Virtual Graphics Interface (VGI) initiative. 2) Guest VRAM dedicated heap created udmabufs This use case, proposed by automative virtualization expert Dmitry Sepp [6], is primarily for automotive hypervisors (such COQOS). It's typically not easy for such hypervisors to get zero-copy via BLOB_MEM_HOST3D, and these hypervisors have had their homebrew versions of udmabuf for many years. It's important to upstream the workarounds that are currently done for such hypervisors. To increase security and isolation, a guest dedicated heap is preferred over guest system memory. We might even need dedicated queues, who knows. crosvm seems like the most likely upstream target due to it's world class blob support and open-source nature. As such, this CL adds basic udmabuf capabilites so these use cases can be developed further via crosvm. [1] https://www.youtube.com/watch?v=lp6z3s1Gig0 [2] crrev.com/c/1325515 [3] goto.google.com/address-space-graphics [4] https://drive.google.com/file/d/19K_6M8QUeOn-x7HVYvoNfnuC6G5vkR8f/view [5] https://lists.freedesktop.org/archives/dri-devel/2021-February/296177.html [6] https://gitlab.freedesktop.org/virgl/virglrenderer/-/issues/159 BUG=chromium:892806, b:173630595 TEST=Create a bunch of udmabufs from the guest, with the subsequent patches Change-Id: Ia8083c0aa065f303f660ec6875ff5fb76f5d7b4f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2786290 Tested-by: Gurchetan Singh <gurchetansingh@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Gurchetan Singh <gurchetansingh@chromium.org> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
2018-10-22 16:16:45 +00:00
type UdmabufCreateList = FlexibleArrayWrapper<udmabuf_create_list, udmabuf_create_item>;
#[derive(Debug)]
pub enum UdmabufError {
DriverOpenFailed(io::Error),
NotPageAligned,
InvalidOffset(GuestMemoryError),
DmabufCreationFail(io::Error),
}
impl fmt::Display for UdmabufError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::UdmabufError::*;
match self {
DriverOpenFailed(e) => write!(f, "failed to open udmabuf driver: {:?}", e),
NotPageAligned => write!(f, "All guest addresses must aligned to 4KiB"),
InvalidOffset(e) => write!(f, "failed to get region offset: {:?}", e),
DmabufCreationFail(e) => write!(f, "failed to create buffer: {:?}", e),
}
}
}
/// The result of an operation in this file.
pub type UdmabufResult<T> = std::result::Result<T, UdmabufError>;
// Returns absolute offset within the memory corresponding to a particular guest address.
// This offset is not relative to a particular mapping.
// # Examples
//
// # fn test_memory_offsets() {
// # let start_addr1 = GuestAddress(0x100)
// # let start_addr2 = GuestAddress(0x1100);
// # let mem = GuestMemory::new(&vec![(start_addr1, 0x1000),(start_addr2, 0x1000)])?;
// # assert_eq!(memory_offset(&mem, GuestAddress(0x1100), 0x1000).unwrap(),0x1000);
// #}
fn memory_offset(mem: &GuestMemory, guest_addr: GuestAddress, len: u64) -> UdmabufResult<u64> {
mem.do_in_region(guest_addr, move |mapping, map_offset, memfd_offset| {
let map_offset = map_offset as u64;
if map_offset
.checked_add(len)
.map_or(true, |a| a > mapping.size() as u64)
{
return Err(GuestMemoryError::InvalidGuestAddress(guest_addr));
}
return Ok(memfd_offset + map_offset);
})
.map_err(UdmabufError::InvalidOffset)
}
/// A convenience wrapper for the Linux kernel's udmabuf driver.
///
/// udmabuf is a kernel driver that turns memfd pages into dmabufs. It can be used for
/// zero-copy buffer sharing between the guest and host when guest memory is backed by
/// memfd pages.
pub struct UdmabufDriver {
driver_fd: File,
}
impl UdmabufDriver {
/// Opens the udmabuf device on success.
pub fn new() -> UdmabufResult<UdmabufDriver> {
const UDMABUF_PATH: &str = "/dev/udmabuf";
let path = Path::new(UDMABUF_PATH);
let fd = OpenOptions::new()
.read(true)
.write(true)
.open(path)
.map_err(UdmabufError::DriverOpenFailed)?;
Ok(UdmabufDriver { driver_fd: fd })
}
/// Creates a dma-buf fd for the given scatter-gather list of guest memory pages (`iovecs`).
pub fn create_udmabuf(
&self,
mem: &GuestMemory,
iovecs: &[(GuestAddress, usize)],
) -> UdmabufResult<RutabagaHandle> {
let pgsize = pagesize();
let mut list = UdmabufCreateList::new(iovecs.len() as usize);
let mut items = list.mut_entries_slice();
for (i, &(addr, len)) in iovecs.iter().enumerate() {
let offset = memory_offset(mem, addr, len as u64)?;
if offset as usize % pgsize != 0 || len % pgsize != 0 {
return Err(UdmabufError::NotPageAligned);
}
// `unwrap` can't panic if `memory_offset obove succeeds.
items[i].memfd = mem.shm_region(addr).unwrap().as_raw_descriptor() as u32;
devices: gpu: complete investigation of udmabuf driver Hello everyone ..! After 2.5 years of "on the side" inquiries, I have finally completed my investigations [1] of the udmabuf!! udmabuf is a kernel driver that turns memfd pages into dmabufs. The original hope was it would reduce texture upload costs for virgl, which it did for larger textures [2]. But no measurable improvements where seen with real games. In addition, a more advanced "gfx-streaming" model has since come into the horizon[3][4], which is more performant, conformant, secure and simpler than virgl. As such, building more on virgl does not seem to be best option, but that's another story for another day. Where does that leave udmabuf, then?!? The investigation was able to turn up two possible use cases: 1) Intel integrated + dGPU PCI-passthrough resource sharing When the dGPU is passthroughed into the guest, the dGPU's memory is not available to the host. Ideally, for zero-copy, we would like to get the render target out of the guest somehow and then send to the display. Many approaches have been proposed, such as Vivek Kasireddy's Vdmabuf driver [5]. The current thinking is virtgpu guest blobs can be exported, and then imported into the dGPU -- Vivek is looking into this approach right now ..!! Sommelier or virtgpu KMS can then share the guest blob with the host. It's a quite complex use case and requires changes to guest Mesa GBM to get (such as metadata query) to get the right modifier. Indeed, some would even say you need a virtgpu context type optimized for sharing across domain boundaries. minigbm already supports this for Android upstream's Virtual Graphics Interface (VGI) initiative. 2) Guest VRAM dedicated heap created udmabufs This use case, proposed by automative virtualization expert Dmitry Sepp [6], is primarily for automotive hypervisors (such COQOS). It's typically not easy for such hypervisors to get zero-copy via BLOB_MEM_HOST3D, and these hypervisors have had their homebrew versions of udmabuf for many years. It's important to upstream the workarounds that are currently done for such hypervisors. To increase security and isolation, a guest dedicated heap is preferred over guest system memory. We might even need dedicated queues, who knows. crosvm seems like the most likely upstream target due to it's world class blob support and open-source nature. As such, this CL adds basic udmabuf capabilites so these use cases can be developed further via crosvm. [1] https://www.youtube.com/watch?v=lp6z3s1Gig0 [2] crrev.com/c/1325515 [3] goto.google.com/address-space-graphics [4] https://drive.google.com/file/d/19K_6M8QUeOn-x7HVYvoNfnuC6G5vkR8f/view [5] https://lists.freedesktop.org/archives/dri-devel/2021-February/296177.html [6] https://gitlab.freedesktop.org/virgl/virglrenderer/-/issues/159 BUG=chromium:892806, b:173630595 TEST=Create a bunch of udmabufs from the guest, with the subsequent patches Change-Id: Ia8083c0aa065f303f660ec6875ff5fb76f5d7b4f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2786290 Tested-by: Gurchetan Singh <gurchetansingh@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Gurchetan Singh <gurchetansingh@chromium.org> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
2018-10-22 16:16:45 +00:00
items[i].__pad = 0;
items[i].offset = offset;
items[i].size = len as u64;
}
// Safe because we always allocate enough space for `udmabuf_create_list`.
let fd = unsafe {
let create_list = list.as_mut_ptr();
(*create_list).flags = UDMABUF_FLAGS_CLOEXEC;
ioctl_with_ptr(&self.driver_fd, UDMABUF_CREATE_LIST(), create_list)
};
if fd < 0 {
return Err(UdmabufError::DmabufCreationFail(io::Error::last_os_error()));
}
// Safe because we validated the file exists.
let os_handle = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
Ok(RutabagaHandle {
os_handle,
handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use base::kernel_has_memfd;
use vm_memory::GuestAddress;
#[test]
fn test_memory_offsets() {
if !kernel_has_memfd() {
return;
}
let start_addr1 = GuestAddress(0x100);
let start_addr2 = GuestAddress(0x1100);
let start_addr3 = GuestAddress(0x2100);
let mem = GuestMemory::new(&vec![
(start_addr1, 0x1000),
(start_addr2, 0x1000),
(start_addr3, 0x1000),
])
.unwrap();
assert_eq!(memory_offset(&mem, GuestAddress(0x300), 1).unwrap(), 0x200);
assert_eq!(
memory_offset(&mem, GuestAddress(0x1200), 1).unwrap(),
0x1100
);
assert_eq!(
memory_offset(&mem, GuestAddress(0x1100), 0x1000).unwrap(),
0x1000
);
assert!(memory_offset(&mem, GuestAddress(0x1100), 0x1001).is_err());
}
#[test]
fn test_udmabuf_create() {
if !kernel_has_memfd() {
return;
}
let driver_result = UdmabufDriver::new();
// Most kernels will not have udmabuf support.
if driver_result.is_err() {
return;
}
let driver = driver_result.unwrap();
let start_addr1 = GuestAddress(0x100);
let start_addr2 = GuestAddress(0x1100);
let start_addr3 = GuestAddress(0x2100);
let sg_list = vec![
(start_addr1, 0x1000),
(start_addr2, 0x1000),
(start_addr3, 0x1000),
];
let mem = GuestMemory::new(&sg_list[..]).unwrap();
let mut udmabuf_create_list = vec![
(start_addr3, 0x1000 as usize),
(start_addr2, 0x1000 as usize),
(start_addr1, 0x1000 as usize),
(GuestAddress(0x4000), 0x1000 as usize),
];
let result = driver.create_udmabuf(&mem, &udmabuf_create_list[..]);
assert_eq!(result.is_err(), true);
udmabuf_create_list.pop();
let rutabaga_handle1 = driver
.create_udmabuf(&mem, &udmabuf_create_list[..])
.unwrap();
assert_eq!(
rutabaga_handle1.handle_type,
RUTABAGA_MEM_HANDLE_TYPE_DMABUF
);
udmabuf_create_list.pop();
// Multiple udmabufs with same memory backing is allowed.
let rutabaga_handle2 = driver
.create_udmabuf(&mem, &udmabuf_create_list[..])
.unwrap();
assert_eq!(
rutabaga_handle2.handle_type,
RUTABAGA_MEM_HANDLE_TYPE_DMABUF
);
}
}