diff options
author | Ryo Hashimoto <hashimoto@google.com> | 2019-12-10 17:14:13 +0900 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-01-07 07:59:06 +0000 |
commit | 0b788defc6452709f0a4ede169ea087236917a5f (patch) | |
tree | ad4d0178639c59d3be607b891d2e79ab6ad32ced | |
parent | 3ec8cc4f52b3c4314b2d02a08d4851e58daef587 (diff) | |
download | crosvm-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.rs | 64 | ||||
-rw-r--r-- | src/crosvm.rs | 4 | ||||
-rw-r--r-- | src/linux.rs | 42 | ||||
-rw-r--r-- | src/main.rs | 50 |
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', |