From patchwork Mon Apr 17 10:57:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13213692 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E1FF8C77B76 for ; Mon, 17 Apr 2023 10:57:48 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5A91A10E412; Mon, 17 Apr 2023 10:57:48 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id A197910E407; Mon, 17 Apr 2023 10:57:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1681729064; x=1713265064; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Ob/dwJocYmR+qSOrl6WCjibq2pU2zMbExS6h8x/hFHo=; b=GHFsaVLG4cwiGF5mfINQ1T30athH56nzqJqCsTtoS7Y3zLcinDxWfUZl aMbTyZXemhd58VyMBSyzXvqDqCu6vZefnTfjb0RRRbjBEomGZ2DHR3Olk K31AZJIUTTEcW9c4Vh3GHdtyV1GQak42Qn+uA4+5PU3avBh0mPgSGl99H WUL3GjBFsj9UUSJlTXOB5vbUstgMW/ofx5vZ7cmvjnM/wfvwGIPgq2wNP ry4ez5lFq6uZB/H6HqWqLQFyJCnyNRxcGoAMvqoyOerCseEtB3PQCfbbu gmpxDaJA78LEFbyAvzq38rGIrENlCzpDDmDHZTc/tZzNO2qJu38XgZ+kb w==; X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="410073178" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="410073178" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:44 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="834363406" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="834363406" Received: from gtohallo-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.232.210]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:42 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Mon, 17 Apr 2023 11:57:27 +0100 Message-Id: <20230417105734.3945840-2-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> References: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 1/8] lib: Extract igt_drm_clients from intel_gpu_top X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: Tvrtko Ursulin Extract some code into a new library to prepare for further work towards making a vendor agnostic gputop tool. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 471 +++++++++++++++++++++++++++++++++++ lib/igt_drm_clients.h | 89 +++++++ lib/meson.build | 8 + tools/intel_gpu_top.c | 562 ++++++------------------------------------ tools/meson.build | 2 +- 5 files changed, 648 insertions(+), 484 deletions(-) create mode 100644 lib/igt_drm_clients.c create mode 100644 lib/igt_drm_clients.h diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c new file mode 100644 index 000000000000..0cb8fcc13424 --- /dev/null +++ b/lib/igt_drm_clients.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "igt_drm_clients.h" +#include "igt_drm_fdinfo.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#endif + +/** + * igt_drm_clients_init: + * @private_data: private data to store in the struct + * + * Allocate and initialise the clients structure to be used with further API + * calls. + */ +struct igt_drm_clients *igt_drm_clients_init(void *private_data) +{ + struct igt_drm_clients *clients; + + clients = malloc(sizeof(*clients)); + if (!clients) + return NULL; + + memset(clients, 0, sizeof(*clients)); + + clients->private_data = private_data; + + return clients; +} + +static struct igt_drm_client * +igt_drm_clients_find(struct igt_drm_clients *clients, + enum igt_drm_client_status status, + unsigned int id) +{ + unsigned int start, num; + struct igt_drm_client *c; + + start = status == IGT_DRM_CLIENT_FREE ? clients->active_clients : 0; /* Free block at the end. */ + num = clients->num_clients - start; + + for (c = &clients->client[start]; num; c++, num--) { + if (status != c->status) + continue; + + if (status == IGT_DRM_CLIENT_FREE || c->id == id) + return c; + } + + return NULL; +} + +static void +igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, + const struct drm_client_fdinfo *info) +{ + unsigned int i; + int len; + + /* Update client pid if it changed (fd sharing). */ + if (c->pid != pid) { + c->pid = pid; + len = snprintf(c->pid_str, sizeof(c->pid_str) - 1, "%u", pid); + if (len > c->clients->max_pid_len) + c->clients->max_pid_len = len; + } + + /* Update client name if it changed (fd sharing). */ + if (strcmp(c->name, name)) { + char *p; + + strncpy(c->name, name, sizeof(c->name) - 1); + strncpy(c->print_name, name, sizeof(c->print_name) - 1); + + p = c->print_name; + while (*p) { + if (!isprint(*p)) + *p = '*'; + p++; + } + + len = strlen(c->print_name); + if (len > c->clients->max_name_len) + c->clients->max_name_len = len; + } + + c->last_runtime = 0; + c->total_runtime = 0; + + for (i = 0; i < c->clients->num_classes; i++) { + assert(i < ARRAY_SIZE(info->busy)); + + if (info->busy[i] < c->last[i]) + continue; /* It will catch up soon. */ + + c->total_runtime += info->busy[i]; + c->val[i] = info->busy[i] - c->last[i]; + c->last_runtime += c->val[i]; + c->last[i] = info->busy[i]; + } + + c->samples++; + c->status = IGT_DRM_CLIENT_ALIVE; +} + +static void +igt_drm_client_add(struct igt_drm_clients *clients, + const struct drm_client_fdinfo *info, + unsigned int pid, char *name) +{ + struct igt_drm_client *c; + + assert(!igt_drm_clients_find(clients, IGT_DRM_CLIENT_ALIVE, info->id)); + + c = igt_drm_clients_find(clients, IGT_DRM_CLIENT_FREE, 0); + if (!c) { + unsigned int idx = clients->num_clients; + + /* + * Grow the array a bit past the current requirement to avoid + * constant reallocation when clients are dynamically appearing + * and disappearing. + */ + clients->num_clients += (clients->num_clients + 2) / 2; + clients->client = realloc(clients->client, + clients->num_clients * sizeof(*c)); + assert(clients->client); + + c = &clients->client[idx]; + memset(c, 0, (clients->num_clients - idx) * sizeof(*c)); + } + + c->id = info->id; + c->clients = clients; + c->val = calloc(clients->num_classes, sizeof(c->val)); + c->last = calloc(clients->num_classes, sizeof(c->last)); + assert(c->val && c->last); + + igt_drm_client_update(c, pid, name, info); +} + +static +void igt_drm_client_free(struct igt_drm_client *c, bool clear) +{ + free(c->val); + free(c->last); + + if (clear) + memset(c, 0, sizeof(*c)); +} + +/** + * igt_drm_clients_sort: + * @clients: Previously initialised clients object + * @cmp: Client comparison callback + * + * Sort the clients array according to the passed in comparison callback which + * is compatible with the qsort(3) semantics. + * + * Caller has to ensure the callback is putting all active + * (IGT_DRM_CLIENT_ALIVE) clients in a single group at the head of the array + * before any other sorting criteria. + */ +struct igt_drm_clients * +igt_drm_clients_sort(struct igt_drm_clients *clients, + int (*cmp)(const void *, const void *)) +{ + unsigned int active, free; + struct igt_drm_client *c; + int tmp; + + if (!clients) + return clients; + + qsort(clients->client, clients->num_clients, sizeof(*clients->client), + cmp); + + /* Trim excessive array space. */ + active = 0; + igt_for_each_drm_client(clients, c, tmp) { + if (c->status != IGT_DRM_CLIENT_ALIVE) + break; /* Active clients are first in the array. */ + active++; + } + + clients->active_clients = active; + + /* Trim excess free space when clients are exiting. */ + free = clients->num_clients - active; + if (free > clients->num_clients / 2) { + active = clients->num_clients - free / 2; + if (active != clients->num_clients) { + clients->num_clients = active; + clients->client = realloc(clients->client, + clients->num_clients * + sizeof(*c)); + } + } + + return clients; +} + +/** + * igt_drm_clients_free: + * @clients: Previously initialised clients object + * + * Free all clients and all memory associated with the clients structure. + */ +void igt_drm_clients_free(struct igt_drm_clients *clients) +{ + struct igt_drm_client *c; + unsigned int tmp; + + igt_for_each_drm_client(clients, c, tmp) + igt_drm_client_free(c, false); + + free(clients->client); + free(clients); +} + +static DIR *opendirat(int at, const char *name) +{ + DIR *dir; + int fd; + + fd = openat(at, name, O_DIRECTORY); + if (fd < 0) + return NULL; + + dir = fdopendir(fd); + if (!dir) + close(fd); + + return dir; +} + +static size_t readat2buf(int at, const char *name, char *buf, const size_t sz) +{ + ssize_t count; + int fd; + + fd = openat(at, name, O_RDONLY); + if (fd <= 0) + return 0; + + count = read(fd, buf, sz - 1); + close(fd); + + if (count > 0) { + buf[count] = 0; + + return count; + } else { + buf[0] = 0; + + return 0; + } +} + +static bool get_task_name(const char *buffer, char *out, unsigned long sz) +{ + char *s = index(buffer, '('); + char *e = rindex(buffer, ')'); + unsigned int len; + + if (!s || !e) + return false; + assert(e >= s); + + len = e - ++s; + if(!len || (len + 1) >= sz) + return false; + + strncpy(out, s, len); + out[len] = 0; + + return true; +} + +static bool is_drm_fd(int fd_dir, const char *name) +{ + struct stat stat; + int ret; + + ret = fstatat(fd_dir, name, &stat, 0); + + return ret == 0 && + (stat.st_mode & S_IFMT) == S_IFCHR && + major(stat.st_rdev) == 226; +} + +static void clients_update_max_lengths(struct igt_drm_clients *clients) +{ + struct igt_drm_client *c; + int tmp; + + clients->max_name_len = 0; + clients->max_pid_len = 0; + + igt_for_each_drm_client(clients, c, tmp) { + int len; + + if (c->status != IGT_DRM_CLIENT_ALIVE) + continue; /* Array not yet sorted by the caller. */ + + len = strlen(c->print_name); + if (len > clients->max_name_len) + clients->max_name_len = len; + + len = strlen(c->pid_str); + if (len > clients->max_pid_len) + clients->max_pid_len = len; + } +} + +/** + * igt_drm_clients_scan: + * @clients: Previously initialised clients object + * @filter_client: Callback for client filtering + * + * Scan all open file descriptors from all processes in order to find all DRM + * clients and manage our internal list. + */ +struct igt_drm_clients * +igt_drm_clients_scan(struct igt_drm_clients *clients, + bool (*filter_client)(const struct igt_drm_clients *, + const struct drm_client_fdinfo *)) +{ + struct dirent *proc_dent; + struct igt_drm_client *c; + bool freed = false; + DIR *proc_dir; + int tmp; + + if (!clients) + return clients; + + /* + * First mark all alive clients as 'probe' so we can figure out which + * ones have existed since the previous scan. + */ + igt_for_each_drm_client(clients, c, tmp) { + assert(c->status != IGT_DRM_CLIENT_PROBE); + if (c->status == IGT_DRM_CLIENT_ALIVE) + c->status = IGT_DRM_CLIENT_PROBE; + else + break; /* Free block at the end of array. */ + } + + proc_dir = opendir("/proc"); + if (!proc_dir) + return clients; + + while ((proc_dent = readdir(proc_dir)) != NULL) { + int pid_dir = -1, fd_dir = -1; + struct dirent *fdinfo_dent; + char client_name[64] = { }; + unsigned int client_pid; + DIR *fdinfo_dir = NULL; + char buf[4096]; + size_t count; + + if (proc_dent->d_type != DT_DIR) + continue; + if (!isdigit(proc_dent->d_name[0])) + continue; + + pid_dir = openat(dirfd(proc_dir), proc_dent->d_name, + O_DIRECTORY | O_RDONLY); + if (pid_dir < 0) + continue; + + count = readat2buf(pid_dir, "stat", buf, sizeof(buf)); + if (!count) + goto next; + + client_pid = atoi(buf); + if (!client_pid) + goto next; + + if (!get_task_name(buf, client_name, sizeof(client_name))) + goto next; + + fd_dir = openat(pid_dir, "fd", O_DIRECTORY | O_RDONLY); + if (fd_dir < 0) + goto next; + + fdinfo_dir = opendirat(pid_dir, "fdinfo"); + if (!fdinfo_dir) + goto next; + + while ((fdinfo_dent = readdir(fdinfo_dir)) != NULL) { + struct drm_client_fdinfo info = { }; + + if (fdinfo_dent->d_type != DT_REG) + continue; + if (!isdigit(fdinfo_dent->d_name[0])) + continue; + + if (!is_drm_fd(fd_dir, fdinfo_dent->d_name)) + continue; + + if (!__igt_parse_drm_fdinfo(dirfd(fdinfo_dir), + fdinfo_dent->d_name, + &info)) + continue; + + if (filter_client && !filter_client(clients, &info)) + continue; + + if (igt_drm_clients_find(clients, IGT_DRM_CLIENT_ALIVE, + info.id)) + continue; /* Skip duplicate fds. */ + + c = igt_drm_clients_find(clients, IGT_DRM_CLIENT_PROBE, + info.id); + if (!c) + igt_drm_client_add(clients, &info, client_pid, + client_name); + else + igt_drm_client_update(c, client_pid, + client_name, &info); + } + +next: + if (fdinfo_dir) + closedir(fdinfo_dir); + if (fd_dir >= 0) + close(fd_dir); + if (pid_dir >= 0) + close(pid_dir); + } + + closedir(proc_dir); + + /* + * Clients still in 'probe' status after the scan have exited and need + * to be freed. + */ + igt_for_each_drm_client(clients, c, tmp) { + if (c->status == IGT_DRM_CLIENT_PROBE) { + igt_drm_client_free(c, true); + freed = true; + } else if (c->status == IGT_DRM_CLIENT_FREE) { + break; + } + } + + if (freed) + clients_update_max_lengths(clients); + + return clients; +} diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h new file mode 100644 index 000000000000..1b03351aea64 --- /dev/null +++ b/lib/igt_drm_clients.h @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2022 Intel Corporation + */ + +#ifndef IGT_DRM_CLIENTS_H +#define IGT_DRM_CLIENTS_H + +#include + +/** + * SECTION:igt_drm_clients + * @short_description: Parsing driver exposed fdinfo to track DRM clients + * @title: DRM clients parsing library + * @include: igt_drm_clients.h + * + * Some DRM drivers expose GPU usage statistics in DRM file descriptor fdinfo + * data as exposed in /proc. (As documented in kernel's + * Documentation/gpu/drm-usage-stats.rst.) + * + * This library enumerates all DRM clients by parsing that data and tracks them + * in a list of clients (struct igt_drm_clients) available for inspection + * after one or more calls to igt_drm_clients_scan. + */ + +struct drm_client_fdinfo; + +enum igt_drm_client_status { + IGT_DRM_CLIENT_FREE = 0, /* mbz */ + IGT_DRM_CLIENT_ALIVE, + IGT_DRM_CLIENT_PROBE +}; + +struct igt_drm_client_engine_class { + unsigned int engine_class; + const char *name; + unsigned int num_engines; +}; + +struct igt_drm_clients; + +struct igt_drm_client { + struct igt_drm_clients *clients; /* Owning list. */ + + enum igt_drm_client_status status; + unsigned int id; /* DRM client id from fdinfo. */ + unsigned int pid; /* PID which has this DRM fd open. */ + char pid_str[10]; /* Cached PID representation. */ + char name[24]; /* Process name of the owning PID. */ + char print_name[24]; /* Name without any non-printable characters. */ + unsigned int samples; /* Count of times scanning updated this client. */ + unsigned long total_runtime; /* Aggregate busyness on all engines since client start. */ + unsigned long last_runtime; /* Aggregate busyness on all engines since previous scan. */ + unsigned long *val; /* Array of engine busyness data, relative to previous scan. */ + uint64_t *last; /* Array of engine busyness data as parsed from fdinfo. */ +}; + +struct igt_drm_clients { + unsigned int num_clients; + unsigned int active_clients; + + unsigned int num_classes; + struct igt_drm_client_engine_class *engine_class; + + int max_pid_len; + int max_name_len; + + void *private_data; + + struct igt_drm_client *client; /* Must be last. */ +}; + +#define igt_for_each_drm_client(clients, c, tmp) \ + for ((tmp) = (clients)->num_clients, c = (clients)->client; \ + (tmp > 0); (tmp)--, (c)++) + +struct igt_drm_clients *igt_drm_clients_init(void *private_data); +void igt_drm_clients_free(struct igt_drm_clients *clients); + +struct igt_drm_clients * +igt_drm_clients_scan(struct igt_drm_clients *clients, + bool (*filter_client)(const struct igt_drm_clients *, + const struct drm_client_fdinfo *)); + +struct igt_drm_clients * +igt_drm_clients_sort(struct igt_drm_clients *clients, + int (*cmp)(const void *, const void *)); + +#endif /* IGT_DRM_CLIENTS_H */ diff --git a/lib/meson.build b/lib/meson.build index ad68089dcf43..de49501e2bb1 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -23,6 +23,7 @@ lib_sources = [ 'igt_debugfs.c', 'igt_device.c', 'igt_device_scan.c', + 'igt_drm_clients.h', 'igt_drm_fdinfo.c', 'igt_aux.c', 'igt_gt.c', @@ -255,6 +256,13 @@ lib_igt_device_scan_build = static_library('igt_device_scan', lib_igt_device_scan = declare_dependency(link_with : lib_igt_device_scan_build, include_directories : inc) +lib_igt_drm_clients_build = static_library('igt_drm_clients', + ['igt_drm_clients.c'], + include_directories : inc) + +lib_igt_drm_clients = declare_dependency(link_with : lib_igt_drm_clients_build, + include_directories : inc) + lib_igt_drm_fdinfo_build = static_library('igt_drm_fdinfo', ['igt_drm_fdinfo.c'], include_directories : inc) diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index b6827b3de9bd..45550fa557d4 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -47,6 +47,7 @@ #include #include "igt_perf.h" +#include "igt_drm_clients.h" #include "igt_drm_fdinfo.h" #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) @@ -81,16 +82,10 @@ struct engine { struct pmu_counter sema; }; -struct engine_class { - unsigned int class; - const char *name; - unsigned int num_engines; -}; - struct engines { unsigned int num_engines; unsigned int num_classes; - struct engine_class *class; + struct igt_drm_client_engine_class *class; unsigned int num_counters; DIR *root; int fd; @@ -679,176 +674,10 @@ static void pmu_sample(struct engines *engines) } } -enum client_status { - FREE = 0, /* mbz */ - ALIVE, - PROBE -}; - -struct clients; - -struct client { - struct clients *clients; - - enum client_status status; - unsigned int id; - unsigned int pid; - char pid_str[10]; - char name[24]; - char print_name[24]; - unsigned int samples; - unsigned long total_runtime; - unsigned long last_runtime; - unsigned long *val; - uint64_t *last; -}; - -struct clients { - unsigned int num_clients; - unsigned int active_clients; - - unsigned int num_classes; - struct engine_class *class; - - int max_pid_len; - int max_name_len; - - char pci_slot[64]; - - struct client *client; -}; - -#define for_each_client(clients, c, tmp) \ - for ((tmp) = (clients)->num_clients, c = (clients)->client; \ - (tmp > 0); (tmp)--, (c)++) - -static struct clients *init_clients(const char *pci_slot) -{ - struct clients *clients; - - clients = malloc(sizeof(*clients)); - if (!clients) - return NULL; - - memset(clients, 0, sizeof(*clients)); - - strncpy(clients->pci_slot, pci_slot, sizeof(clients->pci_slot)); - - return clients; -} - -static struct client * -find_client(struct clients *clients, enum client_status status, unsigned int id) -{ - unsigned int start, num; - struct client *c; - - start = status == FREE ? clients->active_clients : 0; /* Free block at the end. */ - num = clients->num_clients - start; - - for (c = &clients->client[start]; num; c++, num--) { - if (status != c->status) - continue; - - if (status == FREE || c->id == id) - return c; - } - - return NULL; -} - -static void -update_client(struct client *c, unsigned int pid, char *name, - const struct drm_client_fdinfo *info) -{ - unsigned int i; - int len; - - if (c->pid != pid) { - c->pid = pid; - len = snprintf(c->pid_str, sizeof(c->pid_str) - 1, "%u", pid); - if (len > c->clients->max_pid_len) - c->clients->max_pid_len = len; - } - - if (strcmp(c->name, name)) { - char *p; - - strncpy(c->name, name, sizeof(c->name) - 1); - strncpy(c->print_name, name, sizeof(c->print_name) - 1); - - p = c->print_name; - while (*p) { - if (!isprint(*p)) - *p = '*'; - p++; - } - - len = strlen(c->print_name); - if (len > c->clients->max_name_len) - c->clients->max_name_len = len; - } - - c->last_runtime = 0; - c->total_runtime = 0; - - for (i = 0; i < c->clients->num_classes; i++) { - assert(i < ARRAY_SIZE(info->busy)); - - if (info->busy[i] < c->last[i]) - continue; /* It will catch up soon. */ - - c->total_runtime += info->busy[i]; - c->val[i] = info->busy[i] - c->last[i]; - c->last_runtime += c->val[i]; - c->last[i] = info->busy[i]; - } - - c->samples++; - c->status = ALIVE; -} - -static void -add_client(struct clients *clients, const struct drm_client_fdinfo *info, - unsigned int pid, char *name) -{ - struct client *c; - - assert(!find_client(clients, ALIVE, info->id)); - - c = find_client(clients, FREE, 0); - if (!c) { - unsigned int idx = clients->num_clients; - - clients->num_clients += (clients->num_clients + 2) / 2; - clients->client = realloc(clients->client, - clients->num_clients * sizeof(*c)); - assert(clients->client); - - c = &clients->client[idx]; - memset(c, 0, (clients->num_clients - idx) * sizeof(*c)); - } - - c->id = info->id; - c->clients = clients; - c->val = calloc(clients->num_classes, sizeof(c->val)); - c->last = calloc(clients->num_classes, sizeof(c->last)); - assert(c->val && c->last); - - update_client(c, pid, name, info); -} - -static void free_client(struct client *c) -{ - free(c->val); - free(c->last); - memset(c, 0, sizeof(*c)); -} - static int client_last_cmp(const void *_a, const void *_b) { - const struct client *a = _a; - const struct client *b = _b; + const struct igt_drm_client *a = _a; + const struct igt_drm_client *b = _b; long tot_a, tot_b; /* @@ -857,8 +686,8 @@ static int client_last_cmp(const void *_a, const void *_b) * id. */ - tot_a = a->status == ALIVE ? a->last_runtime : -1; - tot_b = b->status == ALIVE ? b->last_runtime : -1; + tot_a = a->status == IGT_DRM_CLIENT_ALIVE ? a->last_runtime : -1; + tot_b = b->status == IGT_DRM_CLIENT_ALIVE ? b->last_runtime : -1; tot_b -= tot_a; if (tot_b > 0) @@ -871,12 +700,12 @@ static int client_last_cmp(const void *_a, const void *_b) static int client_total_cmp(const void *_a, const void *_b) { - const struct client *a = _a; - const struct client *b = _b; + const struct igt_drm_client *a = _a; + const struct igt_drm_client *b = _b; long tot_a, tot_b; - tot_a = a->status == ALIVE ? a->total_runtime : -1; - tot_b = b->status == ALIVE ? b->total_runtime : -1; + tot_a = a->status == IGT_DRM_CLIENT_ALIVE ? a->total_runtime : -1; + tot_b = b->status == IGT_DRM_CLIENT_ALIVE ? b->total_runtime : -1; tot_b -= tot_a; if (tot_b > 0) @@ -889,12 +718,12 @@ static int client_total_cmp(const void *_a, const void *_b) static int client_id_cmp(const void *_a, const void *_b) { - const struct client *a = _a; - const struct client *b = _b; + const struct igt_drm_client *a = _a; + const struct igt_drm_client *b = _b; int id_a, id_b; - id_a = a->status == ALIVE ? a->id : -1; - id_b = b->status == ALIVE ? b->id : -1; + id_a = a->status == IGT_DRM_CLIENT_ALIVE ? a->id : -1; + id_b = b->status == IGT_DRM_CLIENT_ALIVE ? b->id : -1; id_b -= id_a; if (id_b > 0) @@ -907,12 +736,12 @@ static int client_id_cmp(const void *_a, const void *_b) static int client_pid_cmp(const void *_a, const void *_b) { - const struct client *a = _a; - const struct client *b = _b; + const struct igt_drm_client *a = _a; + const struct igt_drm_client *b = _b; int pid_a, pid_b; - pid_a = a->status == ALIVE ? a->pid : INT_MAX; - pid_b = b->status == ALIVE ? b->pid : INT_MAX; + pid_a = a->status == IGT_DRM_CLIENT_ALIVE ? a->pid : INT_MAX; + pid_b = b->status == IGT_DRM_CLIENT_ALIVE ? b->pid : INT_MAX; pid_b -= pid_a; if (pid_b > 0) @@ -925,56 +754,19 @@ static int client_pid_cmp(const void *_a, const void *_b) static int (*client_cmp)(const void *, const void *) = client_last_cmp; -static struct clients *sort_clients(struct clients *clients, - int (*cmp)(const void *, const void *)) -{ - unsigned int active, free; - struct client *c; - int tmp; - - if (!clients) - return clients; - - qsort(clients->client, clients->num_clients, sizeof(*clients->client), - cmp); - - /* Trim excessive array space. */ - active = 0; - for_each_client(clients, c, tmp) { - if (c->status != ALIVE) - break; /* Active clients are first in the array. */ - active++; - } - - clients->active_clients = active; - - free = clients->num_clients - active; - if (free > clients->num_clients / 2) { - active = clients->num_clients - free / 2; - if (active != clients->num_clients) { - clients->num_clients = active; - clients->client = realloc(clients->client, - clients->num_clients * - sizeof(*c)); - } - } - - return clients; -} - static bool aggregate_pids = true; -static struct clients *display_clients(struct clients *clients) +static struct igt_drm_clients *display_clients(struct igt_drm_clients *clients) { - struct client *ac, *c, *cp = NULL; - struct clients *aggregated; + struct igt_drm_client *ac, *c, *cp = NULL; + struct igt_drm_clients *aggregated; int tmp, num = 0; if (!aggregate_pids) goto out; /* Sort by pid first to make it easy to aggregate while walking. */ - sort_clients(clients, client_pid_cmp); + igt_drm_clients_sort(clients, client_pid_cmp); aggregated = calloc(1, sizeof(*clients)); assert(aggregated); @@ -983,23 +775,24 @@ static struct clients *display_clients(struct clients *clients) assert(ac); aggregated->num_classes = clients->num_classes; - aggregated->class = clients->class; + aggregated->engine_class = clients->engine_class; + aggregated->private_data = clients->private_data; aggregated->client = ac; - for_each_client(clients, c, tmp) { + igt_for_each_drm_client(clients, c, tmp) { unsigned int i; - if (c->status == FREE) + if (c->status == IGT_DRM_CLIENT_FREE) break; - assert(c->status == ALIVE); + assert(c->status == IGT_DRM_CLIENT_ALIVE); if (!cp || c->pid != cp->pid) { ac = &aggregated->client[num++]; /* New pid. */ ac->clients = aggregated; - ac->status = ALIVE; + ac->status = IGT_DRM_CLIENT_ALIVE; ac->id = -c->pid; ac->pid = c->pid; strcpy(ac->name, c->name); @@ -1033,237 +826,26 @@ static struct clients *display_clients(struct clients *clients) clients = aggregated; out: - return sort_clients(clients, client_cmp); + return igt_drm_clients_sort(clients, client_cmp); } -static void free_clients(struct clients *clients) +static void free_display_clients(struct igt_drm_clients *clients) { - struct client *c; + struct igt_drm_client *c; unsigned int tmp; - for_each_client(clients, c, tmp) { + /* + * Don't call igt_drm_clients_free or igt_drm_client_free since + * "display" clients are not proper clients and have un-initialized + * fields which we don't want the library to try and free. + */ + igt_for_each_drm_client(clients, c, tmp) free(c->val); - free(c->last); - } free(clients->client); free(clients); } -static bool is_drm_fd(int fd_dir, const char *name) -{ - struct stat stat; - int ret; - - ret = fstatat(fd_dir, name, &stat, 0); - - return ret == 0 && - (stat.st_mode & S_IFMT) == S_IFCHR && - major(stat.st_rdev) == 226; -} - -static bool get_task_name(const char *buffer, char *out, unsigned long sz) -{ - char *s = index(buffer, '('); - char *e = rindex(buffer, ')'); - unsigned int len; - - if (!s || !e) - return false; - assert(e >= s); - - len = e - ++s; - if(!len || (len + 1) >= sz) - return false; - - strncpy(out, s, len); - out[len] = 0; - - return true; -} - -static DIR *opendirat(int at, const char *name) -{ - DIR *dir; - int fd; - - fd = openat(at, name, O_DIRECTORY); - if (fd < 0) - return NULL; - - dir = fdopendir(fd); - if (!dir) - close(fd); - - return dir; -} - -static size_t readat2buf(int at, const char *name, char *buf, const size_t sz) -{ - ssize_t count; - int fd; - - fd = openat(at, name, O_RDONLY); - if (fd <= 0) - return 0; - - count = read(fd, buf, sz - 1); - close(fd); - - if (count > 0) { - buf[count] = 0; - - return count; - } else { - buf[0] = 0; - - return 0; - } -} - -static void clients_update_max_lengths(struct clients *clients) -{ - struct client *c; - int tmp; - - clients->max_name_len = 0; - clients->max_pid_len = 0; - - for_each_client(clients, c, tmp) { - int len; - - if (c->status != ALIVE) - continue; /* Array not yet sorted by the caller. */ - - len = strlen(c->print_name); - if (len > clients->max_name_len) - clients->max_name_len = len; - - len = strlen(c->pid_str); - if (len > clients->max_pid_len) - clients->max_pid_len = len; - } -} - -static struct clients *scan_clients(struct clients *clients, bool display) -{ - struct dirent *proc_dent; - bool freed = false; - struct client *c; - DIR *proc_dir; - int tmp; - - if (!clients) - return clients; - - for_each_client(clients, c, tmp) { - assert(c->status != PROBE); - if (c->status == ALIVE) - c->status = PROBE; - else - break; /* Free block at the end of array. */ - } - - proc_dir = opendir("/proc"); - if (!proc_dir) - return clients; - - while ((proc_dent = readdir(proc_dir)) != NULL) { - int pid_dir = -1, fd_dir = -1; - struct dirent *fdinfo_dent; - char client_name[64] = { }; - unsigned int client_pid; - DIR *fdinfo_dir = NULL; - char buf[4096]; - size_t count; - - if (proc_dent->d_type != DT_DIR) - continue; - if (!isdigit(proc_dent->d_name[0])) - continue; - - pid_dir = openat(dirfd(proc_dir), proc_dent->d_name, - O_DIRECTORY | O_RDONLY); - if (pid_dir < 0) - continue; - - count = readat2buf(pid_dir, "stat", buf, sizeof(buf)); - if (!count) - goto next; - - client_pid = atoi(buf); - if (!client_pid) - goto next; - - if (!get_task_name(buf, client_name, sizeof(client_name))) - goto next; - - fd_dir = openat(pid_dir, "fd", O_DIRECTORY | O_RDONLY); - if (fd_dir < 0) - goto next; - - fdinfo_dir = opendirat(pid_dir, "fdinfo"); - if (!fdinfo_dir) - goto next; - - while ((fdinfo_dent = readdir(fdinfo_dir)) != NULL) { - struct drm_client_fdinfo info = { }; - - if (fdinfo_dent->d_type != DT_REG) - continue; - if (!isdigit(fdinfo_dent->d_name[0])) - continue; - - if (!is_drm_fd(fd_dir, fdinfo_dent->d_name)) - continue; - - if (!__igt_parse_drm_fdinfo(dirfd(fdinfo_dir), - fdinfo_dent->d_name, - &info)) - continue; - - if (strcmp(info.driver, "i915")) - continue; - if (strcmp(info.pdev, clients->pci_slot)) - continue; - if (find_client(clients, ALIVE, info.id)) - continue; /* Skip duplicate fds. */ - - c = find_client(clients, PROBE, info.id); - if (!c) - add_client(clients, &info, client_pid, - client_name); - else - update_client(c, client_pid, client_name, - &info); - } - -next: - if (fdinfo_dir) - closedir(fdinfo_dir); - if (fd_dir >= 0) - close(fd_dir); - if (pid_dir >= 0) - close(pid_dir); - } - - closedir(proc_dir); - - for_each_client(clients, c, tmp) { - if (c->status == PROBE) { - free_client(c); - freed = true; - } else if (c->status == FREE) { - break; - } - } - - if (freed) - clients_update_max_lengths(clients); - - return display ? display_clients(clients) : clients; -} - static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" }; static void n_spaces(const unsigned int n) @@ -2037,15 +1619,15 @@ print_engines_footer(struct engines *engines, double t, static int class_cmp(const void *_a, const void *_b) { - const struct engine_class *a = _a; - const struct engine_class *b = _b; + const struct igt_drm_client_engine_class *a = _a; + const struct igt_drm_client_engine_class *b = _b; - return a->class - b->class; + return a->engine_class - b->engine_class; } static void init_engine_classes(struct engines *engines) { - struct engine_class *classes; + struct igt_drm_client_engine_class *classes; unsigned int i, num; int max = -1; @@ -2072,7 +1654,7 @@ static void init_engine_classes(struct engines *engines) } for (i = 0; i < num; i++) { - classes[i].class = i; + classes[i].engine_class = i; classes[i].name = class_display_name(i); } @@ -2214,7 +1796,7 @@ print_engines(struct engines *engines, double t, int lines, int w, int h) } static int -print_clients_header(struct clients *clients, int lines, +print_clients_header(struct igt_drm_clients *clients, int lines, int con_w, int con_h, int *class_w) { if (output_mode == INTERACTIVE) { @@ -2237,19 +1819,19 @@ print_clients_header(struct clients *clients, int lines, int width; for (i = 0; i < clients->num_classes; i++) { - if (clients->class[i].num_engines) + if (clients->engine_class[i].num_engines) num_active++; } *class_w = width = (con_w - len) / num_active; for (i = 0; i < clients->num_classes; i++) { - const char *name = clients->class[i].name; + const char *name = clients->engine_class[i].name; int name_len = strlen(name); int pad = (width - name_len) / 2; int spaces = width - pad - name_len; - if (!clients->class[i].num_engines) + if (!clients->engine_class[i].num_engines) continue; /* Assert in the ideal world. */ if (pad < 0 || spaces < 0) @@ -2276,10 +1858,10 @@ static bool numeric_clients; static bool filter_idle; static int -print_client(struct client *c, struct engines *engines, double t, int lines, +print_client(struct igt_drm_client *c, struct engines *engines, double t, int lines, int con_w, int con_h, unsigned int period_us, int *class_w) { - struct clients *clients = c->clients; + struct igt_drm_clients *clients = c->clients; unsigned int i; if (output_mode == INTERACTIVE) { @@ -2295,7 +1877,7 @@ print_client(struct client *c, struct engines *engines, double t, int lines, for (i = 0; c->samples > 1 && i < clients->num_classes; i++) { double pct, max; - if (!clients->class[i].num_engines) + if (!clients->engine_class[i].num_engines) continue; /* Assert in the ideal world. */ pct = (double)c->val[i] / period_us / 1e3 * 100; @@ -2305,7 +1887,7 @@ print_client(struct client *c, struct engines *engines, double t, int lines, * client data and time we obtained our time-delta from * PMU. */ - max = 100.0 * clients->class[i].num_engines; + max = 100.0 * clients->engine_class[i].num_engines; if (pct > max) pct = max; @@ -2332,7 +1914,7 @@ print_client(struct client *c, struct engines *engines, double t, int lines, double pct; snprintf(buf, sizeof(buf), "%s", - clients->class[i].name); + clients->engine_class[i].name); pops->open_struct(buf); pct = (double)c->val[i] / period_us / 1e3 * 100; @@ -2354,7 +1936,7 @@ print_client(struct client *c, struct engines *engines, double t, int lines, } static int -print_clients_footer(struct clients *clients, double t, +print_clients_footer(struct igt_drm_clients *clients, double t, int lines, int con_w, int con_h) { if (output_mode == INTERACTIVE) { @@ -2603,10 +2185,21 @@ static unsigned long elapsed_us(struct timespec *prev, unsigned int period_us) return elapsed; } +static bool client_match(const struct igt_drm_clients *clients, + const struct drm_client_fdinfo *info) +{ + if (strcmp(info->driver, "i915")) + return false; + if (strcmp(info->pdev, clients->private_data)) + return false; + + return true; +} + int main(int argc, char **argv) { unsigned int period_us = DEFAULT_PERIOD_MS * 1000; - struct clients *clients = NULL; + struct igt_drm_clients *clients = NULL; bool physical_engines = false; int con_w = -1, con_h = -1; char *output_path = NULL; @@ -2760,16 +2353,17 @@ int main(int argc, char **argv) ret = EXIT_SUCCESS; if (has_drm_fdinfo(&card)) - clients = init_clients(card.pci_slot_name[0] ? - card.pci_slot_name : IGPU_PCI); + clients = igt_drm_clients_init(strdup(card.pci_slot_name[0] ? + card.pci_slot_name : + IGPU_PCI)); init_engine_classes(engines); if (clients) { clients->num_classes = engines->num_classes; - clients->class = engines->class; + clients->engine_class = engines->class; } pmu_sample(engines); - scan_clients(clients, false); + igt_drm_clients_scan(clients, client_match); gettime(&ts); codename = igt_device_get_pretty_name(&card, false); @@ -2777,12 +2371,12 @@ int main(int argc, char **argv) printf("[\n"); while (!stop_top) { - struct clients *disp_clients; + struct igt_drm_clients *disp_clients; + struct igt_drm_client *c; bool consumed = false; unsigned int scan_us; int j, lines = 0; struct winsize ws; - struct client *c; double t; /* Update terminal size. */ @@ -2801,7 +2395,9 @@ int main(int argc, char **argv) pmu_sample(engines); t = (double)(engines->ts.cur - engines->ts.prev) / 1e9; - disp_clients = scan_clients(clients, true); + disp_clients = + display_clients(igt_drm_clients_scan(clients, + client_match)); scan_us = elapsed_us(&ts, period_us); if (stop_top) @@ -2830,9 +2426,9 @@ int main(int argc, char **argv) con_w, con_h, &class_w); - for_each_client(disp_clients, c, j) { - assert(c->status != PROBE); - if (c->status != ALIVE) + igt_for_each_drm_client(disp_clients, c, j) { + assert(c->status != IGT_DRM_CLIENT_PROBE); + if (c->status != IGT_DRM_CLIENT_ALIVE) break; /* Active clients are first in the array. */ if (lines >= con_h) @@ -2853,7 +2449,7 @@ int main(int argc, char **argv) } if (disp_clients != clients) - free_clients(disp_clients); + free_display_clients(disp_clients); if (stop_top) break; @@ -2868,7 +2464,7 @@ int main(int argc, char **argv) printf("]\n"); if (clients) - free_clients(clients); + igt_drm_clients_free(clients); free(codename); err_pmu: diff --git a/tools/meson.build b/tools/meson.build index 88c58adfe333..54e387cf0f4b 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -88,7 +88,7 @@ install_subdir('registers', install_dir : datadir) executable('intel_gpu_top', 'intel_gpu_top.c', install : true, install_rpath : bindir_rpathdir, - dependencies : [lib_igt_perf,lib_igt_device_scan,lib_igt_drm_fdinfo,math]) + dependencies : [lib_igt_perf,lib_igt_device_scan,lib_igt_drm_clients,lib_igt_drm_fdinfo,math]) executable('amd_hdmi_compliance', 'amd_hdmi_compliance.c', dependencies : [tool_deps], From patchwork Mon Apr 17 10:57:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13213693 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E87DEC77B77 for ; Mon, 17 Apr 2023 10:57:51 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3EE8A10E414; Mon, 17 Apr 2023 10:57:49 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id BBC9510E40B; Mon, 17 Apr 2023 10:57:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1681729065; x=1713265065; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=UsGSRblJXYPyh3ONEp++AwC6YBC8aHCbyuHOeC8RU40=; b=OjPJWTL9VGD076+YwB91wp+vA1yPnWK8qcNnCmZbd6Fox8UZ5+y6drMA /U3Kqtk3wz6gCKMtOtg0rnCvb79HiWMnPxFM7rYnRRAkrfXYds6DOun2H TDOPfAy6rjc6T74Fd1zqCpUc2R8hQaxQlxy44pH+M83zIcdjlN27TQqTX eRKd8tG8cl174hkytUFdtmYa7cS8gdoV4jcpYk9ie9xRCS8OiPJc3P1Ib qIKbTZ7fsi/9LTY4l3sT1VZjc9EG/WVvCc7WbSW3fHMetYNqYLwQ7YvfH kAvISGUDhIsLKJz6ntjWevA2sYWyQ33pGYhCJvVQry8GZ69Fcws/A1bAQ w==; X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="410073185" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="410073185" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:45 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="834363412" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="834363412" Received: from gtohallo-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.232.210]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:44 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Mon, 17 Apr 2023 11:57:28 +0100 Message-Id: <20230417105734.3945840-3-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> References: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 2/8] lib: Allow specifying custom engine map X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: Tvrtko Ursulin Instead of hard coding the engine names, allow a map of names to indices to either be passed in or it gets auto-detected (less efficient) while parsing. --- lib/igt_drm_clients.c | 18 ++++++++++++--- lib/igt_drm_clients.h | 3 ++- lib/igt_drm_fdinfo.c | 51 ++++++++++++++++++++++++++--------------- lib/igt_drm_fdinfo.h | 15 +++++++++--- tests/i915/drm_fdinfo.c | 19 +++++++++++---- tools/intel_gpu_top.c | 16 ++++++++++--- 6 files changed, 90 insertions(+), 32 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index 0cb8fcc13424..d06fcb0942a1 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -335,14 +335,26 @@ static void clients_update_max_lengths(struct igt_drm_clients *clients) * igt_drm_clients_scan: * @clients: Previously initialised clients object * @filter_client: Callback for client filtering + * @name_map: Array of engine name strings + * @map_entries: Number of items in the @name_map array * * Scan all open file descriptors from all processes in order to find all DRM * clients and manage our internal list. + * + * If @name_map is provided each found engine in the fdinfo struct must + * correspond to one of the provided names. In this case the index of the engine + * stats tracked in struct igt_drm_client will be tracked under the same index + * as the engine name provided. + * + * If @name_map is not provided engine names will be auto-detected (this is + * less performant) and indices will correspond with auto-detected names as + * listed int clients->engine_class[]. */ struct igt_drm_clients * igt_drm_clients_scan(struct igt_drm_clients *clients, bool (*filter_client)(const struct igt_drm_clients *, - const struct drm_client_fdinfo *)) + const struct drm_client_fdinfo *), + const char **name_map, unsigned int map_entries) { struct dirent *proc_dent; struct igt_drm_client *c; @@ -419,8 +431,8 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, continue; if (!__igt_parse_drm_fdinfo(dirfd(fdinfo_dir), - fdinfo_dent->d_name, - &info)) + fdinfo_dent->d_name, &info, + name_map, map_entries)) continue; if (filter_client && !filter_client(clients, &info)) diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h index 1b03351aea64..431bf5bbd335 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -80,7 +80,8 @@ void igt_drm_clients_free(struct igt_drm_clients *clients); struct igt_drm_clients * igt_drm_clients_scan(struct igt_drm_clients *clients, bool (*filter_client)(const struct igt_drm_clients *, - const struct drm_client_fdinfo *)); + const struct drm_client_fdinfo *), + const char **name_map, unsigned int map_entries); struct igt_drm_clients * igt_drm_clients_sort(struct igt_drm_clients *clients, diff --git a/lib/igt_drm_fdinfo.c b/lib/igt_drm_fdinfo.c index 5beb30b01e00..3f4f0e88b0d8 100644 --- a/lib/igt_drm_fdinfo.c +++ b/lib/igt_drm_fdinfo.c @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Intel Corporation + * Copyright © 2022-2023 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -22,6 +22,7 @@ * */ +#include #include #include #include @@ -53,15 +54,10 @@ static size_t read_fdinfo(char *buf, const size_t sz, int at, const char *name) } static int parse_engine(char *line, struct drm_client_fdinfo *info, - size_t prefix_len, uint64_t *val) + size_t prefix_len, + const char **name_map, unsigned int map_entries, + uint64_t *val) { - static const char *e2class[] = { - "render", - "copy", - "video", - "video-enhance", - "compute", - }; ssize_t name_len; char *name, *p; int found = -1; @@ -77,10 +73,26 @@ static int parse_engine(char *line, struct drm_client_fdinfo *info, name = line + prefix_len; - for (i = 0; i < ARRAY_SIZE(e2class); i++) { - if (!strncmp(name, e2class[i], name_len)) { - found = i; - break; + if (name_map) { + for (i = 0; i < map_entries; i++) { + if (!strncmp(name, name_map[i], name_len)) { + found = i; + break; + } + } + } else { + for (i = 0; i < info->num_engines; i++) { + if (!strncmp(name, info->names[i], name_len)) { + found = i; + break; + } + } + + if (found < 0) { + assert((info->num_engines + 1) < ARRAY_SIZE(info->names)); + assert((strlen(name) + 1) < sizeof(info->names[0])); + strncpy(info->names[info->num_engines], name, name_len); + found = info->num_engines; } } @@ -113,7 +125,8 @@ static const char *find_kv(const char *buf, const char *key, size_t keylen) } unsigned int -__igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info) +__igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info, + const char **name_map, unsigned int map_entries) { char buf[4096], *_buf = buf; char *l, *ctx = NULL; @@ -143,7 +156,7 @@ __igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info) } else if (!strncmp(l, "drm-engine-", 11) && strncmp(l, "drm-engine-capacity-", 20)) { idx = parse_engine(l, info, strlen("drm-engine-"), - &val); + name_map, map_entries, &val); if (idx >= 0) { if (!info->capacity[idx]) info->capacity[idx] = 1; @@ -153,7 +166,7 @@ __igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info) } else if (!strncmp(l, "drm-engine-capacity-", 20)) { idx = parse_engine(l, info, strlen("drm-engine-capacity-"), - &val); + name_map, map_entries, &val); if (idx >= 0) { info->capacity[idx] = val; num_capacity++; @@ -167,7 +180,9 @@ __igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info) return good + info->num_engines + num_capacity; } -unsigned int igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info) +unsigned int +igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info, + const char **name_map, unsigned int map_entries) { unsigned int res; char fd[64]; @@ -181,7 +196,7 @@ unsigned int igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info) if (dir < 0) return false; - res = __igt_parse_drm_fdinfo(dir, fd, info); + res = __igt_parse_drm_fdinfo(dir, fd, info, name_map, map_entries); close(dir); diff --git a/lib/igt_drm_fdinfo.h b/lib/igt_drm_fdinfo.h index 8759471615bd..fa4982f4030e 100644 --- a/lib/igt_drm_fdinfo.h +++ b/lib/igt_drm_fdinfo.h @@ -39,6 +39,7 @@ struct drm_client_fdinfo { unsigned int num_engines; unsigned int capacity[DRM_CLIENT_FDINFO_MAX_ENGINES]; + char names[DRM_CLIENT_FDINFO_MAX_ENGINES][256]; uint64_t busy[DRM_CLIENT_FDINFO_MAX_ENGINES]; }; @@ -47,11 +48,15 @@ struct drm_client_fdinfo { * * @drm_fd: DRM file descriptor * @info: Structure to populate with read data. Must be zeroed. + * @name_map: Optional array of strings representing engine names + * @map_entries: Number of strings in the names array * * Returns the number of valid drm fdinfo keys found or zero if not all * mandatory keys were present or no engines found. */ -unsigned int igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info); +unsigned int +igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info, + const char **name_map, unsigned int map_entries); /** * __igt_parse_drm_fdinfo: Parses the drm fdinfo file @@ -59,11 +64,15 @@ unsigned int igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info); * @dir: File descriptor pointing to /proc/pid/fdinfo directory * @fd: String representation of the file descriptor number to parse. * @info: Structure to populate with read data. Must be zeroed. + * @name_map: Optional array of strings representing engine names + * @map_entries: Number of strings in the names array * * Returns the number of valid drm fdinfo keys found or zero if not all * mandatory keys were present or no engines found. */ -unsigned int __igt_parse_drm_fdinfo(int dir, const char *fd, - struct drm_client_fdinfo *info); +unsigned int +__igt_parse_drm_fdinfo(int dir, const char *fd, + struct drm_client_fdinfo *info, + const char **name_map, unsigned int map_entries); #endif /* IGT_DRM_FDINFO_H */ diff --git a/tests/i915/drm_fdinfo.c b/tests/i915/drm_fdinfo.c index 1b1247553a32..6314d73b5d44 100644 --- a/tests/i915/drm_fdinfo.c +++ b/tests/i915/drm_fdinfo.c @@ -35,6 +35,14 @@ IGT_TEST_DESCRIPTION("Test the i915 drm fdinfo data"); const double tolerance = 0.05f; const unsigned long batch_duration_ns = 500e6; +static const char *engine_map[] = { + "render", + "copy", + "video", + "video-enhance", + "compute", +}; + #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), \ @@ -51,7 +59,8 @@ static void basics(int i915, unsigned int num_classes) struct drm_client_fdinfo info = { }; unsigned int ret; - ret = igt_parse_drm_fdinfo(i915, &info); + ret = igt_parse_drm_fdinfo(i915, &info, engine_map, + ARRAY_SIZE(engine_map)); igt_assert(ret); igt_assert(!strcmp(info.driver, "i915")); @@ -182,7 +191,8 @@ static uint64_t read_busy(int i915, unsigned int class) { struct drm_client_fdinfo info = { }; - igt_assert(igt_parse_drm_fdinfo(i915, &info)); + igt_assert(igt_parse_drm_fdinfo(i915, &info, engine_map, + ARRAY_SIZE(engine_map))); return info.busy[class]; } @@ -271,7 +281,8 @@ static void read_busy_all(int i915, uint64_t *val) { struct drm_client_fdinfo info = { }; - igt_assert(igt_parse_drm_fdinfo(i915, &info)); + igt_assert(igt_parse_drm_fdinfo(i915, &info, engine_map, + ARRAY_SIZE(engine_map))); memcpy(val, info.busy, sizeof(info.busy)); } @@ -742,7 +753,7 @@ igt_main i915 = __drm_open_driver(DRIVER_INTEL); igt_require_gem(i915); - igt_require(igt_parse_drm_fdinfo(i915, &info)); + igt_require(igt_parse_drm_fdinfo(i915, &info, NULL, 0)); ctx = intel_ctx_create_all_physical(i915); diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index 45550fa557d4..54c1ac41cdf8 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -2136,7 +2136,7 @@ static bool has_drm_fdinfo(const struct igt_device_card *card) if (fd < 0) return false; - cnt = igt_parse_drm_fdinfo(fd, &info); + cnt = igt_parse_drm_fdinfo(fd, &info, NULL, 0); close(fd); @@ -2200,6 +2200,13 @@ int main(int argc, char **argv) { unsigned int period_us = DEFAULT_PERIOD_MS * 1000; struct igt_drm_clients *clients = NULL; + static const char *engine_map[] = { + "render", + "copy", + "video", + "video-enhance", + "compute", + }; bool physical_engines = false; int con_w = -1, con_h = -1; char *output_path = NULL; @@ -2363,7 +2370,8 @@ int main(int argc, char **argv) } pmu_sample(engines); - igt_drm_clients_scan(clients, client_match); + igt_drm_clients_scan(clients, client_match, engine_map, + ARRAY_SIZE(engine_map)); gettime(&ts); codename = igt_device_get_pretty_name(&card, false); @@ -2397,7 +2405,9 @@ int main(int argc, char **argv) disp_clients = display_clients(igt_drm_clients_scan(clients, - client_match)); + client_match, + engine_map, + ARRAY_SIZE(engine_map))); scan_us = elapsed_us(&ts, period_us); if (stop_top) From patchwork Mon Apr 17 10:57:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13213691 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 6CE3DC77B72 for ; Mon, 17 Apr 2023 10:57:50 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B923E10E413; Mon, 17 Apr 2023 10:57:48 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id 076F410E40E; Mon, 17 Apr 2023 10:57:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1681729067; x=1713265067; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=DzY7hE7zeogqEZR//styF6tMOOS8cl8q3iL4Y5Wkp/g=; b=VKxtrRtKr8KzCdc5XULLNE1BXtGQptrMZWoLcmKRlI8wUBRcLMpnmN5B h6iM15mZjwU/mIEAwkqnHkGwqx1MmL5jvDXK0QqVq8TWhE2ZXlt0d05t+ NsgRUzCgnzi+SE9aSjkdSZwxOQCZBD1Uw9dC001eXpiliakGnno04RZ95 UptxByyjX3J76SUfo+k7lyeIIae50pyICHngDCW2wtcVFRF5vUOTgGbBD lNFNvtlraACIZOHFSdhYngjT94Jamppg2Oi4j/Yl+XICkhj5qwPF/VLZM sVGHcxtggt3vwOXHxBrXIQnUqocTAM3AcX5pAKHXNMCjH7OY0jQAHs66h w==; X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="410073188" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="410073188" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:46 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="834363421" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="834363421" Received: from gtohallo-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.232.210]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:45 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Mon, 17 Apr 2023 11:57:29 +0100 Message-Id: <20230417105734.3945840-4-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> References: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 3/8] lib/igt_drm_clients: Record client drm minor X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: Tvrtko Ursulin Prepare for supporting clients belonging to multiple DRM cards by storing the DRM minor in the client record. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 22 ++++++++++++++-------- lib/igt_drm_clients.h | 1 + 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index d06fcb0942a1..b837450301dc 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -124,7 +124,7 @@ igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, static void igt_drm_client_add(struct igt_drm_clients *clients, const struct drm_client_fdinfo *info, - unsigned int pid, char *name) + unsigned int pid, char *name, unsigned int drm_minor) { struct igt_drm_client *c; @@ -149,6 +149,7 @@ igt_drm_client_add(struct igt_drm_clients *clients, } c->id = info->id; + c->drm_minor = drm_minor; c->clients = clients; c->val = calloc(clients->num_classes, sizeof(c->val)); c->last = calloc(clients->num_classes, sizeof(c->last)); @@ -295,16 +296,21 @@ static bool get_task_name(const char *buffer, char *out, unsigned long sz) return true; } -static bool is_drm_fd(int fd_dir, const char *name) +static bool is_drm_fd(int fd_dir, const char *name, unsigned int *minor) { struct stat stat; int ret; ret = fstatat(fd_dir, name, &stat, 0); - return ret == 0 && - (stat.st_mode & S_IFMT) == S_IFCHR && - major(stat.st_rdev) == 226; + if (ret == 0 && + (stat.st_mode & S_IFMT) == S_IFCHR && + major(stat.st_rdev) == 226) { + *minor = minor(stat.st_rdev); + return true; + } + + return false; } static void clients_update_max_lengths(struct igt_drm_clients *clients) @@ -382,10 +388,10 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, return clients; while ((proc_dent = readdir(proc_dir)) != NULL) { + unsigned int client_pid, minor = 0; int pid_dir = -1, fd_dir = -1; struct dirent *fdinfo_dent; char client_name[64] = { }; - unsigned int client_pid; DIR *fdinfo_dir = NULL; char buf[4096]; size_t count; @@ -427,7 +433,7 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, if (!isdigit(fdinfo_dent->d_name[0])) continue; - if (!is_drm_fd(fd_dir, fdinfo_dent->d_name)) + if (!is_drm_fd(fd_dir, fdinfo_dent->d_name, &minor)) continue; if (!__igt_parse_drm_fdinfo(dirfd(fdinfo_dir), @@ -446,7 +452,7 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, info.id); if (!c) igt_drm_client_add(clients, &info, client_pid, - client_name); + client_name, minor); else igt_drm_client_update(c, client_pid, client_name, &info); diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h index 431bf5bbd335..591725b8c059 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -44,6 +44,7 @@ struct igt_drm_client { enum igt_drm_client_status status; unsigned int id; /* DRM client id from fdinfo. */ + unsigned int drm_minor; /* DRM minor of this client. */ unsigned int pid; /* PID which has this DRM fd open. */ char pid_str[10]; /* Cached PID representation. */ char name[24]; /* Process name of the owning PID. */ From patchwork Mon Apr 17 10:57:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13213694 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id C0FF9C77B70 for ; Mon, 17 Apr 2023 10:57:53 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8B73910E416; Mon, 17 Apr 2023 10:57:50 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id 4A3EF10E40F; Mon, 17 Apr 2023 10:57:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1681729068; x=1713265068; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=wPKswX9bQg2liRh77nhHxF2eSkz/pUCxRs4m+ja9pys=; b=Db1oqqeiZvB8Bs/sbxZ3gyf6LOAd5RT0pRPry+eKdHbrbvFsK7ZSMNKj HWAa0jYjZ41xwum9Uy3EMX+tM7nBsbcrNlXZngAJgYNMFWW7HWJzcIyjW k6uBdOhduEei2tzLkyxymOm0DeGEksM3wP/XJL1+LvRkx2hikO/fI+UjE 2KUFKvvqXqpxgFtTgIYqTYOUsEb8wKJyN/Urnae8fOKLbCN8kEdbHS9hf C5PuE+5HY1EDFeLeGx9apkGP+1fWJUPNpmSfKY83GK8OdZGWNINeWpEWX mV44kyR6h4sAevpgdyz6y5M3qWUWqCBWQhuY7CnT3jdAnoNLgC30fMPw1 Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="410073191" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="410073191" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:48 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="834363425" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="834363425" Received: from gtohallo-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.232.210]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:46 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Mon, 17 Apr 2023 11:57:30 +0100 Message-Id: <20230417105734.3945840-5-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> References: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 4/8] lib/igt_drm_clients: Support multiple DRM cards X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: Tvrtko Ursulin Require DRM minor match during client lookup. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index b837450301dc..06b66fe95b62 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -49,7 +49,7 @@ struct igt_drm_clients *igt_drm_clients_init(void *private_data) static struct igt_drm_client * igt_drm_clients_find(struct igt_drm_clients *clients, enum igt_drm_client_status status, - unsigned int id) + unsigned int drm_minor, unsigned int id) { unsigned int start, num; struct igt_drm_client *c; @@ -61,7 +61,8 @@ igt_drm_clients_find(struct igt_drm_clients *clients, if (status != c->status) continue; - if (status == IGT_DRM_CLIENT_FREE || c->id == id) + if (status == IGT_DRM_CLIENT_FREE || + (drm_minor == c->drm_minor && c->id == id)) return c; } @@ -128,9 +129,10 @@ igt_drm_client_add(struct igt_drm_clients *clients, { struct igt_drm_client *c; - assert(!igt_drm_clients_find(clients, IGT_DRM_CLIENT_ALIVE, info->id)); + assert(!igt_drm_clients_find(clients, IGT_DRM_CLIENT_ALIVE, + drm_minor, info->id)); - c = igt_drm_clients_find(clients, IGT_DRM_CLIENT_FREE, 0); + c = igt_drm_clients_find(clients, IGT_DRM_CLIENT_FREE, 0, 0); if (!c) { unsigned int idx = clients->num_clients; @@ -445,11 +447,11 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, continue; if (igt_drm_clients_find(clients, IGT_DRM_CLIENT_ALIVE, - info.id)) + minor, info.id)) continue; /* Skip duplicate fds. */ c = igt_drm_clients_find(clients, IGT_DRM_CLIENT_PROBE, - info.id); + minor, info.id); if (!c) igt_drm_client_add(clients, &info, client_pid, client_name, minor); From patchwork Mon Apr 17 10:57:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13213695 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 29277C77B70 for ; Mon, 17 Apr 2023 10:57:59 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 0C7EF10E420; Mon, 17 Apr 2023 10:57:58 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id 8F2BB10E415; Mon, 17 Apr 2023 10:57:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1681729069; x=1713265069; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=BeLNU26y/iJTDz0RSFLiHsyW5YpGy1msVCki5bA1/R0=; b=hZSLmJN97DoXWq0jXuaBFJfDk+txdzQVi/O9Bv7Ys3+imOlBbOf1hTji aUKBzd258mVWyC3ICHkOGMScYI9RvJYt+ZfBMyetNnBnDERp8NqMcs0Lq f9myOYSlouJCPk/cNghzmLV8tcLziNI6bFtQGAnDV3gWUsMBHJNLFYd45 DXQbD2jQhbcDbCN8fOOCUP6q4/QgxE0t+I+ovFCmlHHgRZPr8Pz0XHpaJ hVs5gpjxdCXCfykSrXiXMNsqWZuDF7XoGAMcjgT0o7ZaSFuBYcypCdwG4 DVzqcFbrpQNRXL17GoCuXM6nafSjH7ZPFSmc92CtFSjwX7R9cFH0Phaf+ A==; X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="410073197" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="410073197" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:49 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="834363429" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="834363429" Received: from gtohallo-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.232.210]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:48 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Mon, 17 Apr 2023 11:57:31 +0100 Message-Id: <20230417105734.3945840-6-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> References: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 5/8] lib/igt_drm_fdinfo: Track largest engine index X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: Tvrtko Ursulin Prep code for incoming work. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_fdinfo.c | 2 ++ lib/igt_drm_fdinfo.h | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/igt_drm_fdinfo.c b/lib/igt_drm_fdinfo.c index 3f4f0e88b0d8..b5f8a8679a71 100644 --- a/lib/igt_drm_fdinfo.c +++ b/lib/igt_drm_fdinfo.c @@ -162,6 +162,8 @@ __igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info, info->capacity[idx] = 1; info->busy[idx] = val; info->num_engines++; + if (idx > info->last_engine_index) + info->last_engine_index = idx; } } else if (!strncmp(l, "drm-engine-capacity-", 20)) { idx = parse_engine(l, info, diff --git a/lib/igt_drm_fdinfo.h b/lib/igt_drm_fdinfo.h index fa4982f4030e..6284e05e868a 100644 --- a/lib/igt_drm_fdinfo.h +++ b/lib/igt_drm_fdinfo.h @@ -38,6 +38,7 @@ struct drm_client_fdinfo { unsigned long id; unsigned int num_engines; + unsigned int last_engine_index; unsigned int capacity[DRM_CLIENT_FDINFO_MAX_ENGINES]; char names[DRM_CLIENT_FDINFO_MAX_ENGINES][256]; uint64_t busy[DRM_CLIENT_FDINFO_MAX_ENGINES]; From patchwork Mon Apr 17 10:57:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13213698 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id F101DC77B78 for ; Mon, 17 Apr 2023 10:57:59 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4791B10E422; Mon, 17 Apr 2023 10:57:59 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id 21FC610E41A; Mon, 17 Apr 2023 10:57:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1681729071; x=1713265071; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=qIRYazsq4XhawOXnBJ02Bt+qi5tgYsj1AYvBhz/cNA4=; b=JJhsVju1XpI49C/Ky1S4f9basViorFVAP7FkKSq0FMKfRbTGAvwFI+/z XWHLEjGdIT6euGHgPLNVsB1N5DPWh4IxQybBnO59DGjuugpPiKTLDMULN nxijTzxnboCEIEd0h43DKsF1DNpl8eKifdjLcVkbfMSqnqTWjOoZ4xR4f IyFOaUJ1qChheuGOdGKr5S4kprGBV888ZxnnLHl/QTcR4Luk8bQJvEvIP TgH9qD/RbGl+WDa0JfwyspaySTAIDin6YrGmlYuInHghVKTtGOXDPEAMV L6stgppYJMyo0BRlWwrNyThyWh00VpRjUbxGTchSNzUZr7jyz7ys0fdnm A==; X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="410073200" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="410073200" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:50 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="834363434" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="834363434" Received: from gtohallo-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.232.210]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:49 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Mon, 17 Apr 2023 11:57:32 +0100 Message-Id: <20230417105734.3945840-7-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> References: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 6/8] lib/igt_drm_clients: Decouple hardcoded engine assumptions X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: Tvrtko Ursulin Intel_gpu_top gets it's main engine configuration data via PMU probe and uses that for per client view as well. Furthemore code so far assumed only clients belonging from a single DRM card would be tracked in a single clients list. Break this inter-dependency by moving the engine data to be per client and also have libdrmclient probe the engine configuration independently using the previously added libdrmfdinfo facilities. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 38 +++++++++++-- lib/igt_drm_clients.h | 14 ++--- tools/intel_gpu_top.c | 127 +++++++++++++++++++++++++++++++----------- 3 files changed, 134 insertions(+), 45 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index 06b66fe95b62..5d06337db70e 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -106,7 +106,7 @@ igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, c->last_runtime = 0; c->total_runtime = 0; - for (i = 0; i < c->clients->num_classes; i++) { + for (i = 0; i <= c->engines->max_engine_id; i++) { assert(i < ARRAY_SIZE(info->busy)); if (info->busy[i] < c->last[i]) @@ -128,6 +128,7 @@ igt_drm_client_add(struct igt_drm_clients *clients, unsigned int pid, char *name, unsigned int drm_minor) { struct igt_drm_client *c; + unsigned int i; assert(!igt_drm_clients_find(clients, IGT_DRM_CLIENT_ALIVE, drm_minor, info->id)); @@ -153,8 +154,28 @@ igt_drm_client_add(struct igt_drm_clients *clients, c->id = info->id; c->drm_minor = drm_minor; c->clients = clients; - c->val = calloc(clients->num_classes, sizeof(c->val)); - c->last = calloc(clients->num_classes, sizeof(c->last)); + c->engines = malloc(sizeof(*c->engines)); + assert(c->engines); + memset(c->engines, 0, sizeof(*c->engines)); + c->engines->capacity = calloc(info->last_engine_index + 1, + sizeof(*c->engines->capacity)); + assert(c->engines->capacity); + c->engines->names = calloc(info->last_engine_index + 1, + sizeof(*c->engines->names)); + assert(c->engines->names); + + for (i = 0; i <= info->last_engine_index; i++) { + if (!info->capacity[i]) + continue; + + c->engines->capacity[i] = info->capacity[i]; + c->engines->names[i] = strdup(info->names[i]); + assert(c->engines->names[i]); + c->engines->num_engines++; + c->engines->max_engine_id = i; + } + c->val = calloc(c->engines->max_engine_id + 1, sizeof(c->val)); + c->last = calloc(c->engines->max_engine_id + 1, sizeof(c->last)); assert(c->val && c->last); igt_drm_client_update(c, pid, name, info); @@ -163,6 +184,15 @@ igt_drm_client_add(struct igt_drm_clients *clients, static void igt_drm_client_free(struct igt_drm_client *c, bool clear) { + unsigned int i; + + if (c->engines) { + for (i = 0; i <= c->engines->max_engine_id; i++) + free(c->engines->names[i]); + free(c->engines->capacity); + free(c->engines->names); + } + free(c->engines); free(c->val); free(c->last); @@ -356,7 +386,7 @@ static void clients_update_max_lengths(struct igt_drm_clients *clients) * * If @name_map is not provided engine names will be auto-detected (this is * less performant) and indices will correspond with auto-detected names as - * listed int clients->engine_class[]. + * listed int clients->engines->names[]. */ struct igt_drm_clients * igt_drm_clients_scan(struct igt_drm_clients *clients, diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h index 591725b8c059..36a1547a37e5 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -31,10 +31,12 @@ enum igt_drm_client_status { IGT_DRM_CLIENT_PROBE }; -struct igt_drm_client_engine_class { - unsigned int engine_class; - const char *name; - unsigned int num_engines; +struct igt_drm_client_engines { + unsigned int num_engines; /* Number of discovered active engines. */ + unsigned int max_engine_id; /* Largest engine index discovered. + (Can differ from num_engines - 1 when using the engine map facility.) */ + unsigned int *capacity; /* Array of engine capacities as parsed from fdinfo. */ + char **names; /* Array of engine names, either auto-detected or from the passed in engine map. */ }; struct igt_drm_clients; @@ -43,6 +45,7 @@ struct igt_drm_client { struct igt_drm_clients *clients; /* Owning list. */ enum igt_drm_client_status status; + struct igt_drm_client_engines *engines; /* Engines used by this client, to map with busynees data. */ unsigned int id; /* DRM client id from fdinfo. */ unsigned int drm_minor; /* DRM minor of this client. */ unsigned int pid; /* PID which has this DRM fd open. */ @@ -60,9 +63,6 @@ struct igt_drm_clients { unsigned int num_clients; unsigned int active_clients; - unsigned int num_classes; - struct igt_drm_client_engine_class *engine_class; - int max_pid_len; int max_name_len; diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index 54c1ac41cdf8..a095f8a682fa 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -67,6 +67,12 @@ struct pmu_counter { bool present; }; +struct engine_class { + unsigned int engine_class; + const char *name; + unsigned int num_engines; +}; + struct engine { const char *name; char *display_name; @@ -85,7 +91,7 @@ struct engine { struct engines { unsigned int num_engines; unsigned int num_classes; - struct igt_drm_client_engine_class *class; + struct engine_class *class; unsigned int num_counters; DIR *root; int fd; @@ -117,6 +123,11 @@ struct engines { }; +struct intel_clients { + const char *pci_slot; + struct igt_drm_client_engines classes; +}; + static struct termios termios_orig; __attribute__((format(scanf,3,4))) @@ -774,9 +785,8 @@ static struct igt_drm_clients *display_clients(struct igt_drm_clients *clients) ac = calloc(clients->num_clients, sizeof(*c)); assert(ac); - aggregated->num_classes = clients->num_classes; - aggregated->engine_class = clients->engine_class; aggregated->private_data = clients->private_data; + aggregated->client = ac; igt_for_each_drm_client(clients, c, tmp) { @@ -798,7 +808,8 @@ static struct igt_drm_clients *display_clients(struct igt_drm_clients *clients) strcpy(ac->name, c->name); strcpy(ac->pid_str, c->pid_str); strcpy(ac->print_name, c->print_name); - ac->val = calloc(clients->num_classes, + ac->engines = c->engines; + ac->val = calloc(c->engines->max_engine_id + 1, sizeof(ac->val[0])); assert(ac->val); ac->samples = 1; @@ -813,7 +824,7 @@ static struct igt_drm_clients *display_clients(struct igt_drm_clients *clients) ac->total_runtime += c->total_runtime; ac->last_runtime += c->last_runtime; - for (i = 0; i < clients->num_classes; i++) + for (i = 0; i <= c->engines->max_engine_id; i++) ac->val[i] += c->val[i]; } @@ -837,7 +848,7 @@ static void free_display_clients(struct igt_drm_clients *clients) /* * Don't call igt_drm_clients_free or igt_drm_client_free since * "display" clients are not proper clients and have un-initialized - * fields which we don't want the library to try and free. + * or borrowed fields which we don't want the library to try and free. */ igt_for_each_drm_client(clients, c, tmp) free(c->val); @@ -1619,15 +1630,15 @@ print_engines_footer(struct engines *engines, double t, static int class_cmp(const void *_a, const void *_b) { - const struct igt_drm_client_engine_class *a = _a; - const struct igt_drm_client_engine_class *b = _b; + const struct engine_class *a = _a; + const struct engine_class *b = _b; return a->engine_class - b->engine_class; } static void init_engine_classes(struct engines *engines) { - struct igt_drm_client_engine_class *classes; + struct engine_class *classes; unsigned int i, num; int max = -1; @@ -1799,6 +1810,8 @@ static int print_clients_header(struct igt_drm_clients *clients, int lines, int con_w, int con_h, int *class_w) { + struct intel_clients *iclients = clients->private_data; + if (output_mode == INTERACTIVE) { unsigned int num_active = 0; int len; @@ -1814,25 +1827,25 @@ print_clients_header(struct igt_drm_clients *clients, int lines, if (lines++ >= con_h || len >= con_w) return lines; - if (clients->num_classes) { + if (iclients->classes.num_engines) { unsigned int i; int width; - for (i = 0; i < clients->num_classes; i++) { - if (clients->engine_class[i].num_engines) + for (i = 0; i <= iclients->classes.max_engine_id; i++) { + if (iclients->classes.capacity[i]) num_active++; } *class_w = width = (con_w - len) / num_active; - for (i = 0; i < clients->num_classes; i++) { - const char *name = clients->engine_class[i].name; + for (i = 0; i <= iclients->classes.max_engine_id; i++) { + const char *name = iclients->classes.names[i]; int name_len = strlen(name); int pad = (width - name_len) / 2; int spaces = width - pad - name_len; - if (!clients->engine_class[i].num_engines) - continue; /* Assert in the ideal world. */ + if (!iclients->classes.capacity[i]) + continue; if (pad < 0 || spaces < 0) continue; @@ -1847,7 +1860,7 @@ print_clients_header(struct igt_drm_clients *clients, int lines, n_spaces(con_w - len); printf("\033[0m\n"); } else { - if (clients->num_classes) + if (iclients->classes.num_engines) pops->open_struct("clients"); } @@ -1862,6 +1875,7 @@ print_client(struct igt_drm_client *c, struct engines *engines, double t, int li int con_w, int con_h, unsigned int period_us, int *class_w) { struct igt_drm_clients *clients = c->clients; + struct intel_clients *iclients = clients->private_data; unsigned int i; if (output_mode == INTERACTIVE) { @@ -1874,11 +1888,13 @@ print_client(struct igt_drm_client *c, struct engines *engines, double t, int li clients->max_pid_len, c->pid_str, clients->max_name_len, c->print_name); - for (i = 0; c->samples > 1 && i < clients->num_classes; i++) { + for (i = 0; + c->samples > 1 && i <= iclients->classes.max_engine_id; + i++) { double pct, max; - if (!clients->engine_class[i].num_engines) - continue; /* Assert in the ideal world. */ + if (!iclients->classes.capacity[i]) + continue; pct = (double)c->val[i] / period_us / 1e3 * 100; @@ -1887,7 +1903,7 @@ print_client(struct igt_drm_client *c, struct engines *engines, double t, int li * client data and time we obtained our time-delta from * PMU. */ - max = 100.0 * clients->engine_class[i].num_engines; + max = 100.0 * iclients->classes.capacity[i]; if (pct > max) pct = max; @@ -1910,11 +1926,11 @@ print_client(struct igt_drm_client *c, struct engines *engines, double t, int li if (c->samples > 1) { pops->open_struct("engine-classes"); - for (i = 0; i < clients->num_classes; i++) { + for (i = 0; i <= iclients->classes.max_engine_id; i++) { double pct; snprintf(buf, sizeof(buf), "%s", - clients->engine_class[i].name); + iclients->classes.names[i]); pops->open_struct(buf); pct = (double)c->val[i] / period_us / 1e3 * 100; @@ -1943,7 +1959,9 @@ print_clients_footer(struct igt_drm_clients *clients, double t, if (lines++ < con_h) printf("\n"); } else { - if (clients->num_classes) + struct intel_clients *iclients = clients->private_data; + + if (iclients->classes.num_engines) pops->close_struct(); } @@ -2188,14 +2206,54 @@ static unsigned long elapsed_us(struct timespec *prev, unsigned int period_us) static bool client_match(const struct igt_drm_clients *clients, const struct drm_client_fdinfo *info) { + struct intel_clients *iclients = clients->private_data; + if (strcmp(info->driver, "i915")) return false; - if (strcmp(info->pdev, clients->private_data)) + if (strcmp(info->pdev, iclients->pci_slot)) return false; return true; } +static void +intel_init_clients(struct intel_clients *iclients, + const struct igt_device_card *card, struct engines *engines) +{ + unsigned int i; + + iclients->pci_slot = strdup(card->pci_slot_name[0] ? + card->pci_slot_name : IGPU_PCI); + assert(iclients->pci_slot); + + iclients->classes.num_engines = engines->num_classes; + iclients->classes.max_engine_id = engines->num_classes - 1; + + iclients->classes.capacity = calloc(engines->num_classes, + sizeof(*iclients->classes.capacity)); + assert(iclients->classes.capacity); + iclients->classes.names = calloc(engines->num_classes, + sizeof(*iclients->classes.names)); + assert(iclients->classes.names); + + for (i = 0; i < engines->num_classes; i++) { + if (!engines->class[i].num_engines) + continue; + + iclients->classes.num_engines++; + iclients->classes.max_engine_id = i; + iclients->classes.capacity[i] = engines->class[i].num_engines; + iclients->classes.names[i] = strdup(engines->class[i].name); + } +} + +static void intel_free_clients(struct intel_clients *iclients) +{ + free((void *)iclients->pci_slot); + free(iclients->classes.capacity); + free(iclients->classes.names); +} + int main(int argc, char **argv) { unsigned int period_us = DEFAULT_PERIOD_MS * 1000; @@ -2208,6 +2266,7 @@ int main(int argc, char **argv) "compute", }; bool physical_engines = false; + struct intel_clients iclients; int con_w = -1, con_h = -1; char *output_path = NULL; struct engines *engines; @@ -2331,6 +2390,8 @@ int main(int argc, char **argv) else pmu_device = strdup("i915"); + codename = igt_device_get_pretty_name(&card, false); + engines = discover_engines(pmu_device); if (!engines) { fprintf(stderr, @@ -2359,21 +2420,17 @@ int main(int argc, char **argv) ret = EXIT_SUCCESS; - if (has_drm_fdinfo(&card)) - clients = igt_drm_clients_init(strdup(card.pci_slot_name[0] ? - card.pci_slot_name : - IGPU_PCI)); init_engine_classes(engines); - if (clients) { - clients->num_classes = engines->num_classes; - clients->engine_class = engines->class; + + if (has_drm_fdinfo(&card)) { + intel_init_clients(&iclients, &card, engines); + clients = igt_drm_clients_init(&iclients); } pmu_sample(engines); igt_drm_clients_scan(clients, client_match, engine_map, ARRAY_SIZE(engine_map)); gettime(&ts); - codename = igt_device_get_pretty_name(&card, false); if (output_mode == JSON) printf("[\n"); @@ -2473,8 +2530,10 @@ int main(int argc, char **argv) if (output_mode == JSON) printf("]\n"); - if (clients) + if (clients) { igt_drm_clients_free(clients); + intel_free_clients(&iclients); + } free(codename); err_pmu: From patchwork Mon Apr 17 10:57:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13213696 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 7AC9CC77B76 for ; Mon, 17 Apr 2023 10:57:57 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id ED96E10E41E; Mon, 17 Apr 2023 10:57:56 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id 3699510E415; Mon, 17 Apr 2023 10:57:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1681729072; x=1713265072; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=I8fN56hPOC93sJpZTQiV0/lZcpTgX/cKPLf4SXSb2VQ=; b=DGzJvI81NuDvWrt3qS0mOCOl4pYIbSs4lc2hYYlTgANh/GpUhnfhPTDJ 6+D7qwvR2yUlKSSh+IGIdPn+wlJRPg+6sNyA9t/77nKVxJWXqHpGYDYkZ SWIbYR2tImaSyeOQ+/y6R9vTyWz9NyEpl5Fq62CSbGLI5F6P8OHTx4Kuo QJfIJgc0Km7lIy7z90fW6E6buxy4jdKqYlgj9LcCJBXA6o95HXpRNqFkx egYvsWvWtSjqHvjpIrKT8rmNz/Yu/7rLzQy1QdzKjK4LaAooulH4qCzaS eywv9qCo2PTi1bYxEv1NJaS9RLHZMqy+ufpyd1Z7nsudIsDZ0bK7w9V+h w==; X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="410073203" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="410073203" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:51 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="834363440" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="834363440" Received: from gtohallo-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.232.210]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:50 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Mon, 17 Apr 2023 11:57:33 +0100 Message-Id: <20230417105734.3945840-8-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> References: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 7/8] lib/igt_drm_clients: Enforce client status sort order in the library X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: Tvrtko Ursulin Some libdrmclient operations require that inactive clients are last in the list. Rather than relying on callers of the library sort routine to implement their comparison callbacks correctly, enforce this order directly in the library and let callers comparison callbacks concern themselves only with ordering they are interested in. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 37 +++++++++++++++----- lib/igt_drm_clients.h | 2 +- tools/intel_gpu_top.c | 81 +++++++++++++++++++------------------------ 3 files changed, 65 insertions(+), 55 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index 5d06337db70e..f0294ba81c42 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -200,22 +200,38 @@ void igt_drm_client_free(struct igt_drm_client *c, bool clear) memset(c, 0, sizeof(*c)); } +struct sort_context +{ + int (*user_cmp)(const void *, const void *, void *); +}; + +static int sort_cmp(const void *_a, const void *_b, void *_ctx) +{ + const struct sort_context *ctx = _ctx; + const struct igt_drm_client *a = _a; + const struct igt_drm_client *b = _b; + int cmp = b->status - a->status; + + if (cmp == 0) + return ctx->user_cmp(_a, _b, _ctx); + else + return cmp; +} + /** * igt_drm_clients_sort: * @clients: Previously initialised clients object * @cmp: Client comparison callback * * Sort the clients array according to the passed in comparison callback which - * is compatible with the qsort(3) semantics. - * - * Caller has to ensure the callback is putting all active - * (IGT_DRM_CLIENT_ALIVE) clients in a single group at the head of the array - * before any other sorting criteria. + * is compatible with the qsort(3) semantics, with the third void * argument + * being unused. */ struct igt_drm_clients * igt_drm_clients_sort(struct igt_drm_clients *clients, - int (*cmp)(const void *, const void *)) + int (*cmp)(const void *, const void *, void *)) { + struct sort_context ctx = { .user_cmp = cmp }; unsigned int active, free; struct igt_drm_client *c; int tmp; @@ -223,8 +239,13 @@ igt_drm_clients_sort(struct igt_drm_clients *clients, if (!clients) return clients; - qsort(clients->client, clients->num_clients, sizeof(*clients->client), - cmp); + /* + * Enforce client->status ordering (active followed by free) by running + * the user provided comparison callback wrapped in the one internal + * to the library. + */ + qsort_r(clients->client, clients->num_clients, sizeof(*clients->client), + sort_cmp, &ctx); /* Trim excessive array space. */ active = 0; diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h index 36a1547a37e5..ed795c193986 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -86,6 +86,6 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, struct igt_drm_clients * igt_drm_clients_sort(struct igt_drm_clients *clients, - int (*cmp)(const void *, const void *)); + int (*cmp)(const void *, const void *, void *)); #endif /* IGT_DRM_CLIENTS_H */ diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index a095f8a682fa..453090c298bc 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -685,85 +685,74 @@ static void pmu_sample(struct engines *engines) } } -static int client_last_cmp(const void *_a, const void *_b) +static int +__client_id_cmp(const struct igt_drm_client *a, + const struct igt_drm_client *b) +{ + if (a->id > b->id) + return 1; + else if (a->id < b->id) + return -1; + else + return 0; +} + +static int client_last_cmp(const void *_a, const void *_b, void *unused) { const struct igt_drm_client *a = _a; const struct igt_drm_client *b = _b; - long tot_a, tot_b; + long val_a = a->last_runtime, val_b = b->last_runtime; /* * Sort clients in descending order of runtime in the previous sampling - * period for active ones, followed by inactive. Tie-breaker is client - * id. + * period. Tie-breaker is client id. */ - tot_a = a->status == IGT_DRM_CLIENT_ALIVE ? a->last_runtime : -1; - tot_b = b->status == IGT_DRM_CLIENT_ALIVE ? b->last_runtime : -1; - - tot_b -= tot_a; - if (tot_b > 0) + if (val_a == val_b) + return __client_id_cmp(a, b); + else if (val_b > val_a) return 1; - if (tot_b < 0) + else return -1; - - return (int)b->id - a->id; } -static int client_total_cmp(const void *_a, const void *_b) +static int client_total_cmp(const void *_a, const void *_b, void *unused) { const struct igt_drm_client *a = _a; const struct igt_drm_client *b = _b; - long tot_a, tot_b; + long val_a = a->total_runtime, val_b = b->total_runtime; - tot_a = a->status == IGT_DRM_CLIENT_ALIVE ? a->total_runtime : -1; - tot_b = b->status == IGT_DRM_CLIENT_ALIVE ? b->total_runtime : -1; - - tot_b -= tot_a; - if (tot_b > 0) + if (val_a == val_b) + return __client_id_cmp(a, b); + else if (val_b > val_a) return 1; - if (tot_b < 0) + else return -1; - - return (int)b->id - a->id; } -static int client_id_cmp(const void *_a, const void *_b) +static int client_id_cmp(const void *_a, const void *_b, void *unused) { const struct igt_drm_client *a = _a; const struct igt_drm_client *b = _b; - int id_a, id_b; - - id_a = a->status == IGT_DRM_CLIENT_ALIVE ? a->id : -1; - id_b = b->status == IGT_DRM_CLIENT_ALIVE ? b->id : -1; - - id_b -= id_a; - if (id_b > 0) - return 1; - if (id_b < 0) - return -1; - return (int)b->id - a->id; + return __client_id_cmp(a, b); } -static int client_pid_cmp(const void *_a, const void *_b) +static int client_pid_cmp(const void *_a, const void *_b, void *unused) { const struct igt_drm_client *a = _a; const struct igt_drm_client *b = _b; - int pid_a, pid_b; - - pid_a = a->status == IGT_DRM_CLIENT_ALIVE ? a->pid : INT_MAX; - pid_b = b->status == IGT_DRM_CLIENT_ALIVE ? b->pid : INT_MAX; + int val_a = a->pid, val_b = b->pid; - pid_b -= pid_a; - if (pid_b > 0) + if (val_a == val_b) + return __client_id_cmp(a, b); + else if (val_b > val_a) return -1; - if (pid_b < 0) + else return 1; - - return (int)a->id - b->id; } -static int (*client_cmp)(const void *, const void *) = client_last_cmp; +static int (*client_cmp)(const void *, const void *, void *) = client_last_cmp; static bool aggregate_pids = true; @@ -2035,7 +2024,7 @@ static void interactive_stdin(void) static void select_client_sort(void) { struct { - int (*cmp)(const void *, const void *); + int (*cmp)(const void *, const void *, void *); const char *msg; } cmp[] = { { client_last_cmp, "Sorting clients by current GPU usage." }, From patchwork Mon Apr 17 10:57:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13213697 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 6E557C77B72 for ; Mon, 17 Apr 2023 10:57:58 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DEB2510E41F; Mon, 17 Apr 2023 10:57:57 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id 1C6B710E41A; Mon, 17 Apr 2023 10:57:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1681729074; x=1713265074; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=xba5LeUn1VgqoGZdhWPUBr6UXo+qH8zjETz4VJQdqOA=; b=AakP//1BUzbqMRsKKGph7CiAfoz2GpNVZE33HMcOwXarcsu13HZejjMZ cgm3cQDfs8e0Fs84hBvFZoS+EP3U3L3tZcOrGYeJCwZ/pYal1PTrzfW3N sBr2Yn1B+IKm7T7n1oRqQ59TMt0rGbQH5SRLnomuI8t+z943HbzfUWLpy aGZyBgVeqv9RA4zdAVN+OqkiSXuusBPl53HNVIA25wCNHOWTfMbhm4xNd jxahLg9Wnq+kWk7Cha9AeA+jz7CubMyMDehRSx3nzCh61hm/YXO7VYZxo QgHGxXbVL1GU3zfffHFV6gjNIIR92Y4ku9mJh/Et+95hQwup7+TKShJ8v w==; X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="410073206" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="410073206" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:53 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10682"; a="834363445" X-IronPort-AV: E=Sophos;i="5.99,203,1677571200"; d="scan'208";a="834363445" Received: from gtohallo-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.232.210]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Apr 2023 03:57:52 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Mon, 17 Apr 2023 11:57:34 +0100 Message-Id: <20230417105734.3945840-9-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> References: <20230417105734.3945840-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 8/8] gputop: Basic vendor agnostic GPU top tool X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Rob Clark , =?utf-8?q?Christian_K=C3=B6nig?= , =?utf-8?q?Christian_K=C3=B6nig?= Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: Tvrtko Ursulin Rudimentary vendor agnostic example of how lib_igt_drm_clients can be used to display a sorted by card and usage list of processes using GPUs. Borrows a bit of code from intel_gpu_top but for now omits the fancy features like interactive functionality, card selection, client aggregation, sort modes, JSON output and pretty engine names. Also no support for global GPU or system metrics. On the other hand it shows clients from all DRM cards which intel_gpu_top does not do. Signed-off-by: Tvrtko Ursulin Cc: Rob Clark Cc: Christian König Acked-by: Christian König Reviewed-by: Rob Clark --- tools/gputop.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++ tools/meson.build | 5 + 2 files changed, 271 insertions(+) create mode 100644 tools/gputop.c diff --git a/tools/gputop.c b/tools/gputop.c new file mode 100644 index 000000000000..4fb5ce63e07c --- /dev/null +++ b/tools/gputop.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "igt_drm_clients.h" +#include "igt_drm_fdinfo.h" + +static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" }; + +static void n_spaces(const unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + putchar(' '); +} + +static void print_percentage_bar(double percent, int max_len) +{ + int bar_len, i, len = max_len - 2; + const int w = 8; + + assert(max_len > 0); + + bar_len = ceil(w * percent * len / 100.0); + if (bar_len > w * len) + bar_len = w * len; + + putchar('|'); + + for (i = bar_len; i >= w; i -= w) + printf("%s", bars[w]); + if (i) + printf("%s", bars[i]); + + len -= (bar_len + (w - 1)) / w; + n_spaces(len); + + putchar('|'); +} + +static int +print_client_header(struct igt_drm_client *c, int lines, int con_w, int con_h, + int *engine_w) +{ + int ret, len; + + if (lines++ >= con_h) + return lines; + + printf("\033[7m"); + ret = printf("DRM minor %u", c->drm_minor); + n_spaces(con_w - ret); + + if (lines++ >= con_h) + return lines; + + putchar('\n'); + len = printf("%*s %*s ", + c->clients->max_pid_len, "PID", + c->clients->max_name_len, "NAME"); + + if (c->engines->num_engines) { + unsigned int i; + int width; + + *engine_w = width = (con_w - len) / c->engines->num_engines; + + for (i = 0; i <= c->engines->max_engine_id; i++) { + const char *name = c->engines->names[i]; + int name_len = strlen(name); + int pad = (width - name_len) / 2; + int spaces = width - pad - name_len; + + if (!name) + continue; + + if (pad < 0 || spaces < 0) + continue; + + n_spaces(pad); + printf("%s", name); + n_spaces(spaces); + len += pad + name_len + spaces; + } + } + + n_spaces(con_w - len); + printf("\033[0m\n"); + + return lines; +} + + +static bool +newheader(const struct igt_drm_client *c, const struct igt_drm_client *pc) +{ + return !pc || c->drm_minor != pc->drm_minor; +} + +static int +print_client(struct igt_drm_client *c, struct igt_drm_client **prevc, + double t, int lines, int con_w, int con_h, + unsigned int period_us, int *engine_w) +{ + unsigned int i; + + /* Filter out idle clients. */ + if (!c->total_runtime || c->samples < 2) + return lines; + + /* Print header when moving to a different DRM card. */ + if (newheader(c, *prevc)) { + lines = print_client_header(c, lines, con_w, con_h, engine_w); + if (lines >= con_h) + return lines; + } + + *prevc = c; + + printf("%*s %*s ", + c->clients->max_pid_len, c->pid_str, + c->clients->max_name_len, c->print_name); + lines++; + + for (i = 0; c->samples > 1 && i <= c->engines->max_engine_id; i++) { + double pct; + + if (!c->engines->capacity[i]) + continue; + + pct = (double)c->val[i] / period_us / 1e3 * 100 / + c->engines->capacity[i]; + + /* + * Guard against fluctuations between our scanning period and + * GPU times as exported by the kernel in fdinfo. + */ + if (pct > 100.0) + pct = 100.0; + + print_percentage_bar(pct, *engine_w); + } + + putchar('\n'); + + return lines; +} + +static int +__client_id_cmp(const struct igt_drm_client *a, + const struct igt_drm_client *b) +{ + if (a->id > b->id) + return 1; + else if (a->id < b->id) + return -1; + else + return 0; +} + +static int client_cmp(const void *_a, const void *_b, void *unused) +{ + const struct igt_drm_client *a = _a; + const struct igt_drm_client *b = _b; + long val_a, val_b; + + /* DRM cards into consecutive buckets first. */ + val_a = a->drm_minor; + val_b = b->drm_minor; + if (val_a > val_b) + return 1; + else if (val_b > val_a) + return -1; + + /* + * Within buckets sort by last sampling period aggregated runtime, with + * client id as a tie-breaker. + */ + val_a = a->last_runtime; + val_b = b->last_runtime; + if (val_a == val_b) + return __client_id_cmp(a, b); + else if (val_b > val_a) + return 1; + else + return -1; + +} + +int main(int argc, char **argv) +{ + unsigned int period_us = 2e6; + struct igt_drm_clients *clients = NULL; + int con_w = -1, con_h = -1; + + clients = igt_drm_clients_init(NULL); + if (!clients) + exit(1); + + igt_drm_clients_scan(clients, NULL, NULL, 0); + + for (;;) { + struct igt_drm_client *c, *prevc = NULL; + int i, engine_w = 0, lines = 0; + struct winsize ws; + + if (ioctl(0, TIOCGWINSZ, &ws) != -1) { + con_w = ws.ws_col; + con_h = ws.ws_row; + if (con_w == 0 && con_h == 0) { + /* Serial console. */ + con_w = 80; + con_h = 24; + } + } + + igt_drm_clients_scan(clients, NULL, NULL, 0); + igt_drm_clients_sort(clients, client_cmp); + + printf("\033[H\033[J"); + + igt_for_each_drm_client(clients, c, i) { + assert(c->status != IGT_DRM_CLIENT_PROBE); + if (c->status != IGT_DRM_CLIENT_ALIVE) + break; /* Active clients are first in the array. */ + + lines = print_client(c, &prevc, (double)period_us / 1e6, + lines, con_w, con_h, period_us, + &engine_w); + if (lines >= con_h) + break; + } + + if (lines++ < con_h) + printf("\n"); + + usleep(period_us); + } + + return 0; +} diff --git a/tools/meson.build b/tools/meson.build index 54e387cf0f4b..21e244c24a5b 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -66,6 +66,11 @@ if libudev.found() install : true) endif +executable('gputop', 'gputop.c', + install : true, + install_rpath : bindir_rpathdir, + dependencies : [lib_igt_drm_clients,lib_igt_drm_fdinfo,math]) + intel_l3_parity_src = [ 'intel_l3_parity.c', 'intel_l3_udev_listener.c' ] executable('intel_l3_parity', sources : intel_l3_parity_src, dependencies : tool_deps,