Skip to content

Commit

Permalink
drm/i915/guc: Implement GuC priority management
Browse files Browse the repository at this point in the history
Implement a simple static mapping algorithm of the i915 priority levels
(int, -1k to 1k exposed to user) to the 4 GuC levels. Mapping is as
follows:

i915 level < 0          -> GuC low level     (3)
i915 level == 0         -> GuC normal level  (2)
i915 level < INT_MAX    -> GuC high level    (1)
i915 level == INT_MAX   -> GuC highest level (0)

We believe this mapping should cover the UMD use cases (3 distinct user
levels + 1 kernel level).

In addition to static mapping, a simple counter system is attached to
each context tracking the number of requests inflight on the context at
each level. This is needed as the GuC levels are per context while in
the i915 levels are per request.

v2:
 (Daniele)
  - Add BUILD_BUG_ON to enforce ordering of priority levels
  - Add missing lockdep to guc_prio_fini
  - Check for return before setting context registered flag
  - Map DISPLAY priority or higher to highest guc prio
  - Update comment for guc_prio

Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Reviewed-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Signed-off-by: John Harrison <John.C.Harrison@Intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210727002348.97202-33-matthew.brost@intel.com
  • Loading branch information
Matthew Brost authored and John Harrison committed Jul 28, 2021
1 parent 3a7b726 commit ee242ca
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 5 deletions.
3 changes: 3 additions & 0 deletions drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ static void signal_irq_work(struct irq_work *work)
llist_entry(signal, typeof(*rq), signal_node);
struct list_head cb_list;

if (rq->engine->sched_engine->retire_inflight_request_prio)
rq->engine->sched_engine->retire_inflight_request_prio(rq);

spin_lock(&rq->lock);
list_replace(&rq->fence.cb_list, &cb_list);
__dma_fence_signal__timestamp(&rq->fence, timestamp);
Expand Down
9 changes: 8 additions & 1 deletion drivers/gpu/drm/i915/gt/intel_context_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
#include "intel_engine_types.h"
#include "intel_sseu.h"

#define CONTEXT_REDZONE POISON_INUSE
#include "uc/intel_guc_fwif.h"

#define CONTEXT_REDZONE POISON_INUSE
DECLARE_EWMA(runtime, 3, 8);

struct i915_gem_context;
Expand Down Expand Up @@ -191,6 +192,12 @@ struct intel_context {

/* GuC context blocked fence */
struct i915_sw_fence guc_blocked;

/*
* GuC priority management
*/
u8 guc_prio;
u32 guc_prio_count[GUC_CLIENT_PRIORITY_NUM];
};

#endif /* __INTEL_CONTEXT_TYPES__ */
4 changes: 4 additions & 0 deletions drivers/gpu/drm/i915/gt/intel_engine_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "intel_engine.h"
#include "intel_engine_user.h"
#include "intel_gt.h"
#include "uc/intel_guc_submission.h"

struct intel_engine_cs *
intel_engine_lookup_user(struct drm_i915_private *i915, u8 class, u8 instance)
Expand Down Expand Up @@ -115,6 +116,9 @@ static void set_scheduler_caps(struct drm_i915_private *i915)
disabled |= (I915_SCHEDULER_CAP_ENABLED |
I915_SCHEDULER_CAP_PRIORITY);

if (intel_uc_uses_guc_submission(&i915->gt.uc))
enabled |= I915_SCHEDULER_CAP_STATIC_PRIORITY_MAP;

