| // 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 "magma_system_connection.h" |
| |
| #include <vector> |
| |
| #include "magma_system_device.h" |
| #include "magma_util/macros.h" |
| |
| MagmaSystemConnection::MagmaSystemConnection(std::weak_ptr<MagmaSystemDevice> weak_device, |
| msd_connection_unique_ptr_t msd_connection_t) |
| : device_(weak_device), msd_connection_(std::move(msd_connection_t)) { |
| DASSERT(msd_connection_); |
| } |
| |
| MagmaSystemConnection::~MagmaSystemConnection() { |
| // Remove all contexts before clearing buffers, to give the hardware driver an |
| // indication that faults afterwards may be due to buffer mappings having gone |
| // away due to the shutdown. |
| context_map_.clear(); |
| for (auto iter = buffer_map_.begin(); iter != buffer_map_.end();) { |
| msd_connection_release_buffer(msd_connection(), iter->second.buffer->msd_buf()); |
| iter = buffer_map_.erase(iter); |
| } |
| |
| // Iterating over pool_map_ without the mutex held is safe because the map is only modified from |
| // this thread. |
| for (auto& pool_map_entry : pool_map_) { |
| msd_connection_release_performance_counter_buffer_pool(msd_connection(), |
| pool_map_entry.second.msd_pool); |
| } |
| { |
| // We still need to lock the mutex before modifying the map. |
| std::lock_guard<std::mutex> lock(pool_map_mutex_); |
| pool_map_.clear(); |
| } |
| // Reset all MSD objects before calling ConnectionClosed() because the msd device might go away |
| // any time after ConnectionClosed() and we don't want any dangling dependencies. |
| semaphore_map_.clear(); |
| msd_connection_.reset(); |
| |
| auto device = device_.lock(); |
| if (device) { |
| device->ConnectionClosed(std::this_thread::get_id()); |
| } |
| } |
| |
| uint32_t MagmaSystemConnection::GetDeviceId() { |
| auto device = device_.lock(); |
| return device ? device->GetDeviceId() : 0; |
| } |
| |
| magma::Status MagmaSystemConnection::CreateContext(uint32_t context_id) { |
| auto iter = context_map_.find(context_id); |
| if (iter != context_map_.end()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Attempting to add context with duplicate id"); |
| |
| auto msd_ctx = msd_connection_create_context(msd_connection()); |
| if (!msd_ctx) |
| return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "Failed to create msd context"); |
| |
| auto ctx = std::unique_ptr<MagmaSystemContext>( |
| new MagmaSystemContext(this, msd_context_unique_ptr_t(msd_ctx, &msd_context_destroy))); |
| |
| context_map_.insert(std::make_pair(context_id, std::move(ctx))); |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::DestroyContext(uint32_t context_id) { |
| auto iter = context_map_.find(context_id); |
| if (iter == context_map_.end()) |
| return DRETF(MAGMA_STATUS_INVALID_ARGS, |
| "MagmaSystemConnection:Attempting to destroy invalid context id"); |
| |
| context_map_.erase(iter); |
| return MAGMA_STATUS_OK; |
| } |
| |
| MagmaSystemContext* MagmaSystemConnection::LookupContext(uint32_t context_id) { |
| auto iter = context_map_.find(context_id); |
| if (iter == context_map_.end()) |
| return DRETP(nullptr, "MagmaSystemConnection: Attempting to lookup invalid context id"); |
| |
| return iter->second.get(); |
| } |
| |
| magma::Status MagmaSystemConnection::ExecuteCommandBufferWithResources( |
| uint32_t context_id, std::unique_ptr<magma_command_buffer> command_buffer, |
| std::vector<magma_exec_resource> resources, std::vector<uint64_t> semaphores) { |
| auto context = LookupContext(context_id); |
| if (!context) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "Attempting to execute command buffer on invalid context"); |
| |
| return context->ExecuteCommandBufferWithResources(std::move(command_buffer), std::move(resources), |
| std::move(semaphores)); |
| } |
| |
| magma::Status MagmaSystemConnection::ExecuteImmediateCommands(uint32_t context_id, |
| uint64_t commands_size, |
| void* commands, |
| uint64_t semaphore_count, |
| uint64_t* semaphore_ids) { |
| auto context = LookupContext(context_id); |
| if (!context) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "Attempting to execute command buffer on invalid context"); |
| |
| return context->ExecuteImmediateCommands(commands_size, commands, semaphore_count, semaphore_ids); |
| } |
| |
| magma::Status MagmaSystemConnection::EnablePerformanceCounterAccess( |
| std::unique_ptr<magma::PlatformHandle> access_token) { |
| auto device = device_.lock(); |
| if (!device) { |
| return DRET(MAGMA_STATUS_INTERNAL_ERROR); |
| } |
| uint64_t perf_count_access_token_id = device->perf_count_access_token_id(); |
| DASSERT(perf_count_access_token_id); |
| if (!access_token) { |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| } |
| if (access_token->GetId() != perf_count_access_token_id) { |
| // This is not counted as an error, since it can happen if the client uses the event from the |
| // wrong driver. |
| return MAGMA_STATUS_OK; |
| } |
| |
| DLOG("Performance counter access enabled"); |
| can_access_performance_counters_ = true; |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::ImportBuffer(uint32_t handle, uint64_t id) { |
| auto buffer = magma::PlatformBuffer::Import(handle); |
| if (!buffer) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "failed to import buffer"); |
| |
| buffer->set_local_id(id); |
| |
| auto iter = buffer_map_.find(id); |
| if (iter != buffer_map_.end()) { |
| iter->second.refcount++; |
| return MAGMA_STATUS_OK; |
| } |
| |
| BufferReference ref; |
| ref.buffer = MagmaSystemBuffer::Create(std::move(buffer)); |
| buffer_map_.insert({id, ref}); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::ReleaseBuffer(uint64_t id) { |
| auto iter = buffer_map_.find(id); |
| if (iter == buffer_map_.end()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Attempting to free invalid buffer id %lu", id); |
| |
| if (--iter->second.refcount > 0) |
| return MAGMA_STATUS_OK; |
| |
| msd_connection_release_buffer(msd_connection(), iter->second.buffer->msd_buf()); |
| buffer_map_.erase(iter); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::MapBufferGpu(uint64_t id, uint64_t gpu_va, |
| uint64_t page_offset, uint64_t page_count, |
| uint64_t flags) { |
| auto iter = buffer_map_.find(id); |
| if (iter == buffer_map_.end()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Attempting to gpu map invalid buffer id %lu", id); |
| |
| if (page_count + page_offset < page_count) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Offset overflows"); |
| |
| if (page_count + page_offset > iter->second.buffer->size() / magma::page_size()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Page offset + length too large for buffer"); |
| |
| if (!flags) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Flags must be nonzero"); |
| |
| magma::Status status = msd_connection_map_buffer_gpu( |
| msd_connection(), iter->second.buffer->msd_buf(), gpu_va, page_offset, page_count, flags); |
| if (!status.ok()) |
| return DRET_MSG(status.get(), "msd_connection_map_buffer_gpu failed"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::UnmapBufferGpu(uint64_t id, uint64_t gpu_va) { |
| auto iter = buffer_map_.find(id); |
| if (iter == buffer_map_.end()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Attempting to gpu unmap invalid buffer id"); |
| |
| magma::Status status = |
| msd_connection_unmap_buffer_gpu(msd_connection(), iter->second.buffer->msd_buf(), gpu_va); |
| if (!status.ok()) |
| return DRET_MSG(status.get(), "msd_connection_unmap_buffer_gpu failed"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::BufferRangeOp(uint64_t id, uint32_t op, uint64_t start, |
| uint64_t length) { |
| auto iter = buffer_map_.find(id); |
| if (iter == buffer_map_.end()) |
| return DRETF(false, "Attempting to commit invalid buffer id"); |
| if (start + length < start) { |
| return DRETF(false, "Offset overflows"); |
| } |
| if (start + length > iter->second.buffer->size()) { |
| return DRETF(false, "Page offset too large for buffer"); |
| } |
| return msd_connection_buffer_range_op(msd_connection(), iter->second.buffer->msd_buf(), op, start, |
| length); |
| } |
| |
| // static |
| void MagmaSystemConnection::NotificationCallback(void* token, |
| struct msd_notification_t* notification) { |
| auto connection = reinterpret_cast<MagmaSystemConnection*>(token); |
| |
| if (notification->type == MSD_CONNECTION_NOTIFICATION_PERFORMANCE_COUNTERS_READ_COMPLETED) { |
| std::lock_guard<std::mutex> lock(connection->pool_map_mutex_); |
| |
| auto& data = notification->u.perf_counter_result; |
| |
| auto pool_it = connection->pool_map_.find(data.pool_id); |
| if (pool_it == connection->pool_map_.end()) { |
| DLOG("Driver attempted to lookup deleted pool id %ld\n", data.pool_id); |
| return; |
| } |
| |
| pool_it->second.platform_pool->SendPerformanceCounterCompletion( |
| data.trigger_id, data.buffer_id, data.buffer_offset, data.timestamp, data.result_flags); |
| } else { |
| connection->platform_callback_(connection->platform_token_, notification); |
| } |
| } |
| |
| void MagmaSystemConnection::SetNotificationCallback(msd_connection_notification_callback_t callback, |
| void* token) { |
| if (!token) { |
| msd_connection_set_notification_callback(msd_connection(), nullptr, nullptr); |
| } else { |
| platform_callback_ = callback; |
| platform_token_ = token; |
| msd_connection_set_notification_callback(msd_connection(), &NotificationCallback, this); |
| } |
| } |
| |
| magma::Status MagmaSystemConnection::ImportObject(uint32_t handle, |
| magma::PlatformObject::Type object_type, |
| uint64_t client_id) { |
| if (!client_id) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "client_id must be non zero"); |
| |
| auto device = device_.lock(); |
| if (!device) |
| return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "failed to lock device"); |
| |
| switch (object_type) { |
| case magma::PlatformObject::BUFFER: |
| return ImportBuffer(handle, client_id); |
| |
| case magma::PlatformObject::SEMAPHORE: { |
| // Always import the handle to ensure it gets closed |
| auto platform_sem = magma::PlatformSemaphore::Import(handle); |
| if (!platform_sem) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "failed to import platform semaphore"); |
| |
| platform_sem->set_local_id(client_id); |
| |
| auto iter = semaphore_map_.find(client_id); |
| if (iter != semaphore_map_.end()) { |
| iter->second.refcount++; |
| return MAGMA_STATUS_OK; |
| } |
| |
| auto semaphore = MagmaSystemSemaphore::Create(std::move(platform_sem)); |
| DASSERT(semaphore); |
| |
| SemaphoreReference ref; |
| ref.semaphore = std::move(semaphore); |
| semaphore_map_.insert(std::make_pair(client_id, ref)); |
| } break; |
| default: |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| } |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::ReleaseObject(uint64_t object_id, |
| magma::PlatformObject::Type object_type) { |
| switch (object_type) { |
| case magma::PlatformObject::BUFFER: |
| return ReleaseBuffer(object_id); |
| |
| case magma::PlatformObject::SEMAPHORE: { |
| auto iter = semaphore_map_.find(object_id); |
| if (iter == semaphore_map_.end()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "Attempting to release invalid semaphore id 0x%" PRIx64, object_id); |
| |
| if (--iter->second.refcount > 0) |
| return MAGMA_STATUS_OK; |
| |
| semaphore_map_.erase(iter); |
| } break; |
| default: |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| } |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::EnablePerformanceCounters(const uint64_t* counters, |
| uint64_t counter_count) { |
| if (!can_access_performance_counters_) |
| return DRET(MAGMA_STATUS_ACCESS_DENIED); |
| |
| return msd_connection_enable_performance_counters(msd_connection(), counters, counter_count); |
| } |
| |
| magma::Status MagmaSystemConnection::CreatePerformanceCounterBufferPool( |
| std::unique_ptr<magma::PlatformPerfCountPool> pool) { |
| if (!can_access_performance_counters_) |
| return DRET(MAGMA_STATUS_ACCESS_DENIED); |
| |
| uint64_t pool_id = pool->pool_id(); |
| if (pool_map_.count(pool_id)) |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| |
| { |
| std::lock_guard<std::mutex> lock(pool_map_mutex_); |
| pool_map_[pool_id].platform_pool = std::move(pool); |
| } |
| // |pool_map_mutex_| is unlocked before calling into the driver to prevent deadlocks if the driver |
| // synchronously does MSD_CONNECTION_NOTIFICATION_PERFORMANCE_COUNTERS_READ_COMPLETED. |
| magma_status_t status = msd_connection_create_performance_counter_buffer_pool( |
| msd_connection(), pool_id, &pool_map_[pool_id].msd_pool); |
| if (status != MAGMA_STATUS_OK) { |
| std::lock_guard<std::mutex> lock(pool_map_mutex_); |
| pool_map_.erase(pool_id); |
| } |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status MagmaSystemConnection::ReleasePerformanceCounterBufferPool(uint64_t pool_id) { |
| if (!can_access_performance_counters_) |
| return DRET(MAGMA_STATUS_ACCESS_DENIED); |
| |
| msd_perf_count_pool* msd_pool = LookupPerfCountPool(pool_id); |
| if (!msd_pool) |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| |
| // |pool_map_mutex_| is unlocked before calling into the driver to prevent deadlocks if the driver |
| // synchronously does MSD_CONNECTION_NOTIFICATION_PERFORMANCE_COUNTERS_READ_COMPLETED. |
| magma_status_t status = |
| msd_connection_release_performance_counter_buffer_pool(msd_connection(), msd_pool); |
| { |
| std::lock_guard<std::mutex> lock(pool_map_mutex_); |
| pool_map_.erase(pool_id); |
| } |
| return DRET(status); |
| } |
| |
| magma::Status MagmaSystemConnection::AddPerformanceCounterBufferOffsetToPool(uint64_t pool_id, |
| uint64_t buffer_id, |
| uint64_t buffer_offset, |
| uint64_t buffer_size) { |
| if (!can_access_performance_counters_) |
| return DRET(MAGMA_STATUS_ACCESS_DENIED); |
| std::shared_ptr<MagmaSystemBuffer> buffer = LookupBuffer(buffer_id); |
| if (!buffer) { |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| } |
| msd_perf_count_pool* msd_pool = LookupPerfCountPool(pool_id); |
| if (!msd_pool) { |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| } |
| magma_status_t status = msd_connection_add_performance_counter_buffer_offset_to_pool( |
| msd_connection(), msd_pool, buffer->msd_buf(), buffer_id, buffer_offset, buffer_size); |
| return DRET(status); |
| } |
| |
| magma::Status MagmaSystemConnection::RemovePerformanceCounterBufferFromPool(uint64_t pool_id, |
| uint64_t buffer_id) { |
| if (!can_access_performance_counters_) |
| return DRET(MAGMA_STATUS_ACCESS_DENIED); |
| std::shared_ptr<MagmaSystemBuffer> buffer = LookupBuffer(buffer_id); |
| if (!buffer) { |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| } |
| |
| msd_perf_count_pool* msd_pool = LookupPerfCountPool(pool_id); |
| if (!msd_pool) |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| magma_status_t status = msd_connection_remove_performance_counter_buffer_from_pool( |
| msd_connection(), msd_pool, buffer->msd_buf()); |
| |
| return DRET(status); |
| } |
| |
| magma::Status MagmaSystemConnection::DumpPerformanceCounters(uint64_t pool_id, |
| uint32_t trigger_id) { |
| if (!can_access_performance_counters_) |
| return DRET(MAGMA_STATUS_ACCESS_DENIED); |
| |
| msd_perf_count_pool* msd_pool = LookupPerfCountPool(pool_id); |
| if (!msd_pool) |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| return msd_connection_dump_performance_counters(msd_connection(), msd_pool, trigger_id); |
| } |
| |
| magma::Status MagmaSystemConnection::ClearPerformanceCounters(const uint64_t* counters, |
| uint64_t counter_count) { |
| if (!can_access_performance_counters_) |
| return DRET(MAGMA_STATUS_ACCESS_DENIED); |
| return msd_connection_clear_performance_counters(msd_connection(), counters, counter_count); |
| } |
| |
| std::shared_ptr<MagmaSystemBuffer> MagmaSystemConnection::LookupBuffer(uint64_t id) { |
| auto iter = buffer_map_.find(id); |
| if (iter == buffer_map_.end()) |
| return DRETP(nullptr, "Attempting to lookup invalid buffer id"); |
| |
| return iter->second.buffer; |
| } |
| |
| std::shared_ptr<MagmaSystemSemaphore> MagmaSystemConnection::LookupSemaphore(uint64_t id) { |
| auto iter = semaphore_map_.find(id); |
| if (iter == semaphore_map_.end()) |
| return nullptr; |
| return iter->second.semaphore; |
| } |
| |
| msd_perf_count_pool* MagmaSystemConnection::LookupPerfCountPool(uint64_t id) { |
| auto it = pool_map_.find(id); |
| if (it == pool_map_.end()) |
| return DRETP(nullptr, "Invalid pool id %ld", id); |
| return it->second.msd_pool; |
| } |