drm/i915: Avoid unguarded reads from the request pointer

In commit 86aa7e760a ("drm/i915: Assert that the context-switch
completion matches our context") I added a read to the irq tasklet
handler that compared the on-chip status with that of our sw tracking,
using an unguarded read of the request pointer to get the context and
beyond. Whilst we hold a reference to the request, we do not hold
anything on the context and if we are unlucky it may be reaped from a
second thread retiring the request (since it may retire the request as
soon as the breadcrumb is complete, even before we finish processing the
context switch) as we try to read from the context pointer.

Avoid the racy read from underneath the request by storing the expected
result in the execlist_port[].

v2: Include commentary about port[].request being unprotected.

Fixes: 86aa7e760a ("drm/i915: Assert that the context-switch completion matches our context")
Reported-by: Mika Kuoppala <mika.kuoppala@intel.com>
Testcase: igt/gem_ctx_create
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Mika Kuoppala <mika.kuoppala@intel.com>
Reviewed-by: Mika Kuoppala <mika.kuoppala@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170206170502.30944-2-chris@chris-wilson.co.uk
This commit is contained in:
Chris Wilson 2017-02-06 17:05:02 +00:00
parent eca56a3511
commit 2ffe80aa44
2 changed files with 22 additions and 3 deletions

View File

@ -351,6 +351,7 @@ static void execlists_submit_ports(struct intel_engine_cs *engine)
execlists_context_status_change(port[0].request, execlists_context_status_change(port[0].request,
INTEL_CONTEXT_SCHEDULE_IN); INTEL_CONTEXT_SCHEDULE_IN);
desc[0] = execlists_update_context(port[0].request); desc[0] = execlists_update_context(port[0].request);
GEM_BUG_ONLY(port[0].context_id = upper_32_bits(desc[0]));
port[0].count++; port[0].count++;
if (port[1].request) { if (port[1].request) {
@ -358,6 +359,7 @@ static void execlists_submit_ports(struct intel_engine_cs *engine)
execlists_context_status_change(port[1].request, execlists_context_status_change(port[1].request,
INTEL_CONTEXT_SCHEDULE_IN); INTEL_CONTEXT_SCHEDULE_IN);
desc[1] = execlists_update_context(port[1].request); desc[1] = execlists_update_context(port[1].request);
GEM_BUG_ONLY(port[1].context_id = upper_32_bits(desc[1]));
port[1].count = 1; port[1].count = 1;
} else { } else {
desc[1] = 0; desc[1] = 0;
@ -560,13 +562,29 @@ static void intel_lrc_irq_handler(unsigned long data)
unsigned int idx = ++head % GEN8_CSB_ENTRIES; unsigned int idx = ++head % GEN8_CSB_ENTRIES;
unsigned int status = readl(buf + 2 * idx); unsigned int status = readl(buf + 2 * idx);
/* We are flying near dragons again.
*
* We hold a reference to the request in execlist_port[]
* but no more than that. We are operating in softirq
* context and so cannot hold any mutex or sleep. That
* prevents us stopping the requests we are processing
* in port[] from being retired simultaneously (the
* breadcrumb will be complete before we see the
* context-switch). As we only hold the reference to the
* request, any pointer chasing underneath the request
* is subject to a potential use-after-free. Thus we
* store all of the bookkeeping within port[] as
* required, and avoid using unguarded pointers beneath
* request itself. The same applies to the atomic
* status notifier.
*/
if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK)) if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK))
continue; continue;
/* Check the context/desc id for this event matches */ /* Check the context/desc id for this event matches */
GEM_BUG_ON(readl(buf + 2 * idx + 1) != GEM_BUG_ONLY_ON(readl(buf + 2 * idx + 1) !=
upper_32_bits(intel_lr_context_descriptor(port[0].request->ctx, port[0].context_id);
engine)));
GEM_BUG_ON(port[0].count == 0); GEM_BUG_ON(port[0].count == 0);
if (--port[0].count == 0) { if (--port[0].count == 0) {

View File

@ -381,6 +381,7 @@ struct intel_engine_cs {
struct execlist_port { struct execlist_port {
struct drm_i915_gem_request *request; struct drm_i915_gem_request *request;
unsigned int count; unsigned int count;
GEM_BUG_ONLY_DECLARE(u32 context_id);
} execlist_port[2]; } execlist_port[2];
struct rb_root execlist_queue; struct rb_root execlist_queue;
struct rb_node *execlist_first; struct rb_node *execlist_first;