summary refs log tree commit diff
diff options
context:
space:
mode:
authorRyo Hashimoto <hashimoto@google.com>2019-12-10 17:14:13 +0900
committerCommit Bot <commit-bot@chromium.org>2020-01-07 07:59:06 +0000
commit0b788defc6452709f0a4ede169ea087236917a5f (patch)
treead4d0178639c59d3be607b891d2e79ab6ad32ced
parent3ec8cc4f52b3c4314b2d02a08d4851e58daef587 (diff)
downloadcrosvm-0b788defc6452709f0a4ede169ea087236917a5f.tar
crosvm-0b788defc6452709f0a4ede169ea087236917a5f.tar.gz
crosvm-0b788defc6452709f0a4ede169ea087236917a5f.tar.bz2
crosvm-0b788defc6452709f0a4ede169ea087236917a5f.tar.lz
crosvm-0b788defc6452709f0a4ede169ea087236917a5f.tar.xz
crosvm-0b788defc6452709f0a4ede169ea087236917a5f.tar.zst
crosvm-0b788defc6452709f0a4ede169ea087236917a5f.zip
devices: virtio: wl: Support multiple sockets
Guest can specify which socket it wants to connect by passing a
parameter to VIRTWL_IOCTL_NEW_CTX_NAMED.

Even after this CL, only the unnamed wayland socket is used for composition.
Additional sockets are used for IPC purpose (e.g. camera).

BUG=b:146100044
TEST=Camera works

Cq-Depend: chromium:1962108
Change-Id: Ibd8efbae1b2177cc0381d88d151643183c31b519
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1963412
Tested-by: Ryo Hashimoto <hashimoto@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Commit-Queue: Ryo Hashimoto <hashimoto@chromium.org>
-rw-r--r--devices/src/virtio/wl.rs64
-rw-r--r--src/crosvm.rs4
-rw-r--r--src/linux.rs42
-rw-r--r--src/main.rs50
4 files changed, 108 insertions, 52 deletions
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs
index d8b9bae..5bbd914 100644
--- a/devices/src/virtio/wl.rs
+++ b/devices/src/virtio/wl.rs
@@ -86,6 +86,7 @@ const VIRTIO_WL_CMD_VFD_NEW_DMABUF: u32 = 263;
 const VIRTIO_WL_CMD_VFD_DMABUF_SYNC: u32 = 264;
 #[cfg(feature = "gpu")]
 const VIRTIO_WL_CMD_VFD_SEND_FOREIGN_ID: u32 = 265;
+const VIRTIO_WL_CMD_VFD_NEW_CTX_NAMED: u32 = 266;
 const VIRTIO_WL_RESP_OK: u32 = 4096;
 const VIRTIO_WL_RESP_VFD_NEW: u32 = 4097;
 #[cfg(feature = "wl-dmabuf")]
@@ -275,6 +276,8 @@ enum WlError {
     PollContextAdd(Error),
     DmabufSync(io::Error),
     WriteResponse(io::Error),
+    InvalidString(std::str::Utf8Error),
+    UnknownSocketName(String),
 }
 
 impl Display for WlError {
@@ -300,6 +303,8 @@ impl Display for WlError {
             PollContextAdd(e) => write!(f, "failed to listen to FD on poll context: {}", e),
             DmabufSync(e) => write!(f, "failed to synchronize DMABuf access: {}", e),
             WriteResponse(e) => write!(f, "failed to write response: {}", e),
+            InvalidString(e) => write!(f, "invalid string: {}", e),
+            UnknownSocketName(name) => write!(f, "unknown socket name: {}", name),
         }
     }
 }
@@ -361,6 +366,19 @@ unsafe impl DataInit for CtrlVfdNew {}
 
 #[repr(C)]
 #[derive(Copy, Clone, Default)]
