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:
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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<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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user