hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
kernel/lib/crc-t10dif.c
....@@ -1,11 +1,9 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * T10 Data Integrity Field CRC16 calculation
34 *
45 * Copyright (c) 2007 Oracle Corporation. All rights reserved.
56 * Written by Martin K. Petersen <martin.petersen@oracle.com>
6
- *
7
- * This source code is licensed under the GNU General Public License,
8
- * Version 2. See the file COPYING for more details.
97 */
108
119 #include <linux/types.h>
....@@ -14,30 +12,74 @@
1412 #include <linux/err.h>
1513 #include <linux/init.h>
1614 #include <crypto/hash.h>
15
+#include <crypto/algapi.h>
1716 #include <linux/static_key.h>
17
+#include <linux/notifier.h>
1818
19
-static struct crypto_shash *crct10dif_tfm;
20
-static struct static_key crct10dif_fallback __read_mostly;
19
+static struct crypto_shash __rcu *crct10dif_tfm;
20
+static DEFINE_STATIC_KEY_TRUE(crct10dif_fallback);
21
+static DEFINE_MUTEX(crc_t10dif_mutex);
22
+static struct work_struct crct10dif_rehash_work;
23
+
24
+static int crc_t10dif_notify(struct notifier_block *self, unsigned long val, void *data)
25
+{
26
+ struct crypto_alg *alg = data;
27
+
28
+ if (val != CRYPTO_MSG_ALG_LOADED ||
29
+ strcmp(alg->cra_name, CRC_T10DIF_STRING))
30
+ return NOTIFY_DONE;
31
+
32
+ schedule_work(&crct10dif_rehash_work);
33
+ return NOTIFY_OK;
34
+}
35
+
36
+static void crc_t10dif_rehash(struct work_struct *work)
37
+{
38
+ struct crypto_shash *new, *old;
39
+
40
+ mutex_lock(&crc_t10dif_mutex);
41
+ old = rcu_dereference_protected(crct10dif_tfm,
42
+ lockdep_is_held(&crc_t10dif_mutex));
43
+ new = crypto_alloc_shash(CRC_T10DIF_STRING, 0, 0);
44
+ if (IS_ERR(new)) {
45
+ mutex_unlock(&crc_t10dif_mutex);
46
+ return;
47
+ }
48
+ rcu_assign_pointer(crct10dif_tfm, new);
49
+ mutex_unlock(&crc_t10dif_mutex);
50
+
51
+ if (old) {
52
+ synchronize_rcu();
53
+ crypto_free_shash(old);
54
+ } else {
55
+ static_branch_disable(&crct10dif_fallback);
56
+ }
57
+}
58
+
59
+static struct notifier_block crc_t10dif_nb = {
60
+ .notifier_call = crc_t10dif_notify,
61
+};
2162
2263 __u16 crc_t10dif_update(__u16 crc, const unsigned char *buffer, size_t len)
2364 {
2465 struct {
2566 struct shash_desc shash;
26
- char ctx[2];
67
+ __u16 crc;
2768 } desc;
2869 int err;
2970
30
- if (static_key_false(&crct10dif_fallback))
71
+ if (static_branch_unlikely(&crct10dif_fallback))
3172 return crc_t10dif_generic(crc, buffer, len);
3273
33
- desc.shash.tfm = crct10dif_tfm;
34
- desc.shash.flags = 0;
35
- *(__u16 *)desc.ctx = crc;
36
-
74
+ rcu_read_lock();
75
+ desc.shash.tfm = rcu_dereference(crct10dif_tfm);
76
+ desc.crc = crc;
3777 err = crypto_shash_update(&desc.shash, buffer, len);
78
+ rcu_read_unlock();
79
+
3880 BUG_ON(err);
3981
40
- return *(__u16 *)desc.ctx;
82
+ return desc.crc;
4183 }
4284 EXPORT_SYMBOL(crc_t10dif_update);
4385
....@@ -49,22 +91,41 @@
4991
5092 static int __init crc_t10dif_mod_init(void)
5193 {
52
- crct10dif_tfm = crypto_alloc_shash("crct10dif", 0, 0);
53
- if (IS_ERR(crct10dif_tfm)) {
54
- static_key_slow_inc(&crct10dif_fallback);
55
- crct10dif_tfm = NULL;
56
- }
94
+ INIT_WORK(&crct10dif_rehash_work, crc_t10dif_rehash);
95
+ crypto_register_notifier(&crc_t10dif_nb);
96
+ crc_t10dif_rehash(&crct10dif_rehash_work);
5797 return 0;
5898 }
5999
60100 static void __exit crc_t10dif_mod_fini(void)
61101 {
62
- crypto_free_shash(crct10dif_tfm);
102
+ crypto_unregister_notifier(&crc_t10dif_nb);
103
+ cancel_work_sync(&crct10dif_rehash_work);
104
+ crypto_free_shash(rcu_dereference_protected(crct10dif_tfm, 1));
63105 }
64106
65107 module_init(crc_t10dif_mod_init);
66108 module_exit(crc_t10dif_mod_fini);
67109
68
-MODULE_DESCRIPTION("T10 DIF CRC calculation");
110
+static int crc_t10dif_transform_show(char *buffer, const struct kernel_param *kp)
111
+{
112
+ struct crypto_shash *tfm;
113
+ int len;
114
+
115
+ if (static_branch_unlikely(&crct10dif_fallback))
116
+ return sprintf(buffer, "fallback\n");
117
+
118
+ rcu_read_lock();
119
+ tfm = rcu_dereference(crct10dif_tfm);
120
+ len = snprintf(buffer, PAGE_SIZE, "%s\n",
121
+ crypto_shash_driver_name(tfm));
122
+ rcu_read_unlock();
123
+
124
+ return len;
125
+}
126
+
127
+module_param_call(transform, NULL, crc_t10dif_transform_show, NULL, 0444);
128
+
129
+MODULE_DESCRIPTION("T10 DIF CRC calculation (library API)");
69130 MODULE_LICENSE("GPL");
70131 MODULE_SOFTDEP("pre: crct10dif");