for (i = 0; i < ARRAY_SIZE(map); i++) {
if (engine->flags & BIT(map[i].engine))
enabled |= BIT(map[i].sched);
Expand Down
212 changes: 210 additions & 2 deletions drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ guc_create_virtual(struct intel_engine_cs **siblings, unsigned int count);
*/
#define SCHED_STATE_NO_LOCK_ENABLED BIT(0)
#define SCHED_STATE_NO_LOCK_PENDING_ENABLE BIT(1)
#define SCHED_STATE_NO_LOCK_REGISTERED BIT(2)
static inline bool context_enabled(struct intel_context *ce)
{
return (atomic_read(&ce->guc_sched_state_no_lock) &
Expand Down Expand Up @@ -116,6 +117,24 @@ static inline void clr_context_pending_enable(struct intel_context *ce)
&ce->guc_sched_state_no_lock);
}

static inline bool context_registered(struct intel_context *ce)
{
return (atomic_read(&ce->guc_sched_state_no_lock) &
SCHED_STATE_NO_LOCK_REGISTERED);
}

static inline void set_context_registered(struct intel_context *ce)
{
atomic_or(SCHED_STATE_NO_LOCK_REGISTERED,
&ce->guc_sched_state_no_lock);
}

static inline void clr_context_registered(struct intel_context *ce)
{
atomic_and((u32)~SCHED_STATE_NO_LOCK_REGISTERED,
&ce->guc_sched_state_no_lock);
}

/*
* Below is a set of functions which control the GuC scheduling state which
* require a lock, aside from the special case where the functions are called
Expand Down Expand Up @@ -1092,6 +1111,7 @@ static int steal_guc_id(struct intel_guc *guc)

list_del_init(&ce->guc_id_link);
guc_id = ce->guc_id;
clr_context_registered(ce);
set_context_guc_id_invalid(ce);
return guc_id;
} else {
Expand Down Expand Up @@ -1201,10 +1221,15 @@ static int register_context(struct intel_context *ce, bool loop)
struct intel_guc *guc = ce_to_guc(ce);
u32 offset = intel_guc_ggtt_offset(guc, guc->lrc_desc_pool) +
ce->guc_id * sizeof(struct guc_lrc_desc);
int ret;

trace_intel_context_register(ce);

return __guc_action_register_context(guc, ce->guc_id, offset, loop);
ret = __guc_action_register_context(guc, ce->guc_id, offset, loop);
if (likely(!ret))
set_context_registered(ce);

return ret;
}

static int __guc_action_deregister_context(struct intel_guc *guc,
Expand Down Expand Up @@ -1260,13 +1285,17 @@ static void guc_context_policy_init(struct intel_engine_cs *engine,
desc->preemption_timeout = engine->props.preempt_timeout_ms * 1000;
}

static inline u8 map_i915_prio_to_guc_prio(int prio);

static int guc_lrc_desc_pin(struct intel_context *ce, bool loop)
{
struct intel_engine_cs *engine = ce->engine;
struct intel_runtime_pm *runtime_pm = engine->uncore->rpm;
struct intel_guc *guc = &engine->gt->uc.guc;
u32 desc_idx = ce->guc_id;
struct guc_lrc_desc *desc;
const struct i915_gem_context *ctx;
int prio = I915_CONTEXT_DEFAULT_PRIORITY;
bool context_registered;
intel_wakeref_t wakeref;
int ret = 0;
Expand All @@ -1282,6 +1311,12 @@ static int guc_lrc_desc_pin(struct intel_context *ce, bool loop)

context_registered = lrc_desc_registered(guc, desc_idx);

rcu_read_lock();
ctx = rcu_dereference(ce->gem_context);
if (ctx)
prio = ctx->sched.priority;
rcu_read_unlock();

reset_lrc_desc(guc, desc_idx);
set_lrc_desc_registered(guc, desc_idx, ce);

Expand All @@ -1290,7 +1325,8 @@ static int guc_lrc_desc_pin(struct intel_context *ce, bool loop)
desc->engine_submit_mask = adjust_engine_mask(engine->class,
engine->mask);
desc->hw_context_desc = ce->lrc.lrca;
desc->priority = GUC_CLIENT_PRIORITY_KMD_NORMAL;
ce->guc_prio = map_i915_prio_to_guc_prio(prio);
desc->priority = ce->guc_prio;
desc->context_flags = CONTEXT_REGISTRATION_FLAG_KMD;
guc_context_policy_init(engine, desc);
init_sched_state(ce);
Expand Down Expand Up @@ -1693,11 +1729,17 @@ static inline void guc_lrc_desc_unpin(struct intel_context *ce)
GEM_BUG_ON(ce != __get_context(guc, ce->guc_id));
GEM_BUG_ON(context_enabled(ce));

clr_context_registered(ce);
deregister_context(ce, ce->guc_id, true);
}

static void __guc_context_destroy(struct intel_context *ce)
{
GEM_BUG_ON(ce->guc_prio_count[GUC_CLIENT_PRIORITY_KMD_HIGH] ||
ce->guc_prio_count[GUC_CLIENT_PRIORITY_HIGH] ||
ce->guc_prio_count[GUC_CLIENT_PRIORITY_KMD_NORMAL] ||
ce->guc_prio_count[GUC_CLIENT_PRIORITY_NORMAL]);

lrc_fini(ce);
intel_context_fini(ce);

Expand Down Expand Up @@ -1791,15 +1833,124 @@ static int guc_context_alloc(struct intel_context *ce)
return lrc_alloc(ce, ce->engine);
}

static void guc_context_set_prio(struct intel_guc *guc,
struct intel_context *ce,
u8 prio)
{
u32 action[] = {
INTEL_GUC_ACTION_SET_CONTEXT_PRIORITY,
ce->guc_id,
prio,
};

GEM_BUG_ON(prio < GUC_CLIENT_PRIORITY_KMD_HIGH ||
prio > GUC_CLIENT_PRIORITY_NORMAL);

if (ce->guc_prio == prio || submission_disabled(guc) ||
!context_registered(ce))
return;

guc_submission_send_busy_loop(guc, action, ARRAY_SIZE(action), 0, true);

ce->guc_prio = prio;
trace_intel_context_set_prio(ce);
}

static inline u8 map_i915_prio_to_guc_prio(int prio)
{
if (prio == I915_PRIORITY_NORMAL)
return GUC_CLIENT_PRIORITY_KMD_NORMAL;
else if (prio < I915_PRIORITY_NORMAL)
return GUC_CLIENT_PRIORITY_NORMAL;
else if (prio < I915_PRIORITY_DISPLAY)
return GUC_CLIENT_PRIORITY_HIGH;
else
return GUC_CLIENT_PRIORITY_KMD_HIGH;
}

static inline void add_context_inflight_prio(struct intel_context *ce,
u8 guc_prio)
{
lockdep_assert_held(&ce->guc_active.lock);
GEM_BUG_ON(guc_prio >= ARRAY_SIZE(ce->guc_prio_count));

++ce->guc_prio_count[guc_prio];

/* Overflow protection */
GEM_WARN_ON(!ce->guc_prio_count[guc_prio]);
}

