Files
cpu-check/fvt_controller.h
2020-05-08 13:33:05 -07:00

165 lines
4.4 KiB
C++

// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef FVT_CONTROLLER_
#define FVT_CONTROLLER_
#include <fcntl.h>
#include <unistd.h>
#include <memory>
#include <sstream>
#include "log.h"
#include "utils.h"
// Frequency, Voltage and Thermal (FVT) controller.
class FVTController {
public:
static constexpr int kMinTurboMHz = 1000;
protected:
explicit FVTController(int cpu) : cpu_(cpu) {
ResetFrequencyMeter();
}
public:
virtual ~FVTController() {}
static std::unique_ptr<FVTController> Create(int cpu);
// Monitor per-cpu (or core) frequency control.
void MonitorFrequency() {
const double t = TimeInSeconds();
const int f = GetCurrentFreqMhz();
sum_mHz_ += (t - previous_sample_time_) * f;
previous_sample_time_ = t;
const int mHz = GetCurrentFreqLimitMhz();
if (mHz != max_mHz_) {
LOG_EVERY_N_SECS(INFO, 10) << "Cpu: " << cpu_
<< " max turbo frequency control changed to: " << mHz;
max_mHz_ = mHz;
}
}
// Set the current CPU frequency limit. Warning: do this to both threads of
// HT pair. Requires 'mhz' multiple of 100, within legitimate range.
virtual void SetCurrentFreqLimitMhz(int mhz) = 0;
// Returns the absolute maximum CPU frequency.
virtual int GetAbsoluteFreqLimitMhz() = 0;
// Dont put much stock in this method, it's probably a lousy way to do things.
int GetMeanFreqMhz() const {
return sum_mHz_ / (previous_sample_time_ - t0_);
}
int max_mHz() const { return max_mHz_; }
int limit_mHz() const { return limit_mhz_; }
// Returns true if automatic Power Management enabled.
virtual bool PowerManaged() const = 0;
// Returns frequency, thermal, and voltage condition.
virtual std::string FVT() = 0;
virtual std::string InterestingEnables() const = 0;
// TODO: separate this from FVT controller.
virtual void ControlFastStringOps(bool enable) = 0;
protected:
// Returns the current CPU frequency limit in MHz.
virtual int GetCurrentFreqLimitMhz() = 0;
// Returns the current CPU frequency in MHz.
virtual int GetCurrentFreqMhz() = 0;
void ResetFrequencyMeter() {
t0_ = TimeInSeconds();
previous_sample_time_ = t0_;
sum_mHz_ = 0.0;
}
const int cpu_;
double t0_ = 0.0;
int limit_mhz_ = 0; // const after init
int max_mHz_ = 0;
double sum_mHz_ = 0.0;
double previous_sample_time_ = 0.0;
};
class X86FVTController : public FVTController {
public:
struct CPUIDResult {
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
};
explicit X86FVTController(int cpu) : FVTController(cpu) {
std::stringstream dev;
dev << "/dev/cpu/" << cpu << "/msr";
fd_ = open(dev.str().c_str(), O_RDWR);
if (fd_ < 0) {
LOG(ERROR) << "Cannot open: " << dev.str()
<< " Running me as root?";
}
}
~X86FVTController() override {
if (fd_ >= 0) close(fd_);
}
// Only works for Linux on x86-64
static void GetCPUId(int cpu, uint32_t eax, CPUIDResult* result);
// Return the vendor string from CPUID
static std::string CPUIDVendorString();
protected:
uint64_t ReadMsr(uint32_t reg) const {
if (fd_ < 0) return 0;
uint64_t v = 0;
int rc = pread(fd_, &v, sizeof(v), reg);
if (rc != sizeof(v)) {
LOG_EVERY_N_SECS(ERROR, 60) << "Unable to read cpu: " << cpu_
<< " reg: " << std::hex << reg;
}
return v;
}
void WriteMsr(uint32_t reg, uint64_t v) const {
if (fd_ < 0) return;
int rc = pwrite(fd_, &v, sizeof(v), reg);
if (rc != sizeof(v)) {
fprintf(stderr, "rc = %d sizeof(v) = %lu\n", rc, sizeof(v));
LOG_EVERY_N_SECS(ERROR, 60) << "Unable to write cpu: " << cpu_
<< " reg: " << std::hex << reg;
}
}
private:
static std::string CPUIDVendorStringUncached();
int fd_ = -1;
};
extern std::unique_ptr<FVTController> NewAMDFVTController(int cpu);
extern std::unique_ptr<FVTController> NewIntelFVTController(int cpu);
#endif // FVT_CONTROLLER_