diff mbox series

[PATH,i-g-t] igt: Test tagging support

Message ID 20180907111420.12970-1-tvrtko.ursulin@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series [PATH,i-g-t] igt: Test tagging support | expand

Commit Message

Tvrtko Ursulin Sept. 7, 2018, 11:14 a.m. UTC
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Proposal to add test tags as a replacement for separate test
list which can be difficult to maintain and get out of date.

Putting this maintanenace inline with tests makes it easier
to remember to update the (now implicit) lists, and also enables
richer test selection possibilities for the test runner.

Current method of implying tags from test/subtest names has a
couple of problems one of which is where some names can use
the same token for different meanings. (One example is the
"default" token.) It also creates a name clash between naming
and tagging.

Test tags are strings of tokens separated by spaces, meaning of
which we decide by creating a convetion and helped by the
suitable helper macros.

For example tags can be things like: gem, kms, basic, guc, flip,
semaphore, bz12345678, gt4e, reset, etc..

At runtime this would look something like this (abbreviated for
readability):

  @ tests/gem_sync --list-subtests-with-tags
  ...
  forked-each TAGS="gem "
  forked-store-each TAGS="gem "
  basic-all TAGS="gem basic "
  basic-store-all TAGS="gem basic "

  @ tests/gem_concurrent_blit --list-subtests-with-tags
  ...
  16MiB-swap-gpuX-render-write-read-bcs-bomb TAGS="gem stress "
  16MiB-swap-gpuX-render-write-read-rcs-bomb TAGS="gem stress "
  16MiB-swap-gpuX-render-gpu-read-after-write-bomb TAGS="gem stress "

  @ tests/kms_flip --list-subtests-with-tags | grep basic
  basic-plain-flip TAGS="kms basic "
  basic-flip-vs-dpms TAGS="kms basic "

Test runner could then enable usages like:

  ./run-tests --include gem --exclude stress

Or on simulation for example:

  ./run-tests --exclude stress,reset,rc6,rps,vblank

Which I think could really be useful and more flexible than name
based selection.

The simulation example could also replace the sprinkle of
igt_skip_on_simulation which often lacks context, which tags could
provide.

This RFC implementation automatically adds various tags based on
both binary and subtest names. This is to enable easy transition
to the tag based system.

Idea is that all current tests get some useful tags with minimum
amount of work. Unfortunately this doesn't work for test binaries
which at the moment do not support the subtests enumeration so
those are the ones which would need identifying and converting.

Source code usage, for tests which would want to override the
default tag generation would look something like:

	igt_gem_subtest("basic", "each")
		...

	igt_gem_subtest("manual", "special-setup-test")
		...

        igt_gem_stress_subtest_f("tag", "name-%s", ...) {
	        ...

We can of course polish the details of the API if the idea will
be deemed interesting.

v2: More default tags for untagged tests for easier migration.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 lib/igt_core.c             | 115 +++++++++++++++++++++++++++++++++++--
 lib/igt_core.h             |  29 +++++++++-
 tests/gem_concurrent_all.c |  34 +++++------
 3 files changed, 152 insertions(+), 26 deletions(-)

Comments

Chris Wilson Sept. 7, 2018, 11:43 a.m. UTC | #1
Quoting Tvrtko Ursulin (2018-09-07 12:14:20)
> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> 
> Proposal to add test tags as a replacement for separate test
> list which can be difficult to maintain and get out of date.
> 
> Putting this maintanenace inline with tests makes it easier
> to remember to update the (now implicit) lists, and also enables
> richer test selection possibilities for the test runner.
> 
> Current method of implying tags from test/subtest names has a
> couple of problems one of which is where some names can use
> the same token for different meanings. (One example is the
> "default" token.) It also creates a name clash between naming
> and tagging.
> 
> Test tags are strings of tokens separated by spaces, meaning of
> which we decide by creating a convetion and helped by the
> suitable helper macros.
> 
> For example tags can be things like: gem, kms, basic, guc, flip,
> semaphore, bz12345678, gt4e, reset, etc..
> 
> At runtime this would look something like this (abbreviated for
> readability):
> 
>   @ tests/gem_sync --list-subtests-with-tags
>   ...
>   forked-each TAGS="gem "
>   forked-store-each TAGS="gem "
>   basic-all TAGS="gem basic "
>   basic-store-all TAGS="gem basic "
> 
>   @ tests/gem_concurrent_blit --list-subtests-with-tags
>   ...
>   16MiB-swap-gpuX-render-write-read-bcs-bomb TAGS="gem stress "
>   16MiB-swap-gpuX-render-write-read-rcs-bomb TAGS="gem stress "
>   16MiB-swap-gpuX-render-gpu-read-after-write-bomb TAGS="gem stress "
> 
>   @ tests/kms_flip --list-subtests-with-tags | grep basic
>   basic-plain-flip TAGS="kms basic "
>   basic-flip-vs-dpms TAGS="kms basic "
> 
> Test runner could then enable usages like:
> 
>   ./run-tests --include gem --exclude stress

Minor comment, I like some basic boolean algebra here
--include "gem AND smoketest NOT stress"
:)

