2008-06-25 19:22:42 +04:00
/*
* T10 Data Integrity Field CRC16 calculation
*
* Copyright ( c ) 2007 Oracle Corporation . All rights reserved .
* Written by Martin K . Petersen < martin . petersen @ oracle . com >
*
* This source code is licensed under the GNU General Public License ,
* Version 2. See the file COPYING for more details .
*/
# include <linux/types.h>
# include <linux/module.h>
# include <linux/crc-t10dif.h>
2013-09-07 06:56:26 +04:00
# include <linux/err.h>
# include <linux/init.h>
# include <crypto/hash.h>
2018-08-30 18:00:15 +03:00
# include <crypto/algapi.h>
2013-09-12 09:31:34 +04:00
# include <linux/static_key.h>
2018-08-30 18:00:15 +03:00
# include <linux/notifier.h>
2008-06-25 19:22:42 +04:00
2018-08-30 18:00:15 +03:00
static struct crypto_shash __rcu * crct10dif_tfm ;
2013-09-12 09:31:34 +04:00
static struct static_key crct10dif_fallback __read_mostly ;
2018-09-04 20:52:44 +03:00
static DEFINE_MUTEX ( crc_t10dif_mutex ) ;
2018-08-30 18:00:15 +03:00
static int crc_t10dif_rehash ( struct notifier_block * self , unsigned long val , void * data )
{
struct crypto_alg * alg = data ;
struct crypto_shash * new , * old ;
if ( val ! = CRYPTO_MSG_ALG_LOADED | |
static_key_false ( & crct10dif_fallback ) | |
strncmp ( alg - > cra_name , CRC_T10DIF_STRING , strlen ( CRC_T10DIF_STRING ) ) )
return 0 ;
mutex_lock ( & crc_t10dif_mutex ) ;
old = rcu_dereference_protected ( crct10dif_tfm ,
lockdep_is_held ( & crc_t10dif_mutex ) ) ;
if ( ! old ) {
mutex_unlock ( & crc_t10dif_mutex ) ;
return 0 ;
}
new = crypto_alloc_shash ( " crct10dif " , 0 , 0 ) ;
if ( IS_ERR ( new ) ) {
mutex_unlock ( & crc_t10dif_mutex ) ;
return 0 ;
}
rcu_assign_pointer ( crct10dif_tfm , new ) ;
mutex_unlock ( & crc_t10dif_mutex ) ;
synchronize_rcu ( ) ;
crypto_free_shash ( old ) ;
return 0 ;
}
static struct notifier_block crc_t10dif_nb = {
. notifier_call = crc_t10dif_rehash ,
} ;
2008-06-25 19:22:42 +04:00
2015-05-01 09:23:50 +03:00
__u16 crc_t10dif_update ( __u16 crc , const unsigned char * buffer , size_t len )
2008-06-25 19:22:42 +04:00
{
2013-09-07 06:56:26 +04:00
struct {
struct shash_desc shash ;
char ctx [ 2 ] ;
} desc ;
int err ;
2013-09-12 09:31:34 +04:00
if ( static_key_false ( & crct10dif_fallback ) )
2015-05-01 09:23:50 +03:00
return crc_t10dif_generic ( crc , buffer , len ) ;
2013-09-12 09:31:34 +04:00
2018-08-30 18:00:15 +03:00
rcu_read_lock ( ) ;
desc . shash . tfm = rcu_dereference ( crct10dif_tfm ) ;
2013-09-07 06:56:26 +04:00
desc . shash . flags = 0 ;
2015-05-01 09:23:50 +03:00
* ( __u16 * ) desc . ctx = crc ;
2008-06-25 19:22:42 +04:00
2013-09-07 06:56:26 +04:00
err = crypto_shash_update ( & desc . shash , buffer , len ) ;
2018-08-30 18:00:15 +03:00
rcu_read_unlock ( ) ;
2013-09-07 06:56:26 +04:00
BUG_ON ( err ) ;
2008-06-25 19:22:42 +04:00
2013-09-07 06:56:26 +04:00
return * ( __u16 * ) desc . ctx ;
2008-06-25 19:22:42 +04:00
}
2015-05-01 09:23:50 +03:00
EXPORT_SYMBOL ( crc_t10dif_update ) ;
__u16 crc_t10dif ( const unsigned char * buffer , size_t len )
{
return crc_t10dif_update ( 0 , buffer , len ) ;
}
2008-06-25 19:22:42 +04:00
EXPORT_SYMBOL ( crc_t10dif ) ;
2013-09-07 06:56:26 +04:00
static int __init crc_t10dif_mod_init ( void )
{
2018-08-30 18:00:15 +03:00
crypto_register_notifier ( & crc_t10dif_nb ) ;
2013-09-07 06:56:26 +04:00
crct10dif_tfm = crypto_alloc_shash ( " crct10dif " , 0 , 0 ) ;
2013-09-12 09:31:34 +04:00
if ( IS_ERR ( crct10dif_tfm ) ) {
static_key_slow_inc ( & crct10dif_fallback ) ;
crct10dif_tfm = NULL ;
}
return 0 ;
2013-09-07 06:56:26 +04:00
}
static void __exit crc_t10dif_mod_fini ( void )
{
2018-08-30 18:00:15 +03:00
crypto_unregister_notifier ( & crc_t10dif_nb ) ;
2013-09-07 06:56:26 +04:00
crypto_free_shash ( crct10dif_tfm ) ;
}
module_init ( crc_t10dif_mod_init ) ;
module_exit ( crc_t10dif_mod_fini ) ;
2018-08-30 18:00:16 +03:00
static int crc_t10dif_transform_show ( char * buffer , const struct kernel_param * kp )
{
if ( static_key_false ( & crct10dif_fallback ) )
return sprintf ( buffer , " fallback \n " ) ;
return sprintf ( buffer , " %s \n " ,
crypto_tfm_alg_driver_name ( crypto_shash_tfm ( crct10dif_tfm ) ) ) ;
}
module_param_call ( transform , NULL , crc_t10dif_transform_show , NULL , 0644 ) ;
2008-06-25 19:22:42 +04:00
MODULE_DESCRIPTION ( " T10 DIF CRC calculation " ) ;
MODULE_LICENSE ( " GPL " ) ;
2013-09-07 06:56:26 +04:00
MODULE_SOFTDEP ( " pre: crct10dif " ) ;