static inline void sub_context_inflight_prio(struct intel_context *ce,
u8 guc_prio)
{
lockdep_assert_held(&ce->guc_active.lock);
GEM_BUG_ON(guc_prio >= ARRAY_SIZE(ce->guc_prio_count));

/* Underflow protection */
GEM_WARN_ON(!ce->guc_prio_count[guc_prio]);

--ce->guc_prio_count[guc_prio];
}

static inline void update_context_prio(struct intel_context *ce)
{
struct intel_guc *guc = &ce->engine->gt->uc.guc;
int i;

BUILD_BUG_ON(GUC_CLIENT_PRIORITY_KMD_HIGH != 0);
BUILD_BUG_ON(GUC_CLIENT_PRIORITY_KMD_HIGH > GUC_CLIENT_PRIORITY_NORMAL);

lockdep_assert_held(&ce->guc_active.lock);

for (i = 0; i < ARRAY_SIZE(ce->guc_prio_count); ++i) {
if (ce->guc_prio_count[i]) {
guc_context_set_prio(guc, ce, i);
break;
}
}
}

static inline bool new_guc_prio_higher(u8 old_guc_prio, u8 new_guc_prio)
{
/* Lower value is higher priority */
return new_guc_prio < old_guc_prio;
}

static void add_to_context(struct i915_request *rq)
{
struct intel_context *ce = rq->context;
u8 new_guc_prio = map_i915_prio_to_guc_prio(rq_prio(rq));

GEM_BUG_ON(rq->guc_prio == GUC_PRIO_FINI);

spin_lock(&ce->guc_active.lock);
list_move_tail(&rq->sched.link, &ce->guc_active.requests);

if (rq->guc_prio == GUC_PRIO_INIT) {
rq->guc_prio = new_guc_prio;
add_context_inflight_prio(ce, rq->guc_prio);
} else if (new_guc_prio_higher(rq->guc_prio, new_guc_prio)) {
sub_context_inflight_prio(ce, rq->guc_prio);
rq->guc_prio = new_guc_prio;
add_context_inflight_prio(ce, rq->guc_prio);
}
update_context_prio(ce);

spin_unlock(&ce->guc_active.lock);
}

