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(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, type: 0, mask: 0); |
38 | if (IS_ERR(ptr: new)) { |
39 | mutex_unlock(lock: &crc64_rocksoft_mutex); |
40 | return; |
41 | } |
42 | rcu_assign_pointer(crc64_rocksoft_tfm, new); |
43 | mutex_unlock(lock: &crc64_rocksoft_mutex); |
44 | |
45 | if (old) { |
46 | synchronize_rcu(); |
47 | crypto_free_shash(tfm: 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, p: 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: &desc.shash, data: 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(nb: &crc64_rocksoft_nb); |
90 | crc64_rocksoft_rehash(work: &crc64_rocksoft_rehash_work); |
91 | return 0; |
92 | } |
93 | |
94 | static void __exit crc64_rocksoft_mod_fini(void) |
95 | { |
96 | crypto_unregister_notifier(nb: &crc64_rocksoft_nb); |
97 | cancel_work_sync(work: &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(buf: buffer, fmt: "fallback\n" ); |
111 | |
112 | rcu_read_lock(); |
113 | tfm = rcu_dereference(crc64_rocksoft_tfm); |
114 | len = snprintf(buf: buffer, PAGE_SIZE, fmt: "%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 | |