Revert "cpu-kvm: Support perf counters on hybrid host architectures" (#1127)

Reverts gem5/gem5#1065

Reverting this change because this PR breaks X86 kvm as mentioned in the
issue #1126.
This commit is contained in:
Harshil Patel
2024-05-21 08:14:10 -07:00
committed by GitHub
parent 6f4ba0b422
commit 0824d7f2cd
5 changed files with 82 additions and 275 deletions

View File

@@ -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"
)

View File

@@ -72,7 +72,6 @@ BaseKvmCPU::BaseKvmCPU(const BaseKvmCPUParams &params)
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 &params)
// If we use perf, we create new PerfKVMCounters
if (usePerf) {
hwCycles.reset(PerfKvmCounter::create(allowHybridPerf));
hwInstructions.reset(PerfKvmCounter::create(allowHybridPerf));
hwCycles = std::unique_ptr<PerfKvmCounter>(new PerfKvmCounter());
hwInstructions = std::unique_ptr<PerfKvmCounter>(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);

View File

@@ -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;

View File

@@ -35,7 +35,6 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <dirent.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
@@ -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<const SimplePerfKvmCounter &>(*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<const HybridPerfKvmCounter &>(*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

View File

@@ -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