gpu: Allow more than one resource bridge socket

Currently the wayland device accesses buffers allocated by the gpu
device via a dedicated socket connection.  Upcoming virtual devices like
vdec and camera will also need access to these buffers.  Modify the gpu
device so that it can process requests on multiple resource_bridge
sockets.

Each future device that needs access to gpu device buffers should create
a new resource bridge socket pair and add it to the list of sockets that
the gpu device monitors.

The actual interface between the devices is unchanged.

BUG=b:133381367
TEST=run glxgears in a crostini container with and without gpu enabled

Change-Id: I58693881945965071a53653bf4f86681725267d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1652876
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Chirantan Ekbote <chirantan@chromium.org>
Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
Auto-Submit: Chirantan Ekbote <chirantan@chromium.org>
This commit is contained in:
Chirantan Ekbote 2019-06-11 21:50:46 +09:00 committed by Commit Bot
parent cc91fc8252
commit dd11d43473
2 changed files with 51 additions and 34 deletions

View file

@ -8,7 +8,7 @@ mod protocol;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::i64; use std::i64;
use std::mem::size_of; use std::mem::{self, size_of};
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::rc::Rc; use std::rc::Rc;
@ -474,7 +474,7 @@ struct Worker {
ctrl_evt: EventFd, ctrl_evt: EventFd,
cursor_queue: Queue, cursor_queue: Queue,
cursor_evt: EventFd, cursor_evt: EventFd,
resource_bridge: Option<ResourceResponseSocket>, resource_bridges: Vec<ResourceResponseSocket>,
kill_evt: EventFd, kill_evt: EventFd,
state: Frontend, state: Frontend,
} }
@ -492,9 +492,9 @@ impl Worker {
CtrlQueue, CtrlQueue,
CursorQueue, CursorQueue,
Display, Display,
ResourceBridge,
InterruptResample, InterruptResample,
Kill, Kill,
ResourceBridge { index: usize },
} }
let poll_ctx: PollContext<Token> = match PollContext::new() let poll_ctx: PollContext<Token> = match PollContext::new()
@ -517,12 +517,14 @@ impl Worker {
} }
}; };
if let Some(resource_bridge) = &self.resource_bridge { for (index, bridge) in self.resource_bridges.iter().enumerate() {
if let Err(e) = poll_ctx.add(resource_bridge, Token::ResourceBridge) { if let Err(e) = poll_ctx.add(bridge, Token::ResourceBridge { index }) {
error!("failed to add resource bridge to PollContext: {}", e); error!("failed to add resource bridge to PollContext: {}", e);
} }
} }
// Declare this outside the loop so we don't keep allocating and freeing the vector.
let mut process_resource_bridge = Vec::with_capacity(self.resource_bridges.len());
'poll: loop { 'poll: loop {
// If there are outstanding fences, wake up early to poll them. // If there are outstanding fences, wake up early to poll them.
let duration = if !self.state.fence_descriptors.is_empty() { let duration = if !self.state.fence_descriptors.is_empty() {
@ -539,7 +541,11 @@ impl Worker {
} }
}; };
let mut signal_used = false; let mut signal_used = false;
let mut process_resource_bridge = false;
// Clear the old values and re-initialize with false.
process_resource_bridge.clear();
process_resource_bridge.resize(self.resource_bridges.len(), false);
for event in events.iter_readable() { for event in events.iter_readable() {
match event.token() { match event.token() {
Token::CtrlQueue => { Token::CtrlQueue => {
@ -558,7 +564,7 @@ impl Worker {
let _ = self.exit_evt.write(1); let _ = self.exit_evt.write(1);
} }
} }
Token::ResourceBridge => process_resource_bridge = true, Token::ResourceBridge { index } => process_resource_bridge[index] = true,
Token::InterruptResample => { Token::InterruptResample => {
let _ = self.interrupt_resample_evt.read(); let _ = self.interrupt_resample_evt.read();
if self.interrupt_status.load(Ordering::SeqCst) != 0 { if self.interrupt_status.load(Ordering::SeqCst) != 0 {
@ -587,9 +593,11 @@ impl Worker {
// Process the entire control queue before the resource bridge in case a resource is // Process the entire control queue before the resource bridge in case a resource is
// created or destroyed by the control queue. Processing the resource bridge first may // created or destroyed by the control queue. Processing the resource bridge first may
// lead to a race condition. // lead to a race condition.
if process_resource_bridge { for (bridge, &should_process) in
if let Some(resource_bridge) = &self.resource_bridge { self.resource_bridges.iter().zip(&process_resource_bridge)
self.state.process_resource_bridge(resource_bridge); {
if should_process {
self.state.process_resource_bridge(bridge);
} }
} }
@ -604,7 +612,7 @@ pub struct Gpu {
config_event: bool, config_event: bool,
exit_evt: EventFd, exit_evt: EventFd,
gpu_device_socket: Option<VmMemoryControlRequestSocket>, gpu_device_socket: Option<VmMemoryControlRequestSocket>,
resource_bridge: Option<ResourceResponseSocket>, resource_bridges: Vec<ResourceResponseSocket>,
kill_evt: Option<EventFd>, kill_evt: Option<EventFd>,
wayland_socket_path: PathBuf, wayland_socket_path: PathBuf,
} }
@ -613,14 +621,14 @@ impl Gpu {
pub fn new<P: AsRef<Path>>( pub fn new<P: AsRef<Path>>(
exit_evt: EventFd, exit_evt: EventFd,
gpu_device_socket: Option<VmMemoryControlRequestSocket>, gpu_device_socket: Option<VmMemoryControlRequestSocket>,
resource_bridge: Option<ResourceResponseSocket>, resource_bridges: Vec<ResourceResponseSocket>,
wayland_socket_path: P, wayland_socket_path: P,
) -> Gpu { ) -> Gpu {
Gpu { Gpu {
config_event: false, config_event: false,
exit_evt, exit_evt,
gpu_device_socket, gpu_device_socket,
resource_bridge, resource_bridges,
kill_evt: None, kill_evt: None,
wayland_socket_path: wayland_socket_path.as_ref().to_path_buf(), wayland_socket_path: wayland_socket_path.as_ref().to_path_buf(),
} }
@ -664,8 +672,8 @@ impl VirtioDevice for Gpu {
} }
keep_fds.push(self.exit_evt.as_raw_fd()); keep_fds.push(self.exit_evt.as_raw_fd());
if let Some(resource_bridge) = &self.resource_bridge { for bridge in &self.resource_bridges {
keep_fds.push(resource_bridge.as_raw_fd()); keep_fds.push(bridge.as_raw_fd());
} }
keep_fds keep_fds
} }
@ -739,7 +747,7 @@ impl VirtioDevice for Gpu {
}; };
self.kill_evt = Some(self_kill_evt); self.kill_evt = Some(self_kill_evt);
let resource_bridge = self.resource_bridge.take(); let resource_bridges = mem::replace(&mut self.resource_bridges, Vec::new());
let ctrl_queue = queues.remove(0); let ctrl_queue = queues.remove(0);
let ctrl_evt = queue_evts.remove(0); let ctrl_evt = queue_evts.remove(0);
@ -802,7 +810,7 @@ impl VirtioDevice for Gpu {
ctrl_evt, ctrl_evt,
cursor_queue, cursor_queue,
cursor_evt, cursor_evt,
resource_bridge, resource_bridges,
kill_evt, kill_evt,
state: Frontend::new(Backend::new( state: Frontend::new(Backend::new(
device, device,

View file

@ -552,7 +552,7 @@ fn create_gpu_device(
cfg: &Config, cfg: &Config,
exit_evt: &EventFd, exit_evt: &EventFd,
gpu_device_socket: VmMemoryControlRequestSocket, gpu_device_socket: VmMemoryControlRequestSocket,
gpu_socket: virtio::resource_bridge::ResourceResponseSocket, gpu_sockets: Vec<virtio::resource_bridge::ResourceResponseSocket>,
wayland_socket_path: &Path, wayland_socket_path: &Path,
) -> DeviceResult { ) -> DeviceResult {
let jailed_wayland_path = Path::new("/wayland-0"); let jailed_wayland_path = Path::new("/wayland-0");
@ -560,7 +560,7 @@ fn create_gpu_device(
let dev = virtio::Gpu::new( let dev = virtio::Gpu::new(
exit_evt.try_clone().map_err(Error::CloneEventFd)?, exit_evt.try_clone().map_err(Error::CloneEventFd)?,
Some(gpu_device_socket), Some(gpu_device_socket),
Some(gpu_socket), gpu_sockets,
if cfg.sandbox { if cfg.sandbox {
&jailed_wayland_path &jailed_wayland_path
} else { } else {
@ -835,36 +835,45 @@ fn create_virtio_devices(
} }
#[cfg_attr(not(feature = "gpu"), allow(unused_mut))] #[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
let mut resource_bridge_wl_socket = None::<virtio::resource_bridge::ResourceRequestSocket>; let mut resource_bridges = Vec::<virtio::resource_bridge::ResourceResponseSocket>::new();
if let Some(wayland_socket_path) = cfg.wayland_socket_path.as_ref() {
#[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
let mut wl_resource_bridge = None::<virtio::resource_bridge::ResourceRequestSocket>;
#[cfg(feature = "gpu")]
{
if cfg.gpu {
let (wl_socket, gpu_socket) =
virtio::resource_bridge::pair().map_err(Error::CreateSocket)?;
resource_bridges.push(gpu_socket);
wl_resource_bridge = Some(wl_socket);
}
}
devs.push(create_wayland_device(
cfg,
wayland_socket_path,
wayland_device_socket,
wl_resource_bridge,
)?);
}
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
{ {
if cfg.gpu { if cfg.gpu {
if let Some(wayland_socket_path) = &cfg.wayland_socket_path { if let Some(wayland_socket_path) = &cfg.wayland_socket_path {
let (wl_socket, gpu_socket) =
virtio::resource_bridge::pair().map_err(Error::CreateSocket)?;
resource_bridge_wl_socket = Some(wl_socket);
devs.push(create_gpu_device( devs.push(create_gpu_device(
cfg, cfg,
_exit_evt, _exit_evt,
gpu_device_socket, gpu_device_socket,
gpu_socket, resource_bridges,
wayland_socket_path, wayland_socket_path,
)?); )?);
} }
} }
} }
if let Some(wayland_socket_path) = cfg.wayland_socket_path.as_ref() {
devs.push(create_wayland_device(
cfg,
wayland_socket_path,
wayland_device_socket,
resource_bridge_wl_socket,
)?);
}
if let Some(cid) = cfg.cid { if let Some(cid) = cfg.cid {
devs.push(create_vhost_vsock_device(cfg, cid, mem)?); devs.push(create_vhost_vsock_device(cfg, cid, mem)?);
} }