All of lore.kernel.org
 help / color / mirror / Atom feed
* [igt-dev] [PATCH i-g-t v1 0/1] Add device selection methods
@ 2019-07-11 10:30 Zbigniew Kempczyński
  2019-07-11 10:30 ` [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection Zbigniew Kempczyński
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Zbigniew Kempczyński @ 2019-07-11 10:30 UTC (permalink / raw)
  To: igt-dev

After having a talk with Arek, Oleg and Michał regarding device
selection RFC this patch extends current code to:

1. PCI + platform devices discovery
2. Filters like pci, drm, vc4, exynos, vgem and vkms
3. lsgpu tool for discovering GPU devices
4. Device selection documentation

Zbigniew Kempczyński (1):
  Introduce new method of device selection

 docs/multi-device-selection.txt |  115 +++
 lib/Makefile.sources            |    2 +
 lib/drmtest.c                   |  151 +++-
 lib/drmtest.h                   |    9 +
 lib/igt_core.c                  |   13 +
 lib/igt_device_scan.c           | 1425 +++++++++++++++++++++++++++++++
 lib/igt_device_scan.h           |   72 ++
 lib/meson.build                 |    1 +
 tools/Makefile.sources          |    1 +
 tools/lsgpu.c                   |  285 +++++++
 tools/meson.build               |    1 +
 11 files changed, 2073 insertions(+), 2 deletions(-)
 create mode 100644 docs/multi-device-selection.txt
 create mode 100644 lib/igt_device_scan.c
 create mode 100644 lib/igt_device_scan.h
 create mode 100644 tools/lsgpu.c

-- 
2.21.0

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection
  2019-07-11 10:30 [igt-dev] [PATCH i-g-t v1 0/1] Add device selection methods Zbigniew Kempczyński
@ 2019-07-11 10:30 ` Zbigniew Kempczyński
  2019-07-11 12:43   ` Daniel Vetter
  2019-07-12  8:20   ` Vasilev, Oleg
  2019-07-11 15:13 ` [igt-dev] ✓ Fi.CI.BAT: success for Add device selection methods Patchwork
  2019-07-12 10:08 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
  2 siblings, 2 replies; 10+ messages in thread
From: Zbigniew Kempczyński @ 2019-07-11 10:30 UTC (permalink / raw)
  To: igt-dev; +Cc: Daniel Vetter, Petri Latvala

Change adds device selection based on implemented filters.
Different filters can be added to address different device
selection possibilities.

New device selection uses --device filter or IGT_DEVICE environent
variable. Selection of many devices can be done by using --device
argument multiple times. As IGT_DEVICE enviroment can be set
once ';' is recognized as filter separator.

Tool 'lsgpu' which uses device scanning feature was added.

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>
---
 docs/multi-device-selection.txt |  115 +++
 lib/Makefile.sources            |    2 +
 lib/drmtest.c                   |  151 +++-
 lib/drmtest.h                   |    9 +
 lib/igt_core.c                  |   13 +
 lib/igt_device_scan.c           | 1425 +++++++++++++++++++++++++++++++
 lib/igt_device_scan.h           |   72 ++
 lib/meson.build                 |    1 +
 tools/Makefile.sources          |    1 +
 tools/lsgpu.c                   |  285 +++++++
 tools/meson.build               |    1 +
 11 files changed, 2073 insertions(+), 2 deletions(-)
 create mode 100644 docs/multi-device-selection.txt
 create mode 100644 lib/igt_device_scan.c
 create mode 100644 lib/igt_device_scan.h
 create mode 100644 tools/lsgpu.c

diff --git a/docs/multi-device-selection.txt b/docs/multi-device-selection.txt
new file mode 100644
index 00000000..5b13cfcc
--- /dev/null
+++ b/docs/multi-device-selection.txt
@@ -0,0 +1,115 @@
+Multi-device scanning and selection
+-----------------------------------
+
+1. Scanning
+
+Device selection is build around scanning buses using udev (PCI and platform),
+gathering information about matching devices and using filters on discovered
+data.
+
+PCI scanning is easy and gives all information regarding devices (module doesn't
+need to be loaded). Problems occurs when platform devices are taken into
+account. Some of them have appropriate entries in device tree (like
+Raspberry Pi vc4 driver), so displaying them without loaded module is possible.
+Such devices expose OF_COMPATIBLE_X properties which are matched against
+appropriate driver. Some devices are registered inside platform tree at driver
+loading time so discovering such devices is not possible unless driver is loaded.
+
+
+2. Filtering
+
+After scanning all gathered information are stored in devices array. Filtering
+means creating a view that matches filter on devices array. Filters defined
+on non-discoverable devices loads module (if not already loaded) and execute
+device discovery again.
+
+Using 'lsgpu' tool we can get list of defined filter types ('-l' switch):
+...
+pci       pci:[vendor=%04x/name][,device=%04x][,card=%d]
+	  vendor is hex number or name
+...
+
+pci is filter name, it calls filter on devices array to select only PCI
+devices. For example:
+
+pci:vendor=8086
+find first pci card on the bus with 8086 PCI device ID
+
+pci:vendor=intel
+same as above but uses vendor name (see lsgpu -v)
+
+pci:vendor=8086,card=0
+same as above, card=0 is default
+
+pci:vendor=intel,device=1234,card=1
+finds second Intel card which device ID is 0x1234
+
+
+3. lsgpu
+
+To play with devices 'lsgpu' tool was written. It is mostly wrapper to igt
+devices scan code (igt_device_scan.c).
+
+If run without arguments it displays all recognized cards.
+Usage examples:
+
+# lsgpu -ra
+displays devices in 'simple' mode with only few properties and attributes
+
+# lsgpu -d
+displays devices in 'detail' mode, with all properties and attributes. Some
+attributes are skipped because acquiring value can take even seconds (there's
+special blacklist hash table inside the code)
+
+# lsgpu -v
+displays recognized vendors
+
+# lsgpu -l
+displays defined filter types
+
+# lsgpu -m 'vgem:card=0'
+matches first vgem card at index 0. I
+
+
+# lsgpu --device 'pci:card=0;vgem:card=0;vkms:card=0'
+and
+# lsgpu --device 'pci:card=0' --device 'vgem:card=0' --device 'vkms:card=0'
+and
+# export IGT_DEVICE='pci:card=0;vgem:card=0;vkms:card=0'
+# lsgpu
+matches multiple cards. IGT_DEVICE uses ';' separated filter syntax, it is also
+permitted in --device (function splits argument against ';').
+
+
+4. Compatibility with current device open API
+
+Practically all IGT tests use:
+int __drm_open_driver(int chipset);
+int __drm_open_driver_render(int chipset);
+
+Above functions were extended to use --device / env IGT_DEVICE filters
+(if given during IGT test run). If more than single device were passed
+first matching filter against chipset is selected. This allows test like
+prime_vgem to work, ex:
+
+# ./prime_vgem --r basic-read --device 'pci:card=0' --device 'vgem:card=0'
+# ./prime_vgem --r basic-read --device 'vgem:card=0' --device 'pci:card=0'
+
+
+5. New device selection API
+
+Following prototypes define new API:
+
+/* Multi device API */
+int __drm_open_card_with_nth_filter(int num, int chipset);
+int __drm_open_render_with_nth_filter(int num, int chipset);
+
+When test require DRIVER_INTEL it has to be sure that user didn't passed
+another device (vc4 for example). So if user passed --device vc4:card=0
+this open will fail.
+
+struct igt_device_card;
+int __drm_open_card(struct igt_device_card *card);
+int __drm_open_render(struct igt_device_card *card);
+
+These functions allows to open device regardless chip requirement.
diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index e16de86e..c383a817 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -25,6 +25,8 @@ lib_source_list =	 	\
 	igt_debugfs.h		\
 	igt_device.c		\
 	igt_device.h		\
+	igt_device_scan.c	\
+	igt_device_scan.h	\
 	igt_aux.c		\
 	igt_aux.h		\
 	igt_color_encoding.c	\
diff --git a/lib/drmtest.c b/lib/drmtest.c
index 25f20353..5a9cadc1 100644
--- a/lib/drmtest.c
+++ b/lib/drmtest.c
@@ -55,6 +55,7 @@
 #include "igt_gt.h"
 #include "igt_kmod.h"
 #include "igt_sysfs.h"
+#include "igt_device_scan.h"
 #include "version.h"
 #include "config.h"
 #include "intel_reg.h"
@@ -296,25 +297,171 @@ static int __open_driver(const char *base, int offset, unsigned int chipset)
 	return __search_and_open(base, offset, chipset);
 }
 
+static int __open_driver_exact(const char *name, unsigned int chipset)
+{
+	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+	int fd;
+
+	fd = open_device(name, chipset);
+	if (fd != -1)
+		return fd;
+
+	pthread_mutex_lock(&mutex);
+	for (const struct module *m = modules; m->module; m++) {
+		if (chipset & m->bit) {
+			if (m->modprobe)
+				m->modprobe(m->module);
+			else
+				modprobe(m->module);
+		}
+	}
+	pthread_mutex_unlock(&mutex);
+
+	return open_device(name, chipset);
+}
+
+/**
+ * __find_card_with_chipset
+ * @chipset: chipset for compare with card match
+ * @card: pointer where card information will be stored
+ *
+ * For compatibility mode when multiple --device argument were passed
+ * this function tries to be smart enough to handle tests which opens
+ * more than single device. It iterates over filter list and
+ * compares chipset to card chipset for filter matched.
+ *
+ * Returns:
+ * True if card according to filters added and chipset was found,
+ * false othwerwise.
+ */
+static bool __find_card_with_chipset(int chipset, struct igt_device_card *card)
+{
+	int i, n = igt_device_filter_count();
+	const char *filter;
+	bool match;
+
+	for (i = 0; i < n; i++) {
+		filter = igt_device_filter_get(i);
+		match = igt_device_card_match(filter, card);
+		if (match && (card->chipset == chipset || chipset == DRIVER_ANY))
+			return true;
+	}
+
+	return false;
+}
+
 /**
  * __drm_open_driver:
  * @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL
  *
- * Open the first DRM device we can find, searching up to 16 device nodes
+ * Function opens device with following order:
+ * 1. when --device arguments are present device scanning will be executed,
+ * then matching considers all device which matches filter argument will be selected.
+ * 2. compatibility mode - open the first DRM device we can find,
+ * searching up to 16 device nodes.
  *
  * Returns:
  * An open DRM fd or -1 on error
  */
 int __drm_open_driver(int chipset)
 {
+	int n = igt_device_filter_count();
+
+	if (n) {
+		bool found;
+		struct igt_device_card card;
+
+		found = __find_card_with_chipset(chipset, &card);
+		if (!found || !strlen(card.card))
+			return -1;
+
+		return __open_driver_exact(card.card, chipset);
+	}
+
 	return __open_driver("/dev/dri/card", 0, chipset);
 }
 
-static int __drm_open_driver_render(int chipset)
+int __drm_open_driver_render(int chipset)
 {
+	int n = igt_device_filter_count();
+
+	if (n) {
+		bool found;
+		struct igt_device_card card;
+
+		found = __find_card_with_chipset(chipset, &card);
+		if (!found || !strlen(card.render))
+			return -1;
+
+		return __open_driver_exact(card.render, chipset);
+	}
+
 	return __open_driver("/dev/dri/renderD", 128, chipset);
 }
 
+static int __drm_open_with_nth_filter(int num, int chipset, bool open_render)
+{
+	struct igt_device_card card;
+	const char *filter, *devname;
+	bool match;
+	int n = igt_device_filter_count();
+
+	if (!n || num < 0 || num >= n) {
+		igt_warn("No device filter num == %d passed\n", num);
+		return -1;
+	}
+
+	filter = igt_device_filter_get(num);
+	match = igt_device_card_match(filter, &card);
+	if (!match) {
+		igt_warn("No device match filter: %s\n", filter);
+		return -1;
+	}
+
+	if (chipset != card.chipset) {
+		igt_warn("Filtered device doesn't match chipset (%d != %d)\n",
+			 chipset, card.chipset);
+		return -1;
+	}
+	if (!strlen(card.card))
+		return -1;
+
+	devname = open_render ? card.render : card.card;
+	if (!strlen(devname)) {
+		igt_warn("No %s node matching filter: %s\n",
+			 open_render ? "render" : "card", filter);
+		return -1;
+	}
+	return __open_driver_exact(devname, card.chipset);
+}
+
+int __drm_open_card_with_nth_filter(int num, int chipset)
+{
+	return __drm_open_with_nth_filter(num, chipset, false);
+}
+
+int __drm_open_render_with_nth_filter(int num, int chipset)
+{
+	return __drm_open_with_nth_filter(num, chipset, true);
+}
+
+int __drm_open_card(struct igt_device_card *card)
+{
+	if (!card || !strlen(card->card))
+		return -1;
+
+	return __open_driver_exact(card->card, card->chipset);
+}
+
+int __drm_open_render(struct igt_device_card *card)
+{
+	if (!card || !strlen(card->render))
+		return -1;
+
+	return __open_driver_exact(card->render, card->chipset);
+}
+
+
 static int at_exit_drm_fd = -1;
 static int at_exit_drm_render_fd = -1;
 
diff --git a/lib/drmtest.h b/lib/drmtest.h
index 6c4c3899..8f5888a9 100644
--- a/lib/drmtest.h
+++ b/lib/drmtest.h
@@ -45,6 +45,7 @@
 #define DRIVER_AMDGPU	(1 << 4)
 #define DRIVER_V3D	(1 << 5)
 #define DRIVER_PANFROST	(1 << 6)
+
 /*
  * Exclude DRVER_VGEM from DRIVER_ANY since if you run on a system
  * with vgem as well as a supported driver, you can end up with a
@@ -76,6 +77,14 @@ int drm_open_driver(int chipset);
 int drm_open_driver_master(int chipset);
 int drm_open_driver_render(int chipset);
 int __drm_open_driver(int chipset);
+int __drm_open_driver_render(int chipset);
+
+/* Multi device API */
+int __drm_open_card_with_nth_filter(int num, int chipset);
+int __drm_open_render_with_nth_filter(int num, int chipset);
+struct igt_device_card;
+int __drm_open_card(struct igt_device_card *card);
+int __drm_open_render(struct igt_device_card *card);
 
 void gem_quiescent_gpu(int fd);
 
diff --git a/lib/igt_core.c b/lib/igt_core.c
index 1cbb09f9..9b851175 100644
--- a/lib/igt_core.c
+++ b/lib/igt_core.c
@@ -71,6 +71,7 @@
 #include "igt_sysrq.h"
 #include "igt_rc.h"
 #include "igt_list.h"
+#include "igt_device_scan.h"
 
 #define UNW_LOCAL_ONLY
 #include <libunwind.h>
@@ -304,6 +305,7 @@ enum {
 	OPT_DEBUG,
 	OPT_INTERACTIVE_DEBUG,
 	OPT_SKIP_CRC,
+	OPT_DEVICE,
 	OPT_HELP = 'h'
 };
 
@@ -624,6 +626,7 @@ static void print_usage(const char *help_str, bool output_on_stderr)
 		   "  --skip-crc-compare\n"
 		   "  --help-description\n"
 		   "  --describe\n"
+		   "  --device filter\n"
 		   "  --help|-h\n");
 	if (help_str)
 		fprintf(f, "%s\n", help_str);
@@ -725,6 +728,11 @@ static void common_init_env(void)
 	if (env) {
 		__set_forced_driver(env);
 	}
+
+	env = getenv("IGT_DEVICE");
+	if (env) {
+		igt_device_filter_add(env);
+	}
 }
 
 static int common_init(int *argc, char **argv,
@@ -743,6 +751,7 @@ static int common_init(int *argc, char **argv,
 		{"debug",             optional_argument, NULL, OPT_DEBUG},
 		{"interactive-debug", optional_argument, NULL, OPT_INTERACTIVE_DEBUG},
 		{"skip-crc-compare",  no_argument,       NULL, OPT_SKIP_CRC},
+		{"device",            required_argument, NULL, OPT_DEVICE},
 		{"help",              no_argument,       NULL, OPT_HELP},
 		{0, 0, 0, 0}
 	};
@@ -865,6 +874,10 @@ static int common_init(int *argc, char **argv,
 		case OPT_SKIP_CRC:
 			igt_skip_crc_compare = true;
 			goto out;
+		case OPT_DEVICE:
+			assert(optarg);
+			igt_device_filter_add(optarg);
+			break;
 		case OPT_HELP:
 			print_usage(help_str, false);
 			ret = -1;
diff --git a/lib/igt_device_scan.c b/lib/igt_device_scan.c
new file mode 100644
index 00000000..6cf0aea6
--- /dev/null
+++ b/lib/igt_device_scan.c
@@ -0,0 +1,1425 @@
+/*
+ * 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.h"
+#include "igt_kmod.h"
+#include "igt_sysfs.h"
+#include "igt_device.h"
+#include "igt_device_scan.h"
+#include <glib.h>
+#include <libudev.h>
+#include <linux/limits.h>
+#include <sys/stat.h>
+
+//#define DEBUG_DEVICE_SCAN
+#ifdef DEBUG_DEVICE_SCAN
+#define DBG(...) \
+{ \
+	struct timeval tm; \
+	gettimeofday(&tm, NULL); \
+	printf("%10ld.%03ld: ", tm.tv_sec, tm.tv_usec); \
+	printf(__VA_ARGS__); \
+	}
+
+#else
+#define DBG(...) {}
+#endif
+
+#define IGT_BUS_PCI_DRIVERS_DIR "/sys/bus/pci/drivers"
+#define IGT_DRM_PATH "/dev/dri"
+
+/* Scanning PCI is simple and well defined. Platform devices
+ * are listed in device tree but until driver is really loaded
+ * we only 'expect' their existence.
+ *
+ * From this reason platform scanning was separated to two
+ * distinct scan types - non-discoverable and discoverable.
+ *
+ * 1. Non-discoverable scan type checks modalias attribute.
+ * Such devices will occur within platform subsystem only
+ * after driver is loaded. Example are 'platform:vgem'
+ * 'platform:vkms' and 'platform:exynos'.
+ *
+ * 2. Discoverable scan type checks OF_COMPATIBLE_0 property.
+ * This simple check should be enough to display platform
+ * gpu defined in device tree even if driver wasn't already
+ * loaded.
+ */
+enum igt_devices_scan_type {
+	IGT_SCAN_PCI                       = 1,
+	IGT_SCAN_PLATFORM_NON_DISCOVERABLE = 2,
+	IGT_SCAN_PLATFORM_DISCOVERABLE     = 3,
+};
+
+static GHashTable *blacklist_keys_ht;      //sysattrs we don't want to read
+static GHashTable *gpu_pci_class_ht;       //gpu pci classes we know
+static GHashTable *gpu_vendor_ht;          //search id -> vendor_spec mapping
+static GHashTable *filter_definition_ht;   //supported filters (pci=..., etc.)
+
+/* Generic name->value struct, used to fill hash tables */
+struct name_value {
+	const char *name;
+	gpointer *value;
+};
+
+/* Vendor specific data */
+struct vendor_spec {
+	enum igt_device_type dev_type;
+	const char *vendor;
+	const char *modname;
+	const char *modopts;
+	const char *match_driver; //reported driver name not always equal module name
+	int chipset;
+};
+
+/* Single igt_device found in PCI / platform subsystems */
+struct igt_device {
+	char *pretty_name;
+	struct vendor_spec *vs;
+	GHashTable *props_ht;
+	GHashTable *attrs_ht;
+	char *syspath;
+	char *drvpath;
+	char *drm_card_path;
+	char *drm_render_path;
+	char *vendor;
+	char *device;
+	enum igt_device_type dev_type;
+};
+
+/* Scanned devices struct */
+struct igt_devices {
+	GPtrArray *devs;		//all gpu devices array
+	GPtrArray *view;		//filtered view
+	bool devs_scanned;
+};
+
+/* Scanned devices holder */
+static struct igt_devices igt_devs;
+
+/* PCI device classes we take into account during scan.
+ * Attribute PCI_CLASS is compared with key beneath. */
+struct name_value gpu_pci_class_list[] = {
+	{ "30000", (gpointer) "VGA compatibile controller" },
+	{ "30100", (gpointer) "XGA compatibile controller" },
+	{ "30200", (gpointer) "3D controller" },
+	{ "38000", (gpointer) "Display controller" },
+	{ NULL, },
+};
+
+static struct vendor_spec v_intel  = { .dev_type = DEV_PCI,
+				       .vendor = "Intel",
+				       .modname = "i915",
+				       .chipset = DRIVER_INTEL
+				     };
+static struct vendor_spec v_amd    = { .dev_type = DEV_PCI,
+				       .vendor = "AMD",
+				       .modname = "amdgpu",
+				       .chipset = DRIVER_AMDGPU
+				     };
+static struct vendor_spec v_nvidia  = { .dev_type = DEV_PCI,
+					.vendor = "NVIDIA",
+					.modname = "nvidia",
+					.chipset = DRIVER_ANY
+				      };
+static struct vendor_spec v_vgem    = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
+					.vendor = "Virtual-GEM",
+					.modname = "vgem",
+					.chipset = DRIVER_VGEM
+				      };
+static struct vendor_spec v_vkms   = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
+				       .vendor = "Virtual-KMS",
+				       .modname = "vkms",
+				       .chipset = DRIVER_ANY
+				     };
+static struct vendor_spec v_exynos  = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
+					.vendor = "Samsung",
+					.modname = "exynosdrm",
+					.match_driver = "exynos-drm",
+					.chipset = DRIVER_ANY
+				      };
+static struct vendor_spec v_vc4    = { .dev_type = DEV_PLATFORM_DISCOVERABLE,
+				       .vendor = "Broadcom",
+				       .modname = "vc4",
+				       .match_driver = "vc4-drm",
+				       .chipset = DRIVER_VC4
+				     };
+
+/* Mapping vendor id => vendor_spec should be unique (vendor matching
+ * is written around this).
+ *
+ * Keys must be defined as follows:
+ * PCI devices: PCI_SLOT_ID,
+ * Non-discoverable platform devices: MODALIAS,
+ * Discoverable platform devices: OF_COMPATIBLE_0.
+*/
+struct name_value gpu_vendor_list[] = {
+	{ "8086",                (gpointer) &v_intel },
+	{ "1002",                (gpointer) &v_amd },
+	{ "10DE",                (gpointer) &v_nvidia },
+	{ "platform:vgem",       (gpointer) &v_vgem },
+	{ "platform:vkms",       (gpointer) &v_vkms },
+	{ "platform:exynos-drm", (gpointer) &v_exynos },
+	{ "brcm,bcm2835-vc4",    (gpointer) &v_vc4 },
+	{ NULL, },
+};
+
+/* Generic hash table fill function, requires name / value ptrs array */
+static void fill_ht(GHashTable **ht, struct name_value *data)
+{
+	if (*ht)
+		return;
+
+	*ht = g_hash_table_new(g_str_hash, g_str_equal);
+	igt_assert(*ht);
+
+	while (data->name) {
+		g_hash_table_insert(*ht,
+				    (gpointer) data->name,
+				    data->value);
+		data++;
+	}
+}
+
+#define get_vendor_spec(prop) \
+	g_hash_table_lookup(gpu_vendor_ht, prop)
+
+/* Go through whole vendor hash table and compare against vendor field.
+ * Used mostly with vendor=... filter parameter when PCI id is not matched.
+*/
+static bool is_pci_vendor_name(const char *name)
+{
+	bool ret = false;
+	GList *keys = g_hash_table_get_keys(gpu_vendor_ht);
+
+	if (!name)
+		return false;
+
+	while (keys) {
+		char *k = (char *) keys->data;
+		struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, k);
+		keys = g_list_next(keys);
+
+		if (vs->dev_type != DEV_PCI)
+			continue;
+
+		if (!strcasecmp(name, vs->vendor)) {
+			ret = true;
+			break;
+		}
+	}
+	g_list_free(keys);
+
+	return ret;
+}
+
+/* Reading sysattr values can take time (even seconds),
+ * we want to avoid reading such keys.
+*/
+static void populate_blacklist_keys(void)
+{
+	const char *keys[] = { "config", "modalias",
+			       "resource",
+			       "resource0", "resource1", "resource2",
+			       "resource3", "resource4", "resource5",
+			       "resource0_wc", "resource1_wc", "resource2_wc",
+			       "resource3_wc", "resource4_wc", "resource5_wc",
+			       "driver",
+			       "uevent", NULL};
+	const char *key;
+	int i = 0;
+
+	if (blacklist_keys_ht)
+		return;
+
+	blacklist_keys_ht = g_hash_table_new(g_str_hash, g_str_equal);
+	igt_assert(blacklist_keys_ht);
+
+	while ((key = keys[i++]))
+		g_hash_table_add(blacklist_keys_ht, (gpointer) key);
+}
+
+#define is_on_blacklist(key) \
+	g_hash_table_contains(blacklist_keys_ht, key)
+
+static struct igt_device *igt_device_new(void)
+{
+	struct igt_device *dev;
+	dev = calloc(1, sizeof(struct igt_device));
+	if (!dev)
+		return NULL;
+
+	dev->attrs_ht = g_hash_table_new_full(g_str_hash, g_str_equal,
+					      free, free);
+	dev->props_ht = g_hash_table_new_full(g_str_hash, g_str_equal,
+					      free, free);
+
+	if (dev->attrs_ht && dev->props_ht)
+		return dev;
+
+	return NULL;
+}
+
+static void igt_device_add_prop(struct igt_device *dev,
+				const char *key, const char *value)
+{
+	if (!key || !value)
+		return;
+
+	g_hash_table_insert(dev->props_ht, strdup(key), strdup(value));
+}
+
+static void igt_device_add_attr(struct igt_device *dev,
+				const char *key, const char *value)
+{
+	const char *v = value;
+
+	if (!key)
+		return;
+
+	/* It's possible we have symlink at key filename, but udev
+	 * library resolves only few of them */
+	if (!v) {
+		struct stat st;
+		char path[PATH_MAX];
+		char linkto[PATH_MAX];
+		int len;
+
+		snprintf(path, sizeof(path), "%s/%s", dev->syspath, key);
+		if (lstat(path, &st) != 0)
+			return;
+
+		len = readlink(path, linkto, sizeof(linkto));
+		if (len <= 0 || len == (ssize_t) sizeof(linkto))
+			return;
+		linkto[len] = '\0';
+		v = strrchr(linkto, '/');
+		if (v == NULL)
+			return;
+		v++;
+	}
+
+	g_hash_table_insert(dev->attrs_ht, strdup(key), strdup(v));
+}
+
+static void scan_props(struct udev_device *dev, struct igt_device *idev)
+{
+	struct udev_list_entry *entry;
+
+	entry = udev_device_get_properties_list_entry(dev);
+	while (entry) {
+		const char *name = udev_list_entry_get_name(entry);
+		const char *value = udev_list_entry_get_value(entry);
+		igt_device_add_prop(idev, name, value);
+		entry = udev_list_entry_get_next(entry);
+		DBG("prop: %s, val: %s\n", name, value);
+	}
+}
+
+static void scan_attrs(struct udev_device *dev, struct igt_device *idev)
+{
+	struct udev_list_entry *entry;
+
+	entry = udev_device_get_sysattr_list_entry(dev);
+	while (entry) {
+		const char *key = udev_list_entry_get_name(entry);
+		const char *value;
+
+		if (is_on_blacklist(key)) {
+			entry = udev_list_entry_get_next(entry);
+			continue;
+		}
+
+		value = udev_device_get_sysattr_value(dev, key);
+		igt_device_add_attr(idev, key, value);
+		entry = udev_list_entry_get_next(entry);
+		DBG("attr: %s, val: %s\n", key, value);
+	}
+}
+
+#define get_prop(dev, prop) (char *) g_hash_table_lookup(dev->props_ht, prop)
+#define get_attr(dev, attr) (char *) g_hash_table_lookup(dev->attrs_ht, attr)
+#define get_prop_pci_id(dev)    get_prop(dev, "PCI_ID")
+#define get_prop_pci_slot(dev)  get_prop(dev, "PCI_SLOT_NAME")
+#define get_prop_devpath(dev)   get_prop(dev, "DEVPATH")
+#define get_prop_driver(dev)    get_prop(dev, "DRIVER")
+#define get_prop_modalias(dev)  get_prop(dev, "MODALIAS")
+#define get_prop_of_compatible_0(dev) get_prop(dev, "OF_COMPATIBLE_0")
+
+/* Gets PCI_ID property, splits to xxxx:yyyy and stores
+ * xxxx to dev->vendor and yyyy to dev->device for
+ * faster access.
+ */
+static void set_vendor_device(struct igt_device *dev)
+{
+	const char *pci_id = get_prop_pci_id(dev);
+	if (!pci_id || strlen(pci_id) != 9)
+		return;
+	dev->vendor = strndup(pci_id, 4);
+	dev->device = strndup(pci_id + 5, 4);
+}
+
+static void find_drm_paths(struct igt_device *dev, bool platform_check)
+{
+	char dirname[PATH_MAX];
+	DIR *dir;
+	const char *driver = get_prop_driver(dev);
+	const char *devpath = get_prop_devpath(dev);
+	struct dirent *e;
+
+	/* There's no DRIVER, so no /dev/dri/cardX and /dev/dri/renderD
+	 * files exists. If there's no DEVPATH there's something wrong
+	 * as well. */
+	if (!platform_check && (!driver || !devpath))
+		return;
+
+	snprintf(dirname, PATH_MAX, "%s/drm", dev->syspath);
+	dir = opendir(dirname);
+	if (!dir)
+		return;
+
+	while((e = readdir(dir))) {
+		int n;
+		if (sscanf(e->d_name, "card%d", &n) == 1)
+			dev->drm_card_path = g_strdup_printf("%s/%s",
+							     IGT_DRM_PATH,
+							     e->d_name);
+		else if (sscanf(e->d_name, "renderD%d", &n) == 1)
+			dev->drm_render_path = g_strdup_printf("%s/%s",
+							       IGT_DRM_PATH,
+							       e->d_name);
+	}
+	closedir(dir);
+
+	if (dev->drm_card_path)
+		DBG("card: %s\n", dev->drm_card_path);
+	if (dev->drm_render_path)
+		DBG("rend: %s\n", dev->drm_render_path);
+}
+
+static bool prepare_scan(void)
+{
+	if (!igt_devs.devs)
+		igt_devs.devs = g_ptr_array_sized_new(4);
+	if (!igt_devs.view)
+		igt_devs.view = g_ptr_array_sized_new(4);
+
+	if (!igt_devs.devs || !igt_devs.view)
+		return false;
+
+	return true;
+}
+
+static bool is_valid_pci_gpu(const char *prop)
+{
+	return g_hash_table_contains(gpu_pci_class_ht, prop);
+}
+
+static bool is_valid_platform_gpu(const char *prop)
+{
+	struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, prop);
+	if (!vs)
+		return false;
+
+	return vs->dev_type == DEV_PLATFORM_NON_DISCOVERABLE ||
+			vs->dev_type == DEV_PLATFORM_DISCOVERABLE;
+}
+
+static void add_pci_gpu(struct igt_device *dev)
+{
+	find_drm_paths(dev, false);
+
+	g_ptr_array_add(igt_devs.devs, dev);
+	g_ptr_array_add(igt_devs.view, dev);
+}
+
+static void add_platform_non_discoverable_gpu(struct igt_device *dev)
+{
+	find_drm_paths(dev, true);
+	if (!get_prop_driver(dev))
+		igt_device_add_prop(dev, "DRIVER", dev->vs->modname);
+
+	g_ptr_array_add(igt_devs.devs, dev);
+	g_ptr_array_add(igt_devs.view, dev);
+}
+
+static void add_platform_discoverable_gpu(struct igt_device *dev)
+{
+	find_drm_paths(dev, true);
+	g_ptr_array_add(igt_devs.devs, dev);
+	g_ptr_array_add(igt_devs.view, dev);
+}
+
+/* Classifier table.
+ * Scanning udev devices looks same for pci and platform devices, but differs
+ * in logic regarding adding devices to the device array.
+*/
+struct {
+	bool (*is_valid)(const char *prop); //check is device belongs to the class
+	void (*add)(struct igt_device *dev); //add device to the array and aux stuff
+	unsigned int dev_type;
+	const char *subsys;
+	const char *findprop;
+} cls[] = {
+	[IGT_SCAN_PCI] = { .subsys = "pci",
+			   .findprop = "PCI_CLASS",
+			   .dev_type = DEV_PCI,
+			   .is_valid = is_valid_pci_gpu,
+			   .add = add_pci_gpu,
+			 },
+	[IGT_SCAN_PLATFORM_NON_DISCOVERABLE] = { .subsys = "platform",
+						 .findprop = "MODALIAS",
+						 .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
+						 .is_valid = is_valid_platform_gpu,
+						 .add = add_platform_non_discoverable_gpu,
+					       },
+	[IGT_SCAN_PLATFORM_DISCOVERABLE] = { .subsys = "platform",
+					     .findprop = "OF_COMPATIBLE_0",
+					     .dev_type = DEV_PLATFORM_DISCOVERABLE,
+					     .is_valid = is_valid_platform_gpu,
+					     .add = add_platform_discoverable_gpu,
+					   },
+};
+
+static void scan_devices(enum igt_devices_scan_type scantype)
+{
+	struct udev *udev;
+	struct udev_device *dev;
+	struct udev_enumerate *enumerate;
+	struct udev_list_entry *devices, *dev_list_entry;
+	int ret;
+
+	udev = udev_new();
+	igt_assert(udev);
+
+	enumerate = udev_enumerate_new(udev);
+	igt_assert(enumerate);
+
+	DBG("Scanning subsystem %s\n", cls[scantype].subsys);
+	ret = udev_enumerate_add_match_subsystem(enumerate, cls[scantype].subsys);
+	igt_assert(!ret);
+
+	ret = udev_enumerate_scan_devices(enumerate);
+	igt_assert(!ret);
+
+	devices = udev_enumerate_get_list_entry(enumerate);
+	if (!devices)
+		return;
+
+	udev_list_entry_foreach(dev_list_entry, devices) {
+		struct igt_device *idev;
+		const char *path;
+		const char *prop;
+
+		path = udev_list_entry_get_name(dev_list_entry);
+		dev = udev_device_new_from_syspath(udev, path);
+		prop = udev_device_get_property_value(dev, cls[scantype].findprop);
+		if (!prop || !cls[scantype].is_valid(prop)) {
+			udev_device_unref(dev);
+			continue;
+		}
+		idev = igt_device_new();
+		igt_assert(idev);
+
+		idev->dev_type = cls[scantype].dev_type;
+		idev->syspath = strdup(udev_device_get_syspath(dev));
+		scan_props(dev, idev);
+		scan_attrs(dev, idev);
+
+		switch (idev->dev_type) {
+		case DEV_PCI:
+			set_vendor_device(idev);
+			idev->pretty_name = strdup(get_prop_pci_slot(idev));
+			idev->vs = get_vendor_spec(idev->vendor);
+			break;
+		default:
+			idev->pretty_name = strdup(prop);
+			idev->vs = get_vendor_spec(prop);
+		}
+		igt_assert(idev->vs);
+
+		cls[scantype].add(idev);
+		udev_device_unref(dev);
+	}
+	udev_enumerate_unref(enumerate);
+	udev_unref(udev);
+}
+
+struct name_value filter_definition_list[];
+static void populate_gpu_data(void)
+{
+	fill_ht(&gpu_pci_class_ht, &gpu_pci_class_list[0]);
+	fill_ht(&gpu_vendor_ht, &gpu_vendor_list[0]);
+	fill_ht(&filter_definition_ht, &filter_definition_list[0]);
+}
+
+static void igt_device_free(struct igt_device *dev)
+{
+	free(dev->pretty_name);
+	free(dev->device);
+	free(dev->drm_card_path);
+	free(dev->drm_render_path);
+	free(dev->drvpath);
+	free(dev->syspath);
+	free(dev->vendor);
+	g_hash_table_destroy(dev->attrs_ht);
+	g_hash_table_destroy(dev->props_ht);
+}
+
+void igt_devices_scan(bool force)
+{
+	if (force && igt_devs.devs_scanned) {
+		for (int i = 0; i < igt_devs.devs->len; i++) {
+			struct igt_device *dev = g_ptr_array_index(igt_devs.devs, i);
+			igt_device_free(dev);
+			free(dev);
+		}
+		igt_devs.devs_scanned = false;
+		g_ptr_array_free(igt_devs.view, true);
+		g_ptr_array_free(igt_devs.devs, true);
+		igt_devs.view = NULL;
+		igt_devs.devs = NULL;
+	}
+
+	if (igt_devs.devs_scanned)
+		return;
+
+	populate_blacklist_keys();      //keys from sysattr we skip
+	populate_gpu_data();
+
+	prepare_scan();
+	scan_devices(IGT_SCAN_PCI);
+	scan_devices(IGT_SCAN_PLATFORM_NON_DISCOVERABLE);
+	scan_devices(IGT_SCAN_PLATFORM_DISCOVERABLE);
+	igt_devs.devs_scanned = true;
+}
+
+#define pr_simple(k, v) printf("    %-16s: %s\n", k, v)
+#define pr_simple_prop(dev, key) pr_simple(key, get_prop(dev, key))
+#define pr_simple_attr(dev, key) pr_simple(key, get_attr(dev, key))
+
+static void print_vendor(struct igt_device *dev)
+{
+	struct vendor_spec *vs = dev->vs;
+	char *info = alloca(256);
+
+	snprintf(info, 256, "%s (%s, module: %s%s)", dev->vendor,
+		 vs ? vs->vendor : "unknown",
+		 vs ? vs->modname : "unknown",
+		 dev->drm_card_path ? "" : " [not binded/loaded]");
+	pr_simple("vendor", info);
+}
+
+static void igt_devs_print_simple(GPtrArray *view,
+				  bool show_props, bool show_attrs)
+{
+	struct igt_device *dev;
+	int i;
+
+	if (!view)
+		return;
+
+	for (i = 0; i < view->len; i++) {
+		dev = g_ptr_array_index(view, i);
+		printf("%s\n", dev->pretty_name);
+		pr_simple("syspath", dev->syspath);
+		pr_simple("drm card", dev->drm_card_path);
+		pr_simple("drm render", dev->drm_render_path);
+
+		if (show_props) {
+			if (get_prop_pci_id(dev))
+				pr_simple_prop(dev, "PCI_ID");
+			pr_simple_prop(dev, "DRIVER");
+		}
+
+		/* We show attrs only for PCI devices */
+		if (show_attrs && dev->dev_type == DEV_PCI) {
+			print_vendor(dev);
+			pr_simple("device", dev->device);
+		}
+	}
+}
+
+#define pr_detail(k, v) printf("%-32s: %s\n", k, v)
+
+static void print_ht(GHashTable *ht)
+{
+	GList *keys = g_hash_table_get_keys(ht);
+	keys = g_list_sort(keys, (GCompareFunc) strcmp);
+	while (keys) {
+		char *k = (char *) keys->data;
+		char *v = g_hash_table_lookup(ht, k);
+		pr_detail(k, v);
+		keys = g_list_next(keys);
+	}
+	g_list_free(keys);
+}
+
+static void igt_devs_print_detail(GPtrArray *view,
+				  bool show_props, bool show_attrs)
+{
+	struct igt_device *dev;
+	int i;
+	(void) show_props;
+	(void) show_attrs;
+
+	if (!view)
+		return;
+
+	for (i = 0; i < view->len; i++) {
+		dev = g_ptr_array_index(view, i);
+		printf("========== %s ==========\n", dev->pretty_name);
+		printf("[drm]\n");
+		pr_detail("card device", dev->drm_card_path);
+		pr_detail("render device", dev->drm_render_path);
+		pr_detail("syspath", dev->syspath);
+
+		printf("\n[properties]\n");
+		print_ht(dev->props_ht);
+		printf("\n[attributes]\n");
+		print_ht(dev->attrs_ht);
+	}
+}
+
+static struct print_func {
+	void (*prn)(GPtrArray *, bool, bool);
+} print_functions[] = {
+	[IGT_PRINT_SIMPLE] = { .prn = igt_devs_print_simple },
+	[IGT_PRINT_DETAIL] = { .prn = igt_devs_print_detail },
+};
+
+void igt_devices_print(enum igt_devices_print_type printtype,
+		       bool show_props,
+		       bool show_attrs)
+{
+	print_functions[printtype].prn(igt_devs.view,
+				       show_props, show_attrs);
+}
+
+void igt_devices_print_vendors(void)
+{
+	struct name_value *list = &gpu_vendor_list[0];
+	printf("Recognized vendors:\n");
+
+	printf("%-16s %-32s %-32s %-16s\n", "Device type", "id",
+	       "vendor", "module");
+	while (list->name) {
+		char *k = (char *) list->name;
+		struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, k);
+		if (vs->dev_type == DEV_PCI)
+			printf("%-16s %-32s %-32s %-16s\n", "pci",
+			       k, vs->vendor, vs->modname);
+		else if (vs->dev_type == DEV_PLATFORM_NON_DISCOVERABLE)
+			printf("%-16s %-32s %-32s %-16s\n", "non-discoverable",
+			       k, vs->vendor, vs->modname);
+		else if (vs->dev_type == DEV_PLATFORM_DISCOVERABLE)
+			printf("%-16s %-32s %-32s %-16s\n", "discoverable",
+			       k, vs->vendor, vs->modname);
+		list++;
+	}
+}
+
+static bool sys_write_attr(const char *dirname, const char *attr,
+			   const char *str)
+{
+	int dir;
+
+	dir = open(dirname, O_RDONLY);
+	if (dir < 0)
+		return false;
+
+	if (igt_sysfs_printf(dir, attr, "%s", str) < 0) {
+		printf("Error, can't write to %s, err: %s\n",
+		       attr, strerror(errno));
+		close(dir);
+		return false;
+	}
+
+	close(dir);
+	return true;
+}
+
+static bool ensure_driver_is_loaded(const char *modname, const char *modopts)
+{
+	if (!modname)
+		return false;
+
+	if (!igt_kmod_is_loaded(modname)) {
+		int ret;
+		igt_debug("Loading module %s\n", modname);
+		ret = igt_kmod_load(modname, modopts);
+		if (ret) {
+			igt_warn("Can't load module %s\n", modname);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+#define ensure_device_has_driver_loaded(dev) \
+	ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts)
+
+static struct igt_device *igt_devices_find_pci_id(const char *pci_slot)
+{
+	struct igt_device *dev;
+	int i;
+	GPtrArray *arr = igt_devs.devs;
+
+	if (!pci_slot)
+		return NULL;
+
+	for (i = 0; i < arr->len; i++) {
+		dev = g_ptr_array_index(arr, i);
+		if (dev->dev_type != DEV_PCI)
+			continue;
+		if (!strcmp(pci_slot, get_prop_pci_slot(dev)))
+			return dev;
+	}
+
+	return NULL;
+}
+
+bool igt_device_pci_unbind_module(const char *pci_slot)
+{
+	char path[PATH_MAX];
+	struct igt_device *dev = igt_devices_find_pci_id(pci_slot);
+
+	if (!dev)
+		return false;
+
+	if (dev->dev_type != DEV_PCI)
+		return false;
+
+	igt_assert(dev->vs);
+
+	if (!ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts))
+		return false;
+
+	igt_info("Unbinding driver %s on %s\n", dev->vs->modname, pci_slot);
+	snprintf(path, PATH_MAX, "%s/%s", IGT_BUS_PCI_DRIVERS_DIR,
+		 dev->vs->modname);
+
+	return sys_write_attr(path, "unbind", pci_slot);
+}
+
+bool igt_device_pci_bind_module(const char *pci_slot)
+{
+	char path[PATH_MAX];
+	struct igt_device *dev = igt_devices_find_pci_id(pci_slot);
+
+	if (!dev)
+		return false;
+
+	if (dev->dev_type != DEV_PCI)
+		return false;
+
+	igt_assert(dev->vs);
+
+	if (!ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts))
+		return false;
+
+	igt_info("Binding driver %s to %s\n", dev->vs->modname, pci_slot);
+	snprintf(path, PATH_MAX, "%s/%s", IGT_BUS_PCI_DRIVERS_DIR,
+		 dev->vs->modname);
+
+	return sys_write_attr(path, "bind", pci_slot);
+}
+
+static bool pci_rebind_module(struct igt_device *dev)
+{
+	if (dev && dev->dev_type != DEV_PCI)
+		return false;
+
+	igt_device_pci_unbind_module(get_prop_pci_slot(dev));
+	igt_device_pci_bind_module(get_prop_pci_slot(dev));
+
+	return true;
+}
+
+void igt_devices_bind_modules(void)
+{
+	struct igt_device *dev;
+	int i;
+	GPtrArray *arr;
+	printf("Binding modules...\n");
+
+	arr = igt_devs.devs;
+	for (i = 0; i < arr->len; i++) {
+		dev = g_ptr_array_index(arr, i);
+		if (!dev->drm_card_path) {
+			pci_rebind_module(dev);
+		}
+	}
+}
+
+/* ------------------------------------------------------------------------- */
+#define FILTER_SPEC_NAME_LEN 32
+#define FILTER_SPEC_DATA_LEN 256
+struct filter_spec {
+	char name[FILTER_SPEC_NAME_LEN];
+	char data[FILTER_SPEC_DATA_LEN];
+};
+
+struct filter_func {
+	GPtrArray *(*filter_function)(struct filter_spec *fspec,
+				      struct filter_func *ffunc);
+	const char *help;
+	const char *detail;
+
+	struct vendor_spec *vs; //pointer to vs (if any)
+};
+
+union filter_data {
+	struct {
+		char *card;
+	} drm;
+	struct {
+		char *vendor;
+		char *device;
+		char *card;
+	} pci;
+	struct {
+		char *card;
+	} module;
+	struct {
+		char *card;
+	} compatible;
+};
+
+static GHashTable *split_filter_data(const char *data)
+{
+	GHashTable *ht = g_hash_table_new_full(g_str_hash, g_str_equal,
+					       free, free);
+	gchar **s;
+	gchar **strv = g_strsplit(data, ",", -1);
+
+	s = strv;
+	while (*s) {
+		char *k, *v;
+		v = strchr(*s, '=');
+		if (!v) {
+			s++;
+			continue;
+		}
+		k = strndup(*s, v - (*s));
+		v = strdup(v + 1);
+		g_hash_table_insert(ht, k, v);
+		s++;
+	}
+	g_strfreev(strv);
+
+	return ht;
+}
+
+static bool get_filter_spec(const char *filter, struct filter_spec *spec)
+{
+	if (!filter)
+		return false;
+
+	if (sscanf(filter, "%31[^:]:%255s", spec->name, spec->data) == 2)
+		return true;
+
+	return false;
+}
+
+static GPtrArray *filter_drm(struct filter_spec *fspec,
+			     struct filter_func *ffunc)
+{
+	GHashTable *ht;
+	GPtrArray *devs, *view;
+	union filter_data fdata;
+	int card = -1;
+	char cardstr[NAME_MAX];
+
+	(void) ffunc;
+	DBG("filter drm\n");
+
+	ht = split_filter_data(fspec->data);
+	fdata.drm.card = g_hash_table_lookup(ht, "card");
+
+	view = igt_devs.view;
+	devs = igt_devs.devs;
+
+	if (fdata.drm.card) {
+		sscanf(fdata.drm.card, "%d", &card);
+		if (card < 0) {
+			g_hash_table_destroy(ht);
+			return view;
+		}
+	}
+
+	snprintf(cardstr, NAME_MAX, "%s/card%d", IGT_DRM_PATH, card);
+	for (int i = 0; i < devs->len; i++) {
+		struct igt_device *dev = g_ptr_array_index(devs, i);
+		if (dev->drm_card_path && !strcmp(cardstr, dev->drm_card_path)) {
+			g_ptr_array_add(view, dev);
+			break;
+		}
+	}
+
+	DBG("Filter drm view size: %d\n", view->len);
+
+	g_hash_table_destroy(ht);
+
+	return view;
+}
+
+static GPtrArray *filter_pci(struct filter_spec *fspec,
+			     struct filter_func *ffunc)
+{
+	GHashTable *ht;
+	GPtrArray *devs, *view;
+	union filter_data fdata;
+
+	DBG("filter pci\n");
+
+	(void) ffunc;
+	ht = split_filter_data(fspec->data);
+	fdata.pci.vendor = g_hash_table_lookup(ht, "vendor");
+	fdata.pci.device = g_hash_table_lookup(ht, "device");
+	fdata.pci.card = g_hash_table_lookup(ht, "card");
+
+	view = igt_devs.view;
+	devs = igt_devs.devs;
+
+	for (int i = 0; i < devs->len; i++) {
+		struct igt_device *dev = g_ptr_array_index(devs, i);
+
+		if (dev->dev_type != DEV_PCI)
+			continue;
+
+		/* Skip if 'vendor' doesn't match (hex or name) */
+		if (fdata.pci.vendor &&
+				strcasecmp(fdata.pci.vendor, dev->vendor) &&
+				!is_pci_vendor_name(fdata.pci.vendor))
+			continue;
+
+		/* Skip if 'device' doesn't match */
+		if (fdata.pci.device &&
+				strcasecmp(fdata.pci.device, dev->device))
+			continue;
+
+		g_ptr_array_add(view, dev);
+	}
+
+	/* Leave only n-th element if card is set */
+	if (fdata.pci.card) {
+		int card = -1;
+		sscanf(fdata.pci.card, "%d", &card);
+
+		if (card >= 0 && card < view->len) {
+			struct igt_device *dev = g_ptr_array_index(view, card);
+			g_ptr_array_remove_range(view, 0, view->len);
+			g_ptr_array_add(view, dev);
+		} else {
+			g_ptr_array_remove_range(view, 0, view->len);
+		}
+	}
+	DBG("Filter pci view size: %d\n", view->len);
+
+	g_hash_table_destroy(ht);
+
+	return view;
+}
+
+/*
+ * Refresh view finding first card matching with
+ * vs->match_driver or vs->modname if vs->match_driver is not set
+ */
+static GPtrArray *filter_module(struct filter_spec *fspec,
+				struct filter_func *ffunc)
+{
+	GHashTable *ht;
+	GPtrArray *devs, *view;
+	union filter_data fdata;
+	int card = -1;
+	const char *driver = ffunc->vs->match_driver ?: ffunc->vs->modname;
+
+	DBG("filter module [%s]\n", driver);
+
+	ht = split_filter_data(fspec->data);
+	fdata.module.card = g_hash_table_lookup(ht, "card");
+
+	view = igt_devs.view;
+	devs = igt_devs.devs;
+
+	if (fdata.module.card) {
+		sscanf(fdata.module.card, "%d", &card);
+		if (card < 0) {
+			g_hash_table_destroy(ht);
+			return view;
+		}
+	}
+
+	for (int i = 0; i < devs->len; i++) {
+		const char *modname;
+		struct igt_device *dev = g_ptr_array_index(devs, i);
+
+		modname = get_prop_driver(dev);
+		if (!modname)
+			continue;
+
+		if (!strcmp(modname, driver)) {
+			if (!card) {
+				g_ptr_array_add(view, dev);
+				break;
+			}
+			card--;
+		}
+	}
+
+	DBG("Filter view size: %d\n", view->len);
+
+	g_hash_table_destroy(ht);
+
+	return view;
+}
+
+static const char *find_vendor_id_by_vendor_spec(struct vendor_spec *vs)
+{
+	struct name_value *list = &gpu_vendor_list[0];
+
+	while (list->name) {
+		if ((struct vendor_spec *) list->value == vs)
+			return list->name;
+		list++;
+	}
+	return NULL;
+}
+
+static GPtrArray *filter_compatible(struct filter_spec *fspec,
+				    struct filter_func *ffunc)
+{
+	GHashTable *ht;
+	GPtrArray *devs, *view;
+	union filter_data fdata;
+	int card = -1;
+	const char *of_compatible = find_vendor_id_by_vendor_spec(ffunc->vs);
+
+	DBG("filter compatible[%s]\n", of_compatible);
+
+	ht = split_filter_data(fspec->data);
+	fdata.module.card = g_hash_table_lookup(ht, "card");
+
+	view = igt_devs.view;
+	devs = igt_devs.devs;
+
+	if (fdata.module.card) {
+		sscanf(fdata.module.card, "%d", &card);
+		if (card < 0) {
+			g_hash_table_destroy(ht);
+			return view;
+		}
+	}
+
+	for (int i = 0; i < devs->len; i++) {
+		const char *compat;
+		struct igt_device *dev = g_ptr_array_index(devs, i);
+
+		if (!of_compatible)
+			break;
+
+		compat = get_prop_of_compatible_0(dev);
+		if (compat && !strcmp(compat, of_compatible)) {
+			if (!card) {
+				g_ptr_array_add(view, dev);
+				break;
+			}
+			card--;
+		}
+	}
+
+	DBG("Filter view size: %d\n", view->len);
+
+	g_hash_table_destroy(ht);
+
+	return view;
+}
+
+
+static struct filter_func f_drm = { .filter_function = filter_drm,
+				    .help = "drm:[card=%d]",
+				    .detail = "card is N-card number (from /dev/dri/cardN)\n",
+				  };
+
+static struct filter_func f_pci = { .filter_function = filter_pci,
+				    .help = "pci:[vendor=%04x/name][,device=%04x][,card=%d]",
+				    .detail = "vendor is hex number or name",
+				  };
+
+static struct filter_func f_vgem = { .filter_function = filter_module,
+				     .help = "vgem:[card=%d]",
+				     .detail = "card is n-th vgem card number\n",
+				     .vs = &v_vgem,
+				   };
+
+static struct filter_func f_vkms = { .filter_function = filter_module,
+				     .help = "vkms:[card=%d]",
+				     .detail = "card is n-th vkms card number\n",
+				     .vs = &v_vkms,
+				   };
+
+static struct filter_func f_exynos = { .filter_function = filter_module,
+				       .help = "exynos:[card=%d]",
+				       .detail = "card is n-th exynos-drm card number\n",
+				       .vs = &v_exynos,
+				     };
+
+static struct filter_func f_vc4 = { .filter_function = filter_compatible,
+				    .help = "vc4:[card=%d]",
+				    .detail = "card is n-th vc4 card number\n",
+				    .vs = &v_vc4,
+				  };
+
+struct name_value filter_definition_list[] = {
+	{ "drm",     (gpointer) &f_drm },
+	{ "pci",     (gpointer) &f_pci },
+	{ "vgem",    (gpointer) &f_vgem },
+	{ "vkms",    (gpointer) &f_vkms },
+	{ "exynos",  (gpointer) &f_exynos },
+	{ "vc4",     (gpointer) &f_vc4 },
+	{ NULL, },
+};
+
+void igt_device_print_filter_types(void)
+{
+	GList *keys = g_hash_table_get_keys(filter_definition_ht);
+
+	printf("Filter types:\n---\n");
+	printf("%-8s  %s\n---\n", "filter", "syntax");
+	while (keys) {
+		char *k = (char *) keys->data;
+		struct filter_func *v = g_hash_table_lookup(filter_definition_ht, k);
+		printf("%-8s  %s\n", k, v->help);
+		printf("%-8s  %s\n", "", v->detail);
+		keys = g_list_next(keys);
+	}
+	g_list_free(keys);
+}
+
+static GPtrArray *device_filters = NULL;
+
+#define DEVICE_FILTER_CHECK_ALLOC() \
+	do { \
+		if (!device_filters) \
+			device_filters = g_ptr_array_new_full(2, free); \
+		igt_assert(device_filters); \
+	} while(0)
+
+int igt_device_filter_count(void)
+{
+	DEVICE_FILTER_CHECK_ALLOC();
+
+	return device_filters->len;
+}
+
+int igt_device_filter_add(const char *filter)
+{
+	gchar **strv, **s;
+	int c = 0;
+
+	DEVICE_FILTER_CHECK_ALLOC();
+
+	strv = g_strsplit(filter, ";", -1);
+
+	s = strv;
+	while (*s) {
+		g_ptr_array_add(device_filters, strdup(*s));
+		s++;
+	}
+	g_strfreev(strv);
+
+	return c;
+}
+
+const char *igt_device_filter_get(int num)
+{
+	DEVICE_FILTER_CHECK_ALLOC();
+
+	if (num < 0 || num >= device_filters->len)
+		return NULL;
+
+	return g_ptr_array_index(device_filters, num);
+}
+
+#define dev_is_non_discoverable(vs) \
+	((vs)->dev_type == DEV_PLATFORM_NON_DISCOVERABLE)
+
+bool igt_device_filter_apply(const char *filter)
+{
+	struct filter_spec fspec;
+	struct filter_func *ffunc;
+	bool ret;
+
+	if (!filter)
+		return false;
+
+	ret = get_filter_spec(filter, &fspec);
+	if (!ret) {
+		igt_warn("Can't split filter [%s]\n", filter);
+		return false;
+	}
+
+	ffunc = g_hash_table_lookup(filter_definition_ht, fspec.name);
+	if (!ffunc) {
+		igt_warn("No filter with name [%s]\n", fspec.name);
+		return false;
+	}
+
+	/* Clean view */
+	g_ptr_array_remove_range(igt_devs.view, 0, igt_devs.view->len);
+
+	/* Load module only for non-discoverable devices.
+	 * For discoverables / PCI defer this to card match path
+	*/
+	if (ffunc->vs && dev_is_non_discoverable(ffunc->vs) &&
+			!igt_kmod_is_loaded(ffunc->vs->modname)) {
+		if (!igt_kmod_load(ffunc->vs->modname, ffunc->vs->modopts)) {
+			igt_info("Module %s loaded, rescanning devices\n",
+				 ffunc->vs->modname);
+			igt_devices_scan(true);
+		} else {
+			return false;
+		}
+	}
+
+	ffunc->filter_function(&fspec, ffunc);
+
+	return true;
+}
+
+bool igt_device_filter_apply_nth(int num)
+{
+	const char *filter = igt_device_filter_get(num);
+	if (!filter)
+		return false;
+
+	return igt_device_filter_apply(filter);
+}
+
+/* For PCI devices load or unbind / bind is called in card matching path.
+ * This is for 'lsgpu' tool which doesn't want to make magic driver
+ * loading job in filter apply path (it really want to display PCI devices
+ * in the state they really are).
+*/
+static bool ensure_device_is_ready(struct igt_device *dev,
+				   const char *filter)
+{
+	if (dev->dev_type != DEV_PCI &&
+		dev->dev_type != DEV_PLATFORM_DISCOVERABLE)
+		return false;
+
+	if (dev->drm_card_path || dev->drm_render_path)
+		return true;
+
+	/* So, we have PCI device or discoverable device without
+	 * card/render node, lets try at the beginning to simply
+	 * load the module */
+	if (!ensure_device_has_driver_loaded(dev))
+		return false; //oops!
+
+	/* Rescan devices, filter again and get the device */
+	igt_devices_scan(true);
+	if (igt_device_filter_apply(filter) == false)
+		return false;
+
+	if (!igt_devs.view->len)
+		return false;
+
+	dev = g_ptr_array_index(igt_devs.view, 0);
+	if (dev->drm_card_path || dev->drm_render_path)
+		return true;
+
+	/* For platform device unbind/bind is not performed */
+	if (dev->dev_type == DEV_PLATFORM_DISCOVERABLE)
+		return false;
+
+	/* In this moment we likely already had driver loaded,
+	 * but it wasn't binded to this device. */
+	igt_device_pci_unbind_module(get_prop_pci_slot(dev));
+	igt_device_pci_bind_module(get_prop_pci_slot(dev));
+	igt_devices_scan(true);
+	if (igt_device_filter_apply(filter) == false)
+		return false;
+
+	if (!igt_devs.view->len)
+		return false;
+
+	dev = g_ptr_array_index(igt_devs.view, 0);
+	if (dev->drm_card_path || dev->drm_render_path)
+		return true;
+
+	return false;
+}
+
+#define safe_strncpy(dst, src, size) \
+	if (src) strncpy((dst), (src), (size))
+/*
+ * Returns:
+ * false - no card pointer was passed or card wasn't matched,
+ * true - card matched and returned.
+ */
+bool igt_device_card_match(const char *filter, struct igt_device_card *card)
+{	
+	struct igt_device *dev = NULL;
+
+	if (!card)
+		return false;
+	memset(card, 0, sizeof(*card));
+
+	igt_devices_scan(false);
+
+	if (igt_device_filter_apply(filter) == false)
+		return false;
+
+	if (!igt_devs.view->len)
+		return false;
+
+	dev = g_ptr_array_index(igt_devs.view, 0);
+	if (dev->dev_type == DEV_PCI ||
+		dev->dev_type == DEV_PLATFORM_DISCOVERABLE) {
+		if (!ensure_device_is_ready(dev, filter))
+			return false;
+	}
+
+	if (!igt_devs.view->len) //additional check because rescan could happen
+		return false;
+
+	/* We take first one if more than one card matches filter */
+	dev = g_ptr_array_index(igt_devs.view, 0);
+	card->dev_type = dev->dev_type;
+	card->chipset = dev->vs->chipset;
+	if (dev->dev_type == DEV_PCI) {
+		safe_strncpy(card->module, dev->vs->modname, NAME_MAX);
+		safe_strncpy(card->pci_slot, get_prop_pci_slot(dev),
+			PCI_SLOT_LENGTH);
+	} else {
+		safe_strncpy(card->module, get_prop_driver(dev), NAME_MAX);
+	}
+	safe_strncpy(card->card, dev->drm_card_path, NAME_MAX);
+	safe_strncpy(card->render, dev->drm_render_path, NAME_MAX);
+
+	return true;
+}
diff --git a/lib/igt_device_scan.h b/lib/igt_device_scan.h
new file mode 100644
index 00000000..0e2adf13
--- /dev/null
+++ b/lib/igt_device_scan.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef __IGT_DEVICE_SCAN_H__
+#define __IGT_DEVICE_SCAN_H__
+
+#include <limits.h>
+#include <igt.h>
+
+enum igt_devices_print_type {
+	IGT_PRINT_SIMPLE,
+	IGT_PRINT_DETAIL,
+};
+
+enum igt_device_type {
+	DEV_PCI,
+	DEV_PLATFORM_NON_DISCOVERABLE,
+	DEV_PLATFORM_DISCOVERABLE,
+};
+
+#define PCI_SLOT_LENGTH 13
+struct igt_device_card {
+	enum igt_device_type dev_type;
+	unsigned int chipset; //contains DRIVER_XXX value
+	char module[NAME_MAX];
+	char card[NAME_MAX];
+	char render[NAME_MAX];
+	char pci_slot[PCI_SLOT_LENGTH];
+};
+
+void igt_devices_scan(bool force);
+void igt_devices_print(enum igt_devices_print_type printtype,
+		       bool show_props, bool show_attrs);
+
+void igt_devices_print_vendors(void);
+
+bool igt_device_pci_unbind_module(const char *pci_slot);
+bool igt_device_pci_bind_module(const char *pci_slot);
+void igt_devices_bind_modules(void);
+
+void igt_device_print_filter_types(void);
+
+int igt_device_filter_count(void);
+int igt_device_filter_add(const char *filter);
+const char *igt_device_filter_get(int num);
+bool igt_device_filter_apply(const char *filter);
+bool igt_device_filter_apply_nth(int num);
+
+bool igt_device_card_match(const char *filter, struct igt_device_card *card);
+
+#endif /* __IGT_DEVICE_SCAN_H__ */
diff --git a/lib/meson.build b/lib/meson.build
index 157624e7..826ebbe3 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -10,6 +10,7 @@ lib_sources = [
 	'igt_color_encoding.c',
 	'igt_debugfs.c',
 	'igt_device.c',
+	'igt_device_scan.c',
 	'igt_aux.c',
 	'igt_gpu_power.c',
 	'igt_gt.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..b784ca65
--- /dev/null
+++ b/tools/lsgpu.c
@@ -0,0 +1,285 @@
+/*
+ * 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>
+
+enum {
+	OPT_SHOW_PROPS     = 'r',
+	OPT_SHOW_ATTRS     = 'a',
+	OPT_PRINT_SIMPLE   = 'i',
+	OPT_PRINT_DETAIL   = 'd',
+	OPT_LIST_VENDORS   = 'v',
+	OPT_BIND_MODULES   = 'B',
+	OPT_UNBIND_MODULE  = 'u',
+	OPT_BIND_MODULE    = 'b',
+	OPT_LIST_FILTERS   = 'l',
+	OPT_SET_FILTER     = 's',
+	OPT_MATCH_DEVICE   = 'm',
+	OPT_DEVICE         = 'D',
+	OPT_HELP           = 'h'
+};
+
+bool g_show_props;
+bool g_show_attrs;
+bool g_show_vendors;
+bool g_bind_modules;
+char *g_pci_id;
+bool g_unbind_module;
+bool g_bind_module;
+bool g_list_filters;
+char *g_filter;
+char *g_match_filter;
+bool g_device;
+bool g_help;
+
+static const char *usage_str =
+	"usage: lsgpu [options]\n\n"
+	"Options:\n"
+	"  -r, --show-props            Show device properties\n"
+	"  -a, --show-attrs            Show device attributes\n"
+	"  -i, --print-simple          Print devices as simple list (default)\n"
+	"  -d, --print-details         Print devices with details\n"
+	"  -v, --list-vendors          List recognized vendors\n"
+	"  -B, --bind-modules          Bind modules to unbound PCI cards\n"
+	"  -u, --unbind-module pci_id  Unbind module from pci id device\n"
+	"  -b, --bind-module pci_id    Bind module to pci id device\n"
+	"  -l, --list-filter-types     List registered device filters types\n"
+	"  -s, --set-filter filter     Set filter for processing devices\n"
+	"  -m, --match-device filter   Find device matching to filter\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;
+
+	switch (card->dev_type) {
+	case DEV_PCI:
+		printf("dev type    : %s\n", "PCI");
+		printf("PCI slot    : %s\n", card->pci_slot);
+		break;
+	case DEV_PLATFORM_NON_DISCOVERABLE:
+		printf("dev type    : %s\n", "PLATFORM (NON-DISCOVERABLE)");
+		break;
+	case DEV_PLATFORM_DISCOVERABLE:
+		printf("dev type    : %s\n", "PLATFORM (DISCOVERABLE)");
+		break;
+	}
+	printf("drv chipset : %x\n", card->chipset);
+	printf("drm card    : %s\n", card->card);
+	printf("drm render  : %s\n", card->render);
+	printf("drv module  : %s\n", card->module);
+}
+
+int main(int argc, char *argv[])
+{
+	static struct option long_options[] = {
+		{"show-props",        no_argument,       NULL, OPT_SHOW_PROPS},
+		{"show-attrs",        no_argument,       NULL, OPT_SHOW_ATTRS},
+		{"print-simple",      no_argument,       NULL, OPT_PRINT_SIMPLE},
+		{"print-detail",      no_argument,       NULL, OPT_PRINT_DETAIL},
+		{"list-vendors",      no_argument,       NULL, OPT_LIST_VENDORS},
+		{"bind-modules",      no_argument,       NULL, OPT_BIND_MODULES},
+		{"unbind-module",     required_argument, NULL, OPT_UNBIND_MODULE},
+		{"bind-module",       required_argument, NULL, OPT_BIND_MODULE},
+		{"list-filter-types", no_argument,       NULL, OPT_LIST_FILTERS},
+		{"set-filter",        required_argument, NULL, OPT_SET_FILTER},
+		{"match-device",      required_argument, NULL, OPT_MATCH_DEVICE},
+		{"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;
+
+	while ((c = getopt_long(argc, argv, "PpraidvBu:b:ls:m:D:h",
+				long_options, &index)) != -1) {
+		switch(c) {
+		case OPT_SHOW_PROPS:
+			g_show_props = true;
+			break;
+		case OPT_SHOW_ATTRS:
+			g_show_attrs = true;
+			break;
+		case OPT_PRINT_SIMPLE:
+			printtype = IGT_PRINT_SIMPLE;
+			break;
+		case OPT_PRINT_DETAIL:
+			printtype = IGT_PRINT_DETAIL;
+			break;
+		case OPT_LIST_VENDORS:
+			g_show_vendors = true;
+			break;
+		case OPT_BIND_MODULES:
+			g_bind_modules = true;
+			break;
+		case OPT_UNBIND_MODULE:
+			g_pci_id = strdup(optarg);
+			g_unbind_module = true;
+			break;
+		case OPT_BIND_MODULE:
+			g_pci_id = strdup(optarg);
+			g_bind_module = true;
+			break;
+		case OPT_SET_FILTER:
+			g_filter = strdup(optarg);
+			break;
+		case OPT_LIST_FILTERS:
+			g_list_filters = true;
+			break;
+		case OPT_MATCH_DEVICE:
+			g_match_filter = strdup(optarg);
+			break;
+		case OPT_DEVICE:
+			g_device = true;
+			igt_device_filter_add(optarg);
+			break;
+		case OPT_HELP:
+			g_help = true;
+			break;
+		}
+	}
+
+	if (g_help) {
+		printf("%s\n", usage_str);
+		exit(0);
+	}
+
+	env = getenv("IGT_DEVICE");
+	if (env) {
+		igt_device_filter_add(env);
+		g_device = true;
+	}
+
+	igt_devices_scan(false);
+	if (g_show_vendors) {
+		igt_devices_print_vendors();
+		return 0;
+	}
+	if (g_bind_modules) {
+		igt_devices_bind_modules();
+		return 0;
+	}
+
+	if (g_unbind_module) {
+		igt_device_pci_unbind_module(g_pci_id);
+		return 0;
+	}
+
+	if (g_bind_module) {
+		igt_device_pci_bind_module(g_pci_id);
+		return 0;
+	}
+
+	if (g_list_filters) {
+		igt_device_print_filter_types();
+		return 0;
+	}
+
+	if (g_filter) {
+		igt_device_filter_apply(g_filter);
+	}
+
+	if (g_match_filter) {
+		struct igt_device_card card;
+		if (igt_device_card_match(g_match_filter, &card)) {
+			print_card(&card);
+		} else {
+			printf("No device matching filter [%s] found.\n",
+			       g_match_filter);
+			return 0;
+		}
+		test_device_open(&card);
+
+		return 0;
+	}
+
+	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));
+			igt_device_filter_apply_nth(i);
+			igt_devices_print(printtype, g_show_props, g_show_attrs);
+		}
+		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))
+				continue;
+			print_card(&card);
+			test_device_open(&card);
+			printf("---\n");
+		}
+
+		return 0;
+	}
+
+	igt_devices_print(printtype, g_show_props, g_show_attrs);
+
+	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.21.0

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection
  2019-07-11 10:30 ` [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection Zbigniew Kempczyński
@ 2019-07-11 12:43   ` Daniel Vetter
  2019-07-15  6:25     ` Kempczynski, Zbigniew
  2019-07-12  8:20   ` Vasilev, Oleg
  1 sibling, 1 reply; 10+ messages in thread
From: Daniel Vetter @ 2019-07-11 12:43 UTC (permalink / raw)
  To: Zbigniew Kempczyński; +Cc: igt-dev, Petri Latvala, Daniel Vetter

On Thu, Jul 11, 2019 at 12:30:57PM +0200, Zbigniew Kempczyński wrote:
> Change adds device selection based on implemented filters.
> Different filters can be added to address different device
> selection possibilities.
> 
> New device selection uses --device filter or IGT_DEVICE environent
> variable. Selection of many devices can be done by using --device
> argument multiple times. As IGT_DEVICE enviroment can be set
> once ';' is recognized as filter separator.
> 
> Tool 'lsgpu' which uses device scanning feature was added.
> 
> 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>
> ---
>  docs/multi-device-selection.txt |  115 +++

Instead of text, can we somehow integrate into our gtkdoc stuff? I know
gtkdoc isn't as flexible as the kernel by far, but random .txt files isn't
better either ...

>  lib/Makefile.sources            |    2 +
>  lib/drmtest.c                   |  151 +++-
>  lib/drmtest.h                   |    9 +
>  lib/igt_core.c                  |   13 +
>  lib/igt_device_scan.c           | 1425 +++++++++++++++++++++++++++++++
>  lib/igt_device_scan.h           |   72 ++
>  lib/meson.build                 |    1 +
>  tools/Makefile.sources          |    1 +
>  tools/lsgpu.c                   |  285 +++++++
>  tools/meson.build               |    1 +
>  11 files changed, 2073 insertions(+), 2 deletions(-)
>  create mode 100644 docs/multi-device-selection.txt
>  create mode 100644 lib/igt_device_scan.c
>  create mode 100644 lib/igt_device_scan.h
>  create mode 100644 tools/lsgpu.c

Wrt the design itself: I thought the last discussion on this we agreed on
basing this on udev filters. Not reinvinting an entire device
parser/filter ourselves (which this does here), because that means more
work on arm and anywhere else. Do we have a case of lost in communication
between all the various mail threads going on here?
-Daniel

> 
> diff --git a/docs/multi-device-selection.txt b/docs/multi-device-selection.txt
> new file mode 100644
> index 00000000..5b13cfcc
> --- /dev/null
> +++ b/docs/multi-device-selection.txt
> @@ -0,0 +1,115 @@
> +Multi-device scanning and selection
> +-----------------------------------
> +
> +1. Scanning
> +
> +Device selection is build around scanning buses using udev (PCI and platform),
> +gathering information about matching devices and using filters on discovered
> +data.
> +
> +PCI scanning is easy and gives all information regarding devices (module doesn't
> +need to be loaded). Problems occurs when platform devices are taken into
> +account. Some of them have appropriate entries in device tree (like
> +Raspberry Pi vc4 driver), so displaying them without loaded module is possible.
> +Such devices expose OF_COMPATIBLE_X properties which are matched against
> +appropriate driver. Some devices are registered inside platform tree at driver
> +loading time so discovering such devices is not possible unless driver is loaded.
> +
> +
> +2. Filtering
> +
> +After scanning all gathered information are stored in devices array. Filtering
> +means creating a view that matches filter on devices array. Filters defined
> +on non-discoverable devices loads module (if not already loaded) and execute
> +device discovery again.
> +
> +Using 'lsgpu' tool we can get list of defined filter types ('-l' switch):
> +...
> +pci       pci:[vendor=%04x/name][,device=%04x][,card=%d]
> +	  vendor is hex number or name
> +...
> +
> +pci is filter name, it calls filter on devices array to select only PCI
> +devices. For example:
> +
> +pci:vendor=8086
> +find first pci card on the bus with 8086 PCI device ID
> +
> +pci:vendor=intel
> +same as above but uses vendor name (see lsgpu -v)
> +
> +pci:vendor=8086,card=0
> +same as above, card=0 is default
> +
> +pci:vendor=intel,device=1234,card=1
> +finds second Intel card which device ID is 0x1234
> +
> +
> +3. lsgpu
> +
> +To play with devices 'lsgpu' tool was written. It is mostly wrapper to igt
> +devices scan code (igt_device_scan.c).
> +
> +If run without arguments it displays all recognized cards.
> +Usage examples:
> +
> +# lsgpu -ra
> +displays devices in 'simple' mode with only few properties and attributes
> +
> +# lsgpu -d
> +displays devices in 'detail' mode, with all properties and attributes. Some
> +attributes are skipped because acquiring value can take even seconds (there's
> +special blacklist hash table inside the code)
> +
> +# lsgpu -v
> +displays recognized vendors
> +
> +# lsgpu -l
> +displays defined filter types
> +
> +# lsgpu -m 'vgem:card=0'
> +matches first vgem card at index 0. I
> +
> +
> +# lsgpu --device 'pci:card=0;vgem:card=0;vkms:card=0'
> +and
> +# lsgpu --device 'pci:card=0' --device 'vgem:card=0' --device 'vkms:card=0'
> +and
> +# export IGT_DEVICE='pci:card=0;vgem:card=0;vkms:card=0'
> +# lsgpu
> +matches multiple cards. IGT_DEVICE uses ';' separated filter syntax, it is also
> +permitted in --device (function splits argument against ';').
> +
> +
> +4. Compatibility with current device open API
> +
> +Practically all IGT tests use:
> +int __drm_open_driver(int chipset);
> +int __drm_open_driver_render(int chipset);
> +
> +Above functions were extended to use --device / env IGT_DEVICE filters
> +(if given during IGT test run). If more than single device were passed
> +first matching filter against chipset is selected. This allows test like
> +prime_vgem to work, ex:
> +
> +# ./prime_vgem --r basic-read --device 'pci:card=0' --device 'vgem:card=0'
> +# ./prime_vgem --r basic-read --device 'vgem:card=0' --device 'pci:card=0'
> +
> +
> +5. New device selection API
> +
> +Following prototypes define new API:
> +
> +/* Multi device API */
> +int __drm_open_card_with_nth_filter(int num, int chipset);
> +int __drm_open_render_with_nth_filter(int num, int chipset);
> +
> +When test require DRIVER_INTEL it has to be sure that user didn't passed
> +another device (vc4 for example). So if user passed --device vc4:card=0
> +this open will fail.
> +
> +struct igt_device_card;
> +int __drm_open_card(struct igt_device_card *card);
> +int __drm_open_render(struct igt_device_card *card);
> +
> +These functions allows to open device regardless chip requirement.
> diff --git a/lib/Makefile.sources b/lib/Makefile.sources
> index e16de86e..c383a817 100644
> --- a/lib/Makefile.sources
> +++ b/lib/Makefile.sources
> @@ -25,6 +25,8 @@ lib_source_list =	 	\
>  	igt_debugfs.h		\
>  	igt_device.c		\
>  	igt_device.h		\
> +	igt_device_scan.c	\
> +	igt_device_scan.h	\
>  	igt_aux.c		\
>  	igt_aux.h		\
>  	igt_color_encoding.c	\
> diff --git a/lib/drmtest.c b/lib/drmtest.c
> index 25f20353..5a9cadc1 100644
> --- a/lib/drmtest.c
> +++ b/lib/drmtest.c
> @@ -55,6 +55,7 @@
>  #include "igt_gt.h"
>  #include "igt_kmod.h"
>  #include "igt_sysfs.h"
> +#include "igt_device_scan.h"
>  #include "version.h"
>  #include "config.h"
>  #include "intel_reg.h"
> @@ -296,25 +297,171 @@ static int __open_driver(const char *base, int offset, unsigned int chipset)
>  	return __search_and_open(base, offset, chipset);
>  }
>  
> +static int __open_driver_exact(const char *name, unsigned int chipset)
> +{
> +	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
> +	int fd;
> +
> +	fd = open_device(name, chipset);
> +	if (fd != -1)
> +		return fd;
> +
> +	pthread_mutex_lock(&mutex);
> +	for (const struct module *m = modules; m->module; m++) {
> +		if (chipset & m->bit) {
> +			if (m->modprobe)
> +				m->modprobe(m->module);
> +			else
> +				modprobe(m->module);
> +		}
> +	}
> +	pthread_mutex_unlock(&mutex);
> +
> +	return open_device(name, chipset);
> +}
> +
> +/**
> + * __find_card_with_chipset
> + * @chipset: chipset for compare with card match
> + * @card: pointer where card information will be stored
> + *
> + * For compatibility mode when multiple --device argument were passed
> + * this function tries to be smart enough to handle tests which opens
> + * more than single device. It iterates over filter list and
> + * compares chipset to card chipset for filter matched.
> + *
> + * Returns:
> + * True if card according to filters added and chipset was found,
> + * false othwerwise.
> + */
> +static bool __find_card_with_chipset(int chipset, struct igt_device_card *card)
> +{
> +	int i, n = igt_device_filter_count();
> +	const char *filter;
> +	bool match;
> +
> +	for (i = 0; i < n; i++) {
> +		filter = igt_device_filter_get(i);
> +		match = igt_device_card_match(filter, card);
> +		if (match && (card->chipset == chipset || chipset == DRIVER_ANY))
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
>  /**
>   * __drm_open_driver:
>   * @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL
>   *
> - * Open the first DRM device we can find, searching up to 16 device nodes
> + * Function opens device with following order:
> + * 1. when --device arguments are present device scanning will be executed,
> + * then matching considers all device which matches filter argument will be selected.
> + * 2. compatibility mode - open the first DRM device we can find,
> + * searching up to 16 device nodes.
>   *
>   * Returns:
>   * An open DRM fd or -1 on error
>   */
>  int __drm_open_driver(int chipset)
>  {
> +	int n = igt_device_filter_count();
> +
> +	if (n) {
> +		bool found;
> +		struct igt_device_card card;
> +
> +		found = __find_card_with_chipset(chipset, &card);
> +		if (!found || !strlen(card.card))
> +			return -1;
> +
> +		return __open_driver_exact(card.card, chipset);
> +	}
> +
>  	return __open_driver("/dev/dri/card", 0, chipset);
>  }
>  
> -static int __drm_open_driver_render(int chipset)
> +int __drm_open_driver_render(int chipset)
>  {
> +	int n = igt_device_filter_count();
> +
> +	if (n) {
> +		bool found;
> +		struct igt_device_card card;
> +
> +		found = __find_card_with_chipset(chipset, &card);
> +		if (!found || !strlen(card.render))
> +			return -1;
> +
> +		return __open_driver_exact(card.render, chipset);
> +	}
> +
>  	return __open_driver("/dev/dri/renderD", 128, chipset);
>  }
>  
> +static int __drm_open_with_nth_filter(int num, int chipset, bool open_render)
> +{
> +	struct igt_device_card card;
> +	const char *filter, *devname;
> +	bool match;
> +	int n = igt_device_filter_count();
> +
> +	if (!n || num < 0 || num >= n) {
> +		igt_warn("No device filter num == %d passed\n", num);
> +		return -1;
> +	}
> +
> +	filter = igt_device_filter_get(num);
> +	match = igt_device_card_match(filter, &card);
> +	if (!match) {
> +		igt_warn("No device match filter: %s\n", filter);
> +		return -1;
> +	}
> +
> +	if (chipset != card.chipset) {
> +		igt_warn("Filtered device doesn't match chipset (%d != %d)\n",
> +			 chipset, card.chipset);
> +		return -1;
> +	}
> +	if (!strlen(card.card))
> +		return -1;
> +
> +	devname = open_render ? card.render : card.card;
> +	if (!strlen(devname)) {
> +		igt_warn("No %s node matching filter: %s\n",
> +			 open_render ? "render" : "card", filter);
> +		return -1;
> +	}
> +	return __open_driver_exact(devname, card.chipset);
> +}
> +
> +int __drm_open_card_with_nth_filter(int num, int chipset)
> +{
> +	return __drm_open_with_nth_filter(num, chipset, false);
> +}
> +
> +int __drm_open_render_with_nth_filter(int num, int chipset)
> +{
> +	return __drm_open_with_nth_filter(num, chipset, true);
> +}
> +
> +int __drm_open_card(struct igt_device_card *card)
> +{
> +	if (!card || !strlen(card->card))
> +		return -1;
> +
> +	return __open_driver_exact(card->card, card->chipset);
> +}
> +
> +int __drm_open_render(struct igt_device_card *card)
> +{
> +	if (!card || !strlen(card->render))
> +		return -1;
> +
> +	return __open_driver_exact(card->render, card->chipset);
> +}
> +
> +
>  static int at_exit_drm_fd = -1;
>  static int at_exit_drm_render_fd = -1;
>  
> diff --git a/lib/drmtest.h b/lib/drmtest.h
> index 6c4c3899..8f5888a9 100644
> --- a/lib/drmtest.h
> +++ b/lib/drmtest.h
> @@ -45,6 +45,7 @@
>  #define DRIVER_AMDGPU	(1 << 4)
>  #define DRIVER_V3D	(1 << 5)
>  #define DRIVER_PANFROST	(1 << 6)
> +
>  /*
>   * Exclude DRVER_VGEM from DRIVER_ANY since if you run on a system
>   * with vgem as well as a supported driver, you can end up with a
> @@ -76,6 +77,14 @@ int drm_open_driver(int chipset);
>  int drm_open_driver_master(int chipset);
>  int drm_open_driver_render(int chipset);
>  int __drm_open_driver(int chipset);
> +int __drm_open_driver_render(int chipset);
> +
> +/* Multi device API */
> +int __drm_open_card_with_nth_filter(int num, int chipset);
> +int __drm_open_render_with_nth_filter(int num, int chipset);
> +struct igt_device_card;
> +int __drm_open_card(struct igt_device_card *card);
> +int __drm_open_render(struct igt_device_card *card);
>  
>  void gem_quiescent_gpu(int fd);
>  
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 1cbb09f9..9b851175 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -71,6 +71,7 @@
>  #include "igt_sysrq.h"
>  #include "igt_rc.h"
>  #include "igt_list.h"
> +#include "igt_device_scan.h"
>  
>  #define UNW_LOCAL_ONLY
>  #include <libunwind.h>
> @@ -304,6 +305,7 @@ enum {
>  	OPT_DEBUG,
>  	OPT_INTERACTIVE_DEBUG,
>  	OPT_SKIP_CRC,
> +	OPT_DEVICE,
>  	OPT_HELP = 'h'
>  };
>  
> @@ -624,6 +626,7 @@ static void print_usage(const char *help_str, bool output_on_stderr)
>  		   "  --skip-crc-compare\n"
>  		   "  --help-description\n"
>  		   "  --describe\n"
> +		   "  --device filter\n"
>  		   "  --help|-h\n");
>  	if (help_str)
>  		fprintf(f, "%s\n", help_str);
> @@ -725,6 +728,11 @@ static void common_init_env(void)
>  	if (env) {
>  		__set_forced_driver(env);
>  	}
> +
> +	env = getenv("IGT_DEVICE");
> +	if (env) {
> +		igt_device_filter_add(env);
> +	}
>  }
>  
>  static int common_init(int *argc, char **argv,
> @@ -743,6 +751,7 @@ static int common_init(int *argc, char **argv,
>  		{"debug",             optional_argument, NULL, OPT_DEBUG},
>  		{"interactive-debug", optional_argument, NULL, OPT_INTERACTIVE_DEBUG},
>  		{"skip-crc-compare",  no_argument,       NULL, OPT_SKIP_CRC},
> +		{"device",            required_argument, NULL, OPT_DEVICE},
>  		{"help",              no_argument,       NULL, OPT_HELP},
>  		{0, 0, 0, 0}
>  	};
> @@ -865,6 +874,10 @@ static int common_init(int *argc, char **argv,
>  		case OPT_SKIP_CRC:
>  			igt_skip_crc_compare = true;
>  			goto out;
> +		case OPT_DEVICE:
> +			assert(optarg);
> +			igt_device_filter_add(optarg);
> +			break;
>  		case OPT_HELP:
>  			print_usage(help_str, false);
>  			ret = -1;
> diff --git a/lib/igt_device_scan.c b/lib/igt_device_scan.c
> new file mode 100644
> index 00000000..6cf0aea6
> --- /dev/null
> +++ b/lib/igt_device_scan.c
> @@ -0,0 +1,1425 @@
> +/*
> + * 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.h"
> +#include "igt_kmod.h"
> +#include "igt_sysfs.h"
> +#include "igt_device.h"
> +#include "igt_device_scan.h"
> +#include <glib.h>
> +#include <libudev.h>
> +#include <linux/limits.h>
> +#include <sys/stat.h>
> +
> +//#define DEBUG_DEVICE_SCAN
> +#ifdef DEBUG_DEVICE_SCAN
> +#define DBG(...) \
> +{ \
> +	struct timeval tm; \
> +	gettimeofday(&tm, NULL); \
> +	printf("%10ld.%03ld: ", tm.tv_sec, tm.tv_usec); \
> +	printf(__VA_ARGS__); \
> +	}
> +
> +#else
> +#define DBG(...) {}
> +#endif
> +
> +#define IGT_BUS_PCI_DRIVERS_DIR "/sys/bus/pci/drivers"
> +#define IGT_DRM_PATH "/dev/dri"
> +
> +/* Scanning PCI is simple and well defined. Platform devices
> + * are listed in device tree but until driver is really loaded
> + * we only 'expect' their existence.
> + *
> + * From this reason platform scanning was separated to two
> + * distinct scan types - non-discoverable and discoverable.
> + *
> + * 1. Non-discoverable scan type checks modalias attribute.
> + * Such devices will occur within platform subsystem only
> + * after driver is loaded. Example are 'platform:vgem'
> + * 'platform:vkms' and 'platform:exynos'.
> + *
> + * 2. Discoverable scan type checks OF_COMPATIBLE_0 property.
> + * This simple check should be enough to display platform
> + * gpu defined in device tree even if driver wasn't already
> + * loaded.
> + */
> +enum igt_devices_scan_type {
> +	IGT_SCAN_PCI                       = 1,
> +	IGT_SCAN_PLATFORM_NON_DISCOVERABLE = 2,
> +	IGT_SCAN_PLATFORM_DISCOVERABLE     = 3,
> +};
> +
> +static GHashTable *blacklist_keys_ht;      //sysattrs we don't want to read
> +static GHashTable *gpu_pci_class_ht;       //gpu pci classes we know
> +static GHashTable *gpu_vendor_ht;          //search id -> vendor_spec mapping
> +static GHashTable *filter_definition_ht;   //supported filters (pci=..., etc.)
> +
> +/* Generic name->value struct, used to fill hash tables */
> +struct name_value {
> +	const char *name;
> +	gpointer *value;
> +};
> +
> +/* Vendor specific data */
> +struct vendor_spec {
> +	enum igt_device_type dev_type;
> +	const char *vendor;
> +	const char *modname;
> +	const char *modopts;
> +	const char *match_driver; //reported driver name not always equal module name
> +	int chipset;
> +};
> +
> +/* Single igt_device found in PCI / platform subsystems */
> +struct igt_device {
> +	char *pretty_name;
> +	struct vendor_spec *vs;
> +	GHashTable *props_ht;
> +	GHashTable *attrs_ht;
> +	char *syspath;
> +	char *drvpath;
> +	char *drm_card_path;
> +	char *drm_render_path;
> +	char *vendor;
> +	char *device;
> +	enum igt_device_type dev_type;
> +};
> +
> +/* Scanned devices struct */
> +struct igt_devices {
> +	GPtrArray *devs;		//all gpu devices array
> +	GPtrArray *view;		//filtered view
> +	bool devs_scanned;
> +};
> +
> +/* Scanned devices holder */
> +static struct igt_devices igt_devs;
> +
> +/* PCI device classes we take into account during scan.
> + * Attribute PCI_CLASS is compared with key beneath. */
> +struct name_value gpu_pci_class_list[] = {
> +	{ "30000", (gpointer) "VGA compatibile controller" },
> +	{ "30100", (gpointer) "XGA compatibile controller" },
> +	{ "30200", (gpointer) "3D controller" },
> +	{ "38000", (gpointer) "Display controller" },
> +	{ NULL, },
> +};
> +
> +static struct vendor_spec v_intel  = { .dev_type = DEV_PCI,
> +				       .vendor = "Intel",
> +				       .modname = "i915",
> +				       .chipset = DRIVER_INTEL
> +				     };
> +static struct vendor_spec v_amd    = { .dev_type = DEV_PCI,
> +				       .vendor = "AMD",
> +				       .modname = "amdgpu",
> +				       .chipset = DRIVER_AMDGPU
> +				     };
> +static struct vendor_spec v_nvidia  = { .dev_type = DEV_PCI,
> +					.vendor = "NVIDIA",
> +					.modname = "nvidia",
> +					.chipset = DRIVER_ANY
> +				      };
> +static struct vendor_spec v_vgem    = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
> +					.vendor = "Virtual-GEM",
> +					.modname = "vgem",
> +					.chipset = DRIVER_VGEM
> +				      };
> +static struct vendor_spec v_vkms   = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
> +				       .vendor = "Virtual-KMS",
> +				       .modname = "vkms",
> +				       .chipset = DRIVER_ANY
> +				     };
> +static struct vendor_spec v_exynos  = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
> +					.vendor = "Samsung",
> +					.modname = "exynosdrm",
> +					.match_driver = "exynos-drm",
> +					.chipset = DRIVER_ANY
> +				      };
> +static struct vendor_spec v_vc4    = { .dev_type = DEV_PLATFORM_DISCOVERABLE,
> +				       .vendor = "Broadcom",
> +				       .modname = "vc4",
> +				       .match_driver = "vc4-drm",
> +				       .chipset = DRIVER_VC4
> +				     };
> +
> +/* Mapping vendor id => vendor_spec should be unique (vendor matching
> + * is written around this).
> + *
> + * Keys must be defined as follows:
> + * PCI devices: PCI_SLOT_ID,
> + * Non-discoverable platform devices: MODALIAS,
> + * Discoverable platform devices: OF_COMPATIBLE_0.
> +*/
> +struct name_value gpu_vendor_list[] = {
> +	{ "8086",                (gpointer) &v_intel },
> +	{ "1002",                (gpointer) &v_amd },
> +	{ "10DE",                (gpointer) &v_nvidia },
> +	{ "platform:vgem",       (gpointer) &v_vgem },
> +	{ "platform:vkms",       (gpointer) &v_vkms },
> +	{ "platform:exynos-drm", (gpointer) &v_exynos },
> +	{ "brcm,bcm2835-vc4",    (gpointer) &v_vc4 },
> +	{ NULL, },
> +};
> +
> +/* Generic hash table fill function, requires name / value ptrs array */
> +static void fill_ht(GHashTable **ht, struct name_value *data)
> +{
> +	if (*ht)
> +		return;
> +
> +	*ht = g_hash_table_new(g_str_hash, g_str_equal);
> +	igt_assert(*ht);
> +
> +	while (data->name) {
> +		g_hash_table_insert(*ht,
> +				    (gpointer) data->name,
> +				    data->value);
> +		data++;
> +	}
> +}
> +
> +#define get_vendor_spec(prop) \
> +	g_hash_table_lookup(gpu_vendor_ht, prop)
> +
> +/* Go through whole vendor hash table and compare against vendor field.
> + * Used mostly with vendor=... filter parameter when PCI id is not matched.
> +*/
> +static bool is_pci_vendor_name(const char *name)
> +{
> +	bool ret = false;
> +	GList *keys = g_hash_table_get_keys(gpu_vendor_ht);
> +
> +	if (!name)
> +		return false;
> +
> +	while (keys) {
> +		char *k = (char *) keys->data;
> +		struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, k);
> +		keys = g_list_next(keys);
> +
> +		if (vs->dev_type != DEV_PCI)
> +			continue;
> +
> +		if (!strcasecmp(name, vs->vendor)) {
> +			ret = true;
> +			break;
> +		}
> +	}
> +	g_list_free(keys);
> +
> +	return ret;
> +}
> +
> +/* Reading sysattr values can take time (even seconds),
> + * we want to avoid reading such keys.
> +*/
> +static void populate_blacklist_keys(void)
> +{
> +	const char *keys[] = { "config", "modalias",
> +			       "resource",
> +			       "resource0", "resource1", "resource2",
> +			       "resource3", "resource4", "resource5",
> +			       "resource0_wc", "resource1_wc", "resource2_wc",
> +			       "resource3_wc", "resource4_wc", "resource5_wc",
> +			       "driver",
> +			       "uevent", NULL};
> +	const char *key;
> +	int i = 0;
> +
> +	if (blacklist_keys_ht)
> +		return;
> +
> +	blacklist_keys_ht = g_hash_table_new(g_str_hash, g_str_equal);
> +	igt_assert(blacklist_keys_ht);
> +
> +	while ((key = keys[i++]))
> +		g_hash_table_add(blacklist_keys_ht, (gpointer) key);
> +}
> +
> +#define is_on_blacklist(key) \
> +	g_hash_table_contains(blacklist_keys_ht, key)
> +
> +static struct igt_device *igt_device_new(void)
> +{
> +	struct igt_device *dev;
> +	dev = calloc(1, sizeof(struct igt_device));
> +	if (!dev)
> +		return NULL;
> +
> +	dev->attrs_ht = g_hash_table_new_full(g_str_hash, g_str_equal,
> +					      free, free);
> +	dev->props_ht = g_hash_table_new_full(g_str_hash, g_str_equal,
> +					      free, free);
> +
> +	if (dev->attrs_ht && dev->props_ht)
> +		return dev;
> +
> +	return NULL;
> +}
> +
> +static void igt_device_add_prop(struct igt_device *dev,
> +				const char *key, const char *value)
> +{
> +	if (!key || !value)
> +		return;
> +
> +	g_hash_table_insert(dev->props_ht, strdup(key), strdup(value));
> +}
> +
> +static void igt_device_add_attr(struct igt_device *dev,
> +				const char *key, const char *value)
> +{
> +	const char *v = value;
> +
> +	if (!key)
> +		return;
> +
> +	/* It's possible we have symlink at key filename, but udev
> +	 * library resolves only few of them */
> +	if (!v) {
> +		struct stat st;
> +		char path[PATH_MAX];
> +		char linkto[PATH_MAX];
> +		int len;
> +
> +		snprintf(path, sizeof(path), "%s/%s", dev->syspath, key);
> +		if (lstat(path, &st) != 0)
> +			return;
> +
> +		len = readlink(path, linkto, sizeof(linkto));
> +		if (len <= 0 || len == (ssize_t) sizeof(linkto))
> +			return;
> +		linkto[len] = '\0';
> +		v = strrchr(linkto, '/');
> +		if (v == NULL)
> +			return;
> +		v++;
> +	}
> +
> +	g_hash_table_insert(dev->attrs_ht, strdup(key), strdup(v));
> +}
> +
> +static void scan_props(struct udev_device *dev, struct igt_device *idev)
> +{
> +	struct udev_list_entry *entry;
> +
> +	entry = udev_device_get_properties_list_entry(dev);
> +	while (entry) {
> +		const char *name = udev_list_entry_get_name(entry);
> +		const char *value = udev_list_entry_get_value(entry);
> +		igt_device_add_prop(idev, name, value);
> +		entry = udev_list_entry_get_next(entry);
> +		DBG("prop: %s, val: %s\n", name, value);
> +	}
> +}
> +
> +static void scan_attrs(struct udev_device *dev, struct igt_device *idev)
> +{
> +	struct udev_list_entry *entry;
> +
> +	entry = udev_device_get_sysattr_list_entry(dev);
> +	while (entry) {
> +		const char *key = udev_list_entry_get_name(entry);
> +		const char *value;
> +
> +		if (is_on_blacklist(key)) {
> +			entry = udev_list_entry_get_next(entry);
> +			continue;
> +		}
> +
> +		value = udev_device_get_sysattr_value(dev, key);
> +		igt_device_add_attr(idev, key, value);
> +		entry = udev_list_entry_get_next(entry);
> +		DBG("attr: %s, val: %s\n", key, value);
> +	}
> +}
> +
> +#define get_prop(dev, prop) (char *) g_hash_table_lookup(dev->props_ht, prop)
> +#define get_attr(dev, attr) (char *) g_hash_table_lookup(dev->attrs_ht, attr)
> +#define get_prop_pci_id(dev)    get_prop(dev, "PCI_ID")
> +#define get_prop_pci_slot(dev)  get_prop(dev, "PCI_SLOT_NAME")
> +#define get_prop_devpath(dev)   get_prop(dev, "DEVPATH")
> +#define get_prop_driver(dev)    get_prop(dev, "DRIVER")
> +#define get_prop_modalias(dev)  get_prop(dev, "MODALIAS")
> +#define get_prop_of_compatible_0(dev) get_prop(dev, "OF_COMPATIBLE_0")
> +
> +/* Gets PCI_ID property, splits to xxxx:yyyy and stores
> + * xxxx to dev->vendor and yyyy to dev->device for
> + * faster access.
> + */
> +static void set_vendor_device(struct igt_device *dev)
> +{
> +	const char *pci_id = get_prop_pci_id(dev);
> +	if (!pci_id || strlen(pci_id) != 9)
> +		return;
> +	dev->vendor = strndup(pci_id, 4);
> +	dev->device = strndup(pci_id + 5, 4);
> +}
> +
> +static void find_drm_paths(struct igt_device *dev, bool platform_check)
> +{
> +	char dirname[PATH_MAX];
> +	DIR *dir;
> +	const char *driver = get_prop_driver(dev);
> +	const char *devpath = get_prop_devpath(dev);
> +	struct dirent *e;
> +
> +	/* There's no DRIVER, so no /dev/dri/cardX and /dev/dri/renderD
> +	 * files exists. If there's no DEVPATH there's something wrong
> +	 * as well. */
> +	if (!platform_check && (!driver || !devpath))
> +		return;
> +
> +	snprintf(dirname, PATH_MAX, "%s/drm", dev->syspath);
> +	dir = opendir(dirname);
> +	if (!dir)
> +		return;
> +
> +	while((e = readdir(dir))) {
> +		int n;
> +		if (sscanf(e->d_name, "card%d", &n) == 1)
> +			dev->drm_card_path = g_strdup_printf("%s/%s",
> +							     IGT_DRM_PATH,
> +							     e->d_name);
> +		else if (sscanf(e->d_name, "renderD%d", &n) == 1)
> +			dev->drm_render_path = g_strdup_printf("%s/%s",
> +							       IGT_DRM_PATH,
> +							       e->d_name);
> +	}
> +	closedir(dir);
> +
> +	if (dev->drm_card_path)
> +		DBG("card: %s\n", dev->drm_card_path);
> +	if (dev->drm_render_path)
> +		DBG("rend: %s\n", dev->drm_render_path);
> +}
> +
> +static bool prepare_scan(void)
> +{
> +	if (!igt_devs.devs)
> +		igt_devs.devs = g_ptr_array_sized_new(4);
> +	if (!igt_devs.view)
> +		igt_devs.view = g_ptr_array_sized_new(4);
> +
> +	if (!igt_devs.devs || !igt_devs.view)
> +		return false;
> +
> +	return true;
> +}
> +
> +static bool is_valid_pci_gpu(const char *prop)
> +{
> +	return g_hash_table_contains(gpu_pci_class_ht, prop);
> +}
> +
> +static bool is_valid_platform_gpu(const char *prop)
> +{
> +	struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, prop);
> +	if (!vs)
> +		return false;
> +
> +	return vs->dev_type == DEV_PLATFORM_NON_DISCOVERABLE ||
> +			vs->dev_type == DEV_PLATFORM_DISCOVERABLE;
> +}
> +
> +static void add_pci_gpu(struct igt_device *dev)
> +{
> +	find_drm_paths(dev, false);
> +
> +	g_ptr_array_add(igt_devs.devs, dev);
> +	g_ptr_array_add(igt_devs.view, dev);
> +}
> +
> +static void add_platform_non_discoverable_gpu(struct igt_device *dev)
> +{
> +	find_drm_paths(dev, true);
> +	if (!get_prop_driver(dev))
> +		igt_device_add_prop(dev, "DRIVER", dev->vs->modname);
> +
> +	g_ptr_array_add(igt_devs.devs, dev);
> +	g_ptr_array_add(igt_devs.view, dev);
> +}
> +
> +static void add_platform_discoverable_gpu(struct igt_device *dev)
> +{
> +	find_drm_paths(dev, true);
> +	g_ptr_array_add(igt_devs.devs, dev);
> +	g_ptr_array_add(igt_devs.view, dev);
> +}
> +
> +/* Classifier table.
> + * Scanning udev devices looks same for pci and platform devices, but differs
> + * in logic regarding adding devices to the device array.
> +*/
> +struct {
> +	bool (*is_valid)(const char *prop); //check is device belongs to the class
> +	void (*add)(struct igt_device *dev); //add device to the array and aux stuff
> +	unsigned int dev_type;
> +	const char *subsys;
> +	const char *findprop;
> +} cls[] = {
> +	[IGT_SCAN_PCI] = { .subsys = "pci",
> +			   .findprop = "PCI_CLASS",
> +			   .dev_type = DEV_PCI,
> +			   .is_valid = is_valid_pci_gpu,
> +			   .add = add_pci_gpu,
> +			 },
> +	[IGT_SCAN_PLATFORM_NON_DISCOVERABLE] = { .subsys = "platform",
> +						 .findprop = "MODALIAS",
> +						 .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
> +						 .is_valid = is_valid_platform_gpu,
> +						 .add = add_platform_non_discoverable_gpu,
> +					       },
> +	[IGT_SCAN_PLATFORM_DISCOVERABLE] = { .subsys = "platform",
> +					     .findprop = "OF_COMPATIBLE_0",
> +					     .dev_type = DEV_PLATFORM_DISCOVERABLE,
> +					     .is_valid = is_valid_platform_gpu,
> +					     .add = add_platform_discoverable_gpu,
> +					   },
> +};
> +
> +static void scan_devices(enum igt_devices_scan_type scantype)
> +{
> +	struct udev *udev;
> +	struct udev_device *dev;
> +	struct udev_enumerate *enumerate;
> +	struct udev_list_entry *devices, *dev_list_entry;
> +	int ret;
> +
> +	udev = udev_new();
> +	igt_assert(udev);
> +
> +	enumerate = udev_enumerate_new(udev);
> +	igt_assert(enumerate);
> +
> +	DBG("Scanning subsystem %s\n", cls[scantype].subsys);
> +	ret = udev_enumerate_add_match_subsystem(enumerate, cls[scantype].subsys);
> +	igt_assert(!ret);
> +
> +	ret = udev_enumerate_scan_devices(enumerate);
> +	igt_assert(!ret);
> +
> +	devices = udev_enumerate_get_list_entry(enumerate);
> +	if (!devices)
> +		return;
> +
> +	udev_list_entry_foreach(dev_list_entry, devices) {
> +		struct igt_device *idev;
> +		const char *path;
> +		const char *prop;
> +
> +		path = udev_list_entry_get_name(dev_list_entry);
> +		dev = udev_device_new_from_syspath(udev, path);
> +		prop = udev_device_get_property_value(dev, cls[scantype].findprop);
> +		if (!prop || !cls[scantype].is_valid(prop)) {
> +			udev_device_unref(dev);
> +			continue;
> +		}
> +		idev = igt_device_new();
> +		igt_assert(idev);
> +
> +		idev->dev_type = cls[scantype].dev_type;
> +		idev->syspath = strdup(udev_device_get_syspath(dev));
> +		scan_props(dev, idev);
> +		scan_attrs(dev, idev);
> +
> +		switch (idev->dev_type) {
> +		case DEV_PCI:
> +			set_vendor_device(idev);
> +			idev->pretty_name = strdup(get_prop_pci_slot(idev));
> +			idev->vs = get_vendor_spec(idev->vendor);
> +			break;
> +		default:
> +			idev->pretty_name = strdup(prop);
> +			idev->vs = get_vendor_spec(prop);
> +		}
> +		igt_assert(idev->vs);
> +
> +		cls[scantype].add(idev);
> +		udev_device_unref(dev);
> +	}
> +	udev_enumerate_unref(enumerate);
> +	udev_unref(udev);
> +}
> +
> +struct name_value filter_definition_list[];
> +static void populate_gpu_data(void)
> +{
> +	fill_ht(&gpu_pci_class_ht, &gpu_pci_class_list[0]);
> +	fill_ht(&gpu_vendor_ht, &gpu_vendor_list[0]);
> +	fill_ht(&filter_definition_ht, &filter_definition_list[0]);
> +}
> +
> +static void igt_device_free(struct igt_device *dev)
> +{
> +	free(dev->pretty_name);
> +	free(dev->device);
> +	free(dev->drm_card_path);
> +	free(dev->drm_render_path);
> +	free(dev->drvpath);
> +	free(dev->syspath);
> +	free(dev->vendor);
> +	g_hash_table_destroy(dev->attrs_ht);
> +	g_hash_table_destroy(dev->props_ht);
> +}
> +
> +void igt_devices_scan(bool force)
> +{
> +	if (force && igt_devs.devs_scanned) {
> +		for (int i = 0; i < igt_devs.devs->len; i++) {
> +			struct igt_device *dev = g_ptr_array_index(igt_devs.devs, i);
> +			igt_device_free(dev);
> +			free(dev);
> +		}
> +		igt_devs.devs_scanned = false;
> +		g_ptr_array_free(igt_devs.view, true);
> +		g_ptr_array_free(igt_devs.devs, true);
> +		igt_devs.view = NULL;
> +		igt_devs.devs = NULL;
> +	}
> +
> +	if (igt_devs.devs_scanned)
> +		return;
> +
> +	populate_blacklist_keys();      //keys from sysattr we skip
> +	populate_gpu_data();
> +
> +	prepare_scan();
> +	scan_devices(IGT_SCAN_PCI);
> +	scan_devices(IGT_SCAN_PLATFORM_NON_DISCOVERABLE);
> +	scan_devices(IGT_SCAN_PLATFORM_DISCOVERABLE);
> +	igt_devs.devs_scanned = true;
> +}
> +
> +#define pr_simple(k, v) printf("    %-16s: %s\n", k, v)
> +#define pr_simple_prop(dev, key) pr_simple(key, get_prop(dev, key))
> +#define pr_simple_attr(dev, key) pr_simple(key, get_attr(dev, key))
> +
> +static void print_vendor(struct igt_device *dev)
> +{
> +	struct vendor_spec *vs = dev->vs;
> +	char *info = alloca(256);
> +
> +	snprintf(info, 256, "%s (%s, module: %s%s)", dev->vendor,
> +		 vs ? vs->vendor : "unknown",
> +		 vs ? vs->modname : "unknown",
> +		 dev->drm_card_path ? "" : " [not binded/loaded]");
> +	pr_simple("vendor", info);
> +}
> +
> +static void igt_devs_print_simple(GPtrArray *view,
> +				  bool show_props, bool show_attrs)
> +{
> +	struct igt_device *dev;
> +	int i;
> +
> +	if (!view)
> +		return;
> +
> +	for (i = 0; i < view->len; i++) {
> +		dev = g_ptr_array_index(view, i);
> +		printf("%s\n", dev->pretty_name);
> +		pr_simple("syspath", dev->syspath);
> +		pr_simple("drm card", dev->drm_card_path);
> +		pr_simple("drm render", dev->drm_render_path);
> +
> +		if (show_props) {
> +			if (get_prop_pci_id(dev))
> +				pr_simple_prop(dev, "PCI_ID");
> +			pr_simple_prop(dev, "DRIVER");
> +		}
> +
> +		/* We show attrs only for PCI devices */
> +		if (show_attrs && dev->dev_type == DEV_PCI) {
> +			print_vendor(dev);
> +			pr_simple("device", dev->device);
> +		}
> +	}
> +}
> +
> +#define pr_detail(k, v) printf("%-32s: %s\n", k, v)
> +
> +static void print_ht(GHashTable *ht)
> +{
> +	GList *keys = g_hash_table_get_keys(ht);
> +	keys = g_list_sort(keys, (GCompareFunc) strcmp);
> +	while (keys) {
> +		char *k = (char *) keys->data;
> +		char *v = g_hash_table_lookup(ht, k);
> +		pr_detail(k, v);
> +		keys = g_list_next(keys);
> +	}
> +	g_list_free(keys);
> +}
> +
> +static void igt_devs_print_detail(GPtrArray *view,
> +				  bool show_props, bool show_attrs)
> +{
> +	struct igt_device *dev;
> +	int i;
> +	(void) show_props;
> +	(void) show_attrs;
> +
> +	if (!view)
> +		return;
> +
> +	for (i = 0; i < view->len; i++) {
> +		dev = g_ptr_array_index(view, i);
> +		printf("========== %s ==========\n", dev->pretty_name);
> +		printf("[drm]\n");
> +		pr_detail("card device", dev->drm_card_path);
> +		pr_detail("render device", dev->drm_render_path);
> +		pr_detail("syspath", dev->syspath);
> +
> +		printf("\n[properties]\n");
> +		print_ht(dev->props_ht);
> +		printf("\n[attributes]\n");
> +		print_ht(dev->attrs_ht);
> +	}
> +}
> +
> +static struct print_func {
> +	void (*prn)(GPtrArray *, bool, bool);
> +} print_functions[] = {
> +	[IGT_PRINT_SIMPLE] = { .prn = igt_devs_print_simple },
> +	[IGT_PRINT_DETAIL] = { .prn = igt_devs_print_detail },
> +};
> +
> +void igt_devices_print(enum igt_devices_print_type printtype,
> +		       bool show_props,
> +		       bool show_attrs)
> +{
> +	print_functions[printtype].prn(igt_devs.view,
> +				       show_props, show_attrs);
> +}
> +
> +void igt_devices_print_vendors(void)
> +{
> +	struct name_value *list = &gpu_vendor_list[0];
> +	printf("Recognized vendors:\n");
> +
> +	printf("%-16s %-32s %-32s %-16s\n", "Device type", "id",
> +	       "vendor", "module");
> +	while (list->name) {
> +		char *k = (char *) list->name;
> +		struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, k);
> +		if (vs->dev_type == DEV_PCI)
> +			printf("%-16s %-32s %-32s %-16s\n", "pci",
> +			       k, vs->vendor, vs->modname);
> +		else if (vs->dev_type == DEV_PLATFORM_NON_DISCOVERABLE)
> +			printf("%-16s %-32s %-32s %-16s\n", "non-discoverable",
> +			       k, vs->vendor, vs->modname);
> +		else if (vs->dev_type == DEV_PLATFORM_DISCOVERABLE)
> +			printf("%-16s %-32s %-32s %-16s\n", "discoverable",
> +			       k, vs->vendor, vs->modname);
> +		list++;
> +	}
> +}
> +
> +static bool sys_write_attr(const char *dirname, const char *attr,
> +			   const char *str)
> +{
> +	int dir;
> +
> +	dir = open(dirname, O_RDONLY);
> +	if (dir < 0)
> +		return false;
> +
> +	if (igt_sysfs_printf(dir, attr, "%s", str) < 0) {
> +		printf("Error, can't write to %s, err: %s\n",
> +		       attr, strerror(errno));
> +		close(dir);
> +		return false;
> +	}
> +
> +	close(dir);
> +	return true;
> +}
> +
> +static bool ensure_driver_is_loaded(const char *modname, const char *modopts)
> +{
> +	if (!modname)
> +		return false;
> +
> +	if (!igt_kmod_is_loaded(modname)) {
> +		int ret;
> +		igt_debug("Loading module %s\n", modname);
> +		ret = igt_kmod_load(modname, modopts);
> +		if (ret) {
> +			igt_warn("Can't load module %s\n", modname);
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +#define ensure_device_has_driver_loaded(dev) \
> +	ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts)
> +
> +static struct igt_device *igt_devices_find_pci_id(const char *pci_slot)
> +{
> +	struct igt_device *dev;
> +	int i;
> +	GPtrArray *arr = igt_devs.devs;
> +
> +	if (!pci_slot)
> +		return NULL;
> +
> +	for (i = 0; i < arr->len; i++) {
> +		dev = g_ptr_array_index(arr, i);
> +		if (dev->dev_type != DEV_PCI)
> +			continue;
> +		if (!strcmp(pci_slot, get_prop_pci_slot(dev)))
> +			return dev;
> +	}
> +
> +	return NULL;
> +}
> +
> +bool igt_device_pci_unbind_module(const char *pci_slot)
> +{
> +	char path[PATH_MAX];
> +	struct igt_device *dev = igt_devices_find_pci_id(pci_slot);
> +
> +	if (!dev)
> +		return false;
> +
> +	if (dev->dev_type != DEV_PCI)
> +		return false;
> +
> +	igt_assert(dev->vs);
> +
> +	if (!ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts))
> +		return false;
> +
> +	igt_info("Unbinding driver %s on %s\n", dev->vs->modname, pci_slot);
> +	snprintf(path, PATH_MAX, "%s/%s", IGT_BUS_PCI_DRIVERS_DIR,
> +		 dev->vs->modname);
> +
> +	return sys_write_attr(path, "unbind", pci_slot);
> +}
> +
> +bool igt_device_pci_bind_module(const char *pci_slot)
> +{
> +	char path[PATH_MAX];
> +	struct igt_device *dev = igt_devices_find_pci_id(pci_slot);
> +
> +	if (!dev)
> +		return false;
> +
> +	if (dev->dev_type != DEV_PCI)
> +		return false;
> +
> +	igt_assert(dev->vs);
> +
> +	if (!ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts))
> +		return false;
> +
> +	igt_info("Binding driver %s to %s\n", dev->vs->modname, pci_slot);
> +	snprintf(path, PATH_MAX, "%s/%s", IGT_BUS_PCI_DRIVERS_DIR,
> +		 dev->vs->modname);
> +
> +	return sys_write_attr(path, "bind", pci_slot);
> +}
> +
> +static bool pci_rebind_module(struct igt_device *dev)
> +{
> +	if (dev && dev->dev_type != DEV_PCI)
> +		return false;
> +
> +	igt_device_pci_unbind_module(get_prop_pci_slot(dev));
> +	igt_device_pci_bind_module(get_prop_pci_slot(dev));
> +
> +	return true;
> +}
> +
> +void igt_devices_bind_modules(void)
> +{
> +	struct igt_device *dev;
> +	int i;
> +	GPtrArray *arr;
> +	printf("Binding modules...\n");
> +
> +	arr = igt_devs.devs;
> +	for (i = 0; i < arr->len; i++) {
> +		dev = g_ptr_array_index(arr, i);
> +		if (!dev->drm_card_path) {
> +			pci_rebind_module(dev);
> +		}
> +	}
> +}
> +
> +/* ------------------------------------------------------------------------- */
> +#define FILTER_SPEC_NAME_LEN 32
> +#define FILTER_SPEC_DATA_LEN 256
> +struct filter_spec {
> +	char name[FILTER_SPEC_NAME_LEN];
> +	char data[FILTER_SPEC_DATA_LEN];
> +};
> +
> +struct filter_func {
> +	GPtrArray *(*filter_function)(struct filter_spec *fspec,
> +				      struct filter_func *ffunc);
> +	const char *help;
> +	const char *detail;
> +
> +	struct vendor_spec *vs; //pointer to vs (if any)
> +};
> +
> +union filter_data {
> +	struct {
> +		char *card;
> +	} drm;
> +	struct {
> +		char *vendor;
> +		char *device;
> +		char *card;
> +	} pci;
> +	struct {
> +		char *card;
> +	} module;
> +	struct {
> +		char *card;
> +	} compatible;
> +};
> +
> +static GHashTable *split_filter_data(const char *data)
> +{
> +	GHashTable *ht = g_hash_table_new_full(g_str_hash, g_str_equal,
> +					       free, free);
> +	gchar **s;
> +	gchar **strv = g_strsplit(data, ",", -1);
> +
> +	s = strv;
> +	while (*s) {
> +		char *k, *v;
> +		v = strchr(*s, '=');
> +		if (!v) {
> +			s++;
> +			continue;
> +		}
> +		k = strndup(*s, v - (*s));
> +		v = strdup(v + 1);
> +		g_hash_table_insert(ht, k, v);
> +		s++;
> +	}
> +	g_strfreev(strv);
> +
> +	return ht;
> +}
> +
> +static bool get_filter_spec(const char *filter, struct filter_spec *spec)
> +{
> +	if (!filter)
> +		return false;
> +
> +	if (sscanf(filter, "%31[^:]:%255s", spec->name, spec->data) == 2)
> +		return true;
> +
> +	return false;
> +}
> +
> +static GPtrArray *filter_drm(struct filter_spec *fspec,
> +			     struct filter_func *ffunc)
> +{
> +	GHashTable *ht;
> +	GPtrArray *devs, *view;
> +	union filter_data fdata;
> +	int card = -1;
> +	char cardstr[NAME_MAX];
> +
> +	(void) ffunc;
> +	DBG("filter drm\n");
> +
> +	ht = split_filter_data(fspec->data);
> +	fdata.drm.card = g_hash_table_lookup(ht, "card");
> +
> +	view = igt_devs.view;
> +	devs = igt_devs.devs;
> +
> +	if (fdata.drm.card) {
> +		sscanf(fdata.drm.card, "%d", &card);
> +		if (card < 0) {
> +			g_hash_table_destroy(ht);
> +			return view;
> +		}
> +	}
> +
> +	snprintf(cardstr, NAME_MAX, "%s/card%d", IGT_DRM_PATH, card);
> +	for (int i = 0; i < devs->len; i++) {
> +		struct igt_device *dev = g_ptr_array_index(devs, i);
> +		if (dev->drm_card_path && !strcmp(cardstr, dev->drm_card_path)) {
> +			g_ptr_array_add(view, dev);
> +			break;
> +		}
> +	}
> +
> +	DBG("Filter drm view size: %d\n", view->len);
> +
> +	g_hash_table_destroy(ht);
> +
> +	return view;
> +}
> +
> +static GPtrArray *filter_pci(struct filter_spec *fspec,
> +			     struct filter_func *ffunc)
> +{
> +	GHashTable *ht;
> +	GPtrArray *devs, *view;
> +	union filter_data fdata;
> +
> +	DBG("filter pci\n");
> +
> +	(void) ffunc;
> +	ht = split_filter_data(fspec->data);
> +	fdata.pci.vendor = g_hash_table_lookup(ht, "vendor");
> +	fdata.pci.device = g_hash_table_lookup(ht, "device");
> +	fdata.pci.card = g_hash_table_lookup(ht, "card");
> +
> +	view = igt_devs.view;
> +	devs = igt_devs.devs;
> +
> +	for (int i = 0; i < devs->len; i++) {
> +		struct igt_device *dev = g_ptr_array_index(devs, i);
> +
> +		if (dev->dev_type != DEV_PCI)
> +			continue;
> +
> +		/* Skip if 'vendor' doesn't match (hex or name) */
> +		if (fdata.pci.vendor &&
> +				strcasecmp(fdata.pci.vendor, dev->vendor) &&
> +				!is_pci_vendor_name(fdata.pci.vendor))
> +			continue;
> +
> +		/* Skip if 'device' doesn't match */
> +		if (fdata.pci.device &&
> +				strcasecmp(fdata.pci.device, dev->device))
> +			continue;
> +
> +		g_ptr_array_add(view, dev);
> +	}
> +
> +	/* Leave only n-th element if card is set */
> +	if (fdata.pci.card) {
> +		int card = -1;
> +		sscanf(fdata.pci.card, "%d", &card);
> +
> +		if (card >= 0 && card < view->len) {
> +			struct igt_device *dev = g_ptr_array_index(view, card);
> +			g_ptr_array_remove_range(view, 0, view->len);
> +			g_ptr_array_add(view, dev);
> +		} else {
> +			g_ptr_array_remove_range(view, 0, view->len);
> +		}
> +	}
> +	DBG("Filter pci view size: %d\n", view->len);
> +
> +	g_hash_table_destroy(ht);
> +
> +	return view;
> +}
> +
> +/*
> + * Refresh view finding first card matching with
> + * vs->match_driver or vs->modname if vs->match_driver is not set
> + */
> +static GPtrArray *filter_module(struct filter_spec *fspec,
> +				struct filter_func *ffunc)
> +{
> +	GHashTable *ht;
> +	GPtrArray *devs, *view;
> +	union filter_data fdata;
> +	int card = -1;
> +	const char *driver = ffunc->vs->match_driver ?: ffunc->vs->modname;
> +
> +	DBG("filter module [%s]\n", driver);
> +
> +	ht = split_filter_data(fspec->data);
> +	fdata.module.card = g_hash_table_lookup(ht, "card");
> +
> +	view = igt_devs.view;
> +	devs = igt_devs.devs;
> +
> +	if (fdata.module.card) {
> +		sscanf(fdata.module.card, "%d", &card);
> +		if (card < 0) {
> +			g_hash_table_destroy(ht);
> +			return view;
> +		}
> +	}
> +
> +	for (int i = 0; i < devs->len; i++) {
> +		const char *modname;
> +		struct igt_device *dev = g_ptr_array_index(devs, i);
> +
> +		modname = get_prop_driver(dev);
> +		if (!modname)
> +			continue;
> +
> +		if (!strcmp(modname, driver)) {
> +			if (!card) {
> +				g_ptr_array_add(view, dev);
> +				break;
> +			}
> +			card--;
> +		}
> +	}
> +
> +	DBG("Filter view size: %d\n", view->len);
> +
> +	g_hash_table_destroy(ht);
> +
> +	return view;
> +}
> +
> +static const char *find_vendor_id_by_vendor_spec(struct vendor_spec *vs)
> +{
> +	struct name_value *list = &gpu_vendor_list[0];
> +
> +	while (list->name) {
> +		if ((struct vendor_spec *) list->value == vs)
> +			return list->name;
> +		list++;
> +	}
> +	return NULL;
> +}
> +
> +static GPtrArray *filter_compatible(struct filter_spec *fspec,
> +				    struct filter_func *ffunc)
> +{
> +	GHashTable *ht;
> +	GPtrArray *devs, *view;
> +	union filter_data fdata;
> +	int card = -1;
> +	const char *of_compatible = find_vendor_id_by_vendor_spec(ffunc->vs);
> +
> +	DBG("filter compatible[%s]\n", of_compatible);
> +
> +	ht = split_filter_data(fspec->data);
> +	fdata.module.card = g_hash_table_lookup(ht, "card");
> +
> +	view = igt_devs.view;
> +	devs = igt_devs.devs;
> +
> +	if (fdata.module.card) {
> +		sscanf(fdata.module.card, "%d", &card);
> +		if (card < 0) {
> +			g_hash_table_destroy(ht);
> +			return view;
> +		}
> +	}
> +
> +	for (int i = 0; i < devs->len; i++) {
> +		const char *compat;
> +		struct igt_device *dev = g_ptr_array_index(devs, i);
> +
> +		if (!of_compatible)
> +			break;
> +
> +		compat = get_prop_of_compatible_0(dev);
> +		if (compat && !strcmp(compat, of_compatible)) {
> +			if (!card) {
> +				g_ptr_array_add(view, dev);
> +				break;
> +			}
> +			card--;
> +		}
> +	}
> +
> +	DBG("Filter view size: %d\n", view->len);
> +
> +	g_hash_table_destroy(ht);
> +
> +	return view;
> +}
> +
> +
> +static struct filter_func f_drm = { .filter_function = filter_drm,
> +				    .help = "drm:[card=%d]",
> +				    .detail = "card is N-card number (from /dev/dri/cardN)\n",
> +				  };
> +
> +static struct filter_func f_pci = { .filter_function = filter_pci,
> +				    .help = "pci:[vendor=%04x/name][,device=%04x][,card=%d]",
> +				    .detail = "vendor is hex number or name",
> +				  };
> +
> +static struct filter_func f_vgem = { .filter_function = filter_module,
> +				     .help = "vgem:[card=%d]",
> +				     .detail = "card is n-th vgem card number\n",
> +				     .vs = &v_vgem,
> +				   };
> +
> +static struct filter_func f_vkms = { .filter_function = filter_module,
> +				     .help = "vkms:[card=%d]",
> +				     .detail = "card is n-th vkms card number\n",
> +				     .vs = &v_vkms,
> +				   };
> +
> +static struct filter_func f_exynos = { .filter_function = filter_module,
> +				       .help = "exynos:[card=%d]",
> +				       .detail = "card is n-th exynos-drm card number\n",
> +				       .vs = &v_exynos,
> +				     };
> +
> +static struct filter_func f_vc4 = { .filter_function = filter_compatible,
> +				    .help = "vc4:[card=%d]",
> +				    .detail = "card is n-th vc4 card number\n",
> +				    .vs = &v_vc4,
> +				  };
> +
> +struct name_value filter_definition_list[] = {
> +	{ "drm",     (gpointer) &f_drm },
> +	{ "pci",     (gpointer) &f_pci },
> +	{ "vgem",    (gpointer) &f_vgem },
> +	{ "vkms",    (gpointer) &f_vkms },
> +	{ "exynos",  (gpointer) &f_exynos },
> +	{ "vc4",     (gpointer) &f_vc4 },
> +	{ NULL, },
> +};
> +
> +void igt_device_print_filter_types(void)
> +{
> +	GList *keys = g_hash_table_get_keys(filter_definition_ht);
> +
> +	printf("Filter types:\n---\n");
> +	printf("%-8s  %s\n---\n", "filter", "syntax");
> +	while (keys) {
> +		char *k = (char *) keys->data;
> +		struct filter_func *v = g_hash_table_lookup(filter_definition_ht, k);
> +		printf("%-8s  %s\n", k, v->help);
> +		printf("%-8s  %s\n", "", v->detail);
> +		keys = g_list_next(keys);
> +	}
> +	g_list_free(keys);
> +}
> +
> +static GPtrArray *device_filters = NULL;
> +
> +#define DEVICE_FILTER_CHECK_ALLOC() \
> +	do { \
> +		if (!device_filters) \
> +			device_filters = g_ptr_array_new_full(2, free); \
> +		igt_assert(device_filters); \
> +	} while(0)
> +
> +int igt_device_filter_count(void)
> +{
> +	DEVICE_FILTER_CHECK_ALLOC();
> +
> +	return device_filters->len;
> +}
> +
> +int igt_device_filter_add(const char *filter)
> +{
> +	gchar **strv, **s;
> +	int c = 0;
> +
> +	DEVICE_FILTER_CHECK_ALLOC();
> +
> +	strv = g_strsplit(filter, ";", -1);
> +
> +	s = strv;
> +	while (*s) {
> +		g_ptr_array_add(device_filters, strdup(*s));
> +		s++;
> +	}
> +	g_strfreev(strv);
> +
> +	return c;
> +}
> +
> +const char *igt_device_filter_get(int num)
> +{
> +	DEVICE_FILTER_CHECK_ALLOC();
> +
> +	if (num < 0 || num >= device_filters->len)
> +		return NULL;
> +
> +	return g_ptr_array_index(device_filters, num);
> +}
> +
> +#define dev_is_non_discoverable(vs) \
> +	((vs)->dev_type == DEV_PLATFORM_NON_DISCOVERABLE)
> +
> +bool igt_device_filter_apply(const char *filter)
> +{
> +	struct filter_spec fspec;
> +	struct filter_func *ffunc;
> +	bool ret;
> +
> +	if (!filter)
> +		return false;
> +
> +	ret = get_filter_spec(filter, &fspec);
> +	if (!ret) {
> +		igt_warn("Can't split filter [%s]\n", filter);
> +		return false;
> +	}
> +
> +	ffunc = g_hash_table_lookup(filter_definition_ht, fspec.name);
> +	if (!ffunc) {
> +		igt_warn("No filter with name [%s]\n", fspec.name);
> +		return false;
> +	}
> +
> +	/* Clean view */
> +	g_ptr_array_remove_range(igt_devs.view, 0, igt_devs.view->len);
> +
> +	/* Load module only for non-discoverable devices.
> +	 * For discoverables / PCI defer this to card match path
> +	*/
> +	if (ffunc->vs && dev_is_non_discoverable(ffunc->vs) &&
> +			!igt_kmod_is_loaded(ffunc->vs->modname)) {
> +		if (!igt_kmod_load(ffunc->vs->modname, ffunc->vs->modopts)) {
> +			igt_info("Module %s loaded, rescanning devices\n",
> +				 ffunc->vs->modname);
> +			igt_devices_scan(true);
> +		} else {
> +			return false;
> +		}
> +	}
> +
> +	ffunc->filter_function(&fspec, ffunc);
> +
> +	return true;
> +}
> +
> +bool igt_device_filter_apply_nth(int num)
> +{
> +	const char *filter = igt_device_filter_get(num);
> +	if (!filter)
> +		return false;
> +
> +	return igt_device_filter_apply(filter);
> +}
> +
> +/* For PCI devices load or unbind / bind is called in card matching path.
> + * This is for 'lsgpu' tool which doesn't want to make magic driver
> + * loading job in filter apply path (it really want to display PCI devices
> + * in the state they really are).
> +*/
> +static bool ensure_device_is_ready(struct igt_device *dev,
> +				   const char *filter)
> +{
> +	if (dev->dev_type != DEV_PCI &&
> +		dev->dev_type != DEV_PLATFORM_DISCOVERABLE)
> +		return false;
> +
> +	if (dev->drm_card_path || dev->drm_render_path)
> +		return true;
> +
> +	/* So, we have PCI device or discoverable device without
> +	 * card/render node, lets try at the beginning to simply
> +	 * load the module */
> +	if (!ensure_device_has_driver_loaded(dev))
> +		return false; //oops!
> +
> +	/* Rescan devices, filter again and get the device */
> +	igt_devices_scan(true);
> +	if (igt_device_filter_apply(filter) == false)
> +		return false;
> +
> +	if (!igt_devs.view->len)
> +		return false;
> +
> +	dev = g_ptr_array_index(igt_devs.view, 0);
> +	if (dev->drm_card_path || dev->drm_render_path)
> +		return true;
> +
> +	/* For platform device unbind/bind is not performed */
> +	if (dev->dev_type == DEV_PLATFORM_DISCOVERABLE)
> +		return false;
> +
> +	/* In this moment we likely already had driver loaded,
> +	 * but it wasn't binded to this device. */
> +	igt_device_pci_unbind_module(get_prop_pci_slot(dev));
> +	igt_device_pci_bind_module(get_prop_pci_slot(dev));
> +	igt_devices_scan(true);
> +	if (igt_device_filter_apply(filter) == false)
> +		return false;
> +
> +	if (!igt_devs.view->len)
> +		return false;
> +
> +	dev = g_ptr_array_index(igt_devs.view, 0);
> +	if (dev->drm_card_path || dev->drm_render_path)
> +		return true;
> +
> +	return false;
> +}
> +
> +#define safe_strncpy(dst, src, size) \
> +	if (src) strncpy((dst), (src), (size))
> +/*
> + * Returns:
> + * false - no card pointer was passed or card wasn't matched,
> + * true - card matched and returned.
> + */
> +bool igt_device_card_match(const char *filter, struct igt_device_card *card)
> +{	
> +	struct igt_device *dev = NULL;
> +
> +	if (!card)
> +		return false;
> +	memset(card, 0, sizeof(*card));
> +
> +	igt_devices_scan(false);
> +
> +	if (igt_device_filter_apply(filter) == false)
> +		return false;
> +
> +	if (!igt_devs.view->len)
> +		return false;
> +
> +	dev = g_ptr_array_index(igt_devs.view, 0);
> +	if (dev->dev_type == DEV_PCI ||
> +		dev->dev_type == DEV_PLATFORM_DISCOVERABLE) {
> +		if (!ensure_device_is_ready(dev, filter))
> +			return false;
> +	}
> +
> +	if (!igt_devs.view->len) //additional check because rescan could happen
> +		return false;
> +
> +	/* We take first one if more than one card matches filter */
> +	dev = g_ptr_array_index(igt_devs.view, 0);
> +	card->dev_type = dev->dev_type;
> +	card->chipset = dev->vs->chipset;
> +	if (dev->dev_type == DEV_PCI) {
> +		safe_strncpy(card->module, dev->vs->modname, NAME_MAX);
> +		safe_strncpy(card->pci_slot, get_prop_pci_slot(dev),
> +			PCI_SLOT_LENGTH);
> +	} else {
> +		safe_strncpy(card->module, get_prop_driver(dev), NAME_MAX);
> +	}
> +	safe_strncpy(card->card, dev->drm_card_path, NAME_MAX);
> +	safe_strncpy(card->render, dev->drm_render_path, NAME_MAX);
> +
> +	return true;
> +}
> diff --git a/lib/igt_device_scan.h b/lib/igt_device_scan.h
> new file mode 100644
> index 00000000..0e2adf13
> --- /dev/null
> +++ b/lib/igt_device_scan.h
> @@ -0,0 +1,72 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef __IGT_DEVICE_SCAN_H__
> +#define __IGT_DEVICE_SCAN_H__
> +
> +#include <limits.h>
> +#include <igt.h>
> +
> +enum igt_devices_print_type {
> +	IGT_PRINT_SIMPLE,
> +	IGT_PRINT_DETAIL,
> +};
> +
> +enum igt_device_type {
> +	DEV_PCI,
> +	DEV_PLATFORM_NON_DISCOVERABLE,
> +	DEV_PLATFORM_DISCOVERABLE,
> +};
> +
> +#define PCI_SLOT_LENGTH 13
> +struct igt_device_card {
> +	enum igt_device_type dev_type;
> +	unsigned int chipset; //contains DRIVER_XXX value
> +	char module[NAME_MAX];
> +	char card[NAME_MAX];
> +	char render[NAME_MAX];
> +	char pci_slot[PCI_SLOT_LENGTH];
> +};
> +
> +void igt_devices_scan(bool force);
> +void igt_devices_print(enum igt_devices_print_type printtype,
> +		       bool show_props, bool show_attrs);
> +
> +void igt_devices_print_vendors(void);
> +
> +bool igt_device_pci_unbind_module(const char *pci_slot);
> +bool igt_device_pci_bind_module(const char *pci_slot);
> +void igt_devices_bind_modules(void);
> +
> +void igt_device_print_filter_types(void);
> +
> +int igt_device_filter_count(void);
> +int igt_device_filter_add(const char *filter);
> +const char *igt_device_filter_get(int num);
> +bool igt_device_filter_apply(const char *filter);
> +bool igt_device_filter_apply_nth(int num);
> +
> +bool igt_device_card_match(const char *filter, struct igt_device_card *card);
> +
> +#endif /* __IGT_DEVICE_SCAN_H__ */
> diff --git a/lib/meson.build b/lib/meson.build
> index 157624e7..826ebbe3 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -10,6 +10,7 @@ lib_sources = [
>  	'igt_color_encoding.c',
>  	'igt_debugfs.c',
>  	'igt_device.c',
> +	'igt_device_scan.c',
>  	'igt_aux.c',
>  	'igt_gpu_power.c',
>  	'igt_gt.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..b784ca65
> --- /dev/null
> +++ b/tools/lsgpu.c
> @@ -0,0 +1,285 @@
> +/*
> + * 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>
> +
> +enum {
> +	OPT_SHOW_PROPS     = 'r',
> +	OPT_SHOW_ATTRS     = 'a',
> +	OPT_PRINT_SIMPLE   = 'i',
> +	OPT_PRINT_DETAIL   = 'd',
> +	OPT_LIST_VENDORS   = 'v',
> +	OPT_BIND_MODULES   = 'B',
> +	OPT_UNBIND_MODULE  = 'u',
> +	OPT_BIND_MODULE    = 'b',
> +	OPT_LIST_FILTERS   = 'l',
> +	OPT_SET_FILTER     = 's',
> +	OPT_MATCH_DEVICE   = 'm',
> +	OPT_DEVICE         = 'D',
> +	OPT_HELP           = 'h'
> +};
> +
> +bool g_show_props;
> +bool g_show_attrs;
> +bool g_show_vendors;
> +bool g_bind_modules;
> +char *g_pci_id;
> +bool g_unbind_module;
> +bool g_bind_module;
> +bool g_list_filters;
> +char *g_filter;
> +char *g_match_filter;
> +bool g_device;
> +bool g_help;
> +
> +static const char *usage_str =
> +	"usage: lsgpu [options]\n\n"
> +	"Options:\n"
> +	"  -r, --show-props            Show device properties\n"
> +	"  -a, --show-attrs            Show device attributes\n"
> +	"  -i, --print-simple          Print devices as simple list (default)\n"
> +	"  -d, --print-details         Print devices with details\n"
> +	"  -v, --list-vendors          List recognized vendors\n"
> +	"  -B, --bind-modules          Bind modules to unbound PCI cards\n"
> +	"  -u, --unbind-module pci_id  Unbind module from pci id device\n"
> +	"  -b, --bind-module pci_id    Bind module to pci id device\n"
> +	"  -l, --list-filter-types     List registered device filters types\n"
> +	"  -s, --set-filter filter     Set filter for processing devices\n"
> +	"  -m, --match-device filter   Find device matching to filter\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;
> +
> +	switch (card->dev_type) {
> +	case DEV_PCI:
> +		printf("dev type    : %s\n", "PCI");
> +		printf("PCI slot    : %s\n", card->pci_slot);
> +		break;
> +	case DEV_PLATFORM_NON_DISCOVERABLE:
> +		printf("dev type    : %s\n", "PLATFORM (NON-DISCOVERABLE)");
> +		break;
> +	case DEV_PLATFORM_DISCOVERABLE:
> +		printf("dev type    : %s\n", "PLATFORM (DISCOVERABLE)");
> +		break;
> +	}
> +	printf("drv chipset : %x\n", card->chipset);
> +	printf("drm card    : %s\n", card->card);
> +	printf("drm render  : %s\n", card->render);
> +	printf("drv module  : %s\n", card->module);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	static struct option long_options[] = {
> +		{"show-props",        no_argument,       NULL, OPT_SHOW_PROPS},
> +		{"show-attrs",        no_argument,       NULL, OPT_SHOW_ATTRS},
> +		{"print-simple",      no_argument,       NULL, OPT_PRINT_SIMPLE},
> +		{"print-detail",      no_argument,       NULL, OPT_PRINT_DETAIL},
> +		{"list-vendors",      no_argument,       NULL, OPT_LIST_VENDORS},
> +		{"bind-modules",      no_argument,       NULL, OPT_BIND_MODULES},
> +		{"unbind-module",     required_argument, NULL, OPT_UNBIND_MODULE},
> +		{"bind-module",       required_argument, NULL, OPT_BIND_MODULE},
> +		{"list-filter-types", no_argument,       NULL, OPT_LIST_FILTERS},
> +		{"set-filter",        required_argument, NULL, OPT_SET_FILTER},
> +		{"match-device",      required_argument, NULL, OPT_MATCH_DEVICE},
> +		{"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;
> +
> +	while ((c = getopt_long(argc, argv, "PpraidvBu:b:ls:m:D:h",
> +				long_options, &index)) != -1) {
> +		switch(c) {
> +		case OPT_SHOW_PROPS:
> +			g_show_props = true;
> +			break;
> +		case OPT_SHOW_ATTRS:
> +			g_show_attrs = true;
> +			break;
> +		case OPT_PRINT_SIMPLE:
> +			printtype = IGT_PRINT_SIMPLE;
> +			break;
> +		case OPT_PRINT_DETAIL:
> +			printtype = IGT_PRINT_DETAIL;
> +			break;
> +		case OPT_LIST_VENDORS:
> +			g_show_vendors = true;
> +			break;
> +		case OPT_BIND_MODULES:
> +			g_bind_modules = true;
> +			break;
> +		case OPT_UNBIND_MODULE:
> +			g_pci_id = strdup(optarg);
> +			g_unbind_module = true;
> +			break;
> +		case OPT_BIND_MODULE:
> +			g_pci_id = strdup(optarg);
> +			g_bind_module = true;
> +			break;
> +		case OPT_SET_FILTER:
> +			g_filter = strdup(optarg);
> +			break;
> +		case OPT_LIST_FILTERS:
> +			g_list_filters = true;
> +			break;
> +		case OPT_MATCH_DEVICE:
> +			g_match_filter = strdup(optarg);
> +			break;
> +		case OPT_DEVICE:
> +			g_device = true;
> +			igt_device_filter_add(optarg);
> +			break;
> +		case OPT_HELP:
> +			g_help = true;
> +			break;
> +		}
> +	}
> +
> +	if (g_help) {
> +		printf("%s\n", usage_str);
> +		exit(0);
> +	}
> +
> +	env = getenv("IGT_DEVICE");
> +	if (env) {
> +		igt_device_filter_add(env);
> +		g_device = true;
> +	}
> +
> +	igt_devices_scan(false);
> +	if (g_show_vendors) {
> +		igt_devices_print_vendors();
> +		return 0;
> +	}
> +	if (g_bind_modules) {
> +		igt_devices_bind_modules();
> +		return 0;
> +	}
> +
> +	if (g_unbind_module) {
> +		igt_device_pci_unbind_module(g_pci_id);
> +		return 0;
> +	}
> +
> +	if (g_bind_module) {
> +		igt_device_pci_bind_module(g_pci_id);
> +		return 0;
> +	}
> +
> +	if (g_list_filters) {
> +		igt_device_print_filter_types();
> +		return 0;
> +	}
> +
> +	if (g_filter) {
> +		igt_device_filter_apply(g_filter);
> +	}
> +
> +	if (g_match_filter) {
> +		struct igt_device_card card;
> +		if (igt_device_card_match(g_match_filter, &card)) {
> +			print_card(&card);
> +		} else {
> +			printf("No device matching filter [%s] found.\n",
> +			       g_match_filter);
> +			return 0;
> +		}
> +		test_device_open(&card);
> +
> +		return 0;
> +	}
> +
> +	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));
> +			igt_device_filter_apply_nth(i);
> +			igt_devices_print(printtype, g_show_props, g_show_attrs);
> +		}
> +		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))
> +				continue;
> +			print_card(&card);
> +			test_device_open(&card);
> +			printf("---\n");
> +		}
> +
> +		return 0;
> +	}
> +
> +	igt_devices_print(printtype, g_show_props, g_show_attrs);
> +
> +	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.21.0
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [igt-dev] ✓ Fi.CI.BAT: success for Add device selection methods
  2019-07-11 10:30 [igt-dev] [PATCH i-g-t v1 0/1] Add device selection methods Zbigniew Kempczyński
  2019-07-11 10:30 ` [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection Zbigniew Kempczyński
@ 2019-07-11 15:13 ` Patchwork
  2019-07-12 10:08 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
  2 siblings, 0 replies; 10+ messages in thread
From: Patchwork @ 2019-07-11 15:13 UTC (permalink / raw)
  To: Zbigniew Kempczyński; +Cc: igt-dev

== Series Details ==

Series: Add device selection methods
URL   : https://patchwork.freedesktop.org/series/63553/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_6458 -> IGTPW_3256
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/63553/revisions/1/mbox/

Known issues
------------

  Here are the changes found in IGTPW_3256 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_ctx_create@basic-files:
    - fi-icl-guc:         [PASS][1] -> [INCOMPLETE][2] ([fdo#107713] / [fdo#109100])
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/fi-icl-guc/igt@gem_ctx_create@basic-files.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/fi-icl-guc/igt@gem_ctx_create@basic-files.html

  * igt@gem_mmap_gtt@basic-small-bo-tiledy:
    - fi-icl-u3:          [PASS][3] -> [DMESG-WARN][4] ([fdo#107724])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/fi-icl-u3/igt@gem_mmap_gtt@basic-small-bo-tiledy.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/fi-icl-u3/igt@gem_mmap_gtt@basic-small-bo-tiledy.html

  * igt@kms_frontbuffer_tracking@basic:
    - fi-hsw-peppy:       [PASS][5] -> [DMESG-WARN][6] ([fdo#102614])
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/fi-hsw-peppy/igt@kms_frontbuffer_tracking@basic.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/fi-hsw-peppy/igt@kms_frontbuffer_tracking@basic.html

  
#### Possible fixes ####

  * igt@i915_pm_rpm@module-reload:
    - fi-skl-6770hq:      [FAIL][7] ([fdo#108511]) -> [PASS][8]
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/fi-skl-6770hq/igt@i915_pm_rpm@module-reload.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/fi-skl-6770hq/igt@i915_pm_rpm@module-reload.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#102614]: https://bugs.freedesktop.org/show_bug.cgi?id=102614
  [fdo#107713]: https://bugs.freedesktop.org/show_bug.cgi?id=107713
  [fdo#107724]: https://bugs.freedesktop.org/show_bug.cgi?id=107724
  [fdo#108511]: https://bugs.freedesktop.org/show_bug.cgi?id=108511
  [fdo#109100]: https://bugs.freedesktop.org/show_bug.cgi?id=109100
  [fdo#109485]: https://bugs.freedesktop.org/show_bug.cgi?id=109485
  [fdo#109635 ]: https://bugs.freedesktop.org/show_bug.cgi?id=109635 
  [fdo#110387]: https://bugs.freedesktop.org/show_bug.cgi?id=110387
  [fdo#111045]: https://bugs.freedesktop.org/show_bug.cgi?id=111045
  [fdo#111046 ]: https://bugs.freedesktop.org/show_bug.cgi?id=111046 


Participating hosts (53 -> 47)
------------------------------

  Missing    (6): fi-kbl-soraka fi-byt-squawks fi-bsw-cyan fi-icl-y fi-byt-clapper fi-bdw-samus 


Build changes
-------------

  * IGT: IGT_5093 -> IGTPW_3256

  CI_DRM_6458: fe4d1459b31768c40f907bb859f25197e1af2d07 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_3256: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/
  IGT_5093: 86dc48ede7c33bf69e15f84179d2f9e5b84c179b @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection
  2019-07-11 10:30 ` [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection Zbigniew Kempczyński
  2019-07-11 12:43   ` Daniel Vetter
@ 2019-07-12  8:20   ` Vasilev, Oleg
  2019-07-12  9:18     ` Zbigniew Kempczyński
  1 sibling, 1 reply; 10+ messages in thread
From: Vasilev, Oleg @ 2019-07-12  8:20 UTC (permalink / raw)
  To: Kempczynski, Zbigniew, igt-dev; +Cc: Latvala, Petri, daniel


[-- Attachment #1.1: Type: text/plain, Size: 70540 bytes --]

On Thu, 2019-07-11 at 12:30 +0200, Zbigniew Kempczyński wrote:
> Change adds device selection based on implemented filters.
> Different filters can be added to address different device
> selection possibilities.
> 
> New device selection uses --device filter or IGT_DEVICE environent
> variable. Selection of many devices can be done by using --device
> argument multiple times. As IGT_DEVICE enviroment can be set
> once ';' is recognized as filter separator.
> 
> Tool 'lsgpu' which uses device scanning feature was added.

Hi,

Would you mind splitting the patch into several commits? It is quite
hard to review this as a whole. For example:
1. Device selection
2. lsgpu
3. Documentation
4. ...

There are some comments below.

> 
> 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>
> ---
>  docs/multi-device-selection.txt |  115 +++
>  lib/Makefile.sources            |    2 +
>  lib/drmtest.c                   |  151 +++-
>  lib/drmtest.h                   |    9 +
>  lib/igt_core.c                  |   13 +
>  lib/igt_device_scan.c           | 1425
> +++++++++++++++++++++++++++++++
>  lib/igt_device_scan.h           |   72 ++
>  lib/meson.build                 |    1 +
>  tools/Makefile.sources          |    1 +
>  tools/lsgpu.c                   |  285 +++++++
>  tools/meson.build               |    1 +
>  11 files changed, 2073 insertions(+), 2 deletions(-)
>  create mode 100644 docs/multi-device-selection.txt
>  create mode 100644 lib/igt_device_scan.c
>  create mode 100644 lib/igt_device_scan.h
>  create mode 100644 tools/lsgpu.c
> 
> diff --git a/docs/multi-device-selection.txt b/docs/multi-device-
> selection.txt
> new file mode 100644
> index 00000000..5b13cfcc
> --- /dev/null
> +++ b/docs/multi-device-selection.txt
> @@ -0,0 +1,115 @@
> +Multi-device scanning and selection
> +-----------------------------------
> +
> +1. Scanning
> +
> +Device selection is build around scanning buses using udev (PCI and
> platform),
> +gathering information about matching devices and using filters on
> discovered
> +data.
> +
> +PCI scanning is easy and gives all information regarding devices
> (module doesn't
> +need to be loaded). Problems occurs when platform devices are taken
> into
> +account. Some of them have appropriate entries in device tree (like
> +Raspberry Pi vc4 driver), so displaying them without loaded module
> is possible.
> +Such devices expose OF_COMPATIBLE_X properties which are matched
> against
> +appropriate driver. Some devices are registered inside platform tree
> at driver
> +loading time so discovering such devices is not possible unless
> driver is loaded.
> +
> +
> +2. Filtering
> +
> +After scanning all gathered information are stored in devices array.
> Filtering
> +means creating a view that matches filter on devices array. Filters
> defined
> +on non-discoverable devices loads module (if not already loaded) and
> execute
> +device discovery again.
> +
> +Using 'lsgpu' tool we can get list of defined filter types ('-l'
> switch):
> +...
> +pci       pci:[vendor=%04x/name][,device=%04x][,card=%d]
> +	  vendor is hex number or name
> +...
> +
> +pci is filter name, it calls filter on devices array to select only
> PCI
> +devices. For example:
> +
> +pci:vendor=8086
> +find first pci card on the bus with 8086 PCI device ID
> +
> +pci:vendor=intel
> +same as above but uses vendor name (see lsgpu -v)
> +
> +pci:vendor=8086,card=0
> +same as above, card=0 is default
> +
> +pci:vendor=intel,device=1234,card=1
> +finds second Intel card which device ID is 0x1234
> +
> +
> +3. lsgpu
> +
> +To play with devices 'lsgpu' tool was written. It is mostly wrapper
> to igt
> +devices scan code (igt_device_scan.c).
> +
> +If run without arguments it displays all recognized cards.
> +Usage examples:
> +
> +# lsgpu -ra
> +displays devices in 'simple' mode with only few properties and
> attributes
> +
> +# lsgpu -d
> +displays devices in 'detail' mode, with all properties and
> attributes. Some
> +attributes are skipped because acquiring value can take even seconds
> (there's
> +special blacklist hash table inside the code)
> +
> +# lsgpu -v
> +displays recognized vendors
> +
> +# lsgpu -l
> +displays defined filter types
> +
> +# lsgpu -m 'vgem:card=0'
> +matches first vgem card at index 0. I
> +
> +
> +# lsgpu --device 'pci:card=0;vgem:card=0;vkms:card=0'
> +and
> +# lsgpu --device 'pci:card=0' --device 'vgem:card=0' --device
> 'vkms:card=0'
> +and
> +# export IGT_DEVICE='pci:card=0;vgem:card=0;vkms:card=0'
> +# lsgpu
> +matches multiple cards. IGT_DEVICE uses ';' separated filter syntax,
> it is also
> +permitted in --device (function splits argument against ';').
> +
> +
> +4. Compatibility with current device open API
> +
> +Practically all IGT tests use:
> +int __drm_open_driver(int chipset);
> +int __drm_open_driver_render(int chipset);
> +
> +Above functions were extended to use --device / env IGT_DEVICE
> filters
> +(if given during IGT test run). If more than single device were
> passed
> +first matching filter against chipset is selected. This allows test
> like
> +prime_vgem to work, ex:
> +
> +# ./prime_vgem --r basic-read --device 'pci:card=0' --device
> 'vgem:card=0'
> +# ./prime_vgem --r basic-read --device 'vgem:card=0' --device
> 'pci:card=0'
> +
> +
> +5. New device selection API
> +
> +Following prototypes define new API:
> +
> +/* Multi device API */
> +int __drm_open_card_with_nth_filter(int num, int chipset);
> +int __drm_open_render_with_nth_filter(int num, int chipset);
> +
> +When test require DRIVER_INTEL it has to be sure that user didn't
> passed
> +another device (vc4 for example). So if user passed --device
> vc4:card=0
> +this open will fail.
> +
> +struct igt_device_card;
> +int __drm_open_card(struct igt_device_card *card);
> +int __drm_open_render(struct igt_device_card *card);
> +
> +These functions allows to open device regardless chip requirement.
> diff --git a/lib/Makefile.sources b/lib/Makefile.sources
> index e16de86e..c383a817 100644
> --- a/lib/Makefile.sources
> +++ b/lib/Makefile.sources
> @@ -25,6 +25,8 @@ lib_source_list =	 	\
>  	igt_debugfs.h		\
>  	igt_device.c		\
>  	igt_device.h		\
> +	igt_device_scan.c	\
> +	igt_device_scan.h	\
>  	igt_aux.c		\
>  	igt_aux.h		\
>  	igt_color_encoding.c	\
> diff --git a/lib/drmtest.c b/lib/drmtest.c
> index 25f20353..5a9cadc1 100644
> --- a/lib/drmtest.c
> +++ b/lib/drmtest.c
> @@ -55,6 +55,7 @@
>  #include "igt_gt.h"
>  #include "igt_kmod.h"
>  #include "igt_sysfs.h"
> +#include "igt_device_scan.h"
>  #include "version.h"
>  #include "config.h"
>  #include "intel_reg.h"
> @@ -296,25 +297,171 @@ static int __open_driver(const char *base, int
> offset, unsigned int chipset)
>  	return __search_and_open(base, offset, chipset);
>  }
>  
> +static int __open_driver_exact(const char *name, unsigned int
> chipset)
> +{
> +	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
> +	int fd;
> +
> +	fd = open_device(name, chipset);
> +	if (fd != -1)
> +		return fd;
> +
> +	pthread_mutex_lock(&mutex);
> +	for (const struct module *m = modules; m->module; m++) {
> +		if (chipset & m->bit) {
> +			if (m->modprobe)
> +				m->modprobe(m->module);
> +			else
> +				modprobe(m->module);
> +		}
> +	}
> +	pthread_mutex_unlock(&mutex);
> +
> +	return open_device(name, chipset);
> +}
> +
> +/**
> + * __find_card_with_chipset
> + * @chipset: chipset for compare with card match
> + * @card: pointer where card information will be stored
> + *
> + * For compatibility mode when multiple --device argument were
> passed
> + * this function tries to be smart enough to handle tests which
> opens
> + * more than single device. It iterates over filter list and
> + * compares chipset to card chipset for filter matched.
> + *
> + * Returns:
> + * True if card according to filters added and chipset was found,
> + * false othwerwise.
> + */
> +static bool __find_card_with_chipset(int chipset, struct
> igt_device_card *card)
> +{
> +	int i, n = igt_device_filter_count();
> +	const char *filter;
> +	bool match;
> +
> +	for (i = 0; i < n; i++) {
> +		filter = igt_device_filter_get(i);
> +		match = igt_device_card_match(filter, card);
> +		if (match && (card->chipset == chipset || chipset ==
> DRIVER_ANY))
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
>  /**
>   * __drm_open_driver:
>   * @chipset: OR'd flags for each chipset to search, eg.
> #DRIVER_INTEL
>   *
> - * Open the first DRM device we can find, searching up to 16 device
> nodes
> + * Function opens device with following order:
> + * 1. when --device arguments are present device scanning will be
> executed,
> + * then matching considers all device which matches filter argument
> will be selected.
> + * 2. compatibility mode - open the first DRM device we can find,
> + * searching up to 16 device nodes.
>   *
>   * Returns:
>   * An open DRM fd or -1 on error
>   */
>  int __drm_open_driver(int chipset)
>  {
> +	int n = igt_device_filter_count();
> +
> +	if (n) {
> +		bool found;
> +		struct igt_device_card card;
> +
> +		found = __find_card_with_chipset(chipset, &card);
> +		if (!found || !strlen(card.card))
> +			return -1;
> +
> +		return __open_driver_exact(card.card, chipset);
> +	}
> +
>  	return __open_driver("/dev/dri/card", 0, chipset);
>  }
>  
> -static int __drm_open_driver_render(int chipset)
> +int __drm_open_driver_render(int chipset)
>  {
> +	int n = igt_device_filter_count();
> +
> +	if (n) {
> +		bool found;
> +		struct igt_device_card card;
> +
> +		found = __find_card_with_chipset(chipset, &card);
> +		if (!found || !strlen(card.render))
> +			return -1;
> +
> +		return __open_driver_exact(card.render, chipset);
> +	}
> +
>  	return __open_driver("/dev/dri/renderD", 128, chipset);
>  }
>  
> +static int __drm_open_with_nth_filter(int num, int chipset, bool
> open_render)
> +{
> +	struct igt_device_card card;
> +	const char *filter, *devname;
> +	bool match;
> +	int n = igt_device_filter_count();
> +
> +	if (!n || num < 0 || num >= n) {
> +		igt_warn("No device filter num == %d passed\n", num);
> +		return -1;
> +	}
> +
> +	filter = igt_device_filter_get(num);
> +	match = igt_device_card_match(filter, &card);
> +	if (!match) {
> +		igt_warn("No device match filter: %s\n", filter);
> +		return -1;
> +	}
> +
> +	if (chipset != card.chipset) {
> +		igt_warn("Filtered device doesn't match chipset (%d !=
> %d)\n",
> +			 chipset, card.chipset);
> +		return -1;
> +	}
> +	if (!strlen(card.card))
> +		return -1;
> +
> +	devname = open_render ? card.render : card.card;
> +	if (!strlen(devname)) {
> +		igt_warn("No %s node matching filter: %s\n",
> +			 open_render ? "render" : "card", filter);
> +		return -1;
> +	}
> +	return __open_driver_exact(devname, card.chipset);
> +}
> +
> +int __drm_open_card_with_nth_filter(int num, int chipset)
> +{
> +	return __drm_open_with_nth_filter(num, chipset, false);
> +}
> +
> +int __drm_open_render_with_nth_filter(int num, int chipset)
> +{
> +	return __drm_open_with_nth_filter(num, chipset, true);
> +}
> +
> +int __drm_open_card(struct igt_device_card *card)
> +{
> +	if (!card || !strlen(card->card))
> +		return -1;
> +
> +	return __open_driver_exact(card->card, card->chipset);
> +}
> +
> +int __drm_open_render(struct igt_device_card *card)
> +{
> +	if (!card || !strlen(card->render))
> +		return -1;
> +
> +	return __open_driver_exact(card->render, card->chipset);
> +}
> +
> +
>  static int at_exit_drm_fd = -1;
>  static int at_exit_drm_render_fd = -1;
>  
> diff --git a/lib/drmtest.h b/lib/drmtest.h
> index 6c4c3899..8f5888a9 100644
> --- a/lib/drmtest.h
> +++ b/lib/drmtest.h
> @@ -45,6 +45,7 @@
>  #define DRIVER_AMDGPU	(1 << 4)
>  #define DRIVER_V3D	(1 << 5)
>  #define DRIVER_PANFROST	(1 << 6)
> +
>  /*
>   * Exclude DRVER_VGEM from DRIVER_ANY since if you run on a system
>   * with vgem as well as a supported driver, you can end up with a
> @@ -76,6 +77,14 @@ int drm_open_driver(int chipset);
>  int drm_open_driver_master(int chipset);
>  int drm_open_driver_render(int chipset);
>  int __drm_open_driver(int chipset);
> +int __drm_open_driver_render(int chipset);
> +
> +/* Multi device API */
> +int __drm_open_card_with_nth_filter(int num, int chipset);
> +int __drm_open_render_with_nth_filter(int num, int chipset);
> +struct igt_device_card;
> +int __drm_open_card(struct igt_device_card *card);
> +int __drm_open_render(struct igt_device_card *card);
>  
>  void gem_quiescent_gpu(int fd);
>  
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 1cbb09f9..9b851175 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -71,6 +71,7 @@
>  #include "igt_sysrq.h"
>  #include "igt_rc.h"
>  #include "igt_list.h"
> +#include "igt_device_scan.h"
>  
>  #define UNW_LOCAL_ONLY
>  #include <libunwind.h>
> @@ -304,6 +305,7 @@ enum {
>  	OPT_DEBUG,
>  	OPT_INTERACTIVE_DEBUG,
>  	OPT_SKIP_CRC,
> +	OPT_DEVICE,
>  	OPT_HELP = 'h'
>  };
>  
> @@ -624,6 +626,7 @@ static void print_usage(const char *help_str,
> bool output_on_stderr)
>  		   "  --skip-crc-compare\n"
>  		   "  --help-description\n"
>  		   "  --describe\n"
> +		   "  --device filter\n"
>  		   "  --help|-h\n");
>  	if (help_str)
>  		fprintf(f, "%s\n", help_str);
> @@ -725,6 +728,11 @@ static void common_init_env(void)
>  	if (env) {
>  		__set_forced_driver(env);
>  	}
> +
> +	env = getenv("IGT_DEVICE");
> +	if (env) {
> +		igt_device_filter_add(env);
> +	}
>  }
>  
>  static int common_init(int *argc, char **argv,
> @@ -743,6 +751,7 @@ static int common_init(int *argc, char **argv,
>  		{"debug",             optional_argument, NULL,
> OPT_DEBUG},
>  		{"interactive-debug", optional_argument, NULL,
> OPT_INTERACTIVE_DEBUG},
>  		{"skip-crc-compare",  no_argument,       NULL,
> OPT_SKIP_CRC},
> +		{"device",            required_argument, NULL,
> OPT_DEVICE},
>  		{"help",              no_argument,       NULL,
> OPT_HELP},
>  		{0, 0, 0, 0}
>  	};
> @@ -865,6 +874,10 @@ static int common_init(int *argc, char **argv,
>  		case OPT_SKIP_CRC:
>  			igt_skip_crc_compare = true;
>  			goto out;
> +		case OPT_DEVICE:
> +			assert(optarg);
> +			igt_device_filter_add(optarg);
> +			break;
>  		case OPT_HELP:
>  			print_usage(help_str, false);
>  			ret = -1;
> diff --git a/lib/igt_device_scan.c b/lib/igt_device_scan.c
> new file mode 100644
> index 00000000..6cf0aea6
> --- /dev/null
> +++ b/lib/igt_device_scan.c
> @@ -0,0 +1,1425 @@
> +/*
> + * 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.h"
> +#include "igt_kmod.h"
> +#include "igt_sysfs.h"
> +#include "igt_device.h"
> +#include "igt_device_scan.h"
> +#include <glib.h>
> +#include <libudev.h>
> +#include <linux/limits.h>
> +#include <sys/stat.h>
> +
> +//#define DEBUG_DEVICE_SCAN
> +#ifdef DEBUG_DEVICE_SCAN
> +#define DBG(...) \
> +{ \
> +	struct timeval tm; \
> +	gettimeofday(&tm, NULL); \
> +	printf("%10ld.%03ld: ", tm.tv_sec, tm.tv_usec); \
> +	printf(__VA_ARGS__); \
> +	}
> +
> +#else
> +#define DBG(...) {}
> +#endif
> +
> +#define IGT_BUS_PCI_DRIVERS_DIR "/sys/bus/pci/drivers"
> +#define IGT_DRM_PATH "/dev/dri"
> +
> +/* Scanning PCI is simple and well defined. Platform devices
> + * are listed in device tree but until driver is really loaded
> + * we only 'expect' their existence.
> + *
> + * From this reason platform scanning was separated to two
> + * distinct scan types - non-discoverable and discoverable.
> + *
> + * 1. Non-discoverable scan type checks modalias attribute.
> + * Such devices will occur within platform subsystem only
> + * after driver is loaded. Example are 'platform:vgem'
> + * 'platform:vkms' and 'platform:exynos'.
> + *
> + * 2. Discoverable scan type checks OF_COMPATIBLE_0 property.
> + * This simple check should be enough to display platform
> + * gpu defined in device tree even if driver wasn't already
> + * loaded.
> + */
> +enum igt_devices_scan_type {
> +	IGT_SCAN_PCI                       = 1,
> +	IGT_SCAN_PLATFORM_NON_DISCOVERABLE = 2,
> +	IGT_SCAN_PLATFORM_DISCOVERABLE     = 3,
> +};
> +
> +static GHashTable *blacklist_keys_ht;      //sysattrs we don't want
> to read
> +static GHashTable *gpu_pci_class_ht;       //gpu pci classes we know
> +static GHashTable *gpu_vendor_ht;          //search id ->
> vendor_spec mapping
> +static GHashTable *filter_definition_ht;   //supported filters
> (pci=..., etc.)

Are you sure we need hashtables for all this stuff? Mappings with 5-20
records should work with linear search well enough...

> +
> +/* Generic name->value struct, used to fill hash tables */
> +struct name_value {
> +	const char *name;
> +	gpointer *value;
> +};
> +
> +/* Vendor specific data */
> +struct vendor_spec {
> +	enum igt_device_type dev_type;
> +	const char *vendor;
> +	const char *modname;
> +	const char *modopts;
> +	const char *match_driver; //reported driver name not always
> equal module name
> +	int chipset;
> +};
> +
> +/* Single igt_device found in PCI / platform subsystems */
> +struct igt_device {
> +	char *pretty_name;
> +	struct vendor_spec *vs;
> +	GHashTable *props_ht;
> +	GHashTable *attrs_ht;
> +	char *syspath;
> +	char *drvpath;
> +	char *drm_card_path;
> +	char *drm_render_path;
> +	char *vendor;
> +	char *device;
> +	enum igt_device_type dev_type;
> +};
> +
> +/* Scanned devices struct */
> +struct igt_devices {
> +	GPtrArray *devs;		//all gpu devices array
> +	GPtrArray *view;		//filtered view
> +	bool devs_scanned;
> +};
> +
> +/* Scanned devices holder */
> +static struct igt_devices igt_devs;
> +
> +/* PCI device classes we take into account during scan.
> + * Attribute PCI_CLASS is compared with key beneath. */
> +struct name_value gpu_pci_class_list[] = {
> +	{ "30000", (gpointer) "VGA compatibile controller" },
> +	{ "30100", (gpointer) "XGA compatibile controller" },
> +	{ "30200", (gpointer) "3D controller" },
> +	{ "38000", (gpointer) "Display controller" },
> +	{ NULL, },
> +};
> +
> +static struct vendor_spec v_intel  = { .dev_type = DEV_PCI,
> +				       .vendor = "Intel",
> +				       .modname = "i915",
> +				       .chipset = DRIVER_INTEL
> +				     };
> +static struct vendor_spec v_amd    = { .dev_type = DEV_PCI,
> +				       .vendor = "AMD",
> +				       .modname = "amdgpu",
> +				       .chipset = DRIVER_AMDGPU
> +				     };
> +static struct vendor_spec v_nvidia  = { .dev_type = DEV_PCI,
> +					.vendor = "NVIDIA",
> +					.modname = "nvidia",
> +					.chipset = DRIVER_ANY
> +				      };
> +static struct vendor_spec v_vgem    = { .dev_type =
> DEV_PLATFORM_NON_DISCOVERABLE,
> +					.vendor = "Virtual-GEM",
> +					.modname = "vgem",
> +					.chipset = DRIVER_VGEM
> +				      };
> +static struct vendor_spec v_vkms   = { .dev_type =
> DEV_PLATFORM_NON_DISCOVERABLE,
> +				       .vendor = "Virtual-KMS",
> +				       .modname = "vkms",
> +				       .chipset = DRIVER_ANY
> +				     };
> +static struct vendor_spec v_exynos  = { .dev_type =
> DEV_PLATFORM_NON_DISCOVERABLE,
> +					.vendor = "Samsung",
> +					.modname = "exynosdrm",
> +					.match_driver = "exynos-drm",
> +					.chipset = DRIVER_ANY
> +				      };
> +static struct vendor_spec v_vc4    = { .dev_type =
> DEV_PLATFORM_DISCOVERABLE,
> +				       .vendor = "Broadcom",
> +				       .modname = "vc4",
> +				       .match_driver = "vc4-drm",
> +				       .chipset = DRIVER_VC4
> +				     };
> +
> +/* Mapping vendor id => vendor_spec should be unique (vendor
> matching
> + * is written around this).
> + *
> + * Keys must be defined as follows:
> + * PCI devices: PCI_SLOT_ID,
> + * Non-discoverable platform devices: MODALIAS,
> + * Discoverable platform devices: OF_COMPATIBLE_0.
> +*/
> +struct name_value gpu_vendor_list[] = {
> +	{ "8086",                (gpointer) &v_intel },
> +	{ "1002",                (gpointer) &v_amd },
> +	{ "10DE",                (gpointer) &v_nvidia },
> +	{ "platform:vgem",       (gpointer) &v_vgem },
> +	{ "platform:vkms",       (gpointer) &v_vkms },
> +	{ "platform:exynos-drm", (gpointer) &v_exynos },
> +	{ "brcm,bcm2835-vc4",    (gpointer) &v_vc4 },
> +	{ NULL, },
> +};
> +
> +/* Generic hash table fill function, requires name / value ptrs
> array */
> +static void fill_ht(GHashTable **ht, struct name_value *data)
> +{
> +	if (*ht)
> +		return;
> +
> +	*ht = g_hash_table_new(g_str_hash, g_str_equal);
> +	igt_assert(*ht);
> +
> +	while (data->name) {
> +		g_hash_table_insert(*ht,
> +				    (gpointer) data->name,
> +				    data->value);
> +		data++;
> +	}
> +}
> +
> +#define get_vendor_spec(prop) \
> +	g_hash_table_lookup(gpu_vendor_ht, prop)
> +
> +/* Go through whole vendor hash table and compare against vendor
> field.
> + * Used mostly with vendor=... filter parameter when PCI id is not
> matched.
> +*/
> +static bool is_pci_vendor_name(const char *name)
> +{
> +	bool ret = false;
> +	GList *keys = g_hash_table_get_keys(gpu_vendor_ht);
> +
> +	if (!name)
> +		return false;
> +
> +	while (keys) {
> +		char *k = (char *) keys->data;
> +		struct vendor_spec *vs =
> g_hash_table_lookup(gpu_vendor_ht, k);
> +		keys = g_list_next(keys);
> +
> +		if (vs->dev_type != DEV_PCI)
> +			continue;
> +
> +		if (!strcasecmp(name, vs->vendor)) {
> +			ret = true;
> +			break;
> +		}
> +	}
> +	g_list_free(keys);
> +
> +	return ret;
> +}
> +
> +/* Reading sysattr values can take time (even seconds),
> + * we want to avoid reading such keys.
> +*/
> +static void populate_blacklist_keys(void)
> +{
> +	const char *keys[] = { "config", "modalias",
> +			       "resource",
> +			       "resource0", "resource1", "resource2",
> +			       "resource3", "resource4", "resource5",
> +			       "resource0_wc", "resource1_wc",
> "resource2_wc",
> +			       "resource3_wc", "resource4_wc",
> "resource5_wc",
> +			       "driver",
> +			       "uevent", NULL};
> +	const char *key;
> +	int i = 0;
> +
> +	if (blacklist_keys_ht)
> +		return;
> +
> +	blacklist_keys_ht = g_hash_table_new(g_str_hash, g_str_equal);
> +	igt_assert(blacklist_keys_ht);
> +
> +	while ((key = keys[i++]))
> +		g_hash_table_add(blacklist_keys_ht, (gpointer) key);
> +}
> +
> +#define is_on_blacklist(key) \
> +	g_hash_table_contains(blacklist_keys_ht, key)
> +
> +static struct igt_device *igt_device_new(void)
> +{
> +	struct igt_device *dev;
> +	dev = calloc(1, sizeof(struct igt_device));
> +	if (!dev)
> +		return NULL;
> +
> +	dev->attrs_ht = g_hash_table_new_full(g_str_hash, g_str_equal,
> +					      free, free);
> +	dev->props_ht = g_hash_table_new_full(g_str_hash, g_str_equal,
> +					      free, free);
> +
> +	if (dev->attrs_ht && dev->props_ht)
> +		return dev;
> +
> +	return NULL;
> +}
> +
> +static void igt_device_add_prop(struct igt_device *dev,
> +				const char *key, const char *value)
> +{
> +	if (!key || !value)
> +		return;
> +
> +	g_hash_table_insert(dev->props_ht, strdup(key), strdup(value));
> +}
> +
> +static void igt_device_add_attr(struct igt_device *dev,
> +				const char *key, const char *value)
> +{
> +	const char *v = value;
> +
> +	if (!key)
> +		return;
> +
> +	/* It's possible we have symlink at key filename, but udev
> +	 * library resolves only few of them */
> +	if (!v) {
> +		struct stat st;
> +		char path[PATH_MAX];
> +		char linkto[PATH_MAX];
> +		int len;
> +
> +		snprintf(path, sizeof(path), "%s/%s", dev->syspath,
> key);
> +		if (lstat(path, &st) != 0)
> +			return;
> +
> +		len = readlink(path, linkto, sizeof(linkto));
> +		if (len <= 0 || len == (ssize_t) sizeof(linkto))
> +			return;
> +		linkto[len] = '\0';
> +		v = strrchr(linkto, '/');
> +		if (v == NULL)
> +			return;
> +		v++;
> +	}
> +
> +	g_hash_table_insert(dev->attrs_ht, strdup(key), strdup(v));
> +}
> +
> +static void scan_props(struct udev_device *dev, struct igt_device
> *idev)
> +{
> +	struct udev_list_entry *entry;
> +
> +	entry = udev_device_get_properties_list_entry(dev);
> +	while (entry) {
> +		const char *name = udev_list_entry_get_name(entry);
> +		const char *value = udev_list_entry_get_value(entry);
> +		igt_device_add_prop(idev, name, value);
> +		entry = udev_list_entry_get_next(entry);
> +		DBG("prop: %s, val: %s\n", name, value);
> +	}
> +}
> +
> +static void scan_attrs(struct udev_device *dev, struct igt_device
> *idev)
> +{
> +	struct udev_list_entry *entry;
> +
> +	entry = udev_device_get_sysattr_list_entry(dev);
> +	while (entry) {
> +		const char *key = udev_list_entry_get_name(entry);
> +		const char *value;
> +
> +		if (is_on_blacklist(key)) {
> +			entry = udev_list_entry_get_next(entry);
> +			continue;
> +		}
> +
> +		value = udev_device_get_sysattr_value(dev, key);
> +		igt_device_add_attr(idev, key, value);
> +		entry = udev_list_entry_get_next(entry);
> +		DBG("attr: %s, val: %s\n", key, value);
> +	}
> +}
> +
> +#define get_prop(dev, prop) (char *) g_hash_table_lookup(dev-
> >props_ht, prop)
> +#define get_attr(dev, attr) (char *) g_hash_table_lookup(dev-
> >attrs_ht, attr)
> +#define get_prop_pci_id(dev)    get_prop(dev, "PCI_ID")
> +#define get_prop_pci_slot(dev)  get_prop(dev, "PCI_SLOT_NAME")
> +#define get_prop_devpath(dev)   get_prop(dev, "DEVPATH")
> +#define get_prop_driver(dev)    get_prop(dev, "DRIVER")
> +#define get_prop_modalias(dev)  get_prop(dev, "MODALIAS")
> +#define get_prop_of_compatible_0(dev) get_prop(dev,
> "OF_COMPATIBLE_0")
> +
> +/* Gets PCI_ID property, splits to xxxx:yyyy and stores
> + * xxxx to dev->vendor and yyyy to dev->device for
> + * faster access.
> + */
> +static void set_vendor_device(struct igt_device *dev)
> +{
> +	const char *pci_id = get_prop_pci_id(dev);
> +	if (!pci_id || strlen(pci_id) != 9)
> +		return;
> +	dev->vendor = strndup(pci_id, 4);
> +	dev->device = strndup(pci_id + 5, 4);
> +}
> +
> +static void find_drm_paths(struct igt_device *dev, bool
> platform_check)
> +{
> +	char dirname[PATH_MAX];
> +	DIR *dir;
> +	const char *driver = get_prop_driver(dev);
> +	const char *devpath = get_prop_devpath(dev);
> +	struct dirent *e;
> +
> +	/* There's no DRIVER, so no /dev/dri/cardX and /dev/dri/renderD
> +	 * files exists. If there's no DEVPATH there's something wrong
> +	 * as well. */
> +	if (!platform_check && (!driver || !devpath))
> +		return;
> +
> +	snprintf(dirname, PATH_MAX, "%s/drm", dev->syspath);
> +	dir = opendir(dirname);
> +	if (!dir)
> +		return;
> +
> +	while((e = readdir(dir))) {
> +		int n;
> +		if (sscanf(e->d_name, "card%d", &n) == 1)
> +			dev->drm_card_path = g_strdup_printf("%s/%s",
> +							     IGT_DRM_PA
> TH,
> +							     e-
> >d_name);
> +		else if (sscanf(e->d_name, "renderD%d", &n) == 1)
> +			dev->drm_render_path = g_strdup_printf("%s/%s",
> +							       IGT_DRM_
> PATH,
> +							       e-
> >d_name);
> +	}
> +	closedir(dir);
> +
> +	if (dev->drm_card_path)
> +		DBG("card: %s\n", dev->drm_card_path);
> +	if (dev->drm_render_path)
> +		DBG("rend: %s\n", dev->drm_render_path);
> +}
> +
> +static bool prepare_scan(void)
> +{
> +	if (!igt_devs.devs)
> +		igt_devs.devs = g_ptr_array_sized_new(4);
> +	if (!igt_devs.view)
> +		igt_devs.view = g_ptr_array_sized_new(4);
> +
> +	if (!igt_devs.devs || !igt_devs.view)
> +		return false;
> +
> +	return true;
> +}
> +
> +static bool is_valid_pci_gpu(const char *prop)
> +{
> +	return g_hash_table_contains(gpu_pci_class_ht, prop);
> +}
> +
> +static bool is_valid_platform_gpu(const char *prop)
> +{
> +	struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht,
> prop);
> +	if (!vs)
> +		return false;
> +
> +	return vs->dev_type == DEV_PLATFORM_NON_DISCOVERABLE ||
> +			vs->dev_type == DEV_PLATFORM_DISCOVERABLE;
> +}
> +
> +static void add_pci_gpu(struct igt_device *dev)
> +{
> +	find_drm_paths(dev, false);
> +
> +	g_ptr_array_add(igt_devs.devs, dev);
> +	g_ptr_array_add(igt_devs.view, dev);
> +}
> +
> +static void add_platform_non_discoverable_gpu(struct igt_device
> *dev)
> +{
> +	find_drm_paths(dev, true);
> +	if (!get_prop_driver(dev))
> +		igt_device_add_prop(dev, "DRIVER", dev->vs->modname);
> +
> +	g_ptr_array_add(igt_devs.devs, dev);
> +	g_ptr_array_add(igt_devs.view, dev);
> +}
> +
> +static void add_platform_discoverable_gpu(struct igt_device *dev)
> +{
> +	find_drm_paths(dev, true);
> +	g_ptr_array_add(igt_devs.devs, dev);
> +	g_ptr_array_add(igt_devs.view, dev);
> +}
> +
> +/* Classifier table.
> + * Scanning udev devices looks same for pci and platform devices,
> but differs
> + * in logic regarding adding devices to the device array.
> +*/
> +struct {
> +	bool (*is_valid)(const char *prop); //check is device belongs
> to the class
> +	void (*add)(struct igt_device *dev); //add device to the array
> and aux stuff
> +	unsigned int dev_type;
> +	const char *subsys;
> +	const char *findprop;
> +} cls[] = {
> +	[IGT_SCAN_PCI] = { .subsys = "pci",
> +			   .findprop = "PCI_CLASS",
> +			   .dev_type = DEV_PCI,
> +			   .is_valid = is_valid_pci_gpu,
> +			   .add = add_pci_gpu,
> +			 },
> +	[IGT_SCAN_PLATFORM_NON_DISCOVERABLE] = { .subsys = "platform",
> +						 .findprop =
> "MODALIAS",
> +						 .dev_type =
> DEV_PLATFORM_NON_DISCOVERABLE,
> +						 .is_valid =
> is_valid_platform_gpu,
> +						 .add =
> add_platform_non_discoverable_gpu,
> +					       },
> +	[IGT_SCAN_PLATFORM_DISCOVERABLE] = { .subsys = "platform",
> +					     .findprop =
> "OF_COMPATIBLE_0",
> +					     .dev_type =
> DEV_PLATFORM_DISCOVERABLE,
> +					     .is_valid =
> is_valid_platform_gpu,
> +					     .add =
> add_platform_discoverable_gpu,
> +					   },
> +};
> +
> +static void scan_devices(enum igt_devices_scan_type scantype)
> +{
> +	struct udev *udev;
> +	struct udev_device *dev;
> +	struct udev_enumerate *enumerate;
> +	struct udev_list_entry *devices, *dev_list_entry;
> +	int ret;
> +
> +	udev = udev_new();
> +	igt_assert(udev);
> +
> +	enumerate = udev_enumerate_new(udev);
> +	igt_assert(enumerate);
> +
> +	DBG("Scanning subsystem %s\n", cls[scantype].subsys);
> +	ret = udev_enumerate_add_match_subsystem(enumerate,
> cls[scantype].subsys);
> +	igt_assert(!ret);
> +
> +	ret = udev_enumerate_scan_devices(enumerate);
> +	igt_assert(!ret);
> +
> +	devices = udev_enumerate_get_list_entry(enumerate);
> +	if (!devices)
> +		return;
> +
> +	udev_list_entry_foreach(dev_list_entry, devices) {
> +		struct igt_device *idev;
> +		const char *path;
> +		const char *prop;
> +
> +		path = udev_list_entry_get_name(dev_list_entry);
> +		dev = udev_device_new_from_syspath(udev, path);
> +		prop = udev_device_get_property_value(dev,
> cls[scantype].findprop);
> +		if (!prop || !cls[scantype].is_valid(prop)) {
> +			udev_device_unref(dev);
> +			continue;
> +		}
> +		idev = igt_device_new();
> +		igt_assert(idev);
> +
> +		idev->dev_type = cls[scantype].dev_type;
> +		idev->syspath = strdup(udev_device_get_syspath(dev));
> +		scan_props(dev, idev);
> +		scan_attrs(dev, idev);
> +
> +		switch (idev->dev_type) {
> +		case DEV_PCI:
> +			set_vendor_device(idev);
> +			idev->pretty_name =
> strdup(get_prop_pci_slot(idev));
> +			idev->vs = get_vendor_spec(idev->vendor);
> +			break;
> +		default:
> +			idev->pretty_name = strdup(prop);
> +			idev->vs = get_vendor_spec(prop);
> +		}
> +		igt_assert(idev->vs);
> +
> +		cls[scantype].add(idev);
> +		udev_device_unref(dev);
> +	}
> +	udev_enumerate_unref(enumerate);
> +	udev_unref(udev);
> +}
> +
> +struct name_value filter_definition_list[];
> +static void populate_gpu_data(void)
> +{
> +	fill_ht(&gpu_pci_class_ht, &gpu_pci_class_list[0]);
> +	fill_ht(&gpu_vendor_ht, &gpu_vendor_list[0]);
> +	fill_ht(&filter_definition_ht, &filter_definition_list[0]);
> +}
> +
> +static void igt_device_free(struct igt_device *dev)
> +{
> +	free(dev->pretty_name);
> +	free(dev->device);
> +	free(dev->drm_card_path);
> +	free(dev->drm_render_path);
> +	free(dev->drvpath);
> +	free(dev->syspath);
> +	free(dev->vendor);
> +	g_hash_table_destroy(dev->attrs_ht);
> +	g_hash_table_destroy(dev->props_ht);
> +}
> +
> +void igt_devices_scan(bool force)
> +{
> +	if (force && igt_devs.devs_scanned) {
> +		for (int i = 0; i < igt_devs.devs->len; i++) {
> +			struct igt_device *dev =
> g_ptr_array_index(igt_devs.devs, i);
> +			igt_device_free(dev);
> +			free(dev);
> +		}
> +		igt_devs.devs_scanned = false;
> +		g_ptr_array_free(igt_devs.view, true);
> +		g_ptr_array_free(igt_devs.devs, true);
> +		igt_devs.view = NULL;
> +		igt_devs.devs = NULL;
> +	}
> +
> +	if (igt_devs.devs_scanned)
> +		return;
> +
> +	populate_blacklist_keys();      //keys from sysattr we skip
> +	populate_gpu_data();
> +
> +	prepare_scan();
> +	scan_devices(IGT_SCAN_PCI);
> +	scan_devices(IGT_SCAN_PLATFORM_NON_DISCOVERABLE);
> +	scan_devices(IGT_SCAN_PLATFORM_DISCOVERABLE);
> +	igt_devs.devs_scanned = true;
> +}
> +
> +#define pr_simple(k, v) printf("    %-16s: %s\n", k, v)
> +#define pr_simple_prop(dev, key) pr_simple(key, get_prop(dev, key))
> +#define pr_simple_attr(dev, key) pr_simple(key, get_attr(dev, key))
> +
> +static void print_vendor(struct igt_device *dev)
> +{
> +	struct vendor_spec *vs = dev->vs;
> +	char *info = alloca(256);
> +
> +	snprintf(info, 256, "%s (%s, module: %s%s)", dev->vendor,
> +		 vs ? vs->vendor : "unknown",
> +		 vs ? vs->modname : "unknown",
> +		 dev->drm_card_path ? "" : " [not binded/loaded]");
> +	pr_simple("vendor", info);
> +}
> +
> +static void igt_devs_print_simple(GPtrArray *view,
> +				  bool show_props, bool show_attrs)
> +{
> +	struct igt_device *dev;
> +	int i;
> +
> +	if (!view)
> +		return;
> +
> +	for (i = 0; i < view->len; i++) {
> +		dev = g_ptr_array_index(view, i);
> +		printf("%s\n", dev->pretty_name);
> +		pr_simple("syspath", dev->syspath);
> +		pr_simple("drm card", dev->drm_card_path);
> +		pr_simple("drm render", dev->drm_render_path);
> +
> +		if (show_props) {
> +			if (get_prop_pci_id(dev))
> +				pr_simple_prop(dev, "PCI_ID");
> +			pr_simple_prop(dev, "DRIVER");
> +		}
> +
> +		/* We show attrs only for PCI devices */
> +		if (show_attrs && dev->dev_type == DEV_PCI) {
> +			print_vendor(dev);
> +			pr_simple("device", dev->device);
> +		}
> +	}
> +}
> +
> +#define pr_detail(k, v) printf("%-32s: %s\n", k, v)
> +
> +static void print_ht(GHashTable *ht)
> +{
> +	GList *keys = g_hash_table_get_keys(ht);
> +	keys = g_list_sort(keys, (GCompareFunc) strcmp);
> +	while (keys) {
> +		char *k = (char *) keys->data;
> +		char *v = g_hash_table_lookup(ht, k);
> +		pr_detail(k, v);
> +		keys = g_list_next(keys);
> +	}
> +	g_list_free(keys);
> +}
> +
> +static void igt_devs_print_detail(GPtrArray *view,
> +				  bool show_props, bool show_attrs)
> +{
> +	struct igt_device *dev;
> +	int i;
> +	(void) show_props;
> +	(void) show_attrs;
> +
> +	if (!view)
> +		return;
> +
> +	for (i = 0; i < view->len; i++) {
> +		dev = g_ptr_array_index(view, i);
> +		printf("========== %s ==========\n", dev->pretty_name);
> +		printf("[drm]\n");
> +		pr_detail("card device", dev->drm_card_path);
> +		pr_detail("render device", dev->drm_render_path);
> +		pr_detail("syspath", dev->syspath);
> +
> +		printf("\n[properties]\n");
> +		print_ht(dev->props_ht);
> +		printf("\n[attributes]\n");
> +		print_ht(dev->attrs_ht);
> +	}
> +}
> +
> +static struct print_func {
> +	void (*prn)(GPtrArray *, bool, bool);
> +} print_functions[] = {
> +	[IGT_PRINT_SIMPLE] = { .prn = igt_devs_print_simple },
> +	[IGT_PRINT_DETAIL] = { .prn = igt_devs_print_detail },
> +};
> +
> +void igt_devices_print(enum igt_devices_print_type printtype,
> +		       bool show_props,
> +		       bool show_attrs)
> +{
> +	print_functions[printtype].prn(igt_devs.view,
> +				       show_props, show_attrs);
> +}
> +
> +void igt_devices_print_vendors(void)
> +{
> +	struct name_value *list = &gpu_vendor_list[0];
> +	printf("Recognized vendors:\n");
> +
> +	printf("%-16s %-32s %-32s %-16s\n", "Device type", "id",

Theese %-32s and %-16s look very bulky when you run the command in non-
fullscreen terminal. 

> +	       "vendor", "module");
> +	while (list->name) {
> +		char *k = (char *) list->name;
> +		struct vendor_spec *vs =
> g_hash_table_lookup(gpu_vendor_ht, k);
> +		if (vs->dev_type == DEV_PCI)
> +			printf("%-16s %-32s %-32s %-16s\n", "pci",
> +			       k, vs->vendor, vs->modname);
> +		else if (vs->dev_type == DEV_PLATFORM_NON_DISCOVERABLE)
> +			printf("%-16s %-32s %-32s %-16s\n", "non-
> discoverable",
> +			       k, vs->vendor, vs->modname);
> +		else if (vs->dev_type == DEV_PLATFORM_DISCOVERABLE)
> +			printf("%-16s %-32s %-32s %-16s\n",
> "discoverable",
> +			       k, vs->vendor, vs->modname);
> +		list++;
> +	}
> +}
> +
> +static bool sys_write_attr(const char *dirname, const char *attr,
> +			   const char *str)
> +{
> +	int dir;
> +
> +	dir = open(dirname, O_RDONLY);
> +	if (dir < 0)
> +		return false;
> +
> +	if (igt_sysfs_printf(dir, attr, "%s", str) < 0) {
> +		printf("Error, can't write to %s, err: %s\n",
> +		       attr, strerror(errno));
> +		close(dir);
> +		return false;
> +	}
> +
> +	close(dir);
> +	return true;
> +}
> +
> +static bool ensure_driver_is_loaded(const char *modname, const char
> *modopts)
> +{
> +	if (!modname)
> +		return false;
> +
> +	if (!igt_kmod_is_loaded(modname)) {
> +		int ret;
> +		igt_debug("Loading module %s\n", modname);
> +		ret = igt_kmod_load(modname, modopts);
> +		if (ret) {
> +			igt_warn("Can't load module %s\n", modname);
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +#define ensure_device_has_driver_loaded(dev) \
> +	ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts)
> +
> +static struct igt_device *igt_devices_find_pci_id(const char
> *pci_slot)
> +{
> +	struct igt_device *dev;
> +	int i;
> +	GPtrArray *arr = igt_devs.devs;
> +
> +	if (!pci_slot)
> +		return NULL;
> +
> +	for (i = 0; i < arr->len; i++) {
> +		dev = g_ptr_array_index(arr, i);
> +		if (dev->dev_type != DEV_PCI)
> +			continue;
> +		if (!strcmp(pci_slot, get_prop_pci_slot(dev)))
> +			return dev;
> +	}
> +
> +	return NULL;
> +}
> +
> +bool igt_device_pci_unbind_module(const char *pci_slot)
> +{
> +	char path[PATH_MAX];
> +	struct igt_device *dev = igt_devices_find_pci_id(pci_slot);
> +
> +	if (!dev)
> +		return false;
> +
> +	if (dev->dev_type != DEV_PCI)
> +		return false;
> +
> +	igt_assert(dev->vs);
> +
> +	if (!ensure_driver_is_loaded(dev->vs->modname, dev->vs-
> >modopts))
> +		return false;
> +
> +	igt_info("Unbinding driver %s on %s\n", dev->vs->modname,
> pci_slot);
> +	snprintf(path, PATH_MAX, "%s/%s", IGT_BUS_PCI_DRIVERS_DIR,
> +		 dev->vs->modname);
> +
> +	return sys_write_attr(path, "unbind", pci_slot);
> +}
> +
> +bool igt_device_pci_bind_module(const char *pci_slot)
> +{
> +	char path[PATH_MAX];
> +	struct igt_device *dev = igt_devices_find_pci_id(pci_slot);
> +
> +	if (!dev)
> +		return false;
> +
> +	if (dev->dev_type != DEV_PCI)
> +		return false;
> +
> +	igt_assert(dev->vs);
> +
> +	if (!ensure_driver_is_loaded(dev->vs->modname, dev->vs-
> >modopts))
> +		return false;
> +
> +	igt_info("Binding driver %s to %s\n", dev->vs->modname,
> pci_slot);
> +	snprintf(path, PATH_MAX, "%s/%s", IGT_BUS_PCI_DRIVERS_DIR,
> +		 dev->vs->modname);
> +
> +	return sys_write_attr(path, "bind", pci_slot);
> +}
> +
> +static bool pci_rebind_module(struct igt_device *dev)
> +{
> +	if (dev && dev->dev_type != DEV_PCI)
> +		return false;
> +
> +	igt_device_pci_unbind_module(get_prop_pci_slot(dev));
> +	igt_device_pci_bind_module(get_prop_pci_slot(dev));
> +
> +	return true;
> +}
> +
> +void igt_devices_bind_modules(void)
> +{
> +	struct igt_device *dev;
> +	int i;
> +	GPtrArray *arr;
> +	printf("Binding modules...\n");
> +
> +	arr = igt_devs.devs;
> +	for (i = 0; i < arr->len; i++) {
> +		dev = g_ptr_array_index(arr, i);
> +		if (!dev->drm_card_path) {
> +			pci_rebind_module(dev);
> +		}
> +	}
> +}
> +
> +/* ---------------------------------------------------------------
> ---------- */
> +#define FILTER_SPEC_NAME_LEN 32
> +#define FILTER_SPEC_DATA_LEN 256
> +struct filter_spec {
> +	char name[FILTER_SPEC_NAME_LEN];
> +	char data[FILTER_SPEC_DATA_LEN];
> +};
> +
> +struct filter_func {
> +	GPtrArray *(*filter_function)(struct filter_spec *fspec,
> +				      struct filter_func *ffunc);
> +	const char *help;
> +	const char *detail;
> +
> +	struct vendor_spec *vs; //pointer to vs (if any)
> +};
> +
> +union filter_data {
> +	struct {
> +		char *card;
> +	} drm;
> +	struct {
> +		char *vendor;
> +		char *device;
> +		char *card;
> +	} pci;
> +	struct {
> +		char *card;
> +	} module;
> +	struct {
> +		char *card;
> +	} compatible;
> +};
> +
> +static GHashTable *split_filter_data(const char *data)
> +{
> +	GHashTable *ht = g_hash_table_new_full(g_str_hash, g_str_equal,
> +					       free, free);
> +	gchar **s;
> +	gchar **strv = g_strsplit(data, ",", -1);
> +
> +	s = strv;
> +	while (*s) {
> +		char *k, *v;
> +		v = strchr(*s, '=');
> +		if (!v) {
> +			s++;
> +			continue;
> +		}
> +		k = strndup(*s, v - (*s));
> +		v = strdup(v + 1);
> +		g_hash_table_insert(ht, k, v);
> +		s++;
> +	}
> +	g_strfreev(strv);
> +
> +	return ht;
> +}
> +
> +static bool get_filter_spec(const char *filter, struct filter_spec
> *spec)
> +{
> +	if (!filter)
> +		return false;
> +
> +	if (sscanf(filter, "%31[^:]:%255s", spec->name, spec->data) ==
> 2)
> +		return true;
> +
> +	return false;
> +}
> +
> +static GPtrArray *filter_drm(struct filter_spec *fspec,
> +			     struct filter_func *ffunc)
> +{
> +	GHashTable *ht;
> +	GPtrArray *devs, *view;
> +	union filter_data fdata;
> +	int card = -1;
> +	char cardstr[NAME_MAX];
> +
> +	(void) ffunc;
> +	DBG("filter drm\n");
> +
> +	ht = split_filter_data(fspec->data);
> +	fdata.drm.card = g_hash_table_lookup(ht, "card");
> +
> +	view = igt_devs.view;
> +	devs = igt_devs.devs;
> +
> +	if (fdata.drm.card) {
> +		sscanf(fdata.drm.card, "%d", &card);
> +		if (card < 0) {
> +			g_hash_table_destroy(ht);
> +			return view;
> +		}
> +	}
> +
> +	snprintf(cardstr, NAME_MAX, "%s/card%d", IGT_DRM_PATH, card);
> +	for (int i = 0; i < devs->len; i++) {
> +		struct igt_device *dev = g_ptr_array_index(devs, i);
> +		if (dev->drm_card_path && !strcmp(cardstr, dev-
> >drm_card_path)) {
> +			g_ptr_array_add(view, dev);
> +			break;
> +		}
> +	}
> +
> +	DBG("Filter drm view size: %d\n", view->len);
> +
> +	g_hash_table_destroy(ht);
> +
> +	return view;
> +}
> +
> +static GPtrArray *filter_pci(struct filter_spec *fspec,
> +			     struct filter_func *ffunc)
> +{
> +	GHashTable *ht;
> +	GPtrArray *devs, *view;
> +	union filter_data fdata;
> +
> +	DBG("filter pci\n");
> +
> +	(void) ffunc;
> +	ht = split_filter_data(fspec->data);
> +	fdata.pci.vendor = g_hash_table_lookup(ht, "vendor");
> +	fdata.pci.device = g_hash_table_lookup(ht, "device");
> +	fdata.pci.card = g_hash_table_lookup(ht, "card");
> +
> +	view = igt_devs.view;
> +	devs = igt_devs.devs;
> +
> +	for (int i = 0; i < devs->len; i++) {
> +		struct igt_device *dev = g_ptr_array_index(devs, i);
> +
> +		if (dev->dev_type != DEV_PCI)
> +			continue;
> +
> +		/* Skip if 'vendor' doesn't match (hex or name) */
> +		if (fdata.pci.vendor &&
> +				strcasecmp(fdata.pci.vendor, dev-
> >vendor) &&
> +				!is_pci_vendor_name(fdata.pci.vendor))
> +			continue;
> +
> +		/* Skip if 'device' doesn't match */
> +		if (fdata.pci.device &&
> +				strcasecmp(fdata.pci.device, dev-
> >device))
> +			continue;
> +
> +		g_ptr_array_add(view, dev);
> +	}
> +
> +	/* Leave only n-th element if card is set */
> +	if (fdata.pci.card) {
> +		int card = -1;
> +		sscanf(fdata.pci.card, "%d", &card);
> +
> +		if (card >= 0 && card < view->len) {
> +			struct igt_device *dev =
> g_ptr_array_index(view, card);
> +			g_ptr_array_remove_range(view, 0, view->len);
> +			g_ptr_array_add(view, dev);
> +		} else {
> +			g_ptr_array_remove_range(view, 0, view->len);
> +		}
> +	}
> +	DBG("Filter pci view size: %d\n", view->len);
> +
> +	g_hash_table_destroy(ht);
> +
> +	return view;
> +}
> +
> +/*
> + * Refresh view finding first card matching with
> + * vs->match_driver or vs->modname if vs->match_driver is not set
> + */
> +static GPtrArray *filter_module(struct filter_spec *fspec,
> +				struct filter_func *ffunc)
> +{
> +	GHashTable *ht;
> +	GPtrArray *devs, *view;
> +	union filter_data fdata;
> +	int card = -1;
> +	const char *driver = ffunc->vs->match_driver ?: ffunc->vs-
> >modname;
> +
> +	DBG("filter module [%s]\n", driver);
> +
> +	ht = split_filter_data(fspec->data);
> +	fdata.module.card = g_hash_table_lookup(ht, "card");
> +
> +	view = igt_devs.view;
> +	devs = igt_devs.devs;
> +
> +	if (fdata.module.card) {
> +		sscanf(fdata.module.card, "%d", &card);
> +		if (card < 0) {
> +			g_hash_table_destroy(ht);
> +			return view;
> +		}
> +	}
> +
> +	for (int i = 0; i < devs->len; i++) {
> +		const char *modname;
> +		struct igt_device *dev = g_ptr_array_index(devs, i);
> +
> +		modname = get_prop_driver(dev);
> +		if (!modname)
> +			continue;
> +
> +		if (!strcmp(modname, driver)) {
> +			if (!card) {
> +				g_ptr_array_add(view, dev);
> +				break;
> +			}
> +			card--;
> +		}
> +	}
> +
> +	DBG("Filter view size: %d\n", view->len);
> +
> +	g_hash_table_destroy(ht);
> +
> +	return view;
> +}
> +
> +static const char *find_vendor_id_by_vendor_spec(struct vendor_spec
> *vs)
> +{
> +	struct name_value *list = &gpu_vendor_list[0];
> +
> +	while (list->name) {
> +		if ((struct vendor_spec *) list->value == vs)
> +			return list->name;
> +		list++;
> +	}
> +	return NULL;
> +}
> +
> +static GPtrArray *filter_compatible(struct filter_spec *fspec,
> +				    struct filter_func *ffunc)
> +{
> +	GHashTable *ht;
> +	GPtrArray *devs, *view;
> +	union filter_data fdata;
> +	int card = -1;
> +	const char *of_compatible =
> find_vendor_id_by_vendor_spec(ffunc->vs);
> +
> +	DBG("filter compatible[%s]\n", of_compatible);
> +
> +	ht = split_filter_data(fspec->data);
> +	fdata.module.card = g_hash_table_lookup(ht, "card");
> +
> +	view = igt_devs.view;
> +	devs = igt_devs.devs;
> +
> +	if (fdata.module.card) {
> +		sscanf(fdata.module.card, "%d", &card);
> +		if (card < 0) {
> +			g_hash_table_destroy(ht);
> +			return view;
> +		}
> +	}
> +
> +	for (int i = 0; i < devs->len; i++) {
> +		const char *compat;
> +		struct igt_device *dev = g_ptr_array_index(devs, i);
> +
> +		if (!of_compatible)
> +			break;
> +
> +		compat = get_prop_of_compatible_0(dev);
> +		if (compat && !strcmp(compat, of_compatible)) {
> +			if (!card) {
> +				g_ptr_array_add(view, dev);
> +				break;
> +			}
> +			card--;
> +		}
> +	}
> +
> +	DBG("Filter view size: %d\n", view->len);
> +
> +	g_hash_table_destroy(ht);
> +
> +	return view;
> +}
> +
> +
> +static struct filter_func f_drm = { .filter_function = filter_drm,
> +				    .help = "drm:[card=%d]",
> +				    .detail = "card is N-card number
> (from /dev/dri/cardN)\n",
> +				  };
> +
> +static struct filter_func f_pci = { .filter_function = filter_pci,
> +				    .help =
> "pci:[vendor=%04x/name][,device=%04x][,card=%d]",
> +				    .detail = "vendor is hex number or
> name",
> +				  };
> +
> +static struct filter_func f_vgem = { .filter_function =
> filter_module,
> +				     .help = "vgem:[card=%d]",
Can we use simpler syntax for filters with only card=%d?

Like vkms:0 instead of vkms:card=0. Or maybe set a default such that
"vkms" would equal to "vkms:card=0".

> +				     .detail = "card is n-th vgem card
> number\n",
> +				     .vs = &v_vgem,
> +				   };
> +
> +static struct filter_func f_vkms = { .filter_function =
> filter_module,
> +				     .help = "vkms:[card=%d]",
> +				     .detail = "card is n-th vkms card
> number\n",
> +				     .vs = &v_vkms,
> +				   };
> +
> +static struct filter_func f_exynos = { .filter_function =
> filter_module,
> +				       .help = "exynos:[card=%d]",
> +				       .detail = "card is n-th exynos-
> drm card number\n",
> +				       .vs = &v_exynos,
> +				     };
> +
> +static struct filter_func f_vc4 = { .filter_function =
> filter_compatible,
> +				    .help = "vc4:[card=%d]",
> +				    .detail = "card is n-th vc4 card
> number\n",
> +				    .vs = &v_vc4,
> +				  };
> +
> +struct name_value filter_definition_list[] = {
> +	{ "drm",     (gpointer) &f_drm },
> +	{ "pci",     (gpointer) &f_pci },
> +	{ "vgem",    (gpointer) &f_vgem },
> +	{ "vkms",    (gpointer) &f_vkms },
> +	{ "exynos",  (gpointer) &f_exynos },
> +	{ "vc4",     (gpointer) &f_vc4 },
> +	{ NULL, },
> +};
> +
> +void igt_device_print_filter_types(void)
> +{
> +	GList *keys = g_hash_table_get_keys(filter_definition_ht);
> +
> +	printf("Filter types:\n---\n");
> +	printf("%-8s  %s\n---\n", "filter", "syntax");
> +	while (keys) {
> +		char *k = (char *) keys->data;
> +		struct filter_func *v =
> g_hash_table_lookup(filter_definition_ht, k);
> +		printf("%-8s  %s\n", k, v->help);
> +		printf("%-8s  %s\n", "", v->detail);
> +		keys = g_list_next(keys);
> +	}
> +	g_list_free(keys);
> +}
> +
> +static GPtrArray *device_filters = NULL;
> +
> +#define DEVICE_FILTER_CHECK_ALLOC() \
> +	do { \
> +		if (!device_filters) \
> +			device_filters = g_ptr_array_new_full(2, free);
> \
> +		igt_assert(device_filters); \
> +	} while(0)
> +
> +int igt_device_filter_count(void)
> +{
> +	DEVICE_FILTER_CHECK_ALLOC();
> +
> +	return device_filters->len;
> +}
> +
> +int igt_device_filter_add(const char *filter)
> +{
> +	gchar **strv, **s;
> +	int c = 0;
> +
> +	DEVICE_FILTER_CHECK_ALLOC();
> +
> +	strv = g_strsplit(filter, ";", -1);
> +
> +	s = strv;
> +	while (*s) {
> +		g_ptr_array_add(device_filters, strdup(*s));
> +		s++;
> +	}
> +	g_strfreev(strv);
> +
> +	return c;
> +}
> +
> +const char *igt_device_filter_get(int num)
> +{
> +	DEVICE_FILTER_CHECK_ALLOC();
> +
> +	if (num < 0 || num >= device_filters->len)
> +		return NULL;
> +
> +	return g_ptr_array_index(device_filters, num);
> +}
> +
> +#define dev_is_non_discoverable(vs) \
> +	((vs)->dev_type == DEV_PLATFORM_NON_DISCOVERABLE)
> +
> +bool igt_device_filter_apply(const char *filter)
> +{
> +	struct filter_spec fspec;
> +	struct filter_func *ffunc;
> +	bool ret;
> +
> +	if (!filter)
> +		return false;
> +
> +	ret = get_filter_spec(filter, &fspec);
> +	if (!ret) {
> +		igt_warn("Can't split filter [%s]\n", filter);
> +		return false;
> +	}
> +
> +	ffunc = g_hash_table_lookup(filter_definition_ht, fspec.name);
> +	if (!ffunc) {
> +		igt_warn("No filter with name [%s]\n", fspec.name);
> +		return false;
> +	}
> +
> +	/* Clean view */
> +	g_ptr_array_remove_range(igt_devs.view, 0, igt_devs.view->len);
> +
> +	/* Load module only for non-discoverable devices.
> +	 * For discoverables / PCI defer this to card match path
> +	*/
> +	if (ffunc->vs && dev_is_non_discoverable(ffunc->vs) &&
> +			!igt_kmod_is_loaded(ffunc->vs->modname)) {
> +		if (!igt_kmod_load(ffunc->vs->modname, ffunc->vs-
> >modopts)) {
> +			igt_info("Module %s loaded, rescanning
> devices\n",
> +				 ffunc->vs->modname);
> +			igt_devices_scan(true);
> +		} else {
> +			return false;
> +		}
> +	}
> +
> +	ffunc->filter_function(&fspec, ffunc);
> +
> +	return true;
> +}
> +
> +bool igt_device_filter_apply_nth(int num)
> +{
> +	const char *filter = igt_device_filter_get(num);
> +	if (!filter)
> +		return false;
> +
> +	return igt_device_filter_apply(filter);
> +}
> +
> +/* For PCI devices load or unbind / bind is called in card matching
> path.
> + * This is for 'lsgpu' tool which doesn't want to make magic driver
> + * loading job in filter apply path (it really want to display PCI
> devices
> + * in the state they really are).
> +*/
> +static bool ensure_device_is_ready(struct igt_device *dev,
> +				   const char *filter)
> +{
> +	if (dev->dev_type != DEV_PCI &&
> +		dev->dev_type != DEV_PLATFORM_DISCOVERABLE)
> +		return false;
> +
> +	if (dev->drm_card_path || dev->drm_render_path)
> +		return true;
> +
> +	/* So, we have PCI device or discoverable device without
> +	 * card/render node, lets try at the beginning to simply
> +	 * load the module */
> +	if (!ensure_device_has_driver_loaded(dev))
> +		return false; //oops!
> +
> +	/* Rescan devices, filter again and get the device */
> +	igt_devices_scan(true);
> +	if (igt_device_filter_apply(filter) == false)
> +		return false;
> +
> +	if (!igt_devs.view->len)
> +		return false;
> +
> +	dev = g_ptr_array_index(igt_devs.view, 0);
> +	if (dev->drm_card_path || dev->drm_render_path)
> +		return true;
> +
> +	/* For platform device unbind/bind is not performed */
> +	if (dev->dev_type == DEV_PLATFORM_DISCOVERABLE)
> +		return false;
> +
> +	/* In this moment we likely already had driver loaded,
> +	 * but it wasn't binded to this device. */
> +	igt_device_pci_unbind_module(get_prop_pci_slot(dev));
> +	igt_device_pci_bind_module(get_prop_pci_slot(dev));
> +	igt_devices_scan(true);
> +	if (igt_device_filter_apply(filter) == false)
> +		return false;
> +
> +	if (!igt_devs.view->len)
> +		return false;
> +
> +	dev = g_ptr_array_index(igt_devs.view, 0);
> +	if (dev->drm_card_path || dev->drm_render_path)
> +		return true;
> +
> +	return false;
> +}
> +
> +#define safe_strncpy(dst, src, size) \
> +	if (src) strncpy((dst), (src), (size))
> +/*
> + * Returns:
> + * false - no card pointer was passed or card wasn't matched,
> + * true - card matched and returned.
> + */
> +bool igt_device_card_match(const char *filter, struct
> igt_device_card *card)
> +{	
> +	struct igt_device *dev = NULL;
> +
> +	if (!card)
> +		return false;
> +	memset(card, 0, sizeof(*card));
> +
> +	igt_devices_scan(false);
> +
> +	if (igt_device_filter_apply(filter) == false)
> +		return false;
> +
> +	if (!igt_devs.view->len)
> +		return false;
> +
> +	dev = g_ptr_array_index(igt_devs.view, 0);
> +	if (dev->dev_type == DEV_PCI ||
> +		dev->dev_type == DEV_PLATFORM_DISCOVERABLE) {
> +		if (!ensure_device_is_ready(dev, filter))
> +			return false;
> +	}
> +
> +	if (!igt_devs.view->len) //additional check because rescan
> could happen
> +		return false;
> +
> +	/* We take first one if more than one card matches filter */
> +	dev = g_ptr_array_index(igt_devs.view, 0);
> +	card->dev_type = dev->dev_type;
> +	card->chipset = dev->vs->chipset;
> +	if (dev->dev_type == DEV_PCI) {
> +		safe_strncpy(card->module, dev->vs->modname, NAME_MAX);
> +		safe_strncpy(card->pci_slot, get_prop_pci_slot(dev),
> +			PCI_SLOT_LENGTH);
> +	} else {
> +		safe_strncpy(card->module, get_prop_driver(dev),
> NAME_MAX);
> +	}
> +	safe_strncpy(card->card, dev->drm_card_path, NAME_MAX);
> +	safe_strncpy(card->render, dev->drm_render_path, NAME_MAX);
> +
> +	return true;
> +}
> diff --git a/lib/igt_device_scan.h b/lib/igt_device_scan.h
> new file mode 100644
> index 00000000..0e2adf13
> --- /dev/null
> +++ b/lib/igt_device_scan.h
> @@ -0,0 +1,72 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef __IGT_DEVICE_SCAN_H__
> +#define __IGT_DEVICE_SCAN_H__
> +
> +#include <limits.h>
> +#include <igt.h>
> +
> +enum igt_devices_print_type {
> +	IGT_PRINT_SIMPLE,
> +	IGT_PRINT_DETAIL,
> +};
> +
> +enum igt_device_type {
> +	DEV_PCI,
> +	DEV_PLATFORM_NON_DISCOVERABLE,
> +	DEV_PLATFORM_DISCOVERABLE,
> +};
> +
> +#define PCI_SLOT_LENGTH 13
> +struct igt_device_card {
> +	enum igt_device_type dev_type;
> +	unsigned int chipset; //contains DRIVER_XXX value
> +	char module[NAME_MAX];
> +	char card[NAME_MAX];
> +	char render[NAME_MAX];
> +	char pci_slot[PCI_SLOT_LENGTH];
> +};
> +
> +void igt_devices_scan(bool force);
> +void igt_devices_print(enum igt_devices_print_type printtype,
> +		       bool show_props, bool show_attrs);
> +
> +void igt_devices_print_vendors(void);
> +
> +bool igt_device_pci_unbind_module(const char *pci_slot);
> +bool igt_device_pci_bind_module(const char *pci_slot);
> +void igt_devices_bind_modules(void);
> +
> +void igt_device_print_filter_types(void);
> +
> +int igt_device_filter_count(void);
> +int igt_device_filter_add(const char *filter);
> +const char *igt_device_filter_get(int num);
> +bool igt_device_filter_apply(const char *filter);
> +bool igt_device_filter_apply_nth(int num);
> +
> +bool igt_device_card_match(const char *filter, struct
> igt_device_card *card);
> +
> +#endif /* __IGT_DEVICE_SCAN_H__ */
> diff --git a/lib/meson.build b/lib/meson.build
> index 157624e7..826ebbe3 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -10,6 +10,7 @@ lib_sources = [
>  	'igt_color_encoding.c',
>  	'igt_debugfs.c',
>  	'igt_device.c',
> +	'igt_device_scan.c',
>  	'igt_aux.c',
>  	'igt_gpu_power.c',
>  	'igt_gt.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..b784ca65
> --- /dev/null
> +++ b/tools/lsgpu.c
> @@ -0,0 +1,285 @@
> +/*
> + * 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>
> +
> +enum {
> +	OPT_SHOW_PROPS     = 'r',
> +	OPT_SHOW_ATTRS     = 'a',
> +	OPT_PRINT_SIMPLE   = 'i',
> +	OPT_PRINT_DETAIL   = 'd',
> +	OPT_LIST_VENDORS   = 'v',
> +	OPT_BIND_MODULES   = 'B',
> +	OPT_UNBIND_MODULE  = 'u',
> +	OPT_BIND_MODULE    = 'b',
> +	OPT_LIST_FILTERS   = 'l',
> +	OPT_SET_FILTER     = 's',
> +	OPT_MATCH_DEVICE   = 'm',
> +	OPT_DEVICE         = 'D',
> +	OPT_HELP           = 'h'
> +};
> +
> +bool g_show_props;
> +bool g_show_attrs;
> +bool g_show_vendors;
> +bool g_bind_modules;
> +char *g_pci_id;
> +bool g_unbind_module;
> +bool g_bind_module;
> +bool g_list_filters;
> +char *g_filter;
> +char *g_match_filter;
> +bool g_device;
> +bool g_help;
> +
> +static const char *usage_str =
> +	"usage: lsgpu [options]\n\n"
> +	"Options:\n"
> +	"  -r, --show-props            Show device properties\n"
> +	"  -a, --show-attrs            Show device attributes\n"

What is the difference between those?
Maybe merge those two options into one?

> +	"  -i, --print-simple          Print devices as simple list
> (default)\n"
> +	"  -d, --print-details         Print devices with details\n"
> +	"  -v, --list-vendors          List recognized vendors\n"
> +	"  -B, --bind-modules          Bind modules to unbound PCI
> cards\n"
> +	"  -u, --unbind-module pci_id  Unbind module from pci id
> device\n"
> +	"  -b, --bind-module pci_id    Bind module to pci id device\n"
> +	"  -l, --list-filter-types     List registered device filters
> types\n"

Maybe add those to help?

> +	"  -s, --set-filter filter     Set filter for processing
> devices\n"
> +	"  -m, --match-device filter   Find device matching to
> filter\n"
> +	"  -D, --device filter         Device filter, can be given
> multiple times\n"

What is the difference between theese three options?

What is the semantics of multiple --device? 

Oleg

> +	"  -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;
> +
> +	switch (card->dev_type) {
> +	case DEV_PCI:
> +		printf("dev type    : %s\n", "PCI");
> +		printf("PCI slot    : %s\n", card->pci_slot);
> +		break;
> +	case DEV_PLATFORM_NON_DISCOVERABLE:
> +		printf("dev type    : %s\n", "PLATFORM (NON-
> DISCOVERABLE)");
> +		break;
> +	case DEV_PLATFORM_DISCOVERABLE:
> +		printf("dev type    : %s\n", "PLATFORM
> (DISCOVERABLE)");
> +		break;
> +	}
> +	printf("drv chipset : %x\n", card->chipset);
> +	printf("drm card    : %s\n", card->card);
> +	printf("drm render  : %s\n", card->render);
> +	printf("drv module  : %s\n", card->module);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	static struct option long_options[] = {
> +		{"show-props",        no_argument,       NULL,
> OPT_SHOW_PROPS},
> +		{"show-attrs",        no_argument,       NULL,
> OPT_SHOW_ATTRS},
> +		{"print-simple",      no_argument,       NULL,
> OPT_PRINT_SIMPLE},
> +		{"print-detail",      no_argument,       NULL,
> OPT_PRINT_DETAIL},
> +		{"list-vendors",      no_argument,       NULL,
> OPT_LIST_VENDORS},
> +		{"bind-modules",      no_argument,       NULL,
> OPT_BIND_MODULES},
> +		{"unbind-module",     required_argument, NULL,
> OPT_UNBIND_MODULE},
> +		{"bind-module",       required_argument, NULL,
> OPT_BIND_MODULE},
> +		{"list-filter-types", no_argument,       NULL,
> OPT_LIST_FILTERS},
> +		{"set-filter",        required_argument, NULL,
> OPT_SET_FILTER},
> +		{"match-device",      required_argument, NULL,
> OPT_MATCH_DEVICE},
> +		{"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;
> +
> +	while ((c = getopt_long(argc, argv, "PpraidvBu:b:ls:m:D:h",
> +				long_options, &index)) != -1) {
> +		switch(c) {
> +		case OPT_SHOW_PROPS:
> +			g_show_props = true;
> +			break;
> +		case OPT_SHOW_ATTRS:
> +			g_show_attrs = true;
> +			break;
> +		case OPT_PRINT_SIMPLE:
> +			printtype = IGT_PRINT_SIMPLE;
> +			break;
> +		case OPT_PRINT_DETAIL:
> +			printtype = IGT_PRINT_DETAIL;
> +			break;
> +		case OPT_LIST_VENDORS:
> +			g_show_vendors = true;
> +			break;
> +		case OPT_BIND_MODULES:
> +			g_bind_modules = true;
> +			break;
> +		case OPT_UNBIND_MODULE:
> +			g_pci_id = strdup(optarg);
> +			g_unbind_module = true;
> +			break;
> +		case OPT_BIND_MODULE:
> +			g_pci_id = strdup(optarg);
> +			g_bind_module = true;
> +			break;
> +		case OPT_SET_FILTER:
> +			g_filter = strdup(optarg);
> +			break;
> +		case OPT_LIST_FILTERS:
> +			g_list_filters = true;
> +			break;
> +		case OPT_MATCH_DEVICE:
> +			g_match_filter = strdup(optarg);
> +			break;
> +		case OPT_DEVICE:
> +			g_device = true;
> +			igt_device_filter_add(optarg);
> +			break;
> +		case OPT_HELP:
> +			g_help = true;
> +			break;
> +		}
> +	}
> +
> +	if (g_help) {
> +		printf("%s\n", usage_str);
> +		exit(0);
> +	}
> +
> +	env = getenv("IGT_DEVICE");
> +	if (env) {
> +		igt_device_filter_add(env);
> +		g_device = true;
> +	}
> +
> +	igt_devices_scan(false);
> +	if (g_show_vendors) {
> +		igt_devices_print_vendors();
> +		return 0;
> +	}
> +	if (g_bind_modules) {
> +		igt_devices_bind_modules();
> +		return 0;
> +	}
> +
> +	if (g_unbind_module) {
> +		igt_device_pci_unbind_module(g_pci_id);
> +		return 0;
> +	}
> +
> +	if (g_bind_module) {
> +		igt_device_pci_bind_module(g_pci_id);
> +		return 0;
> +	}
> +
> +	if (g_list_filters) {
> +		igt_device_print_filter_types();
> +		return 0;
> +	}
> +
> +	if (g_filter) {
> +		igt_device_filter_apply(g_filter);
> +	}
> +
> +	if (g_match_filter) {
> +		struct igt_device_card card;
> +		if (igt_device_card_match(g_match_filter, &card)) {
> +			print_card(&card);
> +		} else {
> +			printf("No device matching filter [%s]
> found.\n",
> +			       g_match_filter);
> +			return 0;
> +		}
> +		test_device_open(&card);
> +
> +		return 0;
> +	}
> +
> +	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));
> +			igt_device_filter_apply_nth(i);
> +			igt_devices_print(printtype, g_show_props,
> g_show_attrs);
> +		}
> +		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))
> +				continue;
> +			print_card(&card);
> +			test_device_open(&card);
> +			printf("---\n");
> +		}
> +
> +		return 0;
> +	}
> +
> +	igt_devices_print(printtype, g_show_props, g_show_attrs);
> +
> +	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
>  

[-- Attachment #1.2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3261 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection
  2019-07-12  8:20   ` Vasilev, Oleg
@ 2019-07-12  9:18     ` Zbigniew Kempczyński
  0 siblings, 0 replies; 10+ messages in thread
From: Zbigniew Kempczyński @ 2019-07-12  9:18 UTC (permalink / raw)
  To: Vasilev, Oleg; +Cc: igt-dev, Latvala, Petri, daniel

On Fri, Jul 12, 2019 at 10:20:37AM +0200, Vasilev, Oleg wrote:
> On Thu, 2019-07-11 at 12:30 +0200, Zbigniew Kempczyński wrote:
> > Change adds device selection based on implemented filters.
> > Different filters can be added to address different device
> > selection possibilities.
> > 
> > New device selection uses --device filter or IGT_DEVICE environent
> > variable. Selection of many devices can be done by using --device
> > argument multiple times. As IGT_DEVICE enviroment can be set
> > once ';' is recognized as filter separator.
> > 
> > Tool 'lsgpu' which uses device scanning feature was added.
> 
> Hi,
> 
> Would you mind splitting the patch into several commits? It is quite
> hard to review this as a whole. For example:
> 1. Device selection
> 2. lsgpu
> 3. Documentation
> 4. ...
>

Hi.

Yes, I'll split the patch and resend the patch.

> There are some comments below.
> 

<cut>

> > +static GHashTable *blacklist_keys_ht;      //sysattrs we don't want
> > to read
> > +static GHashTable *gpu_pci_class_ht;       //gpu pci classes we know
> > +static GHashTable *gpu_vendor_ht;          //search id ->
> > vendor_spec mapping
> > +static GHashTable *filter_definition_ht;   //supported filters
> > (pci=..., etc.)
> 
> Are you sure we need hashtables for all this stuff? Mappings with 5-20
> records should work with linear search well enough...

There's always design decision to use or not to use specific data structures.
Using hash tables in glib is easy and well documented. Hash table behaves
better than glibc hashtables. I decided to use them because in the code
I need to answer the question does a specific key exists or what is its
value.  

<cut>

> > +void igt_devices_print_vendors(void)
> > +{
> > +	struct name_value *list = &gpu_vendor_list[0];
> > +	printf("Recognized vendors:\n");
> > +
> > +	printf("%-16s %-32s %-32s %-16s\n", "Device type", "id",
> 
> Theese %-32s and %-16s look very bulky when you run the command in non-
> fullscreen terminal. 

Agree. I will display this list in more condensed form to not to exceed 
80-chars in a row.

<cut>

> > +static struct filter_func f_vgem = { .filter_function =
> > filter_module,
> > +				     .help = "vgem:[card=%d]",
> Can we use simpler syntax for filters with only card=%d?
> 
> Like vkms:0 instead of vkms:card=0. Or maybe set a default such that
> "vkms" would equal to "vkms:card=0".
>
At the moment I assumed we will specify filters using:
filtername:key=value,key2=value2,...

That was for simpler implementation. Syntax where you have 

filtername:value 

lead to additional check in the code I wanted to avoid. As split
filtername:... is done using ':' change filter to select first
matching card could be:
--device 'vgem:' 
Of course I need to handle null in filter value as first vgem card.
I'll add that one if this satisfy your needs.

> > +static const char *usage_str =
> > +	"usage: lsgpu [options]\n\n"
> > +	"Options:\n"
> > +	"  -r, --show-props            Show device properties\n"
> > +	"  -a, --show-attrs            Show device attributes\n"
> 
> What is the difference between those?
> Maybe merge those two options into one?

Udev distincts information about devices to properties and attributes
(sysattrs). Use 'lsgpu -d' on some card to see the difference.

> 
> > +	"  -i, --print-simple          Print devices as simple list
> > (default)\n"
> > +	"  -d, --print-details         Print devices with details\n"
> > +	"  -v, --list-vendors          List recognized vendors\n"
> > +	"  -B, --bind-modules          Bind modules to unbound PCI
> > cards\n"
> > +	"  -u, --unbind-module pci_id  Unbind module from pci id
> > device\n"
> > +	"  -b, --bind-module pci_id    Bind module to pci id device\n"
> > +	"  -l, --list-filter-types     List registered device filters
> > types\n"
> 
> Maybe add those to help?

Could you clarify?

> 
> > +	"  -s, --set-filter filter     Set filter for processing
> > devices\n"
> > +	"  -m, --match-device filter   Find device matching to
> > filter\n"
> > +	"  -D, --device filter         Device filter, can be given
> > multiple times\n"
> 
> What is the difference between theese three options?
> 
> What is the semantics of multiple --device? 

-s sets filter before displaying discovered devices. This doesn't
lead to loading module (so for example PCI card can be displayed
even if module is not loaded yet, same for discoverable devices).

-m matches card, what means returning all data (important are
/dev/dri/cardX, /dev/dri/renderDX and chipset) in a way similar
to stat() (so client has to provide ptr to allocated data structure,
to avoid additional api to free structure if it could contain
few memory allocated strings). In this case code ensures module
for card is loaded (otherwise it couldn't return device names).

-D/--device - tests multiple device selection API. Each 
--device calls stores such filter inside array. If you separate
filters with ';' it will split and add each filter to the array.

> 
> Oleg
> 

Zbigniew
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [igt-dev] ✓ Fi.CI.IGT: success for Add device selection methods
  2019-07-11 10:30 [igt-dev] [PATCH i-g-t v1 0/1] Add device selection methods Zbigniew Kempczyński
  2019-07-11 10:30 ` [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection Zbigniew Kempczyński
  2019-07-11 15:13 ` [igt-dev] ✓ Fi.CI.BAT: success for Add device selection methods Patchwork
@ 2019-07-12 10:08 ` Patchwork
  2 siblings, 0 replies; 10+ messages in thread
From: Patchwork @ 2019-07-12 10:08 UTC (permalink / raw)
  To: Zbigniew Kempczyński; +Cc: igt-dev

== Series Details ==

Series: Add device selection methods
URL   : https://patchwork.freedesktop.org/series/63553/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_6458_full -> IGTPW_3256_full
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/63553/revisions/1/mbox/

Known issues
------------

  Here are the changes found in IGTPW_3256_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_exec_balancer@smoke:
    - shard-iclb:         [PASS][1] -> [SKIP][2] ([fdo#110854])
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-iclb2/igt@gem_exec_balancer@smoke.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-iclb5/igt@gem_exec_balancer@smoke.html

  * igt@i915_suspend@fence-restore-untiled:
    - shard-apl:          [PASS][3] -> [DMESG-WARN][4] ([fdo#108566]) +2 similar issues
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-apl2/igt@i915_suspend@fence-restore-untiled.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-apl7/igt@i915_suspend@fence-restore-untiled.html

  * igt@kms_draw_crc@draw-method-rgb565-mmap-gtt-xtiled:
    - shard-iclb:         [PASS][5] -> [FAIL][6] ([fdo#103184] / [fdo#103232])
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-iclb6/igt@kms_draw_crc@draw-method-rgb565-mmap-gtt-xtiled.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-iclb4/igt@kms_draw_crc@draw-method-rgb565-mmap-gtt-xtiled.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-render:
    - shard-iclb:         [PASS][7] -> [FAIL][8] ([fdo#103167]) +1 similar issue
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-iclb8/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-render.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-iclb3/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-render.html

  * igt@kms_plane@plane-position-covered-pipe-a-planes:
    - shard-apl:          [PASS][9] -> [FAIL][10] ([fdo#110038])
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-apl8/igt@kms_plane@plane-position-covered-pipe-a-planes.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-apl6/igt@kms_plane@plane-position-covered-pipe-a-planes.html
    - shard-kbl:          [PASS][11] -> [FAIL][12] ([fdo#110038])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-kbl1/igt@kms_plane@plane-position-covered-pipe-a-planes.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-kbl4/igt@kms_plane@plane-position-covered-pipe-a-planes.html

  * igt@kms_plane_lowres@pipe-a-tiling-y:
    - shard-iclb:         [PASS][13] -> [FAIL][14] ([fdo#103166])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-iclb6/igt@kms_plane_lowres@pipe-a-tiling-y.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-iclb8/igt@kms_plane_lowres@pipe-a-tiling-y.html

  * igt@kms_psr@psr2_cursor_render:
    - shard-iclb:         [PASS][15] -> [SKIP][16] ([fdo#109441]) +2 similar issues
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-iclb2/igt@kms_psr@psr2_cursor_render.html
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-iclb7/igt@kms_psr@psr2_cursor_render.html

  * igt@kms_setmode@basic:
    - shard-kbl:          [PASS][17] -> [FAIL][18] ([fdo#99912])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-kbl4/igt@kms_setmode@basic.html
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-kbl4/igt@kms_setmode@basic.html

  * igt@tools_test@tools_test:
    - shard-hsw:          [PASS][19] -> [SKIP][20] ([fdo#109271])
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-hsw4/igt@tools_test@tools_test.html
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-hsw6/igt@tools_test@tools_test.html

  
#### Possible fixes ####

  * igt@gem_workarounds@suspend-resume:
    - shard-apl:          [DMESG-WARN][21] ([fdo#108566]) -> [PASS][22] +3 similar issues
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-apl6/igt@gem_workarounds@suspend-resume.html
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-apl5/igt@gem_workarounds@suspend-resume.html

  * igt@i915_pm_rpm@system-suspend:
    - shard-kbl:          [INCOMPLETE][23] ([fdo#103665] / [fdo#107807]) -> [PASS][24]
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-kbl6/igt@i915_pm_rpm@system-suspend.html
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-kbl3/igt@i915_pm_rpm@system-suspend.html

  * igt@kms_ccs@pipe-c-crc-sprite-planes-basic:
    - shard-iclb:         [INCOMPLETE][25] ([fdo#107713]) -> [PASS][26]
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-iclb7/igt@kms_ccs@pipe-c-crc-sprite-planes-basic.html
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-iclb3/igt@kms_ccs@pipe-c-crc-sprite-planes-basic.html

  * igt@kms_cursor_legacy@2x-long-flip-vs-cursor-atomic:
    - shard-glk:          [FAIL][27] ([fdo#104873]) -> [PASS][28]
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-glk9/igt@kms_cursor_legacy@2x-long-flip-vs-cursor-atomic.html
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-glk5/igt@kms_cursor_legacy@2x-long-flip-vs-cursor-atomic.html

  * igt@kms_flip@dpms-vs-vblank-race-interruptible:
    - shard-glk:          [FAIL][29] ([fdo#103060]) -> [PASS][30]
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-glk7/igt@kms_flip@dpms-vs-vblank-race-interruptible.html
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-glk2/igt@kms_flip@dpms-vs-vblank-race-interruptible.html

  * igt@kms_flip@flip-vs-suspend:
    - shard-hsw:          [INCOMPLETE][31] ([fdo#103540]) -> [PASS][32]
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-hsw5/igt@kms_flip@flip-vs-suspend.html
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-hsw8/igt@kms_flip@flip-vs-suspend.html

  * igt@kms_frontbuffer_tracking@fbc-2p-primscrn-shrfb-msflip-blt:
    - shard-glk:          [FAIL][33] ([fdo#103167]) -> [PASS][34]
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-glk1/igt@kms_frontbuffer_tracking@fbc-2p-primscrn-shrfb-msflip-blt.html
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-glk3/igt@kms_frontbuffer_tracking@fbc-2p-primscrn-shrfb-msflip-blt.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-pwrite:
    - shard-iclb:         [FAIL][35] ([fdo#103167]) -> [PASS][36] +2 similar issues
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-iclb5/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-pwrite.html
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-iclb3/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-pwrite.html

  * igt@kms_psr@psr2_sprite_plane_move:
    - shard-iclb:         [SKIP][37] ([fdo#109441]) -> [PASS][38] +2 similar issues
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6458/shard-iclb6/igt@kms_psr@psr2_sprite_plane_move.html
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/shard-iclb2/igt@kms_psr@psr2_sprite_plane_move.html

  
  [fdo#103060]: https://bugs.freedesktop.org/show_bug.cgi?id=103060
  [fdo#103166]: https://bugs.freedesktop.org/show_bug.cgi?id=103166
  [fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
  [fdo#103184]: https://bugs.freedesktop.org/show_bug.cgi?id=103184
  [fdo#103232]: https://bugs.freedesktop.org/show_bug.cgi?id=103232
  [fdo#103540]: https://bugs.freedesktop.org/show_bug.cgi?id=103540
  [fdo#103665]: https://bugs.freedesktop.org/show_bug.cgi?id=103665
  [fdo#104873]: https://bugs.freedesktop.org/show_bug.cgi?id=104873
  [fdo#107713]: https://bugs.freedesktop.org/show_bug.cgi?id=107713
  [fdo#107807]: https://bugs.freedesktop.org/show_bug.cgi?id=107807
  [fdo#108566]: https://bugs.freedesktop.org/show_bug.cgi?id=108566
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109441]: https://bugs.freedesktop.org/show_bug.cgi?id=109441
  [fdo#110038]: https://bugs.freedesktop.org/show_bug.cgi?id=110038
  [fdo#110854]: https://bugs.freedesktop.org/show_bug.cgi?id=110854
  [fdo#99912]: https://bugs.freedesktop.org/show_bug.cgi?id=99912


Participating hosts (11 -> 6)
------------------------------

  Missing    (5): shard-skl pig-hsw-4770r pig-snb-2600 pig-glk-j5005 pig-skl-6260u 


Build changes
-------------

  * IGT: IGT_5093 -> IGTPW_3256
  * Piglit: piglit_4509 -> None

  CI_DRM_6458: fe4d1459b31768c40f907bb859f25197e1af2d07 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_3256: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/
  IGT_5093: 86dc48ede7c33bf69e15f84179d2f9e5b84c179b @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
  piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_3256/
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection
  2019-07-11 12:43   ` Daniel Vetter
@ 2019-07-15  6:25     ` Kempczynski, Zbigniew
  2019-07-15  9:31       ` Daniel Vetter
  0 siblings, 1 reply; 10+ messages in thread
From: Kempczynski, Zbigniew @ 2019-07-15  6:25 UTC (permalink / raw)
  To: daniel; +Cc: igt-dev, Latvala, Petri

On Thu, 2019-07-11 at 14:43 +0200, Daniel Vetter wrote:
> Instead of text, can we somehow integrate into our gtkdoc stuff? I know
> gtkdoc isn't as flexible as the kernel by far, but random .txt files isn't
> better either ...

Agree. I'll rewrite this to gtkdoc.

> 
> >  lib/Makefile.sources            |    2 +
> >  lib/drmtest.c                   |  151 +++-
> >  lib/drmtest.h                   |    9 +
> >  lib/igt_core.c                  |   13 +
> >  lib/igt_device_scan.c           | 1425 +++++++++++++++++++++++++++++++
> >  lib/igt_device_scan.h           |   72 ++
> >  lib/meson.build                 |    1 +
> >  tools/Makefile.sources          |    1 +
> >  tools/lsgpu.c                   |  285 +++++++
> >  tools/meson.build               |    1 +
> >  11 files changed, 2073 insertions(+), 2 deletions(-)
> >  create mode 100644 docs/multi-device-selection.txt
> >  create mode 100644 lib/igt_device_scan.c
> >  create mode 100644 lib/igt_device_scan.h
> >  create mode 100644 tools/lsgpu.c
> 
> Wrt the design itself: I thought the last discussion on this we agreed on
> basing this on udev filters. Not reinvinting an entire device
> parser/filter ourselves (which this does here), because that means more
> work on arm and anywhere else. Do we have a case of lost in communication
> between all the various mail threads going on here?

I've used udev filters for scanning separate bus and returning all devices 
on it. I haven't use all of its filters when I realized that it doesn't allow 
filtering properly on some sysattrs. It resolves symbolic links only 
for limited set of attributes what is a real problem - you can't set 
sysattr filter because value of such attribute is NULL. 

Another thing is that udev provides properties / attributes as list instead 
of key / value hash table. So if you use --device ... multiple times 
you'll iterate over such lists multiple time to get appropriate 'key=value'
instead getting it directly from hashtable. 

That's why I decided resolve each udev device to internal igt_device
once, allowing people who want to write filter just direct access to 
properties / sysattrs. Missing symbolic links resolving is also done
in that step so filter author doesn't need to do this on his own 
each time he would iterate over udev prop/sysattrs lists.

So regarding your sentence in lost communication - I didn't wanted to 
bother anyone with problems described above. I'm not sure even if 
I would send a patch to udev team regarding symbolic links they
would accept it (someone maybe relies on this behavior now, so it
could break compatibility). And we don't have single udev implementation
(eudev) too.

Zbigniew

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection
  2019-07-15  6:25     ` Kempczynski, Zbigniew
@ 2019-07-15  9:31       ` Daniel Vetter
  2019-07-15 11:22         ` Kempczynski, Zbigniew
  0 siblings, 1 reply; 10+ messages in thread
From: Daniel Vetter @ 2019-07-15  9:31 UTC (permalink / raw)
  To: Kempczynski, Zbigniew; +Cc: igt-dev, Latvala, Petri, daniel

On Mon, Jul 15, 2019 at 06:25:04AM +0000, Kempczynski, Zbigniew wrote:
> On Thu, 2019-07-11 at 14:43 +0200, Daniel Vetter wrote:
> > Instead of text, can we somehow integrate into our gtkdoc stuff? I know
> > gtkdoc isn't as flexible as the kernel by far, but random .txt files isn't
> > better either ...
> 
> Agree. I'll rewrite this to gtkdoc.
> 
> > 
> > >  lib/Makefile.sources            |    2 +
> > >  lib/drmtest.c                   |  151 +++-
> > >  lib/drmtest.h                   |    9 +
> > >  lib/igt_core.c                  |   13 +
> > >  lib/igt_device_scan.c           | 1425 +++++++++++++++++++++++++++++++
> > >  lib/igt_device_scan.h           |   72 ++
> > >  lib/meson.build                 |    1 +
> > >  tools/Makefile.sources          |    1 +
> > >  tools/lsgpu.c                   |  285 +++++++
> > >  tools/meson.build               |    1 +
> > >  11 files changed, 2073 insertions(+), 2 deletions(-)
> > >  create mode 100644 docs/multi-device-selection.txt
> > >  create mode 100644 lib/igt_device_scan.c
> > >  create mode 100644 lib/igt_device_scan.h
> > >  create mode 100644 tools/lsgpu.c
> > 
> > Wrt the design itself: I thought the last discussion on this we agreed on
> > basing this on udev filters. Not reinvinting an entire device
> > parser/filter ourselves (which this does here), because that means more
> > work on arm and anywhere else. Do we have a case of lost in communication
> > between all the various mail threads going on here?
> 
> I've used udev filters for scanning separate bus and returning all devices 
> on it. I haven't use all of its filters when I realized that it doesn't allow 
> filtering properly on some sysattrs. It resolves symbolic links only 
> for limited set of attributes what is a real problem - you can't set 
> sysattr filter because value of such attribute is NULL. 
> 
> Another thing is that udev provides properties / attributes as list instead 
> of key / value hash table. So if you use --device ... multiple times 
> you'll iterate over such lists multiple time to get appropriate 'key=value'
> instead getting it directly from hashtable. 
> 
> That's why I decided resolve each udev device to internal igt_device
> once, allowing people who want to write filter just direct access to 
> properties / sysattrs. Missing symbolic links resolving is also done
> in that step so filter author doesn't need to do this on his own 
> each time he would iterate over udev prop/sysattrs lists.
> 
> So regarding your sentence in lost communication - I didn't wanted to 
> bother anyone with problems described above. I'm not sure even if 
> I would send a patch to udev team regarding symbolic links they
> would accept it (someone maybe relies on this behavior now, so it
> could break compatibility). And we don't have single udev implementation
> (eudev) too.

The entire point of review is to create a shared understanding of the
problems involved. Dropping that stuff is pretty crucial.

Also, I'm not really following your description. Maybe there's a gap
between the udev library interface and what I can get at the command line.
But if I look at

# udevadm info -e

And for a specific device, the pile of links/higher level directories,
then I think we should be able to find everything.

Maybe another part of the misunderstanding: Imo we don't want to identify
physical devices, we want to identify drm_devices. Module reload is very
much the exception, and the trickery we have to let igt/lib load the
module if it's not there is imo a bit a hack. Aside from for vgem, where
it makes some sense at least.

So rough algorithimg I had in mind:
1. iterate all drm_devices in sysfs
2. walk the link to physical device
3. go up the hierarchy

Anywhere where we spot a name=value pair that matches what we filter, we
stop, and use that device. Plus augmented with the udevadm info -e stuff,
so you can add arbitrary additional stuff on top. Zero reinvented wheel
needed in igt.

Aside: Maybe we should ditch the module autoload stuff again, except for
vgem. Really no idea why that's needed.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection
  2019-07-15  9:31       ` Daniel Vetter
@ 2019-07-15 11:22         ` Kempczynski, Zbigniew
  0 siblings, 0 replies; 10+ messages in thread
From: Kempczynski, Zbigniew @ 2019-07-15 11:22 UTC (permalink / raw)
  To: daniel; +Cc: igt-dev, Latvala, Petri

On Mon, 2019-07-15 at 11:31 +0200, Daniel Vetter wrote:
> 
> The entire point of review is to create a shared understanding of the
> problems involved. Dropping that stuff is pretty crucial.
> 
> Also, I'm not really following your description. Maybe there's a gap
> between the udev library interface and what I can get at the command line.
> But if I look at
> 
> # udevadm info -e
> 
> And for a specific device, the pile of links/higher level directories,
> then I think we should be able to find everything.

Data acquired from udev database are not enough in some cases to do 
the logic I need in CI. So without following the sysattrs I'm not able
to do what I wanted to achieve with udev filtering only. 

'udevadm info -e' show properties only, sysattrs are not printed.

> Maybe another part of the misunderstanding: Imo we don't want to identify
> physical devices, we want to identify drm_devices. Module reload is very
> much the exception, and the trickery we have to let igt/lib load the
> module if it's not there is imo a bit a hack. Aside from for vgem, where
> it makes some sense at least.
> 
> So rough algorithimg I had in mind:
> 1. iterate all drm_devices in sysfs
> 2. walk the link to physical device
> 3. go up the hierarchy
> 
> Anywhere where we spot a name=value pair that matches what we filter, we
> stop, and use that device. Plus augmented with the udevadm info -e stuff,
> so you can add arbitrary additional stuff on top. Zero reinvented wheel
> needed in igt.
> 
> Aside: Maybe we should ditch the module autoload stuff again, except for
> vgem. Really no idea why that's needed.

All assumptions I made regarding patch I sent were sum of all talks with
different people. Your point of view is strictly udev/drm but other think
about devices in more (how to say it?) contextual manner. 

Could you write how do you think should igt test run should look like
when device filter could be passed by user? 

I mean: ./gem_device_test --device ??? --device ??? 

Zbigniew
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-07-15 11:22 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-11 10:30 [igt-dev] [PATCH i-g-t v1 0/1] Add device selection methods Zbigniew Kempczyński
2019-07-11 10:30 ` [igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection Zbigniew Kempczyński
2019-07-11 12:43   ` Daniel Vetter
2019-07-15  6:25     ` Kempczynski, Zbigniew
2019-07-15  9:31       ` Daniel Vetter
2019-07-15 11:22         ` Kempczynski, Zbigniew
2019-07-12  8:20   ` Vasilev, Oleg
2019-07-12  9:18     ` Zbigniew Kempczyński
2019-07-11 15:13 ` [igt-dev] ✓ Fi.CI.BAT: success for Add device selection methods Patchwork
2019-07-12 10:08 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.