Skip to content

Commit 1a52fae

Browse files
mbrost05johnharr-intel
authored andcommitted
drm/i915/guc: Take GT PM ref when deregistering context
Taking a PM reference to prevent intel_gt_wait_for_idle from short circuiting while a deregister context H2G is in flight. To do this must issue the deregister H2G from a worker as context can be destroyed from an atomic context and taking GT PM ref blows up. Previously we took a runtime PM from this atomic context which worked but will stop working once runtime pm autosuspend in enabled. So this patch is two fold, stop intel_gt_wait_for_idle from short circuting and fix runtime pm autosuspend. v2: (John Harrison) - Split structure changes out in different patch (Tvrtko) - Don't drop lock in deregister_destroyed_contexts v3: (John Harrison) - Flush destroyed contexts before destroying context reg pool Signed-off-by: Matthew Brost <[email protected]> Reviewed-by: John Harrison <[email protected]> Signed-off-by: John Harrison <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 0ea92ac commit 1a52fae

File tree

6 files changed

+121
-54
lines changed

6 files changed

+121
-54
lines changed

drivers/gpu/drm/i915/gt/intel_context.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ intel_context_init(struct intel_context *ce, struct intel_engine_cs *engine)
399399
ce->guc_id.id = GUC_INVALID_LRC_ID;
400400
INIT_LIST_HEAD(&ce->guc_id.link);
401401

402+
INIT_LIST_HEAD(&ce->destroyed_link);
403+
402404
/*
403405
* Initialize fence to be complete as this is expected to be complete
404406
* unless there is a pending schedule disable outstanding.

drivers/gpu/drm/i915/gt/intel_context_types.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,13 @@ struct intel_context {
213213
struct list_head link;
214214
} guc_id;
215215

216+
/**
217+
* @destroyed_link: link in guc->submission_state.destroyed_contexts, in
218+
* list when context is pending to be destroyed (deregistered with the
219+
* GuC), protected by guc->submission_state.lock
220+
*/
221+
struct list_head destroyed_link;
222+
216223
#ifdef CONFIG_DRM_I915_SELFTEST
217224
/**
218225
* @drop_schedule_enable: Force drop of schedule enable G2H for selftest

drivers/gpu/drm/i915/gt/intel_engine_pm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ intel_engine_pm_is_awake(const struct intel_engine_cs *engine)
1616
return intel_wakeref_is_active(&engine->wakeref);
1717
}
1818

19+
static inline void __intel_engine_pm_get(struct intel_engine_cs *engine)
20+
{
21+
__intel_wakeref_get(&engine->wakeref);
22+
}
23+
1924
static inline void intel_engine_pm_get(struct intel_engine_cs *engine)
2025
{
2126
intel_wakeref_get(&engine->wakeref);

drivers/gpu/drm/i915/gt/intel_gt_pm.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ static inline void intel_gt_pm_put_async(struct intel_gt *gt)
4141
intel_wakeref_put_async(&gt->wakeref);
4242
}
4343

44+
#define with_intel_gt_pm(gt, tmp) \
45+
for (tmp = 1, intel_gt_pm_get(gt); tmp; \
46+
intel_gt_pm_put(gt), tmp = 0)
47+
4448
static inline int intel_gt_pm_wait_for_idle(struct intel_gt *gt)
4549
{
4650
return intel_wakeref_wait_for_idle(&gt->wakeref);

drivers/gpu/drm/i915/gt/uc/intel_guc.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ struct intel_guc {
9090
* refs
9191
*/
9292
struct list_head guc_id_list;
93+
/**
94+
* @destroyed_contexts: list of contexts waiting to be destroyed
95+
* (deregistered with the GuC)
96+
*/
97+
struct list_head destroyed_contexts;
98+
/**
99+
* @destroyed_worker: worker to deregister contexts, need as we
100+
* need to take a GT PM reference and can't from destroy
101+
* function as it might be in an atomic context (no sleeping)
102+
*/
103+
struct work_struct destroyed_worker;
93104
} submission_state;
94105

