xref: /openbmc/linux/lib/crc-t10dif.c (revision 29195232)
140b0b3f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f11f594eSMartin K. Petersen /*
3f11f594eSMartin K. Petersen  * T10 Data Integrity Field CRC16 calculation
4f11f594eSMartin K. Petersen  *
5f11f594eSMartin K. Petersen  * Copyright (c) 2007 Oracle Corporation.  All rights reserved.
6f11f594eSMartin K. Petersen  * Written by Martin K. Petersen <martin.petersen@oracle.com>
7f11f594eSMartin K. Petersen  */
8f11f594eSMartin K. Petersen 
9f11f594eSMartin K. Petersen #include <linux/types.h>
10f11f594eSMartin K. Petersen #include <linux/module.h>
11f11f594eSMartin K. Petersen #include <linux/crc-t10dif.h>
1268411521SHerbert Xu #include <linux/err.h>
1368411521SHerbert Xu #include <linux/init.h>
1468411521SHerbert Xu #include <crypto/hash.h>
15b7637754SMartin K. Petersen #include <crypto/algapi.h>
1626052f9bSHerbert Xu #include <linux/static_key.h>
17b7637754SMartin K. Petersen #include <linux/notifier.h>
18f11f594eSMartin K. Petersen 
19b7637754SMartin K. Petersen static struct crypto_shash __rcu *crct10dif_tfm;
20be924e0aSEric Biggers static DEFINE_STATIC_KEY_TRUE(crct10dif_fallback);
21a7e7edfeSkbuild test robot static DEFINE_MUTEX(crc_t10dif_mutex);
223906f640SHerbert Xu static struct work_struct crct10dif_rehash_work;
23b7637754SMartin K. Petersen 
crc_t10dif_notify(struct notifier_block * self,unsigned long val,void * data)243906f640SHerbert Xu static int crc_t10dif_notify(struct notifier_block *self, unsigned long val, void *data)
25b7637754SMartin K. Petersen {
26b7637754SMartin K. Petersen 	struct crypto_alg *alg = data;
27b7637754SMartin K. Petersen 
28b7637754SMartin K. Petersen 	if (val != CRYPTO_MSG_ALG_LOADED ||
2929195232SEric Biggers 	    strcmp(alg->cra_name, CRC_T10DIF_STRING))
3029195232SEric Biggers 		return NOTIFY_DONE;
31b7637754SMartin K. Petersen 
323906f640SHerbert Xu 	schedule_work(&crct10dif_rehash_work);
3329195232SEric Biggers 	return NOTIFY_OK;
343906f640SHerbert Xu }
353906f640SHerbert Xu 
crc_t10dif_rehash(struct work_struct * work)363906f640SHerbert Xu static void crc_t10dif_rehash(struct work_struct *work)
373906f640SHerbert Xu {
383906f640SHerbert Xu 	struct crypto_shash *new, *old;
393906f640SHerbert Xu 
40b7637754SMartin K. Petersen 	mutex_lock(&crc_t10dif_mutex);
41b7637754SMartin K. Petersen 	old = rcu_dereference_protected(crct10dif_tfm,
42b7637754SMartin K. Petersen 					lockdep_is_held(&crc_t10dif_mutex));
4329195232SEric Biggers 	new = crypto_alloc_shash(CRC_T10DIF_STRING, 0, 0);
44b7637754SMartin K. Petersen 	if (IS_ERR(new)) {
45b7637754SMartin K. Petersen 		mutex_unlock(&crc_t10dif_mutex);
463906f640SHerbert Xu 		return;
47b7637754SMartin K. Petersen 	}
48b7637754SMartin K. Petersen 	rcu_assign_pointer(crct10dif_tfm, new);
49b7637754SMartin K. Petersen 	mutex_unlock(&crc_t10dif_mutex);
50b7637754SMartin K. Petersen 
51be924e0aSEric Biggers 	if (old) {
52b7637754SMartin K. Petersen 		synchronize_rcu();
53b7637754SMartin K. Petersen 		crypto_free_shash(old);
54be924e0aSEric Biggers 	} else {
55be924e0aSEric Biggers 		static_branch_disable(&crct10dif_fallback);
56be924e0aSEric Biggers 	}
57b7637754SMartin K. Petersen }
58b7637754SMartin K. Petersen 
59b7637754SMartin K. Petersen static struct notifier_block crc_t10dif_nb = {
603906f640SHerbert Xu 	.notifier_call = crc_t10dif_notify,
61b7637754SMartin K. Petersen };
62f11f594eSMartin K. Petersen 
crc_t10dif_update(__u16 crc,const unsigned char * buffer,size_t len)6310081fb5SAkinobu Mita __u16 crc_t10dif_update(__u16 crc, const unsigned char *buffer, size_t len)
64f11f594eSMartin K. Petersen {
6568411521SHerbert Xu 	struct {
6668411521SHerbert Xu 		struct shash_desc shash;
6729195232SEric Biggers 		__u16 crc;
6868411521SHerbert Xu 	} desc;
6968411521SHerbert Xu 	int err;
70f11f594eSMartin K. Petersen 
71be924e0aSEric Biggers 	if (static_branch_unlikely(&crct10dif_fallback))
7210081fb5SAkinobu Mita 		return crc_t10dif_generic(crc, buffer, len);
7326052f9bSHerbert Xu 
74b7637754SMartin K. Petersen 	rcu_read_lock();
75b7637754SMartin K. Petersen 	desc.shash.tfm = rcu_dereference(crct10dif_tfm);
7629195232SEric Biggers 	desc.crc = crc;
7768411521SHerbert Xu 	err = crypto_shash_update(&desc.shash, buffer, len);
78b7637754SMartin K. Petersen 	rcu_read_unlock();
79b7637754SMartin K. Petersen 
8068411521SHerbert Xu 	BUG_ON(err);
8168411521SHerbert Xu 
8229195232SEric Biggers 	return desc.crc;
83f11f594eSMartin K. Petersen }
8410081fb5SAkinobu Mita EXPORT_SYMBOL(crc_t10dif_update);
8510081fb5SAkinobu Mita 
crc_t10dif(const unsigned char * buffer,size_t len)8610081fb5SAkinobu Mita __u16 crc_t10dif(const unsigned char *buffer, size_t len)
8710081fb5SAkinobu Mita {
8810081fb5SAkinobu Mita 	return crc_t10dif_update(0, buffer, len);
8910081fb5SAkinobu Mita }
90f11f594eSMartin K. Petersen EXPORT_SYMBOL(crc_t10dif);
91f11f594eSMartin K. Petersen 
crc_t10dif_mod_init(void)9268411521SHerbert Xu static int __init crc_t10dif_mod_init(void)
9368411521SHerbert Xu {
943906f640SHerbert Xu 	INIT_WORK(&crct10dif_rehash_work, crc_t10dif_rehash);
95b7637754SMartin K. Petersen 	crypto_register_notifier(&crc_t10dif_nb);
96be924e0aSEric Biggers 	crc_t10dif_rehash(&crct10dif_rehash_work);
9726052f9bSHerbert Xu 	return 0;
9868411521SHerbert Xu }
9968411521SHerbert Xu 
crc_t10dif_mod_fini(void)10068411521SHerbert Xu static void __exit crc_t10dif_mod_fini(void)
10168411521SHerbert Xu {
102b7637754SMartin K. Petersen 	crypto_unregister_notifier(&crc_t10dif_nb);
1033906f640SHerbert Xu 	cancel_work_sync(&crct10dif_rehash_work);
1043906f640SHerbert Xu 	crypto_free_shash(rcu_dereference_protected(crct10dif_tfm, 1));
10568411521SHerbert Xu }
10668411521SHerbert Xu 
10768411521SHerbert Xu module_init(crc_t10dif_mod_init);
10868411521SHerbert Xu module_exit(crc_t10dif_mod_fini);
10968411521SHerbert Xu 
crc_t10dif_transform_show(char * buffer,const struct kernel_param * kp)11011dcb103SMartin K. Petersen static int crc_t10dif_transform_show(char *buffer, const struct kernel_param *kp)
11111dcb103SMartin K. Petersen {
1123906f640SHerbert Xu 	struct crypto_shash *tfm;
1133906f640SHerbert Xu 	int len;
1143906f640SHerbert Xu 
115be924e0aSEric Biggers 	if (static_branch_unlikely(&crct10dif_fallback))
11611dcb103SMartin K. Petersen 		return sprintf(buffer, "fallback\n");
11711dcb103SMartin K. Petersen 
1183906f640SHerbert Xu 	rcu_read_lock();
1193906f640SHerbert Xu 	tfm = rcu_dereference(crct10dif_tfm);
12029195232SEric Biggers 	len = snprintf(buffer, PAGE_SIZE, "%s\n",
12129195232SEric Biggers 		       crypto_shash_driver_name(tfm));
1223906f640SHerbert Xu 	rcu_read_unlock();
1233906f640SHerbert Xu 
1243906f640SHerbert Xu 	return len;
12511dcb103SMartin K. Petersen }
12611dcb103SMartin K. Petersen 
12729195232SEric Biggers module_param_call(transform, NULL, crc_t10dif_transform_show, NULL, 0444);
12811dcb103SMartin K. Petersen 
12929195232SEric Biggers MODULE_DESCRIPTION("T10 DIF CRC calculation (library API)");
130f11f594eSMartin K. Petersen MODULE_LICENSE("GPL");
13168411521SHerbert Xu MODULE_SOFTDEP("pre: crct10dif");
132