summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2020-06-07 09:47:21 +0000
committerAlyssa Ross <hi@alyssa.is>2020-07-02 12:33:02 +0000
commitf57267d7e998ffec48794723b188b35489acfeec (patch)
treed0612accc4c8a20942bf333cb15e34c4203c1c4c
parentb7f9c8db01cd673a8b20bb5649f6cd06371b9695 (diff)
downloadcrosvm-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.rs73
-rw-r--r--src/linux.rs19
-rw-r--r--src/main.rs29
-rw-r--r--vm_control/src/lib.rs46
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),
         }
     }
 }