From b9e1da2ad149b48b63ed45c1255857ad25c171fe Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 27 Mar 2023 17:00:58 +0200 Subject: [PATCH] base: High-level interface for hardware performance counters. --- repos/base-nova/src/lib/base/perf.cc | 87 ++++++++++++++++++++++++++ repos/base/include/base/trace/perf.h | 93 ++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 repos/base-nova/src/lib/base/perf.cc create mode 100644 repos/base/include/base/trace/perf.h diff --git a/repos/base-nova/src/lib/base/perf.cc b/repos/base-nova/src/lib/base/perf.cc new file mode 100644 index 0000000000..fccc3a9b5c --- /dev/null +++ b/repos/base-nova/src/lib/base/perf.cc @@ -0,0 +1,87 @@ + +/* + * \brief Performance Counter infrastructure, NOVA-specific implemantation + * \author Michael Müller + * \date 2022-12-15 + */ + +#include + +#include +#include +#include + +unsigned long Genode::Trace::Performance_counter::private_freemask { 0xffff }; +unsigned long Genode::Trace::Performance_counter::shared_freemask { 0xffff0000 }; + +void Genode::Trace::Performance_counter::_init_masks() +{ + Nova::Hip::Cpu_desc::Vendor vendor = Nova::Hip::Cpu_desc::AMD; + if (vendor == Nova::Hip::Cpu_desc::AMD) + { + private_freemask = 0x3f; // 6 core performance counters + shared_freemask = 0x1f0000; // 5 L3 complex performance counters + } + else if (vendor == Nova::Hip::Cpu_desc::INTEL) + { + private_freemask = 0x7fff; + shared_freemask = 0x7fff0000; // 15 CBO performance counters + } +} + +void Genode::Trace::Performance_counter::setup(unsigned counter, uint64_t event, uint64_t mask, uint64_t flags) +{ + Nova::mword_t evt = event; + Nova::mword_t msk = mask; + Nova::mword_t flg = flags; + Nova::uint8_t rc; + Nova::mword_t type = (counter >>4); + Nova::mword_t sel = type == Performance_counter::CORE ? counter : counter & 0xf; + + if ((rc = (Nova::hpc_ctrl(Nova::HPC_SETUP, sel, type, evt, msk, flg))) != Nova::NOVA_OK) + throw Genode::Trace::Pfc_access_error(rc); +} + +void Genode::Trace::Performance_counter::start(unsigned counter) +{ + Nova::uint8_t rc; + Nova::mword_t type = (counter >> 4); + Nova::mword_t sel = type == Performance_counter::CORE ? counter : counter >>4; + + if ((rc = Nova::hpc_start(sel, type)) != Nova::NOVA_OK) + throw Genode::Trace::Pfc_access_error(rc); +} + +void Genode::Trace::Performance_counter::stop(unsigned counter) +{ + Nova::uint8_t rc; + Nova::mword_t type = (counter >>4); + Nova::mword_t sel = type == Performance_counter::CORE ? counter : counter & 0xf; + + if ((rc = Nova::hpc_stop(sel, type)) != Nova::NOVA_OK) + throw Genode::Trace::Pfc_access_error(rc); +} + +void Genode::Trace::Performance_counter::reset(unsigned counter, unsigned val) +{ + Nova::uint8_t rc; + Nova::mword_t type = (counter >>4); + Nova::mword_t sel = type == Performance_counter::CORE ? counter : counter & 0xf; + + if ((rc = Nova::hpc_reset(sel, type, val)) != Nova::NOVA_OK) + throw Genode::Trace::Pfc_access_error(rc); +} + +Genode::uint64_t Genode::Trace::Performance_counter::read(unsigned counter) +{ + Nova::uint8_t rc; + Nova::mword_t value = 0; + Nova::mword_t type = (counter >>4); + Nova::mword_t sel = type == Performance_counter::CORE ? counter : counter & 0xf; + + if ((rc = Nova::hpc_read(sel, type, value)) != Nova::NOVA_OK) + throw Genode::Trace::Pfc_access_error(rc); + + Genode::log("Performance_counter::read = ", value); + return static_cast(value); +} \ No newline at end of file diff --git a/repos/base/include/base/trace/perf.h b/repos/base/include/base/trace/perf.h new file mode 100644 index 0000000000..e2eb6ee81b --- /dev/null +++ b/repos/base/include/base/trace/perf.h @@ -0,0 +1,93 @@ +/* + * \brief Performance Counter infrastructure + * \author Michael Müller + * \date 2022-12-15 + */ + +#pragma once + +#include + +namespace Genode { namespace Trace { + + class Pfc_no_avail { + }; + + class Performance_counter + { + + private: + static unsigned long private_freemask; + static unsigned long shared_freemask; + + static unsigned _alloc(unsigned long *free_mask) + { + unsigned long current_mask, new_mask; + unsigned bit; + + do + { + current_mask = *free_mask; + bit = __builtin_ffsl(current_mask); + new_mask = current_mask & ~(1 << (bit - 1)); + } while (!__atomic_compare_exchange(free_mask, ¤t_mask, &new_mask, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)); + + if (!bit) // Allocation failed + throw Pfc_no_avail(); + + return bit - 1; // number of the allocated counter + } + + static void _init_masks(); + + public: + typedef unsigned int Counter; + + enum Type + { + CORE = 0, + CACHE = 1 + }; + + static unsigned acquire(Type type) { + return (type == Type::CORE) ? alloc_core() : alloc_cbo(); + } + + static unsigned alloc_cbo() { + if (shared_freemask == 0xffff0000) + _init_masks(); + return _alloc(&shared_freemask); + } + + static unsigned alloc_core() { + if (private_freemask == 0xffff) + _init_masks(); + return _alloc(&private_freemask); + } + + static void release(unsigned counter) { + bool core = static_cast(counter >> 4); + if (core) + private_freemask |= (1 << counter); + else + shared_freemask |= (1 << counter); + } + + static void setup(unsigned counter, Genode::uint64_t event, Genode::uint64_t mask, Genode::uint64_t flags); + static void start(unsigned counter); + static void stop(unsigned counter); + static void reset(unsigned counter, unsigned val=0); + static uint64_t read(unsigned counter); + }; + + class Pfc_access_error { + private: + Genode::uint8_t _rc; + + public: + Pfc_access_error(uint8_t rc) : _rc(rc) {} + Genode::uint8_t error_code() { return _rc; } + }; + + } +} \ No newline at end of file