diff options
author | David Reveman <reveman@chromium.org> | 2018-04-22 21:42:09 -0400 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-05-16 08:34:40 -0700 |
commit | 52ba4e5c6492d69a8ff57e87a0134c148a74a1f2 (patch) | |
tree | e7afc45010d50be4cb42db47314222af8d6b121d /src/linux.rs | |
parent | 0f1770d3ef9469b23edbaaa5f977dc0bb59602c6 (diff) | |
download | crosvm-52ba4e5c6492d69a8ff57e87a0134c148a74a1f2.tar crosvm-52ba4e5c6492d69a8ff57e87a0134c148a74a1f2.tar.gz crosvm-52ba4e5c6492d69a8ff57e87a0134c148a74a1f2.tar.bz2 crosvm-52ba4e5c6492d69a8ff57e87a0134c148a74a1f2.tar.lz crosvm-52ba4e5c6492d69a8ff57e87a0134c148a74a1f2.tar.xz crosvm-52ba4e5c6492d69a8ff57e87a0134c148a74a1f2.tar.zst crosvm-52ba4e5c6492d69a8ff57e87a0134c148a74a1f2.zip |
virtwl: Add DMABuf allocation support.
This implements DMABuf allocation type in the virtio wayland device. We attempt to locate a supported DRM device prior to engaging the device jail. If found, the DRM device is passed to the wayland device code and used to serve DMABuf allocations. DMABuf support can be disabled by not providing crosvm with access to any DRM device nodes. The guest is expected to handle the case when DMABuf allocation fails and fall-back to standard shared memory. This initial change uses DRM directly but is structured in a way that would allow the allocator to be replaced by minigbm with minimal effort. BUG=chromium:837209 TEST=crosvm finds drm device and returns valid dmabufs to guest Change-Id: Ic1fd776dfdfefae2d7b321d449273ef269e9cc62 Reviewed-on: https://chromium-review.googlesource.com/1034088 Commit-Ready: David Reveman <reveman@chromium.org> Tested-by: David Reveman <reveman@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'src/linux.rs')
-rw-r--r-- | src/linux.rs | 81 |
1 files changed, 76 insertions, 5 deletions
diff --git a/src/linux.rs b/src/linux.rs index 6192478..8aae432 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -18,6 +18,8 @@ use std::thread::JoinHandle; use libc; use libc::c_int; +#[cfg(feature = "wl-dmabuf")] +use libc::EINVAL; use device_manager; use devices; @@ -29,7 +31,9 @@ use qcow::{self, QcowFile}; use sys_util::*; use sys_util; use vhost; -use vm_control::VmRequest; +use vm_control::{VmRequest, GpuMemoryAllocator}; +#[cfg(feature = "wl-dmabuf")] +use gpu_buffer; use Config; use DiskType; @@ -48,6 +52,7 @@ pub enum Error { CloneEventFd(sys_util::Error), Cmdline(kernel_cmdline::Error), CreateEventFd(sys_util::Error), + CreateGpuBufferDevice, CreateGuestMemory(Box<error::Error>), CreateIrqChip(Box<error::Error>), CreateKvm(sys_util::Error), @@ -66,6 +71,7 @@ pub enum Error { NetDeviceNew(devices::virtio::NetError), NoVarEmpty, OpenKernel(PathBuf, io::Error), + OpenGpuBufferDevice, PollContextAdd(sys_util::Error), QcowDeviceCreate(qcow::Error), RegisterBalloon(device_manager::Error), @@ -99,6 +105,7 @@ impl fmt::Display for Error { &Error::CloneEventFd(ref e) => write!(f, "failed to clone eventfd: {:?}", e), &Error::Cmdline(ref e) => write!(f, "the given kernel command line was invalid: {}", e), &Error::CreateEventFd(ref e) => write!(f, "failed to create eventfd: {:?}", e), + &Error::CreateGpuBufferDevice => write!(f, "failed to create GPU buffer device"), &Error::CreateGuestMemory(ref e) => write!(f, "failed to create guest memory: {:?}", e), &Error::CreateIrqChip(ref e) => { write!(f, "failed to create in-kernel IRQ chip: {:?}", e) @@ -123,6 +130,7 @@ impl fmt::Display for Error { &Error::OpenKernel(ref p, ref e) => { write!(f, "failed to open kernel image {:?}: {}", p, e) } + &Error::OpenGpuBufferDevice => write!(f, "failed to open GPU buffer device"), &Error::PollContextAdd(ref e) => write!(f, "failed to add fd to poll context: {:?}", e), &Error::QcowDeviceCreate(ref e) => { write!(f, "failed to read qcow formatted file {:?}", e) @@ -543,6 +551,56 @@ fn run_vcpu(vcpu: Vcpu, .map_err(Error::SpawnVcpu) } +#[cfg(feature = "wl-dmabuf")] +struct GpuBufferDevice { + device: gpu_buffer::Device, +} + +#[cfg(feature = "wl-dmabuf")] +impl GpuMemoryAllocator for GpuBufferDevice { + fn allocate(&self, width: u32, height: u32, format: u32) -> sys_util::Result<(File, u32)> { + let buffer = match self.device.create_buffer( + width, + height, + gpu_buffer::Format::from(format), + // Linear layout is a requirement as virtio wayland guest expects + // this for CPU access to the buffer. Scanout and texturing are + // optional as the consumer (wayland compositor) is expected to + // fall-back to a less efficient meachnisms for presentation if + // neccesary. In practice, linear buffers for commonly used formats + // will also support scanout and texturing. + gpu_buffer::Flags::empty().use_linear(true)) { + 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. + let fd = match buffer.export_plane_fd(0) { + Ok(v) => v, + Err(e) => return Err(sys_util::Error::new(e)), + }; + + Ok((fd, buffer.stride())) + } +} + +#[cfg(feature = "wl-dmabuf")] +fn create_gpu_memory_allocator() -> Result<Option<Box<GpuMemoryAllocator>>> { + let undesired: &[&str] = &["vgem"]; + let fd = gpu_buffer::rendernode::open_device(undesired) + .map_err(|_| Error::OpenGpuBufferDevice)?; + let device = gpu_buffer::Device::new(fd) + .map_err(|_| Error::CreateGpuBufferDevice)?; + info!("created GPU buffer device for DMABuf allocations"); + Ok(Some(Box::new(GpuBufferDevice { device }))) +} + +#[cfg(not(feature = "wl-dmabuf"))] +fn create_gpu_memory_allocator() -> Result<Option<Box<GpuMemoryAllocator>>> { + Ok(None) +} + fn run_control(vm: &mut Vm, control_sockets: Vec<UnlinkUnixDatagram>, next_dev_pfn: &mut u64, @@ -552,7 +610,8 @@ fn run_control(vm: &mut Vm, kill_signaled: Arc<AtomicBool>, vcpu_handles: Vec<JoinHandle<()>>, balloon_host_socket: UnixDatagram, - _irqchip_fd: Option<File>) + _irqchip_fd: Option<File>, + gpu_memory_allocator: Option<Box<GpuMemoryAllocator>>) -> Result<()> { const MAX_VM_FD_RECV: usize = 1; @@ -638,8 +697,13 @@ fn run_control(vm: &mut Vm, Ok(request) => { let mut running = true; let response = - request.execute(vm, next_dev_pfn, - &mut running, &balloon_host_socket); + request.execute(vm, + next_dev_pfn, + &mut running, + &balloon_host_socket, + gpu_memory_allocator.as_ref().map(|v| { + v.as_ref() + })); if let Err(e) = response.send(&mut scm, socket.as_ref()) { error!("failed to send VmResponse: {:?}", e); } @@ -751,6 +815,12 @@ pub fn run_config(cfg: Config) -> Result<()> { &mut control_sockets, balloon_device_socket)?; + let gpu_memory_allocator = if cfg.wayland_dmabuf { + create_gpu_memory_allocator()? + } else { + None + }; + for param in &cfg.params { cmdline.insert_str(¶m).map_err(Error::Cmdline)?; } @@ -787,5 +857,6 @@ pub fn run_config(cfg: Config) -> Result<()> { kill_signaled, vcpu_handles, balloon_host_socket, - irq_chip) + irq_chip, + gpu_memory_allocator) } |