diff --git a/src/cpu/kvm/BaseKvmCPU.py b/src/cpu/kvm/BaseKvmCPU.py index a6590ac4f4..2c90acf3e8 100644 --- a/src/cpu/kvm/BaseKvmCPU.py +++ b/src/cpu/kvm/BaseKvmCPU.py @@ -72,12 +72,6 @@ class BaseKvmCPU(BaseCPU): usePerfOverflow = Param.Bool( False, "Use perf event overflow counters (EXPERIMENTAL)" ) - allowHybridPerf = Param.Bool( - True, - "Enable hybrid performance counters if hybrid host architecture " - "detected. Required for accurate stats if gem5 may run on E-core on a " - "hybrid host architecture (uncommon).", - ) alwaysSyncTC = Param.Bool( False, "Always sync thread contexts on entry/exit" ) diff --git a/src/cpu/kvm/base.cc b/src/cpu/kvm/base.cc index 1c9764465d..eaa771d8cf 100644 --- a/src/cpu/kvm/base.cc +++ b/src/cpu/kvm/base.cc @@ -72,7 +72,6 @@ BaseKvmCPU::BaseKvmCPU(const BaseKvmCPUParams ¶ms) threadContextDirty(true), kvmStateDirty(false), usePerf(params.usePerf), - allowHybridPerf(params.allowHybridPerf), vcpuID(-1), vcpuFD(-1), vcpuMMapSize(0), _kvmRun(NULL), mmioRing(NULL), pageSize(sysconf(_SC_PAGE_SIZE)), @@ -108,8 +107,8 @@ BaseKvmCPU::BaseKvmCPU(const BaseKvmCPUParams ¶ms) // If we use perf, we create new PerfKVMCounters if (usePerf) { - hwCycles.reset(PerfKvmCounter::create(allowHybridPerf)); - hwInstructions.reset(PerfKvmCounter::create(allowHybridPerf)); + hwCycles = std::unique_ptr(new PerfKvmCounter()); + hwInstructions = std::unique_ptr(new PerfKvmCounter()); } else { inform("Using KVM CPU without perf. The stats related to the number " "of cycles and instructions executed by the KVM CPU will not " @@ -1410,7 +1409,7 @@ BaseKvmCPU::setupInstCounter(uint64_t period) assert(hwCycles->attached()); hwInstructions->attach(cfgInstructions, 0, // TID (0 => currentThread) - hwCycles.get()); + *hwCycles); if (period) hwInstructions->enableSignals(KVM_KICK_SIGNAL); diff --git a/src/cpu/kvm/base.hh b/src/cpu/kvm/base.hh index e592c27688..3cf70a0bef 100644 --- a/src/cpu/kvm/base.hh +++ b/src/cpu/kvm/base.hh @@ -656,12 +656,6 @@ class BaseKvmCPU : public BaseCPU /** True if using perf; False otherwise*/ bool usePerf; - /** - * Whether to permit using hybrid performance counters if hybrid host - * architecture is auto-detected. - */ - bool allowHybridPerf; - /** KVM internal ID of the vCPU */ long vcpuID; diff --git a/src/cpu/kvm/perfevent.cc b/src/cpu/kvm/perfevent.cc index 187a0cdd76..c5e33abf82 100644 --- a/src/cpu/kvm/perfevent.cc +++ b/src/cpu/kvm/perfevent.cc @@ -35,7 +35,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include @@ -68,54 +67,33 @@ PerfKvmCounterConfig::~PerfKvmCounterConfig() { } + +PerfKvmCounter::PerfKvmCounter(PerfKvmCounterConfig &config, pid_t tid) + : fd(-1), ringBuffer(NULL), pageSize(-1) +{ + attach(config, tid, -1); +} + +PerfKvmCounter::PerfKvmCounter(PerfKvmCounterConfig &config, + pid_t tid, const PerfKvmCounter &parent) + : fd(-1), ringBuffer(NULL), pageSize(-1) +{ + attach(config, tid, parent); +} + PerfKvmCounter::PerfKvmCounter() -{ -} - -PerfKvmCounter * -PerfKvmCounter::create(bool allow_hybrid) -{ - // Check if we're running on a hybrid host architecture. Linux exposes - // this via sysfs. If the directory /sys/devices/cpu exists, then we are - // running on a regular architecture. Otherwise, /sys/devices/cpu_core and - // /sys/devices/cpu_atom should exist. For simplicity, we use the - // existence of /sys/devices/cpu_atom to indicate a hybrid host - // architecture. - const char *atom_path = "/sys/devices/cpu_atom"; - if (allow_hybrid) { - if (DIR *atom_dir = opendir(atom_path)) { - closedir(atom_dir); - - // Since we're running on a hybrid architecture, use a hybrid - // performance counter. This uses two 'physical' performance - // counters to implement a 'logical' one which is the sum of the - // two. - return new HybridPerfKvmCounter(); - } - - if (errno != ENOENT) - warn("Unexpected error code from opendir(%s): %s\n", - atom_path, std::strerror(errno)); - } - - // We're running on a regular architecture, so use a regular - // performance counter. - return new SimplePerfKvmCounter(); -} - -SimplePerfKvmCounter::SimplePerfKvmCounter() : fd(-1), ringBuffer(NULL), pageSize(-1) { } -SimplePerfKvmCounter::~SimplePerfKvmCounter() +PerfKvmCounter::~PerfKvmCounter() { if (attached()) detach(); } void -SimplePerfKvmCounter::detach() +PerfKvmCounter::detach() { assert(attached()); @@ -129,35 +107,35 @@ SimplePerfKvmCounter::detach() } void -SimplePerfKvmCounter::start() +PerfKvmCounter::start() { if (ioctl(PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) panic("KVM: Failed to enable performance counters (%i)\n", errno); } void -SimplePerfKvmCounter::stop() +PerfKvmCounter::stop() { if (ioctl(PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) panic("KVM: Failed to disable performance counters (%i)\n", errno); } void -SimplePerfKvmCounter::period(uint64_t period) +PerfKvmCounter::period(uint64_t period) { if (ioctl(PERF_EVENT_IOC_PERIOD, &period) == -1) panic("KVM: Failed to set period of performance counter (%i)\n", errno); } void -SimplePerfKvmCounter::refresh(int refresh) +PerfKvmCounter::refresh(int refresh) { if (ioctl(PERF_EVENT_IOC_REFRESH, refresh) == -1) panic("KVM: Failed to refresh performance counter (%i)\n", errno); } uint64_t -SimplePerfKvmCounter::read() const +PerfKvmCounter::read() const { uint64_t value; @@ -166,7 +144,7 @@ SimplePerfKvmCounter::read() const } void -SimplePerfKvmCounter::enableSignals(pid_t tid, int signal) +PerfKvmCounter::enableSignals(pid_t tid, int signal) { struct f_owner_ex sigowner; @@ -181,15 +159,11 @@ SimplePerfKvmCounter::enableSignals(pid_t tid, int signal) } void -SimplePerfKvmCounter::attach(const PerfKvmCounterConfig &config, - pid_t tid, const PerfKvmCounter *parent) +PerfKvmCounter::attach(PerfKvmCounterConfig &config, + pid_t tid, int group_fd) { assert(!attached()); - int group_fd = -1; - if (parent) - group_fd = dynamic_cast(*parent).fd; - fd = syscall(__NR_perf_event_open, &config.attr, tid, -1, // CPU (-1 => Any CPU that the task happens to run on) @@ -227,7 +201,7 @@ PerfKvmCounter::sysGettid() } void -SimplePerfKvmCounter::mmapPerf(int pages) +PerfKvmCounter::mmapPerf(int pages) { assert(attached()); assert(ringBuffer == NULL); @@ -250,21 +224,21 @@ SimplePerfKvmCounter::mmapPerf(int pages) } int -SimplePerfKvmCounter::fcntl(int cmd, long p1) +PerfKvmCounter::fcntl(int cmd, long p1) { assert(attached()); return ::fcntl(fd, cmd, p1); } int -SimplePerfKvmCounter::ioctl(int request, long p1) +PerfKvmCounter::ioctl(int request, long p1) { assert(attached()); return ::ioctl(fd, request, p1); } void -SimplePerfKvmCounter::read(void *buf, size_t size) const +PerfKvmCounter::read(void *buf, size_t size) const { char *_buf = (char *)buf; size_t _size = size; @@ -291,97 +265,4 @@ SimplePerfKvmCounter::read(void *buf, size_t size) const } while (_size); } -PerfKvmCounterConfig -HybridPerfKvmCounter::fixupConfig(const PerfKvmCounterConfig &in, - ConfigSubtype config_subtype) -{ - PerfKvmCounterConfig out = in; - out.attr.config |= config_subtype; - if (out.attr.sample_period > 1) - out.attr.sample_period /= 2; - return out; -} - -void -HybridPerfKvmCounter::attach(const PerfKvmCounterConfig &config, pid_t tid, - const PerfKvmCounter *parent) -{ - // We should only be using hybrid performance counters for hardware - // events. - assert(config.attr.type == PERF_TYPE_HARDWARE); - - const SimplePerfKvmCounter *parent_core_counter = nullptr; - const SimplePerfKvmCounter *parent_atom_counter = nullptr; - if (parent) { - const HybridPerfKvmCounter &hybrid_parent = - dynamic_cast(*parent); - parent_core_counter = &hybrid_parent.coreCounter; - parent_atom_counter = &hybrid_parent.atomCounter; - } - - coreCounter.attach(fixupConfig(config, ConfigCore), tid, - parent_core_counter); - atomCounter.attach(fixupConfig(config, ConfigAtom), tid, - parent_atom_counter); -} - -void -HybridPerfKvmCounter::detach() -{ - coreCounter.detach(); - atomCounter.detach(); -} - -bool -HybridPerfKvmCounter::attached() const -{ - assert(coreCounter.attached() == atomCounter.attached()); - return coreCounter.attached(); -} - -void -HybridPerfKvmCounter::start() -{ - coreCounter.start(); - atomCounter.start(); -} - -void -HybridPerfKvmCounter::stop() -{ - coreCounter.stop(); - atomCounter.stop(); -} - -void -HybridPerfKvmCounter::period(uint64_t period) -{ - if (period > 1) - period /= 2; - coreCounter.period(period); - atomCounter.period(period); -} - -void -HybridPerfKvmCounter::refresh(int refresh) -{ - coreCounter.refresh(refresh); - atomCounter.refresh(refresh); -} - -uint64_t -HybridPerfKvmCounter::read() const -{ - // To get the logical counter value, we simply sum the individual physical - // counter values. - return coreCounter.read() + atomCounter.read(); -} - -void -HybridPerfKvmCounter::enableSignals(pid_t tid, int signal) -{ - coreCounter.enableSignals(tid, signal); - atomCounter.enableSignals(tid, signal); -} - } // namespace gem5 diff --git a/src/cpu/kvm/perfevent.hh b/src/cpu/kvm/perfevent.hh index ab75bf6874..70a246f7e9 100644 --- a/src/cpu/kvm/perfevent.hh +++ b/src/cpu/kvm/perfevent.hh @@ -170,23 +170,46 @@ class PerfKvmCounterConfig */ class PerfKvmCounter { - protected: - /** Don't create directly; use PerfKvmCounter::create(). */ - PerfKvmCounter(); - - public: - virtual ~PerfKvmCounter() = default; - +public: /** - * Create a new performance counter. - * This automatically selects the appropriate implementation of - * PerfKvmCounter, depending on whether the host has a hybrid architecture - * (rare case) or not (common case). + * Create and attach a new counter group. + * + * @param config Counter configuration + * @param tid Thread to sample (0 indicates current thread) */ - static PerfKvmCounter *create(bool allow_hybrid); + PerfKvmCounter(PerfKvmCounterConfig &config, pid_t tid); + /** + * Create and attach a new counter and make it a member of an + * exist counter group. + * + * @param config Counter configuration + * @param tid Thread to sample (0 indicates current thread) + * @param parent Group leader + */ + PerfKvmCounter(PerfKvmCounterConfig &config, + pid_t tid, const PerfKvmCounter &parent); + /** + * Create a new counter, but don't attach it. + */ + PerfKvmCounter(); + ~PerfKvmCounter(); + /** - * Attach a counter and optionally make it a member of an existing counter + * Attach a counter. + * + * @note This operation is only supported if the counter isn't + * already attached. + * + * @param config Counter configuration + * @param tid Thread to sample (0 indicates current thread) + */ + void attach(PerfKvmCounterConfig &config, pid_t tid) { + attach(config, tid, -1); + } + + /** + * Attach a counter and make it a member of an existing counter * group. * * @note This operation is only supported if the counter isn't @@ -194,16 +217,18 @@ class PerfKvmCounter * * @param config Counter configuration * @param tid Thread to sample (0 indicates current thread) - * @param parent Group leader (nullptr indicates no group leader) + * @param parent Group leader */ - virtual void attach(const PerfKvmCounterConfig &config, - pid_t tid, const PerfKvmCounter *parent = nullptr) = 0; + void attach(PerfKvmCounterConfig &config, + pid_t tid, const PerfKvmCounter &parent) { + attach(config, tid, parent.fd); + } /** Detach a counter from PerfEvent. */ - virtual void detach() = 0; + void detach(); /** Check if a counter is attached. */ - virtual bool attached() const = 0; + bool attached() const { return fd != -1; } /** * Start counting. @@ -211,7 +236,7 @@ class PerfKvmCounter * @note If this counter is a group leader, it will start the * entire group. */ - virtual void start() = 0; + void start(); /** * Stop counting. @@ -219,7 +244,7 @@ class PerfKvmCounter * @note If this counter is a group leader, it will stop the * entire group. */ - virtual void stop() = 0; + void stop(); /** * Update the period of an overflow counter. @@ -235,15 +260,9 @@ class PerfKvmCounter * since it has inverted check for the return value when copying * parameters from userspace. * - * @note When using a hybrid perf counter, this actually sets - * the period to 1/2 of the value provided. This ensures that an - * overflow will always trigger before more than \p period events - * occur, even in the pathological case when the host execution is - * evenly split between a P-core and E-core. - * * @param period Overflow period in events */ - virtual void period(uint64_t period) = 0; + void period(uint64_t period); /** * Enable a counter for a fixed number of events. @@ -257,12 +276,12 @@ class PerfKvmCounter * @param refresh Number of overflows before disabling the * counter. */ - virtual void refresh(int refresh) = 0; + void refresh(int refresh); /** * Read the current value of a counter. */ - virtual uint64_t read() const = 0; + uint64_t read() const; /** * Enable signal delivery to a thread on counter overflow. @@ -270,7 +289,7 @@ class PerfKvmCounter * @param tid Thread to deliver signal to * @param signal Signal to send upon overflow */ - virtual void enableSignals(pid_t tid, int signal) = 0; + void enableSignals(pid_t tid, int signal); /** * Enable signal delivery on counter overflow. Identical to @@ -287,43 +306,15 @@ private: // Disallow assignment PerfKvmCounter &operator=(const PerfKvmCounter &that); + void attach(PerfKvmCounterConfig &config, pid_t tid, int group_fd); + /** * Get the TID of the current thread. * * @return Current thread's TID */ pid_t sysGettid(); -}; -/** - * An instance of a single physical performance counter. - * In almost all cases, this is the PerfKvmCounter implementation to use. The - * only situation in which you should _not_ use this counter is for creating - a hardware event on a hybrid host architecture. In that case, use - * a HybridPerfKvmCounter. - */ -class SimplePerfKvmCounter final : public PerfKvmCounter -{ - private: - /** Don't create directly; use PerfKvmCounter::create(). */ - SimplePerfKvmCounter(); - - public: - ~SimplePerfKvmCounter(); - - void attach(const PerfKvmCounterConfig &config, - pid_t tid, const PerfKvmCounter *parent) override; - - void detach() override; - bool attached() const override { return fd != -1; } - void start() override; - void stop() override; - void period(uint64_t period) override; - void refresh(int refresh) override; - uint64_t read() const override; - void enableSignals(pid_t tid, int signal) override; - - private: /** * MMAP the PerfEvent file descriptor. * @@ -388,58 +379,6 @@ class SimplePerfKvmCounter final : public PerfKvmCounter /** Cached host page size */ long pageSize; - - friend class PerfKvmCounter; - friend class HybridPerfKvmCounter; -}; - -/** - * Implements a single logical performance counter using two - * physical performance counters (i.e., SimplePerfKvmCounter). - * Use this for hardware counters (i.e., of type PERF_TYPE_HARDWARE) - * when running on a hybrid host architecture. Such architectures have - * multiple types of cores, each of which require their own individual - * performance counter. - */ -class HybridPerfKvmCounter : public PerfKvmCounter -{ - private: - /** Don't create directly; use PerfKvmCounter::create(). */ - HybridPerfKvmCounter() = default; - - public: - void attach(const PerfKvmCounterConfig &config, pid_t tid, - const PerfKvmCounter *parent) override; - void detach() override; - bool attached() const override; - void start() override; - void stop() override; - void period(uint64_t period) override; - void refresh(int refresh) override; - uint64_t read() const override; - void enableSignals(pid_t pid, int signal) override; - - private: - SimplePerfKvmCounter coreCounter; - SimplePerfKvmCounter atomCounter; - - using ConfigSubtype = decltype(perf_event_attr::config); - using SamplePeriod = decltype(perf_event_attr::sample_type); - - /** @{ */ - /** - * These constants for specifying core vs. atom events are taken from - * Linux perf's documentation (tools/perf/Documentation/intel-hybrid.txt - * in the linux source tree). - */ - static inline constexpr ConfigSubtype ConfigCore = 0x4UL << 32; - static inline constexpr ConfigSubtype ConfigAtom = 0x8UL << 32; - /** @} */ - - static PerfKvmCounterConfig fixupConfig(const PerfKvmCounterConfig &in, - ConfigSubtype config_subtype); - - friend class PerfKvmCounter; }; } // namespace gem5