Re: [igt-dev] [PATCH i-g-t v6 3/3] Introduce device selection lsgpu tool

Arkadiusz Hiler
2019-09-10T11:48:58Z

On Wed, Sep 04, 2019 at 12:37:01PM +0200, Zbigniew Kempczyński wrote:
> Tool uses device selection API to scan and display GPU devices.
> It can be used to check filter correctness as well as order
> of filter applying (.igtrc, IGT_DEVICE and --device argument).
>
> Signed-off-by: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com>
> Cc: Arkadiusz Hiler <arkadiusz.hiler@intel.com>
> Cc: Daniel Vetter <daniel@ffwll.ch>
> Cc: Petri Latvala <petri.latvala@intel.com>
> ---
> tools/Makefile.sources | 1 +
> tools/lsgpu.c | 299 +++++++++++++++++++++++++++++++++++++++++
> tools/meson.build | 1 +
> 3 files changed, 301 insertions(+)
> create mode 100644 tools/lsgpu.c
>
> diff --git a/tools/Makefile.sources b/tools/Makefile.sources
> index 50706f41..0e67b654 100644
> --- a/tools/Makefile.sources
> +++ b/tools/Makefile.sources
> @@ -33,6 +33,7 @@ tools_prog_lists = \
> intel_watermark \
> intel_gem_info \
> intel_gvtg_test \
> + lsgpu \
> $(NULL)
>
> dist_bin_SCRIPTS = intel_gpu_abrt
> diff --git a/tools/lsgpu.c b/tools/lsgpu.c
> new file mode 100644
> index 00000000..964bcf88
> --- /dev/null
> +++ b/tools/lsgpu.c
> @@ -0,0 +1,299 @@
> +/*
> + * Copyright © 2019 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_device_scan.h"
> +#include "igt.h"
> +#include <sys/ioctl.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <signal.h>
> +#include <glib.h>
> +
> +/**
> + * SECTION:lsgpu
> + * @short_description: lsgpu
> + * @title: lsgpu
> + * @include: lsgpu.c
> + *
> + * # lsgpu
> + *
> + * The devices can be scanned and displayed using 'lsgpu' tool. Tool also
> + * displays properties and sysattrs (-p switch, means print detail) which
> + * can be used during filter implementation.
> + *
> + * Tool can also be used to try out filters.
> + * To select device use '-d' or '--device' argument like:
> + *
> + * |[
> + * ./lsgpu -d 'pci:vendor=Intel'
> + * === Device filter list ===
> + * [ 0]: pci:vendor=Intel
> +
> + * === Testing device open ===
> + * subsystem : pci
> + * chipset : 1
> + * drm card : /dev/dri/card0
> + * drm render : /dev/dri/renderD128
> + * Device /dev/dri/card0 successfully opened
> + * Device /dev/dri/renderD128 successfully opened
> + * ]|
> + *
> + * Additionally lsgpu tries to open the card and render nodes to verify
> + * permissions. It also uses IGT variable search order:
> + * - use --device first (it overrides IGT_DEVICE and .igtrc Common::Device
> + * settings)
> + * - use IGT_DEVICE enviroment variable if no --device are passed
> + * - use .igtrc Common::Device if no --device nor IGT_DEVICE are passed
> + */