static void guc_prio_fini(struct i915_request *rq, struct intel_context *ce)
{
lockdep_assert_held(&ce->guc_active.lock);

if (rq->guc_prio != GUC_PRIO_INIT &&
rq->guc_prio != GUC_PRIO_FINI) {
sub_context_inflight_prio(ce, rq->guc_prio);
update_context_prio(ce);
}
rq->guc_prio = GUC_PRIO_FINI;
}

static void remove_from_context(struct i915_request *rq)
{
struct intel_context *ce = rq->context;
Expand All @@ -1812,6 +1963,8 @@ static void remove_from_context(struct i915_request *rq)
/* Prevent further __await_execution() registering a cb, then flush */
set_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags);

guc_prio_fini(rq, ce);

spin_unlock_irq(&ce->guc_active.lock);

atomic_dec(&ce->guc_id_ref);
Expand Down Expand Up @@ -2093,6 +2246,39 @@ static void guc_init_breadcrumbs(struct intel_engine_cs *engine)
}
}

static void guc_bump_inflight_request_prio(struct i915_request *rq,
int prio)
{
struct intel_context *ce = rq->context;
u8 new_guc_prio = map_i915_prio_to_guc_prio(prio);

/* Short circuit function */
if (prio < I915_PRIORITY_NORMAL ||
rq->guc_prio == GUC_PRIO_FINI ||
(rq->guc_prio != GUC_PRIO_INIT &&
!new_guc_prio_higher(rq->guc_prio, new_guc_prio)))
return;

spin_lock(&ce->guc_active.lock);
if (rq->guc_prio != GUC_PRIO_FINI) {
if (rq->guc_prio != GUC_PRIO_INIT)
sub_context_inflight_prio(ce, rq->guc_prio);
rq->guc_prio = new_guc_prio;
add_context_inflight_prio(ce, rq->guc_prio);
update_context_prio(ce);
}
spin_unlock(&ce->guc_active.lock);
}

static void guc_retire_inflight_request_prio(struct i915_request *rq)
{
struct intel_context *ce = rq->context;

spin_lock(&ce->guc_active.lock);
guc_prio_fini(rq, ce);
spin_unlock(&ce->guc_active.lock);
}

static void sanitize_hwsp(struct intel_engine_cs *engine)
{
struct intel_timeline *tl;
Expand Down Expand Up @@ -2317,6 +2503,10 @@ int intel_guc_submission_setup(struct intel_engine_cs *engine)
guc->sched_engine->disabled = guc_sched_engine_disabled;
guc->sched_engine->private_data = guc;
guc->sched_engine->destroy = guc_sched_engine_destroy;
guc->sched_engine->bump_inflight_request_prio =
guc_bump_inflight_request_prio;
guc->sched_engine->retire_inflight_request_prio =
guc_retire_inflight_request_prio;
tasklet_setup(&guc->sched_engine->tasklet,
guc_submission_tasklet);
}
Expand Down Expand Up @@ -2694,6 +2884,22 @@ void intel_guc_submission_print_info(struct intel_guc *guc,
drm_printf(p, "\n");
}

static inline void guc_log_context_priority(struct drm_printer *p,
struct intel_context *ce)
{
int i;

drm_printf(p, "\t\tPriority: %d\n",
ce->guc_prio);
drm_printf(p, "\t\tNumber Requests (lower index == higher priority)\n");
for (i = GUC_CLIENT_PRIORITY_KMD_HIGH;
i < GUC_CLIENT_PRIORITY_NUM; ++i) {
drm_printf(p, "\t\tNumber requests in priority band[%d]: %d\n",
i, ce->guc_prio_count[i]);
}
drm_printf(p, "\n");
}

void intel_guc_submission_print_context_info(struct intel_guc *guc,
struct drm_printer *p)
{
Expand All @@ -2716,6 +2922,8 @@ void intel_guc_submission_print_context_info(struct intel_guc *guc,
drm_printf(p, "\t\tSchedule State: 0x%x, 0x%x\n\n",
ce->guc_state.sched_state,
atomic_read(&ce->guc_sched_state_no_lock));

guc_log_context_priority(p, ce);
}
}

Expand Down
Loading

0 comments on commit ee242ca

Please sign in to comment.