blob: df2aac24c7071d95efff74ada46913b94614258d [file]
// Copyright 2017 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.
#ifndef MSD_ARM_CONNECTION_H
#define MSD_ARM_CONNECTION_H
#include <lib/fit/function.h>
#include <lib/fit/thread_safety.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/magma/util/short_macros.h>
#include <lib/magma_service/msd.h>
#include <lib/magma_service/msd_defs.h>
#include <lib/magma_service/util/address_space_allocator.h>
#include <zircon/compiler.h>
#include <deque>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
#include "drivers/msd-arm-mali/include/magma_arm_mali_types.h"
#include "drivers/msd-arm-mali/src/address_space.h"
#include "drivers/msd-arm-mali/src/device_request.h"
#include "drivers/msd-arm-mali/src/gpu_mapping.h"
#include "drivers/msd-arm-mali/src/msd_arm_atom.h"
#include "drivers/msd-arm-mali/src/msd_arm_buffer.h"
#include "drivers/msd-arm-mali/src/msd_arm_semaphore.h"
#include "drivers/msd-arm-mali/src/performance_counters_manager.h"
struct magma_arm_mali_atom;
class MsdArmPerfCountPool;
class PerformanceCounters;
// This can only be accessed on the connection thread.
class MsdArmConnection : public std::enable_shared_from_this<MsdArmConnection>,
public GpuMapping::Owner,
public AddressSpace::Owner {
public:
class Owner {
public:
virtual void ScheduleAtom(std::shared_ptr<MsdArmAtom> atom) = 0;
virtual void CancelAtoms(std::shared_ptr<MsdArmConnection> connection) = 0;
virtual AddressSpaceObserver* GetAddressSpaceObserver() = 0;
virtual ArmMaliCacheCoherencyStatus cache_coherency_status() = 0;
virtual magma::PlatformBusMapper* GetBusMapper() = 0;
virtual bool IsProtectedModeSupported() = 0;
// Called after the connection's destructor has been called, so the
// refcount should be 0.
virtual void DeregisterConnection() = 0;
virtual void SetCurrentThreadToDefaultPriority() = 0;
virtual PerformanceCounters* performance_counters() = 0;
virtual std::shared_ptr<DeviceRequest::Reply> RunTaskOnDeviceThread(FitCallbackTask task) = 0;
virtual std::thread::id GetDeviceThreadId() = 0;
virtual msd::MagmaMemoryPressureLevel GetCurrentMemoryPressureLevel() = 0;
};
static std::shared_ptr<MsdArmConnection> Create(msd::msd_client_id_t client_id, Owner* owner);
virtual ~MsdArmConnection();
msd::msd_client_id_t client_id() { return client_id_; }
void InitializeInspectNode(inspect::Node* parent);
AddressSpace* address_space_for_testing() __TA_NO_THREAD_SAFETY_ANALYSIS {
return address_space_.get();
}
const AddressSpace* const_address_space() const __TA_NO_THREAD_SAFETY_ANALYSIS {
return address_space_.get();
}
// GpuMapping::Owner implementation.
bool RemoveMapping(uint64_t gpu_va) override;
bool UpdateCommittedMemory(GpuMapping* mapping) override;
bool AddMapping(std::unique_ptr<GpuMapping> mapping);
bool ExecuteAtom(size_t* remaining_data_size, magma_arm_mali_atom* atom,
std::vector<std::shared_ptr<magma::PlatformSemaphore>>& semaphores,
std::deque<std::shared_ptr<magma::PlatformSemaphore>>* deprecated_semaphores);
void SetNotificationCallback(msd::NotificationHandler* handler);
void SendNotificationData(MsdArmAtom* atom);
void MarkDestroyed();
// Returns the number of bytes freed due to the change.
size_t PeriodicMemoryPressureCallback();
// Tries to allocate JIT memory for an atom. Returns a status if allocation
// finished (successfully or not) or no status if the allocation needs to be
// retried after a free is completed.
[[nodiscard]] std::optional<ArmMaliResultCode> AllocateJitMemory(
const std::shared_ptr<MsdArmSoftAtom>& atom);
// Process a JIT memory free operation. Doesn't modify the result code of the atom.
void ReleaseJitMemory(const std::shared_ptr<MsdArmSoftAtom>& atom);
// Called only on device thread.
void set_address_space_lost() { address_space_lost_ = true; }
bool address_space_lost() const { return address_space_lost_; }
AddressSpaceObserver* GetAddressSpaceObserver() override {
return owner_->GetAddressSpaceObserver();
}
std::shared_ptr<AddressSpace::Owner> GetSharedPtr() override { return shared_from_this(); }
bool PageInMemory(uint64_t address);
bool SetCommittedPagesForBuffer(MsdArmBuffer* buffer, uint64_t page_offset, uint64_t page_count);
bool CommitMemoryForBuffer(MsdArmBuffer* buffer, uint64_t page_offset, uint64_t page_count);
bool DecommitMemoryForBuffer(MsdArmBuffer* buffer, uint64_t page_offset, uint64_t page_count);
// This is slow because it iterates over all pages for all mappings. It should be used only
// rarely.
bool GetVirtualAddressFromPhysical(uint64_t address, uint64_t* virtual_address_out);
void IncrementContextCount() { context_count_++; }
void DecrementContextCount() { context_count_--; }
uint64_t context_count() const { return context_count_; }
void SendPerfCounterNotification(const msd::PerfCounterResult& results);
magma_status_t EnablePerformanceCounters(std::vector<uint64_t> flags);
magma_status_t DumpPerformanceCounters(std::shared_ptr<MsdArmPerfCountPool> pool,
uint32_t trigger_id);
magma_status_t ReleasePerformanceCounterBufferPool(std::shared_ptr<MsdArmPerfCountPool> pool);
magma_status_t AddPerformanceCounterBufferOffsetToPool(std::shared_ptr<MsdArmPerfCountPool> pool,
std::shared_ptr<MsdArmBuffer> buffer,
uint64_t buffer_id, uint64_t buffer_offset,
uint64_t buffer_size);
magma_status_t RemovePerformanceCounterBufferFromPool(std::shared_ptr<MsdArmPerfCountPool> pool,
std::shared_ptr<MsdArmBuffer> buffer);
std::thread::id GetDeviceThreadId() { return owner_->GetDeviceThreadId(); }
private:
static const uint32_t kMagic = 0x636f6e6e; // "conn" (Connection)
friend class TestConnection;
struct JitMemoryRegion {
// ID the client uses to refer to this region while it's allocated. If 0, the region is not
// currently in use.
uint8_t id;
// Bin ID of the region. Bin IDs must match for the region to be reused.
uint8_t bin_id;
// Usage ID of the region. Usage ID preferably matches.
uint16_t usage_id;
uint64_t gpu_address;
// Number of initial committed pages requested. The region may grow in size
// while in use, and may be shrunk when freed.
uint64_t committed_pages;
std::shared_ptr<MsdArmBuffer> buffer;
inspect::Node node;
inspect::UintProperty id_property;
inspect::UintProperty comitted_page_count_property;
inspect::UintProperty requested_comitted_pages_property;
};
struct JitProperties {
uint8_t trim_level;
uint8_t max_allocations;
};
struct ConnectionPerfCountManager final : public PerformanceCountersManager {
// PerformanceCountersManager implementation. Only called on device thread.
std::vector<uint64_t> EnabledPerfCountFlags() override { return enabled_performance_counters_; }
// Only modified on device thread.
std::vector<uint64_t> enabled_performance_counters_;
};
MsdArmConnection(msd::msd_client_id_t client_id, Owner* owner);
bool Init();
JitMemoryRegion* FindBestJitRegionAddressWithUsage(const magma_arm_jit_memory_allocate_info& info,
bool check_usage) FIT_REQUIRES(address_lock_);
uint64_t FindBestJitRegionAddress(const magma_arm_jit_memory_allocate_info& info);
std::optional<ArmMaliResultCode> AllocateNewJitMemoryRegion(
const magma_arm_jit_memory_allocate_info& info, uint64_t* address_out);
ArmMaliResultCode WriteJitRegionAdddress(const magma_arm_jit_memory_allocate_info& info,
uint64_t address);
// Returns a result code on success or failure. Returns no value if allocation is delayed.
std::optional<ArmMaliResultCode> AllocateOneJitMemoryRegion(
const magma_arm_jit_memory_allocate_info& info);
void ReleaseOneJitMemory(const magma_arm_jit_memory_free_info& info);
// Release all unused JIT regions to save memory. Returns the number of bytes freed.
size_t FreeUnusedJitRegionsIfNeeded() FIT_REQUIRES(address_lock_);
bool RemoveMappingLocked(uint64_t gpu_va) FIT_REQUIRES(address_lock_);
PerformanceCounters* performance_counters() { return owner_->performance_counters(); }
magma::PlatformBusMapper* GetBusMapper() override { return owner_->GetBusMapper(); }
msd::msd_client_id_t client_id_;
inspect::Node node_;
inspect::Node jit_regions_;
inspect::UintProperty client_id_property_;
std::mutex address_lock_;
__THREAD_ANNOTATION(__pt_guarded_by__(address_lock_))
std::unique_ptr<AddressSpace> address_space_;
// Map GPU va to a mapping.
FIT_GUARDED(address_lock_) std::map<uint64_t, std::unique_ptr<GpuMapping>> gpu_mappings_;
FIT_GUARDED(address_lock_) JitProperties jit_properties_;
FIT_GUARDED(address_lock_) std::list<JitMemoryRegion> jit_memory_regions_;
FIT_GUARDED(address_lock_) std::unique_ptr<magma::AddressSpaceAllocator> jit_allocator_;
// Store a list of a small number of mappings to help debug issues when references to freed
// memory.
static constexpr uint32_t kMaxStoredRemovedMappings = 64;
std::deque<std::pair</*gpu_va=*/uint64_t, /*len=*/uint64_t>> recently_removed_mappings_;
Owner* owner_;
// Modified and accessed only from device thread.
bool address_space_lost_ = false;
std::mutex callback_lock_;
msd::NotificationHandler* notification_handler_{};
std::shared_ptr<MsdArmAtom> outstanding_atoms_[256];
std::atomic<uint32_t> context_count_{0};
std::atomic<uint64_t> received_atom_count_{0};
std::atomic<uint64_t> notified_atom_count_{0};
std::vector<magma_arm_mali_status> coalescing_notifications_ FIT_GUARDED(callback_lock_);
std::shared_ptr<ConnectionPerfCountManager> perf_count_manager_;
};
class MsdArmAbiConnection : public msd::Connection {
public:
MsdArmAbiConnection(std::shared_ptr<MsdArmConnection> ptr) : ptr_(std::move(ptr)) {
magic_ = kMagic;
}
magma_status_t MapBuffer(msd::Buffer& buffer, uint64_t gpu_va, uint64_t offset, uint64_t length,
uint64_t flags) override;
magma_status_t UnmapBuffer(msd::Buffer& buffer, uint64_t gpu_va) override;
magma_status_t BufferRangeOp(msd::Buffer& buffer, uint32_t options, uint64_t start_offset,
uint64_t length) override;
void ReleaseBuffer(msd::Buffer& buffer) override;
void SetNotificationCallback(msd::NotificationHandler* handler) override;
std::unique_ptr<msd::Context> CreateContext() override;
magma_status_t EnablePerformanceCounters(cpp20::span<const uint64_t> counters) override;
magma_status_t CreatePerformanceCounterBufferPool(
uint64_t pool_id, std::unique_ptr<msd::PerfCountPool>* pool_out) override;
magma_status_t ReleasePerformanceCounterBufferPool(
std::unique_ptr<msd::PerfCountPool> pool) override;
magma_status_t AddPerformanceCounterBufferOffsetToPool(msd::PerfCountPool& pool,
msd::Buffer& buffer, uint64_t buffer_id,
uint64_t buffer_offset,
uint64_t buffer_size) override;
magma_status_t RemovePerformanceCounterBufferFromPool(msd::PerfCountPool& pool,
msd::Buffer& buffer) override;
magma_status_t DumpPerformanceCounters(msd::PerfCountPool& pool, uint32_t trigger_id) override;
magma_status_t ClearPerformanceCounters(cpp20::span<const uint64_t> counters) override;
std::shared_ptr<MsdArmConnection> ptr() { return ptr_; }
private:
std::shared_ptr<MsdArmConnection> ptr_;
static const uint32_t kMagic = 0x636f6e6e; // "conn" (Connection)
uint32_t magic_;
};
#endif // MSD_ARM_CONNECTION_H