I'd probably tag everything according to which ioctls they use. I feel my
endgoal is still to list tests by their kernel functions (which can and
should be automatically derived), and their hw function which is the
open problem.
-Chris
Tvrtko Ursulin Sept. 12, 2018, 8:48 a.m. UTC | #2
On 07/09/2018 12:43, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-07 12:14:20)
>> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>>
>> Proposal to add test tags as a replacement for separate test
>> list which can be difficult to maintain and get out of date.
>>
>> Putting this maintanenace inline with tests makes it easier
>> to remember to update the (now implicit) lists, and also enables
>> richer test selection possibilities for the test runner.
>>
>> Current method of implying tags from test/subtest names has a
>> couple of problems one of which is where some names can use
>> the same token for different meanings. (One example is the
>> "default" token.) It also creates a name clash between naming
>> and tagging.
>>
>> Test tags are strings of tokens separated by spaces, meaning of
>> which we decide by creating a convetion and helped by the
>> suitable helper macros.
>>
>> For example tags can be things like: gem, kms, basic, guc, flip,
>> semaphore, bz12345678, gt4e, reset, etc..
>>
>> At runtime this would look something like this (abbreviated for
>> readability):
>>
>>    @ tests/gem_sync --list-subtests-with-tags
>>    ...
>>    forked-each TAGS="gem "
>>    forked-store-each TAGS="gem "
>>    basic-all TAGS="gem basic "
>>    basic-store-all TAGS="gem basic "
>>
>>    @ tests/gem_concurrent_blit --list-subtests-with-tags
>>    ...
>>    16MiB-swap-gpuX-render-write-read-bcs-bomb TAGS="gem stress "
>>    16MiB-swap-gpuX-render-write-read-rcs-bomb TAGS="gem stress "
>>    16MiB-swap-gpuX-render-gpu-read-after-write-bomb TAGS="gem stress "
>>
>>    @ tests/kms_flip --list-subtests-with-tags | grep basic
>>    basic-plain-flip TAGS="kms basic "
>>    basic-flip-vs-dpms TAGS="kms basic "
>>
>> Test runner could then enable usages like:
>>
>>    ./run-tests --include gem --exclude stress
> 
> Minor comment, I like some basic boolean algebra here
> --include "gem AND smoketest NOT stress"
> :)

That's what my hypothetical examples showed just with a different syntax!

> I'd probably tag everything according to which ioctls they use. I feel my
> endgoal is still to list tests by their kernel functions (which can and
> should be automatically derived), and their hw function which is the
> open problem.

If we want to do that automatically then tagging in this flavour 
probably doesn't make sense and it should instead be an external database.

If we go on the ioctl granularity it can probably mostly have one 
version, and it should be static enough to be able to live in the tree, 
but if we go more granular, like something derived from kcov, then 
that's out of the window. Since it will be tied both to a platform and 
kernel version.

So I think tagging as proposed here is not appropriate for either, but 
only if we wanted to tag on coarser functional areas and such.

Regards,

