diff options
author | Alyssa Ross <hi@alyssa.is> | 2020-06-07 09:47:21 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2020-07-02 12:33:02 +0000 |
commit | f57267d7e998ffec48794723b188b35489acfeec (patch) | |
tree | d0612accc4c8a20942bf333cb15e34c4203c1c4c | |
parent | b7f9c8db01cd673a8b20bb5649f6cd06371b9695 (diff) | |
download | crosvm-f57267d7e998ffec48794723b188b35489acfeec.tar crosvm-f57267d7e998ffec48794723b188b35489acfeec.tar.gz crosvm-f57267d7e998ffec48794723b188b35489acfeec.tar.bz2 crosvm-f57267d7e998ffec48794723b188b35489acfeec.tar.lz crosvm-f57267d7e998ffec48794723b188b35489acfeec.tar.xz crosvm-f57267d7e998ffec48794723b188b35489acfeec.tar.zst crosvm-f57267d7e998ffec48794723b188b35489acfeec.zip |
devices: enable adding Wl sockets at runtime
-rw-r--r-- | devices/src/virtio/wl.rs | 73 | ||||
-rw-r--r-- | src/linux.rs | 19 | ||||
-rw-r--r-- | src/main.rs | 29 | ||||
-rw-r--r-- | vm_control/src/lib.rs | 46 |
4 files changed, 160 insertions, 7 deletions
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs index 6a412f9..0ebb4ef 100644 --- a/devices/src/virtio/wl.rs +++ b/devices/src/virtio/wl.rs @@ -70,7 +70,10 @@ use super::resource_bridge::*; use super::{ DescriptorChain, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_WL, VIRTIO_F_VERSION_1, }; -use vm_control::{MaybeOwnedFd, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse}; +use vm_control::{ + MaybeOwnedFd, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse, + WlControlCommand, WlControlResponseSocket, WlControlResult, +}; const VIRTWL_SEND_MAX_ALLOCS: usize = 28; const VIRTIO_WL_CMD_VFD_NEW: u32 = 256; @@ -993,6 +996,20 @@ impl WlState { } } + fn add_path(&mut self, name: String, path: PathBuf) -> Result<(), Error> { + if name.bytes().len() > 32 { + return Err(Error::new(libc::EINVAL)); + } + + if self.wayland_paths.contains_key(&name) { + return Err(Error::new(libc::EADDRINUSE)); + } + + self.wayland_paths.insert(name, path); + + Ok(()) + } + fn process_poll_context(&mut self) { let events = match self.poll_ctx.wait_timeout(Duration::from_secs(0)) { Ok(v) => v.to_owned(), @@ -1343,6 +1360,7 @@ pub struct Worker { in_queue: Queue, out_queue: Queue, state: WlState, + control_socket: WlControlResponseSocket, } impl Worker { @@ -1355,6 +1373,7 @@ impl Worker { vm_socket: VmMemoryControlRequestSocket, use_transition_flags: bool, resource_bridge: Option<ResourceRequestSocket>, + control_socket: WlControlResponseSocket, ) -> Worker { Worker { interrupt, @@ -1367,6 +1386,7 @@ impl Worker { use_transition_flags, resource_bridge, ), + control_socket, } } @@ -1379,6 +1399,7 @@ impl Worker { enum Token { InQueue, OutQueue, + CommandSocket, Kill, State, InterruptResample, @@ -1387,6 +1408,7 @@ impl Worker { let poll_ctx: PollContext<Token> = match PollContext::build_with(&[ (&in_queue_evt, Token::InQueue), (&out_queue_evt, Token::OutQueue), + (&self.control_socket, Token::CommandSocket), (&kill_evt, Token::Kill), (&self.state.poll_ctx, Token::State), (self.interrupt.get_resample_evt(), Token::InterruptResample), @@ -1398,6 +1420,11 @@ impl Worker { } }; + if let Err(e) = self.control_socket.send(&WlControlResult::Ready) { + error!("control socket failed to notify readiness: {}", e); + return; + } + 'poll: loop { let mut signal_used_in = false; let mut signal_used_out = false; @@ -1475,6 +1502,25 @@ impl Worker { } } } + Token::CommandSocket => { + let resp = match self.control_socket.recv() { + Ok(WlControlCommand::AddSocket { name, path }) => { + self.state.add_path(name, path).into() + } + Err(MsgError::InvalidData) => { + WlControlResult::Err(Error::new(libc::EINVAL)) + } + Err(e) => { + error!("control socket failed recv: {}", e); + break 'poll; + } + }; + + if let Err(e) = self.control_socket.send(&resp) { + error!("control socket failed send: {}", e); + break 'poll; + } + } Token::Kill => break 'poll, Token::State => self.state.process_poll_context(), Token::InterruptResample => { @@ -1537,6 +1583,7 @@ pub struct Wl { vm_socket: Option<VmMemoryControlRequestSocket>, resource_bridge: Option<ResourceRequestSocket>, use_transition_flags: bool, + control_socket: Option<WlControlResponseSocket>, } use msg_socket2::{ @@ -1552,17 +1599,22 @@ pub struct Params { pub wayland_paths: Map<String, PathBuf>, pub vm_socket: VmMemoryControlRequestSocket, pub resource_bridge: Option<ResourceRequestSocket>, + pub control_socket: WlControlResponseSocket, } impl SerializeWithFds for Params { fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { - let mut state = serializer.serialize_struct("Params", 3)?; + let mut state = serializer.serialize_struct("Params", 4)?; state.serialize_field("wayland_paths", &self.wayland_paths)?; state.serialize_field("vm_socket", &SerializeAdapter::new(&self.vm_socket))?; state.serialize_field( "resource_bridge", &SerializeAdapter::new(&self.resource_bridge), )?; + state.serialize_field( + "control_socket", + &SerializeAdapter::new(&self.control_socket), + )?; state.end() } @@ -1570,10 +1622,11 @@ impl SerializeWithFds for Params { where S: FdSerializer<'fds>, { - let mut state = serializer.serialize_struct("Params", 3)?; + let mut state = serializer.serialize_struct("Params", 4)?; state.serialize_field("wayland_paths", &self.wayland_paths)?; state.serialize_field("vm_socket", &self.vm_socket)?; state.serialize_field("resource_bridge", &self.resource_bridge)?; + state.serialize_field("control_socket", &self.control_socket)?; state.end() } } @@ -1599,7 +1652,7 @@ impl<'de> DeserializeWithFds<'de> for Params { use serde::de::Error; fn too_short_error<E: Error>(len: usize) -> E { - E::invalid_length(len, &"struct Params with 3 elements") + E::invalid_length(len, &"struct Params with 4 elements") } Ok(Params { @@ -1614,6 +1667,11 @@ impl<'de> DeserializeWithFds<'de> for Params { .next_element::<Option<_>>()? .ok_or_else(|| too_short_error(2))? .map(MsgSocket::new), + + control_socket: seq + .next_element()? + .map(MsgSocket::new) + .ok_or_else(|| too_short_error(3))?, }) } } @@ -1635,6 +1693,7 @@ impl VirtioDeviceNew for Wl { wayland_paths, vm_socket, resource_bridge, + control_socket, } = params; Ok(Self { @@ -1644,6 +1703,7 @@ impl VirtioDeviceNew for Wl { vm_socket: Some(vm_socket), resource_bridge, use_transition_flags: false, + control_socket: Some(control_socket), }) } } @@ -1671,6 +1731,9 @@ impl VirtioDevice for Wl { if let Some(resource_bridge) = &self.resource_bridge { keep_fds.push(resource_bridge.as_raw_fd()); } + if let Some(control_socket) = &self.control_socket { + keep_fds.push(control_socket.as_raw_fd()); + } keep_fds } @@ -1717,6 +1780,7 @@ impl VirtioDevice for Wl { let wayland_paths = self.wayland_paths.clone(); let use_transition_flags = self.use_transition_flags; let resource_bridge = self.resource_bridge.take(); + let control_socket = self.control_socket.take().unwrap(); println!("creating worker"); let worker_result = thread::Builder::new() @@ -1731,6 +1795,7 @@ impl VirtioDevice for Wl { vm_socket, use_transition_flags, resource_bridge, + control_socket, ) .run(queue_evts, kill_evt); }); diff --git a/src/linux.rs b/src/linux.rs index d5e12cd..3ef323e 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -11,6 +11,7 @@ use std::ffi::{CStr, OsString}; use std::fmt::{self, Display}; use std::fs::{File, OpenOptions}; use std::io::{self, stdin, Read}; +use std::iter::once; use std::mem; use std::net::Ipv4Addr; #[cfg(feature = "gpu")] @@ -61,7 +62,8 @@ use vm_control::{ DiskControlResult, UsbControlSocket, VmControlResponseSocket, VmIrqRequest, VmIrqResponse, VmIrqResponseSocket, VmMemoryControlRequestSocket, VmMemoryControlResponseSocket, VmMemoryRequest, VmMemoryResponse, VmMsyncRequest, VmMsyncRequestSocket, VmMsyncResponse, - VmMsyncResponseSocket, VmRequest, VmRunMode, + VmMsyncResponseSocket, VmRequest, VmRunMode, WlControlCommand, WlControlRequestSocket, + WlControlResponseSocket, WlControlResult, }; use crate::{Config, DiskOption, Executable, SharedDir, SharedDirKind, TouchDeviceOption}; @@ -772,6 +774,7 @@ fn create_wayland_device( vm_socket: VmMemoryControlRequestSocket, resource_bridge: Option<virtio::resource_bridge::ResourceRequestSocket>, memory_params: MemoryParams, + control_socket: WlControlResponseSocket, ) -> DeviceResult { let mut wayland_socket_paths = cfg.wayland_socket_paths.clone(); @@ -799,6 +802,7 @@ fn create_wayland_device( wayland_paths: wayland_socket_paths.clone(), vm_socket, resource_bridge, + control_socket, }) .unwrap(), ), @@ -1100,6 +1104,7 @@ fn create_virtio_devices( balloon_device_control_socket: BalloonControlResponseSocket, disk_device_control_sockets: &mut Vec<DiskControlResponseSocket>, pmem_device_control_sockets: &mut Vec<VmMsyncRequestSocket>, + wl_device_control_socket: WlControlResponseSocket, ) -> DeviceResult<Vec<VirtioDeviceStub>> { let mut devs = Vec::new(); @@ -1194,6 +1199,7 @@ fn create_virtio_devices( wayland_device_control_socket, wl_resource_bridge, mem_params, + wl_device_control_socket, )?); } @@ -1307,6 +1313,7 @@ fn create_devices( disk_device_control_sockets: &mut Vec<DiskControlResponseSocket>, pmem_device_control_sockets: &mut Vec<VmMsyncRequestSocket>, usb_provider: HostBackendDeviceProvider, + wl_device_control_socket: WlControlResponseSocket, ) -> DeviceResult<Vec<(Box<dyn PciDevice>, Option<Minijail>)>> { let stubs = create_virtio_devices( &cfg, @@ -1321,6 +1328,7 @@ fn create_devices( balloon_device_control_socket, disk_device_control_sockets, pmem_device_control_sockets, + wl_device_control_socket, )?; let mut pci_devices = Vec::new(); @@ -1852,6 +1860,9 @@ pub fn run_config(cfg: Config) -> Result<()> { msg_socket::pair::<VmIrqResponse, VmIrqRequest>().map_err(Error::CreateSocket)?; control_sockets.push(TaggedControlSocket::VmIrq(ioapic_host_socket)); + let (wl_host_socket, wl_device_control_socket) = + msg_socket::pair::<WlControlCommand, WlControlResult>().map_err(Error::CreateSocket)?; + let mut servers = vec![]; let mut memfd_socket_path = None; @@ -1899,6 +1910,7 @@ pub fn run_config(cfg: Config) -> Result<()> { &mut disk_device_control_sockets, &mut pmem_device_control_sockets, usb_provider, + wl_device_control_socket, ) }, ) @@ -1911,6 +1923,7 @@ pub fn run_config(cfg: Config) -> Result<()> { balloon_host_socket, &disk_host_sockets, usb_control_socket, + wl_host_socket, sigchld_fd, sandbox, ) @@ -1923,6 +1936,7 @@ fn run_control( balloon_host_socket: BalloonControlRequestSocket, disk_host_sockets: &[DiskControlRequestSocket], usb_control_socket: UsbControlSocket, + wl_host_socket: WlControlRequestSocket, sigchld_fd: SignalFd, sandbox: bool, ) -> Result<()> { @@ -2049,7 +2063,7 @@ fn run_control( // to the event loop, so that when they become ready, we can process their queues. After the // initial queue is processed, the device becomes ready, and the socket is removed from the // event loop. - for socket in disk_host_sockets.iter().map(AsRef::as_ref) { + for socket in once(wl_host_socket.as_ref()).chain(disk_host_sockets.iter().map(AsRef::as_ref)) { let token = Token::DeviceReady { sock_fd: socket.as_raw_fd(), }; @@ -2261,6 +2275,7 @@ fn run_control( &balloon_host_socket, &disk_host_sockets, &usb_control_socket, + &wl_host_socket, ); match device_socket diff --git a/src/main.rs b/src/main.rs index 87ecdb6..e5b5053 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ use sys_util::{ }; use vm_control::{ BalloonControlCommand, DiskControlCommand, MaybeOwnedFd, UsbControlCommand, UsbControlResult, - VmControlRequestSocket, VmRequest, VmResponse, USB_CONTROL_MAX_PORTS, + VmControlRequestSocket, VmRequest, VmResponse, WlControlCommand, USB_CONTROL_MAX_PORTS, }; fn executable_is_plugin(executable: &Option<Executable>) -> bool { @@ -2178,6 +2178,31 @@ fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> { } } +fn wl_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> { + if args.len() < 2 { + print_help("crosvm wl", "SUBCOMMAND VM_SOCKET...", &[]); + println!("Manage attached virtio Wayland sockets."); + println!("Subcommands:"); + println!(" add NAME PATH VM_SOCKET"); + return Err(()); + } + let subcommand: &str = &args.next().unwrap(); + + let request = match subcommand { + "add" => { + let name = args.next().unwrap(); + let path = args.next().unwrap().into(); + VmRequest::WlCommand(WlControlCommand::AddSocket { name, path }) + } + _ => { + error!("Unknown wl subcommand '{}'", subcommand); + return Err(()); + } + }; + + vms_request(&request, args) +} + fn print_usage() { print_help("crosvm", "[stop|run]", &[]); println!("Commands:"); @@ -2189,6 +2214,7 @@ fn print_usage() { println!(" create_qcow2 Create a new qcow2 disk image file."); println!(" disk Manage attached virtual disk devices."); println!(" usb Manage attached virtual USB devices."); + println!(" wl Manage attached virtio_wl sockets."); println!(" version Show package version."); } @@ -2233,6 +2259,7 @@ fn crosvm_main() -> std::result::Result<(), ()> { Some("create_qcow2") => create_qcow2(args), Some("disk") => disk_cmd(args), Some("usb") => modify_usb(args), + Some("wl") => wl_cmd(args), Some("version") => pkg_version(), Some(c) => { println!("invalid subcommand: {:?}", c); diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs index a9784b1..e800624 100644 --- a/vm_control/src/lib.rs +++ b/vm_control/src/lib.rs @@ -15,6 +15,7 @@ use std::fs::File; use std::io::{Seek, SeekFrom}; use std::mem::ManuallyDrop; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::path::PathBuf; use libc::{EINVAL, EIO, ENODEV}; @@ -511,6 +512,27 @@ impl VmMsyncRequest { } } +#[derive(MsgOnSocket, Debug)] +pub enum WlControlCommand { + AddSocket { name: String, path: PathBuf }, +} + +#[derive(MsgOnSocket, Debug)] +pub enum WlControlResult { + Ready, + Ok, + Err(SysError), +} + +impl From<Result<()>> for WlControlResult { + fn from(result: Result<()>) -> Self { + match result { + Ok(()) => Self::Ok, + Err(e) => Self::Err(e), + } + } +} + pub type BalloonControlRequestSocket = MsgSocket<BalloonControlCommand, BalloonControlResult>; pub type BalloonControlResponseSocket = MsgSocket<BalloonControlResult, BalloonControlCommand>; @@ -531,6 +553,9 @@ pub type VmMsyncResponseSocket = MsgSocket<VmMsyncResponse, VmMsyncRequest>; pub type VmControlRequestSocket = MsgSocket<VmRequest, VmResponse>; pub type VmControlResponseSocket = MsgSocket<VmResponse, VmRequest>; +pub type WlControlRequestSocket = MsgSocket<WlControlCommand, WlControlResult>; +pub type WlControlResponseSocket = MsgSocket<WlControlResult, WlControlCommand>; + /// A request to the main process to perform some operation on the VM. /// /// Unless otherwise noted, each request should expect a `VmResponse::Ok` to be received on success. @@ -552,6 +577,8 @@ pub enum VmRequest { }, /// Command to use controller. UsbCommand(UsbControlCommand), + /// Command for wl driver. + WlCommand(WlControlCommand), } fn register_memory( @@ -592,6 +619,7 @@ impl VmRequest { balloon_host_socket: &'s BalloonControlRequestSocket, disk_host_sockets: &'s [DiskControlRequestSocket], usb_control_socket: &'s UsbControlSocket, + wl_host_socket: &'s WlControlRequestSocket, ) -> Option<&'s UnixSeqpacket> { use VmRequest::*; match *self { @@ -601,6 +629,7 @@ impl VmRequest { BalloonCommand(_) => Some(balloon_host_socket.as_ref()), DiskCommand { disk_index, .. } => disk_host_sockets.get(disk_index).map(AsRef::as_ref), UsbCommand(_) => Some(usb_control_socket.as_ref()), + WlCommand(_) => Some(wl_host_socket.as_ref()), } } @@ -696,6 +725,20 @@ impl VmRequest { } } } + VmRequest::WlCommand(ref cmd) => { + let socket = socket.unwrap(); + if let Err(e) = socket.send_msg_on_socket(cmd) { + error!("fail to send command to wl control socket: {}", e); + return VmResponse::Err(SysError::new(EIO)); + } + match socket.recv_msg_on_socket() { + Ok(response) => VmResponse::WlResponse(response), + Err(e) => { + error!("fail to recv command from usb control socket: {}", e); + VmResponse::Err(SysError::new(EIO)) + } + } + } } } } @@ -727,6 +770,8 @@ pub enum VmResponse { }, /// Results of usb control commands. UsbResponse(UsbControlResult), + /// Results of wl control commands. + WlResponse(WlControlResult), } impl Display for VmResponse { @@ -755,6 +800,7 @@ impl Display for VmResponse { balloon_actual, stats ), UsbResponse(result) => write!(f, "usb control request get result {:?}", result), + WlResponse(result) => write!(f, "wl control request get result {:?}", result), } } } |