diff --git a/Cargo.lock b/Cargo.lock index 62cc52ff41..b905dcd9fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,6 +104,7 @@ dependencies = [ "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "qcow 0.1.0", "rand_ish 0.1.0", + "render_node_forward 0.1.0", "resources 0.1.0", "sync 0.1.0", "sys_util 0.1.0", @@ -405,6 +406,13 @@ dependencies = [ name = "rand_ish" version = "0.1.0" +[[package]] +name = "render_node_forward" +version = "0.1.0" +dependencies = [ + "sys_util 0.1.0", +] + [[package]] name = "resources" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index cc39e5b45a..253604d16b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ gpu = ["devices/gpu"] usb-emulation = ["usb_util"] sandboxed-libusb = ["usb_util/sandboxed-libusb"] tpm = ["devices/tpm"] +gpu-forward = ["render_node_forward"] [dependencies] arch = { path = "arch" } @@ -58,6 +59,7 @@ resources = { path = "resources" } p9 = { path = "p9" } sync = { path = "sync" } rand_ish = { path = "rand_ish" } +render_node_forward = { path = "render_node_forward", optional = true } [target.'cfg(target_arch = "x86_64")'.dependencies] x86_64 = { path = "x86_64" } diff --git a/render_node_forward/Cargo.toml b/render_node_forward/Cargo.toml new file mode 100644 index 0000000000..accd36dbf7 --- /dev/null +++ b/render_node_forward/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "render_node_forward" +version = "0.1.0" +authors = ["The Chromium OS Authors"] + +[lib] +path = "lib.rs" + +[dependencies] +sys_util = { path = "../sys_util" } diff --git a/render_node_forward/lib.rs b/render_node_forward/lib.rs new file mode 100644 index 0000000000..8c04eb959a --- /dev/null +++ b/render_node_forward/lib.rs @@ -0,0 +1,62 @@ +// Copyright 2019 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +extern crate sys_util; + +use sys_util::{GuestAddress, GuestMemory, MemoryMapping}; + +#[link(name = "rendernodehost")] +extern "C" { + fn start_render_node_host( + gpu_host_mem: *mut u8, + gpu_guest_mem_start: u64, + gpu_guest_mem_size: u64, + host_start: *const u8, + host_4g_start: *const u8, + ); +} + +/// The number of bytes in 4 GiB. +pub const FOUR_GB: u64 = (1 << 32); +/// The size required for the render node host in host and guest address space. +pub const RENDER_NODE_HOST_SIZE: u64 = FOUR_GB; + +/// A render node host device that interfaces with the guest render node forwarder. +pub struct RenderNodeHost { + #[allow(dead_code)] + guest_mem: GuestMemory, +} + +impl RenderNodeHost { + /// Starts the render node host forwarding service over the given guest and host address ranges. + pub fn start( + mmap: &MemoryMapping, + gpu_guest_address: u64, + guest_mem: GuestMemory, + ) -> RenderNodeHost { + // Render node forward library need to do address translation between host user space + // address and guest physical address. We could call Rust function from C library. But + // since it's actually a linear mapping now, we just pass the host start address to + // render node forward library. We need two start address here since there would be a + // hole below 4G if guest memory size is bigger than 4G. + + let host_start_addr = guest_mem.get_host_address(GuestAddress(0)).unwrap(); + let host_4g_addr = if guest_mem.memory_size() > FOUR_GB { + guest_mem.get_host_address(GuestAddress(FOUR_GB)).unwrap() + } else { + host_start_addr + }; + // Safe because only valid addresses are given. + unsafe { + start_render_node_host( + mmap.as_ptr(), + gpu_guest_address, + mmap.size() as u64, + host_start_addr, + host_4g_addr, + ) + } + RenderNodeHost { guest_mem } + } +} diff --git a/src/linux.rs b/src/linux.rs index ee659cb5b2..e7583a4393 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -41,6 +41,8 @@ use sys_util::{ FlockOperation, GuestMemory, Killable, PollContext, PollToken, SignalFd, Terminal, TimerFd, SIGRTMIN, }; +#[cfg(feature = "gpu-forward")] +use sys_util::{GuestAddress, MemoryMapping, Protection}; use vhost; use vm_control::{VmRequest, VmResponse, VmRunMode}; @@ -53,8 +55,17 @@ use aarch64::AArch64 as Arch; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use x86_64::X8664arch as Arch; +#[cfg(feature = "gpu-forward")] +extern crate render_node_forward; +#[cfg(feature = "gpu-forward")] +use self::render_node_forward::*; +#[cfg(not(feature = "gpu-forward"))] +type RenderNodeHost = (); + #[derive(Debug)] pub enum Error { + AddGpuDeviceMemory(sys_util::Error), + AllocateGpuDeviceAddress, BalloonDeviceNew(virtio::BalloonError), BlockDeviceNew(sys_util::Error), BlockSignal(sys_util::signal::Error), @@ -101,6 +112,8 @@ pub enum Error { RegisterRng(arch::DeviceRegistrationError), RegisterSignalHandler(sys_util::Error), RegisterWayland(arch::DeviceRegistrationError), + ReserveGpuMemory(sys_util::MmapError), + ReserveMemory(sys_util::Error), ResetTimerFd(sys_util::Error), RngDeviceNew(virtio::RngError), SettingGidMap(io_jail::Error), @@ -120,6 +133,8 @@ impl Display for Error { use self::Error::*; match self { + AddGpuDeviceMemory(e) => write!(f, "failed to add gpu device memory: {}", e), + AllocateGpuDeviceAddress => write!(f, "failed to allocate gpu device guest address"), BalloonDeviceNew(e) => write!(f, "failed to create balloon: {}", e), BlockDeviceNew(e) => write!(f, "failed to create block device: {}", e), BlockSignal(e) => write!(f, "failed to block signal: {}", e), @@ -181,6 +196,8 @@ impl Display for Error { RegisterRng(e) => write!(f, "error registering rng device: {}", e), RegisterSignalHandler(e) => write!(f, "error registering signal handler: {}", e), RegisterWayland(e) => write!(f, "error registering wayland device: {}", e), + ReserveGpuMemory(e) => write!(f, "failed to reserve gpu memory: {}", e), + ReserveMemory(e) => write!(f, "failed to reserve memory: {}", e), ResetTimerFd(e) => write!(f, "failed to reset timerfd: {}", e), RngDeviceNew(e) => write!(f, "failed to set up rng: {}", e), SettingGidMap(e) => write!(f, "error setting GID map: {}", e), @@ -1115,6 +1132,41 @@ pub fn run_config(cfg: Config) -> Result<()> { ) }) .map_err(Error::BuildVm)?; + + let _render_node_host = (); + #[cfg(feature = "gpu-forward")] + let (_render_node_host, linux) = { + // Rebinds linux as mutable. + let mut linux = linux; + + // Reserve memory range for GPU buffer allocation in advance to bypass region count + // limitation. We use mremap/MAP_FIXED later to make sure GPU buffers fall into this range. + let gpu_mmap = + MemoryMapping::new_protection(RENDER_NODE_HOST_SIZE as usize, Protection::none()) + .map_err(Error::ReserveGpuMemory)?; + + // Put the non-accessible memory map into device memory so that no other devices use that + // guest address space. + let gpu_addr = linux + .resources + .allocate_device_addresses(RENDER_NODE_HOST_SIZE) + .ok_or(Error::AllocateGpuDeviceAddress)?; + + let host = RenderNodeHost::start(&gpu_mmap, gpu_addr, linux.vm.get_memory().clone()); + + // Makes the gpu memory accessible at allocated address. + linux + .vm + .add_device_memory( + GuestAddress(gpu_addr), + gpu_mmap, + /* read_only = */ false, + /* log_dirty_pages = */ false, + ) + .map_err(Error::AddGpuDeviceMemory)?; + (host, linux) + }; + run_control( linux, control_server_socket, @@ -1122,6 +1174,7 @@ pub fn run_config(cfg: Config) -> Result<()> { balloon_host_socket, &disk_host_sockets, sigchld_fd, + _render_node_host, ) } @@ -1132,6 +1185,7 @@ fn run_control( balloon_host_socket: UnixSeqpacket, disk_host_sockets: &[MsgSocket], sigchld_fd: SignalFd, + _render_node_host: RenderNodeHost, ) -> Result<()> { // Paths to get the currently available memory and the low memory threshold. const LOWMEM_MARGIN: &str = "/sys/kernel/mm/chromeos-low_mem/margin";