2019-06-03 08:44:46 +03:00
// SPDX-License-Identifier: GPL-2.0-only
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 >
*/
# 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 ;
2020-06-10 09:39:42 +03:00
static DEFINE_STATIC_KEY_TRUE ( crct10dif_fallback ) ;
2018-09-04 20:52:44 +03:00
static DEFINE_MUTEX ( crc_t10dif_mutex ) ;
2020-06-05 09:59:18 +03:00
static struct work_struct crct10dif_rehash_work ;
2018-08-30 18:00:15 +03:00
2020-06-05 09:59:18 +03:00
static int crc_t10dif_notify ( struct notifier_block * self , unsigned long val , void * data )
2018-08-30 18:00:15 +03:00
{
struct crypto_alg * alg = data ;
if ( val ! = CRYPTO_MSG_ALG_LOADED | |
2020-06-10 09:39:43 +03:00
strcmp ( alg - > cra_name , CRC_T10DIF_STRING ) )
return NOTIFY_DONE ;
2018-08-30 18:00:15 +03:00
2020-06-05 09:59:18 +03:00
schedule_work ( & crct10dif_rehash_work ) ;
2020-06-10 09:39:43 +03:00
return NOTIFY_OK ;
2020-06-05 09:59:18 +03:00
}
static void crc_t10dif_rehash ( struct work_struct * work )
{
struct crypto_shash * new , * old ;
2018-08-30 18:00:15 +03:00
mutex_lock ( & crc_t10dif_mutex ) ;
old = rcu_dereference_protected ( crct10dif_tfm ,
lockdep_is_held ( & crc_t10dif_mutex ) ) ;
2020-06-10 09:39:43 +03:00
new = crypto_alloc_shash ( CRC_T10DIF_STRING , 0 , 0 ) ;
2018-08-30 18:00:15 +03:00
if ( IS_ERR ( new ) ) {
mutex_unlock ( & crc_t10dif_mutex ) ;
2020-06-05 09:59:18 +03:00
return ;
2018-08-30 18:00:15 +03:00
}
rcu_assign_pointer ( crct10dif_tfm , new ) ;
mutex_unlock ( & crc_t10dif_mutex ) ;
2020-06-10 09:39:42 +03:00
if ( old ) {
synchronize_rcu ( ) ;
crypto_free_shash ( old ) ;
} else {
static_branch_disable ( & crct10dif_fallback ) ;
}
2018-08-30 18:00:15 +03:00
}
static struct notifier_block crc_t10dif_nb = {
2020-06-05 09:59:18 +03:00
. notifier_call = crc_t10dif_notify ,
2018-08-30 18:00:15 +03:00
} ;
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 ;
2020-06-10 09:39:43 +03:00
__u16 crc ;
2013-09-07 06:56:26 +04:00
} desc ;
int err ;
2020-06-10 09:39:42 +03:00
if ( static_branch_unlikely ( & 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 ) ;
2020-06-10 09:39:43 +03:00
desc . crc = crc ;
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
2020-06-10 09:39:43 +03:00
return desc . crc ;
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 )
{
2020-06-05 09:59:18 +03:00
INIT_WORK ( & crct10dif_rehash_work , crc_t10dif_rehash ) ;
2018-08-30 18:00:15 +03:00
crypto_register_notifier ( & crc_t10dif_nb ) ;
2020-06-10 09:39:42 +03:00
crc_t10dif_rehash ( & crct10dif_rehash_work ) ;
2013-09-12 09:31:34 +04:00
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 ) ;
2020-06-05 09:59:18 +03:00
cancel_work_sync ( & crct10dif_rehash_work ) ;
crypto_free_shash ( rcu_dereference_protected ( crct10dif_tfm , 1 ) ) ;
2013-09-07 06:56:26 +04:00
}
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 )
{
2020-06-05 09:59:18 +03:00
struct crypto_shash * tfm ;
int len ;
2020-06-10 09:39:42 +03:00
if ( static_branch_unlikely ( & crct10dif_fallback ) )
2018-08-30 18:00:16 +03:00
return sprintf ( buffer , " fallback \n " ) ;
2020-06-05 09:59:18 +03:00
rcu_read_lock ( ) ;
tfm = rcu_dereference ( crct10dif_tfm ) ;
2020-06-10 09:39:43 +03:00
len = snprintf ( buffer , PAGE_SIZE , " %s \n " ,
crypto_shash_driver_name ( tfm ) ) ;
2020-06-05 09:59:18 +03:00
rcu_read_unlock ( ) ;
return len ;
2018-08-30 18:00:16 +03:00
}
2020-06-10 09:39:43 +03:00
module_param_call ( transform , NULL , crc_t10dif_transform_show , NULL , 0444 ) ;
2018-08-30 18:00:16 +03:00
2020-06-10 09:39:43 +03:00
MODULE_DESCRIPTION ( " T10 DIF CRC calculation (library API) " ) ;
2008-06-25 19:22:42 +04:00
MODULE_LICENSE ( " GPL " ) ;
2013-09-07 06:56:26 +04:00
MODULE_SOFTDEP ( " pre: crct10dif " ) ;