Tvrtko
Chris Wilson Sept. 12, 2018, 9:07 a.m. UTC | #3
Quoting Tvrtko Ursulin (2018-09-12 09:48:00)
> 
> On 07/09/2018 12:43, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2018-09-07 12:14:20)
> >> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> >>
> >> Proposal to add test tags as a replacement for separate test
> >> list which can be difficult to maintain and get out of date.
> >>
> >> Putting this maintanenace inline with tests makes it easier
> >> to remember to update the (now implicit) lists, and also enables
> >> richer test selection possibilities for the test runner.
> >>
> >> Current method of implying tags from test/subtest names has a
> >> couple of problems one of which is where some names can use
> >> the same token for different meanings. (One example is the
> >> "default" token.) It also creates a name clash between naming
> >> and tagging.
> >>
> >> Test tags are strings of tokens separated by spaces, meaning of
> >> which we decide by creating a convetion and helped by the
> >> suitable helper macros.
> >>
> >> For example tags can be things like: gem, kms, basic, guc, flip,
> >> semaphore, bz12345678, gt4e, reset, etc..
> >>
> >> At runtime this would look something like this (abbreviated for
> >> readability):
> >>
> >>    @ tests/gem_sync --list-subtests-with-tags
> >>    ...
> >>    forked-each TAGS="gem "
> >>    forked-store-each TAGS="gem "
> >>    basic-all TAGS="gem basic "
> >>    basic-store-all TAGS="gem basic "
> >>
> >>    @ tests/gem_concurrent_blit --list-subtests-with-tags
> >>    ...
> >>    16MiB-swap-gpuX-render-write-read-bcs-bomb TAGS="gem stress "
> >>    16MiB-swap-gpuX-render-write-read-rcs-bomb TAGS="gem stress "
> >>    16MiB-swap-gpuX-render-gpu-read-after-write-bomb TAGS="gem stress "
> >>
> >>    @ tests/kms_flip --list-subtests-with-tags | grep basic
> >>    basic-plain-flip TAGS="kms basic "
> >>    basic-flip-vs-dpms TAGS="kms basic "
> >>
> >> Test runner could then enable usages like:
> >>
> >>    ./run-tests --include gem --exclude stress
> > 
> > Minor comment, I like some basic boolean algebra here
> > --include "gem AND smoketest NOT stress"
> > :)
> 
> That's what my hypothetical examples showed just with a different syntax!
> 
> > I'd probably tag everything according to which ioctls they use. I feel my
> > endgoal is still to list tests by their kernel functions (which can and
> > should be automatically derived), and their hw function which is the
> > open problem.
> 
> If we want to do that automatically then tagging in this flavour 
> probably doesn't make sense and it should instead be an external database.
> 
> If we go on the ioctl granularity it can probably mostly have one 
> version, and it should be static enough to be able to live in the tree, 
> but if we go more granular, like something derived from kcov, then 
> that's out of the window. Since it will be tied both to a platform and 
> kernel version.
> 
> So I think tagging as proposed here is not appropriate for either, but 
> only if we wanted to tag on coarser functional areas and such.

