blob: 3b34be40646328eb10bf7631a0f7149950e51dfe [file] [log] [blame]
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <math.h>
#include <unistd.h>
#include <inttypes.h>
#include "igt.h"
#include "igt_hwmon.h"
#include "igt_perf.h"
#include "igt_power.h"
#include "igt_sysfs.h"
static const char *rapl_domains[] = { "cpu", "gpu", "pkg", "ram" };
static int rapl_parse(struct rapl *r, const char *str)
{
locale_t locale, oldlocale;
bool result = true;
char buf[128];
int dir;
memset(r, 0, sizeof(*r));
dir = open("/sys/devices/power", O_RDONLY);
if (dir < 0)
return -errno;
/* Replace user environment with plain C to match kernel format */
locale = newlocale(LC_ALL, "C", 0);
oldlocale = uselocale(locale);
result &= igt_sysfs_scanf(dir, "type", "%"PRIu64, &r->type) == 1;
snprintf(buf, sizeof(buf), "events/energy-%s", str);
result &= igt_sysfs_scanf(dir, buf, "event=%"PRIx64, &r->power) == 1;
snprintf(buf, sizeof(buf), "events/energy-%s.scale", str);
result &= igt_sysfs_scanf(dir, buf, "%lf", &r->scale) == 1;
uselocale(oldlocale);
freelocale(locale);
close(dir);
if (!result)
return -EINVAL;
if (isnan(r->scale) || !r->scale)
return -ERANGE;
return 0;
}
static int rapl_open(struct rapl *r, const char *domain)
{
r->fd = rapl_parse(r, domain);
if (r->fd < 0)
goto err;
r->fd = igt_perf_open(r->type, r->power);
if (r->fd < 0) {
r->fd = -errno;
goto err;
}
return 0;
err:
errno = 0;
return r->fd;
}
static inline bool rapl_read(struct rapl *r, struct power_sample *s)
{
return read(r->fd, s, sizeof(*s)) == sizeof(*s);
}
static inline void rapl_close(struct rapl *r)
{
close(r->fd);
r->fd = -1;
}
/**
* igt_power_open:
* @fd : device fd
* @igt_power : power struct
* @domain: rapl domain
*
* opens the hwmon/rapl fd based on domain
* hwmon fd - domain gpu -dgfx
* rapl fd - all domains - igfx
*
* Returns
* 0 on success, errno otherwise
*/
int igt_power_open(int fd, struct igt_power *p, const char *domain)
{
int i;
p->hwmon_fd = -1;
p->rapl.fd = -1;
if (gem_has_lmem(fd)) {
if (strncmp(domain, "gpu", strlen("gpu")) == 0) {
p->hwmon_fd = igt_hwmon_open(fd);
if (p->hwmon_fd >= 0)
return 0;
}
} else {
for (i = 0; i < ARRAY_SIZE(rapl_domains); i++)
if (strncmp(domain, rapl_domains[i], strlen(rapl_domains[i])) == 0)
return rapl_open(&p->rapl, domain);
}
return -EINVAL;
}
/**
* igt_power_get_energy:
* @igt_power : power struct
* @power_sample: sample of energy and time
*
* Reads energy from hwmon if energy1_input file is present, else read
* from rapl interface
*
*/
void igt_power_get_energy(struct igt_power *power, struct power_sample *s)
{
struct timespec ts;
s->energy = 0;
igt_assert(!clock_gettime(CLOCK_MONOTONIC, &ts));
s->time = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
if (power->hwmon_fd >= 0) {
if (igt_sysfs_has_attr(power->hwmon_fd, "energy1_input"))
s->energy = igt_sysfs_get_u64(power->hwmon_fd, "energy1_input");
} else if (power->rapl.fd >= 0) {
rapl_read(&power->rapl, s);
}
}
/**
* igt_power_get_mJ:
* @igt_power : power struct
* @power_sample: sample of energy and time
*
* Calculates energy difference between two power samples
*
* Returns
* energy in mJ from hwmon/rapl
*/
double igt_power_get_mJ(const struct igt_power *power,
const struct power_sample *p0, const struct power_sample *p1)
{
if (power->hwmon_fd >= 0)
return (p1->energy - p0->energy) * 1e-3;
else if (power->rapl.fd >= 0)
return ((p1->energy - p0->energy) * power->rapl.scale) * 1e3;
return 0;
}
/**
* igt_power_get_mW:
* @igt_power : power struct
* @power_sample: sample of energy and time
*
* Calculates power
*
* Returns
* power in milliWatts
*/
double igt_power_get_mW(const struct igt_power *power,
const struct power_sample *p0, const struct power_sample *p1)
{
return igt_power_get_mJ(power, p0, p1) / igt_power_get_s(p0, p1);
}
/**
* igt_power_get_s:
* @power_sample: sample of energy and time
*
* Returns
* time differnce in seconds
*/
double igt_power_get_s(const struct power_sample *p0,
const struct power_sample *p1)
{
return (p1->time - p0->time) * 1e-9;
}
/**
* igt_power_close:
* @igt_power : power struct
*
* closes hwmon/rapl fd
*
*/
void igt_power_close(struct igt_power *power)
{
if (power->hwmon_fd >= 0) {
close(power->hwmon_fd);
power->hwmon_fd = -1;
} else if (power->rapl.fd >= 0) {
rapl_close(&power->rapl);
}
}