blob: 564c75ddc1e43d21eabbe403808092e4797bc3cb [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_DEVICE_H
#define MSD_ARM_DEVICE_H
#include <fidl/fuchsia.hardware.gpu.mali/cpp/driver/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fit/thread_safety.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/magma/platform/platform_interrupt.h>
#include <lib/magma/platform/platform_semaphore.h>
#include <lib/magma/util/short_macros.h>
#include <lib/magma/util/thread.h>
#include <lib/magma_service/msd.h>
#include <lib/magma_service/msd_defs.h>
#include <lib/magma_service/util/register_io.h>
#include <zircon/compiler.h>
#include <deque>
#include <list>
#include <mutex>
#include <thread>
#include <vector>
#include "drivers/msd-arm-mali/src/address_manager.h"
#include "drivers/msd-arm-mali/src/device_request.h"
#include "drivers/msd-arm-mali/src/gpu_features.h"
#include "drivers/msd-arm-mali/src/job_scheduler.h"
#include "drivers/msd-arm-mali/src/msd_arm_connection.h"
#include "drivers/msd-arm-mali/src/performance_counters.h"
#include "drivers/msd-arm-mali/src/power_manager.h"
#include "mali_register_io.h"
#include "parent_device.h"
class MsdArmDevice : public msd::Device,
public JobScheduler::Owner,
public MsdArmConnection::Owner,
public AddressManager::Owner,
public PerformanceCounters::Owner {
public:
// Creates a device for the given |device_handle| and returns ownership.
// If |start_device_thread| is false, then StartDeviceThread should be called
// to enable device request processing.
static std::unique_ptr<MsdArmDevice> Create(msd::DeviceHandle* device_handle,
bool start_device_thread,
inspect::Node* parent_node = nullptr);
MsdArmDevice();
virtual ~MsdArmDevice();
// msd::Device impl.
void SetMemoryPressureLevel(msd::MagmaMemoryPressureLevel level) override;
magma_status_t Query(uint64_t id, zx::vmo* result_buffer_out, uint64_t* result_out) override;
magma_status_t GetIcdList(std::vector<msd::MsdIcdInfo>* icd_info_out) override;
void DumpStatus(uint32_t dump_flags) override;
std::unique_ptr<msd::Connection> Open(msd::msd_client_id_t client_id) override;
void set_inspect(inspect::Node node) { inspect_ = std::move(node); }
bool Init(msd::DeviceHandle* device_handle);
bool Init(ParentDevice* platform_device, std::unique_ptr<magma::PlatformBusMapper> bus_mapper);
std::shared_ptr<MsdArmConnection> OpenArmConnection(msd::msd_client_id_t client_id);
uint64_t GpuId() { return gpu_features_.gpu_id.reg_value(); }
struct DumpState {
struct CorePowerState {
const char* core_type;
const char* status_type;
uint64_t bitmask;
};
std::vector<CorePowerState> power_states;
// Only accounts for recent past.
uint64_t total_time_ms;
uint64_t active_time_ms;
uint32_t gpu_fault_status;
uint64_t gpu_fault_address;
uint32_t gpu_status;
uint64_t cycle_count;
uint64_t timestamp;
uint32_t gpu_irq_rawstat;
uint32_t gpu_irq_status;
uint32_t gpu_irq_mask;
bool handling_gpu_interrupt{};
uint64_t gpu_interrupt_delay{};
uint64_t gpu_interrupt_time{};
uint32_t job_irq_rawstat;
uint32_t job_irq_status;
uint32_t job_irq_mask;
uint32_t job_irq_js_state;
uint64_t job_interrupt_delay{};
uint64_t job_interrupt_time{};
uint32_t mmu_irq_rawstat;
uint32_t mmu_irq_status;
uint32_t mmu_irq_mask;
bool handling_mmu_interrupt{};
uint64_t mmu_interrupt_delay{};
uint64_t mmu_interrupt_time{};
struct JobSlotStatus {
uint32_t status;
uint64_t head;
uint64_t tail;
uint32_t config;
};
std::vector<JobSlotStatus> job_slot_status;
struct AddressSpaceStatus {
uint32_t status;
uint32_t fault_status;
uint64_t fault_address;
};
std::vector<AddressSpaceStatus> address_space_status;
};
static void DumpRegisters(const GpuFeatures& features, mali::RegisterIo* io,
DumpState* dump_state);
void Dump(DumpState* dump_state, bool from_device_thread);
void DumpToString(std::vector<std::string>* dump_string, bool from_device_thread);
void FormatDump(DumpState& dump_state, std::vector<std::string>* dump_string);
void DumpStatusToLog();
magma::Status ProcessTimestampRequest(std::shared_ptr<magma::PlatformBuffer> buffer);
void RefCycleCounter();
void DerefCycleCounter();
magma::Status QueryTimestamp(std::unique_ptr<magma::PlatformBuffer> buffer);
// MsdArmConnection::Owner implementation.
void ScheduleAtom(std::shared_ptr<MsdArmAtom> atom) override;
void CancelAtoms(std::shared_ptr<MsdArmConnection> connection) override;
AddressSpaceObserver* GetAddressSpaceObserver() override { return address_manager_.get(); }
ArmMaliCacheCoherencyStatus cache_coherency_status() override { return cache_coherency_status_; }
magma::PlatformBusMapper* GetBusMapper() override { return bus_mapper_.get(); }
bool IsProtectedModeSupported() override;
void DeregisterConnection() override;
void SetCurrentThreadToDefaultPriority() override;
PerformanceCounters* performance_counters() override { return perf_counters_.get(); }
std::shared_ptr<DeviceRequest::Reply> RunTaskOnDeviceThread(FitCallbackTask task) override;
std::thread::id GetDeviceThreadId() override { return device_thread_.get_id(); }
msd::MagmaMemoryPressureLevel GetCurrentMemoryPressureLevel() override {
std::lock_guard lock(connection_list_mutex_);
return current_memory_pressure_level_;
}
magma_status_t QueryInfo(uint64_t id, uint64_t* value_out);
magma_status_t QueryReturnsBuffer(uint64_t id, uint32_t* buffer_out);
// PerformanceCounters::Owner implementation.
AddressManager* address_manager() override { return address_manager_.get(); }
MsdArmConnection::Owner* connection_owner() override { return this; }
// Used for testing - allows the driver to assume reset happened without an interrupt.
void set_assume_reset_happened(bool assume) { assume_reset_happened_ = assume; }
private:
#define CHECK_THREAD_IS_CURRENT(x) \
if (x) \
DASSERT(magma::ThreadIdCheck::IsCurrent(*x))
#define CHECK_THREAD_NOT_CURRENT(x) \
if (x) \
DASSERT(!magma::ThreadIdCheck::IsCurrent(*x))
friend class TestMsdArmDevice;
friend class TestNonHardwareMsdArmDevice;
class DumpRequest;
class PerfCounterSampleCompletedRequest;
class MmuInterruptRequest;
class ScheduleAtomRequest;
class CancelAtomsRequest;
class TaskRequest;
class TimestampRequest;
struct InspectEvent {
InspectEvent(inspect::Node* parent, std::string type);
inspect::Node node;
};
struct MaliProperties {
bool supports_protected_mode = false;
bool use_protected_mode_callbacks = false;
};
mali::RegisterIo* register_io() override {
DASSERT(register_io_);
return register_io_.get();
}
void set_register_io(std::unique_ptr<mali::RegisterIo> register_io) {
register_io_ = std::move(register_io);
}
void Destroy();
void StartGpuInterruptThread();
void StartDeviceThread();
int DeviceThreadLoop();
int GpuInterruptThreadLoop();
int MmuInterruptThreadLoop();
bool InitializeInterrupts();
void EnableInterrupts();
void DisableInterrupts();
bool InitializeHardware();
bool InitializeDevicePropertiesBuffer();
void EnqueueDeviceRequest(std::unique_ptr<DeviceRequest> request, bool enqueue_front = false);
static void InitializeHardwareQuirks(GpuFeatures* features, mali::RegisterIo* registers);
bool PowerDownL2();
bool PowerDownShaders();
bool FlushL2();
bool ResetDevice();
void InitInspect();
void UpdateProtectedModeSupported();
void AppendInspectEvent(InspectEvent event);
// Power on all GPU cores.
void EnableAllCores();
void HandleResetInterrupt();
void WatchdogTask();
magma::Status ProcessDumpStatusToLog();
magma::Status ProcessPerfCounterSampleCompleted();
magma::Status ProcessJobInterrupt(uint64_t time);
magma::Status ProcessMmuInterrupt();
magma::Status ProcessScheduleAtoms();
magma::Status ProcessCancelAtoms(std::weak_ptr<MsdArmConnection> connection);
// Called periodically when in a critical memory state to force all contexts to clear JIT memory.
// If |force_instant| is true, this callback was called directly from a change in the critical
// memory pressure state.
void PeriodicCriticalMemoryPressureCallback(bool force_instant);
void ExecuteAtomOnDevice(MsdArmAtom* atom, mali::RegisterIo* registers);
// JobScheduler::Owner implementation.
void RunAtom(MsdArmAtom* atom) override;
void AtomCompleted(MsdArmAtom* atom, ArmMaliResultCode result) override;
void HardStopAtom(MsdArmAtom* atom) override;
void SoftStopAtom(MsdArmAtom* atom) override;
void ReleaseMappingsForAtom(MsdArmAtom* atom) override;
magma::PlatformPort* GetPlatformPort() override;
void UpdateGpuActive(bool active) override;
void EnterProtectedMode() override;
bool ExitProtectedMode() override;
bool IsInProtectedMode() override;
void OutputHangMessage(bool hardware_hang) override;
static const uint32_t kMagic = 0x64657669; //"devi"
uint64_t magic_;
inspect::Node inspect_;
inspect::Node events_;
inspect::UintProperty hang_timeout_count_;
inspect::UintProperty last_hang_timeout_ns_;
inspect::UintProperty semaphore_hang_timeout_count_;
inspect::UintProperty last_semaphore_hang_timeout_ns_;
inspect::BoolProperty protected_mode_supported_property_;
inspect::UintProperty memory_pressure_level_property_;
std::mutex inspect_events_mutex_;
FIT_GUARDED(inspect_events_mutex_) std::deque<InspectEvent> inspect_events_;
fdf::WireSyncClient<fuchsia_hardware_gpu_mali::ArmMali> mali_protocol_client_;
// Flag is set to true if reset completion should trigger FinishExitProtectedMode.
std::atomic_bool exiting_protected_mode_flag_{false};
std::thread device_thread_;
std::unique_ptr<magma::PlatformThreadId> device_thread_id_;
std::atomic_bool device_thread_quit_flag_{false};
std::atomic_bool interrupt_thread_quit_flag_{false};
std::thread gpu_interrupt_thread_;
std::thread mmu_interrupt_thread_;
std::atomic_bool handling_gpu_interrupt_;
std::atomic_bool handling_mmu_interrupt_;
std::atomic<uint64_t> job_interrupt_delay_{};
std::atomic<uint64_t> gpu_interrupt_delay_{};
std::atomic<uint64_t> mmu_interrupt_delay_{};
std::atomic<uint64_t> gpu_interrupt_time_{};
std::atomic<uint64_t> mmu_interrupt_time_{};
uint64_t job_interrupt_time_ = {};
async::Loop loop_{&kAsyncLoopConfigNeverAttachToThread};
// The watchdog loop runs WatchdogTask to help root-cause https://fxbug.dev/42069578.
async::Loop watchdog_loop_{&kAsyncLoopConfigNeverAttachToThread};
std::unique_ptr<magma::PlatformSemaphore> device_request_semaphore_;
std::unique_ptr<magma::PlatformPort> device_port_;
std::mutex device_request_mutex_;
std::list<std::unique_ptr<DeviceRequest>> device_request_list_;
// Triggered on device reset.
std::unique_ptr<magma::PlatformSemaphore> reset_semaphore_;
bool assume_reset_happened_ = false;
std::unique_ptr<magma::PlatformSemaphore> cache_clean_semaphore_;
std::mutex schedule_mutex_;
__TA_GUARDED(schedule_mutex_) std::vector<std::shared_ptr<MsdArmAtom>> atoms_to_schedule_;
ParentDevice* parent_device_;
std::unique_ptr<mali::RegisterIo> register_io_;
std::unique_ptr<magma::PlatformInterrupt> gpu_interrupt_;
std::unique_ptr<magma::PlatformInterrupt> job_interrupt_;
std::unique_ptr<magma::PlatformInterrupt> mmu_interrupt_;
MaliProperties mali_properties_{};
GpuFeatures gpu_features_;
ArmMaliCacheCoherencyStatus cache_coherency_status_ = kArmMaliCacheCoherencyNone;
std::unique_ptr<magma::PlatformBuffer> device_properties_buffer_;
std::unique_ptr<PowerManager> power_manager_;
std::unique_ptr<AddressManager> address_manager_;
std::unique_ptr<JobScheduler> scheduler_;
std::unique_ptr<magma::PlatformBusMapper> bus_mapper_;
uint64_t cycle_counter_refcount_ = 0;
std::unique_ptr<PerformanceCounters> perf_counters_;
std::mutex connection_list_mutex_;
FIT_GUARDED(connection_list_mutex_)
std::vector<std::weak_ptr<MsdArmConnection>> connection_list_;
FIT_GUARDED(connection_list_mutex_)
msd::MagmaMemoryPressureLevel current_memory_pressure_level_ =
msd::MAGMA_MEMORY_PRESSURE_LEVEL_NORMAL;
FIT_GUARDED(connection_list_mutex_)
uint32_t scheduled_memory_pressure_task_count_ = 0;
FIT_GUARDED(connection_list_mutex_)
zx::time next_scheduled_memory_pressure_task_time_{};
};
#endif // MSD_ARM_DEVICE_H