From patchwork Thu May 3 06:37:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wilson X-Patchwork-Id: 10377157 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id CA7166037D for ; Thu, 3 May 2018 06:38:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BBA60251F9 for ; Thu, 3 May 2018 06:38:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B001028E2B; Thu, 3 May 2018 06:38:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00, MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 155B4251F9 for ; Thu, 3 May 2018 06:38:56 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id F03946E69A; Thu, 3 May 2018 06:38:51 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from fireflyinternet.com (mail.fireflyinternet.com [109.228.58.192]) by gabe.freedesktop.org (Postfix) with ESMTPS id 2DD7D6E69A for ; Thu, 3 May 2018 06:38:50 +0000 (UTC) X-Default-Received-SPF: pass (skip=forwardok (res=PASS)) x-ip-name=78.156.65.138; Received: from haswell.alporthouse.com (unverified [78.156.65.138]) by fireflyinternet.com (Firefly Internet (M1)) with ESMTP id 11578248-1500050 for multiple; Thu, 03 May 2018 07:38:44 +0100 Received: by haswell.alporthouse.com (sSMTP sendmail emulation); Thu, 03 May 2018 07:38:42 +0100 From: Chris Wilson To: intel-gfx@lists.freedesktop.org Date: Thu, 3 May 2018 07:37:06 +0100 Message-Id: <20180503063757.22238-20-chris@chris-wilson.co.uk> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180503063757.22238-1-chris@chris-wilson.co.uk> References: <20180503063757.22238-1-chris@chris-wilson.co.uk> X-Originating-IP: 78.156.65.138 X-Country: code=GB country="United Kingdom" ip=78.156.65.138 Subject: [Intel-gfx] [PATCH 20/71] drm/i915/execlists: Force preemption via reset on timeout X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP Install a timer when trying to preempt on behalf of an important context such that if the active context does not honour the preemption request within the desired timeout, then we reset the GPU to allow the important context to run. v2: Install the timer on scheduling the preempt request; long before we even try to inject preemption into the ELSP, as the tasklet/injection may itself be blocked. v3: Update the guc to handle the preemption/tasklet timer. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_engine_cs.c | 4 + drivers/gpu/drm/i915/intel_guc_submission.c | 1 + drivers/gpu/drm/i915/intel_lrc.c | 90 +++++++++++++++++++-- drivers/gpu/drm/i915/intel_ringbuffer.h | 8 +- drivers/gpu/drm/i915/selftests/intel_lrc.c | 65 +++++++++++++++ 5 files changed, 158 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c index bddc57ccfa4a..61dcedddb799 100644 --- a/drivers/gpu/drm/i915/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/intel_engine_cs.c @@ -489,6 +489,9 @@ static void intel_engine_init_execlist(struct intel_engine_cs *engine) execlists->queue_priority = INT_MIN; execlists->queue = RB_ROOT; execlists->first = NULL; + + hrtimer_init(&execlists->preempt_timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); } /** @@ -1041,6 +1044,7 @@ void intel_engines_park(struct drm_i915_private *i915) for_each_engine(engine, i915, id) { /* Flush the residual irq tasklets first. */ + hrtimer_cancel(&engine->execlists.preempt_timer); intel_engine_disarm_breadcrumbs(engine); tasklet_kill(&engine->execlists.tasklet); diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c index 3aa1b9fe86bc..f240a4d2d625 100644 --- a/drivers/gpu/drm/i915/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/intel_guc_submission.c @@ -751,6 +751,7 @@ static void guc_dequeue(struct intel_engine_cs *engine) kmem_cache_free(engine->i915->priorities, p); } done: + execlists_clear_active(execlists, EXECLISTS_ACTIVE_PREEMPT_TIMEOUT); execlists->queue_priority = rb ? to_priolist(rb)->priority : INT_MIN; execlists->first = rb; if (submit) { diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 3e793badf0f2..0c962ed0150d 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -555,6 +555,53 @@ static void inject_preempt_context(struct intel_engine_cs *engine) execlists_set_active(execlists, EXECLISTS_ACTIVE_PREEMPT); } +static enum hrtimer_restart preempt_timeout(struct hrtimer *hrtimer) +{ + struct intel_engine_execlists *execlists = + container_of(hrtimer, typeof(*execlists), preempt_timer); + + GEM_TRACE("%s active=%x\n", + container_of(execlists, + struct intel_engine_cs, + execlists)->name, + execlists->active); + + if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT_TIMEOUT)) + return HRTIMER_NORESTART; + + if (GEM_SHOW_DEBUG()) { + struct intel_engine_cs *engine = + container_of(execlists, typeof(*engine), execlists); + struct drm_printer p = drm_debug_printer(__func__); + + intel_engine_dump(engine, &p, "%s\n", engine->name); + } + + queue_work(system_highpri_wq, &execlists->preempt_reset); + + return HRTIMER_NORESTART; +} + +static void preempt_reset(struct work_struct *work) +{ + struct intel_engine_execlists *execlists = + container_of(work, typeof(*execlists), preempt_reset); + struct intel_engine_cs *engine = + container_of(execlists, struct intel_engine_cs, execlists); + + GEM_TRACE("%s\n", engine->name); + + tasklet_disable(&execlists->tasklet); + + execlists->tasklet.func(execlists->tasklet.data); + + if (execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT_TIMEOUT)) + i915_handle_error(engine->i915, BIT(engine->id), 0, + "preemption time out on %s", engine->name); + + tasklet_enable(&execlists->tasklet); +} + static void complete_preempt_context(struct intel_engine_execlists *execlists) { GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT)); @@ -651,7 +698,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) * priorities of the ports haven't been switch. */ if (port_count(&port[1])) - goto unlock; + goto clear_preempt_timeout; /* * WaIdleLiteRestore:bdw,skl @@ -757,6 +804,8 @@ static void execlists_dequeue(struct intel_engine_cs *engine) /* We must always keep the beast fed if we have work piled up */ GEM_BUG_ON(execlists->first && !port_isset(execlists->port)); +clear_preempt_timeout: + execlists_clear_active(execlists, EXECLISTS_ACTIVE_PREEMPT_TIMEOUT); unlock: spin_unlock_irqrestore(&engine->timeline.lock, flags); @@ -1167,16 +1216,38 @@ static void queue_request(struct intel_engine_cs *engine, &lookup_priolist(engine, node, prio)->requests); } -static void __submit_queue(struct intel_engine_cs *engine, int prio) +static void __submit_queue(struct intel_engine_cs *engine, + int prio, unsigned int timeout) { - engine->execlists.queue_priority = prio; - tasklet_hi_schedule(&engine->execlists.tasklet); + struct intel_engine_execlists * const execlists = &engine->execlists; + int old = execlists->queue_priority; + + GEM_TRACE("%s prio=%d (previous=%d)\n", engine->name, prio, old); + + if (unlikely(execlists_is_active(execlists, + EXECLISTS_ACTIVE_PREEMPT_TIMEOUT))) + hrtimer_cancel(&execlists->preempt_timer); + + execlists->queue_priority = prio; + tasklet_hi_schedule(&execlists->tasklet); + + /* Set a timer to force preemption vs hostile userspace */ + if (timeout && __execlists_need_preempt(prio, old)) { + GEM_TRACE("%s preempt timeout=%uns\n", engine->name, timeout); + + execlists_set_active(execlists, + EXECLISTS_ACTIVE_PREEMPT_TIMEOUT); + hrtimer_start(&execlists->preempt_timer, + ns_to_ktime(timeout), + HRTIMER_MODE_REL); + } } -static void submit_queue(struct intel_engine_cs *engine, int prio) +static void submit_queue(struct intel_engine_cs *engine, + int prio, unsigned int timeout) { if (prio > engine->execlists.queue_priority) - __submit_queue(engine, prio); + __submit_queue(engine, prio, timeout); } static void execlists_submit_request(struct i915_request *request) @@ -1188,7 +1259,7 @@ static void execlists_submit_request(struct i915_request *request) spin_lock_irqsave(&engine->timeline.lock, flags); queue_request(engine, &request->sched, rq_prio(request)); - submit_queue(engine, rq_prio(request)); + submit_queue(engine, rq_prio(request), 0); GEM_BUG_ON(!engine->execlists.first); GEM_BUG_ON(list_empty(&request->sched.link)); @@ -1314,7 +1385,7 @@ static void execlists_schedule(struct i915_request *request, if (prio > engine->execlists.queue_priority && i915_sw_fence_done(&sched_to_request(node)->submit)) - __submit_queue(engine, prio); + __submit_queue(engine, prio, 0); } spin_unlock_irq(&engine->timeline.lock); @@ -2435,6 +2506,9 @@ logical_ring_setup(struct intel_engine_cs *engine) tasklet_init(&engine->execlists.tasklet, execlists_submission_tasklet, (unsigned long)engine); + INIT_WORK(&engine->execlists.preempt_reset, preempt_reset); + engine->execlists.preempt_timer.function = preempt_timeout; + logical_ring_default_vfuncs(engine); logical_ring_default_irqs(engine); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 567e7ec2cef7..8693d4d800ad 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -270,8 +270,9 @@ struct intel_engine_execlists { */ unsigned int active; #define EXECLISTS_ACTIVE_USER 0 -#define EXECLISTS_ACTIVE_PREEMPT 1 -#define EXECLISTS_ACTIVE_HWACK 2 +#define EXECLISTS_ACTIVE_HWACK 1 +#define EXECLISTS_ACTIVE_PREEMPT 2 +#define EXECLISTS_ACTIVE_PREEMPT_TIMEOUT 3 /** * @port_mask: number of execlist ports - 1 @@ -317,6 +318,9 @@ struct intel_engine_execlists { * @preempt_complete_status: expected CSB upon completing preemption */ u32 preempt_complete_status; + + struct hrtimer preempt_timer; + struct work_struct preempt_reset; }; #define INTEL_ENGINE_CS_MAX_NAME 8 diff --git a/drivers/gpu/drm/i915/selftests/intel_lrc.c b/drivers/gpu/drm/i915/selftests/intel_lrc.c index 20279547cb05..2aaab6072512 100644 --- a/drivers/gpu/drm/i915/selftests/intel_lrc.c +++ b/drivers/gpu/drm/i915/selftests/intel_lrc.c @@ -498,12 +498,77 @@ static int live_late_preempt(void *arg) goto err_ctx_lo; } +static void mark_preemption_hang(struct intel_engine_execlists *execlists) +{ + execlists_set_active(execlists, EXECLISTS_ACTIVE_PREEMPT); + execlists_set_active(execlists, EXECLISTS_ACTIVE_PREEMPT_TIMEOUT); +} + +static int live_preempt_timeout(void *arg) +{ + struct drm_i915_private *i915 = arg; + struct intel_engine_cs *engine; + struct i915_gem_context *ctx; + enum intel_engine_id id; + struct spinner spin; + int err = -ENOMEM; + + if (!HAS_LOGICAL_RING_PREEMPTION(i915)) + return 0; + + mutex_lock(&i915->drm.struct_mutex); + + if (spinner_init(&spin, i915)) + goto err_unlock; + + ctx = kernel_context(i915); + if (!ctx) + goto err_spin; + + for_each_engine(engine, i915, id) { + struct i915_request *rq; + + rq = spinner_create_request(&spin, ctx, engine, MI_NOOP); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err_ctx; + } + + i915_request_add(rq); + if (!wait_for_spinner(&spin, rq)) { + i915_gem_set_wedged(i915); + err = -EIO; + goto err_ctx; + } + + GEM_TRACE("%s triggering reset\n", engine->name); + mark_preemption_hang(&engine->execlists); + preempt_reset(&engine->execlists.preempt_reset); + + if (flush_test(i915, I915_WAIT_LOCKED)) { + err = -EIO; + goto err_ctx; + } + } + + err = 0; +err_ctx: + kernel_context_close(ctx); +err_spin: + spinner_fini(&spin); +err_unlock: + flush_test(i915, I915_WAIT_LOCKED); + mutex_unlock(&i915->drm.struct_mutex); + return err; +} + int intel_execlists_live_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { SUBTEST(live_sanitycheck), SUBTEST(live_preempt), SUBTEST(live_late_preempt), + SUBTEST(live_preempt_timeout), }; return i915_subtests(tests, i915); }