95106
/**

drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c

Lines changed: 92 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@
9090
* used for all of GuC submission but that could change in the future.
9191
*
9292
* guc->submission_state.lock
93-
* Protects guc_id allocation for the given GuC, i.e. only one context can be
94-
* doing guc_id allocation operations at a time for each GuC in the system.
93+
* Global lock for GuC submission state. Protects guc_ids and destroyed contexts
94+
* list.
9595
*
9696
* ce->guc_state.lock
9797
* Protects everything under ce->guc_state. Ensures that a context is in the
@@ -719,6 +719,7 @@ static void scrub_guc_desc_for_outstanding_g2h(struct intel_guc *guc)
719719
if (deregister)
720720
guc_signal_context_fence(ce);
721721
if (destroyed) {
722+
intel_gt_pm_put_async(guc_to_gt(guc));
722723
release_guc_id(guc, ce);
723724
__guc_context_destroy(ce);
724725
}
@@ -797,6 +798,8 @@ static void guc_flush_submissions(struct intel_guc *guc)
797798
spin_unlock_irqrestore(&sched_engine->lock, flags);
798799
}
799800

801+
static void guc_flush_destroyed_contexts(struct intel_guc *guc);
802+
800803
void intel_guc_submission_reset_prepare(struct intel_guc *guc)
801804
{
802805
int i;
@@ -815,6 +818,7 @@ void intel_guc_submission_reset_prepare(struct intel_guc *guc)
815818
spin_unlock_irq(&guc_to_gt(guc)->irq_lock);
816819

817820
guc_flush_submissions(guc);
821+
guc_flush_destroyed_contexts(guc);
818822

819823
/*
820824
* Handle any outstanding G2Hs before reset. Call IRQ handler directly
@@ -1126,6 +1130,8 @@ void intel_guc_submission_reset_finish(struct intel_guc *guc)
11261130
intel_gt_unpark_heartbeats(guc_to_gt(guc));
11271131
}
11281132

1133+
static void destroyed_worker_func(struct work_struct *w);
1134+
11291135
/*
11301136
* Set up the memory resources to be shared with the GuC (via the GGTT)
11311137
* at firmware loading time.
@@ -1151,6 +1157,9 @@ int intel_guc_submission_init(struct intel_guc *guc)
11511157
spin_lock_init(&guc->submission_state.lock);
11521158
INIT_LIST_HEAD(&guc->submission_state.guc_id_list);
11531159
ida_init(&guc->submission_state.guc_ids);
1160+
INIT_LIST_HEAD(&guc->submission_state.destroyed_contexts);
1161+
INIT_WORK(&guc->submission_state.destroyed_worker,
1162+
destroyed_worker_func);
11541163

11551164
return 0;
11561165
}
@@ -1160,6 +1169,7 @@ void intel_guc_submission_fini(struct intel_guc *guc)
11601169
if (!guc->lrc_desc_pool)
11611170
return;
11621171

1172+
guc_flush_destroyed_contexts(guc);
11631173
guc_lrc_desc_pool_destroy(guc);
11641174
i915_sched_engine_put(guc->sched_engine);
11651175
}
@@ -1859,11 +1869,30 @@ static void guc_context_sched_disable(struct intel_context *ce)
18591869
static inline void guc_lrc_desc_unpin(struct intel_context *ce)
18601870
{
18611871
struct intel_guc *guc = ce_to_guc(ce);
1872+
struct intel_gt *gt = guc_to_gt(guc);
1873+
unsigned long flags;
1874+
bool disabled;
18621875

1876+
GEM_BUG_ON(!intel_gt_pm_is_awake(gt));
18631877
GEM_BUG_ON(!lrc_desc_registered(guc, ce->guc_id.id));
18641878
GEM_BUG_ON(ce != __get_context(guc, ce->guc_id.id));
18651879
GEM_BUG_ON(context_enabled(ce));
18661880

1881+
/* Seal race with Reset */
1882+
spin_lock_irqsave(&ce->guc_state.lock, flags);
1883+
disabled = submission_disabled(guc);
1884+
if (likely(!disabled)) {
1885+
__intel_gt_pm_get(gt);
1886+
set_context_destroyed(ce);
1887+
clr_context_registered(ce);
1888+
}
1889+
spin_unlock_irqrestore(&ce->guc_state.lock, flags);
1890+
if (unlikely(disabled)) {
1891+
release_guc_id(guc, ce);
1892+
__guc_context_destroy(ce);
1893+
return;
1894+
}
1895+
18671896
deregister_context(ce, ce->guc_id.id);
18681897
}
18691898

