2001-08-20 12:03:02 +04:00
/*
* dm - table . c
*
* Copyright ( C ) 2001 Sistina Software
*
* This software is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 , or ( at
* your option ) any later version .
*
* This software is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with GNU CC ; see the file COPYING . If not , write to
* the Free Software Foundation , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
*/
/*
* Changelog
*
* 16 / 08 / 2001 - First version [ Joe Thornber ]
*/
2001-08-21 18:47:42 +04:00
# include "dm.h"
2001-08-31 16:49:31 +04:00
/* ceiling(n / size) * size */
2001-08-23 16:35:02 +04:00
static inline ulong round_up ( ulong n , ulong size )
2001-08-20 12:03:02 +04:00
{
ulong r = n % size ;
return n + ( r ? ( size - r ) : 0 ) ;
}
2001-08-31 16:49:31 +04:00
/* ceiling(n / size) */
2001-08-23 16:35:02 +04:00
static inline ulong div_up ( ulong n , ulong size )
2001-08-20 12:03:02 +04:00
{
2001-08-23 16:35:02 +04:00
return round_up ( n , size ) / size ;
2001-08-20 12:03:02 +04:00
}
2001-09-02 14:49:20 +04:00
/* similar to ceiling(log_size(n)) */
2001-08-31 19:13:33 +04:00
static uint int_log ( ulong n , ulong base )
2001-08-31 16:49:31 +04:00
{
int result = 0 ;
2001-09-02 14:49:20 +04:00
while ( n > 1 ) {
2001-08-31 16:49:31 +04:00
n = div_up ( n , base ) ;
result + + ;
}
return result ;
}
/*
* return the highest key that you could lookup
* from the n ' th node on level l of the btree .
*/
2001-08-31 19:13:33 +04:00
static offset_t high ( struct dm_table * t , int l , int n )
2001-08-20 12:03:02 +04:00
{
2001-09-05 11:48:11 +04:00
for ( ; l < t - > depth - 1 ; l + + )
2001-09-04 14:17:28 +04:00
n = get_child ( n , CHILDREN_PER_NODE - 1 ) ;
2001-08-31 16:49:31 +04:00
2001-09-05 11:48:11 +04:00
if ( n > = t - > counts [ l ] )
return ( offset_t ) - 1 ;
return get_node ( t , l , n ) [ KEYS_PER_NODE - 1 ] ;
2001-08-20 12:03:02 +04:00
}
2001-08-31 16:49:31 +04:00
/*
* fills in a level of the btree based on the
* highs of the level below it .
*/
2001-08-31 19:13:33 +04:00
static int setup_btree_index ( int l , struct dm_table * t )
2001-08-20 12:03:02 +04:00
{
2001-09-05 11:48:11 +04:00
int n , k ;
offset_t * node ;
2001-08-20 12:03:02 +04:00
2001-09-05 11:48:11 +04:00
for ( n = 0 ; n < t - > counts [ l ] ; n + + ) {
node = get_node ( t , l , n ) ;
2001-09-04 14:17:28 +04:00
2001-09-05 11:48:11 +04:00
for ( k = 0 ; k < KEYS_PER_NODE ; k + + )
node [ k ] = high ( t , l + 1 , get_child ( n , k ) ) ;
2001-08-20 12:03:02 +04:00
}
2001-08-23 16:35:02 +04:00
return 0 ;
2001-08-20 12:03:02 +04:00
}
2001-08-31 16:49:31 +04:00
/*
2001-08-31 19:13:33 +04:00
* highs , and targets are managed as dynamic
* arrays during a table load .
2001-08-31 16:49:31 +04:00
*/
2001-08-31 19:13:33 +04:00
static int alloc_targets ( struct dm_table * t , int num )
2001-08-31 16:49:31 +04:00
{
offset_t * n_highs ;
struct target * n_targets ;
2001-08-31 19:13:33 +04:00
int n = t - > num_targets ;
2001-08-31 16:49:31 +04:00
if ( ! ( n_highs = vmalloc ( sizeof ( * n_highs ) * num ) ) )
return - ENOMEM ;
if ( ! ( n_targets = vmalloc ( sizeof ( * n_targets ) * num ) ) ) {
vfree ( n_highs ) ;
return - ENOMEM ;
}
2001-08-31 19:13:33 +04:00
if ( n ) {
memcpy ( n_highs , t - > highs , sizeof ( * n_highs ) * n ) ;
memcpy ( n_targets , t - > targets , sizeof ( * n_targets ) * n ) ;
2001-08-31 16:49:31 +04:00
}
2001-08-31 19:13:33 +04:00
vfree ( t - > highs ) ;
vfree ( t - > targets ) ;
2001-08-31 16:49:31 +04:00
2001-08-31 19:13:33 +04:00
t - > num_allocated = num ;
t - > highs = n_highs ;
t - > targets = n_targets ;
2001-08-31 16:49:31 +04:00
return 0 ;
}
2001-08-31 19:13:33 +04:00
struct dm_table * dm_table_create ( void )
2001-08-20 17:45:43 +04:00
{
2001-08-31 19:13:33 +04:00
struct dm_table * t = kmalloc ( sizeof ( struct dm_table ) , GFP_NOIO ) ;
2001-08-20 19:22:44 +04:00
2001-08-31 19:13:33 +04:00
if ( ! t )
return 0 ;
2001-08-31 16:49:31 +04:00
2001-08-31 19:13:33 +04:00
memset ( t , 0 , sizeof ( * t ) ) ;
2001-08-21 18:47:42 +04:00
2001-08-31 19:13:33 +04:00
/* allocate a single nodes worth of targets to
begin with */
2001-09-14 13:45:35 +04:00
t - > hardsect_size = PAGE_CACHE_SIZE ;
if ( alloc_targets ( t , KEYS_PER_NODE ) ) {
2001-08-31 19:13:33 +04:00
kfree ( t ) ;
t = 0 ;
}
return t ;
}
void dm_table_destroy ( struct dm_table * t )
{
int i ;
if ( ! t )
return ;
/* free the indexes */
2001-09-02 14:49:20 +04:00
for ( i = 0 ; i < t - > depth - 1 ; i + + ) {
2001-08-31 19:13:33 +04:00
vfree ( t - > index [ i ] ) ;
t - > index [ i ] = 0 ;
}
2001-09-02 14:49:20 +04:00
vfree ( t - > highs ) ;
2001-08-31 19:13:33 +04:00
2001-09-02 14:49:20 +04:00
/* free the targets */
for ( i = 0 ; i < t - > num_targets ; i + + ) {
struct target * tgt = & t - > targets [ i ] ;
2001-09-07 15:34:46 +04:00
if ( tgt - > private )
tgt - > type - > dtr ( t , tgt - > private ) ;
2001-09-02 14:49:20 +04:00
}
2001-08-31 19:13:33 +04:00
vfree ( t - > targets ) ;
2001-09-02 14:49:20 +04:00
kfree ( t ) ;
2001-08-20 17:45:43 +04:00
}
2001-08-31 19:13:33 +04:00
/*
* checks to see if we need to extend highs or targets
*/
static inline int check_space ( struct dm_table * t )
2001-08-31 16:49:31 +04:00
{
2001-08-31 19:13:33 +04:00
if ( t - > num_targets > = t - > num_allocated )
return alloc_targets ( t , t - > num_allocated * 2 ) ;
2001-08-31 16:49:31 +04:00
return 0 ;
}
2001-08-31 19:13:33 +04:00
/*
* adds a target to the map
*/
2001-09-02 14:49:20 +04:00
int dm_table_add_target ( struct dm_table * t , offset_t high ,
struct target_type * type , void * private )
2001-08-20 17:45:43 +04:00
{
2001-08-31 19:13:33 +04:00
int r , n ;
2001-08-31 16:49:31 +04:00
2001-08-31 19:13:33 +04:00
if ( ( r = check_space ( t ) ) )
2001-08-31 16:49:31 +04:00
return r ;
2001-08-20 19:22:44 +04:00
2001-08-31 19:13:33 +04:00
n = t - > num_targets + + ;
t - > highs [ n ] = high ;
2001-09-02 14:49:20 +04:00
t - > targets [ n ] . type = type ;
2001-08-31 19:13:33 +04:00
t - > targets [ n ] . private = private ;
2001-08-31 16:49:31 +04:00
2001-08-21 18:47:42 +04:00
return 0 ;
2001-08-20 17:45:43 +04:00
}
2001-08-31 19:13:33 +04:00
/*
* builds the btree to index the map
*/
int dm_table_complete ( struct dm_table * t )
{
int i , leaf_nodes ;
2001-08-20 19:22:44 +04:00
/* how many indexes will the btree have ? */
2001-08-31 19:13:33 +04:00
leaf_nodes = div_up ( t - > num_targets , KEYS_PER_NODE ) ;
2001-09-04 14:17:28 +04:00
t - > depth = 1 + int_log ( leaf_nodes , CHILDREN_PER_NODE ) ;
2001-08-20 19:22:44 +04:00
2001-08-31 19:13:33 +04:00
/* leaf layer has already been set up */
2001-09-02 14:49:20 +04:00
t - > counts [ t - > depth - 1 ] = leaf_nodes ;
2001-08-31 19:13:33 +04:00
t - > index [ t - > depth - 1 ] = t - > highs ;
2001-08-20 19:22:44 +04:00
2001-09-02 14:49:20 +04:00
/* set up internal nodes, bottom-up */
for ( i = t - > depth - 2 ; i > = 0 ; i - - ) {
2001-09-04 14:17:28 +04:00
t - > counts [ i ] = div_up ( t - > counts [ i + 1 ] , CHILDREN_PER_NODE ) ;
2001-09-02 14:49:20 +04:00
t - > index [ i ] = vmalloc ( NODE_SIZE * t - > counts [ i ] ) ;
setup_btree_index ( i , t ) ;
}
2001-08-20 19:22:44 +04:00
2001-08-23 16:35:02 +04:00
return 0 ;
2001-08-20 17:45:43 +04:00
}
2001-08-20 12:03:02 +04:00