2006-08-07 15:30:28 +04:00
/**************************************************************************
*
* Copyright 2006 Tungsten Graphics , Inc . , Bismarck . , ND . , USA .
* All Rights Reserved .
*
* 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 , sub license , 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 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 NON - INFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS , AUTHORS AND / OR ITS SUPPLIERS 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 .
*
* The above copyright notice and this permission notice ( including the
* next paragraph ) shall be included in all copies or substantial portions
* of the Software .
*
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Simple memory manager interface that keeps track on allocate regions on a
* per " owner " basis . All regions associated with an " owner " can be released
* with a simple call . Typically if the " owner " exists . The owner is any
* " unsigned long " identifier . Can typically be a pointer to a file private
* struct or a context identifier .
*
* Authors :
* Thomas Hellstr <EFBFBD> m < thomas - at - tungstengraphics - dot - com >
*/
# include "drm_sman.h"
2007-07-12 04:26:44 +04:00
struct drm_owner_item {
struct drm_hash_item owner_hash ;
2006-08-07 15:30:28 +04:00
struct list_head sman_list ;
struct list_head mem_blocks ;
2007-07-12 04:26:44 +04:00
} ;
2006-08-07 15:30:28 +04:00
2007-07-12 04:21:05 +04:00
void drm_sman_takedown ( struct drm_sman * sman )
2006-08-07 15:30:28 +04:00
{
drm_ht_remove ( & sman - > user_hash_tab ) ;
drm_ht_remove ( & sman - > owner_hash_tab ) ;
if ( sman - > mm )
drm_free ( sman - > mm , sman - > num_managers * sizeof ( * sman - > mm ) ,
DRM_MEM_MM ) ;
}
EXPORT_SYMBOL ( drm_sman_takedown ) ;
int
2007-07-12 04:21:05 +04:00
drm_sman_init ( struct drm_sman * sman , unsigned int num_managers ,
2006-08-07 15:30:28 +04:00
unsigned int user_order , unsigned int owner_order )
{
int ret = 0 ;
2007-07-12 04:21:05 +04:00
sman - > mm = ( struct drm_sman_mm * ) drm_calloc ( num_managers , sizeof ( * sman - > mm ) ,
2006-08-07 15:30:28 +04:00
DRM_MEM_MM ) ;
if ( ! sman - > mm ) {
ret = - ENOMEM ;
goto out ;
}
sman - > num_managers = num_managers ;
INIT_LIST_HEAD ( & sman - > owner_items ) ;
ret = drm_ht_create ( & sman - > owner_hash_tab , owner_order ) ;
if ( ret )
goto out1 ;
ret = drm_ht_create ( & sman - > user_hash_tab , user_order ) ;
if ( ! ret )
goto out ;
drm_ht_remove ( & sman - > owner_hash_tab ) ;
out1 :
drm_free ( sman - > mm , num_managers * sizeof ( * sman - > mm ) , DRM_MEM_MM ) ;
out :
return ret ;
}
EXPORT_SYMBOL ( drm_sman_init ) ;
static void * drm_sman_mm_allocate ( void * private , unsigned long size ,
unsigned alignment )
{
2007-07-11 10:53:40 +04:00
struct drm_mm * mm = ( struct drm_mm * ) private ;
struct drm_mm_node * tmp ;
2006-08-07 15:30:28 +04:00
2006-08-14 05:35:15 +04:00
tmp = drm_mm_search_free ( mm , size , alignment , 1 ) ;
2006-08-07 15:30:28 +04:00
if ( ! tmp ) {
return NULL ;
}
tmp = drm_mm_get_block ( tmp , size , alignment ) ;
return tmp ;
}
static void drm_sman_mm_free ( void * private , void * ref )
{
2007-07-11 10:53:40 +04:00
struct drm_mm_node * node = ( struct drm_mm_node * ) ref ;
2006-08-07 15:30:28 +04:00
2007-01-08 14:25:47 +03:00
drm_mm_put_block ( node ) ;
2006-08-07 15:30:28 +04:00
}
static void drm_sman_mm_destroy ( void * private )
{
2007-07-11 10:53:40 +04:00
struct drm_mm * mm = ( struct drm_mm * ) private ;
2006-08-07 15:30:28 +04:00
drm_mm_takedown ( mm ) ;
drm_free ( mm , sizeof ( * mm ) , DRM_MEM_MM ) ;
}
2006-08-16 05:55:18 +04:00
static unsigned long drm_sman_mm_offset ( void * private , void * ref )
2006-08-07 15:30:28 +04:00
{
2007-07-11 10:53:40 +04:00
struct drm_mm_node * node = ( struct drm_mm_node * ) ref ;
2006-08-07 15:30:28 +04:00
return node - > start ;
}
int
2007-07-12 04:21:05 +04:00
drm_sman_set_range ( struct drm_sman * sman , unsigned int manager ,
2006-08-07 15:30:28 +04:00
unsigned long start , unsigned long size )
{
2007-07-12 04:21:05 +04:00
struct drm_sman_mm * sman_mm ;
2007-07-11 10:53:40 +04:00
struct drm_mm * mm ;
2006-08-07 15:30:28 +04:00
int ret ;
BUG_ON ( manager > = sman - > num_managers ) ;
sman_mm = & sman - > mm [ manager ] ;
mm = drm_calloc ( 1 , sizeof ( * mm ) , DRM_MEM_MM ) ;
if ( ! mm ) {
return - ENOMEM ;
}
sman_mm - > private = mm ;
ret = drm_mm_init ( mm , start , size ) ;
if ( ret ) {
drm_free ( mm , sizeof ( * mm ) , DRM_MEM_MM ) ;
return ret ;
}
sman_mm - > allocate = drm_sman_mm_allocate ;
sman_mm - > free = drm_sman_mm_free ;
sman_mm - > destroy = drm_sman_mm_destroy ;
sman_mm - > offset = drm_sman_mm_offset ;
return 0 ;
}
EXPORT_SYMBOL ( drm_sman_set_range ) ;
int
2007-07-12 04:21:05 +04:00
drm_sman_set_manager ( struct drm_sman * sman , unsigned int manager ,
struct drm_sman_mm * allocator )
2006-08-07 15:30:28 +04:00
{
BUG_ON ( manager > = sman - > num_managers ) ;
sman - > mm [ manager ] = * allocator ;
return 0 ;
}
2006-12-07 07:31:33 +03:00
EXPORT_SYMBOL ( drm_sman_set_manager ) ;
2006-08-07 15:30:28 +04:00
2007-07-12 04:26:44 +04:00
static struct drm_owner_item * drm_sman_get_owner_item ( struct drm_sman * sman ,
2006-08-07 15:30:28 +04:00
unsigned long owner )
{
int ret ;
2007-07-12 04:26:44 +04:00
struct drm_hash_item * owner_hash_item ;
struct drm_owner_item * owner_item ;
2006-08-07 15:30:28 +04:00
ret = drm_ht_find_item ( & sman - > owner_hash_tab , owner , & owner_hash_item ) ;
if ( ! ret ) {
2007-07-12 04:26:44 +04:00
return drm_hash_entry ( owner_hash_item , struct drm_owner_item ,
2006-08-07 15:30:28 +04:00
owner_hash ) ;
}
owner_item = drm_calloc ( 1 , sizeof ( * owner_item ) , DRM_MEM_MM ) ;
if ( ! owner_item )
goto out ;
INIT_LIST_HEAD ( & owner_item - > mem_blocks ) ;
owner_item - > owner_hash . key = owner ;
if ( drm_ht_insert_item ( & sman - > owner_hash_tab , & owner_item - > owner_hash ) )
goto out1 ;
list_add_tail ( & owner_item - > sman_list , & sman - > owner_items ) ;
return owner_item ;
out1 :
drm_free ( owner_item , sizeof ( * owner_item ) , DRM_MEM_MM ) ;
out :
return NULL ;
}
2007-07-12 04:21:05 +04:00
struct drm_memblock_item * drm_sman_alloc ( struct drm_sman * sman , unsigned int manager ,
2006-08-07 15:30:28 +04:00
unsigned long size , unsigned alignment ,
unsigned long owner )
{
void * tmp ;
2007-07-12 04:21:05 +04:00
struct drm_sman_mm * sman_mm ;
2007-07-12 04:26:44 +04:00
struct drm_owner_item * owner_item ;
2007-07-12 04:21:05 +04:00
struct drm_memblock_item * memblock ;
2006-08-07 15:30:28 +04:00
BUG_ON ( manager > = sman - > num_managers ) ;
sman_mm = & sman - > mm [ manager ] ;
tmp = sman_mm - > allocate ( sman_mm - > private , size , alignment ) ;
if ( ! tmp ) {
return NULL ;
}
memblock = drm_calloc ( 1 , sizeof ( * memblock ) , DRM_MEM_MM ) ;
if ( ! memblock )
goto out ;
memblock - > mm_info = tmp ;
memblock - > mm = sman_mm ;
memblock - > sman = sman ;
if ( drm_ht_just_insert_please
( & sman - > user_hash_tab , & memblock - > user_hash ,
( unsigned long ) memblock , 32 , 0 , 0 ) )
goto out1 ;
owner_item = drm_sman_get_owner_item ( sman , owner ) ;
if ( ! owner_item )
goto out2 ;
list_add_tail ( & memblock - > owner_list , & owner_item - > mem_blocks ) ;
return memblock ;
out2 :
drm_ht_remove_item ( & sman - > user_hash_tab , & memblock - > user_hash ) ;
out1 :
drm_free ( memblock , sizeof ( * memblock ) , DRM_MEM_MM ) ;
out :
sman_mm - > free ( sman_mm - > private , tmp ) ;
return NULL ;
}
EXPORT_SYMBOL ( drm_sman_alloc ) ;
2007-07-12 04:21:05 +04:00
static void drm_sman_free ( struct drm_memblock_item * item )
2006-08-07 15:30:28 +04:00
{
2007-07-12 04:21:05 +04:00
struct drm_sman * sman = item - > sman ;
2006-08-07 15:30:28 +04:00
list_del ( & item - > owner_list ) ;
drm_ht_remove_item ( & sman - > user_hash_tab , & item - > user_hash ) ;
item - > mm - > free ( item - > mm - > private , item - > mm_info ) ;
drm_free ( item , sizeof ( * item ) , DRM_MEM_MM ) ;
}
2007-07-12 04:21:05 +04:00
int drm_sman_free_key ( struct drm_sman * sman , unsigned int key )
2006-08-07 15:30:28 +04:00
{
2007-07-12 04:26:44 +04:00
struct drm_hash_item * hash_item ;
2007-07-12 04:21:05 +04:00
struct drm_memblock_item * memblock_item ;
2006-08-07 15:30:28 +04:00
if ( drm_ht_find_item ( & sman - > user_hash_tab , key , & hash_item ) )
return - EINVAL ;
2007-07-12 04:21:05 +04:00
memblock_item = drm_hash_entry ( hash_item , struct drm_memblock_item ,
user_hash ) ;
2006-08-07 15:30:28 +04:00
drm_sman_free ( memblock_item ) ;
return 0 ;
}
EXPORT_SYMBOL ( drm_sman_free_key ) ;
2007-07-12 04:21:05 +04:00
static void drm_sman_remove_owner ( struct drm_sman * sman ,
2007-07-12 04:26:44 +04:00
struct drm_owner_item * owner_item )
2006-08-07 15:30:28 +04:00
{
list_del ( & owner_item - > sman_list ) ;
drm_ht_remove_item ( & sman - > owner_hash_tab , & owner_item - > owner_hash ) ;
drm_free ( owner_item , sizeof ( * owner_item ) , DRM_MEM_MM ) ;
}
2007-07-12 04:21:05 +04:00
int drm_sman_owner_clean ( struct drm_sman * sman , unsigned long owner )
2006-08-07 15:30:28 +04:00
{
2007-07-12 04:26:44 +04:00
struct drm_hash_item * hash_item ;
struct drm_owner_item * owner_item ;
2006-08-07 15:30:28 +04:00
if ( drm_ht_find_item ( & sman - > owner_hash_tab , owner , & hash_item ) ) {
return - 1 ;
}
2007-07-12 04:26:44 +04:00
owner_item = drm_hash_entry ( hash_item , struct drm_owner_item , owner_hash ) ;
2006-08-07 15:30:28 +04:00
if ( owner_item - > mem_blocks . next = = & owner_item - > mem_blocks ) {
drm_sman_remove_owner ( sman , owner_item ) ;
return - 1 ;
}
return 0 ;
}
EXPORT_SYMBOL ( drm_sman_owner_clean ) ;
2007-07-12 04:21:05 +04:00
static void drm_sman_do_owner_cleanup ( struct drm_sman * sman ,
2007-07-12 04:26:44 +04:00
struct drm_owner_item * owner_item )
2006-08-07 15:30:28 +04:00
{
2007-07-12 04:21:05 +04:00
struct drm_memblock_item * entry , * next ;
2006-08-07 15:30:28 +04:00
list_for_each_entry_safe ( entry , next , & owner_item - > mem_blocks ,
owner_list ) {
drm_sman_free ( entry ) ;
}
drm_sman_remove_owner ( sman , owner_item ) ;
}
2007-07-12 04:21:05 +04:00
void drm_sman_owner_cleanup ( struct drm_sman * sman , unsigned long owner )
2006-08-07 15:30:28 +04:00
{
2007-07-12 04:26:44 +04:00
struct drm_hash_item * hash_item ;
struct drm_owner_item * owner_item ;
2006-08-07 15:30:28 +04:00
if ( drm_ht_find_item ( & sman - > owner_hash_tab , owner , & hash_item ) ) {
return ;
}
2007-07-12 04:26:44 +04:00
owner_item = drm_hash_entry ( hash_item , struct drm_owner_item , owner_hash ) ;
2006-08-07 15:30:28 +04:00
drm_sman_do_owner_cleanup ( sman , owner_item ) ;
}
EXPORT_SYMBOL ( drm_sman_owner_cleanup ) ;
2007-07-12 04:21:05 +04:00
void drm_sman_cleanup ( struct drm_sman * sman )
2006-08-07 15:30:28 +04:00
{
2007-07-12 04:26:44 +04:00
struct drm_owner_item * entry , * next ;
2006-08-07 15:30:28 +04:00
unsigned int i ;
2007-07-12 04:21:05 +04:00
struct drm_sman_mm * sman_mm ;
2006-08-07 15:30:28 +04:00
list_for_each_entry_safe ( entry , next , & sman - > owner_items , sman_list ) {
drm_sman_do_owner_cleanup ( sman , entry ) ;
}
if ( sman - > mm ) {
for ( i = 0 ; i < sman - > num_managers ; + + i ) {
sman_mm = & sman - > mm [ i ] ;
if ( sman_mm - > private ) {
sman_mm - > destroy ( sman_mm - > private ) ;
sman_mm - > private = NULL ;
}
}
}
}
EXPORT_SYMBOL ( drm_sman_cleanup ) ;