@@ -1891,78 +1920,86 @@ static void __guc_context_destroy(struct intel_context *ce)
18911920
}
18921921
}
18931922

1923+
static void guc_flush_destroyed_contexts(struct intel_guc *guc)
1924+
{
1925+
struct intel_context *ce, *cn;
1926+
unsigned long flags;
1927+
1928+
GEM_BUG_ON(!submission_disabled(guc) &&
1929+
guc_submission_initialized(guc));
1930+
1931+
spin_lock_irqsave(&guc->submission_state.lock, flags);
1932+
list_for_each_entry_safe(ce, cn,
1933+
&guc->submission_state.destroyed_contexts,
1934+
destroyed_link) {
1935+
list_del_init(&ce->destroyed_link);
1936+
__release_guc_id(guc, ce);
1937+
__guc_context_destroy(ce);
1938+
}
1939+
spin_unlock_irqrestore(&guc->submission_state.lock, flags);
1940+
}
1941+
1942+
static void deregister_destroyed_contexts(struct intel_guc *guc)
1943+
{
1944+
struct intel_context *ce, *cn;
1945+
unsigned long flags;
1946+
1947+
spin_lock_irqsave(&guc->submission_state.lock, flags);
1948+
list_for_each_entry_safe(ce, cn,
1949+
&guc->submission_state.destroyed_contexts,
1950+
destroyed_link) {
1951+
list_del_init(&ce->destroyed_link);
1952+
guc_lrc_desc_unpin(ce);
1953+
}
1954+
spin_unlock_irqrestore(&guc->submission_state.lock, flags);
1955+
}
1956+
1957+
static void destroyed_worker_func(struct work_struct *w)
1958+
{
1959+
struct intel_guc *guc = container_of(w, struct intel_guc,
1960+
submission_state.destroyed_worker);
1961+
struct intel_gt *gt = guc_to_gt(guc);
1962+
int tmp;
1963+
1964+
with_intel_gt_pm(gt, tmp)
1965+
deregister_destroyed_contexts(guc);
1966+
}
1967+
18941968
static void guc_context_destroy(struct kref *kref)
18951969
{
18961970
struct intel_context *ce = container_of(kref, typeof(*ce), ref);
1897-
struct intel_runtime_pm *runtime_pm = ce->engine->uncore->rpm;
18981971
struct intel_guc *guc = ce_to_guc(ce);
1899-
intel_wakeref_t wakeref;
19001972
unsigned long flags;
1901-
bool disabled;
1973+
bool destroy;
19021974

19031975
/*
19041976
* If the guc_id is invalid this context has been stolen and we can free
19051977
* it immediately. Also can be freed immediately if the context is not
19061978
* registered with the GuC or the GuC is in the middle of a reset.
19071979
*/
1908-
if (context_guc_id_invalid(ce)) {
1909-
__guc_context_destroy(ce);
1910-
return;
1911-
} else if (submission_disabled(guc) ||
1912-
!lrc_desc_registered(guc, ce->guc_id.id)) {
1913-
release_guc_id(guc, ce);
1914-
__guc_context_destroy(ce);
1915-
return;
1916-
}
1917-
1918-
/*
1919-
* We have to acquire the context spinlock and check guc_id again, if it
1920-
* is valid it hasn't been stolen and needs to be deregistered. We
1921-
* delete this context from the list of unpinned guc_id available to
1922-
* steal to seal a race with guc_lrc_desc_pin(). When the G2H CTB
1923-
* returns indicating this context has been deregistered the guc_id is
1924-
* returned to the pool of available guc_id.
1925-
*/
19261980
spin_lock_irqsave(&guc->submission_state.lock, flags);
1927-
if (context_guc_id_invalid(ce)) {
1928-
spin_unlock_irqrestore(&guc->submission_state.lock, flags);
1929-
__guc_context_destroy(ce);
1930-
return;
1981+
destroy = submission_disabled(guc) || context_guc_id_invalid(ce) ||
1982+
!lrc_desc_registered(guc, ce->guc_id.id);
1983+
if (likely(!destroy)) {
1984+
if (!list_empty(&ce->guc_id.link))
1985+
list_del_init(&ce->guc_id.link);
1986+
list_add_tail(&ce->destroyed_link,
1987+
&guc->submission_state.destroyed_contexts);
1988+
} else {
1989+
__release_guc_id(guc, ce);
19311990
}
1932-
1933-
if (!list_empty(&ce->guc_id.link))
1934-
list_del_init(&ce->guc_id.link);
19351991
spin_unlock_irqrestore(&guc->submission_state.lock, flags);
1936-
1937-
/* Seal race with Reset */
1938-
spin_lock_irqsave(&ce->guc_state.lock, flags);
1939-
disabled = submission_disabled(guc);
1940-
if (likely(!disabled)) {
1941-
set_context_destroyed(ce);
1942-
clr_context_registered(ce);
1943-
}
1944-
spin_unlock_irqrestore(&ce->guc_state.lock, flags);
1945-
if (unlikely(disabled)) {
1946-
release_guc_id(guc, ce);
1992+
if (unlikely(destroy)) {
19471993
__guc_context_destroy(ce);
19481994
return;
19491995
}
19501996

19511997
/*
1952-
* We defer GuC context deregistration until the context is destroyed
1953-
* in order to save on CTBs. With this optimization ideally we only need
1954-
* 1 CTB to register the context during the first pin and 1 CTB to
1955-
* deregister the context when the context is destroyed. Without this
1956-
* optimization, a CTB would be needed every pin & unpin.
1957-
*
1958-
* XXX: Need to acqiure the runtime wakeref as this can be triggered
1959-
* from context_free_worker when runtime wakeref is not held.
1960-
* guc_lrc_desc_unpin requires the runtime as a GuC register is written
1961-
* in H2G CTB to deregister the context. A future patch may defer this
1962-
* H2G CTB if the runtime wakeref is zero.
1998+
* We use a worker to issue the H2G to deregister the context as we can
1999+
* take the GT PM for the first time which isn't allowed from an atomic
2000+
* context.
19632001
*/
1964-
with_intel_runtime_pm(runtime_pm, wakeref)
1965-
guc_lrc_desc_unpin(ce);
2002+
queue_work(system_unbound_wq, &guc->submission_state.destroyed_worker);
19662003
}
19672004

19682005
static int guc_context_alloc(struct intel_context *ce)
@@ -2798,6 +2835,7 @@ int intel_guc_deregister_done_process_msg(struct intel_guc *guc,
27982835
intel_context_put(ce);
27992836
} else if (context_destroyed(ce)) {
28002837
/* Context has been destroyed */
2838+
intel_gt_pm_put_async(guc_to_gt(guc));
28012839
release_guc_id(guc, ce);
28022840
__guc_context_destroy(ce);
28032841
}

0 commit comments

Comments
 (0)