Message ID | 20230119164051.2819437-1-lucas.demarchi@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [i-g-t] Remove sysfs_clients | expand |
On 19/01/2023 16:40, Lucas De Marchi wrote: > For some months there was this series merged in drm-tip: > https://patchwork.freedesktop.org/series/86222/ which allowed to track > the client usage stats through sysfs. That support has been removed and > the sysfs_clients test has since been skipping on all platforms as noticed > in https://intel-gfx-ci.01.org/tree/drm-tip/shards-all.html?testfilter=sysfs_clients: > > Test requirement not met in function __igt_unique____real_main1023, file ../../../usr/src/igt-gpu-tools/tests/i915/sysfs_clients.c:1038: > Test requirement: clients != -1 > Last errno: 2, No such file or directory > Subtest busy: SKIP (0.000s) > > The functionality has been replaced by drm-usage-stats (https://docs.kernel.org/gpu/drm-usage-stats.html) > with the i915-specific bits documented at https://docs.kernel.org/gpu/i915.html#i915-usage-stats. > > Remove sysfs_clients test as it's not going to be used anymore. > > Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com> I didn't even realize it has been sitting there all this time.. Acked-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> Regards, Tvrtko > --- > tests/i915/sysfs_clients.c | 1063 ------------------------------------ > tests/meson.build | 1 - > 2 files changed, 1064 deletions(-) > delete mode 100644 tests/i915/sysfs_clients.c > > diff --git a/tests/i915/sysfs_clients.c b/tests/i915/sysfs_clients.c > deleted file mode 100644 > index b49e6a55..00000000 > --- a/tests/i915/sysfs_clients.c > +++ /dev/null > @@ -1,1063 +0,0 @@ > -/* SPDX-License-Identifier: MIT */ > -/* > - * Copyright © 2021 Intel Corporation > - */ > - > -#include <ctype.h> > -#include <dirent.h> > -#include <errno.h> > -#include <fcntl.h> > -#include <inttypes.h> > -#include <limits.h> > -#include <math.h> > -#include <sched.h> > -#include <sys/socket.h> > -#include <sys/stat.h> > -#include <sys/types.h> > -#include <unistd.h> > - > -#include "drmtest.h" > -#include "i915/gem.h" > -#include "i915/gem_create.h" > -#include "i915/gem_engine_topology.h" > -#include "i915/gem_mman.h" > -#include "igt_aux.h" > -#include "igt_dummyload.h" > -#include "igt_sysfs.h" > -#include "intel_ctx.h" > -#include "ioctl_wrappers.h" > - > -#define __require_within_epsilon(x, ref, tol_up, tol_down) \ > - igt_require_f((double)(x) <= (1.0 + (tol_up)) * (double)(ref) && \ > - (double)(x) >= (1.0 - (tol_down)) * (double)(ref), \ > - "'%s' != '%s' (%.3f not within +%.1f%%/-%.1f%% tolerance of %.3f)\n",\ > -#x, #ref, (double)(x), \ > - (tol_up) * 100.0, (tol_down) * 100.0, \ > - (double)(ref)) > - > -#define require_within_epsilon(x, ref, tolerance) \ > - __require_within_epsilon(x, ref, tolerance / 100., tolerance / 100.) > - > -#define __assert_within_epsilon(x, ref, tol_up, tol_down) \ > - igt_assert_f((double)(x) <= (1.0 + (tol_up)) * (double)(ref) && \ > - (double)(x) >= (1.0 - (tol_down)) * (double)(ref), \ > - "'%s' != '%s' (%.3f not within +%.1f%%/-%.1f%% tolerance of %.3f)\n",\ > - #x, #ref, (double)(x), \ > - (tol_up) * 100.0, (tol_down) * 100.0, \ > - (double)(ref)) > - > -#define assert_within_epsilon(x, ref, tolerance) \ > - __assert_within_epsilon(x, ref, tolerance / 100., tolerance / 100.) > - > -#define BUFSZ 280 > - > -#define MI_BATCH_BUFFER_START (0x31 << 23) > -#define MI_BATCH_BUFFER_END (0xa << 23) > -#define MI_ARB_CHECK (0x5 << 23) > - > -#define MI_SEMAPHORE_WAIT (0x1c << 23) > -#define MI_SEMAPHORE_POLL (1 << 15) > -#define MI_SEMAPHORE_SAD_GT_SDD (0 << 12) > -#define MI_SEMAPHORE_SAD_GTE_SDD (1 << 12) > -#define MI_SEMAPHORE_SAD_LT_SDD (2 << 12) > -#define MI_SEMAPHORE_SAD_LTE_SDD (3 << 12) > -#define MI_SEMAPHORE_SAD_EQ_SDD (4 << 12) > -#define MI_SEMAPHORE_SAD_NEQ_SDD (5 << 12) > - > -static void strterm(char *s, int len) > -{ > - if (len < 0) { > - *s = '\0'; > - } else { > - s[len] = '\0'; > - if (s[len - 1] == '\n') > - s[len - 1] = '\0'; > - } > -} > - > -static void pidname(int i915, int clients) > -{ > - struct dirent *de; > - int sv[2], rv[2]; > - char buf[BUFSZ]; > - int me = -1; > - long count; > - pid_t pid; > - DIR *dir; > - > - dir = fdopendir(dup(clients)); > - igt_assert(dir); > - rewinddir(dir); > - > - count = 0; > - while ((de = readdir(dir))) { > - if (!isdigit(de->d_name[0])) > - continue; > - > - snprintf(buf, sizeof(buf), "%s/name", de->d_name); > - strterm(buf, igt_sysfs_read(clients, buf, buf, sizeof(buf) - 1)); > - igt_debug("%s: %s\n", de->d_name, buf); > - > - /* Ignore closed clients created by drm_driver_open() */ > - if (*buf == '\0' || *buf == '<') > - continue; > - > - close(me); > - me = openat(clients, de->d_name, O_DIRECTORY | O_RDONLY); > - count++; > - } > - closedir(dir); > - > - /* We expect there to be only the single client (us) running */ > - igt_assert_eq(count, 1); > - igt_assert(me >= 0); > - > - strterm(buf, igt_sysfs_read(me, "name", buf, sizeof(buf) - 1)); > - > - igt_info("My name: %s\n", buf); > - igt_assert(strcmp(buf, igt_test_name()) == 0); > - > - if (!gem_has_contexts(i915)) > - return; > - > - igt_assert(pipe(sv) == 0); > - igt_assert(pipe(rv) == 0); > - > - /* If give our fd to someone else, they take over ownership of client */ > - igt_fork(child, 1) { > - read(sv[0], &pid, sizeof(pid)); > - > - /* > - * This transfer is based upon the assumption that the > - * transfer is complete ala DRI3, where the parent will > - * close the fd after sending it to the client. That is > - * it is expected that the client be only active in a single > - * process at any time. > - */ > - gem_context_destroy(i915, gem_context_create(i915)); > - > - pid = getpid(); > - write(rv[1], &pid, sizeof(pid)); > - } > - close(sv[0]); > - close(rv[1]); > - > - /* Child exists, but not yet running, we still own the client */ > - strterm(buf, igt_sysfs_read(me, "pid", buf, sizeof(buf) - 1)); > - > - pid = getpid(); > - igt_info("My pid: %s\n", buf); > - igt_assert_eq(atoi(buf), pid); > - > - /* Release and wait for the child */ > - igt_assert_eq(write(sv[1], &pid, sizeof(pid)), sizeof(pid)); > - igt_assert_eq(read(rv[0], &pid, sizeof(pid)), sizeof(pid)); > - > - /* Now child owns the client and pid should be updated to match */ > - strterm(buf, igt_sysfs_read(me, "pid", buf, sizeof(buf) - 1)); > - > - igt_info("New pid: %s\n", buf); > - igt_assert_eq(atoi(buf), pid); > - igt_waitchildren(); > - > - /* Child has definitely gone, but the client should remain */ > - strterm(buf, igt_sysfs_read(me, "pid", buf, sizeof(buf) - 1)); > - > - igt_info("Old pid: %s\n", buf); > - igt_assert_eq(atoi(buf), pid); > - > - /* And if we create a new context, ownership transfers back to us */ > - gem_context_destroy(i915, gem_context_create(i915)); > - strterm(buf, igt_sysfs_read(me, "pid", buf, sizeof(buf) - 1)); > - > - igt_info("Our pid: %s\n", buf); > - igt_assert_eq(atoi(buf), getpid()); > - > - /* Let battle commence. */ > - > - close(sv[1]); > - close(rv[0]); > - close(me); > -} > - > -static long count_clients(int clients) > -{ > - struct dirent *de; > - long count = 0; > - char buf[BUFSZ]; > - DIR *dir; > - > - dir = fdopendir(dup(clients)); > - igt_assert(dir); > - rewinddir(dir); > - > - while ((de = readdir(dir))) { > - int len; > - > - if (!isdigit(de->d_name[0])) > - continue; > - > - snprintf(buf, sizeof(buf), "%s/name", de->d_name); > - len = igt_sysfs_read(clients, buf, buf, sizeof(buf)); > - if (len < 0) > - continue; > - > - count += *buf != '<'; > - } > - closedir(dir); > - > - return count; > -} > - > -static void create(int i915, int clients) > -{ > - int fd[16]; > - > - /* Each new open("/dev/dri/cardN") is a new client */ > - igt_assert_eq(count_clients(clients), 1); > - for (int i = 0; i < ARRAY_SIZE(fd); i++) { > - fd[i] = gem_reopen_driver(i915); > - igt_assert_eq(count_clients(clients), i + 2); > - } > - > - for (int i = 0; i < ARRAY_SIZE(fd); i++) > - close(fd[i]); > - > - /* Cleanup delayed behind rcu */ > - igt_until_timeout(30) { > - sched_yield(); > - if (count_clients(clients) == 1) > - break; > - usleep(10000); > - } > - igt_assert_eq(count_clients(clients), 1); > -} > - > -static const char *find_client(int clients, pid_t pid, char *buf) > -{ > - DIR *dir = fdopendir(dup(clients)); > - > - /* Reading a dir as it changes does not appear to be stable, SEP */ > - for (int pass = 0; pass < 5; pass++) { > - struct dirent *de; > - > - rewinddir(dir); > - fsync(dirfd(dir)); > - while ((de = readdir(dir))) { > - if (!isdigit(de->d_name[0])) > - continue; > - > - snprintf(buf, BUFSZ, "%s/pid", de->d_name); > - igt_sysfs_read(clients, buf, buf, sizeof(buf)); > - if (atoi(buf) != pid) > - continue; > - > - strncpy(buf, de->d_name, BUFSZ); > - goto out; > - } > - usleep(100); > - } > - *buf = '\0'; > -out: > - closedir(dir); > - return buf; > -} > - > -static int find_me(int clients, pid_t pid) > -{ > - char buf[BUFSZ]; > - > - return openat(clients, > - find_client(clients, pid, buf), > - O_DIRECTORY | O_RDONLY); > -} > - > -static int reopen_directory(int fd) > -{ > - char buf[BUFSZ]; > - int dir; > - > - snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd); > - dir = open(buf, O_DIRECTORY | O_RDONLY); > - igt_assert_fd(dir); > - > - return dir; > -} > - > -static unsigned long my_id(int clients, pid_t pid) > -{ > - char buf[BUFSZ]; > - > - return strtoul(find_client(clients, pid, buf), NULL, 0); > -} > - > -static unsigned long recycle_client(int i915, int clients) > -{ > - unsigned long client; > - int device; > - > - device = gem_reopen_driver(i915); > - client = my_id(clients, getpid()); > - close(device); > - > - igt_assert(client != 0); > - return client; > -} > - > -static void recycle(int i915, int clients, int nchildren) > -{ > - /* > - * As we open and close clients, we do not expect to reuse old ids, > - * i.e. we use a cyclic ida. This reduces the likelihood of userspace > - * watchers becoming confused and mistaking the new client as a > - * continuation of the old. > - */ > - igt_assert(my_id(clients, getpid())); > - > - igt_fork(child, nchildren) { > - unsigned long client, last; > - > - /* Reopen the directory fd for each client */ > - clients = reopen_directory(clients); > - > - last = recycle_client(i915, clients); > - igt_info("Child[%d] first client:%lu\n", getpid(), last); > - igt_until_timeout(5) { > - client = recycle_client(i915, clients); > - igt_assert((long)(client - last) > 0); > - last = client; > - } > - igt_info("Child[%d] last client:%lu\n", getpid(), last); > - } > - igt_waitchildren(); > - > - /* Cleanup delayed behind rcu */ > - igt_until_timeout(30) { > - sched_yield(); > - if (count_clients(clients) == 1) > - break; > - usleep(10000); > - } > - igt_assert_eq(count_clients(clients), 1); > -} > - > -static int64_t read_runtime(int client, int class) > -{ > - char buf[80]; > - > - snprintf(buf, sizeof(buf), "busy/%d", class); > - return igt_sysfs_get_u64(client, buf); > -} > - > -#define MAX_CLASS 64 > -static int read_runtimes(int client, int64_t *runtime) > -{ > - int fd = openat(client, "busy", O_DIRECTORY | O_RDONLY); > - DIR *dir = fdopendir(fd); > - struct dirent *de; > - int count = 0; > - > - memset(runtime, 0, sizeof(*runtime) * MAX_CLASS); > - if (!dir) > - return -1; > - > - while ((de = readdir(dir))) { > - int class; > - > - if (!isdigit(de->d_name[0])) > - continue; > - > - class = atoi(de->d_name); > - igt_assert(class < MAX_CLASS); > - runtime[class] = igt_sysfs_get_u64(fd, de->d_name); > - > - count += runtime[class] != 0; > - } > - closedir(dir); > - > - return count; > -} > - > -static uint64_t measured_usleep(unsigned int usec) > -{ > - struct timespec tv; > - unsigned int slept; > - > - slept = igt_nsec_elapsed(memset(&tv, 0, sizeof(tv))); > - igt_assert(slept == 0); > - do { > - usleep(usec - slept); > - slept = igt_nsec_elapsed(&tv) / 1000; > - } while (slept < usec); > - > - return igt_nsec_elapsed(&tv); > -} > - > -static void > -busy_one(int i915, int clients, const intel_ctx_cfg_t *cfg, > - const struct intel_execution_engine2 *e) > -{ > - int64_t active, idle, old, other[MAX_CLASS]; > - struct timespec tv; > - const intel_ctx_t *ctx; > - igt_spin_t *spin; > - uint64_t delay; > - int me; > - > - /* Create a fresh client with 0 runtime */ > - i915 = gem_reopen_driver(i915); > - > - me = find_me(clients, getpid()); > - igt_assert(me != -1); > - > - ctx = intel_ctx_create(i915, cfg); > - spin = igt_spin_new(i915, > - .ctx = ctx, > - .engine = e->flags, > - .flags = IGT_SPIN_POLL_RUN); > - igt_spin_busywait_until_started(spin); > - usleep(10); /* kick the tasklets */ > - > - /* Compensate for discrepancies in execution latencies */ > - idle = old = read_runtime(me, e->class); > - igt_nsec_elapsed(memset(&tv, 0, sizeof(tv))); > - for (int pass = 0; pass <= 10; pass++) { > - usleep(1500 >> pass); > - > - /* Check that we accumulate the runtime, while active */ > - delay = igt_nsec_elapsed(&tv); > - active = read_runtime(me, e->class); > - delay += igt_nsec_elapsed(&tv); > - delay /= 2; /* use the centre point of the read_runtime() */ > - delay += idle; /* tare */ > - > - igt_info("active1[%d]: %'"PRIu64"ns (%'"PRIu64"ns)\n", > - pass, active, delay); > - igt_assert(active > old); /* monotonic */ > - assert_within_epsilon(active, delay, 20); > - > - old = active; > - } > - > - gem_quiescent_gpu(i915); > - > - /* And again now idle */ > - idle = read_runtime(me, e->class); > - igt_info("idle: %'"PRIu64"ns\n", idle); > - igt_assert(idle >= active); > - > - /* Check monotocity of idling */ > - for (int pass = 0; pass < 5; pass++) { > - old = idle; > - > - igt_spin_reset(spin); > - gem_execbuf(i915, &spin->execbuf); > - igt_spin_busywait_until_started(spin); > - usleep(1); /* let the system process the tasklets */ > - active = read_runtime(me, e->class); > - igt_info("idle->active[%d]: %'"PRIu64"ns\n", pass, active); > - igt_assert(active >= old); > - gem_quiescent_gpu(i915); > - > - idle = read_runtime(me, e->class); > - igt_info("active->idle[%d]: %'"PRIu64"ns\n", pass, idle); > - igt_assert(idle >= active); > - } > - > - intel_ctx_destroy(i915, ctx); > - > - /* And finally after the executing context is no more */ > - old = read_runtime(me, e->class); > - igt_info("old: %'"PRIu64"ns\n", old); > - igt_assert_eq_u64(old, idle); > - > - /* Once more on the default context for good luck */ > - igt_spin_reset(spin); > - spin->execbuf.rsvd1 = 0; > - gem_execbuf(i915, &spin->execbuf); > - igt_spin_busywait_until_started(spin); > - usleep(10); /* kick the tasklets */ > - > - idle = old = read_runtime(me, e->class); > - igt_nsec_elapsed(memset(&tv, 0, sizeof(tv))); > - for (int pass = 0; pass <= 10; pass++) { > - usleep(1000 >> pass); > - > - /* Check that we accumulate the runtime, while active */ > - delay = igt_nsec_elapsed(&tv); > - active = read_runtime(me, e->class); > - delay += igt_nsec_elapsed(&tv); > - delay /= 2; /* use the centre point of the read_runtime() */ > - delay += idle; /* tare */ > - > - igt_info("active0[%d]: %'"PRIu64"ns (%'"PRIu64"ns)\n", > - pass, active, delay); > - igt_assert(active > old); > - assert_within_epsilon(active, delay, 20); > - > - old = active; > - } > - > - gem_quiescent_gpu(i915); > - igt_assert_eq(read_runtimes(me, other), 1); > - > - igt_spin_free(i915, spin); > - close(i915); > -} > - > -static void busy_all(int i915, int clients, const intel_ctx_cfg_t *cfg) > -{ > - const struct intel_execution_engine2 *e; > - int64_t active[MAX_CLASS]; > - int64_t idle[MAX_CLASS]; > - int64_t old[MAX_CLASS]; > - uint64_t classes = 0; > - const intel_ctx_t *ctx; > - igt_spin_t *spin; > - int expect = 0; > - int64_t delay; > - int me; > - > - /* Create a fresh client with 0 runtime */ > - i915 = gem_reopen_driver(i915); > - > - me = find_me(clients, getpid()); > - igt_assert(me != -1); > - > - ctx = intel_ctx_create(i915, cfg); > - spin = igt_spin_new(i915, .ctx = ctx, > - .flags = IGT_SPIN_POLL_RUN); > - for_each_ctx_engine(i915, ctx, e) { > - if (!gem_class_can_store_dword(i915, e->class)) > - continue; > - > - spin->execbuf.flags &= ~63; > - spin->execbuf.flags |= e->flags; > - gem_execbuf(i915, &spin->execbuf); > - > - if (!(classes & (1ull << e->class))) > - expect++; > - classes |= 1ull << e->class; > - } > - igt_spin_busywait_until_started(spin); > - > - delay = -500000; /* 500us slack */ > - memset(old, 0, sizeof(old)); > - for (int pass = 0; pass < 5; pass++) { > - delay += measured_usleep(1000 >> pass); > - igt_debug("delay: %'"PRIu64"ns\n", delay); > - > - /* Check that we accumulate the runtime, while active */ > - igt_assert_eq(read_runtimes(me, active), expect); > - for (int i = 0; i < ARRAY_SIZE(active); i++) { > - if (!active[i]) > - continue; > - > - igt_info("active[%d]: %'"PRIu64"ns\n", i, active[i]); > - igt_assert(active[i] > old[i]); /* monotonic */ > - igt_assert(active[i] > delay); /* within reason */ > - } > - > - memcpy(old, active, sizeof(old)); > - } > - > - gem_quiescent_gpu(i915); > - > - /* And again now idle */ > - igt_assert_eq(read_runtimes(me, idle), expect); > - for (int i = 0; i < ARRAY_SIZE(idle); i++) { > - if (!idle[i]) > - continue; > - > - igt_info("idle[%d]: %'"PRIu64"ns\n", i, idle[i]); > - igt_assert(idle[i] >= active[i]); > - } > - > - intel_ctx_destroy(i915, ctx); > - igt_spin_free(i915, spin); > - > - /* And finally after the executing context is no more */ > - igt_assert_eq(read_runtimes(me, old), expect); > - for (int i = 0; i < ARRAY_SIZE(old); i++) { > - if (!old[i]) > - continue; > - > - igt_info("old[%d]: %'"PRIu64"ns\n", i, old[i]); > - igt_assert_eq_u64(old[i], idle[i]); > - } > - > - close(i915); > -} > - > -static void > -split_child(int i915, int clients, const intel_ctx_cfg_t *cfg, > - const struct intel_execution_engine2 *e, > - int sv) > -{ > - int64_t runtime[2] = {}; > - const intel_ctx_t *ctx; > - igt_spin_t *spin; > - int go = 1; > - > - i915 = gem_reopen_driver(i915); > - > - ctx = intel_ctx_create(i915, cfg); > - spin = igt_spin_new(i915, .ctx = ctx, .engine = e->flags); > - igt_spin_end(spin); > - gem_sync(i915, spin->handle); > - > - write(sv, &go, sizeof(go)); > - read(sv, &go, sizeof(go)); > - while (go != -1) { > - struct timespec tv = {}; > - > - igt_spin_reset(spin); > - gem_execbuf(i915, &spin->execbuf); > - igt_nsec_elapsed(&tv); > - read(sv, &go, sizeof(go)); > - igt_spin_end(spin); > - runtime[1] += igt_nsec_elapsed(&tv); > - read(sv, &go, sizeof(go)); > - } > - igt_spin_free(i915, spin); > - > - runtime[0] = read_runtime(find_me(clients, getpid()), e->class); > - intel_ctx_destroy(i915, ctx); > - write(sv, runtime, sizeof(runtime)); > -} > - > -static void > -__split(int i915, int clients, const intel_ctx_cfg_t *cfg, > - const struct intel_execution_engine2 *e, int f, > - void (*fn)(int i915, int clients, const intel_ctx_cfg_t *cfg, > - const struct intel_execution_engine2 *e, > - int sv)) > -{ > - struct client { > - int64_t active[2]; > - int sv[2]; > - int f; > - } client[2]; > - uint64_t total[2] = { 1, 1 }; /* 1ns offset to prevent div-by-zero */ > - int go, stop; > - int i; > - > - for (i = 0; i < ARRAY_SIZE(client); i++) { > - struct client *c = memset(&client[i], 0, sizeof(*c)); > - > - c->f = f; > - f = 100 - f; > - > - igt_assert(socketpair(AF_UNIX, SOCK_DGRAM, 0, c->sv) == 0); > - igt_fork(child, 1) > - fn(i915, clients, cfg, e, c->sv[1]); > - > - read(c->sv[0], &go, sizeof(go)); > - } > - > - /* Alternate between the clients, telling each to be active in turn */ > - i = 0; > - go = 1; > - stop = 0; > - write(client[i].sv[0], &go, sizeof(go)); > - igt_until_timeout(2) { > - measured_usleep(100 * client[i].f); > - write(client[!i].sv[0], &go, sizeof(go)); > - write(client[i].sv[0], &stop, sizeof(stop)); > - > - i = !i; > - } > - write(client[i].sv[0], &stop, sizeof(stop)); > - > - /* Gather up the client runtimes */ > - go = -1; > - for (i = 0; i < ARRAY_SIZE(client); i++) { > - struct client *c = &client[i]; > - > - write(c->sv[0], &go, sizeof(go)); > - igt_assert_eq(read(c->sv[0], c->active, sizeof(c->active)), > - sizeof(c->active)); > - > - total[0] += c->active[0]; > - total[1] += c->active[1]; > - } > - igt_waitchildren(); > - > - /* Print the results, before making any checks */ > - for (i = 0; i < ARRAY_SIZE(client); i++) { > - const struct client *c = &client[i]; > - > - igt_info("active[%02d]: %'"PRIu64"ns (%'"PRIu64"ns), %.1f%%\n", > - c->f, c->active[0], c->active[1], > - c->active[0] * 100. / total[0]); > - } > - > - /* Check that each client received their target runtime */ > - for (i = 0; i < ARRAY_SIZE(client); i++) { > - const struct client *c = &client[i]; > - double t = (100 - c->f) / 5.; /* 20% tolerance for smallest */ > - > - igt_debug("active[%02d]: target runtime %'"PRIu64"ns, %.1f%%\n", > - c->f, c->active[1], > - c->active[1] * 100. / total[1]); > - > - require_within_epsilon(c->active[1], c->f * total[1] / 100., t); > - } > - > - /* Validate each client reported their share of the total runtime */ > - for (i = 0; i < ARRAY_SIZE(client); i++) { > - const struct client *c = &client[i]; > - double t = 100 - c->f; > - > - igt_debug("active[%02d]: runtime %'"PRIu64"ns, %.1f%%\n", > - c->f, c->active[0], > - c->active[0] * 100. / total[0]); > - > - assert_within_epsilon(c->active[0], c->f * total[0] / 100., t); > - } > -} > - > -static void > -split(int i915, int clients, const intel_ctx_cfg_t *cfg, > - const struct intel_execution_engine2 *e, int f) > -{ > - __split(i915, clients, cfg, e, f, split_child); > -} > - > -static void > -sema_child(int i915, int clients, const intel_ctx_cfg_t *cfg, > - const struct intel_execution_engine2 *e, > - int sv) > -{ > - int64_t runtime[2] = {}; > - struct drm_i915_gem_exec_object2 obj = { > - .flags = EXEC_OBJECT_SUPPORTS_48B_ADDRESS > - }; > - struct drm_i915_gem_execbuffer2 execbuf = { > - .buffers_ptr = to_user_pointer(&obj), > - .buffer_count = 1, > - .flags = e->flags, > - }; > - const intel_ctx_t *ctx; > - uint32_t *cs, *sema; > - > - i915 = gem_reopen_driver(i915); > - ctx = intel_ctx_create(i915, cfg); > - execbuf.rsvd1 = ctx->id; > - > - obj.handle = gem_create(i915, 4096); > - obj.offset = obj.handle << 12; > - sema = cs = gem_mmap__device_coherent(i915, obj.handle, > - 0, 4096, PROT_WRITE); > - > - *cs = MI_BATCH_BUFFER_END; > - gem_execbuf(i915, &execbuf); > - gem_sync(i915, obj.handle); > - obj.flags |= EXEC_OBJECT_PINNED; > - > - cs += 16; > - > - *cs++ = MI_ARB_CHECK; > - *cs++ = MI_SEMAPHORE_WAIT | > - MI_SEMAPHORE_POLL | > - MI_SEMAPHORE_SAD_NEQ_SDD | > - (4 - 2); > - *cs++ = 0; > - *cs++ = obj.offset; > - *cs++ = obj.offset >> 32; > - > - *cs++ = MI_BATCH_BUFFER_START | 1 << 8 | 1; > - *cs++ = obj.offset + 64; > - *cs++ = obj.offset >> 32; > - > - *sema = 0; > - gem_execbuf(i915, &execbuf); > - gem_close(i915, obj.handle); > - intel_ctx_destroy(i915, ctx); > - > - write(sv, sema, sizeof(*sema)); > - read(sv, sema, sizeof(*sema)); > - while (*sema != -1) { > - struct timespec tv = {}; > - > - __sync_synchronize(); > - igt_nsec_elapsed(&tv); > - read(sv, sema, sizeof(*sema)); > - > - __sync_synchronize(); > - runtime[1] += igt_nsec_elapsed(&tv); > - read(sv, sema, sizeof(*sema)); > - } > - > - runtime[0] = read_runtime(find_me(clients, getpid()), e->class); > - write(sv, runtime, sizeof(runtime)); > - > - sema[16] = MI_BATCH_BUFFER_END; > - __sync_synchronize(); > -} > - > -static void > -sema(int i915, int clients, const intel_ctx_cfg_t *cfg, > - const struct intel_execution_engine2 *e, int f) > -{ > - __split(i915, clients, cfg, e, f, sema_child); > -} > - > -static int read_all(int clients, pid_t pid, int class, uint64_t *runtime) > -{ > - struct dirent *de; > - char buf[BUFSZ]; > - int count = 0; > - DIR *dir; > - > - dir = fdopendir(dup(clients)); > - igt_assert(dir); > - rewinddir(dir); > - > - while ((de = readdir(dir))) { > - int me; > - > - if (!isdigit(de->d_name[0])) > - continue; > - > - snprintf(buf, sizeof(buf), "%s/pid", de->d_name); > - igt_sysfs_read(clients, buf, buf, sizeof(buf)); > - if (atoi(buf) != pid) > - continue; > - > - me = openat(clients, de->d_name, O_DIRECTORY | O_RDONLY); > - runtime[count++] = read_runtime(me, class); > - close(me); > - } > - > - closedir(dir); > - return count; > -} > - > -static int cmp_u64(const void *A, const void *B) > -{ > - const uint64_t *a = A, *b = B; > - > - if (*a < *b) > - return -1; > - else if (*a > *b) > - return 1; > - else > - return 0; > -} > - > -static void __fair(int i915, int clients, > - int class, const char *name, > - int extra, int duration) > -{ > - const double timeslice_duration_ns = 5e6; > - unsigned int count; > - uint64_t *runtime; > - double threshold; > - double expect; > - int i; > - > - i = 0; > - do { > - int client = gem_reopen_driver(i915); > - uint32_t ctx; > - > - ctx = gem_context_create_for_class(client, class, &count); > - __igt_spin_new(client, ctx); > - } while (++i < count + extra); > - extra = i; > - > - sleep(duration); /* over the course of many timeslices */ > - > - runtime = calloc(extra, sizeof(*runtime)); > - igt_assert_eq(read_all(clients, getpid(), class, runtime), extra); > - > - /* > - * If we imagine that the timeslices are randomly distributed to > - * the clients, we would expect the variance to be modelled > - * by a drunken walk; ergo sqrt(num_timeslices). > - */ > - threshold = sqrt(1e9 * duration / timeslice_duration_ns); > - threshold *= timeslice_duration_ns; > - threshold *= extra > count; /* timeslicing active? */ > - threshold *= 3; /* CI safety factor before crying wolf */ > - threshold += 5e6; /* tolerance for 5ms measuring error */ > - > - expect = 1e9 * count * duration / extra; > - > - qsort(runtime, extra, sizeof(*runtime), cmp_u64); > - igt_info("%s: [%.1f, %.1f, %.1f] ms, expect %1.f +- %.1fms\n", > - name, > - 1e-6 * runtime[0], > - 1e-6 * runtime[extra / 2], > - 1e-6 * runtime[extra - 1], > - 1e-6 * expect, > - 1e-6 * threshold); > - > - assert_within_epsilon(runtime[extra / 2], expect, 20); > - igt_assert_f(runtime[extra - 1] - runtime[0] <= 2 * threshold, > - "Range of timeslices greater than tolerable: %.2fms > %.2fms; unfair!\n", > - 1e-6 * (runtime[extra - 1] - runtime[0]), > - 1e-6 * threshold * 2); > -} > - > -static void fair(int i915, int clients, int extra, int duration) > -{ > - static const char *names[] = { > - [I915_ENGINE_CLASS_RENDER] = "rcs", > - [I915_ENGINE_CLASS_COPY] = "bcs", > - [I915_ENGINE_CLASS_VIDEO] = "vcs", > - [I915_ENGINE_CLASS_VIDEO_ENHANCE] = "vecs", > - }; > - > - gem_quiescent_gpu(i915); > - > - for (int class = 0; class < ARRAY_SIZE(names); class++) { > - unsigned int count; > - uint32_t ctx; > - > - ctx = gem_context_create_for_class(i915, class, &count); > - if (!ctx) > - continue; > - gem_context_destroy(i915, ctx); > - > - igt_dynamic_f("%s", names[class]) { > - igt_drop_caches_set(i915, DROP_RESET_ACTIVE); > - igt_fork(child, 1) > - __fair(i915, clients, class, names[class], > - extra, duration); > - igt_waitchildren(); > - gem_quiescent_gpu(i915); > - } > - igt_drop_caches_set(i915, DROP_RESET_ACTIVE); > - } > -} > - > -static bool has_busy(int clients) > -{ > - bool ok; > - int me; > - > - me = find_me(clients, getpid()); > - ok = faccessat(me, "busy", 0, F_OK) == 0; > - close(me); > - > - return ok; > -} > - > -static void test_busy(int i915, int clients) > -{ > - const struct intel_execution_engine2 *e; > - intel_ctx_cfg_t cfg; > - const int frac[] = { 10, 25, 50 }; > - > - igt_fixture { > - igt_require(gem_has_contexts(i915)); > - igt_require(has_busy(clients)); > - cfg = intel_ctx_cfg_all_physical(i915); > - } > - > - igt_subtest_with_dynamic("busy") { > - for_each_ctx_cfg_engine(i915, &cfg, e) { > - if (!gem_class_can_store_dword(i915, e->class)) > - continue; > - igt_dynamic_f("%s", e->name) { > - gem_quiescent_gpu(i915); > - igt_fork(child, 1) > - busy_one(i915, clients, &cfg, e); > - igt_waitchildren(); > - gem_quiescent_gpu(i915); > - } > - } > - > - igt_dynamic("all-engines") { > - gem_quiescent_gpu(i915); > - igt_fork(child, 1) > - busy_all(i915, clients, &cfg); > - igt_waitchildren(); > - gem_quiescent_gpu(i915); > - } > - } > - > - for (int i = 0; i < ARRAY_SIZE(frac); i++) { > - igt_subtest_with_dynamic_f("split-%d", frac[i]) { > - for_each_ctx_cfg_engine(i915, &cfg, e) { > - igt_dynamic_f("%s", e->name) { > - gem_quiescent_gpu(i915); > - split(i915, clients, &cfg, e, frac[i]); > - gem_quiescent_gpu(i915); > - } > - } > - } > - } > - > - igt_subtest_group { > - igt_fixture { > - //igt_require(gem_scheduler_has_timeslicing(i915)); > - igt_require(gem_scheduler_has_preemption(i915)); > - } > - > - for (int i = 0; i < ARRAY_SIZE(frac); i++) { > - igt_subtest_with_dynamic_f("sema-%d", frac[i]) { > - for_each_ctx_cfg_engine(i915, &cfg, e) { > - if (!gem_class_has_mutable_submission(i915, e->class)) > - continue; > - > - igt_dynamic_f("%s", e->name) { > - igt_drop_caches_set(i915, DROP_RESET_ACTIVE); > - sema(i915, clients, &cfg, e, frac[i]); > - gem_quiescent_gpu(i915); > - } > - igt_drop_caches_set(i915, DROP_RESET_ACTIVE); > - } > - } > - } > - > - for (int i = 0; i < 4; i++) { > - igt_subtest_with_dynamic_f("fair-%d", (1 << i) - 1) > - fair(i915, clients, (1 << i) - 1, 5); > - } > - } > -} > - > -igt_main > -{ > - int i915 = -1, clients = -1; > - > - igt_fixture { > - int sys; > - > - /* Don't allow [too many] extra clients to be opened */ > - i915 = __drm_open_driver(DRIVER_INTEL); > - igt_require_gem(i915); > - > - sys = igt_sysfs_open(i915); > - igt_require(sys != -1); > - > - clients = openat(sys, "clients", O_RDONLY); > - igt_require(clients != -1); > - > - close(sys); > - usleep(10); > - } > - > - igt_subtest("pidname") > - pidname(i915, clients); > - > - igt_subtest("create") > - create(i915, clients); > - > - igt_subtest("recycle") > - recycle(i915, clients, 1); > - > - igt_subtest("recycle-many") > - recycle(i915, clients, 2 * sysconf(_SC_NPROCESSORS_ONLN)); > - > - igt_subtest_group > - test_busy(i915, clients); > - > - igt_fixture { > - close(clients); > - close(i915); > - } > -} > diff --git a/tests/meson.build b/tests/meson.build > index e20a8640..25a4e8a3 100644 > --- a/tests/meson.build > +++ b/tests/meson.build > @@ -235,7 +235,6 @@ i915_progs = [ > 'kms_psr2_sf', > 'kms_psr_stress_test', > 'kms_pwrite_crc', > - 'sysfs_clients', > 'sysfs_defaults', > 'sysfs_heartbeat_interval', > 'sysfs_preempt_timeout',
diff --git a/tests/i915/sysfs_clients.c b/tests/i915/sysfs_clients.c deleted file mode 100644 index b49e6a55..00000000 --- a/tests/i915/sysfs_clients.c +++ /dev/null @@ -1,1063 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2021 Intel Corporation - */ - -#include <ctype.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <math.h> -#include <sched.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "drmtest.h" -#include "i915/gem.h" -#include "i915/gem_create.h" -#include "i915/gem_engine_topology.h" -#include "i915/gem_mman.h" -#include "igt_aux.h" -#include "igt_dummyload.h" -#include "igt_sysfs.h" -#include "intel_ctx.h" -#include "ioctl_wrappers.h" - -#define __require_within_epsilon(x, ref, tol_up, tol_down) \ - igt_require_f((double)(x) <= (1.0 + (tol_up)) * (double)(ref) && \ - (double)(x) >= (1.0 - (tol_down)) * (double)(ref), \ - "'%s' != '%s' (%.3f not within +%.1f%%/-%.1f%% tolerance of %.3f)\n",\ -#x, #ref, (double)(x), \ - (tol_up) * 100.0, (tol_down) * 100.0, \ - (double)(ref)) - -#define require_within_epsilon(x, ref, tolerance) \ - __require_within_epsilon(x, ref, tolerance / 100., tolerance / 100.) - -#define __assert_within_epsilon(x, ref, tol_up, tol_down) \ - igt_assert_f((double)(x) <= (1.0 + (tol_up)) * (double)(ref) && \ - (double)(x) >= (1.0 - (tol_down)) * (double)(ref), \ - "'%s' != '%s' (%.3f not within +%.1f%%/-%.1f%% tolerance of %.3f)\n",\ - #x, #ref, (double)(x), \ - (tol_up) * 100.0, (tol_down) * 100.0, \ - (double)(ref)) - -#define assert_within_epsilon(x, ref, tolerance) \ - __assert_within_epsilon(x, ref, tolerance / 100., tolerance / 100.) - -#define BUFSZ 280 - -#define MI_BATCH_BUFFER_START (0x31 << 23) -#define MI_BATCH_BUFFER_END (0xa << 23) -#define MI_ARB_CHECK (0x5 << 23) - -#define MI_SEMAPHORE_WAIT (0x1c << 23) -#define MI_SEMAPHORE_POLL (1 << 15) -#define MI_SEMAPHORE_SAD_GT_SDD (0 << 12) -#define MI_SEMAPHORE_SAD_GTE_SDD (1 << 12) -#define MI_SEMAPHORE_SAD_LT_SDD (2 << 12) -#define MI_SEMAPHORE_SAD_LTE_SDD (3 << 12) -#define MI_SEMAPHORE_SAD_EQ_SDD (4 << 12) -#define MI_SEMAPHORE_SAD_NEQ_SDD (5 << 12) - -static void strterm(char *s, int len) -{ - if (len < 0) { - *s = '\0'; - } else { - s[len] = '\0'; - if (s[len - 1] == '\n') - s[len - 1] = '\0'; - } -} - -static void pidname(int i915, int clients) -{ - struct dirent *de; - int sv[2], rv[2]; - char buf[BUFSZ]; - int me = -1; - long count; - pid_t pid; - DIR *dir; - - dir = fdopendir(dup(clients)); - igt_assert(dir); - rewinddir(dir); - - count = 0; - while ((de = readdir(dir))) { - if (!isdigit(de->d_name[0])) - continue; - - snprintf(buf, sizeof(buf), "%s/name", de->d_name); - strterm(buf, igt_sysfs_read(clients, buf, buf, sizeof(buf) - 1)); - igt_debug("%s: %s\n", de->d_name, buf); - - /* Ignore closed clients created by drm_driver_open() */ - if (*buf == '\0' || *buf == '<') - continue; - - close(me); - me = openat(clients, de->d_name, O_DIRECTORY | O_RDONLY); - count++; - } - closedir(dir); - - /* We expect there to be only the single client (us) running */ - igt_assert_eq(count, 1); - igt_assert(me >= 0); - - strterm(buf, igt_sysfs_read(me, "name", buf, sizeof(buf) - 1)); - - igt_info("My name: %s\n", buf); - igt_assert(strcmp(buf, igt_test_name()) == 0); - - if (!gem_has_contexts(i915)) - return; - - igt_assert(pipe(sv) == 0); - igt_assert(pipe(rv) == 0); - - /* If give our fd to someone else, they take over ownership of client */ - igt_fork(child, 1) { - read(sv[0], &pid, sizeof(pid)); - - /* - * This transfer is based upon the assumption that the - * transfer is complete ala DRI3, where the parent will - * close the fd after sending it to the client. That is - * it is expected that the client be only active in a single - * process at any time. - */ - gem_context_destroy(i915, gem_context_create(i915)); - - pid = getpid(); - write(rv[1], &pid, sizeof(pid)); - } - close(sv[0]); - close(rv[1]); - - /* Child exists, but not yet running, we still own the client */ - strterm(buf, igt_sysfs_read(me, "pid", buf, sizeof(buf) - 1)); - - pid = getpid(); - igt_info("My pid: %s\n", buf); - igt_assert_eq(atoi(buf), pid); - - /* Release and wait for the child */ - igt_assert_eq(write(sv[1], &pid, sizeof(pid)), sizeof(pid)); - igt_assert_eq(read(rv[0], &pid, sizeof(pid)), sizeof(pid)); - - /* Now child owns the client and pid should be updated to match */ - strterm(buf, igt_sysfs_read(me, "pid", buf, sizeof(buf) - 1)); - - igt_info("New pid: %s\n", buf); - igt_assert_eq(atoi(buf), pid); - igt_waitchildren(); - - /* Child has definitely gone, but the client should remain */ - strterm(buf, igt_sysfs_read(me, "pid", buf, sizeof(buf) - 1)); - - igt_info("Old pid: %s\n", buf); - igt_assert_eq(atoi(buf), pid); - - /* And if we create a new context, ownership transfers back to us */ - gem_context_destroy(i915, gem_context_create(i915)); - strterm(buf, igt_sysfs_read(me, "pid", buf, sizeof(buf) - 1)); - - igt_info("Our pid: %s\n", buf); - igt_assert_eq(atoi(buf), getpid()); - - /* Let battle commence. */ - - close(sv[1]); - close(rv[0]); - close(me); -} - -static long count_clients(int clients) -{ - struct dirent *de; - long count = 0; - char buf[BUFSZ]; - DIR *dir; - - dir = fdopendir(dup(clients)); - igt_assert(dir); - rewinddir(dir); - - while ((de = readdir(dir))) { - int len; - - if (!isdigit(de->d_name[0])) - continue; - - snprintf(buf, sizeof(buf), "%s/name", de->d_name); - len = igt_sysfs_read(clients, buf, buf, sizeof(buf)); - if (len < 0) - continue; - - count += *buf != '<'; - } - closedir(dir); - - return count; -} - -static void create(int i915, int clients) -{ - int fd[16]; - - /* Each new open("/dev/dri/cardN") is a new client */ - igt_assert_eq(count_clients(clients), 1); - for (int i = 0; i < ARRAY_SIZE(fd); i++) { - fd[i] = gem_reopen_driver(i915); - igt_assert_eq(count_clients(clients), i + 2); - } - - for (int i = 0; i < ARRAY_SIZE(fd); i++) - close(fd[i]); - - /* Cleanup delayed behind rcu */ - igt_until_timeout(30) { - sched_yield(); - if (count_clients(clients) == 1) - break; - usleep(10000); - } - igt_assert_eq(count_clients(clients), 1); -} - -static const char *find_client(int clients, pid_t pid, char *buf) -{ - DIR *dir = fdopendir(dup(clients)); - - /* Reading a dir as it changes does not appear to be stable, SEP */ - for (int pass = 0; pass < 5; pass++) { - struct dirent *de; - - rewinddir(dir); - fsync(dirfd(dir)); - while ((de = readdir(dir))) { - if (!isdigit(de->d_name[0])) - continue; - - snprintf(buf, BUFSZ, "%s/pid", de->d_name); - igt_sysfs_read(clients, buf, buf, sizeof(buf)); - if (atoi(buf) != pid) - continue; - - strncpy(buf, de->d_name, BUFSZ); - goto out; - } - usleep(100); - } - *buf = '\0'; -out: - closedir(dir); - return buf; -} - -static int find_me(int clients, pid_t pid) -{ - char buf[BUFSZ]; - - return openat(clients, - find_client(clients, pid, buf), - O_DIRECTORY | O_RDONLY); -} - -static int reopen_directory(int fd) -{ - char buf[BUFSZ]; - int dir; - - snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd); - dir = open(buf, O_DIRECTORY | O_RDONLY); - igt_assert_fd(dir); - - return dir; -} - -static unsigned long my_id(int clients, pid_t pid) -{ - char buf[BUFSZ]; - - return strtoul(find_client(clients, pid, buf), NULL, 0); -} - -static unsigned long recycle_client(int i915, int clients) -{ - unsigned long client; - int device; - - device = gem_reopen_driver(i915); - client = my_id(clients, getpid()); - close(device); - - igt_assert(client != 0); - return client; -} - -static void recycle(int i915, int clients, int nchildren) -{ - /* - * As we open and close clients, we do not expect to reuse old ids, - * i.e. we use a cyclic ida. This reduces the likelihood of userspace - * watchers becoming confused and mistaking the new client as a - * continuation of the old. - */ - igt_assert(my_id(clients, getpid())); - - igt_fork(child, nchildren) { - unsigned long client, last; - - /* Reopen the directory fd for each client */ - clients = reopen_directory(clients); - - last = recycle_client(i915, clients); - igt_info("Child[%d] first client:%lu\n", getpid(), last); - igt_until_timeout(5) { - client = recycle_client(i915, clients); - igt_assert((long)(client - last) > 0); - last = client; - } - igt_info("Child[%d] last client:%lu\n", getpid(), last); - } - igt_waitchildren(); - - /* Cleanup delayed behind rcu */ - igt_until_timeout(30) { - sched_yield(); - if (count_clients(clients) == 1) - break; - usleep(10000); - } - igt_assert_eq(count_clients(clients), 1); -} - -static int64_t read_runtime(int client, int class) -{ - char buf[80]; - - snprintf(buf, sizeof(buf), "busy/%d", class); - return igt_sysfs_get_u64(client, buf); -} - -#define MAX_CLASS 64 -static int read_runtimes(int client, int64_t *runtime) -{ - int fd = openat(client, "busy", O_DIRECTORY | O_RDONLY); - DIR *dir = fdopendir(fd); - struct dirent *de; - int count = 0; - - memset(runtime, 0, sizeof(*runtime) * MAX_CLASS); - if (!dir) - return -1; - - while ((de = readdir(dir))) { - int class; - - if (!isdigit(de->d_name[0])) - continue; - - class = atoi(de->d_name); - igt_assert(class < MAX_CLASS); - runtime[class] = igt_sysfs_get_u64(fd, de->d_name); - - count += runtime[class] != 0; - } - closedir(dir); - - return count; -} - -static uint64_t measured_usleep(unsigned int usec) -{ - struct timespec tv; - unsigned int slept; - - slept = igt_nsec_elapsed(memset(&tv, 0, sizeof(tv))); - igt_assert(slept == 0); - do { - usleep(usec - slept); - slept = igt_nsec_elapsed(&tv) / 1000; - } while (slept < usec); - - return igt_nsec_elapsed(&tv); -} - -static void -busy_one(int i915, int clients, const intel_ctx_cfg_t *cfg, - const struct intel_execution_engine2 *e) -{ - int64_t active, idle, old, other[MAX_CLASS]; - struct timespec tv; - const intel_ctx_t *ctx; - igt_spin_t *spin; - uint64_t delay; - int me; - - /* Create a fresh client with 0 runtime */ - i915 = gem_reopen_driver(i915); - - me = find_me(clients, getpid()); - igt_assert(me != -1); - - ctx = intel_ctx_create(i915, cfg); - spin = igt_spin_new(i915, - .ctx = ctx, - .engine = e->flags, - .flags = IGT_SPIN_POLL_RUN); - igt_spin_busywait_until_started(spin); - usleep(10); /* kick the tasklets */ - - /* Compensate for discrepancies in execution latencies */ - idle = old = read_runtime(me, e->class); - igt_nsec_elapsed(memset(&tv, 0, sizeof(tv))); - for (int pass = 0; pass <= 10; pass++) { - usleep(1500 >> pass); - - /* Check that we accumulate the runtime, while active */ - delay = igt_nsec_elapsed(&tv); - active = read_runtime(me, e->class); - delay += igt_nsec_elapsed(&tv); - delay /= 2; /* use the centre point of the read_runtime() */ - delay += idle; /* tare */ - - igt_info("active1[%d]: %'"PRIu64"ns (%'"PRIu64"ns)\n", - pass, active, delay); - igt_assert(active > old); /* monotonic */ - assert_within_epsilon(active, delay, 20); - - old = active; - } - - gem_quiescent_gpu(i915); - - /* And again now idle */ - idle = read_runtime(me, e->class); - igt_info("idle: %'"PRIu64"ns\n", idle); - igt_assert(idle >= active); - - /* Check monotocity of idling */ - for (int pass = 0; pass < 5; pass++) { - old = idle; - - igt_spin_reset(spin); - gem_execbuf(i915, &spin->execbuf); - igt_spin_busywait_until_started(spin); - usleep(1); /* let the system process the tasklets */ - active = read_runtime(me, e->class); - igt_info("idle->active[%d]: %'"PRIu64"ns\n", pass, active); - igt_assert(active >= old); - gem_quiescent_gpu(i915); - - idle = read_runtime(me, e->class); - igt_info("active->idle[%d]: %'"PRIu64"ns\n", pass, idle); - igt_assert(idle >= active); - } - - intel_ctx_destroy(i915, ctx); - - /* And finally after the executing context is no more */ - old = read_runtime(me, e->class); - igt_info("old: %'"PRIu64"ns\n", old); - igt_assert_eq_u64(old, idle); - - /* Once more on the default context for good luck */ - igt_spin_reset(spin); - spin->execbuf.rsvd1 = 0; - gem_execbuf(i915, &spin->execbuf); - igt_spin_busywait_until_started(spin); - usleep(10); /* kick the tasklets */ - - idle = old = read_runtime(me, e->class); - igt_nsec_elapsed(memset(&tv, 0, sizeof(tv))); - for (int pass = 0; pass <= 10; pass++) { - usleep(1000 >> pass); - - /* Check that we accumulate the runtime, while active */ - delay = igt_nsec_elapsed(&tv); - active = read_runtime(me, e->class); - delay += igt_nsec_elapsed(&tv); - delay /= 2; /* use the centre point of the read_runtime() */ - delay += idle; /* tare */ - - igt_info("active0[%d]: %'"PRIu64"ns (%'"PRIu64"ns)\n", - pass, active, delay); - igt_assert(active > old); - assert_within_epsilon(active, delay, 20); - - old = active; - } - - gem_quiescent_gpu(i915); - igt_assert_eq(read_runtimes(me, other), 1); - - igt_spin_free(i915, spin); - close(i915); -} - -static void busy_all(int i915, int clients, const intel_ctx_cfg_t *cfg) -{ - const struct intel_execution_engine2 *e; - int64_t active[MAX_CLASS]; - int64_t idle[MAX_CLASS]; - int64_t old[MAX_CLASS]; - uint64_t classes = 0; - const intel_ctx_t *ctx; - igt_spin_t *spin; - int expect = 0; - int64_t delay; - int me; - - /* Create a fresh client with 0 runtime */ - i915 = gem_reopen_driver(i915); - - me = find_me(clients, getpid()); - igt_assert(me != -1); - - ctx = intel_ctx_create(i915, cfg); - spin = igt_spin_new(i915, .ctx = ctx, - .flags = IGT_SPIN_POLL_RUN); - for_each_ctx_engine(i915, ctx, e) { - if (!gem_class_can_store_dword(i915, e->class)) - continue; - - spin->execbuf.flags &= ~63; - spin->execbuf.flags |= e->flags; - gem_execbuf(i915, &spin->execbuf); - - if (!(classes & (1ull << e->class))) - expect++; - classes |= 1ull << e->class; - } - igt_spin_busywait_until_started(spin); - - delay = -500000; /* 500us slack */ - memset(old, 0, sizeof(old)); - for (int pass = 0; pass < 5; pass++) { - delay += measured_usleep(1000 >> pass); - igt_debug("delay: %'"PRIu64"ns\n", delay); - - /* Check that we accumulate the runtime, while active */ - igt_assert_eq(read_runtimes(me, active), expect); - for (int i = 0; i < ARRAY_SIZE(active); i++) { - if (!active[i]) - continue; - - igt_info("active[%d]: %'"PRIu64"ns\n", i, active[i]); - igt_assert(active[i] > old[i]); /* monotonic */ - igt_assert(active[i] > delay); /* within reason */ - } - - memcpy(old, active, sizeof(old)); - } - - gem_quiescent_gpu(i915); - - /* And again now idle */ - igt_assert_eq(read_runtimes(me, idle), expect); - for (int i = 0; i < ARRAY_SIZE(idle); i++) { - if (!idle[i]) - continue; - - igt_info("idle[%d]: %'"PRIu64"ns\n", i, idle[i]); - igt_assert(idle[i] >= active[i]); - } - - intel_ctx_destroy(i915, ctx); - igt_spin_free(i915, spin); - - /* And finally after the executing context is no more */ - igt_assert_eq(read_runtimes(me, old), expect); - for (int i = 0; i < ARRAY_SIZE(old); i++) { - if (!old[i]) - continue; - - igt_info("old[%d]: %'"PRIu64"ns\n", i, old[i]); - igt_assert_eq_u64(old[i], idle[i]); - } - - close(i915); -} - -static void -split_child(int i915, int clients, const intel_ctx_cfg_t *cfg, - const struct intel_execution_engine2 *e, - int sv) -{ - int64_t runtime[2] = {}; - const intel_ctx_t *ctx; - igt_spin_t *spin; - int go = 1; - - i915 = gem_reopen_driver(i915); - - ctx = intel_ctx_create(i915, cfg); - spin = igt_spin_new(i915, .ctx = ctx, .engine = e->flags); - igt_spin_end(spin); - gem_sync(i915, spin->handle); - - write(sv, &go, sizeof(go)); - read(sv, &go, sizeof(go)); - while (go != -1) { - struct timespec tv = {}; - - igt_spin_reset(spin); - gem_execbuf(i915, &spin->execbuf); - igt_nsec_elapsed(&tv); - read(sv, &go, sizeof(go)); - igt_spin_end(spin); - runtime[1] += igt_nsec_elapsed(&tv); - read(sv, &go, sizeof(go)); - } - igt_spin_free(i915, spin); - - runtime[0] = read_runtime(find_me(clients, getpid()), e->class); - intel_ctx_destroy(i915, ctx); - write(sv, runtime, sizeof(runtime)); -} - -static void -__split(int i915, int clients, const intel_ctx_cfg_t *cfg, - const struct intel_execution_engine2 *e, int f, - void (*fn)(int i915, int clients, const intel_ctx_cfg_t *cfg, - const struct intel_execution_engine2 *e, - int sv)) -{ - struct client { - int64_t active[2]; - int sv[2]; - int f; - } client[2]; - uint64_t total[2] = { 1, 1 }; /* 1ns offset to prevent div-by-zero */ - int go, stop; - int i; - - for (i = 0; i < ARRAY_SIZE(client); i++) { - struct client *c = memset(&client[i], 0, sizeof(*c)); - - c->f = f; - f = 100 - f; - - igt_assert(socketpair(AF_UNIX, SOCK_DGRAM, 0, c->sv) == 0); - igt_fork(child, 1) - fn(i915, clients, cfg, e, c->sv[1]); - - read(c->sv[0], &go, sizeof(go)); - } - - /* Alternate between the clients, telling each to be active in turn */ - i = 0; - go = 1; - stop = 0; - write(client[i].sv[0], &go, sizeof(go)); - igt_until_timeout(2) { - measured_usleep(100 * client[i].f); - write(client[!i].sv[0], &go, sizeof(go)); - write(client[i].sv[0], &stop, sizeof(stop)); - - i = !i; - } - write(client[i].sv[0], &stop, sizeof(stop)); - - /* Gather up the client runtimes */ - go = -1; - for (i = 0; i < ARRAY_SIZE(client); i++) { - struct client *c = &client[i]; - - write(c->sv[0], &go, sizeof(go)); - igt_assert_eq(read(c->sv[0], c->active, sizeof(c->active)), - sizeof(c->active)); - - total[0] += c->active[0]; - total[1] += c->active[1]; - } - igt_waitchildren(); - - /* Print the results, before making any checks */ - for (i = 0; i < ARRAY_SIZE(client); i++) { - const struct client *c = &client[i]; - - igt_info("active[%02d]: %'"PRIu64"ns (%'"PRIu64"ns), %.1f%%\n", - c->f, c->active[0], c->active[1], - c->active[0] * 100. / total[0]); - } - - /* Check that each client received their target runtime */ - for (i = 0; i < ARRAY_SIZE(client); i++) { - const struct client *c = &client[i]; - double t = (100 - c->f) / 5.; /* 20% tolerance for smallest */ - - igt_debug("active[%02d]: target runtime %'"PRIu64"ns, %.1f%%\n", - c->f, c->active[1], - c->active[1] * 100. / total[1]); - - require_within_epsilon(c->active[1], c->f * total[1] / 100., t); - } - - /* Validate each client reported their share of the total runtime */ - for (i = 0; i < ARRAY_SIZE(client); i++) { - const struct client *c = &client[i]; - double t = 100 - c->f; - - igt_debug("active[%02d]: runtime %'"PRIu64"ns, %.1f%%\n", - c->f, c->active[0], - c->active[0] * 100. / total[0]); - - assert_within_epsilon(c->active[0], c->f * total[0] / 100., t); - } -} - -static void -split(int i915, int clients, const intel_ctx_cfg_t *cfg, - const struct intel_execution_engine2 *e, int f) -{ - __split(i915, clients, cfg, e, f, split_child); -} - -static void -sema_child(int i915, int clients, const intel_ctx_cfg_t *cfg, - const struct intel_execution_engine2 *e, - int sv) -{ - int64_t runtime[2] = {}; - struct drm_i915_gem_exec_object2 obj = { - .flags = EXEC_OBJECT_SUPPORTS_48B_ADDRESS - }; - struct drm_i915_gem_execbuffer2 execbuf = { - .buffers_ptr = to_user_pointer(&obj), - .buffer_count = 1, - .flags = e->flags, - }; - const intel_ctx_t *ctx; - uint32_t *cs, *sema; - - i915 = gem_reopen_driver(i915); - ctx = intel_ctx_create(i915, cfg); - execbuf.rsvd1 = ctx->id; - - obj.handle = gem_create(i915, 4096); - obj.offset = obj.handle << 12; - sema = cs = gem_mmap__device_coherent(i915, obj.handle, - 0, 4096, PROT_WRITE); - - *cs = MI_BATCH_BUFFER_END; - gem_execbuf(i915, &execbuf); - gem_sync(i915, obj.handle); - obj.flags |= EXEC_OBJECT_PINNED; - - cs += 16; - - *cs++ = MI_ARB_CHECK; - *cs++ = MI_SEMAPHORE_WAIT | - MI_SEMAPHORE_POLL | - MI_SEMAPHORE_SAD_NEQ_SDD | - (4 - 2); - *cs++ = 0; - *cs++ = obj.offset; - *cs++ = obj.offset >> 32; - - *cs++ = MI_BATCH_BUFFER_START | 1 << 8 | 1; - *cs++ = obj.offset + 64; - *cs++ = obj.offset >> 32; - - *sema = 0; - gem_execbuf(i915, &execbuf); - gem_close(i915, obj.handle); - intel_ctx_destroy(i915, ctx); - - write(sv, sema, sizeof(*sema)); - read(sv, sema, sizeof(*sema)); - while (*sema != -1) { - struct timespec tv = {}; - - __sync_synchronize(); - igt_nsec_elapsed(&tv); - read(sv, sema, sizeof(*sema)); - - __sync_synchronize(); - runtime[1] += igt_nsec_elapsed(&tv); - read(sv, sema, sizeof(*sema)); - } - - runtime[0] = read_runtime(find_me(clients, getpid()), e->class); - write(sv, runtime, sizeof(runtime)); - - sema[16] = MI_BATCH_BUFFER_END; - __sync_synchronize(); -} - -static void -sema(int i915, int clients, const intel_ctx_cfg_t *cfg, - const struct intel_execution_engine2 *e, int f) -{ - __split(i915, clients, cfg, e, f, sema_child); -} - -static int read_all(int clients, pid_t pid, int class, uint64_t *runtime) -{ - struct dirent *de; - char buf[BUFSZ]; - int count = 0; - DIR *dir; - - dir = fdopendir(dup(clients)); - igt_assert(dir); - rewinddir(dir); - - while ((de = readdir(dir))) { - int me; - - if (!isdigit(de->d_name[0])) - continue; - - snprintf(buf, sizeof(buf), "%s/pid", de->d_name); - igt_sysfs_read(clients, buf, buf, sizeof(buf)); - if (atoi(buf) != pid) - continue; - - me = openat(clients, de->d_name, O_DIRECTORY | O_RDONLY); - runtime[count++] = read_runtime(me, class); - close(me); - } - - closedir(dir); - return count; -} - -static int cmp_u64(const void *A, const void *B) -{ - const uint64_t *a = A, *b = B; - - if (*a < *b) - return -1; - else if (*a > *b) - return 1; - else - return 0; -} - -static void __fair(int i915, int clients, - int class, const char *name, - int extra, int duration) -{ - const double timeslice_duration_ns = 5e6; - unsigned int count; - uint64_t *runtime; - double threshold; - double expect; - int i; - - i = 0; - do { - int client = gem_reopen_driver(i915); - uint32_t ctx; - - ctx = gem_context_create_for_class(client, class, &count); - __igt_spin_new(client, ctx); - } while (++i < count + extra); - extra = i; - - sleep(duration); /* over the course of many timeslices */ - - runtime = calloc(extra, sizeof(*runtime)); - igt_assert_eq(read_all(clients, getpid(), class, runtime), extra); - - /* - * If we imagine that the timeslices are randomly distributed to - * the clients, we would expect the variance to be modelled - * by a drunken walk; ergo sqrt(num_timeslices). - */ - threshold = sqrt(1e9 * duration / timeslice_duration_ns); - threshold *= timeslice_duration_ns; - threshold *= extra > count; /* timeslicing active? */ - threshold *= 3; /* CI safety factor before crying wolf */ - threshold += 5e6; /* tolerance for 5ms measuring error */ - - expect = 1e9 * count * duration / extra; - - qsort(runtime, extra, sizeof(*runtime), cmp_u64); - igt_info("%s: [%.1f, %.1f, %.1f] ms, expect %1.f +- %.1fms\n", - name, - 1e-6 * runtime[0], - 1e-6 * runtime[extra / 2], - 1e-6 * runtime[extra - 1], - 1e-6 * expect, - 1e-6 * threshold); - - assert_within_epsilon(runtime[extra / 2], expect, 20); - igt_assert_f(runtime[extra - 1] - runtime[0] <= 2 * threshold, - "Range of timeslices greater than tolerable: %.2fms > %.2fms; unfair!\n", - 1e-6 * (runtime[extra - 1] - runtime[0]), - 1e-6 * threshold * 2); -} - -static void fair(int i915, int clients, int extra, int duration) -{ - static const char *names[] = { - [I915_ENGINE_CLASS_RENDER] = "rcs", - [I915_ENGINE_CLASS_COPY] = "bcs", - [I915_ENGINE_CLASS_VIDEO] = "vcs", - [I915_ENGINE_CLASS_VIDEO_ENHANCE] = "vecs", - }; - - gem_quiescent_gpu(i915); - - for (int class = 0; class < ARRAY_SIZE(names); class++) { - unsigned int count; - uint32_t ctx; - - ctx = gem_context_create_for_class(i915, class, &count); - if (!ctx) - continue; - gem_context_destroy(i915, ctx); - - igt_dynamic_f("%s", names[class]) { - igt_drop_caches_set(i915, DROP_RESET_ACTIVE); - igt_fork(child, 1) - __fair(i915, clients, class, names[class], - extra, duration); - igt_waitchildren(); - gem_quiescent_gpu(i915); - } - igt_drop_caches_set(i915, DROP_RESET_ACTIVE); - } -} - -static bool has_busy(int clients) -{ - bool ok; - int me; - - me = find_me(clients, getpid()); - ok = faccessat(me, "busy", 0, F_OK) == 0; - close(me); - - return ok; -} - -static void test_busy(int i915, int clients) -{ - const struct intel_execution_engine2 *e; - intel_ctx_cfg_t cfg; - const int frac[] = { 10, 25, 50 }; - - igt_fixture { - igt_require(gem_has_contexts(i915)); - igt_require(has_busy(clients)); - cfg = intel_ctx_cfg_all_physical(i915); - } - - igt_subtest_with_dynamic("busy") { - for_each_ctx_cfg_engine(i915, &cfg, e) { - if (!gem_class_can_store_dword(i915, e->class)) - continue; - igt_dynamic_f("%s", e->name) { - gem_quiescent_gpu(i915); - igt_fork(child, 1) - busy_one(i915, clients, &cfg, e); - igt_waitchildren(); - gem_quiescent_gpu(i915); - } - } - - igt_dynamic("all-engines") { - gem_quiescent_gpu(i915); - igt_fork(child, 1) - busy_all(i915, clients, &cfg); - igt_waitchildren(); - gem_quiescent_gpu(i915); - } - } - - for (int i = 0; i < ARRAY_SIZE(frac); i++) { - igt_subtest_with_dynamic_f("split-%d", frac[i]) { - for_each_ctx_cfg_engine(i915, &cfg, e) { - igt_dynamic_f("%s", e->name) { - gem_quiescent_gpu(i915); - split(i915, clients, &cfg, e, frac[i]); - gem_quiescent_gpu(i915); - } - } - } - } - - igt_subtest_group { - igt_fixture { - //igt_require(gem_scheduler_has_timeslicing(i915)); - igt_require(gem_scheduler_has_preemption(i915)); - } - - for (int i = 0; i < ARRAY_SIZE(frac); i++) { - igt_subtest_with_dynamic_f("sema-%d", frac[i]) { - for_each_ctx_cfg_engine(i915, &cfg, e) { - if (!gem_class_has_mutable_submission(i915, e->class)) - continue; - - igt_dynamic_f("%s", e->name) { - igt_drop_caches_set(i915, DROP_RESET_ACTIVE); - sema(i915, clients, &cfg, e, frac[i]); - gem_quiescent_gpu(i915); - } - igt_drop_caches_set(i915, DROP_RESET_ACTIVE); - } - } - } - - for (int i = 0; i < 4; i++) { - igt_subtest_with_dynamic_f("fair-%d", (1 << i) - 1) - fair(i915, clients, (1 << i) - 1, 5); - } - } -} - -igt_main -{ - int i915 = -1, clients = -1; - - igt_fixture { - int sys; - - /* Don't allow [too many] extra clients to be opened */ - i915 = __drm_open_driver(DRIVER_INTEL); - igt_require_gem(i915); - - sys = igt_sysfs_open(i915); - igt_require(sys != -1); - - clients = openat(sys, "clients", O_RDONLY); - igt_require(clients != -1); - - close(sys); - usleep(10); - } - - igt_subtest("pidname") - pidname(i915, clients); - - igt_subtest("create") - create(i915, clients); - - igt_subtest("recycle") - recycle(i915, clients, 1); - - igt_subtest("recycle-many") - recycle(i915, clients, 2 * sysconf(_SC_NPROCESSORS_ONLN)); - - igt_subtest_group - test_busy(i915, clients); - - igt_fixture { - close(clients); - close(i915); - } -} diff --git a/tests/meson.build b/tests/meson.build index e20a8640..25a4e8a3 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -235,7 +235,6 @@ i915_progs = [ 'kms_psr2_sf', 'kms_psr_stress_test', 'kms_pwrite_crc', - 'sysfs_clients', 'sysfs_defaults', 'sysfs_heartbeat_interval', 'sysfs_preempt_timeout',
For some months there was this series merged in drm-tip: https://patchwork.freedesktop.org/series/86222/ which allowed to track the client usage stats through sysfs. That support has been removed and the sysfs_clients test has since been skipping on all platforms as noticed in https://intel-gfx-ci.01.org/tree/drm-tip/shards-all.html?testfilter=sysfs_clients: Test requirement not met in function __igt_unique____real_main1023, file ../../../usr/src/igt-gpu-tools/tests/i915/sysfs_clients.c:1038: Test requirement: clients != -1 Last errno: 2, No such file or directory Subtest busy: SKIP (0.000s) The functionality has been replaced by drm-usage-stats (https://docs.kernel.org/gpu/drm-usage-stats.html) with the i915-specific bits documented at https://docs.kernel.org/gpu/i915.html#i915-usage-stats. Remove sysfs_clients test as it's not going to be used anymore. Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com> --- tests/i915/sysfs_clients.c | 1063 ------------------------------------ tests/meson.build | 1 - 2 files changed, 1064 deletions(-) delete mode 100644 tests/i915/sysfs_clients.c