devices: gpu: add render server support

When "--gpu-render-server path=<path>" is specified, start the render
server shipped with virglrenderer and initialize virglrenderer with
VIRGLRENDERER_MULTI_PROCESS flag.

The flag makes virgl_renderer_context_create_with_flags create proxy
contexts instead of venus contexts.  Each proxy context requests the
render server to fork a subprocess and executes GPU commands in the
subprocess.

BUG=b:177267762
TEST=run vk and gl apps on volteer

Change-Id: If5e2dc3353572cadb60b0c25a3e0ad14f633db91
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3283508
Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Chia-I Wu <olv@google.com>
diff --git a/src/linux.rs b/src/linux.rs
index 4cadf40..791e61b 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -44,7 +44,7 @@
 use devices::virtio::{self, Console, VirtioDevice};
 #[cfg(feature = "gpu")]
 use devices::virtio::{
-    gpu::{DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH},
+    gpu::{GpuRenderServerParameters, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH},
     vhost::user::vmm::Gpu as VhostUserGpu,
     EventDevice,
 };
@@ -802,6 +802,7 @@
     resource_bridges: Vec<Tube>,
     wayland_socket_path: Option<&PathBuf>,
     x_display: Option<String>,
+    render_server_fd: Option<SafeDescriptor>,
     event_devices: Vec<EventDevice>,
     map_request: Arc<Mutex<Option<ExternalMapping>>>,
 ) -> DeviceResult {
@@ -830,6 +831,7 @@
         resource_bridges,
         display_backends,
         cfg.gpu_parameters.as_ref().unwrap(),
+        render_server_fd,
         event_devices,
         map_request,
         cfg.sandbox,
@@ -884,6 +886,46 @@
     })
 }
 
+#[cfg(feature = "gpu")]
+fn start_gpu_render_server(
+    cfg: &Config,
+    render_server_parameters: &GpuRenderServerParameters,
+) -> Result<SafeDescriptor> {
+    let (server_socket, client_socket) =
+        UnixSeqpacket::pair().context("failed to create render server socket")?;
+
+    let jail = match gpu_jail(cfg, "gpu_render_server")? {
+        Some(mut jail) => {
+            // TODO(olv) bind mount and enable shader cache
+
+            // Run as root in the jail to keep capabilities after execve, which is needed for
+            // mounting to work.  All capabilities will be dropped afterwards.
+            add_current_user_as_root_to_jail(&mut jail)?;
+
+            jail
+        }
+        None => Minijail::new().context("failed to create jail")?,
+    };
+
+    let inheritable_fds = [
+        server_socket.as_raw_descriptor(),
+        libc::STDOUT_FILENO,
+        libc::STDERR_FILENO,
+    ];
+
+    let cmd = &render_server_parameters.path;
+    let cmd_str = cmd
+        .to_str()
+        .ok_or_else(|| anyhow!("invalid render server path"))?;
+    let fd_str = server_socket.as_raw_descriptor().to_string();
+    let args = [cmd_str, "--socket-fd", &fd_str];
+
+    jail.run(cmd, &inheritable_fds, &args)
+        .context("failed to start gpu render server")?;
+
+    Ok(SafeDescriptor::from(client_socket))
+}
+
 fn create_wayland_device(
     cfg: &Config,
     control_tube: Tube,
@@ -1549,6 +1591,12 @@
                 });
                 event_devices.push(EventDevice::keyboard(event_device_socket));
             }
+
+            let mut render_server_fd = None;
+            if let Some(ref render_server_parameters) = gpu_parameters.render_server {
+                render_server_fd = Some(start_gpu_render_server(cfg, render_server_parameters)?);
+            }
+
             devs.push(create_gpu_device(
                 cfg,
                 _exit_evt,
@@ -1557,6 +1605,7 @@
                 // Use the unnamed socket for GPU display screens.
                 cfg.wayland_socket_paths.get(""),
                 cfg.x_display.clone(),
+                render_server_fd,
                 event_devices,
                 map_request,
             )?);
@@ -1869,6 +1918,20 @@
     })
 }
 
+fn add_current_user_as_root_to_jail(jail: &mut Minijail) -> Result<Ids> {
+    let crosvm_uid = geteuid();
+    let crosvm_gid = getegid();
+    jail.uidmap(&format!("0 {0} 1", crosvm_uid))
+        .context("error setting UID map")?;
+    jail.gidmap(&format!("0 {0} 1", crosvm_gid))
+        .context("error setting GID map")?;
+
+    Ok(Ids {
+        uid: crosvm_uid,
+        gid: crosvm_gid,
+    })
+}
+
 trait IntoUnixStream {
     fn into_unix_stream(self) -> Result<UnixStream>;
 }