Yeah, I think the same as well, that tags should be "smoke", "stress".
(But one man's stress is another's minimal workload :(

But both of those are in essence a duration parameter, and I'd prefer if
we expressed those as configurable parameters. At which point there is a
meta level of test scripts to tag ;)

I feel that "gem", "kms" is better expressed in lower level terms, so
what's left to tag? Clients? "display", "opencl", "media", "opengl" ?
Even using client specs for features (e.g. EGL_IMG_context_priority)?

Who and why would use those? From a selfish perspective, I want lcov
with tests sorted in order of increasing "stress" (start with the
precise X does Y test, finish with can it survive pathological client
behaviour for 48 hours).
-Chris
Tvrtko Ursulin Sept. 12, 2018, 1:44 p.m. UTC | #4
On 12/09/2018 10:07, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-12 09:48:00)
>>
>> On 07/09/2018 12:43, Chris Wilson wrote:
>>> Quoting Tvrtko Ursulin (2018-09-07 12:14:20)
>>>> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>>>>
>>>> Proposal to add test tags as a replacement for separate test
>>>> list which can be difficult to maintain and get out of date.
>>>>
>>>> Putting this maintanenace inline with tests makes it easier
>>>> to remember to update the (now implicit) lists, and also enables
>>>> richer test selection possibilities for the test runner.
>>>>
>>>> Current method of implying tags from test/subtest names has a
>>>> couple of problems one of which is where some names can use
>>>> the same token for different meanings. (One example is the
>>>> "default" token.) It also creates a name clash between naming
>>>> and tagging.
>>>>
>>>> Test tags are strings of tokens separated by spaces, meaning of
>>>> which we decide by creating a convetion and helped by the
>>>> suitable helper macros.
>>>>
>>>> For example tags can be things like: gem, kms, basic, guc, flip,
>>>> semaphore, bz12345678, gt4e, reset, etc..
>>>>
>>>> At runtime this would look something like this (abbreviated for
>>>> readability):
>>>>
>>>>     @ tests/gem_sync --list-subtests-with-tags
>>>>     ...
>>>>     forked-each TAGS="gem "
>>>>     forked-store-each TAGS="gem "
>>>>     basic-all TAGS="gem basic "
>>>>     basic-store-all TAGS="gem basic "
>>>>
>>>>     @ tests/gem_concurrent_blit --list-subtests-with-tags
>>>>     ...
>>>>     16MiB-swap-gpuX-render-write-read-bcs-bomb TAGS="gem stress "
>>>>     16MiB-swap-gpuX-render-write-read-rcs-bomb TAGS="gem stress "
>>>>     16MiB-swap-gpuX-render-gpu-read-after-write-bomb TAGS="gem stress "
>>>>
>>>>     @ tests/kms_flip --list-subtests-with-tags | grep basic
>>>>     basic-plain-flip TAGS="kms basic "
>>>>     basic-flip-vs-dpms TAGS="kms basic "
>>>>
>>>> Test runner could then enable usages like:
>>>>
>>>>     ./run-tests --include gem --exclude stress
>>>
>>> Minor comment, I like some basic boolean algebra here
>>> --include "gem AND smoketest NOT stress"
>>> :)
>>
>> That's what my hypothetical examples showed just with a different syntax!
>>
>>> I'd probably tag everything according to which ioctls they use. I feel my
>>> endgoal is still to list tests by their kernel functions (which can and
>>> should be automatically derived), and their hw function which is the
>>> open problem.
>>
>> If we want to do that automatically then tagging in this flavour
>> probably doesn't make sense and it should instead be an external database.
>>
>> If we go on the ioctl granularity it can probably mostly have one
>> version, and it should be static enough to be able to live in the tree,
>> but if we go more granular, like something derived from kcov, then
>> that's out of the window. Since it will be tied both to a platform and
>> kernel version.
>>
>> So I think tagging as proposed here is not appropriate for either, but
>> only if we wanted to tag on coarser functional areas and such.
> 
> Yeah, I think the same as well, that tags should be "smoke", "stress".
> (But one man's stress is another's minimal workload :(
> 
> But both of those are in essence a duration parameter, and I'd prefer if
> we expressed those as configurable parameters. At which point there is a
> meta level of test scripts to tag ;)
> 
> I feel that "gem", "kms" is better expressed in lower level terms, so
> what's left to tag? Clients? "display", "opencl", "media", "opengl" ?
> Even using client specs for features (e.g. EGL_IMG_context_priority)?

If we overcomplicate and try to change too much at once it is deemed to 
fail. GEM, KMS, etc, have this advantage of being very established.

So I was thinking these basic keywords and then something high level 
like reset, rps, stolen, and similar. So for instance we can remove the 
igt_skip_on_simulation, which can be time consuming to answer why it is 
there, and replace it with --exclude reset,... when running under the 
simulator.

True it is manual work to keep the tags up to date. Is it more work than 
the current scheme is TBD.

> Who and why would use those? From a selfish perspective, I want lcov
> with tests sorted in order of increasing "stress" (start with the
> precise X does Y test, finish with can it survive pathological client
> behaviour for 48 hours).

Who and why would use which ones? Tags in general, tags in the style the 
patch proposes, or tags in the flavour you described?

My initial idea was that it would make easy for developers to run an 
approximate sweep when touching a feature. But with good CI and trybot 
maybe no one would use tags and just rely on "outsourced" test runs? 
Could be.. but hey, you convinced me to re-send this. Or was it Joonas? :)

Regards,

Tvrtko
diff mbox series

Patch

diff --git a/lib/igt_core.c b/lib/igt_core.c
index 23bb858fd886..daae1a3e4b56 100644
--- a/lib/igt_core.c
+++ b/lib/igt_core.c
@@ -258,10 +258,12 @@  static unsigned int exit_handler_count;
 const char *igt_interactive_debug;
 
 /* subtests helpers */
-static bool list_subtests = false;
-static char *run_single_subtest = NULL;
+static bool list_subtests, list_subtests_tags;
+static char *run_single_subtest;
 static bool run_single_subtest_found = false;
-static const char *in_subtest = NULL;
+static const char *in_subtest;
+static char *subtest_tags;
+static bool subtest_tags_allocated;
 static struct timespec subtest_time;
 static clockid_t igt_clock = (clockid_t)-1;
 static bool in_fixture = false;
