From patchwork Tue Jan 31 11:32:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13122909 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 52EF1C636CC for ; Tue, 31 Jan 2023 11:33:03 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6D52410E32C; Tue, 31 Jan 2023 11:32:58 +0000 (UTC) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id 95B3C10E31A; Tue, 31 Jan 2023 11:32:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675164776; x=1706700776; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Rjq1jze8605c6OIM4BSjA9sVJjdQNwl9EaboT2oc3As=; b=G7lbEFO7CHTTnlVgzc8LT0GxaW1oWPbaXTT1nstTgJwmTyRMczn6P5I1 t0Ehu2oTKtdAT0MWxfiz03VYtu07neMJNWoxxEepCocAsOv2CS4vN5rLi 8I6N1PxBrSCf9fsz3vc4S7mM83vQ4fBnGyXhyQUj6x6SfQx+pjt/fomqF KOkT2MzOAjDUTM+pP9SQvQd0AjgBNDA+9TIitf5Fk0aeF40/Z9vAy7Efq IZxb2HTlKoxs9kGJahiEnMUkdBklMxl8gZvbu/kcYuROY5uaA/x1xE9SN nwb6UQEsCw3qPF0W6ag/iGyytr33UO7f4xnYAe9NRqUYWjSsinrHFKhQT Q==; X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="308159802" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="308159802" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:48 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="993235718" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="993235718" Received: from rdorchar-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.217.6]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:46 -0800 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Tue, 31 Jan 2023 11:32:30 +0000 Message-Id: <20230131113237.3707217-2-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230131113237.3707217-1-tvrtko.ursulin@linux.intel.com> References: <20230131113237.3707217-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 | 432 ++++++++++++++++++++++++++++++++++ lib/igt_drm_clients.h | 85 +++++++ lib/meson.build | 8 + tools/intel_gpu_top.c | 521 +++++++----------------------------------- tools/meson.build | 2 +- 5 files changed, 606 insertions(+), 442 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..45de2d0f1cc5 --- /dev/null +++ b/lib/igt_drm_clients.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2022 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; + + /* Update client pid if it changed (fd sharing). */ + if (c->pid != pid) + c->pid = pid; + + /* 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++; + } + } + + 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; +} + +/** + * 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; + 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); + else if (c->status == IGT_DRM_CLIENT_FREE) + break; + } + + return clients; +} diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h new file mode 100644 index 000000000000..969793d5b51e --- /dev/null +++ b/lib/igt_drm_clients.h @@ -0,0 +1,85 @@ +// 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 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; + + 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 d49b78ca1a12..e6529f682988 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', @@ -250,6 +251,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 0a1de41b3374..e660f0c6c3b2 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -46,6 +46,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])) @@ -80,16 +81,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; @@ -674,163 +669,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 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; - - 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; - - if (c->pid != pid) - c->pid = pid; - - 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++; - } - } - - 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; /* @@ -839,8 +681,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) @@ -853,12 +695,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) @@ -871,12 +713,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) @@ -889,12 +731,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) @@ -907,56 +749,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); @@ -965,23 +770,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); @@ -1011,207 +817,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 struct clients *scan_clients(struct clients *clients, bool display) -{ - struct dirent *proc_dent; - 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); - else if (c->status == FREE) - break; - } - - return display ? display_clients(clients) : clients; -} - static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" }; static void n_spaces(const unsigned int n) @@ -1915,15 +1540,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; @@ -1950,7 +1575,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); } @@ -2092,7 +1717,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) { @@ -2114,19 +1739,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) @@ -2153,10 +1778,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) { @@ -2170,11 +1795,11 @@ 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; - 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 / - clients->class[i].num_engines; + clients->engine_class[i].num_engines; /* * Guard against possible time-drift between sampling @@ -2206,7 +1831,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; @@ -2228,7 +1853,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) { @@ -2449,10 +2074,21 @@ static void show_help_screen(void) "\n"); } +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; @@ -2597,27 +2233,28 @@ 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); codename = igt_device_get_pretty_name(&card, false); if (output_mode == JSON) printf("[\n"); while (!stop_top) { - struct clients *disp_clients; + struct igt_drm_clients *disp_clients; bool consumed = false; int j, lines = 0; struct winsize ws; - struct client *c; + struct igt_drm_client *c; double t; /* Update terminal size. */ @@ -2636,11 +2273,13 @@ int main(int argc, char **argv) pmu_sample(engines); t = (double)(engines->ts.cur - engines->ts.prev) / 1e9; - disp_clients = scan_clients(clients, true); - if (stop_top) break; + disp_clients = + display_clients(igt_drm_clients_scan(clients, + client_match)); + while (!consumed) { pops->open_struct(NULL); @@ -2664,9 +2303,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) @@ -2687,7 +2326,7 @@ int main(int argc, char **argv) } if (disp_clients != clients) - free_clients(disp_clients); + free_display_clients(disp_clients); if (stop_top) break; @@ -2702,7 +2341,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 d2defec87031..c6194fd15daa 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -87,7 +87,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 Tue Jan 31 11:32: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: 13122908 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 0E97BC636CC for ; Tue, 31 Jan 2023 11:32:58 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3DD0A10E32A; Tue, 31 Jan 2023 11:32:58 +0000 (UTC) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id D4FE510E31A; Tue, 31 Jan 2023 11:32:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675164776; x=1706700776; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=w9L8ZDOXyiAGzN40mSfLTigq0wUk+0ivGyekPlNktAM=; b=LgGg3S+OcicoEGbb7PwMOR0ag9hCwP/WIt6NtBEPJReWokQNoaQc2b3Q HXfRX08xtW9lXRWpNWmp2W6lKjRgbw5ucO2jYOtpip5eFqI4X5INj8X+8 y30j4eSwTyfYU5gKz1GaPGg++jZt3KUf8uCLQ/wlq5zjrykjMmuXd07fz 2pXZ/SujsJ+RFMezNTt4Vd3XGiB/3iyvyGV0KDy8jG4Ksf/jqbqlC7mCP Fy3VmZ2cHlkj9ANvDmzG39Wdn7MfIhGgNVroa4u3CWGs3vV/mrJ1AUujt HR1J1omVbI+rXo2ZZWGSvkrOjYQ2DF8w1wrcOsGJbEAyUWbukFOV5JOLm g==; X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="308159808" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="308159808" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:49 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="993235722" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="993235722" Received: from rdorchar-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.217.6]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:48 -0800 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Tue, 31 Jan 2023 11:32:31 +0000 Message-Id: <20230131113237.3707217-3-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230131113237.3707217-1-tvrtko.ursulin@linux.intel.com> References: <20230131113237.3707217-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 | 48 +++++++++++++++++++++++++++-------------- lib/igt_drm_fdinfo.h | 15 ++++++++++--- tests/i915/drm_fdinfo.c | 19 ++++++++++++---- tools/intel_gpu_top.c | 16 +++++++++++--- 6 files changed, 89 insertions(+), 30 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index 45de2d0f1cc5..eabd43773f2d 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -302,14 +302,26 @@ static bool is_drm_fd(int fd_dir, const char *name) * 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; @@ -385,8 +397,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 969793d5b51e..bced19adb055 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -76,7 +76,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 250d9e8917f2..68c89ad2c17e 100644 --- a/lib/igt_drm_fdinfo.c +++ b/lib/igt_drm_fdinfo.c @@ -22,6 +22,7 @@ * */ +#include #include #include #include @@ -53,14 +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", - }; ssize_t name_len; char *name, *p; int found = -1; @@ -76,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; } } @@ -112,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; @@ -142,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; @@ -152,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++; @@ -166,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]; @@ -180,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 65b8b8307c66..2f300d492853 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]; } @@ -268,7 +278,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)); } @@ -728,7 +739,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 e660f0c6c3b2..2fb9f9b757d5 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -2053,7 +2053,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); @@ -2089,6 +2089,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; @@ -2243,7 +2250,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)); codename = igt_device_get_pretty_name(&card, false); if (output_mode == JSON) @@ -2278,7 +2286,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))); while (!consumed) { pops->open_struct(NULL); From patchwork Tue Jan 31 11:32: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: 13122911 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 077A3C636D3 for ; Tue, 31 Jan 2023 11:33:08 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 9123C10E330; Tue, 31 Jan 2023 11:32:59 +0000 (UTC) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id 17AF310E322; Tue, 31 Jan 2023 11:32:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675164777; x=1706700777; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=gJQpVSG9dZVHayAdIbR7SbqppXSv9xRlV3BEeeUUNgo=; b=DGBj/ZtDsx1PnAgDtYSlD5VyzW8p88DUnwIkyzLK7WhvyL7KDc20V4sV oukFyH7x4kFipTfrNbOFUzZWh37gcbbJPBhqAGq86UkidAD4pN99ypObd vB3GDvTG9hpEVuch5fsNqPmRWt05tH6+rGZ+ftUX+0LjfB19o7du9JQc+ lt+jn8XSgOwlvTIK8XtAxozLmmFBVzIhvqiI/WGw63xDiYaLJ+HW2sY9b AaErorK8da8buLhUtIP8RNX5XGghFD+zM9Bu9P6wrZmqbHnFwX4zuYGTH NcDRMI82rLfWiAsYNuxkfVj8cy0CqSpkR2niuDV01jJHoJuW+7PfwIvHS w==; X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="308159810" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="308159810" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:50 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="993235726" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="993235726" Received: from rdorchar-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.217.6]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:49 -0800 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Tue, 31 Jan 2023 11:32:32 +0000 Message-Id: <20230131113237.3707217-4-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230131113237.3707217-1-tvrtko.ursulin@linux.intel.com> References: <20230131113237.3707217-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 eabd43773f2d..c23a3fae9793 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -115,7 +115,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; @@ -140,6 +140,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)); @@ -286,16 +287,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; } /** @@ -348,10 +354,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; @@ -393,7 +399,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), @@ -412,7 +418,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 bced19adb055..ffa219c9c9fd 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 name[24]; /* Process name of the owning PID. */ char print_name[24]; /* Name without any non-printable characters. */ From patchwork Tue Jan 31 11:32: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: 13122912 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 A300CC38142 for ; Tue, 31 Jan 2023 11:33:09 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 02DF710E335; Tue, 31 Jan 2023 11:33:00 +0000 (UTC) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id 462ED10E31A; Tue, 31 Jan 2023 11:32:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675164777; x=1706700777; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=SFGLmHc7XVOfYnYhdlVGwKT2+NH6RYdNaTqCCMoIymk=; b=gpM5FgMRWLmoHPXvUEhFDhOlM2emfpPC0I7NAD1wxW+c8zABX+1NpqsC trCs9srciw96Bms9C3XBFif4LGHcSZ+X4ekY+4tSsMlRS3rq+6CDoWyYI BEbDoWwRKcSkb6y7edmmp285nxU2ssstQRkux0i0nGkJ//mjIMW9vNtPL OGQvNyBmjZWtVOtKo8hTLnXL4p4L8q4fEHyK3drheNwgVrplEvpQKbbZZ voL6Bde6H8fDs8dHkzAAO95Ay2l48BNV/9PVuQuUT/I/9vpsfpPZbR1DN XQiN3x3IyPIeYcgrIngdBozje6G57w2pDhP823Kv5/VOHxJV5TtnyAg+r A==; X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="308159815" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="308159815" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:52 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="993235729" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="993235729" Received: from rdorchar-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.217.6]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:51 -0800 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Tue, 31 Jan 2023 11:32:33 +0000 Message-Id: <20230131113237.3707217-5-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230131113237.3707217-1-tvrtko.ursulin@linux.intel.com> References: <20230131113237.3707217-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 c23a3fae9793..e11c8b18188f 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; } @@ -119,9 +120,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; @@ -411,11 +413,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 Tue Jan 31 11:32:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13122913 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 597ECC38142 for ; Tue, 31 Jan 2023 11:33:12 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 66F1910E343; Tue, 31 Jan 2023 11:33:03 +0000 (UTC) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id 5B62E10E32A; Tue, 31 Jan 2023 11:32:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675164777; x=1706700777; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=z2kT0bQZvocH3okAW+0SZfN2R5pq5QWrNUFJDy4VNMI=; b=dGKyGEb8FQx9a3c6ZpLZEwnGiZuRHUKVcxQfkEZwRMo4HnSjXcxb3zKN /t+L/CUReNF680tjZB949avkVfQL8mh6IT3TLfvR9OF4P2EXFS2Gu4F2R GvRJu+oV9yplDi+IynFvcYJm7sV4KKSKmvI1rWVJQf4lQbjrL80ooHtwP eqtcOWmiDBwaCSNzSgLGdBuyQ6nwzFV18UMZJ61SyKrD1WDReo/t3zyIg ibwB9wx0eO0zmAKQWP6B020sE1Fph5JnkySy5HFoKgjid2WsTUMzMmO/2 nUG7r55cQuApYHeM/V/QGCfff64ciKp1H9nRmDtfULsovjk36huRAIG51 w==; X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="308159821" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="308159821" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:53 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="993235732" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="993235732" Received: from rdorchar-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.217.6]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:52 -0800 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Tue, 31 Jan 2023 11:32:34 +0000 Message-Id: <20230131113237.3707217-6-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230131113237.3707217-1-tvrtko.ursulin@linux.intel.com> References: <20230131113237.3707217-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 68c89ad2c17e..b850d2210ae7 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 Tue Jan 31 11:32:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13122914 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 3B2B5C636CC for ; Tue, 31 Jan 2023 11:33:13 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 840CB10E34A; Tue, 31 Jan 2023 11:33:03 +0000 (UTC) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id 6DC3A10E32C; Tue, 31 Jan 2023 11:32:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675164777; x=1706700777; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=f5nD5I+AtijYfIMHp5JFXj2pRfnGZrkHAiWvHt8dq4o=; b=fUDiioWrjdUBvOj7oeugJoY9WraPUjnZ4VNQrOcV2frd/T46T9rdS5Wh Wksjk1MssMOOYfoBe5eN3vaKiK7vgNeCW+9QBVpbv5zMX1lZ2D6ZZkULF WIBmxug3cfO8ZlD4c8fk6fn/ItqU+LSAnpE62uFL45ln0mwqEIZ02tpAE ZWgo/F/kyHUsMBVN+ywMwcIvD9dx01kcwFFhriIdo4e9tUQbnPvZirdqq x/mRkHH4RCtZn3cfwfH9G/ZR0/sN9c+lWykRC+w+ezreliXZYJXPUmFmG SWbct3oa0gpylvBp1Z+KOl6cFnBtcF27ufu8MzEye8YV8Zf+BkD14Qih3 w==; X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="308159824" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="308159824" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:54 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="993235736" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="993235736" Received: from rdorchar-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.217.6]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:53 -0800 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Tue, 31 Jan 2023 11:32:35 +0000 Message-Id: <20230131113237.3707217-7-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230131113237.3707217-1-tvrtko.ursulin@linux.intel.com> References: <20230131113237.3707217-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 e11c8b18188f..d507c07fec87 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -97,7 +97,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]) @@ -119,6 +119,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)); @@ -144,8 +145,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); @@ -154,6 +175,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); @@ -323,7 +353,7 @@ static bool is_drm_fd(int fd_dir, const char *name, unsigned int *minor) * * 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 ffa219c9c9fd..0a903b431eaa 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. */ @@ -59,9 +62,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; - void *private_data; struct igt_drm_client *client; /* Must be last. */ diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index 2fb9f9b757d5..e64523df48e4 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -66,6 +66,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; @@ -84,7 +90,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; @@ -116,6 +122,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))) @@ -769,9 +780,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) { @@ -792,7 +802,8 @@ static struct igt_drm_clients *display_clients(struct igt_drm_clients *clients) ac->pid = c->pid; strcpy(ac->name, c->name); 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; @@ -807,7 +818,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]; } @@ -828,7 +839,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); @@ -1540,15 +1551,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; @@ -1720,6 +1731,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) { const char *pidname = " PID NAME "; unsigned int num_active = 0; @@ -1734,25 +1747,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; @@ -1767,7 +1780,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"); } @@ -1782,6 +1795,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) { @@ -1792,14 +1806,16 @@ print_client(struct igt_drm_client *c, struct engines *engines, double t, int li printf("%6u %17s ", c->pid, 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; - 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 / - clients->engine_class[i].num_engines; + iclients->classes.capacity[i]; /* * Guard against possible time-drift between sampling @@ -1827,11 +1843,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; @@ -1860,7 +1876,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(); } @@ -2077,14 +2095,54 @@ static void show_help_screen(void) 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; @@ -2097,6 +2155,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; @@ -2211,6 +2270,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, @@ -2239,20 +2300,16 @@ 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)); - codename = igt_device_get_pretty_name(&card, false); if (output_mode == JSON) printf("[\n"); @@ -2350,8 +2407,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 Tue Jan 31 11:32:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13122915 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 381D5C636D3 for ; Tue, 31 Jan 2023 11:33:14 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id EA35310E34B; Tue, 31 Jan 2023 11:33:03 +0000 (UTC) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id 37A3310E322; Tue, 31 Jan 2023 11:32:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675164778; x=1706700778; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=NhQD4dilGNkAIIK5bNGHtecepu+wvvMh4Z9yYMXd0nQ=; b=fS5wYQEmbyXxhRCuBZrbTeS2+cdMQob6rVM9SEmMNxh9+e0Yq9ig5rnh M6scjrZ2uy30VqS6IyLEYEJvRQFkj3IjV/VnVe9X3B7voEwwm3Ymt3YTi 9CKsGFUT0zXiKrmZq5D5LnF6GtKF7ERH5bVCFM/mKS01/0t2VTRg4DHCD MITcdQ+YW9yo94uFKCykDfuAZB2xbZB716DtdhhaMS2N2NaEebfUzWaMZ GkmDnpOl5MHm9W8Y05T5tHPOQn0yGpypWvirAR/6BiNizuS5xARp8QYjl zNd3wt6a10Z6TYrdCBvrMQcZy7P2n9RlSn07Cs1Ey0JWrIL105semP8tM A==; X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="308159830" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="308159830" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:56 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="993235740" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="993235740" Received: from rdorchar-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.217.6]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:54 -0800 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Tue, 31 Jan 2023 11:32:36 +0000 Message-Id: <20230131113237.3707217-8-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230131113237.3707217-1-tvrtko.ursulin@linux.intel.com> References: <20230131113237.3707217-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 d507c07fec87..b3eda39cd226 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -191,22 +191,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; @@ -214,8 +230,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 0a903b431eaa..df8022d42098 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -82,6 +82,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 e64523df48e4..44f0ca0c28c0 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -680,85 +680,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; @@ -1952,7 +1941,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 Tue Jan 31 11:32:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 13122916 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 1263AC38142 for ; Tue, 31 Jan 2023 11:33:15 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4FB5D10E34D; Tue, 31 Jan 2023 11:33:04 +0000 (UTC) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id 76D9A10E32D; Tue, 31 Jan 2023 11:32:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675164778; x=1706700778; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=vdJNYhh420Kb1pSoSOBhfN4nCjrcb1RISIdAea94MLU=; b=GogNH2BNe1eO1YhyAaKEHYxhCk0VB1OLak0R9K0XOIkoYNQ57lgh2aBn FlBFZ2BC2A4E2sEpEoGlYwzlRMT9arbhw4a6GAdlR4dEQlZk2Ogu6fv0O F31dlCS7mGU+EYZeBAdojorDK3OxPjvOYd7rD7HlkmVmpzwn9oCs38gLE YgS6W4VzZEO2ufAWWyp2W2WyQN8rVSZdjl7L5Gjfuy58WF51g9+ebCA3z +wFwO/jA0vJhR5DP667vAutZQ2sdBYGrkEFbp1XHfqBwoW69BKshx9A1e n4vkZVwLdspodU5DpbqpvEzg/R+AAnpArNcG0B8jHAOXr2aoEfgr3t2K4 w==; X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="308159832" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="308159832" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:57 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10606"; a="993235743" X-IronPort-AV: E=Sophos;i="5.97,261,1669104000"; d="scan'208";a="993235743" Received: from rdorchar-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.213.217.6]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Jan 2023 03:32:56 -0800 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org, Intel-gfx@lists.freedesktop.org Date: Tue, 31 Jan 2023 11:32:37 +0000 Message-Id: <20230131113237.3707217-9-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230131113237.3707217-1-tvrtko.ursulin@linux.intel.com> References: <20230131113237.3707217-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 | 260 ++++++++++++++++++++++++++++++++++++++++++++++ tools/meson.build | 5 + 2 files changed, 265 insertions(+) create mode 100644 tools/gputop.c diff --git a/tools/gputop.c b/tools/gputop.c new file mode 100644 index 000000000000..d259cac1ab17 --- /dev/null +++ b/tools/gputop.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2022 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) +{ + const char *pidname = " PID NAME "; + int ret, len = strlen(pidname); + + if (lines++ >= con_h || len >= con_w) + return lines; + printf("\033[7m"); + ret = printf("DRM minor %u", c->drm_minor); + n_spaces(con_w - ret); + + if (lines++ >= con_h) + return lines; + printf("\n%s", pidname); + + 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("%8u %17s ", c->pid, 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 c6194fd15daa..0a3973dee90d 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -65,6 +65,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,