2019-10-21 18:43:39 +01:00
/*
* SPDX - License - Identifier : MIT
*
* Copyright © 2019 Intel Corporation
*/
# include "i915_request.h"
# include "intel_context.h"
# include "intel_engine_heartbeat.h"
# include "intel_engine_pm.h"
# include "intel_engine.h"
# include "intel_gt.h"
2019-10-23 14:31:08 +01:00
# 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 ;
}
2019-10-21 18:43:39 +01:00
static void idle_pulse ( struct intel_engine_cs * engine , struct i915_request * rq )
{
engine - > wakeref_serial = READ_ONCE ( engine - > serial ) + 1 ;
i915_request_add_active_barriers ( rq ) ;
}
2019-10-23 14:31:08 +01:00
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 ;
rq = engine - > heartbeat . systole ;
if ( rq & & i915_request_completed ( rq ) ) {
i915_request_put ( rq ) ;
engine - > heartbeat . systole = NULL ;
}
2019-11-06 22:34:10 +00:00
if ( ! intel_engine_pm_get_if_awake ( engine ) )
return ;
2019-10-23 14:31:08 +01:00
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 )
{
2019-10-25 14:59:42 +01:00
if ( ! IS_ACTIVE ( CONFIG_DRM_I915_HEARTBEAT_INTERVAL ) )
2019-10-23 14:31:08 +01:00
return ;
next_heartbeat ( engine ) ;
}
void intel_engine_park_heartbeat ( struct intel_engine_cs * engine )
{
2019-11-06 13:31:29 +00:00
if ( cancel_delayed_work ( & engine - > heartbeat . work ) )
i915_request_put ( fetch_and_zero ( & engine - > heartbeat . systole ) ) ;
2019-10-23 14:31:08 +01:00
}
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 */
2019-10-25 14:59:42 +01:00
if ( ! delay & & IS_ACTIVE ( CONFIG_DRM_I915_PREEMPT_TIMEOUT ) ) {
2019-10-23 14:31:08 +01:00
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 ;
}
2019-10-21 18:43:39 +01:00
int intel_engine_pulse ( struct intel_engine_cs * engine )
{
struct i915_sched_attr attr = { . priority = I915_PRIORITY_BARRIER } ;
struct intel_context * ce = engine - > kernel_context ;
struct i915_request * rq ;
int err = 0 ;
if ( ! intel_engine_has_preemption ( engine ) )
return - ENODEV ;
if ( ! intel_engine_pm_get_if_awake ( engine ) )
return 0 ;
if ( mutex_lock_interruptible ( & ce - > timeline - > mutex ) )
goto out_rpm ;
intel_context_enter ( ce ) ;
rq = __i915_request_create ( ce , GFP_NOWAIT | __GFP_NOWARN ) ;
intel_context_exit ( ce ) ;
if ( IS_ERR ( rq ) ) {
err = PTR_ERR ( rq ) ;
goto out_unlock ;
}
rq - > flags | = I915_REQUEST_SENTINEL ;
idle_pulse ( engine , rq ) ;
__i915_request_commit ( rq ) ;
__i915_request_queue ( rq , & attr ) ;
out_unlock :
mutex_unlock ( & ce - > timeline - > mutex ) ;
out_rpm :
intel_engine_pm_put ( engine ) ;
return err ;
}
int intel_engine_flush_barriers ( struct intel_engine_cs * engine )
{
struct i915_request * rq ;
if ( llist_empty ( & engine - > barrier_tasks ) )
return 0 ;
rq = i915_request_create ( engine - > kernel_context ) ;
if ( IS_ERR ( rq ) )
return PTR_ERR ( rq ) ;
idle_pulse ( engine , rq ) ;
i915_request_add ( rq ) ;
return 0 ;
}
# if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
# include "selftest_engine_heartbeat.c"
# endif