@@ -281,6 +283,7 @@  bool test_child;
 
 enum {
  OPT_LIST_SUBTESTS,
+ OPT_LIST_SUBTESTS_TAGS,
  OPT_RUN_SUBTEST,
  OPT_DESCRIPTION,
  OPT_DEBUG,
@@ -549,6 +552,7 @@  static void print_usage(const char *help_str, bool output_on_stderr)
 
 	fprintf(f, "Usage: %s [OPTIONS]\n", command_str);
 	fprintf(f, "  --list-subtests\n"
+		   "  --list-subtests-with-tags\n"
 		   "  --run-subtest <pattern>\n"
 		   "  --debug[=log-domain]\n"
 		   "  --interactive-debug[=domain]\n"
@@ -659,6 +663,7 @@  static int common_init(int *argc, char **argv,
 	int c, option_index = 0, i, x;
 	static struct option long_options[] = {
 		{"list-subtests", 0, 0, OPT_LIST_SUBTESTS},
+		{"list-subtests-with-tags", 0, 0, OPT_LIST_SUBTESTS_TAGS},
 		{"run-subtest", 1, 0, OPT_RUN_SUBTEST},
 		{"help-description", 0, 0, OPT_DESCRIPTION},
 		{"debug", optional_argument, 0, OPT_DEBUG},
@@ -753,6 +758,12 @@  static int common_init(int *argc, char **argv,
 			if (!run_single_subtest)
 				list_subtests = true;
 			break;
+		case OPT_LIST_SUBTESTS_TAGS:
+			if (!run_single_subtest) {
+				list_subtests = true;
+				list_subtests_tags = true;
+			}
+			break;
 		case OPT_RUN_SUBTEST:
 			if (!list_subtests)
 				run_single_subtest = strdup(optarg);
@@ -884,19 +895,100 @@  void igt_simple_init_parse_opts(int *argc, char **argv,
 		    extra_opt_handler, handler_data);
 }
 
+#define __IGT_TAGS_MAX (4096)
+
+static char * __get_tag_str(char *tags)
+{
+	if (tags)
+		return tags;
+
+	tags = malloc(__IGT_TAGS_MAX);
+	igt_assert(tags);
+
+	memset(tags, 0, __IGT_TAGS_MAX);
+
+	return tags;
+}
+
+static char * __add_tag(char *tags, unsigned int *pos, const char *tag)
+{
+	size_t left = __IGT_TAGS_MAX - *pos;
+	char _tag[128];
+	int ret;
+
+	if (!tag)
+		return tags;
+
+	tags = __get_tag_str(tags);
+
+	sprintf(_tag, "%s ", tag);
+	if (!strncmp(tags, _tag, strlen(_tag)))
+		return tags;
+
+	sprintf(_tag, " %s ", tag);
+	if (strstr(tags, _tag))
+		return tags;
+
+	ret = snprintf(tags + *pos, left, "%s ", tag);
+	igt_assert(ret > 0 && ret < left);
+
+	*pos += ret;
+
+	return tags;
+}
+
 /*
  * Note: Testcases which use these helpers MUST NOT output anything to stdout
  * outside of places protected by igt_run_subtest checks - the piglit
  * runner adds every line to the subtest list.
  */
-bool __igt_run_subtest(const char *subtest_name)
+bool __igt_run_subtest(const char *subtest_name, const char *tags)
 {
+	unsigned int tag_idx = 0;
+	char *_tags = NULL;
+	const char *tag;
 	int i;
 
 	assert(!in_subtest);
+	assert(!subtest_tags);
 	assert(!in_fixture);
 	assert(test_with_subtests);
 
+	if (!tags) {
+		if (!strncmp(command_str, "gem_", 4))
+			tag = "gem";
+		else if (!strncmp(command_str, "prime_", 4))
+			tag = "gem";
+		else if (!strncmp(command_str, "kms_", 4))
+			tag = "kms";
+		else if (!strncmp(command_str, "pm_", 3))
+			tag = "pm";
+		else if (!strncmp(command_str, "drv_", 3))
+			tag = "drv";
+		else if (!strncmp(command_str, "debugfs_", 3))
+			tag = "drv";
+		else if (!strncmp(command_str, "core_", 4))
+			tag = "drm";
+		else if (!strncmp(command_str, "drm_", 4))
+			tag = "drm";
+		else if (!strncmp(command_str, "sw_", 4))
+			tag = "external";
+		else
+			tag = NULL;
+
+		_tags = __add_tag(_tags, &tag_idx, tag);
+
+		if (!strncmp(subtest_name, "basic-", 6))
+			tag = "basic";
+		else
+			tag = NULL;
+
+		_tags = __add_tag(_tags, &tag_idx, tag);
+
+		tags = _tags;
+		subtest_tags_allocated = _tags;
+	}
+
 	/* check the subtest name only contains a-z, A-Z, 0-9, '-' and '_' */
 	for (i = 0; subtest_name[i] != '\0'; i++)
 		if (subtest_name[i] != '_' && subtest_name[i] != '-'
@@ -907,7 +999,10 @@  bool __igt_run_subtest(const char *subtest_name)
 		}
 
 	if (list_subtests) {
-		printf("%s\n", subtest_name);
+		if (list_subtests_tags && tags)
+			printf("%s TAGS=\"%s\"\n", subtest_name, tags);
+		else
+			printf("%s\n", subtest_name);
 		return false;
 	}
 
@@ -940,7 +1035,10 @@  bool __igt_run_subtest(const char *subtest_name)
 
 	_igt_log_buffer_reset();
 
+	subtest_tags = (char *)tags;
+
 	igt_gettime(&subtest_time);
+
 	return (in_subtest = subtest_name);
 }
 
@@ -997,7 +1095,12 @@  static void exit_subtest(const char *result)
 
 	igt_terminate_spin_batches();
 
-	in_subtest = NULL;
+	if (subtest_tags_allocated)
+		free(subtest_tags);
+
+	in_subtest = subtest_tags = NULL;
+	subtest_tags_allocated = false;
+
 	siglongjmp(igt_subtest_jmpbuf, 1);
 }
 
diff --git a/lib/igt_core.h b/lib/igt_core.h
index b80e1702a16f..300ff739d97b 100644
--- a/lib/igt_core.h
+++ b/lib/igt_core.h
@@ -145,7 +145,7 @@  int igt_subtest_init_parse_opts(int *argc, char **argv,
 #define igt_subtest_init(argc, argv) \
 	igt_subtest_init_parse_opts(&argc, argv, NULL, NULL, NULL, NULL, NULL);
 
-bool __igt_run_subtest(const char *subtest_name);
+bool __igt_run_subtest(const char *subtest_name, const char *subtest_tags);
 #define __igt_tokencat2(x, y) x ## y
 
 /**
@@ -158,6 +158,29 @@  bool __igt_run_subtest(const char *subtest_name);
  */
 #define igt_tokencat(x, y) __igt_tokencat2(x, y)
 
+#define igt_tagged_subtest(tags, name) \
+	for (; __igt_run_subtest((name), (tags)) && \
+	       (sigsetjmp(igt_subtest_jmpbuf, 1) == 0); \
+	     igt_success())
+
+#define igt_gem_subtest(tags, name) igt_tagged_subtest("gem "tags, name)
+
+#define __igt_tagged_subtest_f(tags, tmp, format...) \
+	for (char tmp [256]; \
+	     snprintf( tmp , sizeof( tmp ), format), \
+	     __igt_run_subtest(tmp, tags) && \
+	     (sigsetjmp(igt_subtest_jmpbuf, 1) == 0); \
+	     igt_success())
+
+#define igt_tagged_subtest_f(tags, f...) \
+	__igt_tagged_subtest_f(tags, igt_tokencat(__tmpchar, __LINE__), f)
+
+#define igt_gem_subtest_f(tags, f...) \
+	igt_tagged_subtest_f("gem "tags, f)
+
+#define igt_gem_stress_subtest_f(tags, f...) \
+	igt_tagged_subtest_f("gem stress "tags, f)
+
 /**
  * igt_subtest:
  * @name: name of the subtest
@@ -169,14 +192,14 @@  bool __igt_run_subtest(const char *subtest_name);
  *
  * This is a simpler version of igt_subtest_f()
  */
-#define igt_subtest(name) for (; __igt_run_subtest((name)) && \
+#define igt_subtest(name) for (; __igt_run_subtest((name), NULL) && \
 				   (sigsetjmp(igt_subtest_jmpbuf, 1) == 0); \
 				   igt_success())
 #define __igt_subtest_f(tmp, format...) \
 	for (char tmp [256]; \
 	     snprintf( tmp , sizeof( tmp ), \
 		      format), \
-	     __igt_run_subtest( tmp ) && \
+	     __igt_run_subtest(tmp, NULL) && \
 	     (sigsetjmp(igt_subtest_jmpbuf, 1) == 0); \
 	     igt_success())
 
diff --git a/tests/gem_concurrent_all.c b/tests/gem_concurrent_all.c
index 4ac08c1b1c90..c1d29421b8ba 100644
--- a/tests/gem_concurrent_all.c
+++ b/tests/gem_concurrent_all.c
@@ -1481,47 +1481,47 @@  run_mode(const char *prefix,
 			igt_subtest_group  {
 				igt_fixture p->require();
 
-				igt_subtest_f("%s-%s-%s-sanitycheck0%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-sanitycheck0%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers, do_basic0,
 							p->copy, h->hang);
 				}
 
-				igt_subtest_f("%s-%s-%s-sanitycheck1%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-sanitycheck1%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers, do_basic1,
 							p->copy, h->hang);
 				}
 
-				igt_subtest_f("%s-%s-%s-sanitycheckN%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-sanitycheckN%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers, do_basicN,
 							p->copy, h->hang);
 				}
 
 				/* try to overwrite the source values */
-				igt_subtest_f("%s-%s-%s-overwrite-source-one%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-overwrite-source-one%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_overwrite_source__one,
 							p->copy, h->hang);
 				}
 
-				igt_subtest_f("%s-%s-%s-overwrite-source%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-overwrite-source%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_overwrite_source,
 							p->copy, h->hang);
 				}
 
