2016-09-09 16:32:33 +02:00
/*
* Copyright 2016 Advanced Micro Devices , Inc .
*
* 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 : Christian König
*/
# include <drm/drmP.h>
# include "amdgpu.h"
struct amdgpu_gtt_mgr {
struct drm_mm mm ;
spinlock_t lock ;
uint64_t available ;
} ;
/**
* amdgpu_gtt_mgr_init - init GTT manager and DRM MM
*
* @ man : TTM memory type manager
* @ p_size : maximum size of GTT
*
* Allocate and initialize the GTT manager .
*/
static int amdgpu_gtt_mgr_init ( struct ttm_mem_type_manager * man ,
unsigned long p_size )
{
struct amdgpu_gtt_mgr * mgr ;
2017-06-28 12:18:54 +02:00
uint64_t start , size ;
2016-09-09 16:32:33 +02:00
mgr = kzalloc ( sizeof ( * mgr ) , GFP_KERNEL ) ;
if ( ! mgr )
return - ENOMEM ;
2017-06-28 12:18:54 +02:00
start = AMDGPU_GTT_MAX_TRANSFER_SIZE * AMDGPU_GTT_NUM_TRANSFER_WINDOWS ;
size = p_size - start ;
drm_mm_init ( & mgr - > mm , start , size ) ;
2016-09-09 16:32:33 +02:00
spin_lock_init ( & mgr - > lock ) ;
mgr - > available = p_size ;
man - > priv = mgr ;
return 0 ;
}
/**
* amdgpu_gtt_mgr_fini - free and destroy GTT manager
*
* @ man : TTM memory type manager
*
* Destroy and free the GTT manager , returns - EBUSY if ranges are still
* allocated inside it .
*/
static int amdgpu_gtt_mgr_fini ( struct ttm_mem_type_manager * man )
{
struct amdgpu_gtt_mgr * mgr = man - > priv ;
spin_lock ( & mgr - > lock ) ;
if ( ! drm_mm_clean ( & mgr - > mm ) ) {
spin_unlock ( & mgr - > lock ) ;
return - EBUSY ;
}
drm_mm_takedown ( & mgr - > mm ) ;
spin_unlock ( & mgr - > lock ) ;
kfree ( mgr ) ;
man - > priv = NULL ;
return 0 ;
}
2017-06-30 10:41:07 +02:00
/**
* amdgpu_gtt_mgr_is_allocated - Check if mem has address space
*
* @ mem : the mem object to check
*
* Check if a mem object has already address space allocated .
*/
bool amdgpu_gtt_mgr_is_allocated ( struct ttm_mem_reg * mem )
{
struct drm_mm_node * node = mem - > mm_node ;
return ( node - > start ! = AMDGPU_BO_INVALID_OFFSET ) ;
}
2016-09-09 16:32:33 +02:00
/**
* amdgpu_gtt_mgr_alloc - allocate new ranges
*
* @ man : TTM memory type manager
* @ tbo : TTM BO we need this range for
* @ place : placement flags and restrictions
* @ mem : the resulting mem object
*
* Allocate the address space for a node .
*/
int amdgpu_gtt_mgr_alloc ( struct ttm_mem_type_manager * man ,
struct ttm_buffer_object * tbo ,
const struct ttm_place * place ,
struct ttm_mem_reg * mem )
{
struct amdgpu_gtt_mgr * mgr = man - > priv ;
struct drm_mm_node * node = mem - > mm_node ;
2017-02-02 21:04:38 +00:00
enum drm_mm_insert_mode mode ;
2016-09-09 16:32:33 +02:00
unsigned long fpfn , lpfn ;
int r ;
2017-06-30 10:41:07 +02:00
if ( amdgpu_gtt_mgr_is_allocated ( mem ) )
2016-09-09 16:32:33 +02:00
return 0 ;
if ( place )
fpfn = place - > fpfn ;
else
fpfn = 0 ;
if ( place & & place - > lpfn )
lpfn = place - > lpfn ;
else
lpfn = man - > size ;
2017-02-02 21:04:38 +00:00
mode = DRM_MM_INSERT_BEST ;
if ( place & & place - > flags & TTM_PL_FLAG_TOPDOWN )
mode = DRM_MM_INSERT_HIGH ;
2016-09-09 16:32:33 +02:00
spin_lock ( & mgr - > lock ) ;
2017-02-02 21:04:38 +00:00
r = drm_mm_insert_node_in_range ( & mgr - > mm , node ,
mem - > num_pages , mem - > page_alignment , 0 ,
fpfn , lpfn , mode ) ;
2016-09-09 16:32:33 +02:00
spin_unlock ( & mgr - > lock ) ;
if ( ! r ) {
mem - > start = node - > start ;
2016-09-20 17:07:31 +08:00
if ( & tbo - > mem = = mem )
tbo - > offset = ( tbo - > mem . start < < PAGE_SHIFT ) +
tbo - > bdev - > man [ tbo - > mem . mem_type ] . gpu_offset ;
2016-09-09 16:32:33 +02:00
}
return r ;
}
2017-04-13 16:16:51 +08:00
void amdgpu_gtt_mgr_print ( struct seq_file * m , struct ttm_mem_type_manager * man )
{
struct amdgpu_device * adev = amdgpu_ttm_adev ( man - > bdev ) ;
struct amdgpu_gtt_mgr * mgr = man - > priv ;
seq_printf ( m , " man size:%llu pages, gtt available:%llu pages, usage:%lluMB \n " ,
man - > size , mgr - > available , ( u64 ) atomic64_read ( & adev - > gtt_usage ) > > 20 ) ;
}
2016-09-09 16:32:33 +02:00
/**
* amdgpu_gtt_mgr_new - allocate a new node
*
* @ man : TTM memory type manager
* @ tbo : TTM BO we need this range for
* @ place : placement flags and restrictions
* @ mem : the resulting mem object
*
* Dummy , allocate the node but no space for it yet .
*/
static int amdgpu_gtt_mgr_new ( struct ttm_mem_type_manager * man ,
struct ttm_buffer_object * tbo ,
const struct ttm_place * place ,
struct ttm_mem_reg * mem )
{
struct amdgpu_gtt_mgr * mgr = man - > priv ;
struct drm_mm_node * node ;
int r ;
spin_lock ( & mgr - > lock ) ;
if ( mgr - > available < mem - > num_pages ) {
spin_unlock ( & mgr - > lock ) ;
return 0 ;
}
mgr - > available - = mem - > num_pages ;
spin_unlock ( & mgr - > lock ) ;
node = kzalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
2016-12-02 16:51:20 +08:00
if ( ! node ) {
r = - ENOMEM ;
goto err_out ;
}
2016-09-09 16:32:33 +02:00
node - > start = AMDGPU_BO_INVALID_OFFSET ;
2016-09-14 10:35:19 +02:00
node - > size = mem - > num_pages ;
2016-09-09 16:32:33 +02:00
mem - > mm_node = node ;
if ( place - > fpfn | | place - > lpfn | | place - > flags & TTM_PL_FLAG_TOPDOWN ) {
r = amdgpu_gtt_mgr_alloc ( man , tbo , place , mem ) ;
if ( unlikely ( r ) ) {
kfree ( node ) ;
mem - > mm_node = NULL ;
2016-12-02 16:51:20 +08:00
r = 0 ;
goto err_out ;
2016-09-09 16:32:33 +02:00
}
} else {
mem - > start = node - > start ;
}
return 0 ;
2016-12-02 16:51:20 +08:00
err_out :
spin_lock ( & mgr - > lock ) ;
mgr - > available + = mem - > num_pages ;
spin_unlock ( & mgr - > lock ) ;
return r ;
2016-09-09 16:32:33 +02:00
}
/**
* amdgpu_gtt_mgr_del - free ranges
*
* @ man : TTM memory type manager
* @ tbo : TTM BO we need this range for
* @ place : placement flags and restrictions
* @ mem : TTM memory object
*
* Free the allocated GTT again .
*/
static void amdgpu_gtt_mgr_del ( struct ttm_mem_type_manager * man ,
struct ttm_mem_reg * mem )
{
struct amdgpu_gtt_mgr * mgr = man - > priv ;
struct drm_mm_node * node = mem - > mm_node ;
if ( ! node )
return ;
spin_lock ( & mgr - > lock ) ;
if ( node - > start ! = AMDGPU_BO_INVALID_OFFSET )
drm_mm_remove_node ( node ) ;
mgr - > available + = mem - > num_pages ;
spin_unlock ( & mgr - > lock ) ;
kfree ( node ) ;
mem - > mm_node = NULL ;
}
/**
* amdgpu_gtt_mgr_debug - dump VRAM table
*
* @ man : TTM memory type manager
* @ prefix : text prefix
*
* Dump the table content using printk .
*/
static void amdgpu_gtt_mgr_debug ( struct ttm_mem_type_manager * man ,
const char * prefix )
{
struct amdgpu_gtt_mgr * mgr = man - > priv ;
2016-12-29 12:09:24 +01:00
struct drm_printer p = drm_debug_printer ( prefix ) ;
2016-09-09 16:32:33 +02:00
spin_lock ( & mgr - > lock ) ;
2016-12-29 12:09:24 +01:00
drm_mm_print ( & mgr - > mm , & p ) ;
2016-09-09 16:32:33 +02:00
spin_unlock ( & mgr - > lock ) ;
}
const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func = {
2016-12-16 17:02:32 -08:00
. init = amdgpu_gtt_mgr_init ,
. takedown = amdgpu_gtt_mgr_fini ,
. get_node = amdgpu_gtt_mgr_new ,
. put_node = amdgpu_gtt_mgr_del ,
. debug = amdgpu_gtt_mgr_debug
2016-09-09 16:32:33 +02:00
} ;