[Intel-gfx] [PATCH v2] drm/i915/selftests: Exercise context switching in parallel
Tvrtko Ursulin
tvrtko.ursulin at linux.intel.com
Mon Sep 30 13:47:26 UTC 2019
On 30/09/2019 12:31, 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.
>
> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
> Cc: Mika Kuoppala <mika.kuoppala at linux.intel.com>
> ---
> Keep struct_mutex the outer lock while it still exists
> ---
> .../drm/i915/gem/selftests/i915_gem_context.c | 203 ++++++++++++++++++
> 1 file changed, 203 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..8325c7329dc7 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,208 @@ 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;
> + int err;
> +
> + mutex_lock(&i915->drm.struct_mutex);
> + rq = i915_request_create(arg->ce[0]);
> + if (IS_ERR(rq)) {
> + mutex_unlock(&i915->drm.struct_mutex);
> + return PTR_ERR(rq);
> + }
> +
> + i915_request_add(rq);
> + mutex_unlock(&i915->drm.struct_mutex);
> +
> + mutex_lock(&i915->drm.struct_mutex);
unlock-lock! :) I guess in anticipation of removing it all.
> + rq = i915_request_create(arg->ce[1]);
> + 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;
> +
> + count = 0;
> + do {
> + struct i915_request *rq;
> +
> + mutex_lock(&i915->drm.struct_mutex);
> + rq = i915_request_create(arg->ce[0]);
> + if (IS_ERR(rq)) {
> + mutex_unlock(&i915->drm.struct_mutex);
> + return PTR_ERR(rq);
> + }
> +
> + i915_request_add(rq);
> + mutex_unlock(&i915->drm.struct_mutex);
> +
> + mutex_lock(&i915->drm.struct_mutex);
> + rq = i915_request_create(arg->ce[1]);
> + 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 i915_gem_context *ctx[2];
> + struct parallel_switch *data;
> + int (* const *fn)(void *arg);
> + struct drm_file *file;
> + int err = 0;
> + int n;
> +
> + /*
> + * 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);
> +
> + data = kcalloc(I915_NUM_ENGINES, sizeof(*data), GFP_KERNEL);
There is a little bit of mixing up I915_NUM_ENGINES and gem engines
(which contains the num_engines field) in this function.
I think it would be better to limit to one - so maybe get the count from
gem engines? It can't change during selftest so don't have to have them
locked for the whole time.
> + if (!data)
mock_file_free
> + return -ENOMEM;
> +
> + for (n = 0; n < ARRAY_SIZE(ctx); n++) {
> + struct i915_gem_engines_iter it;
> + struct intel_context *ce;
> +
> + mutex_lock(&i915->drm.struct_mutex);
> + ctx[n] = live_context(i915, file);
> + if (IS_ERR(ctx[n])) {
> + err = PTR_ERR(ctx[n]);
> + goto out_locked;
> + }
> +
> + for_each_gem_engine(ce,
> + i915_gem_context_lock_engines(ctx[n]), it) {
> + err = intel_context_pin(ce);
> + if (err) {
> + i915_gem_context_unlock_engines(ctx[n]);
> + goto out_locked;
> + }
> + data[ce->engine->legacy_idx].ce[n] = ce;
IMHO a bit confusing to use legacy_idx - makes it sound like there is
some significance to the legacy part so why not just use engine->id?
> + }
> + i915_gem_context_unlock_engines(ctx[n]);
> + 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 < I915_NUM_ENGINES; n++) {
> + if (data[n].ce[0] == NULL || data[n].ce[1] == 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 < I915_NUM_ENGINES; 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 < I915_NUM_ENGINES; n++) {
> + if (data[n].ce[0])
> + intel_context_unpin(data[n].ce[0]);
> + if (data[n].ce[1])
> + intel_context_unpin(data[n].ce[1]);
> + }
> + 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 +1883,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),
>
Regards,
Tvrtko
More information about the Intel-gfx
mailing list