[Intel-gfx] [PATCH v4] drm/i915/selftests: Exercise context switching in parallel

Tvrtko Ursulin tvrtko.ursulin at linux.intel.com
Mon Sep 30 16:18:26 UTC 2019


On 30/09/2019 15:49, Chris Wilson wrote:
> We currently test context switching on each engine as a basic stress
> test (just verifying that nothing explodes if we execute 2 requests from
> different contexts sequentially). What we have not tested is what
> happens if we try and do so on all available engines simultaneously,
> putting our SW and the HW under the maximal stress.
> 
> v2: Clone the set of engines from the first context into the secondary
> contexts.
> 
> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
> Cc: Mika Kuoppala <mika.kuoppala at linux.intel.com>
> Cc: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> ---
>   .../drm/i915/gem/selftests/i915_gem_context.c | 225 ++++++++++++++++++
>   1 file changed, 225 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> index dc25bcc3e372..81a83c34404c 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> @@ -156,6 +156,230 @@ static int live_nop_switch(void *arg)
>   	return err;
>   }
>   
> +struct parallel_switch {
> +	struct task_struct *tsk;
> +	struct intel_context *ce[2];
> +};
> +
> +static int __live_parallel_switch1(void *data)
> +{
> +	struct parallel_switch *arg = data;
> +	struct drm_i915_private *i915 = arg->ce[0]->engine->i915;
> +	IGT_TIMEOUT(end_time);
> +	unsigned long count;
> +
> +	count = 0;
> +	do {
> +		struct i915_request *rq = NULL;
> +		int err, n;
> +
> +		for (n = 0; n < ARRAY_SIZE(arg->ce); n++) {
> +			i915_request_put(rq);
> +
> +			mutex_lock(&i915->drm.struct_mutex);
> +			rq = i915_request_create(arg->ce[n]);
> +			if (IS_ERR(rq)) {
> +				mutex_unlock(&i915->drm.struct_mutex);
> +				return PTR_ERR(rq);
> +			}
> +
> +			i915_request_get(rq);
> +			i915_request_add(rq);
> +			mutex_unlock(&i915->drm.struct_mutex);
> +		}
> +
> +		err = 0;
> +		if (i915_request_wait(rq, 0, HZ / 5) < 0)
> +			err = -ETIME;
> +		i915_request_put(rq);
> +		if (err)
> +			return err;
> +
> +		count++;
> +	} while (!__igt_timeout(end_time, NULL));
> +
> +	pr_info("%s: %lu switches (sync)\n", arg->ce[0]->engine->name, count);
> +	return 0;
> +}
> +
> +static int __live_parallel_switchN(void *data)
> +{
> +	struct parallel_switch *arg = data;
> +	struct drm_i915_private *i915 = arg->ce[0]->engine->i915;
> +	IGT_TIMEOUT(end_time);
> +	unsigned long count;
> +	int n;
> +
> +	count = 0;
> +	do {
> +		for (n = 0; n < ARRAY_SIZE(arg->ce); n++) {
> +			struct i915_request *rq;
> +
> +			mutex_lock(&i915->drm.struct_mutex);
> +			rq = i915_request_create(arg->ce[n]);
> +			if (IS_ERR(rq)) {
> +				mutex_unlock(&i915->drm.struct_mutex);
> +				return PTR_ERR(rq);
> +			}
> +
> +			i915_request_add(rq);
> +			mutex_unlock(&i915->drm.struct_mutex);
> +		}
> +
> +		count++;
> +	} while (!__igt_timeout(end_time, NULL));
> +
> +	pr_info("%s: %lu switches (many)\n", arg->ce[0]->engine->name, count);
> +	return 0;
> +}
> +
> +static int live_parallel_switch(void *arg)
> +{
> +	struct drm_i915_private *i915 = arg;
> +	static int (* const func[])(void *arg) = {
> +		__live_parallel_switch1,
> +		__live_parallel_switchN,
> +		NULL,
> +	};
> +	struct parallel_switch *data = NULL;
> +	struct i915_gem_engines *engines;
> +	struct i915_gem_engines_iter it;
> +	int (* const *fn)(void *arg);
> +	struct i915_gem_context *ctx;
> +	struct intel_context *ce;
> +	struct drm_file *file;
> +	int n, m, count;
> +	int err = 0;
> +
> +	/*
> +	 * Check we can process switches on all engines simultaneously.
> +	 */
> +
> +	if (!DRIVER_CAPS(i915)->has_logical_contexts)
> +		return 0;
> +
> +	file = mock_file(i915);
> +	if (IS_ERR(file))
> +		return PTR_ERR(file);
> +
> +	mutex_lock(&i915->drm.struct_mutex);
> +
> +	ctx = live_context(i915, file);
> +	if (IS_ERR(ctx)) {
> +		err = PTR_ERR(ctx);
> +		goto out_locked;
> +	}
> +
> +	engines = i915_gem_context_lock_engines(ctx);
> +	count = engines->num_engines;
> +
> +	data = kcalloc(count, sizeof(*data), GFP_KERNEL);
> +	if (!data) {
> +		i915_gem_context_unlock_engines(ctx);
> +		err = -ENOMEM;
> +		goto out_locked;
> +	}
> +
> +	m = 0; /* Use the first context as our template for the engines */
> +	for_each_gem_engine(ce, engines, it) {
> +		err = intel_context_pin(ce);
> +		if (err) {
> +			i915_gem_context_unlock_engines(ctx);
> +			goto out_locked;
> +		}
> +		data[m++].ce[0] = intel_context_get(ce);
> +	}
> +	i915_gem_context_unlock_engines(ctx);
> +
> +	/* Clone the same set of engines in the other contexts */
> +	for (n = 1; n < ARRAY_SIZE(data->ce); n++) {
> +		ctx = live_context(i915, file);
> +		if (IS_ERR(ctx)) {
> +			err = PTR_ERR(ctx);
> +			goto out_locked;
> +		}
> +
> +		for (m = 0; m < count; m++) {
> +			if (!data[m].ce[0])
> +				continue;
> +
> +			ce = intel_context_create(ctx, data[m].ce[0]->engine);
> +			if (IS_ERR(ce))
> +				goto out_locked;
> +
> +			err = intel_context_pin(ce);
> +			if (err) {
> +				intel_context_put(ce);
> +				goto out_locked;
> +			}
> +
> +			data[m].ce[n] = ce;
> +		}
> +	}
> +
> +	mutex_unlock(&i915->drm.struct_mutex);
> +
> +	for (fn = func; !err && *fn; fn++) {
> +		struct igt_live_test t;
> +		int n;
> +
> +		mutex_lock(&i915->drm.struct_mutex);
> +		err = igt_live_test_begin(&t, i915, __func__, "");
> +		mutex_unlock(&i915->drm.struct_mutex);
> +		if (err)
> +			break;
> +
> +		for (n = 0; n < count; n++) {
> +			if (data[n].ce[0] == NULL)
> +				continue;
> +
> +			data[n].tsk = kthread_run(*fn, &data[n],
> +						  "igt/parallel:%s",
> +						  data[n].ce[0]->engine->name);
> +			if (IS_ERR(data[n].tsk)) {
> +				err = PTR_ERR(data[n].tsk);
> +				break;
> +			}
> +			get_task_struct(data[n].tsk);
> +		}
> +
> +		for (n = 0; n < count; n++) {
> +			int status;
> +
> +			if (IS_ERR_OR_NULL(data[n].tsk))
> +				continue;
> +
> +			status = kthread_stop(data[n].tsk);
> +			if (status && !err)
> +				err = status;
> +
> +			put_task_struct(data[n].tsk);
> +			data[n].tsk = NULL;
> +		}
> +
> +		mutex_lock(&i915->drm.struct_mutex);
> +		if (igt_live_test_end(&t))
> +			err = -EIO;
> +		mutex_unlock(&i915->drm.struct_mutex);
> +	}
> +
> +	mutex_lock(&i915->drm.struct_mutex);
> +out_locked:
> +	for (n = 0; n < count; n++) {
> +		for (m = 0; m < ARRAY_SIZE(data->ce); m++) {
> +			if (!data[n].ce[m])
> +				continue;
> +
> +			intel_context_unpin(data[n].ce[m]);
> +			intel_context_put(data[n].ce[m]);
> +		}
> +	}
> +	mutex_unlock(&i915->drm.struct_mutex);
> +	kfree(data);
> +	mock_file_free(i915, file);
> +	return err;
> +}
> +
>   static unsigned long real_page_count(struct drm_i915_gem_object *obj)
>   {
>   	return huge_gem_object_phys_size(obj) >> PAGE_SHIFT;
> @@ -1681,6 +1905,7 @@ int i915_gem_context_live_selftests(struct drm_i915_private *i915)
>   {
>   	static const struct i915_subtest tests[] = {
>   		SUBTEST(live_nop_switch),
> +		SUBTEST(live_parallel_switch),
>   		SUBTEST(igt_ctx_exec),
>   		SUBTEST(igt_ctx_readonly),
>   		SUBTEST(igt_ctx_sseu),
> 

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin at intel.com>

Regards,

Tvrtko


More information about the Intel-gfx mailing list