crc64-rocksoft.c (3283B)
1// SPDX-License-Identifier: GPL-2.0-only 2 3#include <linux/types.h> 4#include <linux/module.h> 5#include <linux/crc64.h> 6#include <linux/err.h> 7#include <linux/init.h> 8#include <crypto/hash.h> 9#include <crypto/algapi.h> 10#include <linux/static_key.h> 11#include <linux/notifier.h> 12 13static struct crypto_shash __rcu *crc64_rocksoft_tfm; 14static DEFINE_STATIC_KEY_TRUE(crc64_rocksoft_fallback); 15static DEFINE_MUTEX(crc64_rocksoft_mutex); 16static struct work_struct crc64_rocksoft_rehash_work; 17 18static int crc64_rocksoft_notify(struct notifier_block *self, unsigned long val, void *data) 19{ 20 struct crypto_alg *alg = data; 21 22 if (val != CRYPTO_MSG_ALG_LOADED || 23 strcmp(alg->cra_name, CRC64_ROCKSOFT_STRING)) 24 return NOTIFY_DONE; 25 26 schedule_work(&crc64_rocksoft_rehash_work); 27 return NOTIFY_OK; 28} 29 30static void crc64_rocksoft_rehash(struct work_struct *work) 31{ 32 struct crypto_shash *new, *old; 33 34 mutex_lock(&crc64_rocksoft_mutex); 35 old = rcu_dereference_protected(crc64_rocksoft_tfm, 36 lockdep_is_held(&crc64_rocksoft_mutex)); 37 new = crypto_alloc_shash(CRC64_ROCKSOFT_STRING, 0, 0); 38 if (IS_ERR(new)) { 39 mutex_unlock(&crc64_rocksoft_mutex); 40 return; 41 } 42 rcu_assign_pointer(crc64_rocksoft_tfm, new); 43 mutex_unlock(&crc64_rocksoft_mutex); 44 45 if (old) { 46 synchronize_rcu(); 47 crypto_free_shash(old); 48 } else { 49 static_branch_disable(&crc64_rocksoft_fallback); 50 } 51} 52 53static struct notifier_block crc64_rocksoft_nb = { 54 .notifier_call = crc64_rocksoft_notify, 55}; 56 57u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len) 58{ 59 struct { 60 struct shash_desc shash; 61 u64 crc; 62 } desc; 63 int err; 64 65 if (static_branch_unlikely(&crc64_rocksoft_fallback)) 66 return crc64_rocksoft_generic(crc, buffer, len); 67 68 rcu_read_lock(); 69 desc.shash.tfm = rcu_dereference(crc64_rocksoft_tfm); 70 desc.crc = crc; 71 err = crypto_shash_update(&desc.shash, buffer, len); 72 rcu_read_unlock(); 73 74 BUG_ON(err); 75 76 return desc.crc; 77} 78EXPORT_SYMBOL_GPL(crc64_rocksoft_update); 79 80u64 crc64_rocksoft(const unsigned char *buffer, size_t len) 81{ 82 return crc64_rocksoft_update(0, buffer, len); 83} 84EXPORT_SYMBOL_GPL(crc64_rocksoft); 85 86static int __init crc64_rocksoft_mod_init(void) 87{ 88 INIT_WORK(&crc64_rocksoft_rehash_work, crc64_rocksoft_rehash); 89 crypto_register_notifier(&crc64_rocksoft_nb); 90 crc64_rocksoft_rehash(&crc64_rocksoft_rehash_work); 91 return 0; 92} 93 94static void __exit crc64_rocksoft_mod_fini(void) 95{ 96 crypto_unregister_notifier(&crc64_rocksoft_nb); 97 cancel_work_sync(&crc64_rocksoft_rehash_work); 98 crypto_free_shash(rcu_dereference_protected(crc64_rocksoft_tfm, 1)); 99} 100 101module_init(crc64_rocksoft_mod_init); 102module_exit(crc64_rocksoft_mod_fini); 103 104static int crc64_rocksoft_transform_show(char *buffer, const struct kernel_param *kp) 105{ 106 struct crypto_shash *tfm; 107 int len; 108 109 if (static_branch_unlikely(&crc64_rocksoft_fallback)) 110 return sprintf(buffer, "fallback\n"); 111 112 rcu_read_lock(); 113 tfm = rcu_dereference(crc64_rocksoft_tfm); 114 len = snprintf(buffer, PAGE_SIZE, "%s\n", 115 crypto_shash_driver_name(tfm)); 116 rcu_read_unlock(); 117 118 return len; 119} 120 121module_param_call(transform, NULL, crc64_rocksoft_transform_show, NULL, 0444); 122 123MODULE_AUTHOR("Keith Busch <kbusch@kernel.org>"); 124MODULE_DESCRIPTION("Rocksoft model CRC64 calculation (library API)"); 125MODULE_LICENSE("GPL"); 126MODULE_SOFTDEP("pre: crc64");