From f1cf31b888ca1cc842db15e685685b1d9f1f6275 Mon Sep 17 00:00:00 2001 From: David Reveman <reveman@google.com> Date: Sun, 21 Apr 2019 09:10:10 +0200 Subject: [PATCH] Use FIDL for QEMU pipe stream on Fuchsia. This is needed as POSIX IO support on Fuchsia is being deprecated. Benchmarks also show that FIDL API is ~ 25X faster for large transfers to host so this will result in a performance improvement. Bug: 111137294 Test: none Change-Id: I4538adb5075a25a2d099332b6f3eae42eab717e5 Merged-In: Iedda195ef0931c1f561f4b60ab811d968a83c0e8 --- BUILD.gn | 7 +- system/OpenglSystemCommon/QemuPipeStream.h | 11 + .../QemuPipeStreamFuchsia.cpp | 296 ++++++++++++++++++ 3 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp diff --git a/BUILD.gn b/BUILD.gn index 1f50018..348ab87 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -87,7 +87,11 @@ shared_library("vulkan_goldfish") { ldflags = [ "-static-libstdc++" ] if (target_os == "fuchsia") { - sources += [ "fuchsia/port.cc" ] + sources -= [ "system/OpenglSystemCommon/QemuPipeStream.cpp" ] + sources += [ + "fuchsia/port.cc", + "system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp", + ] include_dirs += [ "//third_party/vulkan_loader_and_validation_layers/include", @@ -101,6 +105,7 @@ shared_library("vulkan_goldfish") { deps = [ "//zircon/public/fidl/fuchsia-hardware-goldfish-address-space:fuchsia-hardware-goldfish-address-space_c", "//zircon/public/fidl/fuchsia-hardware-goldfish-control:fuchsia-hardware-goldfish-control_c", + "//zircon/public/fidl/fuchsia-hardware-goldfish-pipe:fuchsia-hardware-goldfish-pipe_c", "//zircon/public/fidl/fuchsia-sysmem", "//zircon/public/lib/fdio", "//zircon/public/lib/trace", diff --git a/system/OpenglSystemCommon/QemuPipeStream.h b/system/OpenglSystemCommon/QemuPipeStream.h index 0884d57..8d64ab8 100644 --- a/system/OpenglSystemCommon/QemuPipeStream.h +++ b/system/OpenglSystemCommon/QemuPipeStream.h @@ -25,6 +25,12 @@ #include "qemu_pipe.h" +#ifdef __Fuchsia__ +#include <lib/zx/channel.h> +#include <lib/zx/event.h> +#include <lib/zx/vmo.h> +#endif + class QemuPipeStream : public IOStream { public: typedef enum { ERR_INVALID_SOCKET = -1000 } QemuPipeStreamError; @@ -48,6 +54,11 @@ private: QEMU_PIPE_HANDLE m_sock; size_t m_bufsize; unsigned char *m_buf; +#ifdef __Fuchsia__ + zx::channel m_channel; + zx::event m_event; + zx::vmo m_vmo; +#endif QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize); }; diff --git a/system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp b/system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp new file mode 100644 index 0000000..f4ee61c --- /dev/null +++ b/system/OpenglSystemCommon/QemuPipeStreamFuchsia.cpp @@ -0,0 +1,296 @@ +/* +* Copyright (C) 2019 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "QemuPipeStream.h" + +#include <cutils/log.h> +#include <errno.h> +#include <fuchsia/hardware/goldfish/pipe/c/fidl.h> +#include <lib/fdio/fdio.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <zircon/process.h> + +#include <utility> + +QemuPipeStream::QemuPipeStream(size_t bufSize) : + IOStream(bufSize), + m_sock(-1), + m_bufsize(bufSize), + m_buf(nullptr) +{ +} + +QemuPipeStream::QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize) : + IOStream(bufSize), + m_sock(sock), + m_bufsize(bufSize), + m_buf(nullptr) +{ +} + +QemuPipeStream::~QemuPipeStream() +{ + if (m_channel.is_valid()) { + flush(); + } + if (m_buf) { + zx_status_t status = zx_vmar_unmap(zx_vmar_root_self(), + reinterpret_cast<zx_vaddr_t>(m_buf), + m_bufsize); + if (status != ZX_OK) { + ALOGE("zx_vmar_unmap failed: %d\n", status); + abort(); + } + } +} + +int QemuPipeStream::connect(void) +{ + int fd = TEMP_FAILURE_RETRY(open(QEMU_PIPE_PATH, O_RDWR)); + if (fd < 0) { + ALOGE("%s: failed to open " QEMU_PIPE_PATH ": %s", + __FUNCTION__, strerror(errno)); + return -1; + } + + zx::channel channel; + zx_status_t status = fdio_get_service_handle( + fd, channel.reset_and_get_address()); + if (status != ZX_OK) { + ALOGE("%s: failed to get service handle for " QEMU_PIPE_PATH ": %d", + __FUNCTION__, status); + close(fd); + return -1; + } + + zx::event event; + status = zx::event::create(0, &event); + if (status != ZX_OK) { + ALOGE("%s: failed to create event: %d", __FUNCTION__, status); + return -1; + } + zx::event event_copy; + status = event.duplicate(ZX_RIGHT_SAME_RIGHTS, &event_copy); + if (status != ZX_OK) { + ALOGE("%s: failed to duplicate event: %d", __FUNCTION__, status); + return -1; + } + + status = fuchsia_hardware_goldfish_pipe_DeviceSetEvent( + channel.get(), event_copy.release()); + if (status != ZX_OK) { + ALOGE("%s: failed to set event: %d:%d", __FUNCTION__, status); + return -1; + } + + zx_status_t status2 = ZX_OK; + zx::vmo vmo; + status = fuchsia_hardware_goldfish_pipe_DeviceGetBuffer( + channel.get(), &status2, vmo.reset_and_get_address()); + if (status != ZX_OK || status2 != ZX_OK) { + ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2); + return -1; + } + + size_t len = strlen("pipe:opengles"); + status = vmo.write("pipe:opengles", 0, len + 1); + if (status != ZX_OK) { + ALOGE("%s: failed write pipe name", __FUNCTION__); + return -1; + } + + uint64_t actual; + status = fuchsia_hardware_goldfish_pipe_DeviceWrite( + channel.get(), len + 1, 0, &status2, &actual); + if (status != ZX_OK || status2 != ZX_OK) { + ALOGD("%s: connecting to pipe service failed: %d:%d", __FUNCTION__, + status, status2); + return -1; + } + + m_channel = std::move(channel); + m_event = std::move(event); + m_vmo = std::move(vmo); + return 0; +} + +void *QemuPipeStream::allocBuffer(size_t minSize) +{ + zx_status_t status; + if (m_buf) { + if (minSize <= m_bufsize) { + return m_buf; + } + status = zx_vmar_unmap(zx_vmar_root_self(), + reinterpret_cast<zx_vaddr_t>(m_buf), + m_bufsize); + if (status != ZX_OK) { + ALOGE("zx_vmar_unmap failed: %d\n", status); + abort(); + } + m_buf = nullptr; + } + + size_t allocSize = m_bufsize < minSize ? minSize : m_bufsize; + + zx_status_t status2 = ZX_OK; + status = fuchsia_hardware_goldfish_pipe_DeviceSetBufferSize( + m_channel.get(), allocSize, &status2); + if (status != ZX_OK || status2 != ZX_OK) { + ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2); + return nullptr; + } + + zx::vmo vmo; + status = fuchsia_hardware_goldfish_pipe_DeviceGetBuffer( + m_channel.get(), &status2, vmo.reset_and_get_address()); + if (status != ZX_OK || status2 != ZX_OK) { + ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2); + return nullptr; + } + + zx_vaddr_t mapped_addr; + status = zx_vmar_map(zx_vmar_root_self(), + ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, + 0, vmo.get(), 0, allocSize, &mapped_addr); + if (status != ZX_OK) { + ALOGE("%s: failed to map buffer: %d:%d", __FUNCTION__, status); + return nullptr; + } + + m_buf = reinterpret_cast<unsigned char*>(mapped_addr); + m_bufsize = allocSize; + m_vmo = std::move(vmo); + return m_buf; +} + +int QemuPipeStream::commitBuffer(size_t size) +{ + if (size == 0) return 0; + + size_t remaining = size; + while (remaining) { + zx_status_t status2 = ZX_OK; + uint64_t actual = 0; + zx_status_t status = fuchsia_hardware_goldfish_pipe_DeviceWrite( + m_channel.get(), remaining, size - remaining, &status2, &actual); + if (status != ZX_OK) { + ALOGD("%s: Failed writing to pipe: %d", __FUNCTION__, status); + return -1; + } + if (actual) { + remaining -= actual; + continue; + } + if (status2 != ZX_ERR_SHOULD_WAIT) { + ALOGD("%s: Error writing to pipe: %d", __FUNCTION__, status2); + return -1; + } + zx_signals_t observed = ZX_SIGNAL_NONE; + status = m_event.wait_one( + fuchsia_hardware_goldfish_pipe_SIGNAL_WRITABLE | + fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP, + zx::time::infinite(), &observed); + if (status != ZX_OK) { + ALOGD("%s: wait_one failed: %d", __FUNCTION__, status); + return -1; + } + if (observed & fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP) { + ALOGD("%s: Remote end hungup", __FUNCTION__); + return -1; + } + } + + return 0; +} + +int QemuPipeStream::writeFully(const void *buf, size_t len) +{ + ALOGE("%s: unsupported", __FUNCTION__); + abort(); + return -1; +} + +QEMU_PIPE_HANDLE QemuPipeStream::getSocket() const { + return m_sock; +} + +const unsigned char *QemuPipeStream::readFully(void *buf, size_t len) +{ + if (!m_channel.is_valid()) return nullptr; + + if (!buf) { + if (len > 0) { + ALOGE("QemuPipeStream::readFully failed, buf=NULL, len %zu, lethal" + " error, exiting.", len); + abort(); + } + return nullptr; + } + + size_t remaining = len; + while (remaining) { + size_t readSize = m_bufsize < remaining ? m_bufsize : remaining; + zx_status_t status2 = ZX_OK; + uint64_t actual = 0; + zx_status_t status = fuchsia_hardware_goldfish_pipe_DeviceRead( + m_channel.get(), readSize, 0, &status2, &actual); + if (status != ZX_OK) { + ALOGD("%s: Failed reading from pipe: %d", __FUNCTION__, status); + return nullptr; + } + if (actual) { + m_vmo.read(static_cast<char *>(buf) + (len - remaining), 0, actual); + remaining -= actual; + continue; + } + if (status2 != ZX_ERR_SHOULD_WAIT) { + ALOGD("%s: Error reading from pipe: %d", __FUNCTION__, status2); + return nullptr; + } + zx_signals_t observed = ZX_SIGNAL_NONE; + status = m_event.wait_one( + fuchsia_hardware_goldfish_pipe_SIGNAL_READABLE | + fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP, + zx::time::infinite(), &observed); + if (status != ZX_OK) { + ALOGD("%s: wait_one failed: %d", __FUNCTION__, status); + return nullptr; + } + if (observed & fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP) { + ALOGD("%s: Remote end hungup", __FUNCTION__); + return nullptr; + } + } + + return static_cast<const unsigned char *>(buf); +} + +const unsigned char *QemuPipeStream::read(void *buf, size_t *inout_len) +{ + ALOGE("%s: unsupported", __FUNCTION__); + abort(); + return nullptr; +} + +int QemuPipeStream::recv(void *buf, size_t len) +{ + ALOGE("%s: unsupported", __FUNCTION__); + abort(); + return -1; +} -- GitLab