-				igt_subtest_f("%s-%s-%s-overwrite-source-read-bcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-overwrite-source-read-bcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_overwrite_source_read_bcs,
 							p->copy, h->hang);
 				}
 
-				igt_subtest_f("%s-%s-%s-overwrite-source-read-rcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-overwrite-source-read-rcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					igt_require(rendercopy);
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
@@ -1529,7 +1529,7 @@  run_mode(const char *prefix,
 							p->copy, h->hang);
 				}
 
-				igt_subtest_f("%s-%s-%s-overwrite-source-rev%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-overwrite-source-rev%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_overwrite_source__rev,
@@ -1537,21 +1537,21 @@  run_mode(const char *prefix,
 				}
 
 				/* try to intermix copies with GPU copies*/
-				igt_subtest_f("%s-%s-%s-intermix-rcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-intermix-rcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					igt_require(rendercopy);
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_intermix_rcs,
 							p->copy, h->hang);
 				}
-				igt_subtest_f("%s-%s-%s-intermix-bcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-intermix-bcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					igt_require(rendercopy);
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_intermix_bcs,
 							p->copy, h->hang);
 				}
-				igt_subtest_f("%s-%s-%s-intermix-both%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-intermix-both%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					igt_require(rendercopy);
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
@@ -1560,7 +1560,7 @@  run_mode(const char *prefix,
 				}
 
 				/* try to read the results before the copy completes */
-				igt_subtest_f("%s-%s-%s-early-read%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-early-read%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_early_read,
@@ -1568,13 +1568,13 @@  run_mode(const char *prefix,
 				}
 
 				/* concurrent reads */
-				igt_subtest_f("%s-%s-%s-read-read-bcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-read-read-bcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_read_read_bcs,
 							p->copy, h->hang);
 				}
-				igt_subtest_f("%s-%s-%s-read-read-rcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-read-read-rcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					igt_require(rendercopy);
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
@@ -1583,13 +1583,13 @@  run_mode(const char *prefix,
 				}
 
 				/* split copying between rings */
-				igt_subtest_f("%s-%s-%s-write-read-bcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-write-read-bcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_write_read_bcs,
 							p->copy, h->hang);
 				}
-				igt_subtest_f("%s-%s-%s-write-read-rcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-write-read-rcs%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					igt_require(rendercopy);
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
@@ -1598,7 +1598,7 @@  run_mode(const char *prefix,
 				}
 
 				/* and finally try to trick the kernel into loosing the pending write */
-				igt_subtest_f("%s-%s-%s-gpu-read-after-write%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
+				igt_gem_stress_subtest_f("", "%s-%s-%s-gpu-read-after-write%s%s", prefix, mode->name, p->prefix, suffix, h->suffix) {
 					buffers_create(&buffers);
 					run_wrap_func(&buffers,
 							do_gpu_read_after_write,