2019-11-12 22:36:00 +00:00
/*
* SPDX - License - Identifier : MIT
*
* Copyright © 2019 Intel Corporation
*/
# include "gt/intel_engine_pm.h"
# include "i915_selftest.h"
# include "gem/selftests/mock_context.h"
# include "selftests/igt_reset.h"
# include "selftests/igt_spinner.h"
struct live_mocs {
2020-02-18 16:21:48 +00:00
struct drm_i915_mocs_table mocs ;
struct drm_i915_mocs_table l3cc ;
2019-11-12 22:36:00 +00:00
struct i915_vma * scratch ;
void * vaddr ;
} ;
static int request_add_sync ( struct i915_request * rq , int err )
{
i915_request_get ( rq ) ;
i915_request_add ( rq ) ;
if ( i915_request_wait ( rq , 0 , HZ / 5 ) < 0 )
err = - ETIME ;
i915_request_put ( rq ) ;
return err ;
}
static int request_add_spin ( struct i915_request * rq , struct igt_spinner * spin )
{
int err = 0 ;
i915_request_get ( rq ) ;
i915_request_add ( rq ) ;
if ( spin & & ! igt_wait_for_spinner ( spin , rq ) )
err = - ETIME ;
i915_request_put ( rq ) ;
return err ;
}
static struct i915_vma * create_scratch ( struct intel_gt * gt )
{
struct drm_i915_gem_object * obj ;
struct i915_vma * vma ;
int err ;
obj = i915_gem_object_create_internal ( gt - > i915 , PAGE_SIZE ) ;
if ( IS_ERR ( obj ) )
return ERR_CAST ( obj ) ;
i915_gem_object_set_cache_coherency ( obj , I915_CACHING_CACHED ) ;
vma = i915_vma_instance ( obj , & gt - > ggtt - > vm , NULL ) ;
if ( IS_ERR ( vma ) ) {
i915_gem_object_put ( obj ) ;
return vma ;
}
err = i915_vma_pin ( vma , 0 , 0 , PIN_GLOBAL ) ;
if ( err ) {
i915_gem_object_put ( obj ) ;
return ERR_PTR ( err ) ;
}
return vma ;
}
static int live_mocs_init ( struct live_mocs * arg , struct intel_gt * gt )
{
2020-02-18 16:21:48 +00:00
struct drm_i915_mocs_table table ;
unsigned int flags ;
2019-11-12 22:36:00 +00:00
int err ;
2020-02-18 16:21:48 +00:00
memset ( arg , 0 , sizeof ( * arg ) ) ;
flags = get_mocs_settings ( gt - > i915 , & table ) ;
if ( ! flags )
2019-11-12 22:36:00 +00:00
return - EINVAL ;
2020-02-18 16:21:48 +00:00
if ( flags & HAS_RENDER_L3CC )
arg - > l3cc = table ;
if ( flags & ( HAS_GLOBAL_MOCS | HAS_ENGINE_MOCS ) )
arg - > mocs = table ;
2019-11-12 22:36:00 +00:00
arg - > scratch = create_scratch ( gt ) ;
if ( IS_ERR ( arg - > scratch ) )
return PTR_ERR ( arg - > scratch ) ;
arg - > vaddr = i915_gem_object_pin_map ( arg - > scratch - > obj , I915_MAP_WB ) ;
if ( IS_ERR ( arg - > vaddr ) ) {
err = PTR_ERR ( arg - > vaddr ) ;
goto err_scratch ;
}
return 0 ;
err_scratch :
i915_vma_unpin_and_release ( & arg - > scratch , 0 ) ;
return err ;
}
static void live_mocs_fini ( struct live_mocs * arg )
{
i915_vma_unpin_and_release ( & arg - > scratch , I915_VMA_RELEASE_MAP ) ;
}
static int read_regs ( struct i915_request * rq ,
u32 addr , unsigned int count ,
uint32_t * offset )
{
unsigned int i ;
u32 * cs ;
GEM_BUG_ON ( ! IS_ALIGNED ( * offset , sizeof ( u32 ) ) ) ;
cs = intel_ring_begin ( rq , 4 * count ) ;
if ( IS_ERR ( cs ) )
return PTR_ERR ( cs ) ;
for ( i = 0 ; i < count ; i + + ) {
* cs + + = MI_STORE_REGISTER_MEM_GEN8 | MI_USE_GGTT ;
* cs + + = addr ;
* cs + + = * offset ;
* cs + + = 0 ;
addr + = sizeof ( u32 ) ;
* offset + = sizeof ( u32 ) ;
}
intel_ring_advance ( rq , cs ) ;
return 0 ;
}
static int read_mocs_table ( struct i915_request * rq ,
const struct drm_i915_mocs_table * table ,
uint32_t * offset )
{
u32 addr ;
2020-06-02 23:09:53 +01:00
if ( HAS_GLOBAL_MOCS_REGISTERS ( rq - > engine - > i915 ) )
2019-11-12 22:36:00 +00:00
addr = global_mocs_offset ( ) ;
else
addr = mocs_offset ( rq - > engine ) ;
return read_regs ( rq , addr , table - > n_entries , offset ) ;
}
static int read_l3cc_table ( struct i915_request * rq ,
const struct drm_i915_mocs_table * table ,
uint32_t * offset )
{
u32 addr = i915_mmio_reg_offset ( GEN9_LNCFCMOCS ( 0 ) ) ;
return read_regs ( rq , addr , ( table - > n_entries + 1 ) / 2 , offset ) ;
}
static int check_mocs_table ( struct intel_engine_cs * engine ,
const struct drm_i915_mocs_table * table ,
uint32_t * * vaddr )
{
unsigned int i ;
u32 expect ;
for_each_mocs ( expect , table , i ) {
if ( * * vaddr ! = expect ) {
pr_err ( " %s: Invalid MOCS[%d] entry, found %08x, expected %08x \n " ,
engine - > name , i , * * vaddr , expect ) ;
return - EINVAL ;
}
+ + * vaddr ;
}
return 0 ;
}
static bool mcr_range ( struct drm_i915_private * i915 , u32 offset )
{
/*
* Registers in this range are affected by the MCR selector
* which only controls CPU initiated MMIO . Routing does not
* work for CS access so we cannot verify them on this path .
*/
return INTEL_GEN ( i915 ) > = 8 & & offset > = 0xb000 & & offset < = 0xb4ff ;
}
static int check_l3cc_table ( struct intel_engine_cs * engine ,
const struct drm_i915_mocs_table * table ,
uint32_t * * vaddr )
{
/* Can we read the MCR range 0xb00 directly? See intel_workarounds! */
u32 reg = i915_mmio_reg_offset ( GEN9_LNCFCMOCS ( 0 ) ) ;
unsigned int i ;
u32 expect ;
for_each_l3cc ( expect , table , i ) {
if ( ! mcr_range ( engine - > i915 , reg ) & & * * vaddr ! = expect ) {
pr_err ( " %s: Invalid L3CC[%d] entry, found %08x, expected %08x \n " ,
engine - > name , i , * * vaddr , expect ) ;
return - EINVAL ;
}
+ + * vaddr ;
reg + = 4 ;
}
return 0 ;
}
static int check_mocs_engine ( struct live_mocs * arg ,
struct intel_context * ce )
{
struct i915_vma * vma = arg - > scratch ;
struct i915_request * rq ;
u32 offset ;
u32 * vaddr ;
int err ;
memset32 ( arg - > vaddr , STACK_MAGIC , PAGE_SIZE / sizeof ( u32 ) ) ;
rq = intel_context_create_request ( ce ) ;
if ( IS_ERR ( rq ) )
return PTR_ERR ( rq ) ;
i915_vma_lock ( vma ) ;
err = i915_request_await_object ( rq , vma - > obj , true ) ;
if ( ! err )
err = i915_vma_move_to_active ( vma , rq , EXEC_OBJECT_WRITE ) ;
i915_vma_unlock ( vma ) ;
/* Read the mocs tables back using SRM */
offset = i915_ggtt_offset ( vma ) ;
if ( ! err )
2020-02-18 16:21:48 +00:00
err = read_mocs_table ( rq , & arg - > mocs , & offset ) ;
2019-11-12 22:36:00 +00:00
if ( ! err & & ce - > engine - > class = = RENDER_CLASS )
2020-02-18 16:21:48 +00:00
err = read_l3cc_table ( rq , & arg - > l3cc , & offset ) ;
2019-11-12 22:36:00 +00:00
offset - = i915_ggtt_offset ( vma ) ;
GEM_BUG_ON ( offset > PAGE_SIZE ) ;
err = request_add_sync ( rq , err ) ;
if ( err )
return err ;
/* Compare the results against the expected tables */
vaddr = arg - > vaddr ;
if ( ! err )
2020-02-18 16:21:48 +00:00
err = check_mocs_table ( ce - > engine , & arg - > mocs , & vaddr ) ;
2019-11-12 22:36:00 +00:00
if ( ! err & & ce - > engine - > class = = RENDER_CLASS )
2020-02-18 16:21:48 +00:00
err = check_l3cc_table ( ce - > engine , & arg - > l3cc , & vaddr ) ;
2019-11-12 22:36:00 +00:00
if ( err )
return err ;
GEM_BUG_ON ( arg - > vaddr + offset ! = vaddr ) ;
return 0 ;
}
static int live_mocs_kernel ( void * arg )
{
struct intel_gt * gt = arg ;
struct intel_engine_cs * engine ;
enum intel_engine_id id ;
struct live_mocs mocs ;
int err ;
/* Basic check the system is configured with the expected mocs table */
err = live_mocs_init ( & mocs , gt ) ;
if ( err )
return err ;
for_each_engine ( engine , gt , id ) {
2019-11-25 10:58:56 +00:00
intel_engine_pm_get ( engine ) ;
2019-11-12 22:36:00 +00:00
err = check_mocs_engine ( & mocs , engine - > kernel_context ) ;
2019-11-25 10:58:56 +00:00
intel_engine_pm_put ( engine ) ;
2019-11-12 22:36:00 +00:00
if ( err )
break ;
}
live_mocs_fini ( & mocs ) ;
return err ;
}
static int live_mocs_clean ( void * arg )
{
struct intel_gt * gt = arg ;
struct intel_engine_cs * engine ;
enum intel_engine_id id ;
struct live_mocs mocs ;
int err ;
/* Every new context should see the same mocs table */
err = live_mocs_init ( & mocs , gt ) ;
if ( err )
return err ;
for_each_engine ( engine , gt , id ) {
struct intel_context * ce ;
2019-12-21 16:03:24 +00:00
ce = intel_context_create ( engine ) ;
2019-11-12 22:36:00 +00:00
if ( IS_ERR ( ce ) ) {
err = PTR_ERR ( ce ) ;
break ;
}
err = check_mocs_engine ( & mocs , ce ) ;
intel_context_put ( ce ) ;
if ( err )
break ;
}
live_mocs_fini ( & mocs ) ;
return err ;
}
static int active_engine_reset ( struct intel_context * ce ,
const char * reason )
{
struct igt_spinner spin ;
struct i915_request * rq ;
int err ;
err = igt_spinner_init ( & spin , ce - > engine - > gt ) ;
if ( err )
return err ;
rq = igt_spinner_create_request ( & spin , ce , MI_NOOP ) ;
if ( IS_ERR ( rq ) ) {
igt_spinner_fini ( & spin ) ;
return PTR_ERR ( rq ) ;
}
err = request_add_spin ( rq , & spin ) ;
if ( err = = 0 )
err = intel_engine_reset ( ce - > engine , reason ) ;
igt_spinner_end ( & spin ) ;
igt_spinner_fini ( & spin ) ;
return err ;
}
static int __live_mocs_reset ( struct live_mocs * mocs ,
struct intel_context * ce )
{
int err ;
err = intel_engine_reset ( ce - > engine , " mocs " ) ;
if ( err )
return err ;
err = check_mocs_engine ( mocs , ce ) ;
if ( err )
return err ;
err = active_engine_reset ( ce , " mocs " ) ;
if ( err )
return err ;
err = check_mocs_engine ( mocs , ce ) ;
if ( err )
return err ;
intel_gt_reset ( ce - > engine - > gt , ce - > engine - > mask , " mocs " ) ;
err = check_mocs_engine ( mocs , ce ) ;
if ( err )
return err ;
return 0 ;
}
static int live_mocs_reset ( void * arg )
{
struct intel_gt * gt = arg ;
struct intel_engine_cs * engine ;
enum intel_engine_id id ;
struct live_mocs mocs ;
int err = 0 ;
/* Check the mocs setup is retained over per-engine and global resets */
if ( ! intel_has_reset_engine ( gt ) )
return 0 ;
err = live_mocs_init ( & mocs , gt ) ;
if ( err )
return err ;
igt_global_reset_lock ( gt ) ;
for_each_engine ( engine , gt , id ) {
struct intel_context * ce ;
2019-12-21 16:03:24 +00:00
ce = intel_context_create ( engine ) ;
2019-11-12 22:36:00 +00:00
if ( IS_ERR ( ce ) ) {
err = PTR_ERR ( ce ) ;
break ;
}
intel_engine_pm_get ( engine ) ;
err = __live_mocs_reset ( & mocs , ce ) ;
intel_engine_pm_put ( engine ) ;
intel_context_put ( ce ) ;
if ( err )
break ;
}
igt_global_reset_unlock ( gt ) ;
live_mocs_fini ( & mocs ) ;
return err ;
}
int intel_mocs_live_selftests ( struct drm_i915_private * i915 )
{
static const struct i915_subtest tests [ ] = {
SUBTEST ( live_mocs_kernel ) ,
SUBTEST ( live_mocs_clean ) ,
SUBTEST ( live_mocs_reset ) ,
} ;
struct drm_i915_mocs_table table ;
if ( ! get_mocs_settings ( i915 , & table ) )
return 0 ;
return intel_gt_live_subtests ( tests , & i915 - > gt ) ;
}