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 13 static struct crypto_shash __rcu *crc64_rocksoft_tfm; 14 static DEFINE_STATIC_KEY_TRUE(crc64_rocksoft_fallback); 15 static DEFINE_MUTEX(crc64_rocksoft_mutex); 16 static struct work_struct crc64_rocksoft_rehash_work; 17 18 static 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 30 static 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 53 static struct notifier_block crc64_rocksoft_nb = { 54 .notifier_call = crc64_rocksoft_notify, 55 }; 56 57 u64 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 } 78 EXPORT_SYMBOL_GPL(crc64_rocksoft_update); 79 80 u64 crc64_rocksoft(const unsigned char *buffer, size_t len) 81 { 82 return crc64_rocksoft_update(0, buffer, len); 83 } 84 EXPORT_SYMBOL_GPL(crc64_rocksoft); 85 86 static 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 94 static 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 101 module_init(crc64_rocksoft_mod_init); 102 module_exit(crc64_rocksoft_mod_fini); 103 104 static 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 121 module_param_call(transform, NULL, crc64_rocksoft_transform_show, NULL, 0444); 122 123 MODULE_AUTHOR("Keith Busch <kbusch@kernel.org>"); 124 MODULE_DESCRIPTION("Rocksoft model CRC64 calculation (library API)"); 125 MODULE_LICENSE("GPL"); 126 MODULE_SOFTDEP("pre: crc64"); 127