2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2001 - 2002 Sistina Software ( UK ) Limited .
2009-01-06 06:05:17 +03:00
* Copyright ( C ) 2006 - 2008 Red Hat GmbH
2005-04-17 02:20:36 +04:00
*
* This file is released under the GPL .
*/
2009-01-06 06:05:15 +03:00
# include "dm-exception-store.h"
2005-04-17 02:20:36 +04:00
2009-04-02 22:55:34 +04:00
# include <linux/ctype.h>
2005-04-17 02:20:36 +04:00
# include <linux/mm.h>
# include <linux/pagemap.h>
# include <linux/vmalloc.h>
# include <linux/slab.h>
2009-01-06 06:05:17 +03:00
# define DM_MSG_PREFIX "snapshot exception stores"
2005-04-17 02:20:36 +04:00
2009-04-02 22:55:31 +04:00
static LIST_HEAD ( _exception_store_types ) ;
static DEFINE_SPINLOCK ( _lock ) ;
static struct dm_exception_store_type * __find_exception_store_type ( const char * name )
{
struct dm_exception_store_type * type ;
list_for_each_entry ( type , & _exception_store_types , list )
if ( ! strcmp ( name , type - > name ) )
return type ;
return NULL ;
}
static struct dm_exception_store_type * _get_exception_store_type ( const char * name )
{
struct dm_exception_store_type * type ;
spin_lock ( & _lock ) ;
type = __find_exception_store_type ( name ) ;
if ( type & & ! try_module_get ( type - > module ) )
type = NULL ;
spin_unlock ( & _lock ) ;
return type ;
}
/*
* get_type
* @ type_name
*
* Attempt to retrieve the dm_exception_store_type by name . If not already
* available , attempt to load the appropriate module .
*
* Exstore modules are named " dm-exstore- " followed by the ' type_name ' .
* Modules may contain multiple types .
* This function will first try the module " dm-exstore-<type_name> " ,
* then truncate ' type_name ' on the last ' - ' and try again .
*
* For example , if type_name was " clustered-shared " , it would search
* ' dm - exstore - clustered - shared ' then ' dm - exstore - clustered ' .
*
* ' dm - exception - store - < type_name > ' is too long of a name in my
* opinion , which is why I ' ve chosen to have the files
* containing exception store implementations be ' dm - exstore - < type_name > ' .
* If you want your module to be autoloaded , you will follow this
* naming convention .
*
* Returns : dm_exception_store_type * on success , NULL on failure
*/
static struct dm_exception_store_type * get_type ( const char * type_name )
{
char * p , * type_name_dup ;
struct dm_exception_store_type * type ;
type = _get_exception_store_type ( type_name ) ;
if ( type )
return type ;
type_name_dup = kstrdup ( type_name , GFP_KERNEL ) ;
if ( ! type_name_dup ) {
DMERR ( " No memory left to attempt load for \" %s \" " , type_name ) ;
return NULL ;
}
while ( request_module ( " dm-exstore-%s " , type_name_dup ) | |
! ( type = _get_exception_store_type ( type_name ) ) ) {
p = strrchr ( type_name_dup , ' - ' ) ;
if ( ! p )
break ;
p [ 0 ] = ' \0 ' ;
}
if ( ! type )
DMWARN ( " Module for exstore type \" %s \" not found. " , type_name ) ;
kfree ( type_name_dup ) ;
return type ;
}
static void put_type ( struct dm_exception_store_type * type )
{
spin_lock ( & _lock ) ;
module_put ( type - > module ) ;
spin_unlock ( & _lock ) ;
}
int dm_exception_store_type_register ( struct dm_exception_store_type * type )
{
int r = 0 ;
spin_lock ( & _lock ) ;
if ( ! __find_exception_store_type ( type - > name ) )
list_add ( & type - > list , & _exception_store_types ) ;
else
r = - EEXIST ;
spin_unlock ( & _lock ) ;
return r ;
}
EXPORT_SYMBOL ( dm_exception_store_type_register ) ;
int dm_exception_store_type_unregister ( struct dm_exception_store_type * type )
{
spin_lock ( & _lock ) ;
if ( ! __find_exception_store_type ( type - > name ) ) {
spin_unlock ( & _lock ) ;
return - EINVAL ;
}
list_del ( & type - > list ) ;
spin_unlock ( & _lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( dm_exception_store_type_unregister ) ;
2009-04-02 22:55:34 +04:00
/*
* Round a number up to the nearest ' size ' boundary . size must
* be a power of 2.
*/
static ulong round_up ( ulong n , ulong size )
{
size - - ;
return ( n + size ) & ~ size ;
}
static int set_chunk_size ( struct dm_exception_store * store ,
const char * chunk_size_arg , char * * error )
{
unsigned long chunk_size_ulong ;
char * value ;
chunk_size_ulong = simple_strtoul ( chunk_size_arg , & value , 10 ) ;
if ( * chunk_size_arg = = ' \0 ' | | * value ! = ' \0 ' ) {
* error = " Invalid chunk size " ;
return - EINVAL ;
}
if ( ! chunk_size_ulong ) {
store - > chunk_size = store - > chunk_mask = store - > chunk_shift = 0 ;
return 0 ;
}
/*
* Chunk size must be multiple of page size . Silently
* round up if it ' s not .
*/
chunk_size_ulong = round_up ( chunk_size_ulong , PAGE_SIZE > > 9 ) ;
/* Check chunk_size is a power of 2 */
if ( ! is_power_of_2 ( chunk_size_ulong ) ) {
* error = " Chunk size is not a power of 2 " ;
return - EINVAL ;
}
/* Validate the chunk size against the device block size */
2009-05-23 01:17:49 +04:00
if ( chunk_size_ulong % ( bdev_logical_block_size ( store - > cow - > bdev ) > > 9 ) ) {
2009-04-02 22:55:34 +04:00
* error = " Chunk size is not a multiple of device blocksize " ;
return - EINVAL ;
}
store - > chunk_size = chunk_size_ulong ;
store - > chunk_mask = chunk_size_ulong - 1 ;
store - > chunk_shift = ffs ( chunk_size_ulong ) - 1 ;
return 0 ;
}
int dm_exception_store_create ( struct dm_target * ti , int argc , char * * argv ,
unsigned * args_used ,
2009-04-02 22:55:31 +04:00
struct dm_exception_store * * store )
{
int r = 0 ;
2009-06-30 18:18:14 +04:00
struct dm_exception_store_type * type = NULL ;
2009-04-02 22:55:31 +04:00
struct dm_exception_store * tmp_store ;
2009-04-02 22:55:34 +04:00
char persistent ;
if ( argc < 3 ) {
ti - > error = " Insufficient exception store arguments " ;
return - EINVAL ;
}
2009-04-02 22:55:31 +04:00
tmp_store = kmalloc ( sizeof ( * tmp_store ) , GFP_KERNEL ) ;
2009-04-02 22:55:34 +04:00
if ( ! tmp_store ) {
ti - > error = " Exception store allocation failed " ;
2009-04-02 22:55:31 +04:00
return - ENOMEM ;
2009-04-02 22:55:34 +04:00
}
2009-04-02 22:55:31 +04:00
2009-04-02 22:55:34 +04:00
persistent = toupper ( * argv [ 1 ] ) ;
2009-06-30 18:18:14 +04:00
if ( persistent = = ' P ' )
type = get_type ( " P " ) ;
else if ( persistent = = ' N ' )
type = get_type ( " N " ) ;
else {
2009-04-02 22:55:34 +04:00
ti - > error = " Persistent flag is not P or N " ;
2009-04-02 22:55:31 +04:00
return - EINVAL ;
}
2009-04-02 22:55:34 +04:00
if ( ! type ) {
ti - > error = " Exception store type not recognised " ;
r = - EINVAL ;
goto bad_type ;
}
2009-04-02 22:55:31 +04:00
tmp_store - > type = type ;
2009-04-02 22:55:32 +04:00
tmp_store - > ti = ti ;
2009-04-02 22:55:31 +04:00
2009-04-02 22:55:34 +04:00
r = dm_get_device ( ti , argv [ 0 ] , 0 , 0 ,
FMODE_READ | FMODE_WRITE , & tmp_store - > cow ) ;
if ( r ) {
ti - > error = " Cannot get COW device " ;
goto bad_cow ;
}
2009-04-02 22:55:32 +04:00
2009-04-02 22:55:34 +04:00
r = set_chunk_size ( tmp_store , argv [ 2 ] , & ti - > error ) ;
if ( r )
goto bad_cow ;
2009-04-02 22:55:33 +04:00
2009-04-02 22:55:31 +04:00
r = type - > ctr ( tmp_store , 0 , NULL ) ;
if ( r ) {
2009-04-02 22:55:34 +04:00
ti - > error = " Exception store type constructor failed " ;
goto bad_ctr ;
2009-04-02 22:55:31 +04:00
}
2009-04-02 22:55:34 +04:00
* args_used = 3 ;
2009-04-02 22:55:31 +04:00
* store = tmp_store ;
return 0 ;
2009-04-02 22:55:34 +04:00
bad_ctr :
dm_put_device ( ti , tmp_store - > cow ) ;
bad_cow :
put_type ( type ) ;
bad_type :
kfree ( tmp_store ) ;
return r ;
2009-04-02 22:55:31 +04:00
}
EXPORT_SYMBOL ( dm_exception_store_create ) ;
void dm_exception_store_destroy ( struct dm_exception_store * store )
{
store - > type - > dtr ( store ) ;
2009-04-02 22:55:34 +04:00
dm_put_device ( store - > ti , store - > cow ) ;
2009-04-02 22:55:31 +04:00
put_type ( store - > type ) ;
kfree ( store ) ;
}
EXPORT_SYMBOL ( dm_exception_store_destroy ) ;
2009-01-06 06:05:17 +03:00
int dm_exception_store_init ( void )
2005-04-17 02:20:36 +04:00
{
int r ;
2009-01-06 06:05:17 +03:00
r = dm_transient_snapshot_init ( ) ;
if ( r ) {
DMERR ( " Unable to register transient exception store type. " ) ;
goto transient_fail ;
2005-04-17 02:20:36 +04:00
}
2009-01-06 06:05:17 +03:00
r = dm_persistent_snapshot_init ( ) ;
if ( r ) {
DMERR ( " Unable to register persistent exception store type " ) ;
goto persistent_fail ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
2009-01-06 06:05:17 +03:00
persistent_fail :
dm_persistent_snapshot_exit ( ) ;
transient_fail :
return r ;
2005-04-17 02:20:36 +04:00
}
2009-01-06 06:05:17 +03:00
void dm_exception_store_exit ( void )
2005-04-17 02:20:36 +04:00
{
2009-01-06 06:05:17 +03:00
dm_persistent_snapshot_exit ( ) ;
dm_transient_snapshot_exit ( ) ;
2005-04-17 02:20:36 +04:00
}