2017-02-13 17:15:32 +00:00
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE .
*
*/
# include <linux/prime_numbers.h>
# include "../i915_selftest.h"
# include "i915_random.h"
static int cpu_set ( struct drm_i915_gem_object * obj ,
unsigned long offset ,
u32 v )
{
unsigned int needs_clflush ;
struct page * page ;
2018-07-30 08:53:51 +01:00
void * map ;
u32 * cpu ;
2017-02-13 17:15:32 +00:00
int err ;
err = i915_gem_obj_prepare_shmem_write ( obj , & needs_clflush ) ;
if ( err )
return err ;
page = i915_gem_object_get_page ( obj , offset > > PAGE_SHIFT ) ;
map = kmap_atomic ( page ) ;
2018-07-30 08:53:51 +01:00
cpu = map + offset_in_page ( offset ) ;
2018-07-06 18:49:26 +01:00
2018-07-30 08:53:51 +01:00
if ( needs_clflush & CLFLUSH_BEFORE )
drm_clflush_virt_range ( cpu , sizeof ( * cpu ) ) ;
2018-07-06 18:49:26 +01:00
2018-07-30 08:53:51 +01:00
* cpu = v ;
2018-07-06 18:49:26 +01:00
2018-07-30 08:53:51 +01:00
if ( needs_clflush & CLFLUSH_AFTER )
drm_clflush_virt_range ( cpu , sizeof ( * cpu ) ) ;
2018-07-06 18:49:26 +01:00
2017-02-13 17:15:32 +00:00
kunmap_atomic ( map ) ;
i915_gem_obj_finish_shmem_access ( obj ) ;
2018-07-30 08:53:51 +01:00
2017-02-13 17:15:32 +00:00
return 0 ;
}
static int cpu_get ( struct drm_i915_gem_object * obj ,
unsigned long offset ,
u32 * v )
{
unsigned int needs_clflush ;
struct page * page ;
2018-07-30 08:53:51 +01:00
void * map ;
u32 * cpu ;
2017-02-13 17:15:32 +00:00
int err ;
err = i915_gem_obj_prepare_shmem_read ( obj , & needs_clflush ) ;
if ( err )
return err ;
page = i915_gem_object_get_page ( obj , offset > > PAGE_SHIFT ) ;
map = kmap_atomic ( page ) ;
2018-07-30 08:53:51 +01:00
cpu = map + offset_in_page ( offset ) ;
2018-07-06 18:49:26 +01:00
2018-07-30 08:53:51 +01:00
if ( needs_clflush & CLFLUSH_BEFORE )
drm_clflush_virt_range ( cpu , sizeof ( * cpu ) ) ;
2018-07-06 18:49:26 +01:00
2018-07-30 08:53:51 +01:00
* v = * cpu ;
2017-02-13 17:15:32 +00:00
2018-07-30 08:53:51 +01:00
kunmap_atomic ( map ) ;
2017-02-13 17:15:32 +00:00
i915_gem_obj_finish_shmem_access ( obj ) ;
2018-07-30 08:53:51 +01:00
2017-02-13 17:15:32 +00:00
return 0 ;
}
static int gtt_set ( struct drm_i915_gem_object * obj ,
unsigned long offset ,
u32 v )
{
struct i915_vma * vma ;
2017-11-14 19:18:42 +00:00
u32 __iomem * map ;
2017-02-13 17:15:32 +00:00
int err ;
err = i915_gem_object_set_to_gtt_domain ( obj , true ) ;
if ( err )
return err ;
vma = i915_gem_object_ggtt_pin ( obj , NULL , 0 , 0 , PIN_MAPPABLE ) ;
if ( IS_ERR ( vma ) )
return PTR_ERR ( vma ) ;
map = i915_vma_pin_iomap ( vma ) ;
i915_vma_unpin ( vma ) ;
if ( IS_ERR ( map ) )
return PTR_ERR ( map ) ;
2017-11-14 19:18:42 +00:00
iowrite32 ( v , & map [ offset / sizeof ( * map ) ] ) ;
2017-02-13 17:15:32 +00:00
i915_vma_unpin_iomap ( vma ) ;
return 0 ;
}
static int gtt_get ( struct drm_i915_gem_object * obj ,
unsigned long offset ,
u32 * v )
{
struct i915_vma * vma ;
2017-11-14 19:18:42 +00:00
u32 __iomem * map ;
2017-02-13 17:15:32 +00:00
int err ;
err = i915_gem_object_set_to_gtt_domain ( obj , false ) ;
if ( err )
return err ;
vma = i915_gem_object_ggtt_pin ( obj , NULL , 0 , 0 , PIN_MAPPABLE ) ;
if ( IS_ERR ( vma ) )
return PTR_ERR ( vma ) ;
map = i915_vma_pin_iomap ( vma ) ;
i915_vma_unpin ( vma ) ;
if ( IS_ERR ( map ) )
return PTR_ERR ( map ) ;
2017-11-14 19:18:42 +00:00
* v = ioread32 ( & map [ offset / sizeof ( * map ) ] ) ;
2017-02-13 17:15:32 +00:00
i915_vma_unpin_iomap ( vma ) ;
return 0 ;
}
static int wc_set ( struct drm_i915_gem_object * obj ,
unsigned long offset ,
u32 v )
{
2017-11-14 19:18:42 +00:00
u32 * map ;
2017-02-13 17:15:32 +00:00
int err ;
2017-04-12 12:01:11 +01:00
err = i915_gem_object_set_to_wc_domain ( obj , true ) ;
2017-02-13 17:15:32 +00:00
if ( err )
return err ;
map = i915_gem_object_pin_map ( obj , I915_MAP_WC ) ;
if ( IS_ERR ( map ) )
return PTR_ERR ( map ) ;
map [ offset / sizeof ( * map ) ] = v ;
i915_gem_object_unpin_map ( obj ) ;
return 0 ;
}
static int wc_get ( struct drm_i915_gem_object * obj ,
unsigned long offset ,
u32 * v )
{
2017-11-14 19:18:42 +00:00
u32 * map ;
2017-02-13 17:15:32 +00:00
int err ;
2017-04-12 12:01:11 +01:00
err = i915_gem_object_set_to_wc_domain ( obj , false ) ;
2017-02-13 17:15:32 +00:00
if ( err )
return err ;
map = i915_gem_object_pin_map ( obj , I915_MAP_WC ) ;
if ( IS_ERR ( map ) )
return PTR_ERR ( map ) ;
* v = map [ offset / sizeof ( * map ) ] ;
i915_gem_object_unpin_map ( obj ) ;
return 0 ;
}
static int gpu_set ( struct drm_i915_gem_object * obj ,
unsigned long offset ,
u32 v )
{
struct drm_i915_private * i915 = to_i915 ( obj - > base . dev ) ;
2018-02-21 09:56:36 +00:00
struct i915_request * rq ;
2017-02-13 17:15:32 +00:00
struct i915_vma * vma ;
2017-02-14 11:32:42 +00:00
u32 * cs ;
2017-02-13 17:15:32 +00:00
int err ;
err = i915_gem_object_set_to_gtt_domain ( obj , true ) ;
if ( err )
return err ;
vma = i915_gem_object_ggtt_pin ( obj , NULL , 0 , 0 , 0 ) ;
if ( IS_ERR ( vma ) )
return PTR_ERR ( vma ) ;
2019-03-05 18:03:30 +00:00
rq = i915_request_alloc ( i915 - > engine [ RCS0 ] , i915 - > kernel_context ) ;
2017-02-13 17:15:32 +00:00
if ( IS_ERR ( rq ) ) {
i915_vma_unpin ( vma ) ;
return PTR_ERR ( rq ) ;
}
2017-02-14 11:32:42 +00:00
cs = intel_ring_begin ( rq , 4 ) ;
if ( IS_ERR ( cs ) ) {
2018-06-12 11:51:35 +01:00
i915_request_add ( rq ) ;
2017-02-13 17:15:32 +00:00
i915_vma_unpin ( vma ) ;
2017-02-14 11:32:42 +00:00
return PTR_ERR ( cs ) ;
2017-02-13 17:15:32 +00:00
}
if ( INTEL_GEN ( i915 ) > = 8 ) {
2017-02-14 11:32:42 +00:00
* cs + + = MI_STORE_DWORD_IMM_GEN4 | 1 < < 22 ;
* cs + + = lower_32_bits ( i915_ggtt_offset ( vma ) + offset ) ;
* cs + + = upper_32_bits ( i915_ggtt_offset ( vma ) + offset ) ;
* cs + + = v ;
2017-02-13 17:15:32 +00:00
} else if ( INTEL_GEN ( i915 ) > = 4 ) {
2018-07-06 15:23:22 +01:00
* cs + + = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT ;
2017-02-14 11:32:42 +00:00
* cs + + = 0 ;
* cs + + = i915_ggtt_offset ( vma ) + offset ;
* cs + + = v ;
2017-02-13 17:15:32 +00:00
} else {
2018-07-06 15:23:22 +01:00
* cs + + = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL ;
2017-02-14 11:32:42 +00:00
* cs + + = i915_ggtt_offset ( vma ) + offset ;
* cs + + = v ;
* cs + + = MI_NOOP ;
2017-02-13 17:15:32 +00:00
}
2017-02-14 11:32:42 +00:00
intel_ring_advance ( rq , cs ) ;
2017-02-13 17:15:32 +00:00
2018-07-06 11:39:44 +01:00
err = i915_vma_move_to_active ( vma , rq , EXEC_OBJECT_WRITE ) ;
2017-02-13 17:15:32 +00:00
i915_vma_unpin ( vma ) ;
2018-06-12 11:51:35 +01:00
i915_request_add ( rq ) ;
2017-02-13 17:15:32 +00:00
2018-07-06 11:39:44 +01:00
return err ;
2017-02-13 17:15:32 +00:00
}
static bool always_valid ( struct drm_i915_private * i915 )
{
return true ;
}
2018-07-06 07:53:08 +01:00
static bool needs_fence_registers ( struct drm_i915_private * i915 )
{
2019-02-20 14:56:37 +00:00
return ! i915_terminally_wedged ( i915 ) ;
2018-07-06 07:53:08 +01:00
}
2017-02-13 17:15:32 +00:00
static bool needs_mi_store_dword ( struct drm_i915_private * i915 )
{
2019-02-20 14:56:37 +00:00
if ( i915_terminally_wedged ( i915 ) )
2018-07-06 07:53:08 +01:00
return false ;
2019-03-05 18:03:30 +00:00
return intel_engine_can_store_dword ( i915 - > engine [ RCS0 ] ) ;
2017-02-13 17:15:32 +00:00
}
static const struct igt_coherency_mode {
const char * name ;
int ( * set ) ( struct drm_i915_gem_object * , unsigned long offset , u32 v ) ;
int ( * get ) ( struct drm_i915_gem_object * , unsigned long offset , u32 * v ) ;
bool ( * valid ) ( struct drm_i915_private * i915 ) ;
} igt_coherency_mode [ ] = {
{ " cpu " , cpu_set , cpu_get , always_valid } ,
2018-07-06 07:53:08 +01:00
{ " gtt " , gtt_set , gtt_get , needs_fence_registers } ,
2017-02-13 17:15:32 +00:00
{ " wc " , wc_set , wc_get , always_valid } ,
{ " gpu " , gpu_set , NULL , needs_mi_store_dword } ,
{ } ,
} ;
static int igt_gem_coherency ( void * arg )
{
const unsigned int ncachelines = PAGE_SIZE / 64 ;
I915_RND_STATE ( prng ) ;
struct drm_i915_private * i915 = arg ;
const struct igt_coherency_mode * read , * write , * over ;
struct drm_i915_gem_object * obj ;
2019-01-14 14:21:22 +00:00
intel_wakeref_t wakeref ;
2017-02-13 17:15:32 +00:00
unsigned long count , n ;
u32 * offsets , * values ;
2017-02-14 14:35:09 +00:00
int err = 0 ;
2017-02-13 17:15:32 +00:00
/* We repeatedly write, overwrite and read from a sequence of
* cachelines in order to try and detect incoherency ( unflushed writes
* from either the CPU or GPU ) . Each setter / getter uses our cache
* domain API which should prevent incoherency .
*/
offsets = kmalloc_array ( ncachelines , 2 * sizeof ( u32 ) , GFP_KERNEL ) ;
if ( ! offsets )
return - ENOMEM ;
for ( count = 0 ; count < ncachelines ; count + + )
offsets [ count ] = count * 64 + 4 * ( count % 16 ) ;
values = offsets + ncachelines ;
mutex_lock ( & i915 - > drm . struct_mutex ) ;
2019-01-14 14:21:22 +00:00
wakeref = intel_runtime_pm_get ( i915 ) ;
2017-02-13 17:15:32 +00:00
for ( over = igt_coherency_mode ; over - > name ; over + + ) {
if ( ! over - > set )
continue ;
if ( ! over - > valid ( i915 ) )
continue ;
for ( write = igt_coherency_mode ; write - > name ; write + + ) {
if ( ! write - > set )
continue ;
if ( ! write - > valid ( i915 ) )
continue ;
for ( read = igt_coherency_mode ; read - > name ; read + + ) {
if ( ! read - > get )
continue ;
if ( ! read - > valid ( i915 ) )
continue ;
for_each_prime_number_from ( count , 1 , ncachelines ) {
obj = i915_gem_object_create_internal ( i915 , PAGE_SIZE ) ;
if ( IS_ERR ( obj ) ) {
err = PTR_ERR ( obj ) ;
goto unlock ;
}
i915_random_reorder ( offsets , ncachelines , & prng ) ;
for ( n = 0 ; n < count ; n + + )
values [ n ] = prandom_u32_state ( & prng ) ;
for ( n = 0 ; n < count ; n + + ) {
err = over - > set ( obj , offsets [ n ] , ~ values [ n ] ) ;
if ( err ) {
pr_err ( " Failed to set stale value[%ld/%ld] in object using %s, err=%d \n " ,
n , count , over - > name , err ) ;
goto put_object ;
}
}
for ( n = 0 ; n < count ; n + + ) {
err = write - > set ( obj , offsets [ n ] , values [ n ] ) ;
if ( err ) {
pr_err ( " Failed to set value[%ld/%ld] in object using %s, err=%d \n " ,
n , count , write - > name , err ) ;
goto put_object ;
}
}
for ( n = 0 ; n < count ; n + + ) {
u32 found ;
err = read - > get ( obj , offsets [ n ] , & found ) ;
if ( err ) {
pr_err ( " Failed to get value[%ld/%ld] in object using %s, err=%d \n " ,
n , count , read - > name , err ) ;
goto put_object ;
}
if ( found ! = values [ n ] ) {
pr_err ( " Value[%ld/%ld] mismatch, (overwrite with %s) wrote [%s] %x read [%s] %x (inverse %x), at offset %x \n " ,
n , count , over - > name ,
write - > name , values [ n ] ,
read - > name , found ,
~ values [ n ] , offsets [ n ] ) ;
err = - EINVAL ;
goto put_object ;
}
}
__i915_gem_object_release_unless_active ( obj ) ;
}
}
}
}
unlock :
2019-01-14 14:21:22 +00:00
intel_runtime_pm_put ( i915 , wakeref ) ;
2017-02-13 17:15:32 +00:00
mutex_unlock ( & i915 - > drm . struct_mutex ) ;
kfree ( offsets ) ;
return err ;
put_object :
__i915_gem_object_release_unless_active ( obj ) ;
goto unlock ;
}
int i915_gem_coherency_live_selftests ( struct drm_i915_private * i915 )
{
static const struct i915_subtest tests [ ] = {
SUBTEST ( igt_gem_coherency ) ,
} ;
return i915_subtests ( tests , i915 ) ;
}