Move gpu allocator to resources

Combine GPU buffer allocation with the system resource allocator making
life easier as only one allocator needs to get passed to the execute
function.

Change-Id: I199eb0fd6b99b629aaec1ae3295e8a1942da5309
Signed-off-by: Dylan Reid <dgreid@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1099856
This commit is contained in:
Dylan Reid 2018-06-07 15:42:41 -07:00 committed by chrome-bot
parent 20d71f8928
commit 228e4a6a91
14 changed files with 160 additions and 129 deletions

3
Cargo.lock generated
View file

@ -104,6 +104,7 @@ dependencies = [
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
"net_sys 0.1.0",
"net_util 0.1.0",
"resources 0.1.0",
"sys_util 0.1.0",
"vhost 0.1.0",
"virtio_sys 0.1.0",
@ -302,6 +303,8 @@ name = "resources"
version = "0.1.0"
dependencies = [
"data_model 0.1.0",
"gpu_buffer 0.1.0",
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
"sys_util 0.1.0",
]

View file

@ -12,7 +12,7 @@ panic = 'abort'
[features]
plugin = ["plugin_proto", "crosvm_plugin", "protobuf"]
default-no-sandbox = []
wl-dmabuf = ["devices/wl-dmabuf", "gpu_buffer"]
wl-dmabuf = ["devices/wl-dmabuf", "gpu_buffer", "resources/wl-dmabuf"]
[dependencies]
arch = { path = "arch" }

View file

@ -205,12 +205,12 @@ impl arch::LinuxArch for AArch64 {
}
/// Returns a system resource allocator.
fn get_resource_allocator(mem_size: u64) -> SystemAllocator {
fn get_resource_allocator(mem_size: u64, gpu_allocation: bool) -> SystemAllocator {
let device_addr_start = Self::get_base_dev_pfn(mem_size) * sys_util::pagesize() as u64;
AddressRanges::new()
.add_device_addresses(device_addr_start, u64::max_value() - device_addr_start)
.add_mmio_addresses(AARCH64_MMIO_BASE, 0x10000)
.create_allocator(AARCH64_IRQ_BASE).unwrap()
.create_allocator(AARCH64_IRQ_BASE, gpu_allocation).unwrap()
}
/// This adds any early platform devices for this architecture.

View file

@ -87,7 +87,7 @@ pub trait LinuxArch {
fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline;
/// Returns a system resource allocator.
fn get_resource_allocator(mem_size: u64) -> SystemAllocator;
fn get_resource_allocator(mem_size: u64, gpu_allocation: bool) -> SystemAllocator;
/// Sets up the IO bus for this platform
///

View file

@ -13,6 +13,7 @@ libc = "*"
io_jail = { path = "../io_jail" }
net_sys = { path = "../net_sys" }
net_util = { path = "../net_util" }
resources = { path = "../resources" }
sys_util = { path = "../sys_util" }
vhost = { path = "../vhost" }
virtio_sys = { path = "../virtio_sys" }

View file

@ -10,6 +10,7 @@ extern crate io_jail;
extern crate libc;
extern crate net_sys;
extern crate net_util;
extern crate resources;
#[macro_use]
extern crate sys_util;
extern crate vhost;

View file

@ -58,13 +58,14 @@ use libc::{dup, EBADF, EINVAL};
use data_model::*;
use data_model::VolatileMemoryError;
use resources::GpuMemoryDesc;
use sys_util::{Error, Result, EventFd, Scm, SharedMemory, GuestAddress, GuestMemory,
GuestMemoryError, PollContext, PollToken, FileFlags, pipe, round_up_to_page_size};
#[cfg(feature = "wl-dmabuf")]
use sys_util::ioctl_with_ref;
use vm_control::{VmControlError, VmRequest, VmResponse, MaybeOwnedFd, GpuMemoryDesc};
use vm_control::{VmControlError, VmRequest, VmResponse, MaybeOwnedFd};
use super::{VirtioDevice, Queue, DescriptorChain, INTERRUPT_STATUS_USED_RING, TYPE_WL};
const VIRTWL_SEND_MAX_ALLOCS: usize = 28;

View file

@ -3,6 +3,11 @@ name = "resources"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
[features]
wl-dmabuf = ["gpu_buffer"]
[dependencies]
data_model = { path = "../data_model" }
gpu_buffer = { path = "../gpu_buffer", optional = true }
libc = "*"
sys_util = { path = "../sys_util" }

View file

@ -0,0 +1,106 @@
// Copyright 2018 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.
use std::fs::File;
#[cfg(feature = "wl-dmabuf")]
use libc::EINVAL;
#[cfg(feature = "wl-dmabuf")]
use gpu_buffer;
use sys_util;
#[derive(Debug)]
pub enum GpuAllocatorError {
OpenGpuBufferDevice,
CreateGpuBufferDevice,
}
/// 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
/// 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)
-> sys_util::Result<(File, GpuMemoryDesc)>;
}
#[cfg(feature = "wl-dmabuf")]
pub 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, GpuMemoryDesc)>
{
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 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)),
};
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))
}
}
#[cfg(feature = "wl-dmabuf")]
pub fn create_gpu_memory_allocator() -> Result<Option<Box<GpuMemoryAllocator>>, GpuAllocatorError> {
let undesired: &[&str] = &["vgem", "pvr"];
let fd = gpu_buffer::rendernode::open_device(undesired)
.map_err(|_| GpuAllocatorError::OpenGpuBufferDevice)?;
let device = gpu_buffer::Device::new(fd)
.map_err(|_| GpuAllocatorError::CreateGpuBufferDevice)?;
Ok(Some(Box::new(GpuBufferDevice { device })))
}
#[cfg(not(feature = "wl-dmabuf"))]
pub fn create_gpu_memory_allocator() -> Result<Option<Box<GpuMemoryAllocator>>, GpuAllocatorError> {
Ok(None)
}

