127 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 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");127