2015-04-20 16:55:21 -04:00
/*
* Copyright 2008 Advanced Micro Devices , Inc .
* Copyright 2008 Red Hat Inc .
* Copyright 2009 Jerome Glisse .
*
* 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 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 COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) 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 .
*
* Authors : Dave Airlie
* Alex Deucher
* Jerome Glisse
*/
# include <drm/drmP.h>
# include <drm/amdgpu_drm.h>
# include "amdgpu.h"
# include "amdgpu_trace.h"
/*
* GPUVM
* GPUVM is similar to the legacy gart on older asics , however
* rather than there being a single global gart table
* for the entire GPU , there are multiple VM page tables active
* at any given time . The VM page tables can contain a mix
* vram pages and system memory pages and system memory pages
* can be mapped as snooped ( cached system pages ) or unsnooped
* ( uncached system pages ) .
* Each VM has an ID associated with it and there is a page table
* associated with each VMID . When execting a command buffer ,
* the kernel tells the the ring what VMID to use for that command
* buffer . VMIDs are allocated dynamically as commands are submitted .
* The userspace drivers maintain their own address space and the kernel
* sets up their pages tables accordingly when they submit their
* command buffers and a VMID is assigned .
* Cayman / Trinity support up to 8 active VMs at any given time ;
* SI supports 16.
*/
/**
* amdgpu_vm_num_pde - return the number of page directory entries
*
* @ adev : amdgpu_device pointer
*
2016-01-26 12:17:11 +01:00
* Calculate the number of page directory entries .
2015-04-20 16:55:21 -04:00
*/
static unsigned amdgpu_vm_num_pdes ( struct amdgpu_device * adev )
{
return adev - > vm_manager . max_pfn > > amdgpu_vm_block_size ;
}
/**
* amdgpu_vm_directory_size - returns the size of the page directory in bytes
*
* @ adev : amdgpu_device pointer
*
2016-01-26 12:17:11 +01:00
* Calculate the size of the page directory in bytes .
2015-04-20 16:55:21 -04:00
*/
static unsigned amdgpu_vm_directory_size ( struct amdgpu_device * adev )
{
return AMDGPU_GPU_PAGE_ALIGN ( amdgpu_vm_num_pdes ( adev ) * 8 ) ;
}
/**
2015-12-11 15:16:32 +01:00
* amdgpu_vm_get_pd_bo - add the VM PD to a validation list
2015-04-20 16:55:21 -04:00
*
* @ vm : vm providing the BOs
2015-12-11 14:39:05 +01:00
* @ validated : head of validation list
2015-12-11 15:16:32 +01:00
* @ entry : entry to add
2015-04-20 16:55:21 -04:00
*
* Add the page directory to the list of BOs to
2015-12-11 15:16:32 +01:00
* validate for command submission .
2015-04-20 16:55:21 -04:00
*/
2015-12-11 15:16:32 +01:00
void amdgpu_vm_get_pd_bo ( struct amdgpu_vm * vm ,
struct list_head * validated ,
struct amdgpu_bo_list_entry * entry )
2015-04-20 16:55:21 -04:00
{
2015-12-11 15:16:32 +01:00
entry - > robj = vm - > page_directory ;
entry - > priority = 0 ;
entry - > tv . bo = & vm - > page_directory - > tbo ;
entry - > tv . shared = true ;
list_add ( & entry - > tv . head , validated ) ;
}
2015-04-20 16:55:21 -04:00
2015-12-11 15:16:32 +01:00
/**
2015-12-11 21:01:23 +01:00
* amdgpu_vm_get_bos - add the vm BOs to a duplicates list
2015-12-11 15:16:32 +01:00
*
* @ vm : vm providing the BOs
2015-12-11 14:39:05 +01:00
* @ duplicates : head of duplicates list
2015-04-20 16:55:21 -04:00
*
2015-12-11 21:01:23 +01:00
* Add the page directory to the BO duplicates list
* for command submission .
2015-04-20 16:55:21 -04:00
*/
2015-12-11 21:01:23 +01:00
void amdgpu_vm_get_pt_bos ( struct amdgpu_vm * vm , struct list_head * duplicates )
2015-04-20 16:55:21 -04:00
{
2015-12-11 21:01:23 +01:00
unsigned i ;
2015-04-20 16:55:21 -04:00
/* add the vm page table to the list */
2015-12-11 21:01:23 +01:00
for ( i = 0 ; i < = vm - > max_pde_used ; + + i ) {
struct amdgpu_bo_list_entry * entry = & vm - > page_tables [ i ] . entry ;
if ( ! entry - > robj )
2015-04-20 16:55:21 -04:00
continue ;
2015-12-11 21:01:23 +01:00
list_add ( & entry - > tv . head , duplicates ) ;
2015-04-20 16:55:21 -04:00
}
2016-01-11 15:35:21 +01:00
}
/**
* amdgpu_vm_move_pt_bos_in_lru - move the PT BOs to the LRU tail
*
* @ adev : amdgpu device instance
* @ vm : vm providing the BOs
*
* Move the PT BOs to the tail of the LRU .
*/
void amdgpu_vm_move_pt_bos_in_lru ( struct amdgpu_device * adev ,
struct amdgpu_vm * vm )
{
struct ttm_bo_global * glob = adev - > mman . bdev . glob ;
unsigned i ;
spin_lock ( & glob - > lru_lock ) ;
for ( i = 0 ; i < = vm - > max_pde_used ; + + i ) {
struct amdgpu_bo_list_entry * entry = & vm - > page_tables [ i ] . entry ;
if ( ! entry - > robj )
continue ;
ttm_bo_move_to_lru_tail ( & entry - > robj - > tbo ) ;
}
spin_unlock ( & glob - > lru_lock ) ;
2015-04-20 16:55:21 -04:00
}
/**
* amdgpu_vm_grab_id - allocate the next free VMID
*
* @ vm : vm to allocate id for
2015-07-20 16:09:40 +02:00
* @ ring : ring we want to submit job to
* @ sync : sync object where we add dependencies
2016-01-18 17:01:42 +01:00
* @ fence : fence protecting ID from reuse
2015-04-20 16:55:21 -04:00
*
2015-07-20 16:09:40 +02:00
* Allocate an id for the vm , adding fences to the sync obj as necessary .
2015-04-20 16:55:21 -04:00
*/
2015-07-20 16:09:40 +02:00
int amdgpu_vm_grab_id ( struct amdgpu_vm * vm , struct amdgpu_ring * ring ,
2016-01-18 17:01:42 +01:00
struct amdgpu_sync * sync , struct fence * fence )
2015-04-20 16:55:21 -04:00
{
struct amdgpu_vm_id * vm_id = & vm - > ids [ ring - > idx ] ;
struct amdgpu_device * adev = ring - > adev ;
2016-01-21 10:19:11 +01:00
struct amdgpu_vm_manager_id * id ;
int r ;
2015-04-20 16:55:21 -04:00
2016-01-18 17:01:42 +01:00
mutex_lock ( & adev - > vm_manager . lock ) ;
2015-04-20 16:55:21 -04:00
/* check if the id is still valid */
2015-11-14 21:31:40 +01:00
if ( vm_id - > id ) {
long owner ;
2016-01-21 10:19:11 +01:00
id = & adev - > vm_manager . ids [ vm_id - > id ] ;
owner = atomic_long_read ( & id - > owner ) ;
2015-11-14 21:31:40 +01:00
if ( owner = = ( long ) vm ) {
2016-01-21 10:19:11 +01:00
list_move_tail ( & id - > list , & adev - > vm_manager . ids_lru ) ;
2016-01-07 18:15:22 +01:00
trace_amdgpu_vm_grab_id ( vm , vm_id - > id , ring - > idx ) ;
2015-04-20 16:55:21 -04:00
2016-01-21 10:19:11 +01:00
fence_put ( id - > active ) ;
id - > active = fence_get ( fence ) ;
2015-04-20 16:55:21 -04:00
2016-01-18 17:01:42 +01:00
mutex_unlock ( & adev - > vm_manager . lock ) ;
2015-07-20 16:09:40 +02:00
return 0 ;
2015-04-20 16:55:21 -04:00
}
}
2016-01-21 10:19:11 +01:00
/* we definately need to flush */
vm_id - > pd_gpu_addr = ~ 0ll ;
2015-07-20 16:09:40 +02:00
2016-01-21 10:19:11 +01:00
id = list_first_entry ( & adev - > vm_manager . ids_lru ,
struct amdgpu_vm_manager_id ,
list ) ;
list_move_tail ( & id - > list , & adev - > vm_manager . ids_lru ) ;
atomic_long_set ( & id - > owner , ( long ) vm ) ;
2016-01-18 17:01:42 +01:00
2016-01-21 10:19:11 +01:00
vm_id - > id = id - adev - > vm_manager . ids ;
trace_amdgpu_vm_grab_id ( vm , vm_id - > id , ring - > idx ) ;
2016-01-18 17:01:42 +01:00
2016-01-21 10:19:11 +01:00
r = amdgpu_sync_fence ( ring - > adev , sync , id - > active ) ;
2016-01-18 17:01:42 +01:00
2016-01-21 10:19:11 +01:00
if ( ! r ) {
fence_put ( id - > active ) ;
id - > active = fence_get ( fence ) ;
2015-04-20 16:55:21 -04:00
}
2016-01-18 17:01:42 +01:00
mutex_unlock ( & adev - > vm_manager . lock ) ;
2016-01-21 10:19:11 +01:00
return r ;
2015-04-20 16:55:21 -04:00
}
/**
* amdgpu_vm_flush - hardware flush the vm
*
* @ ring : ring to use for flush
* @ vm : vm we want to flush
* @ updates : last vm update that we waited for
*
2016-01-26 12:17:11 +01:00
* Flush the vm .
2015-04-20 16:55:21 -04:00
*/
void amdgpu_vm_flush ( struct amdgpu_ring * ring ,
struct amdgpu_vm * vm ,
2015-08-20 18:33:59 +08:00
struct fence * updates )
2015-04-20 16:55:21 -04:00
{
uint64_t pd_addr = amdgpu_bo_gpu_offset ( vm - > page_directory ) ;
struct amdgpu_vm_id * vm_id = & vm - > ids [ ring - > idx ] ;
2015-08-20 18:33:59 +08:00
struct fence * flushed_updates = vm_id - > flushed_updates ;
2015-10-29 17:01:19 +01:00
bool is_later ;
2015-08-20 18:33:59 +08:00
2015-10-29 17:01:19 +01:00
if ( ! flushed_updates )
is_later = true ;
else if ( ! updates )
is_later = false ;
else
is_later = fence_is_later ( updates , flushed_updates ) ;
2015-04-20 16:55:21 -04:00
2015-10-29 17:01:19 +01:00
if ( pd_addr ! = vm_id - > pd_gpu_addr | | is_later ) {
2015-04-20 16:55:21 -04:00
trace_amdgpu_vm_flush ( pd_addr , ring - > idx , vm_id - > id ) ;
2015-10-29 17:01:19 +01:00
if ( is_later ) {
2015-08-20 18:33:59 +08:00
vm_id - > flushed_updates = fence_get ( updates ) ;
fence_put ( flushed_updates ) ;
}
2015-04-20 16:55:21 -04:00
vm_id - > pd_gpu_addr = pd_addr ;
amdgpu_ring_emit_vm_flush ( ring , vm_id - > id , vm_id - > pd_gpu_addr ) ;
}
}
/**
* amdgpu_vm_bo_find - find the bo_va for a specific vm & bo
*
* @ vm : requested vm
* @ bo : requested buffer object
*
2016-01-26 12:17:11 +01:00
* Find @ bo inside the requested vm .
2015-04-20 16:55:21 -04:00
* Search inside the @ bos vm list for the requested vm
* Returns the found bo_va or NULL if none is found
*
* Object has to be reserved !
*/
struct amdgpu_bo_va * amdgpu_vm_bo_find ( struct amdgpu_vm * vm ,
struct amdgpu_bo * bo )
{
struct amdgpu_bo_va * bo_va ;
list_for_each_entry ( bo_va , & bo - > va , bo_list ) {
if ( bo_va - > vm = = vm ) {
return bo_va ;
}
}
return NULL ;
}
/**
* amdgpu_vm_update_pages - helper to call the right asic function
*
* @ adev : amdgpu_device pointer
2015-11-30 14:19:26 +01:00
* @ gtt : GART instance to use for mapping
* @ gtt_flags : GTT hw access flags
2015-04-20 16:55:21 -04:00
* @ ib : indirect buffer to fill with commands
* @ pe : addr of the page entry
* @ addr : dst addr to write into pe
* @ count : number of page entries to update
* @ incr : increase next addr by incr bytes
* @ flags : hw access flags
*
* Traces the parameters and calls the right asic functions
* to setup the page table using the DMA .
*/
static void amdgpu_vm_update_pages ( struct amdgpu_device * adev ,
2015-11-30 14:19:26 +01:00
struct amdgpu_gart * gtt ,
uint32_t gtt_flags ,
2015-04-20 16:55:21 -04:00
struct amdgpu_ib * ib ,
uint64_t pe , uint64_t addr ,
unsigned count , uint32_t incr ,
2015-11-30 14:19:26 +01:00
uint32_t flags )
2015-04-20 16:55:21 -04:00
{
trace_amdgpu_vm_set_page ( pe , addr , count , incr , flags ) ;
2015-11-30 14:19:26 +01:00
if ( ( gtt = = & adev - > gart ) & & ( flags = = gtt_flags ) ) {
uint64_t src = gtt - > table_addr + ( addr > > 12 ) * 8 ;
2015-04-20 16:55:21 -04:00
amdgpu_vm_copy_pte ( adev , ib , pe , src , count ) ;
2015-11-30 14:19:26 +01:00
} else if ( gtt ) {
dma_addr_t * pages_addr = gtt - > pages_addr ;
2015-11-30 13:26:07 +01:00
amdgpu_vm_write_pte ( adev , ib , pages_addr , pe , addr ,
count , incr , flags ) ;
} else if ( count < 3 ) {
amdgpu_vm_write_pte ( adev , ib , NULL , pe , addr ,
count , incr , flags ) ;
2015-04-20 16:55:21 -04:00
} else {
amdgpu_vm_set_pte_pde ( adev , ib , pe , addr ,
count , incr , flags ) ;
}
}
/**
* amdgpu_vm_clear_bo - initially clear the page dir / table
*
* @ adev : amdgpu_device pointer
* @ bo : bo to clear
2015-11-13 13:43:22 +08:00
*
* need to reserve bo first before calling it .
2015-04-20 16:55:21 -04:00
*/
static int amdgpu_vm_clear_bo ( struct amdgpu_device * adev ,
2016-02-01 12:53:58 +01:00
struct amdgpu_vm * vm ,
2015-04-20 16:55:21 -04:00
struct amdgpu_bo * bo )
{
struct amdgpu_ring * ring = adev - > vm_manager . vm_pte_funcs_ring ;
2015-08-03 12:57:31 +08:00
struct fence * fence = NULL ;
2016-02-01 12:20:25 +01:00
struct amdgpu_job * job ;
2015-04-20 16:55:21 -04:00
unsigned entries ;
uint64_t addr ;
int r ;
2015-05-25 14:44:05 +08:00
r = reservation_object_reserve_shared ( bo - > tbo . resv ) ;
if ( r )
return r ;
2015-04-20 16:55:21 -04:00
r = ttm_bo_validate ( & bo - > tbo , & bo - > placement , true , false ) ;
if ( r )
2015-11-13 13:43:22 +08:00
goto error ;
2015-04-20 16:55:21 -04:00
addr = amdgpu_bo_gpu_offset ( bo ) ;
entries = amdgpu_bo_size ( bo ) / 8 ;
2016-02-01 12:20:25 +01:00
r = amdgpu_job_alloc_with_ib ( adev , 64 , & job ) ;
if ( r )
2015-11-13 13:43:22 +08:00
goto error ;
2015-04-20 16:55:21 -04:00
2016-02-01 12:20:25 +01:00
amdgpu_vm_update_pages ( adev , NULL , 0 , & job - > ibs [ 0 ] , addr , 0 , entries ,
0 , 0 ) ;
amdgpu_ring_pad_ib ( ring , & job - > ibs [ 0 ] ) ;
WARN_ON ( job - > ibs [ 0 ] . length_dw > 64 ) ;
2016-02-01 12:53:58 +01:00
r = amdgpu_job_submit ( job , ring , & vm - > entity ,
AMDGPU_FENCE_OWNER_VM , & fence ) ;
2015-04-20 16:55:21 -04:00
if ( r )
goto error_free ;
2016-02-01 12:20:25 +01:00
amdgpu_bo_fence ( bo , fence , true ) ;
2015-08-12 12:58:31 +08:00
fence_put ( fence ) ;
2016-01-15 11:25:00 +08:00
return 0 ;
2015-11-13 13:43:22 +08:00
2015-04-20 16:55:21 -04:00
error_free :
2016-02-01 12:20:25 +01:00
amdgpu_job_free ( job ) ;
2015-04-20 16:55:21 -04:00
2015-11-13 13:43:22 +08:00
error :
2015-04-20 16:55:21 -04:00
return r ;
}
/**
2015-11-30 13:26:07 +01:00
* amdgpu_vm_map_gart - Resolve gart mapping of addr
2015-04-20 16:55:21 -04:00
*
2015-11-30 13:26:07 +01:00
* @ pages_addr : optional DMA address to use for lookup
2015-04-20 16:55:21 -04:00
* @ addr : the unmapped addr
*
* Look up the physical address of the page that the pte resolves
2015-11-30 13:26:07 +01:00
* to and return the pointer for the page table entry .
2015-04-20 16:55:21 -04:00
*/
2015-11-30 13:26:07 +01:00
uint64_t amdgpu_vm_map_gart ( const dma_addr_t * pages_addr , uint64_t addr )
2015-04-20 16:55:21 -04:00
{
uint64_t result ;
2015-11-30 13:26:07 +01:00
if ( pages_addr ) {
/* page table offset */
result = pages_addr [ addr > > PAGE_SHIFT ] ;
/* in case cpu page size != gpu page size*/
result | = addr & ( ~ PAGE_MASK ) ;
} else {
/* No mapping required */
result = addr ;
}
2015-04-20 16:55:21 -04:00
2015-11-30 13:26:07 +01:00
result & = 0xFFFFFFFFFFFFF000ULL ;
2015-04-20 16:55:21 -04:00
return result ;
}
/**
* amdgpu_vm_update_pdes - make sure that page directory is valid
*
* @ adev : amdgpu_device pointer
* @ vm : requested vm
* @ start : start of GPU address range
* @ end : end of GPU address range
*
* Allocates new page tables if necessary
2016-01-26 12:17:11 +01:00
* and updates the page directory .
2015-04-20 16:55:21 -04:00
* Returns 0 for success , error for failure .
*/
int amdgpu_vm_update_page_directory ( struct amdgpu_device * adev ,
struct amdgpu_vm * vm )
{
struct amdgpu_ring * ring = adev - > vm_manager . vm_pte_funcs_ring ;
struct amdgpu_bo * pd = vm - > page_directory ;
uint64_t pd_addr = amdgpu_bo_gpu_offset ( pd ) ;
uint32_t incr = AMDGPU_VM_PTE_COUNT * 8 ;
uint64_t last_pde = ~ 0 , last_pt = ~ 0 ;
unsigned count = 0 , pt_idx , ndw ;
2016-02-01 12:20:25 +01:00
struct amdgpu_job * job ;
2015-07-21 16:52:10 +08:00
struct amdgpu_ib * ib ;
2015-08-03 12:57:31 +08:00
struct fence * fence = NULL ;
2015-07-21 16:52:10 +08:00
2015-04-20 16:55:21 -04:00
int r ;
/* padding, etc. */
ndw = 64 ;
/* assume the worst case */
ndw + = vm - > max_pde_used * 6 ;
2016-02-01 12:20:25 +01:00
r = amdgpu_job_alloc_with_ib ( adev , ndw * 4 , & job ) ;
if ( r )
2015-04-20 16:55:21 -04:00
return r ;
2016-02-01 12:20:25 +01:00
ib = & job - > ibs [ 0 ] ;
2015-04-20 16:55:21 -04:00
/* walk over the address space and update the page directory */
for ( pt_idx = 0 ; pt_idx < = vm - > max_pde_used ; + + pt_idx ) {
2015-12-11 21:01:23 +01:00
struct amdgpu_bo * bo = vm - > page_tables [ pt_idx ] . entry . robj ;
2015-04-20 16:55:21 -04:00
uint64_t pde , pt ;
if ( bo = = NULL )
continue ;
pt = amdgpu_bo_gpu_offset ( bo ) ;
if ( vm - > page_tables [ pt_idx ] . addr = = pt )
continue ;
vm - > page_tables [ pt_idx ] . addr = pt ;
pde = pd_addr + pt_idx * 8 ;
if ( ( ( last_pde + 8 * count ) ! = pde ) | |
( ( last_pt + incr * count ) ! = pt ) ) {
if ( count ) {
2015-11-30 14:19:26 +01:00
amdgpu_vm_update_pages ( adev , NULL , 0 , ib ,
last_pde , last_pt ,
count , incr ,
AMDGPU_PTE_VALID ) ;
2015-04-20 16:55:21 -04:00
}
count = 1 ;
last_pde = pde ;
last_pt = pt ;
} else {
+ + count ;
}
}
if ( count )
2015-11-30 14:19:26 +01:00
amdgpu_vm_update_pages ( adev , NULL , 0 , ib , last_pde , last_pt ,
count , incr , AMDGPU_PTE_VALID ) ;
2015-04-20 16:55:21 -04:00
2015-07-21 16:52:10 +08:00
if ( ib - > length_dw ! = 0 ) {
2016-01-31 12:20:55 +01:00
amdgpu_ring_pad_ib ( ring , ib ) ;
2016-02-08 12:13:05 +01:00
amdgpu_sync_resv ( adev , & job - > sync , pd - > tbo . resv ,
AMDGPU_FENCE_OWNER_VM ) ;
2015-07-21 16:52:10 +08:00
WARN_ON ( ib - > length_dw > ndw ) ;
2016-02-01 12:53:58 +01:00
r = amdgpu_job_submit ( job , ring , & vm - > entity ,
AMDGPU_FENCE_OWNER_VM , & fence ) ;
2015-08-03 12:57:31 +08:00
if ( r )
goto error_free ;
2015-08-14 20:08:40 +02:00
2015-08-03 12:57:31 +08:00
amdgpu_bo_fence ( pd , fence , true ) ;
2015-08-14 20:08:40 +02:00
fence_put ( vm - > page_directory_fence ) ;
vm - > page_directory_fence = fence_get ( fence ) ;
2015-08-12 12:58:31 +08:00
fence_put ( fence ) ;
2015-07-21 16:52:10 +08:00
2016-02-01 12:20:25 +01:00
} else {
amdgpu_job_free ( job ) ;
2015-07-21 16:52:10 +08:00
}
2015-04-20 16:55:21 -04:00
return 0 ;
2015-07-21 16:52:10 +08:00
error_free :
2016-02-01 12:20:25 +01:00
amdgpu_job_free ( job ) ;
2015-08-03 12:57:31 +08:00
return r ;
2015-04-20 16:55:21 -04:00
}
/**
* amdgpu_vm_frag_ptes - add fragment information to PTEs
*
* @ adev : amdgpu_device pointer
2015-11-30 14:19:26 +01:00
* @ gtt : GART instance to use for mapping
* @ gtt_flags : GTT hw mapping flags
2015-04-20 16:55:21 -04:00
* @ ib : IB for the update
* @ pe_start : first PTE to handle
* @ pe_end : last PTE to handle
* @ addr : addr those PTEs should point to
* @ flags : hw mapping flags
*/
static void amdgpu_vm_frag_ptes ( struct amdgpu_device * adev ,
2015-11-30 14:19:26 +01:00
struct amdgpu_gart * gtt ,
uint32_t gtt_flags ,
2015-04-20 16:55:21 -04:00
struct amdgpu_ib * ib ,
uint64_t pe_start , uint64_t pe_end ,
2015-11-30 14:19:26 +01:00
uint64_t addr , uint32_t flags )
2015-04-20 16:55:21 -04:00
{
/**
* The MC L1 TLB supports variable sized pages , based on a fragment
* field in the PTE . When this field is set to a non - zero value , page
* granularity is increased from 4 KB to ( 1 < < ( 12 + frag ) ) . The PTE
* flags are considered valid for all PTEs within the fragment range
* and corresponding mappings are assumed to be physically contiguous .
*
* The L1 TLB can store a single PTE for the whole fragment ,
* significantly increasing the space available for translation
* caching . This leads to large improvements in throughput when the
* TLB is under pressure .
*
* The L2 TLB distributes small and large fragments into two
* asymmetric partitions . The large fragment cache is significantly
* larger . Thus , we try to use large fragments wherever possible .
* Userspace can support this by aligning virtual base address and
* allocation size to the fragment size .
*/
/* SI and newer are optimized for 64KB */
uint64_t frag_flags = AMDGPU_PTE_FRAG_64KB ;
uint64_t frag_align = 0x80 ;
uint64_t frag_start = ALIGN ( pe_start , frag_align ) ;
uint64_t frag_end = pe_end & ~ ( frag_align - 1 ) ;
unsigned count ;
2016-01-26 12:37:49 +01:00
/* Abort early if there isn't anything to do */
if ( pe_start = = pe_end )
return ;
2015-04-20 16:55:21 -04:00
/* system pages are non continuously */
2015-11-30 14:19:26 +01:00
if ( gtt | | ! ( flags & AMDGPU_PTE_VALID ) | | ( frag_start > = frag_end ) ) {
2015-04-20 16:55:21 -04:00
count = ( pe_end - pe_start ) / 8 ;
2015-11-30 14:19:26 +01:00
amdgpu_vm_update_pages ( adev , gtt , gtt_flags , ib , pe_start ,
addr , count , AMDGPU_GPU_PAGE_SIZE ,
flags ) ;
2015-04-20 16:55:21 -04:00
return ;
}
/* handle the 4K area at the beginning */
if ( pe_start ! = frag_start ) {
count = ( frag_start - pe_start ) / 8 ;
2015-11-30 14:19:26 +01:00
amdgpu_vm_update_pages ( adev , NULL , 0 , ib , pe_start , addr ,
count , AMDGPU_GPU_PAGE_SIZE , flags ) ;
2015-04-20 16:55:21 -04:00
addr + = AMDGPU_GPU_PAGE_SIZE * count ;
}
/* handle the area in the middle */
count = ( frag_end - frag_start ) / 8 ;
2015-11-30 14:19:26 +01:00
amdgpu_vm_update_pages ( adev , NULL , 0 , ib , frag_start , addr , count ,
AMDGPU_GPU_PAGE_SIZE , flags | frag_flags ) ;
2015-04-20 16:55:21 -04:00
/* handle the 4K area at the end */
if ( frag_end ! = pe_end ) {
addr + = AMDGPU_GPU_PAGE_SIZE * count ;
count = ( pe_end - frag_end ) / 8 ;
2015-11-30 14:19:26 +01:00
amdgpu_vm_update_pages ( adev , NULL , 0 , ib , frag_end , addr ,
count , AMDGPU_GPU_PAGE_SIZE , flags ) ;
2015-04-20 16:55:21 -04:00
}
}
/**
* amdgpu_vm_update_ptes - make sure that page tables are valid
*
* @ adev : amdgpu_device pointer
2015-11-30 14:19:26 +01:00
* @ gtt : GART instance to use for mapping
* @ gtt_flags : GTT hw mapping flags
2015-04-20 16:55:21 -04:00
* @ vm : requested vm
* @ start : start of GPU address range
* @ end : end of GPU address range
* @ dst : destination address to map to
* @ flags : mapping flags
*
2016-01-26 12:17:11 +01:00
* Update the page tables in the range @ start - @ end .
2015-04-20 16:55:21 -04:00
*/
2016-01-26 11:40:46 +01:00
static void amdgpu_vm_update_ptes ( struct amdgpu_device * adev ,
struct amdgpu_gart * gtt ,
uint32_t gtt_flags ,
struct amdgpu_vm * vm ,
struct amdgpu_ib * ib ,
uint64_t start , uint64_t end ,
uint64_t dst , uint32_t flags )
2015-04-20 16:55:21 -04:00
{
2016-01-26 12:37:49 +01:00
const uint64_t mask = AMDGPU_VM_PTE_COUNT - 1 ;
uint64_t last_pe_start = ~ 0 , last_pe_end = ~ 0 , last_dst = ~ 0 ;
2015-04-20 16:55:21 -04:00
uint64_t addr ;
/* walk over the address space and update the page tables */
for ( addr = start ; addr < end ; ) {
uint64_t pt_idx = addr > > amdgpu_vm_block_size ;
2015-12-11 21:01:23 +01:00
struct amdgpu_bo * pt = vm - > page_tables [ pt_idx ] . entry . robj ;
2015-04-20 16:55:21 -04:00
unsigned nptes ;
2016-01-26 12:37:49 +01:00
uint64_t pe_start ;
2015-04-20 16:55:21 -04:00
if ( ( addr & ~ mask ) = = ( end & ~ mask ) )
nptes = end - addr ;
else
nptes = AMDGPU_VM_PTE_COUNT - ( addr & mask ) ;
2016-01-26 12:37:49 +01:00
pe_start = amdgpu_bo_gpu_offset ( pt ) ;
pe_start + = ( addr & mask ) * 8 ;
2015-04-20 16:55:21 -04:00
2016-01-26 12:37:49 +01:00
if ( last_pe_end ! = pe_start ) {
2015-04-20 16:55:21 -04:00
2016-01-26 12:37:49 +01:00
amdgpu_vm_frag_ptes ( adev , gtt , gtt_flags , ib ,
last_pe_start , last_pe_end ,
last_dst , flags ) ;
2015-04-20 16:55:21 -04:00
2016-01-26 12:37:49 +01:00
last_pe_start = pe_start ;
last_pe_end = pe_start + 8 * nptes ;
2015-04-20 16:55:21 -04:00
last_dst = dst ;
} else {
2016-01-26 12:37:49 +01:00
last_pe_end + = 8 * nptes ;
2015-04-20 16:55:21 -04:00
}
addr + = nptes ;
dst + = nptes * AMDGPU_GPU_PAGE_SIZE ;
}
2016-01-26 12:37:49 +01:00
amdgpu_vm_frag_ptes ( adev , gtt , gtt_flags , ib ,
last_pe_start , last_pe_end ,
last_dst , flags ) ;
2015-04-20 16:55:21 -04:00
}
/**
* amdgpu_vm_bo_update_mapping - update a mapping in the vm page table
*
* @ adev : amdgpu_device pointer
2015-11-30 14:19:26 +01:00
* @ gtt : GART instance to use for mapping
2016-01-25 14:27:31 +01:00
* @ gtt_flags : flags as they are used for GTT
2015-04-20 16:55:21 -04:00
* @ vm : requested vm
2016-01-25 14:27:31 +01:00
* @ start : start of mapped range
* @ last : last mapped entry
* @ flags : flags for the entries
2015-04-20 16:55:21 -04:00
* @ addr : addr to set the area to
* @ fence : optional resulting fence
*
2016-01-25 14:27:31 +01:00
* Fill in the page table entries between @ start and @ last .
2015-04-20 16:55:21 -04:00
* Returns 0 for success , - EINVAL for failure .
*/
static int amdgpu_vm_bo_update_mapping ( struct amdgpu_device * adev ,
2015-11-30 14:19:26 +01:00
struct amdgpu_gart * gtt ,
uint32_t gtt_flags ,
2015-04-20 16:55:21 -04:00
struct amdgpu_vm * vm ,
2016-01-25 14:27:31 +01:00
uint64_t start , uint64_t last ,
uint32_t flags , uint64_t addr ,
struct fence * * fence )
2015-04-20 16:55:21 -04:00
{
struct amdgpu_ring * ring = adev - > vm_manager . vm_pte_funcs_ring ;
2016-01-26 11:40:46 +01:00
void * owner = AMDGPU_FENCE_OWNER_VM ;
2015-04-20 16:55:21 -04:00
unsigned nptes , ncmds , ndw ;
2016-02-01 12:20:25 +01:00
struct amdgpu_job * job ;
2015-07-21 16:52:10 +08:00
struct amdgpu_ib * ib ;
2015-08-03 12:57:31 +08:00
struct fence * f = NULL ;
2015-04-20 16:55:21 -04:00
int r ;
2016-01-26 11:40:46 +01:00
/* sync to everything on unmapping */
if ( ! ( flags & AMDGPU_PTE_VALID ) )
owner = AMDGPU_FENCE_OWNER_UNDEFINED ;
2016-01-25 14:27:31 +01:00
nptes = last - start + 1 ;
2015-04-20 16:55:21 -04:00
/*
* reserve space for one command every ( 1 < < BLOCK_SIZE )
* entries or 2 k dwords ( whatever is smaller )
*/
ncmds = ( nptes > > min ( amdgpu_vm_block_size , 11 ) ) + 1 ;
/* padding, etc. */
ndw = 64 ;
2015-11-30 14:19:26 +01:00
if ( ( gtt = = & adev - > gart ) & & ( flags = = gtt_flags ) ) {
2015-04-20 16:55:21 -04:00
/* only copy commands needed */
ndw + = ncmds * 7 ;
2015-11-30 14:19:26 +01:00
} else if ( gtt ) {
2015-04-20 16:55:21 -04:00
/* header for write data commands */
ndw + = ncmds * 4 ;
/* body of write data command */
ndw + = nptes * 2 ;
} else {
/* set page commands needed */
ndw + = ncmds * 10 ;
/* two extra commands for begin/end of fragment */
ndw + = 2 * 10 ;
}
2016-02-01 12:20:25 +01:00
r = amdgpu_job_alloc_with_ib ( adev , ndw * 4 , & job ) ;
if ( r )
2015-04-20 16:55:21 -04:00
return r ;
2016-02-01 12:20:25 +01:00
ib = & job - > ibs [ 0 ] ;
2015-07-21 16:52:10 +08:00
2016-02-08 12:13:05 +01:00
r = amdgpu_sync_resv ( adev , & job - > sync , vm - > page_directory - > tbo . resv ,
2016-01-26 11:40:46 +01:00
owner ) ;
if ( r )
goto error_free ;
2015-04-20 16:55:21 -04:00
2016-01-26 11:40:46 +01:00
r = reservation_object_reserve_shared ( vm - > page_directory - > tbo . resv ) ;
if ( r )
goto error_free ;
amdgpu_vm_update_ptes ( adev , gtt , gtt_flags , vm , ib , start , last + 1 ,
addr , flags ) ;
2015-04-20 16:55:21 -04:00
2016-01-31 12:20:55 +01:00
amdgpu_ring_pad_ib ( ring , ib ) ;
2015-07-21 16:52:10 +08:00
WARN_ON ( ib - > length_dw > ndw ) ;
2016-02-01 12:53:58 +01:00
r = amdgpu_job_submit ( job , ring , & vm - > entity ,
AMDGPU_FENCE_OWNER_VM , & f ) ;
2015-08-03 12:57:31 +08:00
if ( r )
goto error_free ;
2015-04-20 16:55:21 -04:00
2015-09-04 10:47:56 +02:00
amdgpu_bo_fence ( vm - > page_directory , f , true ) ;
2015-08-03 12:57:31 +08:00
if ( fence ) {
fence_put ( * fence ) ;
* fence = fence_get ( f ) ;
}
2015-08-12 12:58:31 +08:00
fence_put ( f ) ;
2015-04-20 16:55:21 -04:00
return 0 ;
2015-07-21 16:52:10 +08:00
error_free :
2016-02-01 12:20:25 +01:00
amdgpu_job_free ( job ) ;
2015-08-03 12:57:31 +08:00
return r ;
2015-04-20 16:55:21 -04:00
}
2016-01-25 14:27:31 +01:00
/**
* amdgpu_vm_bo_split_mapping - split a mapping into smaller chunks
*
* @ adev : amdgpu_device pointer
* @ gtt : GART instance to use for mapping
* @ vm : requested vm
* @ mapping : mapped range and flags to use for the update
* @ addr : addr to set the area to
* @ gtt_flags : flags as they are used for GTT
* @ fence : optional resulting fence
*
* Split the mapping into smaller chunks so that each update fits
* into a SDMA IB .
* Returns 0 for success , - EINVAL for failure .
*/
static int amdgpu_vm_bo_split_mapping ( struct amdgpu_device * adev ,
struct amdgpu_gart * gtt ,
uint32_t gtt_flags ,
struct amdgpu_vm * vm ,
struct amdgpu_bo_va_mapping * mapping ,
uint64_t addr , struct fence * * fence )
{
const uint64_t max_size = 64ULL * 1024ULL * 1024ULL / AMDGPU_GPU_PAGE_SIZE ;
uint64_t start = mapping - > it . start ;
uint32_t flags = gtt_flags ;
int r ;
/* normally,bo_va->flags only contians READABLE and WIRTEABLE bit go here
* but in case of something , we filter the flags in first place
*/
if ( ! ( mapping - > flags & AMDGPU_PTE_READABLE ) )
flags & = ~ AMDGPU_PTE_READABLE ;
if ( ! ( mapping - > flags & AMDGPU_PTE_WRITEABLE ) )
flags & = ~ AMDGPU_PTE_WRITEABLE ;
trace_amdgpu_vm_bo_update ( mapping ) ;
addr + = mapping - > offset ;
if ( ! gtt | | ( ( gtt = = & adev - > gart ) & & ( flags = = gtt_flags ) ) )
return amdgpu_vm_bo_update_mapping ( adev , gtt , gtt_flags , vm ,
start , mapping - > it . last ,
flags , addr , fence ) ;
while ( start ! = mapping - > it . last + 1 ) {
uint64_t last ;
last = min ( ( uint64_t ) mapping - > it . last , start + max_size ) ;
r = amdgpu_vm_bo_update_mapping ( adev , gtt , gtt_flags , vm ,
start , last , flags , addr ,
fence ) ;
if ( r )
return r ;
start = last + 1 ;
addr + = max_size ;
}
return 0 ;
}
2015-04-20 16:55:21 -04:00
/**
* amdgpu_vm_bo_update - update all BO mappings in the vm page table
*
* @ adev : amdgpu_device pointer
* @ bo_va : requested BO and VM object
* @ mem : ttm mem
*
* Fill in the page table entries for @ bo_va .
* Returns 0 for success , - EINVAL for failure .
*
* Object have to be reserved and mutex must be locked !
*/
int amdgpu_vm_bo_update ( struct amdgpu_device * adev ,
struct amdgpu_bo_va * bo_va ,
struct ttm_mem_reg * mem )
{
struct amdgpu_vm * vm = bo_va - > vm ;
struct amdgpu_bo_va_mapping * mapping ;
2015-11-30 14:19:26 +01:00
struct amdgpu_gart * gtt = NULL ;
2015-04-20 16:55:21 -04:00
uint32_t flags ;
uint64_t addr ;
int r ;
if ( mem ) {
2015-09-07 12:32:09 +02:00
addr = ( u64 ) mem - > start < < PAGE_SHIFT ;
2015-11-30 14:19:26 +01:00
switch ( mem - > mem_type ) {
case TTM_PL_TT :
gtt = & bo_va - > bo - > adev - > gart ;
break ;
case TTM_PL_VRAM :
2015-04-20 16:55:21 -04:00
addr + = adev - > vm_manager . vram_base_offset ;
2015-11-30 14:19:26 +01:00
break ;
default :
break ;
}
2015-04-20 16:55:21 -04:00
} else {
addr = 0 ;
}
flags = amdgpu_ttm_tt_pte_flags ( adev , bo_va - > bo - > tbo . ttm , mem ) ;
2015-07-30 11:53:42 +02:00
spin_lock ( & vm - > status_lock ) ;
if ( ! list_empty ( & bo_va - > vm_status ) )
list_splice_init ( & bo_va - > valids , & bo_va - > invalids ) ;
spin_unlock ( & vm - > status_lock ) ;
list_for_each_entry ( mapping , & bo_va - > invalids , list ) {
2016-01-25 14:27:31 +01:00
r = amdgpu_vm_bo_split_mapping ( adev , gtt , flags , vm , mapping , addr ,
& bo_va - > last_pt_update ) ;
2015-04-20 16:55:21 -04:00
if ( r )
return r ;
}
2015-09-28 12:00:23 +02:00
if ( trace_amdgpu_vm_bo_mapping_enabled ( ) ) {
list_for_each_entry ( mapping , & bo_va - > valids , list )
trace_amdgpu_vm_bo_mapping ( mapping ) ;
list_for_each_entry ( mapping , & bo_va - > invalids , list )
trace_amdgpu_vm_bo_mapping ( mapping ) ;
}
2015-04-20 16:55:21 -04:00
spin_lock ( & vm - > status_lock ) ;
2015-08-14 13:36:41 +08:00
list_splice_init ( & bo_va - > invalids , & bo_va - > valids ) ;
2015-04-20 16:55:21 -04:00
list_del_init ( & bo_va - > vm_status ) ;
2015-07-30 11:53:42 +02:00
if ( ! mem )
list_add ( & bo_va - > vm_status , & vm - > cleared ) ;
2015-04-20 16:55:21 -04:00
spin_unlock ( & vm - > status_lock ) ;
return 0 ;
}
/**
* amdgpu_vm_clear_freed - clear freed BOs in the PT
*
* @ adev : amdgpu_device pointer
* @ vm : requested vm
*
* Make sure all freed BOs are cleared in the PT .
* Returns 0 for success .
*
* PTs have to be reserved and mutex must be locked !
*/
int amdgpu_vm_clear_freed ( struct amdgpu_device * adev ,
struct amdgpu_vm * vm )
{
struct amdgpu_bo_va_mapping * mapping ;
int r ;
2015-12-04 17:17:00 +08:00
spin_lock ( & vm - > freed_lock ) ;
2015-04-20 16:55:21 -04:00
while ( ! list_empty ( & vm - > freed ) ) {
mapping = list_first_entry ( & vm - > freed ,
struct amdgpu_bo_va_mapping , list ) ;
list_del ( & mapping - > list ) ;
2015-12-04 17:17:00 +08:00
spin_unlock ( & vm - > freed_lock ) ;
2016-01-25 14:27:31 +01:00
r = amdgpu_vm_bo_split_mapping ( adev , NULL , 0 , vm , mapping ,
0 , NULL ) ;
2015-04-20 16:55:21 -04:00
kfree ( mapping ) ;
if ( r )
return r ;
2015-12-04 17:17:00 +08:00
spin_lock ( & vm - > freed_lock ) ;
2015-04-20 16:55:21 -04:00
}
2015-12-04 17:17:00 +08:00
spin_unlock ( & vm - > freed_lock ) ;
2015-04-20 16:55:21 -04:00
return 0 ;
}
/**
* amdgpu_vm_clear_invalids - clear invalidated BOs in the PT
*
* @ adev : amdgpu_device pointer
* @ vm : requested vm
*
* Make sure all invalidated BOs are cleared in the PT .
* Returns 0 for success .
*
* PTs have to be reserved and mutex must be locked !
*/
int amdgpu_vm_clear_invalids ( struct amdgpu_device * adev ,
2015-05-26 15:01:54 +08:00
struct amdgpu_vm * vm , struct amdgpu_sync * sync )
2015-04-20 16:55:21 -04:00
{
2015-05-26 15:01:54 +08:00
struct amdgpu_bo_va * bo_va = NULL ;
2015-07-06 22:06:40 +02:00
int r = 0 ;
2015-04-20 16:55:21 -04:00
spin_lock ( & vm - > status_lock ) ;
while ( ! list_empty ( & vm - > invalidated ) ) {
bo_va = list_first_entry ( & vm - > invalidated ,
struct amdgpu_bo_va , vm_status ) ;
spin_unlock ( & vm - > status_lock ) ;
2015-11-18 11:17:39 +08:00
mutex_lock ( & bo_va - > mutex ) ;
2015-04-20 16:55:21 -04:00
r = amdgpu_vm_bo_update ( adev , bo_va , NULL ) ;
2015-11-18 11:17:39 +08:00
mutex_unlock ( & bo_va - > mutex ) ;
2015-04-20 16:55:21 -04:00
if ( r )
return r ;
spin_lock ( & vm - > status_lock ) ;
}
spin_unlock ( & vm - > status_lock ) ;
2015-05-26 15:01:54 +08:00
if ( bo_va )
2015-08-03 18:19:38 +08:00
r = amdgpu_sync_fence ( adev , sync , bo_va - > last_pt_update ) ;
2015-07-06 22:06:40 +02:00
return r ;
2015-04-20 16:55:21 -04:00
}
/**
* amdgpu_vm_bo_add - add a bo to a specific vm
*
* @ adev : amdgpu_device pointer
* @ vm : requested vm
* @ bo : amdgpu buffer object
*
2016-01-26 12:17:11 +01:00
* Add @ bo into the requested vm .
2015-04-20 16:55:21 -04:00
* Add @ bo to the list of bos associated with the vm
* Returns newly added bo_va or NULL for failure
*
* Object has to be reserved !
*/
struct amdgpu_bo_va * amdgpu_vm_bo_add ( struct amdgpu_device * adev ,
struct amdgpu_vm * vm ,
struct amdgpu_bo * bo )
{
struct amdgpu_bo_va * bo_va ;
bo_va = kzalloc ( sizeof ( struct amdgpu_bo_va ) , GFP_KERNEL ) ;
if ( bo_va = = NULL ) {
return NULL ;
}
bo_va - > vm = vm ;
bo_va - > bo = bo ;
bo_va - > ref_count = 1 ;
INIT_LIST_HEAD ( & bo_va - > bo_list ) ;
2015-07-30 11:53:42 +02:00
INIT_LIST_HEAD ( & bo_va - > valids ) ;
INIT_LIST_HEAD ( & bo_va - > invalids ) ;
2015-04-20 16:55:21 -04:00
INIT_LIST_HEAD ( & bo_va - > vm_status ) ;
2015-11-18 11:17:39 +08:00
mutex_init ( & bo_va - > mutex ) ;
2015-04-20 16:55:21 -04:00
list_add_tail ( & bo_va - > bo_list , & bo - > va ) ;
return bo_va ;
}
/**
* amdgpu_vm_bo_map - map bo inside a vm
*
* @ adev : amdgpu_device pointer
* @ bo_va : bo_va to store the address
* @ saddr : where to map the BO
* @ offset : requested offset in the BO
* @ flags : attributes of pages ( read / write / valid / etc . )
*
* Add a mapping of the BO at the specefied addr into the VM .
* Returns 0 for success , error for failure .
*
2015-11-13 14:18:38 +08:00
* Object has to be reserved and unreserved outside !
2015-04-20 16:55:21 -04:00
*/
int amdgpu_vm_bo_map ( struct amdgpu_device * adev ,
struct amdgpu_bo_va * bo_va ,
uint64_t saddr , uint64_t offset ,
uint64_t size , uint32_t flags )
{
struct amdgpu_bo_va_mapping * mapping ;
struct amdgpu_vm * vm = bo_va - > vm ;
struct interval_tree_node * it ;
unsigned last_pfn , pt_idx ;
uint64_t eaddr ;
int r ;
2015-05-18 14:37:27 +02:00
/* validate the parameters */
if ( saddr & AMDGPU_GPU_PAGE_MASK | | offset & AMDGPU_GPU_PAGE_MASK | |
2015-11-13 14:18:38 +08:00
size = = 0 | | size & AMDGPU_GPU_PAGE_MASK )
2015-05-18 14:37:27 +02:00
return - EINVAL ;
2015-04-20 16:55:21 -04:00
/* make sure object fit at this offset */
2015-11-23 17:43:48 -05:00
eaddr = saddr + size - 1 ;
2015-11-13 14:18:38 +08:00
if ( ( saddr > = eaddr ) | | ( offset + size > amdgpu_bo_size ( bo_va - > bo ) ) )
2015-04-20 16:55:21 -04:00
return - EINVAL ;
last_pfn = eaddr / AMDGPU_GPU_PAGE_SIZE ;
2015-11-23 17:43:48 -05:00
if ( last_pfn > = adev - > vm_manager . max_pfn ) {
dev_err ( adev - > dev , " va above limit (0x%08X >= 0x%08X) \n " ,
2015-04-20 16:55:21 -04:00
last_pfn , adev - > vm_manager . max_pfn ) ;
return - EINVAL ;
}
saddr / = AMDGPU_GPU_PAGE_SIZE ;
eaddr / = AMDGPU_GPU_PAGE_SIZE ;
2015-11-13 13:32:01 +08:00
spin_lock ( & vm - > it_lock ) ;
2015-11-23 17:43:48 -05:00
it = interval_tree_iter_first ( & vm - > va , saddr , eaddr ) ;
2015-11-13 13:32:01 +08:00
spin_unlock ( & vm - > it_lock ) ;
2015-04-20 16:55:21 -04:00
if ( it ) {
struct amdgpu_bo_va_mapping * tmp ;
tmp = container_of ( it , struct amdgpu_bo_va_mapping , it ) ;
/* bo and tmp overlap, invalid addr */
dev_err ( adev - > dev , " bo %p va 0x%010Lx-0x%010Lx conflict with "
" 0x%010lx-0x%010lx \n " , bo_va - > bo , saddr , eaddr ,
tmp - > it . start , tmp - > it . last + 1 ) ;
r = - EINVAL ;
2015-10-16 14:06:19 +08:00
goto error ;
2015-04-20 16:55:21 -04:00
}
mapping = kmalloc ( sizeof ( * mapping ) , GFP_KERNEL ) ;
if ( ! mapping ) {
r = - ENOMEM ;
2015-10-16 14:06:19 +08:00
goto error ;
2015-04-20 16:55:21 -04:00
}
INIT_LIST_HEAD ( & mapping - > list ) ;
mapping - > it . start = saddr ;
2015-11-23 17:43:48 -05:00
mapping - > it . last = eaddr ;
2015-04-20 16:55:21 -04:00
mapping - > offset = offset ;
mapping - > flags = flags ;
2015-11-18 11:17:39 +08:00
mutex_lock ( & bo_va - > mutex ) ;
2015-07-30 11:53:42 +02:00
list_add ( & mapping - > list , & bo_va - > invalids ) ;
2015-11-18 11:17:39 +08:00
mutex_unlock ( & bo_va - > mutex ) ;
2015-11-13 13:32:01 +08:00
spin_lock ( & vm - > it_lock ) ;
2015-04-20 16:55:21 -04:00
interval_tree_insert ( & mapping - > it , & vm - > va ) ;
2015-11-13 13:32:01 +08:00
spin_unlock ( & vm - > it_lock ) ;
2015-06-09 16:58:33 +02:00
trace_amdgpu_vm_bo_map ( bo_va , mapping ) ;
2015-04-20 16:55:21 -04:00
/* Make sure the page tables are allocated */
saddr > > = amdgpu_vm_block_size ;
eaddr > > = amdgpu_vm_block_size ;
BUG_ON ( eaddr > = amdgpu_vm_num_pdes ( adev ) ) ;
if ( eaddr > vm - > max_pde_used )
vm - > max_pde_used = eaddr ;
/* walk over the address space and allocate the page tables */
for ( pt_idx = saddr ; pt_idx < = eaddr ; + + pt_idx ) {
2015-09-04 10:47:56 +02:00
struct reservation_object * resv = vm - > page_directory - > tbo . resv ;
2015-12-11 21:01:23 +01:00
struct amdgpu_bo_list_entry * entry ;
2015-04-20 16:55:21 -04:00
struct amdgpu_bo * pt ;
2015-12-11 21:01:23 +01:00
entry = & vm - > page_tables [ pt_idx ] . entry ;
if ( entry - > robj )
2015-04-20 16:55:21 -04:00
continue ;
r = amdgpu_bo_create ( adev , AMDGPU_VM_PTE_COUNT * 8 ,
AMDGPU_GPU_PAGE_SIZE , true ,
2015-08-27 00:14:16 -04:00
AMDGPU_GEM_DOMAIN_VRAM ,
AMDGPU_GEM_CREATE_NO_CPU_ACCESS ,
2015-09-04 10:47:56 +02:00
NULL , resv , & pt ) ;
2015-11-13 14:18:38 +08:00
if ( r )
2015-04-20 16:55:21 -04:00
goto error_free ;
2015-11-13 14:18:38 +08:00
2015-11-27 16:49:00 +01:00
/* Keep a reference to the page table to avoid freeing
* them up in the wrong order .
*/
pt - > parent = amdgpu_bo_ref ( vm - > page_directory ) ;
2016-02-01 12:53:58 +01:00
r = amdgpu_vm_clear_bo ( adev , vm , pt ) ;
2015-04-20 16:55:21 -04:00
if ( r ) {
amdgpu_bo_unref ( & pt ) ;
goto error_free ;
}
2015-12-11 21:01:23 +01:00
entry - > robj = pt ;
entry - > priority = 0 ;
entry - > tv . bo = & entry - > robj - > tbo ;
entry - > tv . shared = true ;
2015-04-20 16:55:21 -04:00
vm - > page_tables [ pt_idx ] . addr = 0 ;
}
return 0 ;
error_free :
list_del ( & mapping - > list ) ;
2015-11-13 13:32:01 +08:00
spin_lock ( & vm - > it_lock ) ;
2015-04-20 16:55:21 -04:00
interval_tree_remove ( & mapping - > it , & vm - > va ) ;
2015-11-13 13:32:01 +08:00
spin_unlock ( & vm - > it_lock ) ;
2015-06-09 16:58:33 +02:00
trace_amdgpu_vm_bo_unmap ( bo_va , mapping ) ;
2015-04-20 16:55:21 -04:00
kfree ( mapping ) ;
2015-10-16 14:06:19 +08:00
error :
2015-04-20 16:55:21 -04:00
return r ;
}
/**
* amdgpu_vm_bo_unmap - remove bo mapping from vm
*
* @ adev : amdgpu_device pointer
* @ bo_va : bo_va to remove the address from
* @ saddr : where to the BO is mapped
*
* Remove a mapping of the BO at the specefied addr from the VM .
* Returns 0 for success , error for failure .
*
2015-11-13 14:18:38 +08:00
* Object has to be reserved and unreserved outside !
2015-04-20 16:55:21 -04:00
*/
int amdgpu_vm_bo_unmap ( struct amdgpu_device * adev ,
struct amdgpu_bo_va * bo_va ,
uint64_t saddr )
{
struct amdgpu_bo_va_mapping * mapping ;
struct amdgpu_vm * vm = bo_va - > vm ;
2015-07-30 11:53:42 +02:00
bool valid = true ;
2015-04-20 16:55:21 -04:00
2015-06-05 20:56:17 +02:00
saddr / = AMDGPU_GPU_PAGE_SIZE ;
2015-11-18 11:17:39 +08:00
mutex_lock ( & bo_va - > mutex ) ;
2015-07-30 11:53:42 +02:00
list_for_each_entry ( mapping , & bo_va - > valids , list ) {
2015-04-20 16:55:21 -04:00
if ( mapping - > it . start = = saddr )
break ;
}
2015-07-30 11:53:42 +02:00
if ( & mapping - > list = = & bo_va - > valids ) {
valid = false ;
list_for_each_entry ( mapping , & bo_va - > invalids , list ) {
if ( mapping - > it . start = = saddr )
break ;
}
2015-11-18 11:17:39 +08:00
if ( & mapping - > list = = & bo_va - > invalids ) {
mutex_unlock ( & bo_va - > mutex ) ;
2015-07-30 11:53:42 +02:00
return - ENOENT ;
2015-11-18 11:17:39 +08:00
}
2015-04-20 16:55:21 -04:00
}
2015-11-18 11:17:39 +08:00
mutex_unlock ( & bo_va - > mutex ) ;
2015-04-20 16:55:21 -04:00
list_del ( & mapping - > list ) ;
2015-11-13 13:32:01 +08:00
spin_lock ( & vm - > it_lock ) ;
2015-04-20 16:55:21 -04:00
interval_tree_remove ( & mapping - > it , & vm - > va ) ;
2015-11-13 13:32:01 +08:00
spin_unlock ( & vm - > it_lock ) ;
2015-06-09 16:58:33 +02:00
trace_amdgpu_vm_bo_unmap ( bo_va , mapping ) ;
2015-04-20 16:55:21 -04:00
2015-12-04 17:17:00 +08:00
if ( valid ) {
spin_lock ( & vm - > freed_lock ) ;
2015-04-20 16:55:21 -04:00
list_add ( & mapping - > list , & vm - > freed ) ;
2015-12-04 17:17:00 +08:00
spin_unlock ( & vm - > freed_lock ) ;
} else {
2015-04-20 16:55:21 -04:00
kfree ( mapping ) ;
2015-12-04 17:17:00 +08:00
}
2015-04-20 16:55:21 -04:00
return 0 ;
}
/**
* amdgpu_vm_bo_rmv - remove a bo to a specific vm
*
* @ adev : amdgpu_device pointer
* @ bo_va : requested bo_va
*
2016-01-26 12:17:11 +01:00
* Remove @ bo_va - > bo from the requested vm .
2015-04-20 16:55:21 -04:00
*
* Object have to be reserved !
*/
void amdgpu_vm_bo_rmv ( struct amdgpu_device * adev ,
struct amdgpu_bo_va * bo_va )
{
struct amdgpu_bo_va_mapping * mapping , * next ;
struct amdgpu_vm * vm = bo_va - > vm ;
list_del ( & bo_va - > bo_list ) ;
spin_lock ( & vm - > status_lock ) ;
list_del ( & bo_va - > vm_status ) ;
spin_unlock ( & vm - > status_lock ) ;
2015-07-30 11:53:42 +02:00
list_for_each_entry_safe ( mapping , next , & bo_va - > valids , list ) {
2015-04-20 16:55:21 -04:00
list_del ( & mapping - > list ) ;
2015-11-13 13:32:01 +08:00
spin_lock ( & vm - > it_lock ) ;
2015-04-20 16:55:21 -04:00
interval_tree_remove ( & mapping - > it , & vm - > va ) ;
2015-11-13 13:32:01 +08:00
spin_unlock ( & vm - > it_lock ) ;
2015-06-09 16:58:33 +02:00
trace_amdgpu_vm_bo_unmap ( bo_va , mapping ) ;
2015-12-04 17:17:00 +08:00
spin_lock ( & vm - > freed_lock ) ;
2015-07-30 11:53:42 +02:00
list_add ( & mapping - > list , & vm - > freed ) ;
2015-12-04 17:17:00 +08:00
spin_unlock ( & vm - > freed_lock ) ;
2015-07-30 11:53:42 +02:00
}
list_for_each_entry_safe ( mapping , next , & bo_va - > invalids , list ) {
list_del ( & mapping - > list ) ;
2015-11-13 13:32:01 +08:00
spin_lock ( & vm - > it_lock ) ;
2015-07-30 11:53:42 +02:00
interval_tree_remove ( & mapping - > it , & vm - > va ) ;
2015-11-13 13:32:01 +08:00
spin_unlock ( & vm - > it_lock ) ;
2015-07-30 11:53:42 +02:00
kfree ( mapping ) ;
2015-04-20 16:55:21 -04:00
}
2015-08-03 18:19:38 +08:00
fence_put ( bo_va - > last_pt_update ) ;
2015-11-18 11:17:39 +08:00
mutex_destroy ( & bo_va - > mutex ) ;
2015-04-20 16:55:21 -04:00
kfree ( bo_va ) ;
}
/**
* amdgpu_vm_bo_invalidate - mark the bo as invalid
*
* @ adev : amdgpu_device pointer
* @ vm : requested vm
* @ bo : amdgpu buffer object
*
2016-01-26 12:17:11 +01:00
* Mark @ bo as invalid .
2015-04-20 16:55:21 -04:00
*/
void amdgpu_vm_bo_invalidate ( struct amdgpu_device * adev ,
struct amdgpu_bo * bo )
{
struct amdgpu_bo_va * bo_va ;
list_for_each_entry ( bo_va , & bo - > va , bo_list ) {
2015-07-30 11:53:42 +02:00
spin_lock ( & bo_va - > vm - > status_lock ) ;
if ( list_empty ( & bo_va - > vm_status ) )
2015-04-20 16:55:21 -04:00
list_add ( & bo_va - > vm_status , & bo_va - > vm - > invalidated ) ;
2015-07-30 11:53:42 +02:00
spin_unlock ( & bo_va - > vm - > status_lock ) ;
2015-04-20 16:55:21 -04:00
}
}
/**
* amdgpu_vm_init - initialize a vm instance
*
* @ adev : amdgpu_device pointer
* @ vm : requested vm
*
2016-01-26 12:17:11 +01:00
* Init @ vm fields .
2015-04-20 16:55:21 -04:00
*/
int amdgpu_vm_init ( struct amdgpu_device * adev , struct amdgpu_vm * vm )
{
2016-02-01 12:53:58 +01:00
struct amdgpu_ring * ring = adev - > vm_manager . vm_pte_funcs_ring ;
2015-04-20 16:55:21 -04:00
const unsigned align = min ( AMDGPU_VM_PTB_ALIGN_SIZE ,
AMDGPU_VM_PTE_COUNT * 8 ) ;
2016-01-19 17:59:46 +09:00
unsigned pd_size , pd_entries ;
2016-02-01 12:53:58 +01:00
struct amd_sched_rq * rq ;
2015-04-20 16:55:21 -04:00
int i , r ;
for ( i = 0 ; i < AMDGPU_MAX_RINGS ; + + i ) {
vm - > ids [ i ] . id = 0 ;
vm - > ids [ i ] . flushed_updates = NULL ;
}
vm - > va = RB_ROOT ;
spin_lock_init ( & vm - > status_lock ) ;
INIT_LIST_HEAD ( & vm - > invalidated ) ;
2015-07-30 11:53:42 +02:00
INIT_LIST_HEAD ( & vm - > cleared ) ;
2015-04-20 16:55:21 -04:00
INIT_LIST_HEAD ( & vm - > freed ) ;
2015-11-13 13:32:01 +08:00
spin_lock_init ( & vm - > it_lock ) ;
2015-12-04 17:17:00 +08:00
spin_lock_init ( & vm - > freed_lock ) ;
2015-04-20 16:55:21 -04:00
pd_size = amdgpu_vm_directory_size ( adev ) ;
pd_entries = amdgpu_vm_num_pdes ( adev ) ;
/* allocate page table array */
2016-01-19 17:59:46 +09:00
vm - > page_tables = drm_calloc_large ( pd_entries , sizeof ( struct amdgpu_vm_pt ) ) ;
2015-04-20 16:55:21 -04:00
if ( vm - > page_tables = = NULL ) {
DRM_ERROR ( " Cannot allocate memory for page table array \n " ) ;
return - ENOMEM ;
}
2016-02-01 12:53:58 +01:00
/* create scheduler entity for page table updates */
rq = & ring - > sched . sched_rq [ AMD_SCHED_PRIORITY_KERNEL ] ;
r = amd_sched_entity_init ( & ring - > sched , & vm - > entity ,
rq , amdgpu_sched_jobs ) ;
if ( r )
return r ;
2015-08-14 20:08:40 +02:00
vm - > page_directory_fence = NULL ;
2015-04-20 16:55:21 -04:00
r = amdgpu_bo_create ( adev , pd_size , align , true ,
2015-08-27 00:14:16 -04:00
AMDGPU_GEM_DOMAIN_VRAM ,
AMDGPU_GEM_CREATE_NO_CPU_ACCESS ,
2015-09-03 17:34:59 +02:00
NULL , NULL , & vm - > page_directory ) ;
2015-04-20 16:55:21 -04:00
if ( r )
2016-02-01 12:53:58 +01:00
goto error_free_sched_entity ;
2015-11-13 13:43:22 +08:00
r = amdgpu_bo_reserve ( vm - > page_directory , false ) ;
2016-02-01 12:53:58 +01:00
if ( r )
goto error_free_page_directory ;
r = amdgpu_vm_clear_bo ( adev , vm , vm - > page_directory ) ;
2015-11-13 13:43:22 +08:00
amdgpu_bo_unreserve ( vm - > page_directory ) ;
2016-02-01 12:53:58 +01:00
if ( r )
goto error_free_page_directory ;
2015-04-20 16:55:21 -04:00
return 0 ;
2016-02-01 12:53:58 +01:00
error_free_page_directory :
amdgpu_bo_unref ( & vm - > page_directory ) ;
vm - > page_directory = NULL ;
error_free_sched_entity :
amd_sched_entity_fini ( & ring - > sched , & vm - > entity ) ;
return r ;
2015-04-20 16:55:21 -04:00
}
/**
* amdgpu_vm_fini - tear down a vm instance
*
* @ adev : amdgpu_device pointer
* @ vm : requested vm
*
2016-01-26 12:17:11 +01:00
* Tear down @ vm .
2015-04-20 16:55:21 -04:00
* Unbind the VM and remove all bos from the vm bo list
*/
void amdgpu_vm_fini ( struct amdgpu_device * adev , struct amdgpu_vm * vm )
{
2016-02-01 12:53:58 +01:00
struct amdgpu_ring * ring = adev - > vm_manager . vm_pte_funcs_ring ;
2015-04-20 16:55:21 -04:00
struct amdgpu_bo_va_mapping * mapping , * tmp ;
int i ;
2016-02-01 12:53:58 +01:00
amd_sched_entity_fini ( & ring - > sched , & vm - > entity ) ;
2015-04-20 16:55:21 -04:00
if ( ! RB_EMPTY_ROOT ( & vm - > va ) ) {
dev_err ( adev - > dev , " still active bo inside vm \n " ) ;
}
rbtree_postorder_for_each_entry_safe ( mapping , tmp , & vm - > va , it . rb ) {
list_del ( & mapping - > list ) ;
interval_tree_remove ( & mapping - > it , & vm - > va ) ;
kfree ( mapping ) ;
}
list_for_each_entry_safe ( mapping , tmp , & vm - > freed , list ) {
list_del ( & mapping - > list ) ;
kfree ( mapping ) ;
}
for ( i = 0 ; i < amdgpu_vm_num_pdes ( adev ) ; i + + )
2015-12-11 21:01:23 +01:00
amdgpu_bo_unref ( & vm - > page_tables [ i ] . entry . robj ) ;
2016-01-19 17:59:46 +09:00
drm_free_large ( vm - > page_tables ) ;
2015-04-20 16:55:21 -04:00
amdgpu_bo_unref ( & vm - > page_directory ) ;
2015-08-14 20:08:40 +02:00
fence_put ( vm - > page_directory_fence ) ;
2015-04-20 16:55:21 -04:00
for ( i = 0 ; i < AMDGPU_MAX_RINGS ; + + i ) {
2015-11-14 21:31:40 +01:00
unsigned id = vm - > ids [ i ] . id ;
atomic_long_cmpxchg ( & adev - > vm_manager . ids [ id ] . owner ,
( long ) vm , 0 ) ;
2015-08-20 18:33:59 +08:00
fence_put ( vm - > ids [ i ] . flushed_updates ) ;
2015-04-20 16:55:21 -04:00
}
}
2015-11-15 20:52:06 +01:00
2016-01-21 10:19:11 +01:00
/**
* amdgpu_vm_manager_init - init the VM manager
*
* @ adev : amdgpu_device pointer
*
* Initialize the VM manager structures
*/
void amdgpu_vm_manager_init ( struct amdgpu_device * adev )
{
unsigned i ;
INIT_LIST_HEAD ( & adev - > vm_manager . ids_lru ) ;
/* skip over VMID 0, since it is the system VM */
for ( i = 1 ; i < adev - > vm_manager . num_ids ; + + i )
list_add_tail ( & adev - > vm_manager . ids [ i ] . list ,
& adev - > vm_manager . ids_lru ) ;
}
2015-11-15 20:52:06 +01:00
/**
* amdgpu_vm_manager_fini - cleanup VM manager
*
* @ adev : amdgpu_device pointer
*
* Cleanup the VM manager and free resources .
*/
void amdgpu_vm_manager_fini ( struct amdgpu_device * adev )
{
unsigned i ;
for ( i = 0 ; i < AMDGPU_NUM_VM ; + + i )
2015-11-14 21:31:40 +01:00
fence_put ( adev - > vm_manager . ids [ i ] . active ) ;
2015-11-15 20:52:06 +01:00
}