View file

@ -4,10 +4,15 @@
//! Manages system resources that can be allocated to VMs and their devices.
#[cfg(feature = "wl-dmabuf")]
extern crate gpu_buffer;
extern crate libc;
extern crate sys_util;
mod address_allocator;
mod gpu_allocator;
mod system_allocator;
pub use address_allocator::AddressAllocator;
pub use gpu_allocator::{GpuMemoryAllocator, GpuMemoryDesc, GpuMemoryPlaneDesc};
pub use system_allocator::{AddressRanges, SystemAllocator};

View file

@ -3,6 +3,7 @@
// found in the LICENSE file.
use address_allocator::AddressAllocator;
use gpu_allocator::{self, GpuMemoryAllocator};
use sys_util::pagesize;
/// Manages allocating system resources such as address space and interrupt numbers.
@ -21,11 +22,11 @@ use sys_util::pagesize;
/// assert_eq!(a.allocate_device_addresses(0x100), Some(0x10000000));
/// }
/// ```
#[derive(Debug, Eq, PartialEq)]
pub struct SystemAllocator {
io_address_space: Option<AddressAllocator>,
device_address_space: AddressAllocator,
mmio_address_space: AddressAllocator,
gpu_allocator: Option<Box<GpuMemoryAllocator>>,
next_irq: u32,
}
@ -40,10 +41,12 @@ impl SystemAllocator {
/// * `dev_size` - The size of device memory.
/// * `mmio_base` - The starting address of MMIO space.
/// * `mmio_size` - The size of MMIO space.
/// * `create_gpu_allocator` - If true, enable gpu memory allocation.
/// * `first_irq` - The first irq number to give out.
fn new(io_base: Option<u64>, io_size: Option<u64>,
dev_base: u64, dev_size: u64,
mmio_base: u64, mmio_size: u64,
create_gpu_allocator: bool,
first_irq: u32) -> Option<Self> {
let page_size = pagesize() as u64;
Some(SystemAllocator {
@ -54,6 +57,11 @@ impl SystemAllocator {
},
device_address_space: AddressAllocator::new(dev_base, dev_size, Some(page_size))?,
mmio_address_space: AddressAllocator::new(mmio_base, mmio_size, Some(page_size))?,
gpu_allocator: if create_gpu_allocator {
gpu_allocator::create_gpu_memory_allocator().ok()?
} else {
None
},
next_irq: first_irq,
})
}
@ -82,6 +90,11 @@ impl SystemAllocator {
pub fn allocate_mmio_addresses(&mut self, size: u64) -> Option<u64> {
self.mmio_address_space.allocate(size)
}
/// Gets an allocator to be used for GPU memory.
pub fn gpu_memory_allocator(&self) -> Option<&GpuMemoryAllocator> {
self.gpu_allocator.as_ref().map(|v| v.as_ref())
}
}
/// Used to build a system address map for use in creating a `SystemAllocator`.
@ -124,10 +137,12 @@ impl AddressRanges {
self
}
pub fn create_allocator(&self, first_irq: u32) -> Option<SystemAllocator> {
pub fn create_allocator(&self, first_irq: u32,
gpu_allocation: bool) -> Option<SystemAllocator> {
SystemAllocator::new(self.io_base, self.io_size,
self.device_base?, self.device_size?,
self.mmio_base?, self.mmio_size?,
gpu_allocation,
first_irq)
}
}

View file

@ -18,8 +18,6 @@ use std::thread::JoinHandle;
use libc;
use libc::c_int;
#[cfg(feature = "wl-dmabuf")]
use libc::EINVAL;
use devices;
use io_jail::{self, Minijail};
@ -31,9 +29,7 @@ use sys_util::*;
use sys_util;
use resources::SystemAllocator;
use vhost;
use vm_control::{VmRequest, GpuMemoryAllocator, GpuMemoryPlaneDesc, GpuMemoryDesc};
#[cfg(feature = "wl-dmabuf")]
use gpu_buffer;
use vm_control::VmRequest;
use Config;
use DiskType;
@ -53,7 +49,6 @@ 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),
@ -72,7 +67,6 @@ pub enum Error {
NetDeviceNew(devices::virtio::NetError),
NoVarEmpty,
OpenKernel(PathBuf, io::Error),
OpenGpuBufferDevice,
PollContextAdd(sys_util::Error),
QcowDeviceCreate(qcow::Error),
RegisterBalloon(MmioRegisterError),
@ -107,7 +101,6 @@ 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)
@ -132,7 +125,6 @@ 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)
@ -672,65 +664,6 @@ 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, GpuMemoryDesc)> {
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 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)),
};
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))
}
}
#[cfg(feature = "wl-dmabuf")]
fn create_gpu_memory_allocator() -> Result<Option<Box<GpuMemoryAllocator>>> {
let undesired: &[&str] = &["vgem", "pvr"];
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(mut vm: Vm,
control_sockets: Vec<UnlinkUnixDatagram>,
mut resources: SystemAllocator,
@ -740,8 +673,7 @@ fn run_control(mut vm: Vm,
kill_signaled: Arc<AtomicBool>,
vcpu_handles: Vec<JoinHandle<()>>,
balloon_host_socket: UnixDatagram,
_irqchip_fd: Option<File>,
gpu_memory_allocator: Option<Box<GpuMemoryAllocator>>)
_irqchip_fd: Option<File>)
-> Result<()> {
const MAX_VM_FD_RECV: usize = 1;
@ -830,10 +762,7 @@ fn run_control(mut vm: Vm,
request.execute(&mut vm,
&mut resources,
&mut running,
&balloon_host_socket,
gpu_memory_allocator.as_ref().map(|v| {
v.as_ref()
}));
&balloon_host_socket);
if let Err(e) = response.send(&mut scm, socket.as_ref()) {
error!("failed to send VmResponse: {:?}", e);
}
@ -915,7 +844,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
let mem_size = cfg.memory.unwrap_or(256) << 20;
let mut resources = Arch::get_resource_allocator(mem_size as u64);
let mut resources = Arch::get_resource_allocator(mem_size as u64, cfg.wayland_dmabuf);
let mem = Arch::setup_memory(mem_size as u64).map_err(|e| Error::CreateGuestMemory(e))?;
let kvm = Kvm::new().map_err(Error::CreateKvm)?;
let mut vm = Arch::create_vm(&kvm, mem.clone()).map_err(|e| Error::CreateVm(e))?;
@ -946,12 +875,6 @@ pub fn run_config(cfg: Config) -> Result<()> {
balloon_device_socket,
&mut resources)?;
let gpu_memory_allocator = if cfg.wayland_dmabuf {
create_gpu_memory_allocator()?
} else {
None
};
for param in &cfg.params {
cmdline.insert_str(&param).map_err(Error::Cmdline)?;
}
@ -988,6 +911,5 @@ pub fn run_config(cfg: Config) -> Result<()> {
kill_signaled,
vcpu_handles,
balloon_host_socket,
irq_chip,
gpu_memory_allocator)
irq_chip)
}

