mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-05 10:10:41 +00:00
devices: use nested PollContext in wayland device
The wl device was the last user of the old Poller. BUG=chromium:816692 TEST=run wayland under crosvm Change-Id: I6c1c1db2774a6e783b7bd1109288328d75ad2223 Reviewed-on: https://chromium-review.googlesource.com/1000102 Commit-Ready: Zach Reizner <zachr@chromium.org> Tested-by: Zach Reizner <zachr@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
parent
c1b74eb8b1
commit
d86e698ec8
2 changed files with 96 additions and 74 deletions
|
@ -46,12 +46,13 @@ use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use data_model::*;
|
use data_model::*;
|
||||||
use data_model::VolatileMemoryError;
|
use data_model::VolatileMemoryError;
|
||||||
|
|
||||||
use sys_util::{Error, Result, EventFd, Poller, Pollable, Scm, SharedMemory, GuestAddress,
|
use sys_util::{Error, Result, EventFd, Scm, SharedMemory, GuestAddress, GuestMemory,
|
||||||
GuestMemory, GuestMemoryError, FileFlags, pipe};
|
GuestMemoryError, PollContext, PollToken, FileFlags, pipe};
|
||||||
|
|
||||||
use vm_control::{VmControlError, VmRequest, VmResponse, MaybeOwnedFd};
|
use vm_control::{VmControlError, VmRequest, VmResponse, MaybeOwnedFd};
|
||||||
use super::{VirtioDevice, Queue, DescriptorChain, INTERRUPT_STATUS_USED_RING, TYPE_WL};
|
use super::{VirtioDevice, Queue, DescriptorChain, INTERRUPT_STATUS_USED_RING, TYPE_WL};
|
||||||
|
@ -78,11 +79,6 @@ const VIRTIO_WL_VFD_MAP: u32 = 0x2;
|
||||||
const VIRTIO_WL_VFD_CONTROL: u32 = 0x4;
|
const VIRTIO_WL_VFD_CONTROL: u32 = 0x4;
|
||||||
const VIRTIO_WL_F_TRANS_FLAGS: u32 = 0x01;
|
const VIRTIO_WL_F_TRANS_FLAGS: u32 = 0x01;
|
||||||
|
|
||||||
const Q_IN: u32 = 0;
|
|
||||||
const Q_OUT: u32 = 1;
|
|
||||||
const KILL: u32 = 2;
|
|
||||||
const VFD_BASE_TOKEN: u32 = 0x100;
|
|
||||||
|
|
||||||
const QUEUE_SIZE: u16 = 16;
|
const QUEUE_SIZE: u16 = 16;
|
||||||
const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE, QUEUE_SIZE];
|
const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE, QUEUE_SIZE];
|
||||||
|
|
||||||
|
@ -278,6 +274,7 @@ enum WlError {
|
||||||
WritePipe(io::Error),
|
WritePipe(io::Error),
|
||||||
RecvVfd(Error),
|
RecvVfd(Error),
|
||||||
ReadPipe(io::Error),
|
ReadPipe(io::Error),
|
||||||
|
PollContextAdd(Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for WlError {
|
impl fmt::Display for WlError {
|
||||||
|
@ -303,6 +300,7 @@ impl error::Error for WlError {
|
||||||
WlError::WritePipe(_) => "Failed to write to a pipe",
|
WlError::WritePipe(_) => "Failed to write to a pipe",
|
||||||
WlError::RecvVfd(_) => "Failed to recv on a socket",
|
WlError::RecvVfd(_) => "Failed to recv on a socket",
|
||||||
WlError::ReadPipe(_) => "Failed to read a pipe",
|
WlError::ReadPipe(_) => "Failed to read a pipe",
|
||||||
|
WlError::PollContextAdd(_) => "Failed to listen to FD on poll context",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -591,7 +589,7 @@ impl WlVfd {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The FD that gets sent if this VFD is sent over a socket.
|
// The FD that gets sent if this VFD is sent over a socket.
|
||||||
fn fd(&self) -> Option<RawFd> {
|
fn send_fd(&self) -> Option<RawFd> {
|
||||||
self.guest_shared_memory
|
self.guest_shared_memory
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|&(_, ref fd)| fd.as_raw_fd())
|
.map(|&(_, ref fd)| fd.as_raw_fd())
|
||||||
|
@ -599,6 +597,17 @@ impl WlVfd {
|
||||||
.or(self.remote_pipe.as_ref().map(|p| p.as_raw_fd()))
|
.or(self.remote_pipe.as_ref().map(|p| p.as_raw_fd()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The FD that is used for polling for events on this VFD.
|
||||||
|
fn poll_fd(&self) -> Option<&AsRawFd> {
|
||||||
|
self.socket
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| s as &AsRawFd)
|
||||||
|
.or(self.local_pipe
|
||||||
|
.as_ref()
|
||||||
|
.map(|&(_, ref p)| p as &AsRawFd))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Sends data/files from the guest to the host over this VFD.
|
// Sends data/files from the guest to the host over this VFD.
|
||||||
fn send(&mut self, scm: &mut Scm, fds: &[RawFd], data: VolatileSlice) -> WlResult<WlResp> {
|
fn send(&mut self, scm: &mut Scm, fds: &[RawFd], data: VolatileSlice) -> WlResult<WlResp> {
|
||||||
if let Some(ref socket) = self.socket {
|
if let Some(ref socket) = self.socket {
|
||||||
|
@ -687,6 +696,7 @@ struct WlState {
|
||||||
wayland_path: PathBuf,
|
wayland_path: PathBuf,
|
||||||
vm: VmRequester,
|
vm: VmRequester,
|
||||||
use_transition_flags: bool,
|
use_transition_flags: bool,
|
||||||
|
poll_ctx: PollContext<u32>,
|
||||||
vfds: Map<u32, WlVfd>,
|
vfds: Map<u32, WlVfd>,
|
||||||
next_vfd_id: u32,
|
next_vfd_id: u32,
|
||||||
scm: Scm,
|
scm: Scm,
|
||||||
|
@ -701,6 +711,7 @@ impl WlState {
|
||||||
WlState {
|
WlState {
|
||||||
wayland_path: wayland_path,
|
wayland_path: wayland_path,
|
||||||
vm: VmRequester::new(vm_socket),
|
vm: VmRequester::new(vm_socket),
|
||||||
|
poll_ctx: PollContext::new().expect("failed to create PollContext"),
|
||||||
use_transition_flags,
|
use_transition_flags,
|
||||||
scm: Scm::new(VIRTWL_SEND_MAX_ALLOCS),
|
scm: Scm::new(VIRTWL_SEND_MAX_ALLOCS),
|
||||||
vfds: Map::new(),
|
vfds: Map::new(),
|
||||||
|
@ -734,6 +745,9 @@ impl WlState {
|
||||||
} else {
|
} else {
|
||||||
return Ok(WlResp::InvalidFlags);
|
return Ok(WlResp::InvalidFlags);
|
||||||
};
|
};
|
||||||
|
self.poll_ctx
|
||||||
|
.add(vfd.poll_fd().unwrap(), id)
|
||||||
|
.map_err(WlError::PollContextAdd)?;
|
||||||
let resp = WlResp::VfdNew {
|
let resp = WlResp::VfdNew {
|
||||||
id: id,
|
id: id,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
|
@ -791,7 +805,10 @@ impl WlState {
|
||||||
|
|
||||||
match self.vfds.entry(id) {
|
match self.vfds.entry(id) {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
entry.insert(WlVfd::connect(&self.wayland_path)?);
|
let vfd = entry.insert(WlVfd::connect(&self.wayland_path)?);
|
||||||
|
self.poll_ctx
|
||||||
|
.add(vfd.poll_fd().unwrap(), id)
|
||||||
|
.map_err(WlError::PollContextAdd)?;
|
||||||
Ok(WlResp::VfdNew {
|
Ok(WlResp::VfdNew {
|
||||||
id: id,
|
id: id,
|
||||||
flags,
|
flags,
|
||||||
|
@ -804,6 +821,30 @@ impl WlState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_poll_context(&mut self) {
|
||||||
|
let events = match self.poll_ctx.wait_timeout(Duration::from_secs(0)) {
|
||||||
|
Ok(v) => v.to_owned(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed polling for vfd evens: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for event in events.as_ref().iter_readable() {
|
||||||
|
if let Err(e) = self.recv(event.token()) {
|
||||||
|
error!("failed to recv from vfd: {:?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for event in events.as_ref().iter_hungup() {
|
||||||
|
if !event.readable() {
|
||||||
|
if let Err(e) = self.close(event.token()) {
|
||||||
|
warn!("failed to close vfd: {:?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn close(&mut self, vfd_id: u32) -> WlResult<WlResp> {
|
fn close(&mut self, vfd_id: u32) -> WlResult<WlResp> {
|
||||||
let mut to_delete = Set::new();
|
let mut to_delete = Set::new();
|
||||||
for &(dest_vfd_id, ref q) in self.in_queue.iter() {
|
for &(dest_vfd_id, ref q) in self.in_queue.iter() {
|
||||||
|
@ -835,7 +876,7 @@ impl WlState {
|
||||||
for (&id, fd) in vfd_ids[..vfd_count].iter().zip(fds.iter_mut()) {
|
for (&id, fd) in vfd_ids[..vfd_count].iter().zip(fds.iter_mut()) {
|
||||||
match self.vfds.get(&id.into()) {
|
match self.vfds.get(&id.into()) {
|
||||||
Some(vfd) => {
|
Some(vfd) => {
|
||||||
match vfd.fd() {
|
match vfd.send_fd() {
|
||||||
Some(vfd_fd) => *fd = vfd_fd,
|
Some(vfd_fd) => *fd = vfd_fd,
|
||||||
None => return Ok(WlResp::InvalidType),
|
None => return Ok(WlResp::InvalidType),
|
||||||
}
|
}
|
||||||
|
@ -873,8 +914,13 @@ impl WlState {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
for file in self.in_file_queue.drain(..) {
|
for file in self.in_file_queue.drain(..) {
|
||||||
self.vfds
|
let vfd = WlVfd::from_file(self.vm.clone(), file)?;
|
||||||
.insert(self.next_vfd_id, WlVfd::from_file(self.vm.clone(), file)?);
|
if let Some(poll_fd) = vfd.poll_fd() {
|
||||||
|
self.poll_ctx
|
||||||
|
.add(poll_fd, self.next_vfd_id)
|
||||||
|
.map_err(WlError::PollContextAdd)?;
|
||||||
|
}
|
||||||
|
self.vfds.insert(self.next_vfd_id, vfd);
|
||||||
self.in_queue
|
self.in_queue
|
||||||
.push_back((vfd_id, WlRecv::Vfd { id: self.next_vfd_id }));
|
.push_back((vfd_id, WlRecv::Vfd { id: self.next_vfd_id }));
|
||||||
self.next_vfd_id += 1;
|
self.next_vfd_id += 1;
|
||||||
|
@ -994,27 +1040,6 @@ impl WlState {
|
||||||
}
|
}
|
||||||
self.in_queue.pop_front();
|
self.in_queue.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_sockets<'a, F>(&'a self, mut f: F)
|
|
||||||
where F: FnMut(u32, &'a UnixStream)
|
|
||||||
{
|
|
||||||
for (id, socket) in self.vfds
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(&k, v)| v.socket.as_ref().map(|s| (k, s))) {
|
|
||||||
f(id, socket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter_pipes<'a, F>(&'a self, mut f: F)
|
|
||||||
where F: FnMut(u32, &'a File)
|
|
||||||
{
|
|
||||||
for (id, local_pipe) in
|
|
||||||
self.vfds
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(&k, v)| v.local_pipe.as_ref().map(|&(_, ref p)| (k, p))) {
|
|
||||||
f(id, local_pipe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Worker {
|
struct Worker {
|
||||||
|
@ -1057,39 +1082,40 @@ impl Worker {
|
||||||
fn run(&mut self, mut queue_evts: Vec<EventFd>, kill_evt: EventFd) {
|
fn run(&mut self, mut queue_evts: Vec<EventFd>, kill_evt: EventFd) {
|
||||||
let in_queue_evt = queue_evts.remove(0);
|
let in_queue_evt = queue_evts.remove(0);
|
||||||
let out_queue_evt = queue_evts.remove(0);
|
let out_queue_evt = queue_evts.remove(0);
|
||||||
let mut token_vfd_id_map = Map::new();
|
#[derive(PollToken)]
|
||||||
let mut poller = Poller::new(3);
|
enum Token {
|
||||||
'poll: loop {
|
InQueue,
|
||||||
let tokens = {
|
OutQueue,
|
||||||
// TODO(zachr): somehow keep pollables from allocating every loop
|
Kill,
|
||||||
// The capacity is always the 3 static eventfds plus the number of vfd sockets. To
|
State,
|
||||||
// estimate the number of vfd sockets, we use the previous poll's vfd id map size,
|
}
|
||||||
// which was equal to the number of vfd sockets.
|
|
||||||
let mut pollables = Vec::with_capacity(3 + token_vfd_id_map.len());
|
let poll_ctx: PollContext<Token> =
|
||||||
pollables.push((Q_IN, &in_queue_evt as &Pollable));
|
match PollContext::new()
|
||||||
pollables.push((Q_OUT, &out_queue_evt as &Pollable));
|
.and_then(|pc| pc.add(&in_queue_evt, Token::InQueue).and(Ok(pc)))
|
||||||
pollables.push((KILL, &kill_evt as &Pollable));
|
.and_then(|pc| pc.add(&out_queue_evt, Token::OutQueue).and(Ok(pc)))
|
||||||
token_vfd_id_map.clear();
|
.and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
|
||||||
// TODO(zachr): leave these out if there is no Q_IN to use
|
.and_then(|pc| pc.add(&self.state.poll_ctx, Token::State).and(Ok(pc))) {
|
||||||
self.state
|
Ok(pc) => pc,
|
||||||
.iter_sockets(|id, socket| {
|
Err(e) => {
|
||||||
let token = VFD_BASE_TOKEN + token_vfd_id_map.len() as u32;
|
error!("failed creating PollContext: {:?}", e);
|
||||||
token_vfd_id_map.insert(token, id);
|
return;
|
||||||
pollables.push((token, socket));
|
}
|
||||||
});
|
|
||||||
self.state
|
|
||||||
.iter_pipes(|id, pipe| {
|
|
||||||
let token = VFD_BASE_TOKEN + token_vfd_id_map.len() as u32;
|
|
||||||
token_vfd_id_map.insert(token, id);
|
|
||||||
pollables.push((token, pipe));
|
|
||||||
});
|
|
||||||
poller.poll(&pollables[..]).expect("error: failed poll")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
'poll: loop {
|
||||||
let mut signal_used = false;
|
let mut signal_used = false;
|
||||||
for &token in tokens {
|
let events = match poll_ctx.wait() {
|
||||||
match token {
|
Ok(v) => v,
|
||||||
Q_IN => {
|
Err(e) => {
|
||||||
|
error!("failed polling for events: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for event in events.iter() {
|
||||||
|
match event.token() {
|
||||||
|
Token::InQueue => {
|
||||||
let _ = in_queue_evt.read();
|
let _ = in_queue_evt.read();
|
||||||
// Used to buffer descriptor indexes that are invalid for our uses.
|
// Used to buffer descriptor indexes that are invalid for our uses.
|
||||||
let mut rejects = [0u16; QUEUE_SIZE as usize];
|
let mut rejects = [0u16; QUEUE_SIZE as usize];
|
||||||
|
@ -1114,7 +1140,7 @@ impl Worker {
|
||||||
self.in_queue.add_used(&self.mem, reject, 0);
|
self.in_queue.add_used(&self.mem, reject, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Q_OUT => {
|
Token::OutQueue => {
|
||||||
let _ = out_queue_evt.read();
|
let _ = out_queue_evt.read();
|
||||||
// Used to buffer filled in descriptors that will be added to the used queue
|
// Used to buffer filled in descriptors that will be added to the used queue
|
||||||
// after iterating the available queue.
|
// after iterating the available queue.
|
||||||
|
@ -1159,15 +1185,8 @@ impl Worker {
|
||||||
self.out_queue.add_used(&self.mem, index, len);
|
self.out_queue.add_used(&self.mem, index, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KILL => break 'poll,
|
Token::Kill => break 'poll,
|
||||||
v => {
|
Token::State => self.state.process_poll_context(),
|
||||||
if let Some(&id) = token_vfd_id_map.get(&v) {
|
|
||||||
let res = self.state.recv(id);
|
|
||||||
if let Err(e) = res {
|
|
||||||
error!("failed to receive vfd {}: {:?}", id, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,3 +43,6 @@ restart_syscall: 1
|
||||||
# Allow F_GETFL only
|
# Allow F_GETFL only
|
||||||
fcntl: arg1 == 3
|
fcntl: arg1 == 3
|
||||||
pipe2: 1
|
pipe2: 1
|
||||||
|
epoll_create1: 1
|
||||||
|
epoll_ctl: 1
|
||||||
|
epoll_wait: 1
|
||||||
|
|
Loading…
Reference in a new issue