blob: d8d453da7e593c031ef4334bf5b60a622126d139 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform_connection.h"
#include "platform_connection_client.h"
#include "gtest/gtest.h"
#include <chrono>
#include <poll.h>
#include <thread>
namespace {
constexpr uint32_t kImmediateCommandCount = 128;
// The total size of all commands should not be a multiple of the receive buffer size.
constexpr uint32_t kImmediateCommandSize = 2048 * 3 / 2 / kImmediateCommandCount;
} // namespace
class TestPlatformConnection {
public:
static std::unique_ptr<TestPlatformConnection> Create();
TestPlatformConnection(std::unique_ptr<magma::PlatformConnectionClient> client_connection,
std::thread ipc_thread,
std::shared_ptr<magma::PlatformConnection> connection)
: client_connection_(std::move(client_connection)), ipc_thread_(std::move(ipc_thread)),
connection_(std::move(connection))
{
}
~TestPlatformConnection()
{
client_connection_.reset();
connection_.reset();
if (ipc_thread_.joinable())
ipc_thread_.join();
EXPECT_TRUE(test_complete);
}
void TestImportBuffer()
{
auto buf = magma::PlatformBuffer::Create(1, "test");
test_buffer_id = buf->id();
EXPECT_EQ(client_connection_->ImportBuffer(buf.get()), 0);
EXPECT_EQ(client_connection_->GetError(), 0);
}
void TestReleaseBuffer()
{
auto buf = magma::PlatformBuffer::Create(1, "test");
test_buffer_id = buf->id();
EXPECT_EQ(client_connection_->ImportBuffer(buf.get()), 0);
EXPECT_EQ(client_connection_->ReleaseBuffer(test_buffer_id), 0);
EXPECT_EQ(client_connection_->GetError(), 0);
}
void TestImportObject()
{
auto semaphore = magma::PlatformSemaphore::Create();
test_semaphore_id = semaphore->id();
uint32_t handle;
EXPECT_TRUE(semaphore->duplicate_handle(&handle));
EXPECT_EQ(client_connection_->ImportObject(handle, magma::PlatformObject::SEMAPHORE), 0);
EXPECT_EQ(client_connection_->GetError(), 0);
}
void TestReleaseObject()
{
auto semaphore = magma::PlatformSemaphore::Create();
test_semaphore_id = semaphore->id();
uint32_t handle;
EXPECT_TRUE(semaphore->duplicate_handle(&handle));
EXPECT_EQ(client_connection_->ImportObject(handle, magma::PlatformObject::SEMAPHORE), 0);
EXPECT_EQ(
client_connection_->ReleaseObject(test_semaphore_id, magma::PlatformObject::SEMAPHORE),
0);
EXPECT_EQ(client_connection_->GetError(), 0);
}
void TestCreateContext()
{
uint32_t context_id;
client_connection_->CreateContext(&context_id);
EXPECT_EQ(client_connection_->GetError(), 0);
EXPECT_EQ(test_context_id, context_id);
}
void TestDestroyContext()
{
client_connection_->DestroyContext(test_context_id);
EXPECT_EQ(client_connection_->GetError(), 0);
}
void TestExecuteCommandBuffer()
{
auto buf = magma::PlatformBuffer::Create(1, "test");
test_buffer_id = buf->id();
uint32_t handle;
EXPECT_TRUE(buf->duplicate_handle(&handle));
client_connection_->ExecuteCommandBuffer(handle, test_context_id);
EXPECT_EQ(client_connection_->GetError(), 0);
}
void TestGetError()
{
EXPECT_EQ(client_connection_->GetError(), 0);
test_complete = true;
}
void TestMapUnmapBuffer()
{
auto buf = magma::PlatformBuffer::Create(1, "test");
test_buffer_id = buf->id();
EXPECT_EQ(client_connection_->ImportBuffer(buf.get()), 0);
EXPECT_EQ(client_connection_->MapBufferGpu(buf->id(), PAGE_SIZE * 1000, 1u, 2u, 5), 0);
EXPECT_EQ(client_connection_->UnmapBufferGpu(buf->id(), PAGE_SIZE * 1000), 0);
EXPECT_EQ(client_connection_->CommitBuffer(buf->id(), 1000, 2000), 0);
EXPECT_EQ(client_connection_->GetError(), 0);
}
void TestNotificationChannel()
{
pollfd pfd = {.fd = client_connection_->GetNotificationChannelFd(), .events = POLLIN};
int poll_status = poll(&pfd, 1, 5000);
EXPECT_EQ(1, poll_status);
uint32_t out_data;
uint64_t out_data_size;
// Data was written when the channel was created, so it should be
// available.
magma_status_t status = client_connection_->ReadNotificationChannel(
&out_data, sizeof(out_data), &out_data_size);
EXPECT_EQ(MAGMA_STATUS_OK, status);
EXPECT_EQ(sizeof(out_data), out_data_size);
EXPECT_EQ(5u, out_data);
// No more data to read.
poll_status = poll(&pfd, 1, 0);
EXPECT_EQ(0, poll_status);
status = client_connection_->ReadNotificationChannel(&out_data, sizeof(out_data),
&out_data_size);
EXPECT_EQ(MAGMA_STATUS_OK, status);
EXPECT_EQ(0u, out_data_size);
// Shutdown other end of pipe.
connection_->ShutdownEvent()->Signal();
connection_.reset();
ipc_thread_.join();
EXPECT_TRUE(got_null_notification);
// Poll should still terminate early.
poll_status = poll(&pfd, 1, 5000);
EXPECT_EQ(1, poll_status);
status = client_connection_->ReadNotificationChannel(&out_data, sizeof(out_data),
&out_data_size);
EXPECT_EQ(MAGMA_STATUS_CONNECTION_LOST, status);
test_complete = true;
}
void TestExecuteImmediateCommands()
{
uint8_t commands_buffer[kImmediateCommandSize * kImmediateCommandCount] = {};
uint64_t semaphore_ids[]{0, 1, 2};
magma_system_inline_command_buffer commands[kImmediateCommandCount];
for (size_t i = 0; i < kImmediateCommandCount; i++) {
commands[i].data = commands_buffer;
commands[i].size = kImmediateCommandSize;
commands[i].semaphore_count = 3;
commands[i].semaphores = semaphore_ids;
}
client_connection_->ExecuteImmediateCommands(test_context_id, kImmediateCommandCount,
commands);
EXPECT_EQ(client_connection_->GetError(), 0);
}
void TestMultipleGetError()
{
std::vector<std::thread> threads;
for (uint32_t i = 0; i < 1000; i++) {
threads.push_back(std::thread(
[this]() { EXPECT_EQ(MAGMA_STATUS_OK, client_connection_->GetError()); }));
}
for (auto& thread : threads) {
thread.join();
}
test_complete = true;
}
static uint64_t test_buffer_id;
static uint32_t test_context_id;
static uint64_t test_semaphore_id;
static bool got_null_notification;
static magma_status_t test_error;
static bool test_complete;
static std::unique_ptr<magma::PlatformSemaphore> test_semaphore;
private:
static void IpcThreadFunc(std::shared_ptr<magma::PlatformConnection> connection)
{
while (connection->HandleRequest())
;
}
std::unique_ptr<magma::PlatformConnectionClient> client_connection_;
std::thread ipc_thread_;
std::shared_ptr<magma::PlatformConnection> connection_;
};
uint64_t TestPlatformConnection::test_buffer_id;
uint64_t TestPlatformConnection::test_semaphore_id;
uint32_t TestPlatformConnection::test_context_id;
magma_status_t TestPlatformConnection::test_error;
bool TestPlatformConnection::test_complete;
std::unique_ptr<magma::PlatformSemaphore> TestPlatformConnection::test_semaphore;
bool TestPlatformConnection::got_null_notification;
class TestDelegate : public magma::PlatformConnection::Delegate {
public:
bool ImportBuffer(uint32_t handle, uint64_t* buffer_id_out) override
{
auto buf = magma::PlatformBuffer::Import(handle);
EXPECT_EQ(buf->id(), TestPlatformConnection::test_buffer_id);
TestPlatformConnection::test_complete = true;
return true;
}
bool ReleaseBuffer(uint64_t buffer_id) override
{
EXPECT_EQ(buffer_id, TestPlatformConnection::test_buffer_id);
TestPlatformConnection::test_complete = true;
return true;
}
bool ImportObject(uint32_t handle, magma::PlatformObject::Type object_type) override
{
auto semaphore = magma::PlatformSemaphore::Import(handle);
EXPECT_EQ(semaphore->id(), TestPlatformConnection::test_semaphore_id);
TestPlatformConnection::test_complete = true;
return true;
}
bool ReleaseObject(uint64_t object_id, magma::PlatformObject::Type object_type) override
{
EXPECT_EQ(object_id, TestPlatformConnection::test_semaphore_id);
TestPlatformConnection::test_complete = true;
return true;
}
bool CreateContext(uint32_t context_id) override
{
TestPlatformConnection::test_context_id = context_id;
TestPlatformConnection::test_complete = true;
return true;
}
bool DestroyContext(uint32_t context_id) override
{
EXPECT_EQ(context_id, TestPlatformConnection::test_context_id);
TestPlatformConnection::test_complete = true;
return true;
}
magma::Status ExecuteCommandBuffer(uint32_t command_buffer_handle, uint32_t context_id) override
{
auto buffer = magma::PlatformBuffer::Import(command_buffer_handle);
EXPECT_EQ(buffer->id(), TestPlatformConnection::test_buffer_id);
EXPECT_EQ(context_id, TestPlatformConnection::test_context_id);
TestPlatformConnection::test_complete = true;
return MAGMA_STATUS_OK;
}
bool MapBufferGpu(uint64_t buffer_id, uint64_t gpu_va, uint64_t page_offset,
uint64_t page_count, uint64_t flags) override
{
EXPECT_EQ(TestPlatformConnection::test_buffer_id, buffer_id);
EXPECT_EQ(PAGE_SIZE * 1000lu, gpu_va);
EXPECT_EQ(1u, page_offset);
EXPECT_EQ(2u, page_count);
EXPECT_EQ(5u, flags);
return true;
}
bool UnmapBufferGpu(uint64_t buffer_id, uint64_t gpu_va) override
{
EXPECT_EQ(TestPlatformConnection::test_buffer_id, buffer_id);
EXPECT_EQ(PAGE_SIZE * 1000lu, gpu_va);
return true;
}
bool CommitBuffer(uint64_t buffer_id, uint64_t page_offset, uint64_t page_count) override
{
EXPECT_EQ(TestPlatformConnection::test_buffer_id, buffer_id);
EXPECT_EQ(1000lu, page_offset);
EXPECT_EQ(2000lu, page_count);
return true;
}
void SetNotificationCallback(msd_connection_notification_callback_t callback,
void* token) override
{
if (!token) {
// This doesn't count as test complete because it should happen in every test when the
// server shuts down.
TestPlatformConnection::got_null_notification = true;
} else {
uint32_t data = 5;
msd_notification_t n = {.type = MSD_CONNECTION_NOTIFICATION_CHANNEL_SEND};
*reinterpret_cast<uint32_t*>(n.u.channel_send.data) = data;
n.u.channel_send.size = sizeof(data);
callback(token, &n);
}
}
magma::Status ExecuteImmediateCommands(uint32_t context_id, uint64_t commands_size,
void* commands, uint64_t semaphore_count,
uint64_t* semaphores) override
{
EXPECT_GE(2048u, commands_size);
uint8_t received_bytes[2048] = {};
EXPECT_EQ(0, memcmp(received_bytes, commands, commands_size));
uint64_t command_count = commands_size / kImmediateCommandSize;
EXPECT_EQ(3u * command_count, semaphore_count);
for (uint32_t i = 0; i < command_count; i++) {
EXPECT_EQ(0u, semaphores[0]);
EXPECT_EQ(1u, semaphores[1]);
EXPECT_EQ(2u, semaphores[2]);
semaphores += 3;
}
immediate_commands_bytes_executed_ += commands_size;
TestPlatformConnection::test_complete =
immediate_commands_bytes_executed_ == kImmediateCommandSize * kImmediateCommandCount;
return MAGMA_STATUS_OK;
}
uint64_t immediate_commands_bytes_executed_ = 0;
};
std::unique_ptr<TestPlatformConnection> TestPlatformConnection::Create()
{
test_buffer_id = 0xcafecafecafecafe;
test_semaphore_id = ~0u;
test_context_id = 0xdeadbeef;
test_error = 0x12345678;
test_complete = false;
got_null_notification = false;
auto delegate = std::make_unique<TestDelegate>();
auto connection = magma::PlatformConnection::Create(std::move(delegate));
if (!connection)
return DRETP(nullptr, "failed to create PlatformConnection");
auto client_connection = magma::PlatformConnectionClient::Create(
connection->GetClientEndpoint(), connection->GetClientNotificationEndpoint());
if (!client_connection)
return DRETP(nullptr, "failed to create PlatformConnectionClient");
auto ipc_thread = std::thread(IpcThreadFunc, connection);
return std::make_unique<TestPlatformConnection>(std::move(client_connection),
std::move(ipc_thread), connection);
}
TEST(PlatformConnection, GetError)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestGetError();
}
TEST(PlatformConnection, ImportBuffer)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestImportBuffer();
}
TEST(PlatformConnection, ReleaseBuffer)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestReleaseBuffer();
}
TEST(PlatformConnection, ImportObject)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestImportObject();
}
TEST(PlatformConnection, ReleaseObject)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestReleaseObject();
}
TEST(PlatformConnection, CreateContext)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestCreateContext();
}
TEST(PlatformConnection, DestroyContext)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestDestroyContext();
}
TEST(PlatformConnection, ExecuteCommandBuffer)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestExecuteCommandBuffer();
}
TEST(PlatformConnection, MapUnmapBuffer)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestMapUnmapBuffer();
}
TEST(PlatformConnection, NotificationChannel)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestNotificationChannel();
}
TEST(PlatformConnection, ExecuteImmediateCommands)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestExecuteImmediateCommands();
}
TEST(PlatformConnection, MultipleGetError)
{
auto Test = TestPlatformConnection::Create();
ASSERT_NE(Test, nullptr);
Test->TestMultipleGetError();
}