2023-01-25 23:00:44 +03:00
// SPDX-License-Identifier: GPL-2.0-only
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>
2011-07-03 21:58:33 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# 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
static int set_chunk_size ( struct dm_exception_store * store ,
const char * chunk_size_arg , char * * error )
{
2023-01-25 23:14:58 +03:00
unsigned int chunk_size ;
2009-04-02 22:55:34 +04:00
2012-07-27 18:07:59 +04:00
if ( kstrtouint ( chunk_size_arg , 10 , & chunk_size ) ) {
2009-04-02 22:55:34 +04:00
* error = " Invalid chunk size " ;
return - EINVAL ;
}
2012-07-27 18:07:59 +04:00
if ( ! chunk_size ) {
2009-04-02 22:55:34 +04:00
store - > chunk_size = store - > chunk_mask = store - > chunk_shift = 0 ;
return 0 ;
}
2012-07-27 18:07:59 +04:00
return dm_exception_store_set_chunk_size ( store , chunk_size , error ) ;
2009-09-04 23:40:41 +04:00
}
int dm_exception_store_set_chunk_size ( struct dm_exception_store * store ,
2023-01-25 23:14:58 +03:00
unsigned int chunk_size ,
2009-09-04 23:40:41 +04:00
char * * error )
{
2009-04-02 22:55:34 +04:00
/* Check chunk_size is a power of 2 */
2009-10-17 02:18:17 +04:00
if ( ! is_power_of_2 ( chunk_size ) ) {
2009-04-02 22:55:34 +04:00
* error = " Chunk size is not a power of 2 " ;
return - EINVAL ;
}
/* Validate the chunk size against the device block size */
2009-12-11 02:52:12 +03:00
if ( chunk_size %
2010-08-12 07:13:51 +04:00
( bdev_logical_block_size ( dm_snap_cow ( store - > snap ) - > bdev ) > > 9 ) | |
chunk_size %
( bdev_logical_block_size ( dm_snap_origin ( store - > snap ) - > bdev ) > > 9 ) ) {
2009-04-02 22:55:34 +04:00
* error = " Chunk size is not a multiple of device blocksize " ;
return - EINVAL ;
}
2009-10-17 02:18:17 +04:00
if ( chunk_size > INT_MAX > > SECTOR_SHIFT ) {
2009-09-04 23:40:43 +04:00
* error = " Chunk size is too high " ;
return - EINVAL ;
}
2009-10-17 02:18:17 +04:00
store - > chunk_size = chunk_size ;
store - > chunk_mask = chunk_size - 1 ;
2015-10-02 18:21:24 +03:00
store - > chunk_shift = __ffs ( chunk_size ) ;
2009-04-02 22:55:34 +04:00
return 0 ;
}
int dm_exception_store_create ( struct dm_target * ti , int argc , char * * argv ,
2009-12-11 02:52:12 +03:00
struct dm_snapshot * snap ,
2023-01-25 23:14:58 +03:00
unsigned int * 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 ;
2009-12-11 02:52:12 +03:00
if ( argc < 2 ) {
2009-04-02 22:55:34 +04:00
ti - > error = " Insufficient exception store arguments " ;
return - EINVAL ;
}
2009-04-02 22:55:31 +04:00
2015-10-09 01:05:41 +03:00
tmp_store = kzalloc ( 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-12-11 02:52:12 +03:00
persistent = toupper ( * argv [ 0 ] ) ;
2009-06-30 18:18:14 +04:00
if ( persistent = = ' P ' )
type = get_type ( " P " ) ;
else if ( persistent = = ' N ' )
type = get_type ( " N " ) ;
else {
2015-10-09 01:05:41 +03:00
ti - > error = " Exception store type is not P or N " ;
2009-12-11 02:51:52 +03:00
r = - EINVAL ;
goto bad_type ;
2009-04-02 22:55:31 +04:00
}
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-12-11 02:52:12 +03:00
tmp_store - > snap = snap ;
2009-04-02 22:55:31 +04:00
2009-12-11 02:52:12 +03:00
r = set_chunk_size ( tmp_store , argv [ 1 ] , & ti - > error ) ;
2009-04-02 22:55:34 +04:00
if ( r )
2009-12-11 02:52:12 +03:00
goto bad ;
2009-04-02 22:55:33 +04:00
2015-10-09 01:05:41 +03:00
r = type - > ctr ( tmp_store , ( strlen ( argv [ 0 ] ) > 1 ? & argv [ 0 ] [ 1 ] : NULL ) ) ;
2009-04-02 22:55:31 +04:00
if ( r ) {
2009-04-02 22:55:34 +04:00
ti - > error = " Exception store type constructor failed " ;
2009-12-11 02:52:12 +03:00
goto bad ;
2009-04-02 22:55:31 +04:00
}
2009-12-11 02:52:12 +03:00
* args_used = 2 ;
2009-04-02 22:55:31 +04:00
* store = tmp_store ;
return 0 ;
2009-04-02 22:55:34 +04:00
2009-12-11 02:52:12 +03:00
bad :
2009-04-02 22:55:34 +04:00
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 ) ;
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 :
2012-03-28 21:41:22 +04:00
dm_transient_snapshot_exit ( ) ;
2009-01-06 06:05:17 +03:00
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
}