View file

@ -28,7 +28,7 @@ use libc::{ERANGE, EINVAL, ENODEV};
use byteorder::{LittleEndian, WriteBytesExt};
use data_model::{DataInit, Le32, Le64, VolatileMemory};
use sys_util::{EventFd, Result, Error as SysError, MmapError, MemoryMapping, Scm, GuestAddress};
use resources::SystemAllocator;
use resources::{GpuMemoryDesc, GpuMemoryPlaneDesc, SystemAllocator};
use kvm::{IoeventAddress, Vm};
#[derive(Debug, PartialEq)]
@ -130,34 +130,6 @@ fn register_memory(vm: &mut Vm, allocator: &mut SystemAllocator,
Ok((addr >> 12, 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
/// 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, GpuMemoryDesc)>;
}
impl VmRequest {
/// Receive a `VmRequest` from the given socket.
///
@ -247,8 +219,7 @@ impl VmRequest {
/// `VmResponse` with the intended purpose of sending the response back over the socket that
/// received this `VmRequest`.
pub fn execute(&self, vm: &mut Vm, sys_allocator: &mut SystemAllocator, running: &mut bool,
balloon_host_socket: &UnixDatagram,
gpu_memory_allocator: Option<&GpuMemoryAllocator>) -> VmResponse {
balloon_host_socket: &UnixDatagram) -> VmResponse {
*running = true;
match self {
&VmRequest::Exit => {
@ -289,14 +260,15 @@ impl VmRequest {
}
}
&VmRequest::AllocateAndRegisterGpuMemory {width, height, format} => {
let gpu_allocator = match gpu_memory_allocator {
Some(v) => v,
let (mut fd, desc) = match sys_allocator.gpu_memory_allocator() {
Some(gpu_allocator) => {
match gpu_allocator.allocate(width, height, format) {
Ok(v) => v,
Err(e) => return VmResponse::Err(e),
}
}
None => return VmResponse::Err(SysError::new(ENODEV)),
};
let (mut fd, desc) = match gpu_allocator.allocate(width, height, format) {
Ok(v) => v,
Err(e) => return VmResponse::Err(e),
};
// Determine size of buffer using 0 byte seek from end. This is preferred over
// `stride * height` as it's not limited to packed pixel formats.
let size = match fd.seek(SeekFrom::End(0)) {

View file

@ -317,14 +317,14 @@ impl arch::LinuxArch for X8664arch {
}
/// Returns a system resource allocator.
fn get_resource_allocator(mem_size: u64) -> SystemAllocator {
fn get_resource_allocator(mem_size: u64, gpu_allocation: bool) -> SystemAllocator {
const MMIO_BASE: u64 = 0xe0000000;
let device_addr_start = Self::get_base_dev_pfn(mem_size) * sys_util::pagesize() as u64;
AddressRanges::new()
.add_io_addresses(0xc000, 0x10000)
.add_mmio_addresses(MMIO_BASE, 0x10000)
.add_device_addresses(device_addr_start, u64::max_value() - device_addr_start)
.create_allocator(X86_64_IRQ_BASE).unwrap()
.create_allocator(X86_64_IRQ_BASE, gpu_allocation).unwrap()
}
/// Sets up the IO bus for this platform