2016-08-24 15:51:49 +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_vram_mgr {
struct drm_mm mm ;
spinlock_t lock ;
} ;
/**
* amdgpu_vram_mgr_init - init VRAM manager and DRM MM
*
* @ man : TTM memory type manager
* @ p_size : maximum size of VRAM
*
* Allocate and initialize the VRAM manager .
*/
static int amdgpu_vram_mgr_init ( struct ttm_mem_type_manager * man ,
unsigned long p_size )
{
struct amdgpu_vram_mgr * mgr ;
mgr = kzalloc ( sizeof ( * mgr ) , GFP_KERNEL ) ;
if ( ! mgr )
return - ENOMEM ;
drm_mm_init ( & mgr - > mm , 0 , p_size ) ;
spin_lock_init ( & mgr - > lock ) ;
man - > priv = mgr ;
return 0 ;
}
/**
* amdgpu_vram_mgr_fini - free and destroy VRAM manager
*
* @ man : TTM memory type manager
*
* Destroy and free the VRAM manager , returns - EBUSY if ranges are still
* allocated inside it .
*/
static int amdgpu_vram_mgr_fini ( struct ttm_mem_type_manager * man )
{
struct amdgpu_vram_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 ;
}
/**
* amdgpu_vram_mgr_new - 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 VRAM for the given BO .
*/
static int amdgpu_vram_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_bo * bo = container_of ( tbo , struct amdgpu_bo , tbo ) ;
struct amdgpu_vram_mgr * mgr = man - > priv ;
struct drm_mm * mm = & mgr - > mm ;
struct drm_mm_node * nodes ;
enum drm_mm_search_flags sflags = DRM_MM_SEARCH_DEFAULT ;
enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT ;
unsigned long lpfn , num_nodes , pages_per_node , pages_left ;
unsigned i ;
int r ;
lpfn = place - > lpfn ;
if ( ! lpfn )
lpfn = man - > size ;
if ( bo - > flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS | |
2016-11-04 10:12:38 +01:00
place - > lpfn | | amdgpu_vram_page_split = = - 1 ) {
2016-08-24 15:51:49 +02:00
pages_per_node = ~ 0ul ;
num_nodes = 1 ;
} else {
pages_per_node = max ( ( uint32_t ) amdgpu_vram_page_split ,
mem - > page_alignment ) ;
num_nodes = DIV_ROUND_UP ( mem - > num_pages , pages_per_node ) ;
}
nodes = kcalloc ( num_nodes , sizeof ( * nodes ) , GFP_KERNEL ) ;
if ( ! nodes )
return - ENOMEM ;
if ( place - > flags & TTM_PL_FLAG_TOPDOWN ) {
sflags = DRM_MM_SEARCH_BELOW ;
aflags = DRM_MM_CREATE_TOP ;
}
pages_left = mem - > num_pages ;
spin_lock ( & mgr - > lock ) ;
for ( i = 0 ; i < num_nodes ; + + i ) {
unsigned long pages = min ( pages_left , pages_per_node ) ;
uint32_t alignment = mem - > page_alignment ;
if ( pages = = pages_per_node )
alignment = pages_per_node ;
else
sflags | = DRM_MM_SEARCH_BEST ;
r = drm_mm_insert_node_in_range_generic ( mm , & nodes [ i ] , pages ,
alignment , 0 ,
place - > fpfn , lpfn ,
sflags , aflags ) ;
if ( unlikely ( r ) )
goto error ;
pages_left - = pages ;
}
spin_unlock ( & mgr - > lock ) ;
mem - > start = num_nodes = = 1 ? nodes [ 0 ] . start : AMDGPU_BO_INVALID_OFFSET ;
mem - > mm_node = nodes ;
return 0 ;
error :
while ( i - - )
drm_mm_remove_node ( & nodes [ i ] ) ;
spin_unlock ( & mgr - > lock ) ;
kfree ( nodes ) ;
return r = = - ENOSPC ? 0 : r ;
}
/**
* amdgpu_vram_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 VRAM again .
*/
static void amdgpu_vram_mgr_del ( struct ttm_mem_type_manager * man ,
struct ttm_mem_reg * mem )
{
struct amdgpu_vram_mgr * mgr = man - > priv ;
struct drm_mm_node * nodes = mem - > mm_node ;
unsigned pages = mem - > num_pages ;
if ( ! mem - > mm_node )
return ;
spin_lock ( & mgr - > lock ) ;
while ( pages ) {
pages - = nodes - > size ;
drm_mm_remove_node ( nodes ) ;
+ + nodes ;
}
spin_unlock ( & mgr - > lock ) ;
kfree ( mem - > mm_node ) ;
mem - > mm_node = NULL ;
}
/**
* amdgpu_vram_mgr_debug - dump VRAM table
*
* @ man : TTM memory type manager
* @ prefix : text prefix
*
* Dump the table content using printk .
*/
static void amdgpu_vram_mgr_debug ( struct ttm_mem_type_manager * man ,
const char * prefix )
{
struct amdgpu_vram_mgr * mgr = man - > priv ;
spin_lock ( & mgr - > lock ) ;
drm_mm_debug_table ( & mgr - > mm , prefix ) ;
spin_unlock ( & mgr - > lock ) ;
}
const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
amdgpu_vram_mgr_init ,
amdgpu_vram_mgr_fini ,
amdgpu_vram_mgr_new ,
amdgpu_vram_mgr_del ,
amdgpu_vram_mgr_debug
} ;