1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
// SPDX-License-Identifier: MIT
/*
* Copyright © 2024 Collabora Ltd.
*
* Author: Adrian Larumbe <adrian.larumbe@collabora.com>
*
*/
#include <unistd.h>
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "igt_profiling.h"
#define SYSFS_DRM "/sys/class/drm"
#define NUM_DEVICES 10
/**
* igt_devices_profiled
*
* Gives us an array of igt_profiled_device structures, each of which contains
* the full path of the DRM device's sysfs profiling knob and its original
* state, so that it can be restored later on.
*
* Returns: NULL-terminated array of struct igt_profiled_device pointers, or
* NULL on failure.
*/
struct igt_profiled_device *igt_devices_profiled(void)
{
struct igt_profiled_device *profiled_devices;
unsigned int devlist_len = NUM_DEVICES;
unsigned int i = 0;
struct dirent *entry;
DIR *dev_dir;
/* The return array will be resized in case there are too many devices */
profiled_devices = malloc(devlist_len * sizeof(struct igt_profiled_device));
if (!profiled_devices)
return NULL;
dev_dir = opendir(SYSFS_DRM);
if (!dev_dir)
goto end;
while ((entry = readdir(dev_dir)) != NULL) {
char path[PATH_MAX];
char orig_state;
int sysfs_fd;
/* All DRM device entries are symlinks to other paths within sysfs */
if (entry->d_type != DT_LNK)
continue;
/* We're only interested in render nodes */
if (strstr(entry->d_name, "render") != entry->d_name)
continue;
snprintf(path, sizeof(path), "%s/%s/device/%s",
SYSFS_DRM, entry->d_name, "profiling");
if (access(path, F_OK))
continue;
sysfs_fd = open(path, O_RDONLY);
if (sysfs_fd == -1)
continue;
if (read(sysfs_fd, &orig_state, 1) <= 0) {
close(sysfs_fd);
continue;
}
if (i == (devlist_len - 1)) {
struct igt_profiled_device *new_profiled_devices;
devlist_len += NUM_DEVICES;
new_profiled_devices = realloc(profiled_devices, devlist_len);
if (!new_profiled_devices)
goto end;
profiled_devices = new_profiled_devices;
}
profiled_devices[i].syspath = strdup(path);
profiled_devices[i++].original_state = orig_state;
close(sysfs_fd);
}
if (i == 0)
goto end;
else
profiled_devices[i].syspath = NULL; /* Array terminator */
return profiled_devices;
end:
free(profiled_devices);
return NULL;
}
/**
* igt_devices_configure_profiling
* @devices: NULL-terminated array of igt_profiled_device structures.
* @enable: If True, then enable profiling, otherwise restore to original state
*
* For every single device's profiling knob sysfs path in the NULL-terminated
* 'devices' array, set it to '1' if bool equals true. Otherwise set it to
* its original state at the time it was first probed in igt_devices_profiled
*
*/
void igt_devices_configure_profiling(struct igt_profiled_device *devices, bool enable)
{
assert(devices);
for (unsigned int i = 0; devices[i].syspath; i++) {
int sysfs_fd = open(devices[i].syspath, O_WRONLY);
if (sysfs_fd < 0)
continue;
write(sysfs_fd, enable ? "1" : &devices[i].original_state, 1);
close(sysfs_fd);
}
}
/**
* igt_devices_configure_profiling
* @devices: NULL-terminated array of igt_profiled_device structures.
*
* For every single struct igt_profiled_device in the 'devices' array,
* free its duplicated syspath string, and then free the array itself.
*
*/
void igt_devices_free_profiling(struct igt_profiled_device *devices)
{
assert(devices);
for (unsigned int i = 0; devices[i].syspath; i++)
free(devices[i].syspath);
free(devices);
}
/**
* igt_devices_configure_profiling
* @devices: NULL-terminated array of igt_profiled_device structures.
*
* For every single struct igt_profiled_device in the 'devices' array,
* check whether the sysfs profiling knob has changed its state since
* the last time its original state was registered, and then update it
* accordingly. This is usually a symptom that there are other profilers
* currently trying to toggle the sysfs knob, or perhaps more than one
* instance of the same profiler.
* The goal of this function is ensuring the sysfs knob is eventually
* restored to a coherent state, even though a small race window is
* possible. There's nothing we can do about this, so this function
* tries to mitigate that situation in a best-effort fashion.
*
*/
void igt_devices_update_original_profiling_state(struct igt_profiled_device *devices)
{
assert(devices);
for (unsigned int i = 0; devices[i].syspath; i++) {
char new_state;
int sysfs_fd;
sysfs_fd = open(devices[i].syspath, O_RDWR);
if (sysfs_fd == -1)
continue;
if (!read(sysfs_fd, &new_state, 1)) {
close(sysfs_fd);
continue;
}
if (new_state == '0') {
write(sysfs_fd, "1", 1);
devices[i].original_state = new_state;
}
close(sysfs_fd);
}
}
|