Commit 058179e7 authored by Chris Wilson's avatar Chris Wilson

drm/i915/gt: Replace hangcheck by heartbeats

Replace sampling the engine state every so often with a periodic
heartbeat request to measure the health of an engine. This is coupled
with the forced-preemption to allow long running requests to survive so
long as they do not block other users.
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Jon Bloomfield <jon.bloomfield@intel.com>
Reviewed-by: default avatarJon Bloomfield <jon.bloomfield@intel.com>
Reviewed-by: default avatarTvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191023133108.21401-5-chris@chris-wilson.co.uk
parent 2e0986a5
...@@ -12,6 +12,17 @@ config DRM_I915_USERFAULT_AUTOSUSPEND ...@@ -12,6 +12,17 @@ config DRM_I915_USERFAULT_AUTOSUSPEND
May be 0 to disable the extra delay and solely use the device level May be 0 to disable the extra delay and solely use the device level
runtime pm autosuspend delay tunable. runtime pm autosuspend delay tunable.
config DRM_I915_HEARTBEAT_INTERVAL
int "Interval between heartbeat pulses (ms)"
default 2500 # milliseconds
help
The driver sends a periodic heartbeat down all active engines to
check the health of the GPU and undertake regular house-keeping of
internal driver state.
May be 0 to disable heartbeats and therefore disable automatic GPU
hang detection.
config DRM_I915_PREEMPT_TIMEOUT config DRM_I915_PREEMPT_TIMEOUT
int "Preempt timeout (ms, jiffy granularity)" int "Preempt timeout (ms, jiffy granularity)"
default 100 # milliseconds default 100 # milliseconds
......
...@@ -87,7 +87,6 @@ gt-y += \ ...@@ -87,7 +87,6 @@ gt-y += \
gt/intel_gt_pm.o \ gt/intel_gt_pm.o \
gt/intel_gt_pm_irq.o \ gt/intel_gt_pm_irq.o \
gt/intel_gt_requests.o \ gt/intel_gt_requests.o \
gt/intel_hangcheck.o \
gt/intel_llc.o \ gt/intel_llc.o \
gt/intel_lrc.o \ gt/intel_lrc.o \
gt/intel_rc6.o \ gt/intel_rc6.o \
......
...@@ -14863,7 +14863,7 @@ static void intel_plane_unpin_fb(struct intel_plane_state *old_plane_state) ...@@ -14863,7 +14863,7 @@ static void intel_plane_unpin_fb(struct intel_plane_state *old_plane_state)
static void fb_obj_bump_render_priority(struct drm_i915_gem_object *obj) static void fb_obj_bump_render_priority(struct drm_i915_gem_object *obj)
{ {
struct i915_sched_attr attr = { struct i915_sched_attr attr = {
.priority = I915_PRIORITY_DISPLAY, .priority = I915_USER_PRIORITY(I915_PRIORITY_DISPLAY),
}; };
i915_gem_object_wait_priority(obj, 0, &attr); i915_gem_object_wait_priority(obj, 0, &attr);
......
...@@ -462,6 +462,5 @@ int i915_gem_object_wait(struct drm_i915_gem_object *obj, ...@@ -462,6 +462,5 @@ int i915_gem_object_wait(struct drm_i915_gem_object *obj,
int i915_gem_object_wait_priority(struct drm_i915_gem_object *obj, int i915_gem_object_wait_priority(struct drm_i915_gem_object *obj,
unsigned int flags, unsigned int flags,
const struct i915_sched_attr *attr); const struct i915_sched_attr *attr);
#define I915_PRIORITY_DISPLAY I915_USER_PRIORITY(I915_PRIORITY_MAX)
#endif #endif
...@@ -76,8 +76,6 @@ void i915_gem_suspend(struct drm_i915_private *i915) ...@@ -76,8 +76,6 @@ void i915_gem_suspend(struct drm_i915_private *i915)
intel_gt_suspend(&i915->gt); intel_gt_suspend(&i915->gt);
intel_uc_suspend(&i915->gt.uc); intel_uc_suspend(&i915->gt.uc);
cancel_delayed_work_sync(&i915->gt.hangcheck.work);
i915_gem_drain_freed_objects(i915); i915_gem_drain_freed_objects(i915);
} }
......
...@@ -91,38 +91,6 @@ struct intel_gt; ...@@ -91,38 +91,6 @@ struct intel_gt;
/* seqno size is actually only a uint32, but since we plan to use MI_FLUSH_DW to /* seqno size is actually only a uint32, but since we plan to use MI_FLUSH_DW to
* do the writes, and that must have qw aligned offsets, simply pretend it's 8b. * do the writes, and that must have qw aligned offsets, simply pretend it's 8b.
*/ */
enum intel_engine_hangcheck_action {
ENGINE_IDLE = 0,
ENGINE_WAIT,
ENGINE_ACTIVE_SEQNO,
ENGINE_ACTIVE_HEAD,
ENGINE_ACTIVE_SUBUNITS,
ENGINE_WAIT_KICK,
ENGINE_DEAD,
};
static inline const char *
hangcheck_action_to_str(const enum intel_engine_hangcheck_action a)
{
switch (a) {
case ENGINE_IDLE:
return "idle";
case ENGINE_WAIT:
return "wait";
case ENGINE_ACTIVE_SEQNO:
return "active seqno";
case ENGINE_ACTIVE_HEAD:
return "active head";
case ENGINE_ACTIVE_SUBUNITS:
return "active subunits";
case ENGINE_WAIT_KICK:
return "wait kick";
case ENGINE_DEAD:
return "dead";
}
return "unknown";
}
static inline unsigned int static inline unsigned int
execlists_num_ports(const struct intel_engine_execlists * const execlists) execlists_num_ports(const struct intel_engine_execlists * const execlists)
......
...@@ -308,6 +308,8 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id) ...@@ -308,6 +308,8 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id)
engine->instance = info->instance; engine->instance = info->instance;
__sprint_engine_name(engine); __sprint_engine_name(engine);
engine->props.heartbeat_interval_ms =
CONFIG_DRM_I915_HEARTBEAT_INTERVAL;
engine->props.preempt_timeout_ms = engine->props.preempt_timeout_ms =
CONFIG_DRM_I915_PREEMPT_TIMEOUT; CONFIG_DRM_I915_PREEMPT_TIMEOUT;
engine->props.stop_timeout_ms = engine->props.stop_timeout_ms =
...@@ -609,7 +611,6 @@ static int intel_engine_setup_common(struct intel_engine_cs *engine) ...@@ -609,7 +611,6 @@ static int intel_engine_setup_common(struct intel_engine_cs *engine)
intel_engine_init_active(engine, ENGINE_PHYSICAL); intel_engine_init_active(engine, ENGINE_PHYSICAL);
intel_engine_init_breadcrumbs(engine); intel_engine_init_breadcrumbs(engine);
intel_engine_init_execlists(engine); intel_engine_init_execlists(engine);
intel_engine_init_hangcheck(engine);
intel_engine_init_cmd_parser(engine); intel_engine_init_cmd_parser(engine);
intel_engine_init__pm(engine); intel_engine_init__pm(engine);
...@@ -1470,8 +1471,13 @@ void intel_engine_dump(struct intel_engine_cs *engine, ...@@ -1470,8 +1471,13 @@ void intel_engine_dump(struct intel_engine_cs *engine,
drm_printf(m, "*** WEDGED ***\n"); drm_printf(m, "*** WEDGED ***\n");
drm_printf(m, "\tAwake? %d\n", atomic_read(&engine->wakeref.count)); drm_printf(m, "\tAwake? %d\n", atomic_read(&engine->wakeref.count));
drm_printf(m, "\tHangcheck: %d ms ago\n",
jiffies_to_msecs(jiffies - engine->hangcheck.action_timestamp)); rcu_read_lock();
rq = READ_ONCE(engine->heartbeat.systole);
if (rq)
drm_printf(m, "\tHeartbeat: %d ms ago\n",
jiffies_to_msecs(jiffies - rq->emitted_jiffies));
rcu_read_unlock();
drm_printf(m, "\tReset count: %d (global %d)\n", drm_printf(m, "\tReset count: %d (global %d)\n",
i915_reset_engine_count(error, engine), i915_reset_engine_count(error, engine),
i915_reset_count(error)); i915_reset_count(error));
......
...@@ -11,6 +11,30 @@ ...@@ -11,6 +11,30 @@
#include "intel_engine_pm.h" #include "intel_engine_pm.h"
#include "intel_engine.h" #include "intel_engine.h"
#include "intel_gt.h" #include "intel_gt.h"
#include "intel_reset.h"
/*
* While the engine is active, we send a periodic pulse along the engine
* to check on its health and to flush any idle-barriers. If that request
* is stuck, and we fail to preempt it, we declare the engine hung and
* issue a reset -- in the hope that restores progress.
*/
static bool next_heartbeat(struct intel_engine_cs *engine)
{
long delay;
delay = READ_ONCE(engine->props.heartbeat_interval_ms);
if (!delay)
return false;
delay = msecs_to_jiffies_timeout(delay);
if (delay >= HZ)
delay = round_jiffies_up_relative(delay);
schedule_delayed_work(&engine->heartbeat.work, delay);
return true;
}
static void idle_pulse(struct intel_engine_cs *engine, struct i915_request *rq) static void idle_pulse(struct intel_engine_cs *engine, struct i915_request *rq)
{ {
...@@ -18,6 +42,139 @@ static void idle_pulse(struct intel_engine_cs *engine, struct i915_request *rq) ...@@ -18,6 +42,139 @@ static void idle_pulse(struct intel_engine_cs *engine, struct i915_request *rq)
i915_request_add_active_barriers(rq); i915_request_add_active_barriers(rq);
} }
static void show_heartbeat(const struct i915_request *rq,
struct intel_engine_cs *engine)
{
struct drm_printer p = drm_debug_printer("heartbeat");
intel_engine_dump(engine, &p,
"%s heartbeat {prio:%d} not ticking\n",
engine->name,
rq->sched.attr.priority);
}
static void heartbeat(struct work_struct *wrk)
{
struct i915_sched_attr attr = {
.priority = I915_USER_PRIORITY(I915_PRIORITY_MIN),
};
struct intel_engine_cs *engine =
container_of(wrk, typeof(*engine), heartbeat.work.work);
struct intel_context *ce = engine->kernel_context;
struct i915_request *rq;
if (!intel_engine_pm_get_if_awake(engine))
return;
rq = engine->heartbeat.systole;
if (rq && i915_request_completed(rq)) {
i915_request_put(rq);
engine->heartbeat.systole = NULL;
}
if (intel_gt_is_wedged(engine->gt))
goto out;
if (engine->heartbeat.systole) {
if (engine->schedule &&
rq->sched.attr.priority < I915_PRIORITY_BARRIER) {
/*
* Gradually raise the priority of the heartbeat to
* give high priority work [which presumably desires
* low latency and no jitter] the chance to naturally
* complete before being preempted.
*/
attr.priority = I915_PRIORITY_MASK;
if (rq->sched.attr.priority >= attr.priority)
attr.priority |= I915_USER_PRIORITY(I915_PRIORITY_HEARTBEAT);
if (rq->sched.attr.priority >= attr.priority)
attr.priority = I915_PRIORITY_BARRIER;
local_bh_disable();
engine->schedule(rq, &attr);
local_bh_enable();
} else {
if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
show_heartbeat(rq, engine);
intel_gt_handle_error(engine->gt, engine->mask,
I915_ERROR_CAPTURE,
"stopped heartbeat on %s",
engine->name);
}
goto out;
}
if (engine->wakeref_serial == engine->serial)
goto out;
mutex_lock(&ce->timeline->mutex);
intel_context_enter(ce);
rq = __i915_request_create(ce, GFP_NOWAIT | __GFP_NOWARN);
intel_context_exit(ce);
if (IS_ERR(rq))
goto unlock;
idle_pulse(engine, rq);
if (i915_modparams.enable_hangcheck)
engine->heartbeat.systole = i915_request_get(rq);
__i915_request_commit(rq);
__i915_request_queue(rq, &attr);
unlock:
mutex_unlock(&ce->timeline->mutex);
out:
if (!next_heartbeat(engine))
i915_request_put(fetch_and_zero(&engine->heartbeat.systole));
intel_engine_pm_put(engine);
}
void intel_engine_unpark_heartbeat(struct intel_engine_cs *engine)
{
if (!CONFIG_DRM_I915_HEARTBEAT_INTERVAL)
return;
next_heartbeat(engine);
}
void intel_engine_park_heartbeat(struct intel_engine_cs *engine)
{
cancel_delayed_work(&engine->heartbeat.work);
i915_request_put(fetch_and_zero(&engine->heartbeat.systole));
}
void intel_engine_init_heartbeat(struct intel_engine_cs *engine)
{
INIT_DELAYED_WORK(&engine->heartbeat.work, heartbeat);
}
int intel_engine_set_heartbeat(struct intel_engine_cs *engine,
unsigned long delay)
{
int err;
/* Send one last pulse before to cleanup persistent hogs */
if (!delay && CONFIG_DRM_I915_PREEMPT_TIMEOUT) {
err = intel_engine_pulse(engine);
if (err)
return err;
}
WRITE_ONCE(engine->props.heartbeat_interval_ms, delay);
if (intel_engine_pm_get_if_awake(engine)) {
if (delay)
intel_engine_unpark_heartbeat(engine);
else
intel_engine_park_heartbeat(engine);
intel_engine_pm_put(engine);
}
return 0;
}
int intel_engine_pulse(struct intel_engine_cs *engine) int intel_engine_pulse(struct intel_engine_cs *engine)
{ {
struct i915_sched_attr attr = { .priority = I915_PRIORITY_BARRIER }; struct i915_sched_attr attr = { .priority = I915_PRIORITY_BARRIER };
......
...@@ -9,6 +9,14 @@ ...@@ -9,6 +9,14 @@
struct intel_engine_cs; struct intel_engine_cs;
void intel_engine_init_heartbeat(struct intel_engine_cs *engine);
int intel_engine_set_heartbeat(struct intel_engine_cs *engine,
unsigned long delay);
void intel_engine_park_heartbeat(struct intel_engine_cs *engine);
void intel_engine_unpark_heartbeat(struct intel_engine_cs *engine);
int intel_engine_pulse(struct intel_engine_cs *engine); int intel_engine_pulse(struct intel_engine_cs *engine);
int intel_engine_flush_barriers(struct intel_engine_cs *engine); int intel_engine_flush_barriers(struct intel_engine_cs *engine);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "i915_drv.h" #include "i915_drv.h"
#include "intel_engine.h" #include "intel_engine.h"
#include "intel_engine_heartbeat.h"
#include "intel_engine_pm.h" #include "intel_engine_pm.h"
#include "intel_engine_pool.h" #include "intel_engine_pool.h"
#include "intel_gt.h" #include "intel_gt.h"
...@@ -34,7 +35,7 @@ static int __engine_unpark(struct intel_wakeref *wf) ...@@ -34,7 +35,7 @@ static int __engine_unpark(struct intel_wakeref *wf)
if (engine->unpark) if (engine->unpark)
engine->unpark(engine); engine->unpark(engine);
intel_engine_init_hangcheck(engine); intel_engine_unpark_heartbeat(engine);
return 0; return 0;
} }
...@@ -158,6 +159,7 @@ static int __engine_park(struct intel_wakeref *wf) ...@@ -158,6 +159,7 @@ static int __engine_park(struct intel_wakeref *wf)
call_idle_barriers(engine); /* cleanup after wedging */ call_idle_barriers(engine); /* cleanup after wedging */
intel_engine_park_heartbeat(engine);
intel_engine_disarm_breadcrumbs(engine); intel_engine_disarm_breadcrumbs(engine);
intel_engine_pool_park(&engine->pool); intel_engine_pool_park(&engine->pool);
...@@ -188,6 +190,7 @@ void intel_engine_init__pm(struct intel_engine_cs *engine) ...@@ -188,6 +190,7 @@ void intel_engine_init__pm(struct intel_engine_cs *engine)
struct intel_runtime_pm *rpm = engine->uncore->rpm; struct intel_runtime_pm *rpm = engine->uncore->rpm;
intel_wakeref_init(&engine->wakeref, rpm, &wf_ops); intel_wakeref_init(&engine->wakeref, rpm, &wf_ops);
intel_engine_init_heartbeat(engine);
} }
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/workqueue.h>
#include "i915_gem.h" #include "i915_gem.h"
#include "i915_pmu.h" #include "i915_pmu.h"
...@@ -76,14 +77,6 @@ struct intel_instdone { ...@@ -76,14 +77,6 @@ struct intel_instdone {
u32 row[I915_MAX_SLICES][I915_MAX_SUBSLICES]; u32 row[I915_MAX_SLICES][I915_MAX_SUBSLICES];
}; };
struct intel_engine_hangcheck {
u64 acthd;
u32 last_ring;
u32 last_head;
unsigned long action_timestamp;
struct intel_instdone instdone;
};
struct intel_ring { struct intel_ring {
struct kref ref; struct kref ref;
struct i915_vma *vma; struct i915_vma *vma;
...@@ -331,6 +324,11 @@ struct intel_engine_cs { ...@@ -331,6 +324,11 @@ struct intel_engine_cs {
intel_engine_mask_t saturated; /* submitting semaphores too late? */ intel_engine_mask_t saturated; /* submitting semaphores too late? */
struct {
struct delayed_work work;
struct i915_request *systole;
} heartbeat;
unsigned long serial; unsigned long serial;
unsigned long wakeref_serial; unsigned long wakeref_serial;
...@@ -481,8 +479,6 @@ struct intel_engine_cs { ...@@ -481,8 +479,6 @@ struct intel_engine_cs {
/* status_notifier: list of callbacks for context-switch changes */ /* status_notifier: list of callbacks for context-switch changes */
struct atomic_notifier_head context_status_notifier; struct atomic_notifier_head context_status_notifier;
struct intel_engine_hangcheck hangcheck;
#define I915_ENGINE_NEEDS_CMD_PARSER BIT(0) #define I915_ENGINE_NEEDS_CMD_PARSER BIT(0)
#define I915_ENGINE_SUPPORTS_STATS BIT(1) #define I915_ENGINE_SUPPORTS_STATS BIT(1)
#define I915_ENGINE_HAS_PREEMPTION BIT(2) #define I915_ENGINE_HAS_PREEMPTION BIT(2)
...@@ -549,6 +545,7 @@ struct intel_engine_cs { ...@@ -549,6 +545,7 @@ struct intel_engine_cs {
} stats; } stats;
struct { struct {
unsigned long heartbeat_interval_ms;
unsigned long preempt_timeout_ms; unsigned long preempt_timeout_ms;
unsigned long stop_timeout_ms; unsigned long stop_timeout_ms;
} props; } props;
......
...@@ -22,7 +22,6 @@ void intel_gt_init_early(struct intel_gt *gt, struct drm_i915_private *i915) ...@@ -22,7 +22,6 @@ void intel_gt_init_early(struct intel_gt *gt, struct drm_i915_private *i915)
INIT_LIST_HEAD(&gt->closed_vma); INIT_LIST_HEAD(&gt->closed_vma);
spin_lock_init(&gt->closed_lock); spin_lock_init(&gt->closed_lock);
intel_gt_init_hangcheck(gt);
intel_gt_init_reset(gt); intel_gt_init_reset(gt);
intel_gt_init_requests(gt); intel_gt_init_requests(gt);
intel_gt_pm_init_early(gt); intel_gt_pm_init_early(gt);
......
...@@ -46,8 +46,6 @@ void intel_gt_clear_error_registers(struct intel_gt *gt, ...@@ -46,8 +46,6 @@ void intel_gt_clear_error_registers(struct intel_gt *gt,
void intel_gt_flush_ggtt_writes(struct intel_gt *gt); void intel_gt_flush_ggtt_writes(struct intel_gt *gt);
void intel_gt_chipset_flush(struct intel_gt *gt); void intel_gt_chipset_flush(struct intel_gt *gt);
void intel_gt_init_hangcheck(struct intel_gt *gt);
static inline u32 intel_gt_scratch_offset(const struct intel_gt *gt, static inline u32 intel_gt_scratch_offset(const struct intel_gt *gt,
enum intel_gt_scratch_field field) enum intel_gt_scratch_field field)
{ {
...@@ -59,6 +57,4 @@ static inline bool intel_gt_is_wedged(struct intel_gt *gt) ...@@ -59,6 +57,4 @@ static inline bool intel_gt_is_wedged(struct intel_gt *gt)
return __intel_reset_failed(&gt->reset); return __intel_reset_failed(&gt->reset);
} }
void intel_gt_queue_hangcheck(struct intel_gt *gt);
#endif /* __INTEL_GT_H__ */ #endif /* __INTEL_GT_H__ */
...@@ -47,7 +47,6 @@ static int __gt_unpark(struct intel_wakeref *wf) ...@@ -47,7 +47,6 @@ static int __gt_unpark(struct intel_wakeref *wf)
i915_pmu_gt_unparked(i915); i915_pmu_gt_unparked(i915);
intel_gt_queue_hangcheck(gt);
intel_gt_unpark_requests(gt); intel_gt_unpark_requests(gt);
return 0; return 0;
......
...@@ -27,14 +27,6 @@ struct i915_ggtt; ...@@ -27,14 +27,6 @@ struct i915_ggtt;
struct intel_engine_cs; struct intel_engine_cs;
struct intel_uncore; struct intel_uncore;
struct intel_hangcheck {
/* For hangcheck timer */
#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */
#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)
struct delayed_work work;
};
struct intel_gt { struct intel_gt {
struct drm_i915_private *i915; struct drm_i915_private *i915;
struct intel_uncore *uncore; struct intel_uncore *uncore;
...@@ -68,7 +60,6 @@ struct intel_gt { ...@@ -68,7 +60,6 @@ struct intel_gt {
struct list_head closed_vma; struct list_head closed_vma;
spinlock_t closed_lock; /* guards the list of closed_vma */ spinlock_t closed_lock; /* guards the list of closed_vma */
struct intel_hangcheck hangcheck;
struct intel_reset reset; struct intel_reset reset;
/** /**
......
This diff is collapsed.
...@@ -1024,8 +1024,6 @@ void intel_gt_reset(struct intel_gt *gt, ...@@ -1024,8 +1024,6 @@ void intel_gt_reset(struct intel_gt *gt,
if (ret) if (ret)
goto taint; goto taint;
intel_gt_queue_hangcheck(gt);
finish: finish:
reset_finish(gt, awake); reset_finish(gt, awake);
unlock: unlock:
...@@ -1353,4 +1351,5 @@ void __intel_fini_wedge(struct intel_wedge_me *w) ...@@ -1353,4 +1351,5 @@ void __intel_fini_wedge(struct intel_wedge_me *w)
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftest_reset.c" #include "selftest_reset.c"
#include "selftest_hangcheck.c"
#endif #endif
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
* Copyright © 2018 Intel Corporation * Copyright © 2018 Intel Corporation
*/ */
#include <linux/sort.h>
#include "i915_drv.h" #include "i915_drv.h"
#include "intel_gt_requests.h" #include "intel_gt_requests.h"
...@@ -150,11 +152,180 @@ static int live_idle_pulse(void *arg) ...@@ -150,11 +152,180 @@ static int live_idle_pulse(void *arg)
return err; return err;
} }
static int cmp_u32(const void *_a, const void *_b)
{
const u32 *a = _a, *b = _b;
return *a - *b;
}
static int __live_heartbeat_fast(struct intel_engine_cs *engine)
{
struct intel_context *ce;
struct i915_request *rq;
ktime_t t0, t1;
u32 times[5];
int err;
int i;
ce = intel_context_create(engine->kernel_context->gem_context,
engine);
if (IS_ERR(ce))
return PTR_ERR(ce);
intel_engine_pm_get(engine);
err = intel_engine_set_heartbeat(engine, 1);
if (err)
goto err_pm;
for (i = 0; i < ARRAY_SIZE(times); i++) {
/* Manufacture a tick */
do {
while (READ_ONCE(engine->heartbeat.systole))
flush_delayed_work(&engine->heartbeat.work);
engine->serial++; /* quick, pretend we are not idle! */
flush_delayed_work(&engine->heartbeat.work);
if (!delayed_work_pending(&engine->heartbeat.work)) {
pr_err("%s: heartbeat did not start\n",
engine->name);
err = -EINVAL;
goto err_pm;
}
rcu_read_lock();
rq = READ_ONCE(engine->heartbeat.systole);
if (rq)
rq = i915_request_get_rcu(rq);
rcu_read_unlock();
} while (!rq);
t0 = ktime_get();
while (rq == READ_ONCE(engine->heartbeat.systole))
yield(); /* work is on the local cpu! */
t1 = ktime_get();
i915_request_put(rq);
times[i] = ktime_us_delta(t1, t0);
}
sort(times, ARRAY_SIZE(times), sizeof(times[0]), cmp_u32, NULL);
pr_info("%s: Heartbeat delay: %uus [%u, %u]\n",
engine->name,
times[ARRAY_SIZE(times) / 2],
times[0],
times[ARRAY_SIZE(times) - 1]);
/* Min work delay is 2 * 2 (worst), +1 for scheduling, +1 for slack */
if (times[ARRAY_SIZE(times) / 2] > jiffies_to_usecs(6)) {
pr_err("%s: Heartbeat delay was %uus, expected less than %dus\n",
engine->name,
times[ARRAY_SIZE(times) / 2],
jiffies_to_usecs(6));
err = -EINVAL;
}
intel_engine_set_heartbeat(engine, CONFIG_DRM_I915_HEARTBEAT_INTERVAL);
err_pm:
intel_engine_pm_put(engine);
intel_context_put(ce);
return err;
}
static int live_heartbeat_fast(void *arg)
{
struct intel_gt *gt = arg;
struct intel_engine_cs *engine;
enum intel_engine_id id;
int err;
/* Check that the heartbeat ticks at the desired rate. */
if (!CONFIG_DRM_I915_HEARTBEAT_INTERVAL)
return 0;
for_each_engine(engine, gt, id) {
err = __live_heartbeat_fast(engine);
if (err)
break;
}
return err;
}
static int __live_heartbeat_off(struct intel_engine_cs *engine)
{
int err;
intel_engine_pm_get(engine);
engine->serial++;
flush_delayed_work(&engine->heartbeat.work);
if (!delayed_work_pending(&engine->heartbeat.work)) {
pr_err("%s: heartbeat not running\n",
engine->name);
err = -EINVAL;
goto err_pm;
}
err = intel_engine_set_heartbeat(engine, 0);
if (err)
goto err_pm;
engine->serial++;
flush_delayed_work(&engine->heartbeat.work);
if (delayed_work_pending(&engine->heartbeat.work)) {
pr_err("%s: heartbeat still running\n",
engine->name);
err = -EINVAL;
goto err_beat;
}
if (READ_ONCE(engine->heartbeat.systole)) {
pr_err("%s: heartbeat still allocated\n",
engine->name);
err = -EINVAL;
goto err_beat;
}
err_beat:
intel_engine_set_heartbeat(engine, CONFIG_DRM_I915_HEARTBEAT_INTERVAL);
err_pm:
intel_engine_pm_put(engine);
return err;
}
static int live_heartbeat_off(void *arg)
{
struct intel_gt *gt = arg;
struct intel_engine_cs *engine;
enum intel_engine_id id;
int err;
/* Check that we can turn off heartbeat and not interrupt VIP */
if (!CONFIG_DRM_I915_HEARTBEAT_INTERVAL)
return 0;
for_each_engine(engine, gt, id) {
if (!intel_engine_has_preemption(engine))
continue;
err = __live_heartbeat_off(engine);
if (err)
break;
}
return err;
}
int intel_heartbeat_live_selftests(struct drm_i915_private *i915) int intel_heartbeat_live_selftests(struct drm_i915_private *i915)
{ {
static const struct i915_subtest tests[] = { static const struct i915_subtest tests[] = {
SUBTEST(live_idle_flush), SUBTEST(live_idle_flush),
SUBTEST(live_idle_pulse), SUBTEST(live_idle_pulse),
SUBTEST(live_heartbeat_fast),
SUBTEST(live_heartbeat_off),
}; };
int saved_hangcheck; int saved_hangcheck;
int err; int err;
......
...@@ -1686,7 +1686,6 @@ int intel_hangcheck_live_selftests(struct drm_i915_private *i915) ...@@ -1686,7 +1686,6 @@ int intel_hangcheck_live_selftests(struct drm_i915_private *i915)
}; };
struct intel_gt *gt = &i915->gt; struct intel_gt *gt = &i915->gt;
intel_wakeref_t wakeref; intel_wakeref_t wakeref;
bool saved_hangcheck;
int err; int err;
if (!intel_has_gpu_reset(gt)) if (!intel_has_gpu_reset(gt))
...@@ -1696,12 +1695,9 @@ int intel_hangcheck_live_selftests(struct drm_i915_private *i915) ...@@ -1696,12 +1695,9 @@ int intel_hangcheck_live_selftests(struct drm_i915_private *i915)
return -EIO; /* we're long past hope of a successful reset */ return -EIO; /* we're long past hope of a successful reset */
wakeref = intel_runtime_pm_get(gt->uncore->rpm); wakeref = intel_runtime_pm_get(gt->uncore->rpm);
saved_hangcheck = fetch_and_zero(&i915_modparams.enable_hangcheck);
drain_delayed_work(&gt->hangcheck.work); /* flush param */
err = intel_gt_live_subtests(tests, gt); err = intel_gt_live_subtests(tests, gt);
i915_modparams.enable_hangcheck = saved_hangcheck;
intel_runtime_pm_put(gt->uncore->rpm, wakeref); intel_runtime_pm_put(gt->uncore->rpm, wakeref);
return err; return err;
......
...@@ -1011,92 +1011,6 @@ static int i915_frequency_info(struct seq_file *m, void *unused) ...@@ -1011,92 +1011,6 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
return ret; return ret;
} }
static void i915_instdone_info(struct drm_i915_private *dev_priv,
struct seq_file *m,
struct intel_instdone *instdone)
{
const struct sseu_dev_info *sseu = &RUNTIME_INFO(dev_priv)->sseu;
int slice;
int subslice;
seq_printf(m, "\t\tINSTDONE: 0x%08x\n",
instdone->instdone);
if (INTEL_GEN(dev_priv) <= 3)
return;
seq_printf(m, "\t\tSC_INSTDONE: 0x%08x\n",
instdone->slice_common);
if (INTEL_GEN(dev_priv) <= 6)
return;
for_each_instdone_slice_subslice(dev_priv, sseu, slice, subslice)
seq_printf(m, "\t\tSAMPLER_INSTDONE[%d][%d]: 0x%08x\n",
slice, subslice, instdone->sampler[slice][subslice]);
for_each_instdone_slice_subslice(dev_priv, sseu, slice, subslice)
seq_printf(m, "\t\tROW_INSTDONE[%d][%d]: 0x%08x\n",
slice, subslice, instdone->row[slice][subslice]);
}
static int i915_hangcheck_info(struct seq_file *m, void *unused)
{
struct drm_i915_private *i915 = node_to_i915(m->private);
struct intel_gt *gt = &i915->gt;
struct intel_engine_cs *engine;
intel_wakeref_t wakeref;
enum intel_engine_id id;
seq_printf(m, "Reset flags: %lx\n", gt->reset.flags);
if (test_bit(I915_WEDGED, &gt->reset.flags))
seq_puts(m, "\tWedged\n");
if (test_bit(I915_RESET_BACKOFF, &gt->reset.flags))
seq_puts(m, "\tDevice (global) reset in progress\n");
if (!i915_modparams.enable_hangcheck) {
seq_puts(m, "Hangcheck disabled\n");
return 0;
}
if (timer_pending(&gt->hangcheck.work.timer))
seq_printf(m, "Hangcheck active, timer fires in %dms\n",
jiffies_to_msecs(gt->hangcheck.work.timer.expires -
jiffies));
else if (delayed_work_pending(&gt->hangcheck.work))
seq_puts(m, "Hangcheck active, work pending\n");
else
seq_puts(m, "Hangcheck inactive\n");
seq_printf(m, "GT active? %s\n", yesno(gt->awake));
with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
for_each_engine(engine, i915, id) {
struct intel_instdone instdone;
seq_printf(m, "%s: %d ms ago\n",
engine->name,
jiffies_to_msecs(jiffies -
engine->hangcheck.action_timestamp));
seq_printf(m, "\tACTHD = 0x%08llx [current 0x%08llx]\n",
(long long)engine->hangcheck.acthd,
intel_engine_get_active_head(engine));
intel_engine_get_instdone(engine, &instdone);
seq_puts(m, "\tinstdone read =\n");
i915_instdone_info(i915, m, &instdone);
seq_puts(m, "\tinstdone accu =\n");
i915_instdone_info(i915, m,
&engine->hangcheck.instdone);
}
}
return 0;
}
static int ironlake_drpc_info(struct seq_file *m) static int ironlake_drpc_info(struct seq_file *m)
{ {
struct drm_i915_private *i915 = node_to_i915(m->private); struct drm_i915_private *i915 = node_to_i915(m->private);
...@@ -4363,7 +4277,6 @@ static const struct drm_info_list i915_debugfs_list[] = { ...@@ -4363,7 +4277,6 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_guc_stage_pool", i915_guc_stage_pool, 0}, {"i915_guc_stage_pool", i915_guc_stage_pool, 0},
{"i915_huc_load_status", i915_huc_load_status_info, 0}, {"i915_huc_load_status", i915_huc_load_status_info, 0},
{"i915_frequency_info", i915_frequency_info, 0}, {"i915_frequency_info", i915_frequency_info, 0},
{"i915_hangcheck_info", i915_hangcheck_info, 0},
{"i915_drpc_info", i915_drpc_info, 0}, {"i915_drpc_info", i915_drpc_info, 0},
{"i915_ring_freq_table", i915_ring_freq_table, 0}, {"i915_ring_freq_table", i915_ring_freq_table, 0},
{"i915_frontbuffer_tracking", i915_frontbuffer_tracking, 0}, {"i915_frontbuffer_tracking", i915_frontbuffer_tracking, 0},
......
...@@ -1548,10 +1548,7 @@ void i915_driver_remove(struct drm_i915_private *i915) ...@@ -1548,10 +1548,7 @@ void i915_driver_remove(struct drm_i915_private *i915)
i915_driver_modeset_remove(i915); i915_driver_modeset_remove(i915);
/* Free error state after interrupts are fully disabled. */
cancel_delayed_work_sync(&i915->gt.hangcheck.work);
i915_reset_error_state(i915); i915_reset_error_state(i915);
i915_gem_driver_remove(i915); i915_gem_driver_remove(i915);
intel_power_domains_driver_remove(i915); intel_power_domains_driver_remove(i915);
......
...@@ -1846,7 +1846,6 @@ void i915_driver_remove(struct drm_i915_private *i915); ...@@ -1846,7 +1846,6 @@ void i915_driver_remove(struct drm_i915_private *i915);
int i915_resume_switcheroo(struct drm_i915_private *i915); int i915_resume_switcheroo(struct drm_i915_private *i915);
int i915_suspend_switcheroo(struct drm_i915_private *i915, pm_message_t state); int i915_suspend_switcheroo(struct drm_i915_private *i915, pm_message_t state);
void intel_engine_init_hangcheck(struct intel_engine_cs *engine);
int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool on); int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool on);
static inline bool intel_gvt_active(struct drm_i915_private *dev_priv) static inline bool intel_gvt_active(struct drm_i915_private *dev_priv)
......
...@@ -534,10 +534,6 @@ static void error_print_engine(struct drm_i915_error_state_buf *m, ...@@ -534,10 +534,6 @@ static void error_print_engine(struct drm_i915_error_state_buf *m,
} }
err_printf(m, " ring->head: 0x%08x\n", ee->cpu_ring_head); err_printf(m, " ring->head: 0x%08x\n", ee->cpu_ring_head);
err_printf(m, " ring->tail: 0x%08x\n", ee->cpu_ring_tail); err_printf(m, " ring->tail: 0x%08x\n", ee->cpu_ring_tail);
err_printf(m, " hangcheck timestamp: %dms (%lu%s)\n",
jiffies_to_msecs(ee->hangcheck_timestamp - epoch),
ee->hangcheck_timestamp,
ee->hangcheck_timestamp == epoch ? "; epoch" : "");
err_printf(m, " engine reset count: %u\n", ee->reset_count); err_printf(m, " engine reset count: %u\n", ee->reset_count);
for (n = 0; n < ee->num_ports; n++) { for (n = 0; n < ee->num_ports; n++) {
...@@ -679,11 +675,8 @@ static void __err_print_to_sgl(struct drm_i915_error_state_buf *m, ...@@ -679,11 +675,8 @@ static void __err_print_to_sgl(struct drm_i915_error_state_buf *m,
ts = ktime_to_timespec64(error->uptime); ts = ktime_to_timespec64(error->uptime);
err_printf(m, "Uptime: %lld s %ld us\n", err_printf(m, "Uptime: %lld s %ld us\n",
(s64)ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC); (s64)ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC);
err_printf(m, "Epoch: %lu jiffies (%u HZ)\n", error->epoch, HZ); err_printf(m, "Capture: %lu jiffies; %d ms ago\n",
err_printf(m, "Capture: %lu jiffies; %d ms ago, %d ms after epoch\n", error->capture, jiffies_to_msecs(jiffies - error->capture));
error->capture,
jiffies_to_msecs(jiffies - error->capture),
jiffies_to_msecs(error->capture - error->epoch));
for (ee = error->engine; ee; ee = ee->next) for (ee = error->engine; ee; ee = ee->next)
err_printf(m, "Active process (on ring %s): %s [%d]\n", err_printf(m, "Active process (on ring %s): %s [%d]\n",
...@@ -742,7 +735,7 @@ static void __err_print_to_sgl(struct drm_i915_error_state_buf *m, ...@@ -742,7 +735,7 @@ static void __err_print_to_sgl(struct drm_i915_error_state_buf *m,
err_printf(m, "GTT_CACHE_EN: 0x%08x\n", error->gtt_cache); err_printf(m, "GTT_CACHE_EN: 0x%08x\n", error->gtt_cache);
for (ee = error->engine; ee; ee = ee->next) for (ee = error->engine; ee; ee = ee->next)
error_print_engine(m, ee, error->epoch); error_print_engine(m, ee, error->capture);
for (ee = error->engine; ee; ee = ee->next) { for (ee = error->engine; ee; ee = ee->next) {
const struct drm_i915_error_object *obj; const struct drm_i915_error_object *obj;
...@@ -770,7 +763,7 @@ static void __err_print_to_sgl(struct drm_i915_error_state_buf *m, ...@@ -770,7 +763,7 @@ static void __err_print_to_sgl(struct drm_i915_error_state_buf *m,
for (j = 0; j < ee->num_requests; j++) for (j = 0; j < ee->num_requests; j++)
error_print_request(m, " ", error_print_request(m, " ",
&ee->requests[j], &ee->requests[j],
error->epoch); error->capture);
} }
print_error_obj(m, ee->engine, "ringbuffer", ee->ringbuffer); print_error_obj(m, ee->engine, "ringbuffer", ee->ringbuffer);
...@@ -1144,8 +1137,6 @@ static void error_record_engine_registers(struct i915_gpu_state *error, ...@@ -1144,8 +1137,6 @@ static void error_record_engine_registers(struct i915_gpu_state *error,
} }
ee->idle = intel_engine_is_idle(engine); ee->idle = intel_engine_is_idle(engine);
if (!ee->idle)
ee->hangcheck_timestamp = engine->hangcheck.action_timestamp;
ee->reset_count = i915_reset_engine_count(&dev_priv->gpu_error, ee->reset_count = i915_reset_engine_count(&dev_priv->gpu_error,
engine); engine);
...@@ -1657,20 +1648,6 @@ static void capture_params(struct i915_gpu_state *error) ...@@ -1657,20 +1648,6 @@ static void capture_params(struct i915_gpu_state *error)
i915_params_copy(&error->params, &i915_modparams); i915_params_copy(&error->params, &i915_modparams);
} }
static unsigned long capture_find_epoch(const struct i915_gpu_state *error)
{
const struct drm_i915_error_engine *ee;
unsigned long epoch = error->capture;
for (ee = error->engine; ee; ee = ee->next) {
if (ee->hangcheck_timestamp &&
time_before(ee->hangcheck_timestamp, epoch))
epoch = ee->hangcheck_timestamp;
}
return epoch;
}
static void capture_finish(struct i915_gpu_state *error) static void capture_finish(struct i915_gpu_state *error)
{ {
struct i915_ggtt *ggtt = &error->i915->ggtt; struct i915_ggtt *ggtt = &error->i915->ggtt;
...@@ -1722,8 +1699,6 @@ i915_capture_gpu_state(struct drm_i915_private *i915) ...@@ -1722,8 +1699,6 @@ i915_capture_gpu_state(struct drm_i915_private *i915)
error->overlay = intel_overlay_capture_error_state(i915); error->overlay = intel_overlay_capture_error_state(i915);
error->display = intel_display_capture_error_state(i915); error->display = intel_display_capture_error_state(i915);
error->epoch = capture_find_epoch(error);
capture_finish(error); capture_finish(error);
compress_fini(&compress); compress_fini(&compress);
......
...@@ -34,7 +34,6 @@ struct i915_gpu_state { ...@@ -34,7 +34,6 @@ struct i915_gpu_state {
ktime_t boottime; ktime_t boottime;
ktime_t uptime; ktime_t uptime;
unsigned long capture; unsigned long capture;
unsigned long epoch;
struct drm_i915_private *i915; struct drm_i915_private *i915;
...@@ -86,7 +85,6 @@ struct i915_gpu_state { ...@@ -86,7 +85,6 @@ struct i915_gpu_state {
/* Software tracked state */ /* Software tracked state */
bool idle; bool idle;
unsigned long hangcheck_timestamp;
int num_requests; int num_requests;
u32 reset_count; u32 reset_count;
......
...@@ -16,6 +16,12 @@ enum { ...@@ -16,6 +16,12 @@ enum {
I915_PRIORITY_MIN = I915_CONTEXT_MIN_USER_PRIORITY - 1, I915_PRIORITY_MIN = I915_CONTEXT_MIN_USER_PRIORITY - 1,
I915_PRIORITY_NORMAL = I915_CONTEXT_DEFAULT_PRIORITY, I915_PRIORITY_NORMAL = I915_CONTEXT_DEFAULT_PRIORITY,
I915_PRIORITY_MAX = I915_CONTEXT_MAX_USER_PRIORITY + 1, I915_PRIORITY_MAX = I915_CONTEXT_MAX_USER_PRIORITY + 1,
/* A preemptive pulse used to monitor the health of each engine */
I915_PRIORITY_HEARTBEAT,
/* Interactive workload, scheduled for immediate pageflipping */
I915_PRIORITY_DISPLAY,
}; };
#define I915_USER_PRIORITY_SHIFT 2 #define I915_USER_PRIORITY_SHIFT 2
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment