Skip to content

Commit ef29440

Browse files
icklejlahtine-intel
authored andcommitted
drm/i915: Avoid using rq->engine after free during i915_fence_release
In order to be valid to dereference during the i915_fence_release, after retiring the fence and releasing its refererences, we assume that rq->engine can only be a real engine (that stay intact until the device is shutdown after all fences have been flushed). However, due to a quirk of preempt-to-busy, we may retire a request that still belongs to a virtual engine and so eventually free it with rq->engine being invalid. To avoid dereferencing that invalid engine, we look at the execution_mask which if it indicates it may be executed on more than one engine, we know it originated on a virtual engine and may still be on one. Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/1906 Fixes: 43acd65 ("drm/i915: Keep a per-engine request pool") Signed-off-by: Chris Wilson <[email protected]> Cc: Tvrtko Ursulin <[email protected]> Reviewed-by: Tvrtko Ursulin <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected] (cherry picked from commit 32a4605) Signed-off-by: Joonas Lahtinen <[email protected]>
1 parent 9ef36fc commit ef29440

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

drivers/gpu/drm/i915/i915_request.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,39 @@ static void i915_fence_release(struct dma_fence *fence)
121121
i915_sw_fence_fini(&rq->submit);
122122
i915_sw_fence_fini(&rq->semaphore);
123123

124-
/* Keep one request on each engine for reserved use under mempressure */
125-
if (!cmpxchg(&rq->engine->request_pool, NULL, rq))
124+
/*
125+
* Keep one request on each engine for reserved use under mempressure
126+
*
127+
* We do not hold a reference to the engine here and so have to be
128+
* very careful in what rq->engine we poke. The virtual engine is
129+
* referenced via the rq->context and we released that ref during
130+
* i915_request_retire(), ergo we must not dereference a virtual
131+
* engine here. Not that we would want to, as the only consumer of
132+
* the reserved engine->request_pool is the power management parking,
133+
* which must-not-fail, and that is only run on the physical engines.
134+
*
135+
* Since the request must have been executed to be have completed,
136+
* we know that it will have been processed by the HW and will
137+
* not be unsubmitted again, so rq->engine and rq->execution_mask
138+
* at this point is stable. rq->execution_mask will be a single
139+
* bit if the last and _only_ engine it could execution on was a
140+
* physical engine, if it's multiple bits then it started on and
141+
* could still be on a virtual engine. Thus if the mask is not a
142+
* power-of-two we assume that rq->engine may still be a virtual
143+
* engine and so a dangling invalid pointer that we cannot dereference
144+
*
145+
* For example, consider the flow of a bonded request through a virtual
146+
* engine. The request is created with a wide engine mask (all engines
147+
* that we might execute on). On processing the bond, the request mask
148+
* is reduced to one or more engines. If the request is subsequently
149+
* bound to a single engine, it will then be constrained to only
150+
* execute on that engine and never returned to the virtual engine
151+
* after timeslicing away, see __unwind_incomplete_requests(). Thus we
152+
* know that if the rq->execution_mask is a single bit, rq->engine
153+
* can be a physical engine with the exact corresponding mask.
154+
*/
155+
if (is_power_of_2(rq->execution_mask) &&
156+
!cmpxchg(&rq->engine->request_pool, NULL, rq))
126157
return;
127158

128159
kmem_cache_free(global.slab_requests, rq);

0 commit comments

Comments
 (0)