This repository has been archived on 2024-04-10. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
elektron/rapl-daemon/util.go

110 lines
2.9 KiB
Go

package main
import (
"fmt"
"io/ioutil"
"math"
"os"
"path/filepath"
"strconv"
"strings"
)
const raplPrefixCPU = "intel-rapl"
// constraint_0 is usually the longer window while constraint_1 is usually the shorter window
const maxPowerFileLongWindow = "constraint_0_max_power_uw"
const powerLimitFileLongWindow = "constraint_0_power_limit_uw"
// capNode uses pseudo files made available by the Linux kernel
// in order to capNode CPU power. More information is available at:
// https://www.kernel.org/doc/html/latest/power/powercap/powercap.html
func capNode(base string, percentage int) ([]string, []string, error) {
if percentage <= 0 || percentage > 100 {
return nil, nil, fmt.Errorf("cap percentage must be between 0 (non-inclusive) and 100 (inclusive): %d", percentage)
}
files, err := ioutil.ReadDir(base)
if err != nil {
return nil, nil, err
}
var capped, failed []string
for _, file := range files {
fields := strings.Split(file.Name(), ":")
// Fields should be in the form intel-rapl:X where X is the power zone
// We ignore sub-zones which follow the form intel-rapl:X:Y
if len(fields) != 2 {
continue
}
if fields[0] == raplPrefixCPU {
maxPower, err := maxPower(filepath.Join(base, file.Name(), maxPowerFileLongWindow))
if err != nil {
failed = append(failed, file.Name())
fmt.Println("unable to retreive max power for zone ", err)
continue
}
// We use floats to mitigate the possibility of an integer overflow.
powercap := uint64(math.Ceil(float64(maxPower) * (float64(percentage) / 100)))
if err := capZone(filepath.Join(base, file.Name(), powerLimitFileLongWindow), powercap); err != nil {
failed = append(failed, file.Name())
fmt.Println("unable to write powercap value: ", err)
continue
}
capped = append(capped, file.Name())
}
}
if len(failed) > 0 {
return capped, failed, fmt.Errorf("some zones were not able to be powercapped")
}
return capped, nil, nil
}
// maxPower returns the value in float of the maximum watts a power zone can use.
func maxPower(maxFile string) (uint64, error) {
maxPower, err := ioutil.ReadFile(maxFile)
if err != nil {
return 0, err
}
maxPoweruW, err := strconv.ParseUint(strings.TrimSpace(string(maxPower)), 10, 64)
if err != nil {
return 0, err
}
return maxPoweruW, nil
}
// capZone caps a power zone to a specific amount of watts specified by value
func capZone(limitFile string, value uint64) error {
if _, err := os.Stat(limitFile); os.IsNotExist(err) {
return err
}
err := ioutil.WriteFile(limitFile, []byte(strconv.FormatUint(value, 10)), 0644)
if err != nil {
return err
}
return nil
}
func currentCap(limit string) (uint64, error) {
powercap, err := ioutil.ReadFile(limit)
if err != nil {
return 0, err
}
powercapuW, err := strconv.ParseUint(strings.TrimSpace(string(powercap)), 10, 64)
if err != nil {
return 0, err
}
return powercapuW, nil
}