Implement mos_gem_bo_wait
InflightList is based on the version in mesa (except converted to C++),
because it's thread-safe and the most optimized.
Change-Id: Ia0b08c49e2cdc4e3aba0e387745f14121e9f18ac
diff --git a/media_driver/linux/common/os/magma/inflight_list.cpp b/media_driver/linux/common/os/magma/inflight_list.cpp
new file mode 100644
index 0000000..4af26e3
--- /dev/null
+++ b/media_driver/linux/common/os/magma/inflight_list.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2019 Google, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "inflight_list.h"
+#include <time.h>
+
+static uint64_t gettime_ns(void)
+{
+ struct timespec current;
+ clock_gettime(CLOCK_MONOTONIC, ¤t);
+ constexpr uint64_t kNsecPerSec = 1000000000;
+ return static_cast<uint64_t>(current.tv_sec * kNsecPerSec + current.tv_nsec);
+}
+
+static uint64_t get_relative_timeout(uint64_t abs_timeout)
+{
+ uint64_t now = gettime_ns();
+
+ if (abs_timeout < now)
+ return 0;
+ return abs_timeout - now;
+}
+
+static magma_status_t wait_notification_channel(magma_handle_t channel, int64_t timeout_ns)
+{
+ magma_poll_item_t item = {
+ .handle = channel,
+ .type = MAGMA_POLL_TYPE_HANDLE,
+ .condition = MAGMA_POLL_CONDITION_READABLE,
+ };
+ return magma_poll(&item, 1, timeout_ns);
+}
+
+InflightList::InflightList()
+{
+ wait_ = wait_notification_channel;
+ read_ = magma_read_notification_channel2;
+}
+
+InflightList::~InflightList()
+{
+}
+
+void InflightList::add( uint64_t buffer_id)
+{
+ assert(buffer_id != 0);
+ buffers_.push_back(buffer_id);
+}
+
+bool InflightList::remove(uint64_t buffer_id)
+{
+ auto it = std::find(buffers_.begin(), buffers_.end(), buffer_id);
+ if (it == buffers_.end()) {
+ return false;
+ }
+ buffers_.erase(it);
+ return true;
+}
+
+bool InflightList::is_inflight(uint64_t buffer_id)
+{
+ return std::find(buffers_.begin(), buffers_.end(), buffer_id) != buffers_.end();
+}
+
+bool InflightList::TryUpdate(magma_connection_t connection)
+{
+ if (!mutex_.try_lock()) {
+ return false;
+ }
+
+ update(connection);
+
+ mutex_.unlock();
+
+ return true;
+}
+
+magma_status_t InflightList::WaitForBuffer(magma_connection_t connection,
+ magma_handle_t notification_channel, uint64_t buffer_id,
+ uint64_t timeout_ns)
+{
+ // Calculate deadline before potentially blocking on the mutex
+ uint64_t start = gettime_ns();
+ uint64_t deadline = start + timeout_ns;
+
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ magma_status_t status = MAGMA_STATUS_OK;
+
+ if (is_inflight(buffer_id)) {
+
+ while (true) {
+ // First pass optimization: optimistically try reading the notification channel;
+ // may avoid an unnecessary wait.
+ update(connection);
+
+ if (!is_inflight(buffer_id))
+ break;
+
+ if (timeout_ns == 0) {
+ // Optimization: don't bother making the wait system call since the notification
+ // channel was just drained.
+ status = MAGMA_STATUS_TIMED_OUT;
+ break;
+ }
+
+ status = wait_(notification_channel, get_relative_timeout(deadline));
+ if (status != MAGMA_STATUS_OK) {
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+void InflightList::AddAndUpdate(magma_connection_t connection,
+ struct magma_exec_resource* resources, uint32_t count)
+{
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ for (uint32_t i = 0; i < count; i++) {
+ add(resources[i].buffer_id);
+ }
+
+ update(connection);
+}
+
+void InflightList::update(magma_connection_t connection)
+{
+ uint64_t bytes_available = 0;
+ magma_bool_t more_data = false;
+ while (true) {
+ magma_status_t status =
+ read_(connection, notification_buffer, sizeof(notification_buffer),
+ &bytes_available, &more_data);
+ if (status != MAGMA_STATUS_OK) {
+ return;
+ }
+ if (bytes_available == 0)
+ return;
+ assert(bytes_available % sizeof(uint64_t) == 0);
+ for (uint32_t i = 0; i < bytes_available / sizeof(uint64_t); i++) {
+ assert(is_inflight(notification_buffer[i]));
+ remove(notification_buffer[i]);
+ }
+ if (!more_data)
+ return;
+ }
+}
diff --git a/media_driver/linux/common/os/magma/inflight_list.h b/media_driver/linux/common/os/magma/inflight_list.h
new file mode 100644
index 0000000..f652462
--- /dev/null
+++ b/media_driver/linux/common/os/magma/inflight_list.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2019 Google, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef INFLIGHT_LIST_H
+#define INFLIGHT_LIST_H
+
+#include <assert.h>
+#include <magma.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <deque>
+#include <mutex>
+
+// A convenience utility for maintaining a list of inflight command buffers,
+// by reading completed buffer ids from the magma notification channel.
+
+typedef magma_status_t (*wait_notification_channel_t)(magma_handle_t channel, int64_t timeout_ns);
+
+typedef magma_status_t (*read_notification_channel_t)(magma_connection_t connection, void* buffer,
+ uint64_t buffer_size,
+ uint64_t* buffer_size_out,
+ magma_bool_t* more_data_out);
+
+class InflightList {
+ public:
+ InflightList();
+ ~InflightList();
+
+ void add(uint64_t buffer_id);
+ bool remove(uint64_t buffer_id);
+
+ bool is_inflight(uint64_t buffer_id);
+
+ void update(magma_connection_t connection);
+
+ // Returns true if the list lock was obtained. Threadsafe.
+ bool TryUpdate(magma_connection_t connection);
+
+ // Wait for the given |buffer_id| to be removed from the inflight list. Threadsafe.
+ magma_status_t WaitForBuffer(magma_connection_t connection,
+ magma_handle_t notification_channel, uint64_t buffer_id,
+ uint64_t timeout_ns);
+
+ // Adds the given buffers to the inflight list and services the notification channel. Threadsafe.
+ void AddAndUpdate(magma_connection_t connection,
+ struct magma_exec_resource* resources, uint32_t count);
+
+ private:
+ wait_notification_channel_t wait_;
+ read_notification_channel_t read_;
+
+ std::deque<uint64_t> buffers_;
+ std::mutex mutex_;
+ uint64_t notification_buffer[4096 / sizeof(uint64_t)];
+};
+
+#endif // INFLIGHT_LIST_H
diff --git a/media_driver/linux/common/os/magma/media_srcs.cmake b/media_driver/linux/common/os/magma/media_srcs.cmake
index 73f9e46..01d3296 100644
--- a/media_driver/linux/common/os/magma/media_srcs.cmake
+++ b/media_driver/linux/common/os/magma/media_srcs.cmake
@@ -24,6 +24,7 @@
${CMAKE_CURRENT_LIST_DIR}/mos_bufmgr_magma.cpp
${CMAKE_CURRENT_LIST_DIR}/mos_bufmgr_stub.c
${CMAKE_CURRENT_LIST_DIR}/simple_allocator.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/inflight_list.cpp
${CMAKE_CURRENT_LIST_DIR}/../i915/mos_bufmgr_api.c
)
diff --git a/media_driver/linux/common/os/magma/mos_bufmgr_magma.cpp b/media_driver/linux/common/os/magma/mos_bufmgr_magma.cpp
index af79ea6..0a2cd4e 100644
--- a/media_driver/linux/common/os/magma/mos_bufmgr_magma.cpp
+++ b/media_driver/linux/common/os/magma/mos_bufmgr_magma.cpp
@@ -25,6 +25,7 @@
#include <zircon/process.h>
#include <zircon/syscalls.h>
+#include "inflight_list.h"
#include "mos_bufmgr.h"
#include "mos_bufmgr_priv.h"
#include "simple_allocator.h"
@@ -53,6 +54,7 @@
~MagmaBufMgr();
magma_connection_t connection() const { return connection_; }
+ magma_handle_t notification_handle() const { return notification_handle_; }
uint32_t device_id() const { return device_id_; }
@@ -68,11 +70,15 @@
MagmaBo* CreateBo(const char *name, unsigned long size);
+ InflightList* inflight_list() { return &inflight_list_;}
+
private:
magma_connection_t connection_ {};
+ magma_handle_t notification_handle_{};
uint32_t device_id_;
std::mutex allocator_mutex_;
std::unique_ptr<SimpleAllocator> allocator_ __attribute__((__guarded_by__(allocator_mutex_)));
+ InflightList inflight_list_;
};
@@ -413,6 +419,8 @@
MagmaBufMgr::MagmaBufMgr(magma_connection_t connection, uint32_t device_id) :
connection_(connection), device_id_(device_id) {
+ notification_handle_ = magma_get_notification_channel_handle(connection_);
+
mos_bufmgr::bo_alloc = bufmgr_bo_alloc;
mos_bufmgr::bo_alloc_for_render = bufmgr_bo_alloc_for_render;
mos_bufmgr::bo_alloc_tiled = bufmgr_bo_alloc_tiled;
@@ -569,6 +577,7 @@
constexpr uint32_t kExpectedFlags = I915_EXEC_BSD|I915_EXEC_BSD_RING1;
auto bo = static_cast<MagmaBo*>(mos_linux_bo);
+ auto bufmgr = static_cast<MagmaBufMgr*>(ctx->bufmgr);
LOG_VERBOSE("mos_gem_bo_context_exec2 bo %lu used %d context_id %u num_cliprects %d DR4 %d flags 0x%x kExpectedFlags 0x%x",
bo->id(), used, ctx->ctx_id, num_cliprects, DR4, flags, kExpectedFlags);
@@ -589,6 +598,14 @@
.flags = kMagmaIntelGenCommandBufferForVideo,
};
+ // Add to inflight list first to avoid race with any other thread reading completions from the
+ // notification channel, in case this thread is preempted just after sending the command buffer
+ // and the completion happens quickly.
+ bufmgr->inflight_list()->AddAndUpdate(
+ bufmgr->connection(), resources.data(),
+ resources.size());
+
+
magma_status_t status = magma_execute_command_buffer_with_resources2(bo->magma_bufmgr()->connection(),
ctx->ctx_id,
&command_buffer,
@@ -622,3 +639,19 @@
*prime_fd = handle;
return 0;
}
+
+int mos_gem_bo_wait(struct mos_linux_bo *mos_linux_bo, int64_t timeout_ns)
+{
+ auto bo = static_cast<MagmaBo*>(mos_linux_bo);
+ auto bufmgr = bo->magma_bufmgr();
+ magma_status_t status = bufmgr->inflight_list()->WaitForBuffer(
+ bufmgr->connection(),
+ bufmgr->notification_handle(),
+ bo->id(),
+ timeout_ns);
+ if (status != MAGMA_STATUS_OK) {
+ LOG_VERBOSE("WaitForbuffer failed: %d", status);
+ return -1;
+ }
+ return 0;
+}
diff --git a/media_driver/linux/common/os/magma/mos_bufmgr_stub.c b/media_driver/linux/common/os/magma/mos_bufmgr_stub.c
index b0aeb9c..ff88479 100644
--- a/media_driver/linux/common/os/magma/mos_bufmgr_stub.c
+++ b/media_driver/linux/common/os/magma/mos_bufmgr_stub.c
@@ -33,12 +33,6 @@
fflush(stderr); \
} while (0)
-int mos_gem_bo_wait(struct mos_linux_bo *bo, int64_t timeout_ns)
-{
- LOG_VERBOSE("mos_gem_bo_wait unimplemented");
- return 0;
-}
-
void mos_bufmgr_gem_enable_reuse(struct mos_bufmgr *bufmgr)
{
LOG_VERBOSE("mos_bufmgr_gem_enable_reuse unimplemented");