blob: 83182020b49845f5aa798ef2099052032c512300 [file] [log] [blame]
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include <inttypes.h>
#include <sys/stat.h>
#ifdef __linux__
#include <sys/sysmacros.h>
#endif
#include <sys/mount.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <i915_drm.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include "igt_core.h"
#include "igt_sysfs.h"
#include "igt_device.h"
#include "igt_io.h"
#include "intel_chipset.h"
/**
* SECTION:igt_sysfs
* @short_description: Support code for sysfs features
* @title: sysfs
* @include: igt.h
*
* This library provides helpers to access sysfs features. Right now it only
* provides basic support for like igt_sysfs_open().
*/
enum {
GT,
RPS,
SYSFS_NUM_TYPES,
};
static const char *i915_attr_name[SYSFS_NUM_TYPES][SYSFS_NUM_ATTR] = {
{
"gt_act_freq_mhz",
"gt_cur_freq_mhz",
"gt_min_freq_mhz",
"gt_max_freq_mhz",
"gt_RP0_freq_mhz",
"gt_RP1_freq_mhz",
"gt_RPn_freq_mhz",
"gt_idle_freq_mhz",
"gt_boost_freq_mhz",
"power/rc6_enable",
"power/rc6_residency_ms",
"power/rc6p_residency_ms",
"power/rc6pp_residency_ms",
"power/media_rc6_residency_ms",
},
{
"rps_act_freq_mhz",
"rps_cur_freq_mhz",
"rps_min_freq_mhz",
"rps_max_freq_mhz",
"rps_RP0_freq_mhz",
"rps_RP1_freq_mhz",
"rps_RPn_freq_mhz",
"rps_idle_freq_mhz",
"rps_boost_freq_mhz",
"rc6_enable",
"rc6_residency_ms",
"rc6p_residency_ms",
"rc6pp_residency_ms",
"media_rc6_residency_ms",
},
};
/**
* igt_sysfs_dir_id_to_name:
* @dir: sysfs directory fd
* @id: sysfs attribute id
*
* Returns attribute name corresponding to attribute id in either the
* per-gt or legacy per-device sysfs
*
* Returns:
* Attribute name in sysfs
*/
const char *igt_sysfs_dir_id_to_name(int dir, enum i915_attr_id id)
{
igt_assert((uint32_t)id < SYSFS_NUM_ATTR);
if (igt_sysfs_has_attr(dir, i915_attr_name[RPS][id]))
return i915_attr_name[RPS][id];
return i915_attr_name[GT][id];
}
/**
* igt_sysfs_path_id_to_name:
* @path: sysfs directory path
* @id: sysfs attribute id
*
* Returns attribute name corresponding to attribute id in either the
* per-gt or legacy per-device sysfs
*
* Returns:
* Attribute name in sysfs
*/
const char *igt_sysfs_path_id_to_name(const char *path, enum i915_attr_id id)
{
int dir;
const char *name;
dir = open(path, O_RDONLY);
igt_assert(dir);
name = igt_sysfs_dir_id_to_name(dir, id);
close(dir);
return name;
}
/**
* igt_sysfs_has_attr:
* @dir: sysfs directory fd
* @attr: attr inside sysfs dir that needs to be checked for existence
*
* This checks if specified attr exists in device sysfs directory.
*
* Returns:
* true if attr exists in sysfs, false otherwise.
*/
bool igt_sysfs_has_attr(int dir, const char *attr)
{
return !faccessat(dir, attr, F_OK, 0);
}
/**
* igt_sysfs_path:
* @device: fd of the device
* @path: buffer to fill with the sysfs path to the device
* @pathlen: length of @path buffer
*
* This finds the sysfs directory corresponding to @device.
*
* Returns:
* The directory path, or NULL on failure.
*/
char *igt_sysfs_path(int device, char *path, int pathlen)
{
struct stat st;
if (igt_debug_on(device < 0))
return NULL;
if (igt_debug_on(fstat(device, &st)) || igt_debug_on(!S_ISCHR(st.st_mode)))
return NULL;
snprintf(path, pathlen, "/sys/dev/char/%d:%d",
major(st.st_rdev), minor(st.st_rdev));
if (access(path, F_OK))
return NULL;
return path;
}
/**
* igt_sysfs_open:
* @device: fd of the device
*
* This opens the sysfs directory corresponding to device for use
* with igt_sysfs_set() and igt_sysfs_get().
*
* Returns:
* The directory fd, or -1 on failure.
*/
int igt_sysfs_open(int device)
{
char path[80];
if (!igt_sysfs_path(device, path, sizeof(path)))
return -1;
return open(path, O_RDONLY);
}
/**
* xe_sysfs_gt_path:
* @xe_device: fd of the device
* @gt: gt number
* @path: buffer to fill with the sysfs gt path to the device
* @pathlen: length of @path buffer
*
* Returns:
* The directory path, or NULL on failure.
*/
char *xe_sysfs_gt_path(int xe_device, int gt, char *path, int pathlen)
{
struct stat st;
if (xe_device < 0)
return NULL;
if (igt_debug_on(fstat(xe_device, &st)) || igt_debug_on(!S_ISCHR(st.st_mode)))
return NULL;
if (IS_PONTEVECCHIO(intel_get_drm_devid(xe_device)))
snprintf(path, pathlen, "/sys/dev/char/%d:%d/device/tile%d/gt%d",
major(st.st_rdev), minor(st.st_rdev), gt, gt);
else
snprintf(path, pathlen, "/sys/dev/char/%d:%d/device/tile0/gt%d",
major(st.st_rdev), minor(st.st_rdev), gt);
if (!access(path, F_OK))
return path;
return NULL;
}
/**
* xe_sysfs_gt_open:
* @xe_device: fd of the device
* @gt: gt number
*
* This opens the sysfs gt directory corresponding to device and tile for use
*
* Returns:
* The directory fd, or -1 on failure.
*/
int xe_sysfs_gt_open(int xe_device, int gt)
{
char path[96];
if (!xe_sysfs_gt_path(xe_device, gt, path, sizeof(path)))
return -1;
return open(path, O_RDONLY);
}
/**
* igt_sysfs_gt_path:
* @device: fd of the device
* @gt: gt number
* @path: buffer to fill with the sysfs gt path to the device
* @pathlen: length of @path buffer
*
* This finds the sysfs directory corresponding to @device and @gt. If the gt
* specific directory is not available and gt is 0, path is filled with sysfs
* base directory.
*
* Returns:
* The directory path, or NULL on failure.
*/
char *igt_sysfs_gt_path(int device, int gt, char *path, int pathlen)
{
struct stat st;
if (device < 0)
return NULL;
if (igt_debug_on(fstat(device, &st)) || igt_debug_on(!S_ISCHR(st.st_mode)))
return NULL;
snprintf(path, pathlen, "/sys/dev/char/%d:%d/gt/gt%d",
major(st.st_rdev), minor(st.st_rdev), gt);
if (!access(path, F_OK))
return path;
if (!gt)
return igt_sysfs_path(device, path, pathlen);
return NULL;
}
/**
* igt_sysfs_gt_open:
* @device: fd of the device
* @gt: gt number
*
* This opens the sysfs gt directory corresponding to device and gt for use
* with igt_sysfs_set() and igt_sysfs_get().
*
* Returns:
* The directory fd, or -1 on failure.
*/
int igt_sysfs_gt_open(int device, int gt)
{
char path[96];
if (!igt_sysfs_gt_path(device, gt, path, sizeof(path)))
return -1;
return open(path, O_RDONLY);
}
/**
* igt_sysfs_get_num_gt:
* @device: fd of the device
*
* Reads number of GT sysfs entries.
* Asserts for atleast one GT entry.
* (see igt_sysfs_gt_path).
*
* Returns: Number of GTs.
*/
int igt_sysfs_get_num_gt(int device)
{
int num_gts = 0;
char path[96];
while (igt_sysfs_gt_path(device, num_gts, path, sizeof(path)))
++num_gts;
igt_assert_f(num_gts > 0, "No GT sysfs entry is found.");
return num_gts;
}
/**
* igt_sysfs_write:
* @dir: directory for the device from igt_sysfs_open()
* @attr: name of the sysfs node to open
* @data: the block to write from
* @len: the length to write
*
* This writes @len bytes from @data to the sysfs file.
*
* Returns:
* The number of bytes written, or -errno on error.
*/
int igt_sysfs_write(int dir, const char *attr, const void *data, int len)
{
int fd;
fd = openat(dir, attr, O_WRONLY);
if (igt_debug_on(fd < 0))
return -errno;
len = igt_writen(fd, data, len);
close(fd);
return len;
}
/**
* igt_sysfs_read:
* @dir: directory for the device from igt_sysfs_open()
* @attr: name of the sysfs node to open
* @data: the block to read into
* @len: the maximum length to read
*
* This reads @len bytes from the sysfs file to @data
*
* Returns:
* The length read, -errno on failure.
*/
int igt_sysfs_read(int dir, const char *attr, void *data, int len)
{
int fd;
fd = openat(dir, attr, O_RDONLY);
if (igt_debug_on(fd < 0))
return -errno;
len = igt_readn(fd, data, len);
close(fd);
return len;
}
/**
* igt_sysfs_set:
* @dir: directory for the device from igt_sysfs_open()
* @attr: name of the sysfs node to open
* @value: the string to write
*
* This writes the value to the sysfs file.
*
* Returns:
* True on success, false on failure.
*/
bool igt_sysfs_set(int dir, const char *attr, const char *value)
{
int len = strlen(value);
return igt_sysfs_write(dir, attr, value, len) == len;
}
/**
* igt_sysfs_get:
* @dir: directory for the device from igt_sysfs_open()
* @attr: name of the sysfs node to open
*
* This reads the value from the sysfs file.
*
* Returns:
* A nul-terminated string, must be freed by caller after use, or NULL
* on failure.
*/
char *igt_sysfs_get(int dir, const char *attr)
{
char *buf;
size_t len, offset, rem;
ssize_t ret;
int fd;
fd = openat(dir, attr, O_RDONLY);
if (igt_debug_on(fd < 0))
return NULL;
offset = 0;
len = 64;
rem = len - offset - 1;
buf = malloc(len);
if (igt_debug_on(!buf))
goto out;
while ((ret = igt_readn(fd, buf + offset, rem)) == rem) {
char *newbuf;
newbuf = realloc(buf, 2*len);
if (igt_debug_on(!newbuf))
break;
buf = newbuf;
len *= 2;
offset += ret;
rem = len - offset - 1;
}
if (ret > 0)
offset += ret;
buf[offset] = '\0';
while (offset > 0 && buf[offset-1] == '\n')
buf[--offset] = '\0';
out:
close(fd);
return buf;
}
/**
* igt_sysfs_scanf:
* @dir: directory for the device from igt_sysfs_open()
* @attr: name of the sysfs node to open
* @fmt: scanf format string
* @...: Additional paramaters to store the scaned input values
*
* scanf() wrapper for sysfs.
*
* Returns:
* Number of values successfully scanned (which can be 0), EOF on errors or
* premature end of file.
*/
int igt_sysfs_scanf(int dir, const char *attr, const char *fmt, ...)
{
FILE *file;
int fd;
int ret = -1;
fd = openat(dir, attr, O_RDONLY);
if (igt_debug_on(fd < 0))
return -1;
file = fdopen(fd, "r");
if (!igt_debug_on(!file)) {
va_list ap;
va_start(ap, fmt);
ret = vfscanf(file, fmt, ap);
va_end(ap);
fclose(file);
} else {
close(fd);
}
return ret;
}
int igt_sysfs_vprintf(int dir, const char *attr, const char *fmt, va_list ap)
{
char stack[128], *buf = stack;
va_list tmp;
int ret, fd;
fd = openat(dir, attr, O_WRONLY);
if (igt_debug_on(fd < 0))
return -errno;
va_copy(tmp, ap);
ret = vsnprintf(buf, sizeof(stack), fmt, tmp);
va_end(tmp);
if (igt_debug_on(ret < 0))
return -EINVAL;
if (ret > sizeof(stack)) {
unsigned int len = ret + 1;
buf = malloc(len);
if (igt_debug_on(!buf))
return -ENOMEM;
ret = vsnprintf(buf, ret, fmt, ap);
if (igt_debug_on(ret > len)) {
free(buf);
return -EINVAL;
}
}
ret = igt_writen(fd, buf, ret);
close(fd);
if (buf != stack)
free(buf);
return ret;
}
/**
* igt_sysfs_printf:
* @dir: directory for the device from igt_sysfs_open()
* @attr: name of the sysfs node to open
* @fmt: printf format string
* @...: Additional paramaters to store the scaned input values
*
* printf() wrapper for sysfs.
*
* Returns:
* Number of characters written, negative value on error.
*/
int igt_sysfs_printf(int dir, const char *attr, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = igt_sysfs_vprintf(dir, attr, fmt, ap);
va_end(ap);
return ret;
}
/**
* __igt_sysfs_get_u32:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to read
* @value: pointer for storing read value
*
* Convenience wrapper to read a unsigned 32bit integer from a sysfs file.
*
* Returns:
* True if value successfully read, false otherwise.
*/
bool __igt_sysfs_get_u32(int dir, const char *attr, uint32_t *value)
{
if (igt_debug_on(igt_sysfs_scanf(dir, attr, "%u", value) != 1))
return false;
return true;
}
/**
* igt_sysfs_get_u32:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to read
*
* Convenience wrapper to read a unsigned 32bit integer from a sysfs file.
* It asserts on failure.
*
* Returns:
* Read value.
*/
uint32_t igt_sysfs_get_u32(int dir, const char *attr)
{
uint32_t value;
igt_assert_f(__igt_sysfs_get_u32(dir, attr, &value),
"Failed to read %s attribute (%s)\n", attr, strerror(errno));
return value;
}
/**
* __igt_sysfs_set_u32:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to write
* @value: value to set
*
* Convenience wrapper to write a unsigned 32bit integer to a sysfs file.
*
* Returns:
* True if successfully written, false otherwise.
*/
bool __igt_sysfs_set_u32(int dir, const char *attr, uint32_t value)
{
return igt_sysfs_printf(dir, attr, "%u", value) > 0;
}
/**
* igt_sysfs_set_u32:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to write
* @value: value to set
*
* Convenience wrapper to write a unsigned 32bit integer to a sysfs file.
* It asserts on failure.
*/
void igt_sysfs_set_u32(int dir, const char *attr, uint32_t value)
{
igt_assert_f(__igt_sysfs_set_u32(dir, attr, value),
"Failed to write %u to %s attribute (%s)\n", value, attr, strerror(errno));
}
/**
* __igt_sysfs_get_u64:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to read
* @value: pointer for storing read value
*
* Convenience wrapper to read a unsigned 64bit integer from a sysfs file.
*
* Returns:
* True if value successfully read, false otherwise.
*/
bool __igt_sysfs_get_u64(int dir, const char *attr, uint64_t *value)
{
if (igt_debug_on(igt_sysfs_scanf(dir, attr, "%"PRIu64, value) != 1))
return false;
return true;
}
/**
* igt_sysfs_get_u64:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to read
*
* Convenience wrapper to read a unsigned 64bit integer from a sysfs file.
* It asserts on failure.
*
* Returns:
* Read value.
*/
uint64_t igt_sysfs_get_u64(int dir, const char *attr)
{
uint64_t value;
igt_assert_f(__igt_sysfs_get_u64(dir, attr, &value),
"Failed to read %s attribute (%s)\n", attr, strerror(errno));
return value;
}
/**
* __igt_sysfs_set_u64:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to write
* @value: value to set
*
* Convenience wrapper to write a unsigned 64bit integer to a sysfs file.
*
* Returns:
* True if successfully written, false otherwise.
*/
bool __igt_sysfs_set_u64(int dir, const char *attr, uint64_t value)
{
return igt_sysfs_printf(dir, attr, "%"PRIu64, value) > 0;
}
/**
* igt_sysfs_set_u64:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to write
* @value: value to set
*
* Convenience wrapper to write a unsigned 64bit integer to a sysfs file.
* It asserts on failure.
*/
void igt_sysfs_set_u64(int dir, const char *attr, uint64_t value)
{
igt_assert_f(__igt_sysfs_set_u64(dir, attr, value),
"Failed to write %"PRIu64" to %s attribute (%s)\n",
value, attr, strerror(errno));
}
/**
* __igt_sysfs_get_boolean:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to read
* @value: pointer for storing read value
*
* Convenience wrapper to read a boolean sysfs file.
*
* Returns:
* True if value successfully read, false otherwise.
*/
bool __igt_sysfs_get_boolean(int dir, const char *attr, bool *value)
{
char *buf;
int ret, read_value;
buf = igt_sysfs_get(dir, attr);
if (igt_debug_on_f(!buf, "Failed to read %s attribute (%s)\n", attr, strerror(errno)))
return false;
ret = sscanf(buf, "%d", &read_value);
if (((ret == 1) && (read_value == 1)) || ((ret == 0) && !strcasecmp(buf, "Y"))) {
*value = true;
} else if (((ret == 1) && (read_value == 0)) || ((ret == 0) && !strcasecmp(buf, "N"))) {
*value = false;
} else {
igt_debug("Value read from %s attribute (%s) is not as expected (0|1|N|Y|n|y)\n",
attr, buf);
free(buf);
return false;
}
free(buf);
return true;
}
/**
* igt_sysfs_get_boolean:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to read
*
* Convenience wrapper to read a boolean sysfs file.
* It asserts on failure.
*
* Returns:
* Read value.
*/
bool igt_sysfs_get_boolean(int dir, const char *attr)
{
bool value;
igt_assert(__igt_sysfs_get_boolean(dir, attr, &value));
return value;
}
/**
* __igt_sysfs_set_boolean:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to write
* @value: value to set
*
* Convenience wrapper to write a boolean sysfs file.
*
* Returns:
* True if successfully written, false otherwise.
*/
bool __igt_sysfs_set_boolean(int dir, const char *attr, bool value)
{
return igt_sysfs_printf(dir, attr, "%d", value) == 1;
}
/**
* igt_sysfs_set_boolean:
* @dir: directory corresponding to attribute
* @attr: name of the sysfs node to write
* @value: value to set
*
* Convenience wrapper to write a boolean sysfs file.
* It asserts on failure.
*/
void igt_sysfs_set_boolean(int dir, const char *attr, bool value)
{
igt_assert_f(__igt_sysfs_set_boolean(dir, attr, value),
"Failed to write %u to %s attribute (%s)\n", value, attr, strerror(errno));
}
static void bind_con(const char *name, bool enable)
{
const char *path = "/sys/class/vtconsole";
DIR *dir;
struct dirent *de;
dir = opendir(path);
if (!dir)
return;
while ((de = readdir(dir))) {
char buf[PATH_MAX];
int fd, len;
if (strncmp(de->d_name, "vtcon", 5))
continue;
sprintf(buf, "%s/%s/name", path, de->d_name);
fd = open(buf, O_RDONLY);
if (fd < 0)
continue;
buf[sizeof(buf) - 1] = '\0';
len = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (len >= 0)
buf[len] = '\0';
if (!strstr(buf, name))
continue;
sprintf(buf, "%s/%s/bind", path, de->d_name);
fd = open(buf, O_WRONLY);
if (fd != -1) {
igt_ignore_warn(write(fd, enable ? "1\n" : "0\n", 2));
close(fd);
}
break;
}
closedir(dir);
}
/**
* bind_fbcon:
* @enable: boolean value
*
* This functions enables/disables the text console running on top of the
* framebuffer device.
*/
void bind_fbcon(bool enable)
{
/*
* The vtcon bind interface seems somewhat broken. Possibly
* depending on the order the console drivers have been
* registered you either have to unbind the old driver,
* or bind the new driver. Let's do both.
*/
bind_con("dummy device", !enable);
bind_con("frame buffer device", enable);
}
/**
* kick_snd_hda_intel:
*
* This functions unbinds the snd_hda_intel driver so the module cand be
* unloaded.
*
*/
void kick_snd_hda_intel(void)
{
DIR *dir;
struct dirent *snd_hda;
int fd; size_t len;
const char *dpath = "/sys/bus/pci/drivers/snd_hda_intel";
const char *path = "/sys/bus/pci/drivers/snd_hda_intel/unbind";
const char *devid = "0000:";
fd = open(path, O_WRONLY);
if (fd < 0) {
return;
}
dir = opendir(dpath);
if (!dir)
goto out;
len = strlen(devid);
while ((snd_hda = readdir(dir))) {
struct stat st;
char fpath[PATH_MAX];
if (*snd_hda->d_name == '.')
continue;
snprintf(fpath, sizeof(fpath), "%s/%s", dpath, snd_hda->d_name);
if (lstat(fpath, &st))
continue;
if (!S_ISLNK(st.st_mode))
continue;
if (!strncmp(devid, snd_hda->d_name, len)) {
igt_ignore_warn(write(fd, snd_hda->d_name,
strlen(snd_hda->d_name)));
}
}
closedir(dir);
out:
close(fd);
}
static int fbcon_cursor_blink_fd = -1;
static char fbcon_cursor_blink_prev_value[2];
static void fbcon_cursor_blink_restore(int sig)
{
write(fbcon_cursor_blink_fd, fbcon_cursor_blink_prev_value,
strlen(fbcon_cursor_blink_prev_value) + 1);
close(fbcon_cursor_blink_fd);
}
/**
* fbcon_blink_enable:
* @enable: if true enables the fbcon cursor blinking otherwise disables it
*
* Enables or disables the cursor blinking in fbcon, it also restores the
* previous blinking state when exiting test.
*
*/
void fbcon_blink_enable(bool enable)
{
const char *cur_blink_path = "/sys/class/graphics/fbcon/cursor_blink";
int fd, r;
char buffer[2];
fd = open(cur_blink_path, O_RDWR);
igt_require(fd >= 0);
/* Restore original value on exit */
if (fbcon_cursor_blink_fd == -1) {
r = read(fd, fbcon_cursor_blink_prev_value,
sizeof(fbcon_cursor_blink_prev_value));
if (r > 0) {
fbcon_cursor_blink_fd = dup(fd);
igt_assert(fbcon_cursor_blink_fd >= 0);
igt_install_exit_handler(fbcon_cursor_blink_restore);
}
}
r = snprintf(buffer, sizeof(buffer), enable ? "1" : "0");
write(fd, buffer, r + 1);
close(fd);
}
static bool rw_attr_equal_within_epsilon(uint64_t x, uint64_t ref, double tol)
{
return (x <= (1.0 + tol) * ref) && (x >= (1.0 - tol) * ref);
}
/* Sweep the range of values for an attribute to identify matching reads/writes */
static int rw_attr_sweep(igt_sysfs_rw_attr_t *rw)
{
uint64_t get = 0, set = rw->start;
int num_points = 0;
bool ret;
igt_debug("'%s': sweeping range of values\n", rw->attr);
while (set < UINT64_MAX / 2) {
ret = __igt_sysfs_set_u64(rw->dir, rw->attr, set);
__igt_sysfs_get_u64(rw->dir, rw->attr, &get);
igt_debug("'%s': ret %d set %lu get %lu\n", rw->attr, ret, set, get);
if (ret && rw_attr_equal_within_epsilon(get, set, rw->tol)) {
igt_debug("'%s': matches\n", rw->attr);
num_points++;
}
set *= 2;
}
igt_debug("'%s': done sweeping\n", rw->attr);
return num_points ? 0 : -ENOENT;
}
/**
* igt_sysfs_rw_attr_verify:
* @rw: 'struct igt_sysfs_rw_attr' describing a rw sysfs attr
*
* This function attempts to verify writable sysfs attributes, that is the
* attribute is first written to and then read back and it is verified that
* the read value matches the written value to a tolerance. However, when
* we try to do this we run into the issue that a sysfs attribute might
* have a behavior where the read value is different from the written value
* for any reason. For example, attributes such as power, voltage,
* frequency and time typically have a linear region outside which they are
* clamped (the values saturate). Therefore for such attributes read values
* match the written value only in the linear region and when writing we
* don't know if we are writing to the linear or to the clamped region.
*
* Therefore the verification implemented here takes the approach of
* sweeping across the range of possible values of the attribute (this is
* done using 'doubling' rather than linearly) and seeing where there are
* matches. There should be at least one match (to a tolerance) for the
* verification to have succeeded.
*/
void igt_sysfs_rw_attr_verify(igt_sysfs_rw_attr_t *rw)
{
uint64_t prev = 0, get = 0;
struct stat st;
int ret;
igt_assert(!fstatat(rw->dir, rw->attr, &st, 0));
igt_assert(st.st_mode & 0222); /* writable */
igt_assert(rw->start); /* cannot be 0 */
__igt_sysfs_get_u64(rw->dir, rw->attr, &prev);
igt_debug("'%s': prev %lu\n", rw->attr, prev);
ret = rw_attr_sweep(rw);
/*
* Restore previous value: we don't assert before this point so
* that we can restore the attr before asserting
*/
igt_sysfs_set_u64(rw->dir, rw->attr, prev);
__igt_sysfs_get_u64(rw->dir, rw->attr, &get);
igt_assert_eq(get, prev);
igt_assert(!ret);
}
/**
* igt_sysfs_engines:
* @xe: fd of the device
* @engines: fd of the directory engine
* @property: property array
* @test: Dynamic engine test
*
* It iterates over sysfs/engines and runs a dynamic engine test.
*
*/
void igt_sysfs_engines(int xe, int engines, const char **property,
void (*test)(int, int, const char **))
{
struct dirent *de;
DIR *dir;
lseek(engines, 0, SEEK_SET);
dir = fdopendir(engines);
if (!dir)
close(engines);
while ((de = readdir(dir))) {
int engine_fd;
if (*de->d_name == '.')
continue;
engine_fd = openat(engines, de->d_name, O_RDONLY);
if (engine_fd < 0)
continue;
igt_dynamic(de->d_name) {
if (property) {
struct stat st;
igt_require(fstatat(engine_fd, property[0], &st, 0) == 0);
igt_require(fstatat(engine_fd, property[1], &st, 0) == 0);
igt_require(fstatat(engine_fd, property[2], &st, 0) == 0);
}
errno = 0;
test(xe, engine_fd, property);
}
close(engine_fd);
}
}
/**
* xe_sysfs_tile_path:
* @xe_device: fd of the device
* @tile: tile number
* @path: buffer to fill with the sysfs tile path to the device
* @pathlen: length of @path buffer
*
* Returns:
* The directory path, or NULL on failure.
*/
char *xe_sysfs_tile_path(int xe_device, int tile, char *path, int pathlen)
{
struct stat st;
if (xe_device < 0)
return NULL;
if (igt_debug_on(fstat(xe_device, &st)) || igt_debug_on(!S_ISCHR(st.st_mode)))
return NULL;
snprintf(path, pathlen, "/sys/dev/char/%d:%d/device/tile%d",
major(st.st_rdev), minor(st.st_rdev), tile);
if (!access(path, F_OK))
return path;
return NULL;
}
/**
* xe_sysfs_tile_open:
* @xe_device: fd of the device
* @tile: tile number
*
* This opens the sysfs tile directory corresponding to device and tile for use
*
* Returns:
* The directory fd, or -1 on failure.
*/
int xe_sysfs_tile_open(int xe_device, int tile)
{
char path[96];
if (!xe_sysfs_tile_path(xe_device, tile, path, sizeof(path)))
return -1;
return open(path, O_RDONLY);
}
/**
* xe_sysfs_get_num_tiles:
* @xe_device: fd of the device
*
* Reads number of tile sysfs entries.
* Asserts for at least one tile entry.
* (see xe_sysfs_tile_path).
*
* Returns: Number of tiles.
*/
int xe_sysfs_get_num_tiles(int xe_device)
{
int num_tiles = 0;
char path[96];
while (xe_sysfs_tile_path(xe_device, num_tiles, path, sizeof(path)))
++num_tiles;
igt_assert_f(num_tiles > 0, "No GT sysfs entry is found.");
return num_tiles;
}