+struct CtrlVfdNewCtxNamed {
+    hdr: CtrlHeader,
+    id: Le32,
+    flags: Le32, // Ignored.
+    pfn: Le64,   // Ignored.
+    size: Le32,  // Ignored.
+    name: [u8; 32],
+}
+
+unsafe impl DataInit for CtrlVfdNewCtxNamed {}
+
+#[repr(C)]
+#[derive(Copy, Clone, Default)]
 #[cfg(feature = "wl-dmabuf")]
 struct CtrlVfdNewDmabuf {
     hdr: CtrlHeader,
@@ -808,7 +826,7 @@ enum WlRecv {
 }
 
 struct WlState {
-    wayland_path: PathBuf,
+    wayland_paths: Map<String, PathBuf>,
     vm: VmRequester,
     resource_bridge: Option<ResourceRequestSocket>,
     use_transition_flags: bool,
@@ -823,13 +841,13 @@ struct WlState {
 
 impl WlState {
     fn new(
-        wayland_path: PathBuf,
+        wayland_paths: Map<String, PathBuf>,
         vm_socket: VmMemoryControlRequestSocket,
         use_transition_flags: bool,
         resource_bridge: Option<ResourceRequestSocket>,
     ) -> WlState {
         WlState {
-            wayland_path,
+            wayland_paths,
             vm: VmRequester::new(vm_socket),
             resource_bridge,
             poll_ctx: PollContext::new().expect("failed to create PollContext"),
@@ -950,7 +968,7 @@ impl WlState {
         }
     }
 
-    fn new_context(&mut self, id: u32) -> WlResult<WlResp> {
+    fn new_context(&mut self, id: u32, name: &str) -> WlResult<WlResp> {
         if id & VFD_ID_HOST_MASK != 0 {
             return Ok(WlResp::InvalidId);
         }
@@ -963,7 +981,12 @@ impl WlState {
 
         match self.vfds.entry(id) {
             Entry::Vacant(entry) => {
-                let vfd = entry.insert(WlVfd::connect(&self.wayland_path)?);
+                let vfd = entry.insert(WlVfd::connect(
+                    &self
+                        .wayland_paths
+                        .get(name)
+                        .ok_or(WlError::UnknownSocketName(name.to_string()))?,
+                )?);
                 self.poll_ctx
                     .add(vfd.poll_fd().unwrap(), id)
                     .map_err(WlError::PollContextAdd)?;
@@ -1203,7 +1226,7 @@ impl WlState {
             }
             VIRTIO_WL_CMD_VFD_NEW_CTX => {
                 let ctrl = reader.read_obj::<CtrlVfd>().map_err(WlError::ParseDesc)?;
-                self.new_context(ctrl.id.into())
+                self.new_context(ctrl.id.into(), "")
             }
             VIRTIO_WL_CMD_VFD_NEW_PIPE => {
                 let ctrl = reader
@@ -1230,6 +1253,19 @@ impl WlState {
                     .map_err(WlError::ParseDesc)?;
                 self.dmabuf_sync(ctrl.id.into(), ctrl.flags.into())
             }
+            VIRTIO_WL_CMD_VFD_NEW_CTX_NAMED => {
+                let ctrl = reader
+                    .read_obj::<CtrlVfdNewCtxNamed>()
+                    .map_err(WlError::ParseDesc)?;
+                let name_len = ctrl
+                    .name
+                    .iter()
+                    .position(|x| x == &0)
+                    .unwrap_or(ctrl.name.len());
+                let name =
+                    std::str::from_utf8(&ctrl.name[..name_len]).map_err(WlError::InvalidString)?;
+                self.new_context(ctrl.id.into(), name)
+            }
             op_type => {
                 warn!("unexpected command {}", op_type);
                 Ok(WlResp::InvalidCommand)
@@ -1332,7 +1368,7 @@ impl Worker {
         interrupt: Interrupt,
         in_queue: Queue,
         out_queue: Queue,
-        wayland_path: PathBuf,
+        wayland_paths: Map<String, PathBuf>,
         vm_socket: VmMemoryControlRequestSocket,
         use_transition_flags: bool,
         resource_bridge: Option<ResourceRequestSocket>,
@@ -1343,7 +1379,7 @@ impl Worker {
             in_queue,
             out_queue,
             state: WlState::new(
-                wayland_path,
+                wayland_paths,
                 vm_socket,
                 use_transition_flags,
                 resource_bridge,
@@ -1514,22 +1550,22 @@ impl Worker {
 pub struct Wl {
     kill_evt: Option<EventFd>,
     worker_thread: Option<thread::JoinHandle<()>>,
-    wayland_path: PathBuf,
+    wayland_paths: Map<String, PathBuf>,
     vm_socket: Option<VmMemoryControlRequestSocket>,
     resource_bridge: Option<ResourceRequestSocket>,
     use_transition_flags: bool,
 }
 
 impl Wl {
-    pub fn new<P: AsRef<Path>>(
-        wayland_path: P,
+    pub fn new(
+        wayland_paths: Map<String, PathBuf>,
         vm_socket: VmMemoryControlRequestSocket,
         resource_bridge: Option<ResourceRequestSocket>,
     ) -> Result<Wl> {
         Ok(Wl {
             kill_evt: None,
             worker_thread: None,
-            wayland_path: wayland_path.as_ref().to_owned(),
+            wayland_paths,
             vm_socket: Some(vm_socket),
             resource_bridge,
             use_transition_flags: false,
@@ -1603,7 +1639,7 @@ impl VirtioDevice for Wl {
         self.kill_evt = Some(self_kill_evt);
 
         if let Some(vm_socket) = self.vm_socket.take() {
-            let wayland_path = self.wayland_path.clone();
+            let wayland_paths = self.wayland_paths.clone();
             let use_transition_flags = self.use_transition_flags;
             let resource_bridge = self.resource_bridge.take();
             let worker_result =
@@ -1615,7 +1651,7 @@ impl VirtioDevice for Wl {
                             interrupt,
                             queues.remove(0),
                             queues.remove(0),
-                            wayland_path,
+                            wayland_paths,
                             vm_socket,
                             use_transition_flags,
                             resource_bridge,
diff --git a/src/crosvm.rs b/src/crosvm.rs
index 8a15c6b..ad3d4d1 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -142,7 +142,7 @@ pub struct Config {
     pub vhost_net: bool,
     pub tap_fd: Vec<RawFd>,
     pub cid: Option<u64>,
-    pub wayland_socket_path: Option<PathBuf>,
+    pub wayland_socket_paths: BTreeMap<String, PathBuf>,
     pub wayland_dmabuf: bool,
     pub x_display: Option<String>,
     pub shared_dirs: Vec<SharedDir>,
@@ -193,7 +193,7 @@ impl Default for Config {
             #[cfg(feature = "gpu")]
             gpu_parameters: None,
             software_tpm: false,
-            wayland_socket_path: None,
+            wayland_socket_paths: BTreeMap::new(),
             wayland_dmabuf: false,
             x_display: None,
             display_window_keyboard: false,
diff --git a/src/linux.rs b/src/linux.rs
index dbfad21..c37be83 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -589,7 +589,7 @@ fn create_gpu_device(
     exit_evt: &EventFd,
     gpu_device_socket: VmMemoryControlRequestSocket,
     gpu_sockets: Vec<virtio::resource_bridge::ResourceResponseSocket>,
-    wayland_socket_path: Option<PathBuf>,
+    wayland_socket_path: Option<&PathBuf>,
     x_display: Option<String>,
     event_devices: Vec<EventDevice>,
 ) -> DeviceResult {
@@ -600,7 +600,7 @@ fn create_gpu_device(
         virtio::DisplayBackend::Null,
     ];
 
-    if let Some(socket_path) = wayland_socket_path.as_ref() {
+    if let Some(socket_path) = wayland_socket_path {
         display_backends.insert(
             0,
             virtio::DisplayBackend::Wayland(if cfg.sandbox {
@@ -664,7 +664,7 @@ fn create_gpu_device(
             // Bind mount the wayland socket into jail's root. This is necessary since each
             // new wayland context must open() the socket.
             if let Some(path) = wayland_socket_path {
-                jail.mount_bind(path.as_ref(), jailed_wayland_path, true)?;
+                jail.mount_bind(path, jailed_wayland_path, true)?;
             }
 
             add_crosvm_user_to_jail(&mut jail, "gpu")?;
@@ -691,25 +691,18 @@ fn create_gpu_device(
 
 fn create_wayland_device(
     cfg: &Config,
-    socket_path: &Path,
     socket: VmMemoryControlRequestSocket,
     resource_bridge: Option<virtio::resource_bridge::ResourceRequestSocket>,
 ) -> DeviceResult {
-    let wayland_socket_dir = socket_path.parent().ok_or(Error::InvalidWaylandPath)?;
-    let wayland_socket_name = socket_path.file_name().ok_or(Error::InvalidWaylandPath)?;
-    let jailed_wayland_dir = Path::new("/wayland");
-    let jailed_wayland_path = jailed_wayland_dir.join(wayland_socket_name);
-
-    let dev = virtio::Wl::new(
-        if cfg.sandbox {
-            &jailed_wayland_path
-        } else {
-            socket_path
-        },
-        socket,
-        resource_bridge,
-    )
-    .map_err(Error::WaylandDeviceNew)?;
+    let wayland_socket_dirs = cfg
+        .wayland_socket_paths
+        .iter()
+        .map(|(_name, path)| path.parent())
+        .collect::<Option<Vec<_>>>()
+        .ok_or(Error::InvalidWaylandPath)?;
+
+    let dev = virtio::Wl::new(cfg.wayland_socket_paths.clone(), socket, resource_bridge)
+        .map_err(Error::WaylandDeviceNew)?;
 
     let jail = match simple_jail(&cfg, "wl_device.policy")? {
         Some(mut jail) => {
@@ -727,8 +720,9 @@ fn create_wayland_device(
             // each new wayland context must open() the socket. If the wayland socket is ever
             // destroyed and remade in the same host directory, new connections will be possible
             // without restarting the wayland device.
-            jail.mount_bind(wayland_socket_dir, jailed_wayland_dir, true)?;
-
+            for dir in &wayland_socket_dirs {
+                jail.mount_bind(dir, dir, true)?;
+            }
             add_crosvm_user_to_jail(&mut jail, "Wayland")?;
 
             Some(jail)
@@ -965,7 +959,7 @@ fn create_virtio_devices(
     #[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
     let mut resource_bridges = Vec::<virtio::resource_bridge::ResourceResponseSocket>::new();
 
-    if let Some(wayland_socket_path) = cfg.wayland_socket_path.as_ref() {
+    if !cfg.wayland_socket_paths.is_empty() {
         #[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
         let mut wl_resource_bridge = None::<virtio::resource_bridge::ResourceRequestSocket>;
 
@@ -981,7 +975,6 @@ fn create_virtio_devices(
 
         devs.push(create_wayland_device(
             cfg,
-            wayland_socket_path,
             wayland_device_socket,
             wl_resource_bridge,
         )?);
@@ -1020,7 +1013,8 @@ fn create_virtio_devices(
                 _exit_evt,
                 gpu_device_socket,
                 resource_bridges,
-                cfg.wayland_socket_path.clone(),
+                // Use the unnamed socket for GPU display screens.
+                cfg.wayland_socket_paths.get(""),
                 cfg.x_display.clone(),
                 event_devices,
             )?);
diff --git a/src/main.rs b/src/main.rs
index eee8bb1..e52534d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -629,19 +629,45 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 )
         }
         "wayland-sock" => {
-            if cfg.wayland_socket_path.is_some() {
-                return Err(argument::Error::TooManyArguments(
-                    "`wayland-sock` already given".to_owned(),
-                ));
+            let mut components = value.unwrap().split(',');
+            let path =
+                PathBuf::from(
+                    components
+                        .next()
+                        .ok_or_else(|| argument::Error::InvalidValue {
+                            value: value.unwrap().to_owned(),
+                            expected: "missing socket path",
+                        })?,
+                );
+            let mut name = "";
+            for c in components {
+                let mut kv = c.splitn(2, '=');
+                let (kind, value) = match (kv.next(), kv.next()) {
+                    (Some(kind), Some(value)) => (kind, value),
+                    _ => {
+                        return Err(argument::Error::InvalidValue {
+                            value: c.to_owned(),
+                            expected: "option must be of the form `kind=value`",
+                        })
+                    }
+                };
+                match kind {
+                    "name" => name = value,
+                    _ => {
+                        return Err(argument::Error::InvalidValue {
+                            value: kind.to_owned(),
+                            expected: "unrecognized option",
+                        })
+                    }
+                }
             }
-            let wayland_socket_path = PathBuf::from(value.unwrap());
-            if !wayland_socket_path.exists() {
-                return Err(argument::Error::InvalidValue {
-                    value: value.unwrap().to_string(),
-                    expected: "Wayland socket does not exist",
-                });
+            if cfg.wayland_socket_paths.contains_key(name) {
+                return Err(argument::Error::TooManyArguments(format!(
+                    "wayland socket name already used: '{}'",
+                    name
+                )));
             }
-            cfg.wayland_socket_path = Some(wayland_socket_path);
+            cfg.wayland_socket_paths.insert(name.to_string(), path);
         }
         #[cfg(feature = "wl-dmabuf")]
         "wayland-dmabuf" => cfg.wayland_dmabuf = true,
@@ -1038,7 +1064,7 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
           Argument::value("x-display", "DISPLAY", "X11 display name to use."),
           Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
           Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
-          Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
+          Argument::value("wayland-sock", "PATH[,name=NAME]", "Path to the Wayland socket to use. The unnamed one is used for displaying virtual screens. Named ones are only for IPC."),
           #[cfg(feature = "wl-dmabuf")]
           Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
           Argument::short_value('s',