From patchwork Thu Mar 31 14:03:37 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797144 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 7A853C4332F for ; Thu, 31 Mar 2022 14:04:20 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A34B210F1B8; Thu, 31 Mar 2022 14:04:14 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id 5AB7810F00F; Thu, 31 Mar 2022 14:04:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735453; x=1680271453; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=j8IDcifSkmYaG8hkR8nLt8wfi+CMQBqaU1z/y2kzYG4=; b=dGazgX8ro/2htkaUO6RmmKJJrwCfvzZlSQg8OObs8qxHv/BH05lqKCAf lafx7Nt76qQleaOqAUTHqHagmflmxtqoIITJ3WU/Z6pNmFj8X3RfYZsCK 3fk0kz3uQDJ9d7/qF+ujNqlafb9RbKNhGTnt7OfJ6EsUnAU9e2LDjFzwj HkwbpLoba0LkFdwMyaiWy8P/yzg85tCxpRznqUa5w+b9MnsuuLMK3Ku9s 9O+NOHrHqjJF7ObcbvJ+ctf1XBDQuKXrcDF+ckkfFPKLyVvGKzsmww4// x3VjMr9xs+zL2CS+KKJVChzrMvJ1GNiRpFgeZ/Kx8YvjTExrIM47Gce8Q g==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027191" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027191" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:00 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347171" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:03:59 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 01/12] lib: Helper library for parsing i915 fdinfo output Date: Thu, 31 Mar 2022 15:03:37 +0100 Message-Id: <20220331140348.2985832-2-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Tvrtko Ursulin Tests and intel_gpu_top will share common code for parsing this file. v2: * Fix key-value parsing if valid key line ends with ':'. * Add DRM_CLIENT_FDINFO_MAX_ENGINES. (Umesh) * Always zero terminate read buffer. (Umesh) Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_fdinfo.c | 186 +++++++++++++++++++++++++++++++++++++++++++ lib/igt_drm_fdinfo.h | 50 ++++++++++++ lib/meson.build | 7 ++ 3 files changed, 243 insertions(+) create mode 100644 lib/igt_drm_fdinfo.c create mode 100644 lib/igt_drm_fdinfo.h diff --git a/lib/igt_drm_fdinfo.c b/lib/igt_drm_fdinfo.c new file mode 100644 index 000000000000..c2462a108cf0 --- /dev/null +++ b/lib/igt_drm_fdinfo.c @@ -0,0 +1,186 @@ +/* + * Copyright © 2022 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmtest.h" + +#include "igt_drm_fdinfo.h" + +static size_t read_fdinfo(char *buf, const size_t sz, int at, const char *name) +{ + size_t count; + int fd; + + fd = openat(at, name, O_RDONLY); + if (fd < 0) + return 0; + + buf[sz - 1] = 0; + count = read(fd, buf, sz); + buf[sz - 1] = 0; + close(fd); + + return count; +} + +static int parse_engine(char *line, struct drm_client_fdinfo *info, + size_t prefix_len, uint64_t *val) +{ + static const char *e2class[] = { + "render", + "copy", + "video", + "video-enhance", + }; + ssize_t name_len; + char *name, *p; + int found = -1; + unsigned int i; + + p = index(line, ':'); + if (!p || p == line) + return -1; + + name_len = p - line - prefix_len; + if (name_len < 1) + return -1; + + name = line + prefix_len; + + for (i = 0; i < ARRAY_SIZE(e2class); i++) { + if (!strncmp(name, e2class[i], name_len)) { + found = i; + break; + } + } + + if (found >= 0) { + while (*++p && isspace(*p)); + *val = strtoull(p, NULL, 10); + } + + return found; +} + +static const char *find_kv(const char *buf, const char *key, size_t keylen) +{ + const char *p = buf; + + if (strncmp(buf, key, keylen)) + return NULL; + + p = index(buf, ':'); + if (!p || p == buf) + return NULL; + if ((p - buf) != keylen) + return NULL; + + p++; + while (*p && isspace(*p)) + p++; + + return *p ? p : NULL; +} + +bool +__igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info) +{ + char buf[4096], *_buf = buf; + char *l, *ctx = NULL; + unsigned int good = 0; + size_t count; + + count = read_fdinfo(buf, sizeof(buf), dir, fd); + if (!count) + return false; + + while ((l = strtok_r(_buf, "\n", &ctx))) { + uint64_t val = 0; + const char *v; + int idx; + + _buf = NULL; + + if ((v = find_kv(l, "drm-driver", strlen("drm-driver")))) { + strncpy(info->driver, v, sizeof(info->driver) - 1); + good++; + } else if ((v = find_kv(l, "drm-pdev", strlen("drm-pdev")))) { + strncpy(info->pdev, v, sizeof(info->pdev) - 1); + } else if ((v = find_kv(l, "drm-client-id", + strlen("drm-client-id")))) { + info->id = atol(v); + good++; + } else if (!strncmp(l, "drm-engine-", 11) && + strncmp(l, "drm-engine-capacity-", 20)) { + idx = parse_engine(l, info, strlen("drm-engine-"), + &val); + if (idx >= 0) { + if (!info->capacity[idx]) + info->capacity[idx] = 1; + info->busy[idx] = val; + info->num_engines++; + } + } else if (!strncmp(l, "drm-engine-capacity-", 20)) { + idx = parse_engine(l, info, + strlen("drm-engine-capacity-"), + &val); + if (idx >= 0) + info->capacity[idx] = val; + } + } + + if (good < 2 || !info->num_engines) + return false; /* fdinfo format not as expected */ + + return true; +} + +bool igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info) +{ + char fd[64]; + int dir, ret; + bool res; + + ret = snprintf(fd, sizeof(fd), "%u", drm_fd); + if (ret < 0 || ret == sizeof(fd)) + return false; + + dir = open("/proc/self/fdinfo", O_DIRECTORY | O_RDONLY); + if (dir < 0) + return false; + + res = __igt_parse_drm_fdinfo(dir, fd, info); + + close(dir); + + return res; +} diff --git a/lib/igt_drm_fdinfo.h b/lib/igt_drm_fdinfo.h new file mode 100644 index 000000000000..424e3efef5d4 --- /dev/null +++ b/lib/igt_drm_fdinfo.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2022 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef IGT_DRM_FDINFO_H +#define IGT_DRM_FDINFO_H + +#include +#include +#include +#include + +#define DRM_CLIENT_FDINFO_MAX_ENGINES 16 + +struct drm_client_fdinfo { + char driver[128]; + char pdev[128]; + unsigned long id; + + unsigned int num_engines; + unsigned int capacity[DRM_CLIENT_FDINFO_MAX_ENGINES]; + uint64_t busy[DRM_CLIENT_FDINFO_MAX_ENGINES]; +}; + +bool igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info); + +bool __igt_parse_drm_fdinfo(int dir, const char *fd, + struct drm_client_fdinfo *info); + +#endif /* IGT_DRM_FDINFO_H */ diff --git a/lib/meson.build b/lib/meson.build index 6fc1958604b3..ccee7a596561 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -18,6 +18,7 @@ lib_sources = [ 'igt_debugfs.c', 'igt_device.c', 'igt_device_scan.c', + 'igt_drm_fdinfo.c', 'igt_aux.c', 'igt_gt.c', 'igt_halffloat.c', @@ -218,6 +219,12 @@ 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_fdinfo_build = static_library('igt_drm_fdinfo', + ['igt_drm_fdinfo.c'], + include_directories : inc) + +lib_igt_drm_fdinfo = declare_dependency(link_with : lib_igt_drm_fdinfo_build, + include_directories : inc) i915_perf_files = [ 'igt_list.c', 'i915/perf.c', From patchwork Thu Mar 31 14:03:38 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797146 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 134D1C433F5 for ; Thu, 31 Mar 2022 14:04:34 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6570E10F1CB; Thu, 31 Mar 2022 14:04:16 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id 3467710EF8E; Thu, 31 Mar 2022 14:04:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735454; x=1680271454; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=bsBEajtAc+zuGAmp1jsUA6Sbrw/v/K8sQAWPKL1xGlI=; b=S3NdA7N800N9AZzzWUyPoJH+T2POWe2Ucisam8bD7O529i39c1z+5xwd Mf4pZyt8C5mrRI8oOo9E+5mT9pYKWx502qHUc8v07j94Sb7BsgvHQn6jZ SSkg9NFwp6gonLDFypNEbRD0OXNr129X3vYxvwhHs5bU/RvwYQVDkeev9 PsYxCjIt85kfRMG7zG/HM8IjmuYydVvn2eWnFrt5sDSl6EokVKfJoNRm/ 02UdrDZkgmLmUTwl0XbxubeW5bWcysn4vqZa1+t1st2yv3oaJz11orPVh VZQSgE8IvHTDYlI87i3/nJtDfUdES9FUiIv8o5nYBuOE88Sej+9EmsaZA g==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027207" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027207" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:02 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347195" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:00 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 02/12] tests/i915/drm_fdinfo: Basic and functional tests for GPU busyness exported via fdinfo Date: Thu, 31 Mar 2022 15:03:38 +0100 Message-Id: <20220331140348.2985832-3-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Tvrtko Ursulin Mostly inherited from the perf_pmu, some basic tests, and some tests to verify exported GPU busyness is as expected. Signed-off-by: Tvrtko Ursulin --- tests/i915/drm_fdinfo.c | 555 ++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 8 + 2 files changed, 563 insertions(+) create mode 100644 tests/i915/drm_fdinfo.c diff --git a/tests/i915/drm_fdinfo.c b/tests/i915/drm_fdinfo.c new file mode 100644 index 000000000000..e3b1ebb0f454 --- /dev/null +++ b/tests/i915/drm_fdinfo.c @@ -0,0 +1,555 @@ +/* + * Copyright © 2022 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include "igt.h" +#include "igt_core.h" +#include "igt_device.h" +#include "igt_drm_fdinfo.h" +#include "i915/gem.h" +#include "intel_ctx.h" + +IGT_TEST_DESCRIPTION("Test the i915 drm fdinfo data"); + +const double tolerance = 0.05f; +const unsigned long batch_duration_ns = 500e6; + +#define __assert_within_epsilon(x, ref, tol_up, tol_down) \ + igt_assert_f((double)(x) <= (1.0 + (tol_up)) * (double)(ref) && \ + (double)(x) >= (1.0 - (tol_down)) * (double)(ref), \ + "'%s' != '%s' (%f not within +%.1f%%/-%.1f%% tolerance of %f)\n",\ + #x, #ref, (double)(x), \ + (tol_up) * 100.0, (tol_down) * 100.0, \ + (double)(ref)) + +#define assert_within_epsilon(x, ref, tolerance) \ + __assert_within_epsilon(x, ref, tolerance, tolerance) + +static void basics(int i915, unsigned int num_classes) +{ + struct drm_client_fdinfo info = { }; + bool ret; + + ret = igt_parse_drm_fdinfo(i915, &info); + igt_assert(ret); + + igt_assert(!strcmp(info.driver, "i915")); + + igt_assert_eq(info.num_engines, num_classes); +} + +/* + * Helper for cases where we assert on time spent sleeping (directly or + * indirectly), so make it more robust by ensuring the system sleep time + * is within test tolerance to start with. + */ +static unsigned int measured_usleep(unsigned int usec) +{ + struct timespec ts = { }; + unsigned int slept; + + slept = igt_nsec_elapsed(&ts); + igt_assert(slept == 0); + do { + usleep(usec - slept); + slept = igt_nsec_elapsed(&ts) / 1000; + } while (slept < usec); + + return igt_nsec_elapsed(&ts); +} + +#define TEST_BUSY (1) +#define FLAG_SYNC (2) +#define TEST_TRAILING_IDLE (4) +#define FLAG_HANG (8) +#define TEST_ISOLATION (16) + +static igt_spin_t *__spin_poll(int fd, uint64_t ahnd, const intel_ctx_t *ctx, + const struct intel_execution_engine2 *e) +{ + struct igt_spin_factory opts = { + .ahnd = ahnd, + .ctx = ctx, + .engine = e->flags, + }; + + if (gem_class_can_store_dword(fd, e->class)) + opts.flags |= IGT_SPIN_POLL_RUN; + + return __igt_spin_factory(fd, &opts); +} + +static unsigned long __spin_wait(int fd, igt_spin_t *spin) +{ + struct timespec start = { }; + + igt_nsec_elapsed(&start); + + if (igt_spin_has_poll(spin)) { + unsigned long timeout = 0; + + while (!igt_spin_has_started(spin)) { + unsigned long t = igt_nsec_elapsed(&start); + + igt_assert(gem_bo_busy(fd, spin->handle)); + if ((t - timeout) > 250e6) { + timeout = t; + igt_warn("Spinner not running after %.2fms\n", + (double)t / 1e6); + igt_assert(t < 2e9); + } + } + } else { + igt_debug("__spin_wait - usleep mode\n"); + usleep(500e3); /* Better than nothing! */ + } + + igt_assert(gem_bo_busy(fd, spin->handle)); + return igt_nsec_elapsed(&start); +} + +static igt_spin_t *__spin_sync(int fd, uint64_t ahnd, const intel_ctx_t *ctx, + const struct intel_execution_engine2 *e) +{ + igt_spin_t *spin = __spin_poll(fd, ahnd, ctx, e); + + __spin_wait(fd, spin); + + return spin; +} + +static igt_spin_t *spin_sync(int fd, uint64_t ahnd, const intel_ctx_t *ctx, + const struct intel_execution_engine2 *e) +{ + igt_require_gem(fd); + + return __spin_sync(fd, ahnd, ctx, e); +} + +static void end_spin(int fd, igt_spin_t *spin, unsigned int flags) +{ + if (!spin) + return; + + igt_spin_end(spin); + + if (flags & FLAG_SYNC) + gem_sync(fd, spin->handle); + + if (flags & TEST_TRAILING_IDLE) { + unsigned long t, timeout = 0; + struct timespec start = { }; + + igt_nsec_elapsed(&start); + + do { + t = igt_nsec_elapsed(&start); + + if (gem_bo_busy(fd, spin->handle) && + (t - timeout) > 10e6) { + timeout = t; + igt_warn("Spinner not idle after %.2fms\n", + (double)t / 1e6); + } + + usleep(1e3); + } while (t < batch_duration_ns / 5); + } +} + +static uint64_t read_busy(int i915, unsigned int class) +{ + struct drm_client_fdinfo info = { }; + + igt_assert(igt_parse_drm_fdinfo(i915, &info)); + + return info.busy[class]; +} + +static void +single(int gem_fd, const intel_ctx_t *ctx, + const struct intel_execution_engine2 *e, unsigned int flags) +{ + unsigned long slept; + igt_spin_t *spin; + uint64_t val; + int spin_fd; + uint64_t ahnd; + + if (flags & TEST_ISOLATION) { + spin_fd = gem_reopen_driver(gem_fd); + ctx = intel_ctx_create_all_physical(spin_fd); + } else { + spin_fd = gem_fd; + } + + ahnd = get_reloc_ahnd(spin_fd, ctx->id); + + if (flags & TEST_BUSY) + spin = spin_sync(spin_fd, ahnd, ctx, e); + else + spin = NULL; + + val = read_busy(gem_fd, e->class); + slept = measured_usleep(batch_duration_ns / 1000); + if (flags & TEST_TRAILING_IDLE) + end_spin(spin_fd, spin, flags); + val = read_busy(gem_fd, e->class) - val; + + if (flags & FLAG_HANG) + igt_force_gpu_reset(spin_fd); + else + end_spin(spin_fd, spin, FLAG_SYNC); + + assert_within_epsilon(val, + (flags & TEST_BUSY) && !(flags & TEST_ISOLATION) ? + slept : 0.0f, + tolerance); + + /* Check for idle after hang. */ + if (flags & FLAG_HANG) { + gem_quiescent_gpu(spin_fd); + igt_assert(!gem_bo_busy(spin_fd, spin->handle)); + + val = read_busy(gem_fd, e->class); + slept = measured_usleep(batch_duration_ns / 1000); + val = read_busy(gem_fd, e->class) - val; + + assert_within_epsilon(val, 0, tolerance); + } + + igt_spin_free(spin_fd, spin); + put_ahnd(ahnd); + + gem_quiescent_gpu(spin_fd); +} + +static void log_busy(unsigned int num_engines, uint64_t *val) +{ + char buf[1024]; + int rem = sizeof(buf); + unsigned int i; + char *p = buf; + + for (i = 0; i < num_engines; i++) { + int len; + + len = snprintf(p, rem, "%u=%" PRIu64 "\n", i, val[i]); + igt_assert(len > 0); + rem -= len; + p += len; + } + + igt_info("%s", buf); +} + +static void read_busy_all(int i915, uint64_t *val) +{ + struct drm_client_fdinfo info = { }; + + igt_assert(igt_parse_drm_fdinfo(i915, &info)); + + memcpy(val, info.busy, sizeof(info.busy)); +} + +static void +busy_check_all(int gem_fd, const intel_ctx_t *ctx, + const struct intel_execution_engine2 *e, + const unsigned int num_engines, + const unsigned int classes[16], const unsigned int num_classes, + unsigned int flags) +{ + uint64_t ahnd = get_reloc_ahnd(gem_fd, ctx->id); + uint64_t tval[2][16]; + unsigned long slept; + uint64_t val[16]; + igt_spin_t *spin; + unsigned int i; + + memset(tval, 0, sizeof(tval)); + + spin = spin_sync(gem_fd, ahnd, ctx, e); + + read_busy_all(gem_fd, tval[0]); + slept = measured_usleep(batch_duration_ns / 1000); + if (flags & TEST_TRAILING_IDLE) + end_spin(gem_fd, spin, flags); + read_busy_all(gem_fd, tval[1]); + + end_spin(gem_fd, spin, FLAG_SYNC); + igt_spin_free(gem_fd, spin); + put_ahnd(ahnd); + + for (i = 0; i < num_classes; i++) + val[i] = tval[1][i] - tval[0][i]; + + log_busy(num_classes, val); + + for (i = 0; i < num_classes; i++) { + double target = i == e->class ? slept : 0.0f; + + assert_within_epsilon(val[i], target, tolerance); + } + + gem_quiescent_gpu(gem_fd); +} + +static void +__submit_spin(int gem_fd, igt_spin_t *spin, + const struct intel_execution_engine2 *e, + int offset) +{ + struct drm_i915_gem_execbuffer2 eb = spin->execbuf; + + eb.flags &= ~(0x3f | I915_EXEC_BSD_MASK); + eb.flags |= e->flags | I915_EXEC_NO_RELOC; + eb.batch_start_offset += offset; + + gem_execbuf(gem_fd, &eb); +} + +static void +most_busy_check_all(int gem_fd, const intel_ctx_t *ctx, + const struct intel_execution_engine2 *e, + const unsigned int num_engines, + const unsigned int classes[16], + const unsigned int num_classes, + unsigned int flags) +{ + uint64_t ahnd = get_reloc_ahnd(gem_fd, ctx->id); + unsigned int busy_class[num_classes]; + struct intel_execution_engine2 *e_; + igt_spin_t *spin = NULL; + uint64_t tval[2][16]; + unsigned long slept; + uint64_t val[16]; + unsigned int i; + + memset(busy_class, 0, sizeof(busy_class)); + memset(tval, 0, sizeof(tval)); + + for_each_ctx_engine(gem_fd, ctx, e_) { + if (e->class == e_->class && e->instance == e_->instance) { + continue; + } else if (spin) { + __submit_spin(gem_fd, spin, e_, 64); + busy_class[e_->class]++; + } else { + spin = __spin_poll(gem_fd, ahnd, ctx, e_); + busy_class[e_->class]++; + } + } + igt_require(spin); /* at least one busy engine */ + + /* Small delay to allow engines to start. */ + usleep(__spin_wait(gem_fd, spin) * num_engines / 1e3); + + read_busy_all(gem_fd, tval[0]); + slept = measured_usleep(batch_duration_ns / 1000); + if (flags & TEST_TRAILING_IDLE) + end_spin(gem_fd, spin, flags); + read_busy_all(gem_fd, tval[1]); + + end_spin(gem_fd, spin, FLAG_SYNC); + igt_spin_free(gem_fd, spin); + put_ahnd(ahnd); + + for (i = 0; i < num_classes; i++) + val[i] = tval[1][i] - tval[0][i]; + + log_busy(num_classes, val); + + for (i = 0; i < num_classes; i++) { + double target = slept * busy_class[i]; + + assert_within_epsilon(val[i], target, tolerance); + } + gem_quiescent_gpu(gem_fd); +} + +static void +all_busy_check_all(int gem_fd, const intel_ctx_t *ctx, + const unsigned int num_engines, + const unsigned int classes[16], + const unsigned int num_classes, + unsigned int flags) +{ + uint64_t ahnd = get_reloc_ahnd(gem_fd, ctx->id); + unsigned int busy_class[num_classes]; + struct intel_execution_engine2 *e; + igt_spin_t *spin = NULL; + uint64_t tval[2][16]; + unsigned long slept; + uint64_t val[16]; + unsigned int i; + + memset(busy_class, 0, sizeof(busy_class)); + memset(tval, 0, sizeof(tval)); + + for_each_ctx_engine(gem_fd, ctx, e) { + if (spin) + __submit_spin(gem_fd, spin, e, 64); + else + spin = __spin_poll(gem_fd, ahnd, ctx, e); + busy_class[e->class]++; + } + + /* Small delay to allow engines to start. */ + usleep(__spin_wait(gem_fd, spin) * num_engines / 1e3); + + read_busy_all(gem_fd, tval[0]); + slept = measured_usleep(batch_duration_ns / 1000); + if (flags & TEST_TRAILING_IDLE) + end_spin(gem_fd, spin, flags); + read_busy_all(gem_fd, tval[1]); + + end_spin(gem_fd, spin, FLAG_SYNC); + igt_spin_free(gem_fd, spin); + put_ahnd(ahnd); + + for (i = 0; i < num_classes; i++) + val[i] = tval[1][i] - tval[0][i]; + + log_busy(num_classes, val); + + for (i = 0; i < num_classes; i++) { + double target = slept * busy_class[i]; + + assert_within_epsilon(val[i], target, tolerance); + } + gem_quiescent_gpu(gem_fd); +} + +#define test_each_engine(T, i915, ctx, e) \ + igt_subtest_with_dynamic(T) for_each_ctx_engine(i915, ctx, e) \ + igt_dynamic_f("%s", e->name) + +igt_main +{ + unsigned int num_engines = 0, num_classes = 0; + const struct intel_execution_engine2 *e; + unsigned int classes[16] = { }; + const intel_ctx_t *ctx = NULL; + int i915 = -1; + + igt_fixture { + unsigned int i; + + i915 = __drm_open_driver(DRIVER_INTEL); + + igt_require_gem(i915); + + ctx = intel_ctx_create_all_physical(i915); + + for_each_ctx_engine(i915, ctx, e) { + num_engines++; + igt_assert(e->class < ARRAY_SIZE(classes)); + classes[e->class]++; + } + igt_require(num_engines); + + for (i = 0; i < ARRAY_SIZE(classes); i++) { + if (classes[i]) + num_classes++; + } + igt_assert(num_classes); + } + + /** + * Test basic fdinfo content. + */ + igt_subtest("basics") + basics(i915, num_classes); + + /** + * Test that engines show no load when idle. + */ + test_each_engine("idle", i915, ctx, e) + single(i915, ctx, e, 0); + + /** + * Test that a single engine reports load correctly. + */ + test_each_engine("busy", i915, ctx, e) + single(i915, ctx, e, TEST_BUSY); + + test_each_engine("busy-idle", i915, ctx, e) + single(i915, ctx, e, TEST_BUSY | TEST_TRAILING_IDLE); + + test_each_engine("busy-hang", i915, ctx, e) { + igt_hang_t hang = igt_allow_hang(i915, ctx->id, 0); + + single(i915, ctx, e, TEST_BUSY | FLAG_HANG); + + igt_disallow_hang(i915, hang); + } + + /** + * Test that when one engine is loaded other report no + * load. + */ + test_each_engine("busy-check-all", i915, ctx, e) + busy_check_all(i915, ctx, e, num_engines, classes, num_classes, + TEST_BUSY); + + test_each_engine("busy-idle-check-all", i915, ctx, e) + busy_check_all(i915, ctx, e, num_engines, classes, num_classes, + TEST_BUSY | TEST_TRAILING_IDLE); + + /** + * Test that when all except one engine are loaded all + * loads are correctly reported. + */ + test_each_engine("most-busy-check-all", i915, ctx, e) + most_busy_check_all(i915, ctx, e, num_engines, + classes, num_classes, + TEST_BUSY); + + test_each_engine("most-busy-idle-check-all", i915, ctx, e) + most_busy_check_all(i915, ctx, e, num_engines, + classes, num_classes, + TEST_BUSY | TEST_TRAILING_IDLE); + + /** + * Test that when all engines are loaded all loads are + * correctly reported. + */ + igt_subtest("all-busy-check-all") + all_busy_check_all(i915, ctx, num_engines, classes, num_classes, + TEST_BUSY); + + igt_subtest("all-busy-idle-check-all") + all_busy_check_all(i915, ctx, num_engines, classes, num_classes, + TEST_BUSY | TEST_TRAILING_IDLE); + + /** + * Test for no cross-client contamination. + */ + test_each_engine("isolation", i915, ctx, e) + single(i915, ctx, e, TEST_BUSY | TEST_ISOLATION); + + igt_fixture { + intel_ctx_destroy(i915, ctx); + close(i915); + } +} diff --git a/tests/meson.build b/tests/meson.build index a152d2a05291..b0eab3d6c8e3 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -293,6 +293,14 @@ foreach prog : i915_progs test_list += prog endforeach +test_executables += executable('drm_fdinfo', + join_paths('i915', 'drm_fdinfo.c'), + dependencies : test_deps + [ lib_igt_drm_fdinfo ], + install_dir : libexecdir, + install_rpath : libexecdir_rpathdir, + install : true) +test_list += 'drm_fdinfo' + test_executables += executable('dumb_buffer', 'dumb_buffer.c', dependencies : test_deps + [ libatomic ], install_dir : libexecdir, From patchwork Thu Mar 31 14:03:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797150 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 94D46C433EF for ; Thu, 31 Mar 2022 14:04:52 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6C62910F41E; Thu, 31 Mar 2022 14:04:25 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id ADB7E10F213; Thu, 31 Mar 2022 14:04:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735456; x=1680271456; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=vryLocoYrqJD4dC7FQKbjCdd4H3DHemms3sjt4ygzFA=; b=Rr1mD/h9OOFV1DS+GbhU3PmgzyOJQq06uKU4wOjYQ0ppJPAqRkuwRfjv dKr4T7xZfES5SF3DfxvyhulRgkHxmevd+oxiy6qkuP+z86w9zz5YI4tUU 2pUrInrRcTWPkzSmqYzSfLEU2zSYv67brroIlIUGCyVt0OuDgAi/2BuIa DxXrkx8cOBjqRf0hlSSy3QzLyRuiGZ++sAkHkRfcQNTC7KVVHUygstNYC F8bKjC4e/A3AfQT0fNumd9Bp+2mtRdCiLsQ/WSiTOI5pZf1XRpIrV2TOF pZ9WrGclATnRg2HfuGoVf5am2e/TTmLxItHuzCtFlVf2I3ICge9tbxOp4 Q==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027222" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027222" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:04 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347220" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:02 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 03/12] intel-gpu-top: Add support for per client stats Date: Thu, 31 Mar 2022 15:03:39 +0100 Message-Id: <20220331140348.2985832-4-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Tvrtko Ursulin Use the i915 exported data in /proc//fdinfo to show GPU utilization per DRM client. Example of the output: intel-gpu-top: Intel Tigerlake (Gen12) @ /dev/dri/card0 - 220/ 221 MHz 70% RC6; 0.62/ 7.08 W; 760 irqs/s ENGINES BUSY MI_SEMA MI_WAIT Render/3D 23.06% |██████▊ | 0% 0% Blitter 0.00% | | 0% 0% Video 5.40% |█▋ | 0% 0% VideoEnhance 20.67% |██████ | 0% 0% PID NAME Render/3D Blitter Video VideoEnhance 3082 mpv | || ||▌ ||██ | 3117 neverball |█▉ || || || | 1 systemd |▍ || || || | 2338 gnome-shell | || || || | Signed-off-by: Tvrtko Ursulin --- man/intel_gpu_top.rst | 4 + tools/intel_gpu_top.c | 801 +++++++++++++++++++++++++++++++++++++++++- tools/meson.build | 2 +- 3 files changed, 804 insertions(+), 3 deletions(-) diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst index b3b765b05feb..f4dbfc5b44d9 100644 --- a/man/intel_gpu_top.rst +++ b/man/intel_gpu_top.rst @@ -56,6 +56,10 @@ Supported keys: 'q' Exit from the tool. 'h' Show interactive help. '1' Toggle between aggregated engine class and physical engine mode. + 'n' Toggle display of numeric client busyness overlay. + 's' Toggle between sort modes (runtime, total runtime, pid, client id). + 'i' Toggle display of clients which used no GPU time. + 'H' Toggle between per PID aggregation and individual clients. DEVICE SELECTION ================ diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index bc11fce2bb1e..73815cdea8aa 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -43,8 +43,10 @@ #include #include #include +#include #include "igt_perf.h" +#include "igt_drm_fdinfo.h" #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) @@ -311,7 +313,8 @@ static int engine_cmp(const void *__a, const void *__b) return a->instance - b->instance; } -#define is_igpu_pci(x) (strcmp(x, "0000:00:02.0") == 0) +#define IGPU_PCI "0000:00:02.0" +#define is_igpu_pci(x) (strcmp(x, IGPU_PCI) == 0) #define is_igpu(x) (strcmp(x, "i915") == 0) static struct engines *discover_engines(char *device) @@ -635,6 +638,547 @@ 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, uint64_t val[16]) +{ + 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++) { + if (val[i] < c->last[i]) + continue; /* It will catch up soon. */ + + c->total_runtime += val[i]; + c->val[i] = val[i] - c->last[i]; + c->last_runtime += c->val[i]; + c->last[i] = val[i]; + } + + c->samples++; + c->status = ALIVE; +} + +static void +add_client(struct clients *clients, unsigned int id, unsigned int pid, + char *name, uint64_t busy[16]) +{ + struct client *c; + + assert(!find_client(clients, ALIVE, 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 = 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, busy); +} + +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; + long tot_a, tot_b; + + /* + * Sort clients in descending order of runtime in the previous sampling + * period for active ones, followed by inactive. Tie-breaker is client + * id. + */ + + tot_a = a->status == ALIVE ? a->last_runtime : -1; + tot_b = b->status == ALIVE ? b->last_runtime : -1; + + tot_b -= tot_a; + if (tot_b > 0) + return 1; + if (tot_b < 0) + return -1; + + return (int)b->id - a->id; +} + +static int client_total_cmp(const void *_a, const void *_b) +{ + const struct client *a = _a; + const struct 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_b -= tot_a; + if (tot_b > 0) + return 1; + if (tot_b < 0) + return -1; + + return (int)b->id - a->id; +} + +static int client_id_cmp(const void *_a, const void *_b) +{ + const struct client *a = _a; + const struct 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_b -= id_a; + if (id_b > 0) + return 1; + if (id_b < 0) + return -1; + + return (int)b->id - a->id; +} + +static int client_pid_cmp(const void *_a, const void *_b) +{ + const struct client *a = _a; + const struct 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_b -= pid_a; + if (pid_b > 0) + return -1; + if (pid_b < 0) + return 1; + + return (int)a->id - b->id; +} + +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) +{ + struct client *ac, *c, *cp = NULL; + struct 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); + + aggregated = calloc(1, sizeof(*clients)); + assert(aggregated); + + ac = calloc(clients->num_clients, sizeof(*c)); + assert(ac); + + aggregated->num_classes = clients->num_classes; + aggregated->class = clients->class; + aggregated->client = ac; + + for_each_client(clients, c, tmp) { + unsigned int i; + + if (c->status == FREE) + break; + + assert(c->status == ALIVE); + + if ((cp && c->pid != cp->pid) || !cp) { + ac = &aggregated->client[num++]; + + /* New pid. */ + ac->clients = aggregated; + ac->status = ALIVE; + ac->id = -c->pid; + ac->pid = c->pid; + strcpy(ac->name, c->name); + strcpy(ac->print_name, c->print_name); + ac->val = calloc(clients->num_classes, + sizeof(ac->val[0])); + assert(ac->val); + ac->samples = 1; + } + + cp = c; + + if (c->samples < 2) + continue; + + ac->samples = 2; /* All what matters for display. */ + ac->total_runtime += c->total_runtime; + ac->last_runtime += c->last_runtime; + + for (i = 0; i < clients->num_classes; i++) + ac->val[i] += c->val[i]; + } + + aggregated->num_clients = num; + aggregated->active_clients = num; + + clients = aggregated; + +out: + return sort_clients(clients, client_cmp); +} + +static void free_clients(struct clients *clients) +{ + struct client *c; + unsigned int tmp; + + for_each_client(clients, c, tmp) { + free(c->val); + free(c->last); + } + + free(clients->client); + free(clients); +} + +static bool is_drm_fd(DIR *fd_dir, const char *name) +{ + struct stat stat; + int ret; + + ret = fstatat(dirfd(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; + + len = --e - ++s + 1; + if(!len || (len + 1) >= sz) + return false; + + strncpy(out, s, len); + out[len] = 0; + + return true; +} + +static DIR *opendirat(DIR *at, const char *name) +{ + DIR *dir; + int fd; + + fd = openat(dirfd(at), name, O_DIRECTORY); + if (fd < 0) + return NULL; + + dir = fdopendir(fd); + if (!dir) + close(fd); + + return dir; +} + +static FILE *fropenat(DIR *at, const char *name) +{ + FILE *f; + int fd; + + fd = openat(dirfd(at), name, O_RDONLY); + if (fd < 0) + return NULL; + + f = fdopen(fd, "r"); + if (!f) + close(fd); + + return f; +} + +static size_t freadat2buf(char *buf, const size_t sz, DIR *at, const char *name) +{ + size_t count; + FILE *f; + + f = fropenat(at, name); + if (!f) + return 0; + + memset(buf, 0, sz); + count = fread(buf, 1, sz, f); + fclose(f); + + return count; +} + +static struct clients *scan_clients(struct clients *clients) +{ + 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) { + DIR *pid_dir = NULL, *fd_dir = NULL, *fdinfo_dir = NULL; + struct dirent *fdinfo_dent; + char client_name[64] = { }; + unsigned int client_pid; + char buf[4096]; + size_t count; + + if (proc_dent->d_type != DT_DIR) + continue; + if (!isdigit(proc_dent->d_name[0])) + continue; + + pid_dir = opendirat(proc_dir, proc_dent->d_name); + if (!pid_dir) + continue; + + count = freadat2buf(buf, sizeof(buf), pid_dir, "stat"); + 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 = opendirat(pid_dir, "fd"); + if (!fd_dir) + 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.id, client_pid, + client_name, info.busy); + else + update_client(c, client_pid, client_name, + info.busy); + } + +next: + if (fdinfo_dir) + closedir(fdinfo_dir); + if (fd_dir) + closedir(fd_dir); + if (pid_dir) + closedir(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_clients(clients); +} + static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" }; static void n_spaces(const unsigned int n) @@ -776,6 +1320,18 @@ json_close_struct(void) fflush(stdout); } +static void +__json_add_member(const char *key, const char *val) +{ + assert(json_indent_level < ARRAY_SIZE(json_indent)); + + fprintf(out, "%s%s\"%s\": \"%s\"", + json_struct_members ? ",\n" : "", + json_indent[json_indent_level], key, val); + + json_struct_members++; +} + static unsigned int json_add_member(const struct cnt_group *parent, struct cnt_item *item, unsigned int headers) @@ -1501,6 +2057,157 @@ print_engines(struct engines *engines, double t, int lines, int w, int h) return lines; } +static int +print_clients_header(struct clients *clients, int lines, + int con_w, int con_h, int *class_w) +{ + if (output_mode == INTERACTIVE) { + const char *pidname = " PID NAME "; + unsigned int num_active = 0; + int len = strlen(pidname); + + if (lines++ >= con_h) + return lines; + + printf("\033[7m"); + printf("%s", pidname); + + if (lines++ >= con_h || len >= con_w) + return lines; + + if (clients->num_classes) { + unsigned int i; + int width; + + for (i = 0; i < clients->num_classes; i++) { + if (clients->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; + int name_len = strlen(name); + int pad = (width - name_len) / 2; + int spaces = width - pad - name_len; + + if (!clients->class[i].num_engines) + continue; /* Assert in the ideal world. */ + + 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"); + } else { + if (clients->num_classes) + pops->open_struct("clients"); + } + + return lines; +} + +static bool numeric_clients; +static bool filter_idle; + +static int +print_client(struct 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; + unsigned int i; + + if (output_mode == INTERACTIVE) { + if (filter_idle && (!c->total_runtime || c->samples < 2)) + return lines; + + lines++; + + printf("%6u %17s ", c->pid, c->print_name); + + for (i = 0; c->samples > 1 && i < clients->num_classes; i++) { + double pct; + + if (!clients->class[i].num_engines) + continue; /* Assert in the ideal world. */ + + pct = (double)c->val[i] / period_us / 1e3 * 100 / + clients->class[i].num_engines; + + /* + * Guard against possible time-drift between sampling + * client data and time we obtained our time-delta from + * PMU. + */ + if (pct > 100.0) + pct = 100.0; + + print_percentage_bar(pct, *class_w, numeric_clients); + } + + putchar('\n'); + } else if (output_mode == JSON) { + char buf[64]; + + snprintf(buf, sizeof(buf), "%u", c->id); + pops->open_struct(buf); + + __json_add_member("name", c->print_name); + + snprintf(buf, sizeof(buf), "%u", c->pid); + __json_add_member("pid", buf); + + if (c->samples > 1) { + pops->open_struct("engine-classes"); + + for (i = 0; i < clients->num_classes; i++) { + double pct; + + snprintf(buf, sizeof(buf), "%s", + clients->class[i].name); + pops->open_struct(buf); + + pct = (double)c->val[i] / period_us / 1e3 * 100; + snprintf(buf, sizeof(buf), "%f", pct); + __json_add_member("busy", buf); + + __json_add_member("unit", "%"); + + pops->close_struct(); + } + + pops->close_struct(); + } + + pops->close_struct(); + } + + return lines; +} + +static int +print_clients_footer(struct clients *clients, double t, + int lines, int con_w, int con_h) +{ + if (output_mode == INTERACTIVE) { + if (lines++ < con_h) + printf("\n"); + } else { + if (clients->num_classes) + pops->close_struct(); + } + + return lines; +} + static void restore_term(void) { tcsetattr(STDIN_FILENO, TCSANOW, &termios_orig); @@ -1565,6 +2272,31 @@ static void interactive_stdin(void) assert(ret == 0); } +static void select_client_sort(void) +{ + struct { + int (*cmp)(const void *, const void *); + const char *msg; + } cmp[] = { + { client_last_cmp, "Sorting clients by current GPU usage." }, + { client_total_cmp, "Sorting clients by accummulated GPU usage." }, + { client_pid_cmp, "Sorting clients by pid." }, + { client_id_cmp, "Sorting clients by DRM id." }, + }; + static unsigned int client_sort; + +bump: + if (++client_sort >= ARRAY_SIZE(cmp)) + client_sort = 0; + + client_cmp = cmp[client_sort].cmp; + header_msg = cmp[client_sort].msg; + + /* Sort by client id makes no sense with pid aggregation. */ + if (aggregate_pids && client_cmp == client_id_cmp) + goto bump; +} + static bool in_help; static void process_help_stdin(void) @@ -1607,9 +2339,29 @@ static void process_normal_stdin(void) else header_msg = "Showing physical engines."; break; + case 'i': + filter_idle ^= true; + if (filter_idle) + header_msg = "Hiding inactive clients."; + else + header_msg = "Showing inactive clients."; + break; + case 'n': + numeric_clients ^= true; + break; + case 's': + select_client_sort(); + break; case 'h': in_help = true; break; + case 'H': + aggregate_pids ^= true; + if (aggregate_pids) + header_msg = "Aggregating clients."; + else + header_msg = "Showing individual clients."; + break; }; } } @@ -1637,6 +2389,10 @@ static void show_help_screen(void) printf( "Help for interactive commands:\n\n" " '1' Toggle between aggregated engine class and physical engine mode.\n" +" 'n' Toggle display of numeric client busyness overlay.\n" +" 's' Toggle between sort modes (runtime, total runtime, pid, client id).\n" +" 'i' Toggle display of clients which used no GPU time.\n" +" 'H' Toggle between per PID aggregation and individual clients.\n" "\n" " 'h' or 'q' Exit interactive help.\n" "\n"); @@ -1645,6 +2401,7 @@ static void show_help_screen(void) int main(int argc, char **argv) { unsigned int period_us = DEFAULT_PERIOD_MS * 1000; + struct clients *clients = NULL; int con_w = -1, con_h = -1; char *output_path = NULL; struct engines *engines; @@ -1783,15 +2540,24 @@ int main(int argc, char **argv) ret = EXIT_SUCCESS; + clients = init_clients(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; + } pmu_sample(engines); + scan_clients(clients); codename = igt_device_get_pretty_name(&card, false); while (!stop_top) { + struct clients *disp_clients; bool consumed = false; + int j, lines = 0; struct winsize ws; - int lines = 0; + struct client *c; double t; /* Update terminal size. */ @@ -1810,6 +2576,8 @@ int main(int argc, char **argv) pmu_sample(engines); t = (double)(engines->ts.cur - engines->ts.prev) / 1e9; + disp_clients = scan_clients(clients); + if (stop_top) break; @@ -1829,12 +2597,41 @@ int main(int argc, char **argv) lines = print_engines(engines, t, lines, con_w, con_h); + if (disp_clients) { + int class_w; + + lines = print_clients_header(disp_clients, lines, + con_w, con_h, + &class_w); + + for_each_client(disp_clients, c, j) { + assert(c->status != PROBE); + if (c->status != ALIVE) + break; /* Active clients are first in the array. */ + + if (lines >= con_h) + break; + + lines = print_client(c, engines, t, + lines, con_w, + con_h, period_us, + &class_w); + } + + lines = print_clients_footer(disp_clients, t, + lines, con_w, + con_h); + } + pops->close_struct(); } if (stop_top) break; + if (disp_clients != clients) + free_clients(disp_clients); + if (output_mode == INTERACTIVE) process_stdin(period_us); else diff --git a/tools/meson.build b/tools/meson.build index b6b9753463a9..771d0b9e3d5d 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -91,7 +91,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,math]) + dependencies : [lib_igt_perf,lib_igt_device_scan,lib_igt_drm_fdinfo,math]) executable('amd_hdmi_compliance', 'amd_hdmi_compliance.c', dependencies : [tool_deps], From patchwork Thu Mar 31 14:03:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797152 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 2099EC433F5 for ; Thu, 31 Mar 2022 14:04:56 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 0FC3989115; Thu, 31 Mar 2022 14:04:29 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id 1D6B710F234; Thu, 31 Mar 2022 14:04:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735458; x=1680271458; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=W2A2KVAd7dam9mGbLllRuGBoxc9d9am1UTZhrjI5kg0=; b=jnA6C1y77nzC2lve7n92i4dWRUQzZ9ODQgyPNo57LCXYVDIxxXMI2wBa sopDp7xnXyHNTaYjhHyAWkua/E6U8ArLvWWXvmD3OODbe6TsUuudE6igH pLxcv0x1lbm7ZOpNZbyYp7hfCbl5OHFxaDwS75E8qOl4NVT6ljfTXDLe+ d92v9b8P96Q4da34EL0PsGO48l+ixqIS1gm87tMWN5zzSx2JpkqjwNOXw K5HSIkqVZiUx45NkBzOKRF1IK0fUO9Nzyf9aLEeC9H3lHxtjdZ3XDx5ki xQJOCPNy18FW3DcgnaJdeDYbkXnQZr9qs6l1No4IN+m8QjD+FAZDdzWne g==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027234" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027234" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:05 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347235" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:04 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 04/12] lib: Extract igt_drm_clients from intel_gpu_top Date: Thu, 31 Mar 2022 15:03:40 +0100 Message-Id: <20220331140348.2985832-5-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Tvrtko Ursulin Code movement with some improvements to prepare for further work in making a vendor agnostic gputop tool possible. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 386 +++++++++++++++++++++++++++++++ lib/igt_drm_clients.h | 102 +++++++++ lib/meson.build | 8 + tools/intel_gpu_top.c | 516 ++++++------------------------------------ tools/meson.build | 2 +- 5 files changed, 570 insertions(+), 444 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..58d82648d821 --- /dev/null +++ b/lib/igt_drm_clients.c @@ -0,0 +1,386 @@ +/* + * Copyright © 2022 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "igt_drm_clients.h" +#include "igt_drm_fdinfo.h" + +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; +} + +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; +} + +void +igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, + uint64_t val[16]) +{ + 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++) { + if (val[i] < c->last[i]) + continue; /* It will catch up soon. */ + + c->total_runtime += val[i]; + c->val[i] = val[i] - c->last[i]; + c->last_runtime += c->val[i]; + c->last[i] = val[i]; + } + + c->samples++; + c->status = IGT_DRM_CLIENT_ALIVE; +} + +void +igt_drm_client_add(struct igt_drm_clients *clients, unsigned int id, + unsigned int pid, char *name, uint64_t busy[16]) +{ + struct igt_drm_client *c; + + assert(!igt_drm_clients_find(clients, IGT_DRM_CLIENT_ALIVE, id)); + + c = igt_drm_clients_find(clients, IGT_DRM_CLIENT_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 = 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, busy); +} + +void igt_drm_client_free(struct igt_drm_client *c) +{ + free(c->val); + free(c->last); + memset(c, 0, sizeof(*c)); +} + +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; + + 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; +} + +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); + + 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(char *buf, const size_t sz, int at, const char *name) +{ + size_t count; + int fd; + + fd = openat(at, name, O_RDONLY); + if (fd < 0) + return 0; + + buf[sz - 1] = 0; + count = read(fd, buf, sz); + close(fd); + + return count; +} + +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; + + len = --e - ++s + 1; + 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; +} + +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; + + 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(buf, sizeof(buf), pid_dir, "stat"); + 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; + + memset(&info, 0, sizeof(info)); + + 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.id, client_pid, + client_name, info.busy); + else + igt_drm_client_update(c, client_pid, + client_name, info.busy); + } + +next: + if (fdinfo_dir) + closedir(fdinfo_dir); + if (fd_dir >= 0) + close(fd_dir); + if (pid_dir >= 0) + close(pid_dir); + } + + closedir(proc_dir); + + igt_for_each_drm_client(clients, c, tmp) { + if (c->status == IGT_DRM_CLIENT_PROBE) + igt_drm_client_free(c); + 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..f52080847291 --- /dev/null +++ b/lib/igt_drm_clients.h @@ -0,0 +1,102 @@ +/* + * Copyright © 2022 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef IGT_DRM_CLIENTS_H +#define IGT_DRM_CLIENTS_H + +#include + +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; + + enum igt_drm_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 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_client * +igt_drm_clients_find(struct igt_drm_clients *clients, + enum igt_drm_client_status status, + unsigned int id); + +struct igt_drm_clients * +igt_drm_clients_sort(struct igt_drm_clients *clients, + int (*cmp)(const void *, const void *)); + +void igt_drm_client_update(struct igt_drm_client *c, + unsigned int pid, char *name, uint64_t val[16]); + +void igt_drm_client_add(struct igt_drm_clients *clients, unsigned int id, + unsigned int pid, char *name, uint64_t busy[16]); + +void igt_drm_client_free(struct igt_drm_client *c); + +#endif /* IGT_DRM_CLIENTS_H */ diff --git a/lib/meson.build b/lib/meson.build index ccee7a596561..6a1475fdafda 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -18,6 +18,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', @@ -219,6 +220,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 73815cdea8aa..c4f990b2a7d0 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; @@ -638,160 +633,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, uint64_t val[16]) -{ - 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++) { - if (val[i] < c->last[i]) - continue; /* It will catch up soon. */ - - c->total_runtime += val[i]; - c->val[i] = val[i] - c->last[i]; - c->last_runtime += c->val[i]; - c->last[i] = val[i]; - } - - c->samples++; - c->status = ALIVE; -} - -static void -add_client(struct clients *clients, unsigned int id, unsigned int pid, - char *name, uint64_t busy[16]) -{ - struct client *c; - - assert(!find_client(clients, ALIVE, 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 = 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, busy); -} - -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; /* @@ -800,8 +645,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) @@ -814,12 +659,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) @@ -832,12 +677,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) @@ -850,12 +695,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) @@ -868,56 +713,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); @@ -926,23 +734,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) || !cp) { 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); @@ -972,213 +781,21 @@ 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; - unsigned int tmp; + struct igt_drm_client *c; + int tmp; - for_each_client(clients, c, tmp) { + igt_for_each_drm_client(clients, c, tmp) free(c->val); - free(c->last); - } free(clients->client); free(clients); } -static bool is_drm_fd(DIR *fd_dir, const char *name) -{ - struct stat stat; - int ret; - - ret = fstatat(dirfd(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; - - len = --e - ++s + 1; - if(!len || (len + 1) >= sz) - return false; - - strncpy(out, s, len); - out[len] = 0; - - return true; -} - -static DIR *opendirat(DIR *at, const char *name) -{ - DIR *dir; - int fd; - - fd = openat(dirfd(at), name, O_DIRECTORY); - if (fd < 0) - return NULL; - - dir = fdopendir(fd); - if (!dir) - close(fd); - - return dir; -} - -static FILE *fropenat(DIR *at, const char *name) -{ - FILE *f; - int fd; - - fd = openat(dirfd(at), name, O_RDONLY); - if (fd < 0) - return NULL; - - f = fdopen(fd, "r"); - if (!f) - close(fd); - - return f; -} - -static size_t freadat2buf(char *buf, const size_t sz, DIR *at, const char *name) -{ - size_t count; - FILE *f; - - f = fropenat(at, name); - if (!f) - return 0; - - memset(buf, 0, sz); - count = fread(buf, 1, sz, f); - fclose(f); - - return count; -} - -static struct clients *scan_clients(struct clients *clients) -{ - 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) { - DIR *pid_dir = NULL, *fd_dir = NULL, *fdinfo_dir = NULL; - struct dirent *fdinfo_dent; - char client_name[64] = { }; - unsigned int client_pid; - char buf[4096]; - size_t count; - - if (proc_dent->d_type != DT_DIR) - continue; - if (!isdigit(proc_dent->d_name[0])) - continue; - - pid_dir = opendirat(proc_dir, proc_dent->d_name); - if (!pid_dir) - continue; - - count = freadat2buf(buf, sizeof(buf), pid_dir, "stat"); - 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 = opendirat(pid_dir, "fd"); - if (!fd_dir) - 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.id, client_pid, - client_name, info.busy); - else - update_client(c, client_pid, client_name, - info.busy); - } - -next: - if (fdinfo_dir) - closedir(fdinfo_dir); - if (fd_dir) - closedir(fd_dir); - if (pid_dir) - closedir(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_clients(clients); -} - static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" }; static void n_spaces(const unsigned int n) @@ -1881,15 +1498,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; @@ -1916,7 +1533,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); } @@ -2058,7 +1675,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) { @@ -2080,19 +1697,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) @@ -2119,10 +1736,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) { @@ -2136,11 +1753,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 @@ -2172,7 +1789,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; @@ -2194,7 +1811,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) { @@ -2398,10 +2015,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; int con_w = -1, con_h = -1; char *output_path = NULL; struct engines *engines; @@ -2540,24 +2168,24 @@ int main(int argc, char **argv) ret = EXIT_SUCCESS; - 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); + igt_drm_clients_scan(clients, client_match); codename = igt_device_get_pretty_name(&card, false); 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. */ @@ -2576,7 +2204,9 @@ int main(int argc, char **argv) pmu_sample(engines); t = (double)(engines->ts.cur - engines->ts.prev) / 1e9; - disp_clients = scan_clients(clients); + disp_clients = + display_clients(igt_drm_clients_scan(clients, + client_match)); if (stop_top) break; @@ -2604,9 +2234,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) @@ -2630,7 +2260,7 @@ int main(int argc, char **argv) break; if (disp_clients != clients) - free_clients(disp_clients); + free_display_clients(disp_clients); if (output_mode == INTERACTIVE) process_stdin(period_us); diff --git a/tools/meson.build b/tools/meson.build index 771d0b9e3d5d..e65aceeef9fa 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -91,7 +91,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 Thu Mar 31 14:03:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797149 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 1BCBAC43219 for ; Thu, 31 Mar 2022 14:04:50 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 9F28210F3F5; Thu, 31 Mar 2022 14:04:22 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id 8CDB910F3D5; Thu, 31 Mar 2022 14:04:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735458; x=1680271458; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=aLw/i3dZuRmOSJJSqGtQtUAAEHFtWKzv5NUSHEru36A=; b=eHCmSo3NjUrnqxBYSskQ7Bt8otDqTgyK98ARawMHdb58xsgtHjEapb1m 9uTe2RG0izoiQBvO06eHIDAB/CSSuL2+YJquploUUjk47thvPu/4EeFJi RgWReJYjhTzV6NRf/bGEReCMPOnEoJtFykLbK0ezXgRQPgJl+zjirSuDO sdkffU3jwz4Ad1a5ICbm6RQGde2vlr54XgcqiTwQDWc9WugyobkTNYTpd Gp4jhTF3LavDipS0dKknTiQmodozJn13b2B4J4v7SO2EUPj9cLXMKV6pf wyp/uf6TP5VqRUKpk9DOFFD/lL4viZhzEKunc5w5QfBRcVt6lDOJw2D0y Q==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027243" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027243" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:07 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347259" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:05 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 05/12] libdrmfdinfo: Allow specifying custom engine map Date: Thu, 31 Mar 2022 15:03:41 +0100 Message-Id: <20220331140348.2985832-6-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 7 +++--- lib/igt_drm_clients.h | 3 ++- lib/igt_drm_fdinfo.c | 47 +++++++++++++++++++++++++++-------------- lib/igt_drm_fdinfo.h | 7 ++++-- tests/i915/drm_fdinfo.c | 16 +++++++++++--- tools/intel_gpu_top.c | 13 ++++++++++-- 6 files changed, 66 insertions(+), 27 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index 58d82648d821..591602f4c9f5 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -269,7 +269,8 @@ static bool is_drm_fd(int fd_dir, const char *name) 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; @@ -343,8 +344,8 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, memset(&info, 0, sizeof(info)); 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 f52080847291..91e9da4c0733 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -80,7 +80,8 @@ void igt_drm_clients_free(struct igt_drm_clients *clients); struct igt_drm_clients * igt_drm_clients_scan(struct igt_drm_clients *clients, bool (*filter_client)(const struct igt_drm_clients *, - const struct drm_client_fdinfo *)); + const struct drm_client_fdinfo *), + const char **name_map, unsigned int map_entries); struct igt_drm_client * igt_drm_clients_find(struct igt_drm_clients *clients, diff --git a/lib/igt_drm_fdinfo.c b/lib/igt_drm_fdinfo.c index c2462a108cf0..d5c66881ed43 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) } bool -__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; } @@ -164,7 +178,8 @@ __igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info) return true; } -bool igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info) +bool igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info, + const char **name_map, unsigned int map_entries) { char fd[64]; int dir, ret; @@ -178,7 +193,7 @@ bool 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 424e3efef5d4..4d14e7931532 100644 --- a/lib/igt_drm_fdinfo.h +++ b/lib/igt_drm_fdinfo.h @@ -39,12 +39,15 @@ 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]; }; -bool igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info); +bool igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info, + const char **name_map, unsigned int map_entries); bool __igt_parse_drm_fdinfo(int dir, const char *fd, - struct drm_client_fdinfo *info); + 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 e3b1ebb0f454..d1053a53ae2c 100644 --- a/tests/i915/drm_fdinfo.c +++ b/tests/i915/drm_fdinfo.c @@ -34,6 +34,13 @@ 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", +}; + #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), \ @@ -50,7 +57,8 @@ static void basics(int i915, unsigned int num_classes) struct drm_client_fdinfo info = { }; bool 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")); @@ -181,7 +189,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]; } @@ -267,7 +276,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)); } diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index c4f990b2a7d0..99e8e1d8ffd4 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -2030,6 +2030,12 @@ 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", + }; int con_w = -1, con_h = -1; char *output_path = NULL; struct engines *engines; @@ -2177,7 +2183,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); while (!stop_top) { @@ -2206,7 +2213,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))); if (stop_top) break; From patchwork Thu Mar 31 14:03:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797153 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 6B7F1C43217 for ; Thu, 31 Mar 2022 14:04:58 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 16E1E8911A; Thu, 31 Mar 2022 14:04:29 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id BD52D10F3E6; Thu, 31 Mar 2022 14:04:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735458; x=1680271458; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=JrPPcF8Z/g19JKyVRgayx1eXG/HFkPd8TCdk6t/qE70=; b=OGsAsiJLLx39hKiyYYwX8IRJvdHjaglvY0Z3dIyPE5Jh8SdwcO5sY0co Z6EG2bIzq1xv3EMp0pCP3dRW05rY4idF9eChxccFO72WJ10UiBl1Ci5Mx 7bFSgPOzmf8Eg7RCIp8Ex388A9lK8xZfk8KUovhA61/IcAqkMbJ/m5OwB 299oAuc+ig5Xw6300fdhSGkqWvoMLUl9m6ux5bd7Y5b3Xy9ThFsZnlyyL 81ED0kTu/icrpRVcIkUCflfRFc+uRp46hw9V6ui4r3Pci3szPYjxCHZI1 gUtxHxnzvJWtHPU2bj+s4UvicW8XYde1cJnk1zc7tK2JrP0Jg6h1Tm41l g==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027250" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027250" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:08 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347285" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:07 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 06/12] libdrmclients: Record client drm minor Date: Thu, 31 Mar 2022 15:03:42 +0100 Message-Id: <20220331140348.2985832-7-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 | 33 ++++++++++++++++++++------------- lib/igt_drm_clients.h | 6 ++++-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index 591602f4c9f5..116479a9f363 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -117,12 +117,13 @@ igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, } void -igt_drm_client_add(struct igt_drm_clients *clients, unsigned int id, - unsigned int pid, char *name, uint64_t busy[16]) +igt_drm_client_add(struct igt_drm_clients *clients, + struct drm_client_fdinfo *info, + unsigned int pid, char *name, unsigned int drm_minor) { struct igt_drm_client *c; - assert(!igt_drm_clients_find(clients, IGT_DRM_CLIENT_ALIVE, id)); + 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) { @@ -137,13 +138,14 @@ igt_drm_client_add(struct igt_drm_clients *clients, unsigned int id, memset(c, 0, (clients->num_clients - idx) * sizeof(*c)); } - c->id = id; + 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)); assert(c->val && c->last); - igt_drm_client_update(c, pid, name, busy); + igt_drm_client_update(c, pid, name, info->busy); } void igt_drm_client_free(struct igt_drm_client *c) @@ -254,16 +256,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; } struct igt_drm_clients * @@ -293,10 +300,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; @@ -338,7 +345,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; memset(&info, 0, sizeof(info)); @@ -358,8 +365,8 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, c = igt_drm_clients_find(clients, IGT_DRM_CLIENT_PROBE, info.id); if (!c) - igt_drm_client_add(clients, info.id, client_pid, - client_name, info.busy); + igt_drm_client_add(clients, &info, client_pid, + client_name, minor); else igt_drm_client_update(c, client_pid, client_name, info.busy); diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h index 91e9da4c0733..7a6318c6af5f 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -48,6 +48,7 @@ struct igt_drm_client { enum igt_drm_client_status status; unsigned int id; + unsigned int drm_minor; unsigned int pid; char name[24]; char print_name[24]; @@ -95,8 +96,9 @@ igt_drm_clients_sort(struct igt_drm_clients *clients, void igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, uint64_t val[16]); -void igt_drm_client_add(struct igt_drm_clients *clients, unsigned int id, - unsigned int pid, char *name, uint64_t busy[16]); +void igt_drm_client_add(struct igt_drm_clients *clients, + struct drm_client_fdinfo *, + unsigned int pid, char *name, unsigned int drm_minor); void igt_drm_client_free(struct igt_drm_client *c); From patchwork Thu Mar 31 14:03:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797154 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 DEE2AC433EF for ; Thu, 31 Mar 2022 14:04:59 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DEF4789194; Thu, 31 Mar 2022 14:04:29 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id F3B4310F21D; Thu, 31 Mar 2022 14:04:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735459; x=1680271459; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=/3T8I3j+Efc5imHVVXY8ChABE/kooCa0kMx4cvT/1BI=; b=Cf7b1eA92hFlUNArCVIwGYCzuNaXJZUeXPs+hhmDQSoaXnS5qBCF8+iC Rk7yMCpD+mcS+jyUKmUhwhYiz6NJYaZ6iN5IUICz8JveuJqTD4WYxk0gC aI2I5o/lGHPVlF91xXX/p0T5II9ZF8tSv84LVRESixjaafXNYdpFe3ck+ 3QUf3qusq2lHXwEW4GnQlGnrQfFjcuqO5EazvOY0p5XIGBxtaDqLcE6iz W1ni0Gj/04mkXKa0+VUscrXrr0e5UIHr8z07w44hYgH9w+Kdgk2S1IojX bFyGO3wCWxXXZpV9Vliwu/dFnW6uexHPu4ueG2qobD92zW4aScT6d9H0F Q==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027258" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027258" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:09 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347307" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:08 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 07/12] libdrmclient: Support multiple DRM cards Date: Thu, 31 Mar 2022 15:03:43 +0100 Message-Id: <20220331140348.2985832-8-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Tvrtko Ursulin Require DRM minor match during client lookup. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 14 ++++++++------ lib/igt_drm_clients.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index 116479a9f363..cf044597d537 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -57,7 +57,7 @@ struct igt_drm_clients *igt_drm_clients_init(void *private_data) 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; @@ -69,7 +69,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; } @@ -123,9 +124,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; @@ -359,11 +361,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); diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h index 7a6318c6af5f..6da445eab34a 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -87,7 +87,7 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, 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); struct igt_drm_clients * igt_drm_clients_sort(struct igt_drm_clients *clients, From patchwork Thu Mar 31 14:03:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797148 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 AC34EC433F5 for ; Thu, 31 Mar 2022 14:04:48 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D400910F3FA; Thu, 31 Mar 2022 14:04:22 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id E8EA110F3E7; Thu, 31 Mar 2022 14:04:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735460; x=1680271460; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=D5S4W/HNVTQ+KLF9VD20tKomJ0DvLFrzK2wte8e4wbs=; b=S0KhYHrLIn1ZNxUBo9ipNW9NoJqLbtOvbHx7LasD59lCNlXgSZpAVqij fv2mk3vG+Rq5eM5S+T5tUWLmH8G9ZFKcpHSoDN6jeBSWjyg1mkyQFZJE+ m7vBwFg5lqtuiDMkQCF0+Wh4sd838ts9tEPew4yn0akL0ZaKAChPY3b85 goT8jpk1s0vDCCAFJqkTZCCyQlFDGq+u294Vcj2nzqtqFMfHFeYP+RZXD 5DjfyuqshVfv4KBLNbiFISGDyzRrp0wHYZQMdsHmmL2Gr1DeQvjvE9RRX PWfRkPBNiDmYl64XL8tGBMSgSCT5jmkni2fZbi6a3Gmhf35EdthKrniz7 A==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027262" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027262" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:11 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347326" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:10 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 08/12] libdrmfdinfo: Track largest engine index Date: Thu, 31 Mar 2022 15:03:44 +0100 Message-Id: <20220331140348.2985832-9-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 d5c66881ed43..3926cbd25ecf 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 4d14e7931532..e567600fe53b 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 Thu Mar 31 14:03:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797147 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 79915C433EF for ; Thu, 31 Mar 2022 14:04:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3FC7610F403; Thu, 31 Mar 2022 14:04:23 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id EC2D810F3E8; Thu, 31 Mar 2022 14:04:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735460; x=1680271460; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=9QbY9cIMI/UV2FGPHqrZ1zT2QMBADl5533ZRvbwaGFo=; b=FfRqLYkgc3EhDwO4amByZmC+/9L0TNu3wCCInPpZ8zOVDYBXi42hgRWU vrIhp91opct35JkgM0WXPDn42Kzbian0lMNIqkdWHLlbgfqyW03tKmcJt 17b5eHFPGhDETY/ILaZwl+Mzj6obnqB5tCDmAcWKUteRsmnvT0QtcNN/Z JSN0aFCvjQHAGEx7KIxypeakuUT4BAp7QQyzyZdX8q9krzw6QvZ/v/RUS BnxcZxITq8NJQ7omc2LuVCgPiTqFJrayH2IFii3FP3MOc772AgE8Dwan5 p2uPqH/RwUXN/z2rpalcSsHnZQafu+v5sS8n412K5rx1M0XQi/yWygsJF g==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027277" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027277" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:12 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347342" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:11 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 09/12] libdrmclient/intel_gpu_top: Decouple hardcoded engine assumptions Date: Thu, 31 Mar 2022 15:03:45 +0100 Message-Id: <20220331140348.2985832-10-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 | 34 +++++++++++-- lib/igt_drm_clients.h | 11 ++--- tools/intel_gpu_top.c | 112 ++++++++++++++++++++++++++++++------------ 3 files changed, 117 insertions(+), 40 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index cf044597d537..6299f3632b74 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -103,7 +103,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++) { if (val[i] < c->last[i]) continue; /* It will catch up soon. */ @@ -123,6 +123,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)); @@ -143,8 +144,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->busy); @@ -152,6 +173,13 @@ igt_drm_client_add(struct igt_drm_clients *clients, void igt_drm_client_free(struct igt_drm_client *c) { + unsigned int i; + + 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); memset(c, 0, sizeof(*c)); diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h index 6da445eab34a..4668df2d7fd6 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -35,10 +35,11 @@ enum igt_drm_client_status { IGT_DRM_CLIENT_PROBE }; -struct igt_drm_client_engine_class { - unsigned int engine_class; - const char *name; +struct igt_drm_client_engines { unsigned int num_engines; + unsigned int max_engine_id; + unsigned int *capacity; + char **names; }; struct igt_drm_clients; @@ -47,6 +48,7 @@ struct igt_drm_client { struct igt_drm_clients *clients; enum igt_drm_client_status status; + struct igt_drm_client_engines *engines; unsigned int id; unsigned int drm_minor; unsigned int pid; @@ -63,9 +65,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 99e8e1d8ffd4..fb7ec9ae6d74 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))) @@ -733,9 +744,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) { @@ -756,7 +766,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; @@ -771,7 +782,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]; } @@ -1498,15 +1509,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; @@ -1678,6 +1689,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; @@ -1692,25 +1705,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; @@ -1725,7 +1738,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"); } @@ -1740,6 +1753,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) { @@ -1750,14 +1764,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 @@ -1785,11 +1801,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; @@ -1818,7 +1834,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(); } @@ -2018,14 +2036,47 @@ 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); + } +} + int main(int argc, char **argv) { unsigned int period_us = DEFAULT_PERIOD_MS * 1000; @@ -2036,6 +2087,7 @@ int main(int argc, char **argv) "video", "video-enhance", }; + struct intel_clients iclients; int con_w = -1, con_h = -1; char *output_path = NULL; struct engines *engines; @@ -2146,6 +2198,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, @@ -2174,18 +2228,14 @@ int main(int argc, char **argv) ret = EXIT_SUCCESS; - 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; - } + + 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); while (!stop_top) { struct igt_drm_clients *disp_clients; From patchwork Thu Mar 31 14:03:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797156 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 B1D72C433F5 for ; Thu, 31 Mar 2022 14:05:03 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 00D20890D3; Thu, 31 Mar 2022 14:04:29 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id A168E10F3F0; Thu, 31 Mar 2022 14:04:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735460; x=1680271460; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=k8oXj9pkWUwvSGfVFpbikXm0TD++sjG6Zkk2XcP66oI=; b=UiR8xCOoCphqMNt8oFnrlq/8hfW4DDOd/9kt5zOJ0ST1QfamT+bW1w82 aipqAInAPfZYoup7oJ9udt/+ShmJ7PBdANCR2Lgz64VF5bKVJXplyn1d6 ajPD2z4r+rbA3jQUYjwp7OS0blmaXNUgtGZK3b/L+GSpHdASZYgYXpbj0 q5mrKC6xavLwUczzQZ8XXfOee606MCLQcuFps5ApJRuDbJcxPThCchSqo Zg6ccrO2Bme4Yz9kHuwOIyBo4vgz+dzT8tjnXNlbLDPt3ZyS1CJ1q7KXa 9bS9m/OdegZ65PRSzqd2cGvAmqwcMMdv32eZvnI0Ys9vjcGDJvv6uKyEA w==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027289" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027289" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:14 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347356" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:12 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 10/12] libdrmclient: Enforce client status sort order in the library Date: Thu, 31 Mar 2022 15:03:46 +0100 Message-Id: <20220331140348.2985832-11-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 | 25 +++++++++++-- lib/igt_drm_clients.h | 2 +- tools/intel_gpu_top.c | 81 +++++++++++++++++++------------------------ 3 files changed, 58 insertions(+), 50 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index 6299f3632b74..e8e86969f984 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -185,10 +185,29 @@ void igt_drm_client_free(struct igt_drm_client *c) 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; +} + 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; @@ -196,8 +215,8 @@ igt_drm_clients_sort(struct igt_drm_clients *clients, if (!clients) return clients; - qsort(clients->client, clients->num_clients, sizeof(*clients->client), - cmp); + 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 4668df2d7fd6..47547aa8b75f 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -90,7 +90,7 @@ igt_drm_clients_find(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 *)); void igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, uint64_t val[16]); diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index fb7ec9ae6d74..fcc4e5501d3d 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -644,85 +644,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; @@ -1910,7 +1899,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 Thu Mar 31 14:03:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797155 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 5842FC433FE for ; Thu, 31 Mar 2022 14:05:01 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DD0BB89191; Thu, 31 Mar 2022 14:04:29 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id 1AAD910F3F5; Thu, 31 Mar 2022 14:04:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735461; x=1680271461; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=swj5SXsn4gJVTmUEenT3MRID7PSQknmpIH/17D6TzQw=; b=barCBQY1l/almNNA0coLYTNNMXboiNhory2AnAZvHRJmPmARiipvATQ1 ireWI7he36QIWwaX/0O1OhkcqyoxfsG/yjXpIQbMaVrt/ekKgDsxY2Cs4 NJIwQZyTmDzZGf5i5A5vZsCm7KgiK804TQn5u8jzitB1HoMqxziAC6Ass KDiCr2Z61N4snTGOpXGg3Sg7nBCto242MxwYtmto14W6hKeyhPUB5uZTi REdbwYzQEHoko12nE2Q+2xyZXkWrg2+1d68AzB20R+o7HkC+wwQFb2VaU aiEjiagi+gsGTf0yvzHzCl/UCGFODSF/7om/29T6EFC29ISGs31c7a6Mb w==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027293" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027293" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:15 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347368" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:14 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 11/12] libdrmclient: Unexport igt_drm_client_update Date: Thu, 31 Mar 2022 15:03:47 +0100 Message-Id: <20220331140348.2985832-12-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Tvrtko Ursulin It is currently unused so no need to export it as API for now. Also change the signature to take struct drm_client_fdinfo in order to avoid needing to pass in a sized array. Signed-off-by: Tvrtko Ursulin --- lib/igt_drm_clients.c | 16 ++++++++-------- lib/igt_drm_clients.h | 3 --- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/igt_drm_clients.c b/lib/igt_drm_clients.c index e8e86969f984..32e1d7a0f20e 100644 --- a/lib/igt_drm_clients.c +++ b/lib/igt_drm_clients.c @@ -77,9 +77,9 @@ igt_drm_clients_find(struct igt_drm_clients *clients, return NULL; } -void +static void igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, - uint64_t val[16]) + const struct drm_client_fdinfo *info) { unsigned int i; @@ -104,13 +104,13 @@ igt_drm_client_update(struct igt_drm_client *c, unsigned int pid, char *name, c->total_runtime = 0; for (i = 0; i <= c->engines->max_engine_id; i++) { - if (val[i] < c->last[i]) + if (info->busy[i] < c->last[i]) continue; /* It will catch up soon. */ - c->total_runtime += val[i]; - c->val[i] = val[i] - c->last[i]; + c->total_runtime += info->busy[i]; + c->val[i] = info->busy[i] - c->last[i]; c->last_runtime += c->val[i]; - c->last[i] = val[i]; + c->last[i] = info->busy[i]; } c->samples++; @@ -168,7 +168,7 @@ igt_drm_client_add(struct igt_drm_clients *clients, 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->busy); + igt_drm_client_update(c, pid, name, info); } void igt_drm_client_free(struct igt_drm_client *c) @@ -418,7 +418,7 @@ igt_drm_clients_scan(struct igt_drm_clients *clients, client_name, minor); else igt_drm_client_update(c, client_pid, - client_name, info.busy); + client_name, &info); } next: diff --git a/lib/igt_drm_clients.h b/lib/igt_drm_clients.h index 47547aa8b75f..6817acc34e81 100644 --- a/lib/igt_drm_clients.h +++ b/lib/igt_drm_clients.h @@ -92,9 +92,6 @@ struct igt_drm_clients * igt_drm_clients_sort(struct igt_drm_clients *clients, int (*cmp)(const void *, const void *, void *)); -void igt_drm_client_update(struct igt_drm_client *c, - unsigned int pid, char *name, uint64_t val[16]); - void igt_drm_client_add(struct igt_drm_clients *clients, struct drm_client_fdinfo *, unsigned int pid, char *name, unsigned int drm_minor); From patchwork Thu Mar 31 14:03:48 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 12797151 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 4201EC433F5 for ; Thu, 31 Mar 2022 14:04:54 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 42FC989061; Thu, 31 Mar 2022 14:04:28 +0000 (UTC) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id AA6AC10F2DC; Thu, 31 Mar 2022 14:04:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1648735461; x=1680271461; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=aTVrt2cqXjYUP16ctlEazT6xf2B1Gz3HeJDo6NhzfT8=; b=LUEWxM+TzDZcxE7ErV4gUXnnF11YhBIT6ACJLR0MgpOgVdjROA0I2Tjz Kf1MsawGL8bLBt9KkZotLEoVuYOnkTR1InXJCuhCeESe8hO+6IEWND9VD sQHamBSqNVUJIuMlTMBxBRQ2SesVVPCgVzTMafhwgKfr7kizr5GQWThHO keisI5kn5Htj6yHecKMhHk8XT/AHHOTvd3eWDZIuP6Bakyd+VyeZJtaJK yQZKRB9t85ZypxjEQBOZzsie7IdTYNUxJL1NleVwz/hLR+/Wasr9qwiMS u+KcF9Tl9xITJPLflsU8B4toLfV98WsPowVGcUKU9NKDPi71xPBMY9KJ9 Q==; X-IronPort-AV: E=McAfee;i="6200,9189,10302"; a="260027307" X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="260027307" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:17 -0700 X-IronPort-AV: E=Sophos;i="5.90,225,1643702400"; d="scan'208";a="547347378" Received: from juanniex-mobl.ger.corp.intel.com (HELO tursulin-mobl2.home) ([10.213.215.247]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Mar 2022 07:04:15 -0700 From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Subject: [PATCH i-g-t 12/12] gputop: Basic vendor agnostic GPU top tool Date: Thu, 31 Mar 2022 15:03:48 +0100 Message-Id: <20220331140348.2985832-13-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> References: <20220331140348.2985832-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Rob Clark , Intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Tvrtko Ursulin Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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. Signed-off-by: Tvrtko Ursulin Cc: Rob Clark --- tools/gputop.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++ tools/meson.build | 5 + 2 files changed, 281 insertions(+) create mode 100644 tools/gputop.c diff --git a/tools/gputop.c b/tools/gputop.c new file mode 100644 index 000000000000..1a30d03753c4 --- /dev/null +++ b/tools/gputop.c @@ -0,0 +1,276 @@ +/* + * Copyright © 2022 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#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" + +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) + +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 bool filter_idle = true; + +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; + + if (filter_idle && (!c->total_runtime || c->samples < 2)) + return lines; + + 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 possible time-drift between sampling + * client data and time we obtained our time-delta from + * PMU. + */ + 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; + + 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; + + 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 e65aceeef9fa..508964004bb0 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -69,6 +69,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,