note to self: add tools/* to the generated documentation

> +
> +enum {
> + OPT_PRINT_DETAIL = 'p',
> + OPT_LIST_VENDORS = 'v',
> + OPT_LIST_FILTERS = 'l',
> + OPT_DEVICE = 'd',
> + OPT_HELP = 'h'
> +};
> +
> +static bool g_show_vendors;
> +static bool g_list_filters;
> +static bool g_device;
> +static bool g_help;
> +static char *igt_rc_device;
> +
> +static const char *usage_str =
> + "usage: lsgpu [options]\n\n"
> + "Options:\n"
> + " -p, --print-details Print devices with details\n"
> + " -v, --list-vendors List recognized vendors\n"
> + " -l, --list-filter-types List registered device filters types\n"
> + " -d, --device filter Device filter, can be given multiple times\n"
> + " -h, --help Show this help message and exit\n";
> +
> +static void test_device_open(struct igt_device_card *card)
> +{
> + int fd;
> +
> + if (!card)
> + return;
> +
> + fd = drm_open_card(card);
> + if (fd >= 0) {
> + printf("Device %s successfully opened\n", card->card);
> + close(fd);
> + } else {
> + if (strlen(card->card))
> + printf("Cannot open card %s device\n", card->card);
> + else
> + printf("Cannot open card device, empty name\n");
> + }
> +
> + fd = drm_open_render(card);
> + if (fd >= 0) {
> + printf("Device %s successfully opened\n", card->render);
> + close(fd);
> + } else {
> + if (strlen(card->render))
> + printf("Cannot open render %s device\n", card->render);
> + else
> + printf("Cannot open render device, empty name\n");
> + }
> +}
> +
> +static void print_card(struct igt_device_card *card)
> +{
> + if (!card)
> + return;
> +
> + printf("subsystem : %s\n", card->subsystem);
> + printf("chipset : %x\n", card->chipset);
> + printf("drm card : %s\n", card->card);
> + printf("drm render : %s\n", card->render);
> +}
> +
> +/* Partially copied from igt_core to simulate device selection order:
> + * 1. --device (IGT_DEVICE and .igtrc Common::Device are ignored)
> + * 2. IGT_DEVICE env (.igtrc Common::Device is ignored)
> + * 3. .igtrc (overrides
> + */
> +static void common_init_config(void)
> +{
> + char *key_file_env = NULL;
> + char *key_file_loc = NULL;
> + GError *error = NULL;
> + GKeyFile *igt_key_file;
> + int ret;
> +
> + /* Filter count > 0, just skip .igtrc */
> + if (igt_device_filter_count())
> + return;
> +
> + /* Determine igt config path */
> + key_file_env = getenv("IGT_CONFIG_PATH");
> + if (key_file_env) {
> + key_file_loc = key_file_env;
> + } else {
> + key_file_loc = malloc(100);
> + snprintf(key_file_loc, 100, "%s/.igtrc", g_get_home_dir());
> + }
> +
> + /* Load igt config file */
> + igt_key_file = g_key_file_new();
> + ret = g_key_file_load_from_file(igt_key_file, key_file_loc,
> + G_KEY_FILE_NONE, &error);
> + if (!ret) {
> + g_error_free(error);
> + g_key_file_free(igt_key_file);
> + igt_key_file = NULL;
> +
> + return;
> + }
> +
> + g_clear_error(&error);
> +
> + if (!igt_rc_device)
> + igt_rc_device = g_key_file_get_string(igt_key_file, "Common",
> + "Device", &error);
> +
> + if (igt_rc_device) {
> + igt_device_filter_add(igt_rc_device);
> + g_device = true;
> + }
> +
> + g_clear_error(&error);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + static struct option long_options[] = {
> + {"print-detail", no_argument, NULL, OPT_PRINT_DETAIL},
> + {"list-vendors", no_argument, NULL, OPT_LIST_VENDORS},
> + {"list-filter-types", no_argument, NULL, OPT_LIST_FILTERS},
> + {"device", required_argument, NULL, OPT_DEVICE},
> + {"help", no_argument, NULL, OPT_HELP},
> + {0, 0, 0, 0}
> + };
> + int c, index = 0;
> + const char *env;
> + enum igt_devices_print_type printtype = IGT_PRINT_SIMPLE;
> +
> + env = getenv("IGT_DEVICE");
> + if (env) {
> + igt_device_filter_add(env);
> + g_device = true;
> + }
> +
> + while ((c = getopt_long(argc, argv, "pvld:h",
> + long_options, &index)) != -1) {
> + switch(c) {
> +
> + case OPT_PRINT_DETAIL:
> + printtype = IGT_PRINT_DETAIL;
> + break;
> + case OPT_LIST_VENDORS:
> + g_show_vendors = true;
> + break;
> + case OPT_LIST_FILTERS:
> + g_list_filters = true;
> + break;
> + case OPT_DEVICE:
> + g_device = true;
> + if (getenv("IGT_DEVICE") && igt_device_filter_count()) {
> + unsetenv("IGT_DEVICE");
> + igt_rc_device = NULL;
> + igt_device_filter_free_all();
> + }
> + igt_device_filter_add(optarg);
> + break;
> + case OPT_HELP:
> + g_help = true;
> + break;
> + }
> + }
> + common_init_config();
> +
> + if (g_help) {
> + printf("%s\n", usage_str);
> + exit(0);
> + }
> +
> + if (g_show_vendors) {
> + igt_devices_print_vendors();
> + return 0;
> + }
> +
> + if (g_list_filters) {
> + igt_device_print_filter_types();
> + return 0;
> + }
> +
> + igt_devices_scan(false);
> +
> + if (getenv("IGT_DEVICE")) {
> + printf("Notice: Using IGT_DEVICE device settings [%s]\n",
> + getenv("IGT_DEVICE"));
> + } else if (igt_rc_device) {
> + printf("Notice: Using .igtrc device settings [%s]\n",
> + igt_rc_device);
> + } else {
> + if (igt_device_filter_count())
> + printf("Notice: Using --device filters\n");
> + }

wow, this mimicking igt_core made this really hard to follow...

note to self: rework both, and use igt_load_igtrc() once [Petri's patch]
lands.

[Petri's patch]: https://patchwork.freedesktop.org/patch/329762/

The only think that was a bit confusing for me is that there is no
(clear) way of getting rid of a filter set elsewhere, e.g.:

$ export IGT_DEVICE=vgem:card=0

$ build/tools/lsgpu
Notice: Using IGT_DEVICE device settings [vgem:card=0]
=== Device filter list ===
[ 0]: vgem:card=0

=== Testing device open ===
[ 0]: No device found for filter

$ build/tools/lsgpu --device
build/tools/lsgpu: option '--device' requires an argument
Notice: Using IGT_DEVICE device settings [vgem:card=0]
=== Device filter list ===
[ 0]: vgem:card=0

=== Testing device open ===
[ 0]: No device found for filter

$ build/tools/lsgpu --device ""
=== Device filter list ===

=== Testing device open ===

$ build/tools/lsgpu --device "*"
(lsgpu:9275) igt_device_scan-WARNING: No filter with name [*]
(lsgpu:9275) igt_device_scan-WARNING: Warning on condition !is_valid in function igt_device_filter_add, file ../lib/igt_device_scan.c:1274
=== Device filter list ===

=== Testing device open ===

$ build/tools/lsgpu --device "any"
(lsgpu:9296) igt_device_scan-WARNING: No filter with name [any]
(lsgpu:9296) igt_device_scan-WARNING: Warning on condition !is_valid in function igt_device_filter_add, file ../lib/igt_device_scan.c:1274
=== Device filter list ===

=== Testing device open ===

But I don't think it will haunt many people and it clearly states what
is going on with the filters. We can fix the behavior for "" or "any"
later.

I'll open issues based on the two notes above when mergeing the series.

Reviewed-by: Arkadiusz Hiler <arkadiusz.hiler@intel.com>

> +
> + if (g_device) {
> + int n = igt_device_filter_count();
> + printf("=== Device filter list ===\n");
> + for (int i = 0; i < n; i++) {
> + printf("[%2d]: %s\n", i,
> + igt_device_filter_get(i));
> + }
> + printf("\n");
> +
> + printf("=== Testing device open ===\n");
> + for (int i = 0; i < n; i++) {
> + struct igt_device_card card;
> + const char *filter = igt_device_filter_get(i);
> +
> + if (!igt_device_card_match(filter, &card)) {
> + printf("[%2d]: No device found for filter\n\n",
> + i);
> + continue;
> + }
> + printf("[%2d]: Device detail:\n", i);
> + print_card(&card);
> + test_device_open(&card);
> + if (printtype == IGT_PRINT_DETAIL) {
> + printf("\n");
> + igt_devices_print(printtype);
> + }
> + printf("-------------------------------------------\n");
> + }
> +
> + return 0;
> + }
> +
> + igt_devices_print(printtype);
> +
> + return 0;
> +}
> diff --git a/tools/meson.build b/tools/meson.build
> index 6e72b263..9b3a2a69 100644
> --- a/tools/meson.build
> +++ b/tools/meson.build
> @@ -36,6 +36,7 @@ tools_progs = [
> 'intel_gem_info',
> 'intel_gvtg_test',
> 'dpcd_reg',
> + 'lsgpu',
> ]
> tool_deps = igt_deps
>
> --
> 2.23.0
>