xref: /OK3568_Linux_fs/kernel/lib/crc-t10dif.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * T10 Data Integrity Field CRC16 calculation
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2007 Oracle Corporation.  All rights reserved.
6*4882a593Smuzhiyun  * Written by Martin K. Petersen <martin.petersen@oracle.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/types.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/crc-t10dif.h>
12*4882a593Smuzhiyun #include <linux/err.h>
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun #include <crypto/hash.h>
15*4882a593Smuzhiyun #include <crypto/algapi.h>
16*4882a593Smuzhiyun #include <linux/static_key.h>
17*4882a593Smuzhiyun #include <linux/notifier.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun static struct crypto_shash __rcu *crct10dif_tfm;
20*4882a593Smuzhiyun static DEFINE_STATIC_KEY_TRUE(crct10dif_fallback);
21*4882a593Smuzhiyun static DEFINE_MUTEX(crc_t10dif_mutex);
22*4882a593Smuzhiyun static struct work_struct crct10dif_rehash_work;
23*4882a593Smuzhiyun 
crc_t10dif_notify(struct notifier_block * self,unsigned long val,void * data)24*4882a593Smuzhiyun static int crc_t10dif_notify(struct notifier_block *self, unsigned long val, void *data)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	struct crypto_alg *alg = data;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	if (val != CRYPTO_MSG_ALG_LOADED ||
29*4882a593Smuzhiyun 	    strcmp(alg->cra_name, CRC_T10DIF_STRING))
30*4882a593Smuzhiyun 		return NOTIFY_DONE;
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	schedule_work(&crct10dif_rehash_work);
33*4882a593Smuzhiyun 	return NOTIFY_OK;
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun 
crc_t10dif_rehash(struct work_struct * work)36*4882a593Smuzhiyun static void crc_t10dif_rehash(struct work_struct *work)
37*4882a593Smuzhiyun {
38*4882a593Smuzhiyun 	struct crypto_shash *new, *old;
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	mutex_lock(&crc_t10dif_mutex);
41*4882a593Smuzhiyun 	old = rcu_dereference_protected(crct10dif_tfm,
42*4882a593Smuzhiyun 					lockdep_is_held(&crc_t10dif_mutex));
43*4882a593Smuzhiyun 	new = crypto_alloc_shash(CRC_T10DIF_STRING, 0, 0);
44*4882a593Smuzhiyun 	if (IS_ERR(new)) {
45*4882a593Smuzhiyun 		mutex_unlock(&crc_t10dif_mutex);
46*4882a593Smuzhiyun 		return;
47*4882a593Smuzhiyun 	}
48*4882a593Smuzhiyun 	rcu_assign_pointer(crct10dif_tfm, new);
49*4882a593Smuzhiyun 	mutex_unlock(&crc_t10dif_mutex);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	if (old) {
52*4882a593Smuzhiyun 		synchronize_rcu();
53*4882a593Smuzhiyun 		crypto_free_shash(old);
54*4882a593Smuzhiyun 	} else {
55*4882a593Smuzhiyun 		static_branch_disable(&crct10dif_fallback);
56*4882a593Smuzhiyun 	}
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun static struct notifier_block crc_t10dif_nb = {
60*4882a593Smuzhiyun 	.notifier_call = crc_t10dif_notify,
61*4882a593Smuzhiyun };
62*4882a593Smuzhiyun 
crc_t10dif_update(__u16 crc,const unsigned char * buffer,size_t len)63*4882a593Smuzhiyun __u16 crc_t10dif_update(__u16 crc, const unsigned char *buffer, size_t len)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun 	struct {
66*4882a593Smuzhiyun 		struct shash_desc shash;
67*4882a593Smuzhiyun 		__u16 crc;
68*4882a593Smuzhiyun 	} desc;
69*4882a593Smuzhiyun 	int err;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	if (static_branch_unlikely(&crct10dif_fallback))
72*4882a593Smuzhiyun 		return crc_t10dif_generic(crc, buffer, len);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	rcu_read_lock();
75*4882a593Smuzhiyun 	desc.shash.tfm = rcu_dereference(crct10dif_tfm);
76*4882a593Smuzhiyun 	desc.crc = crc;
77*4882a593Smuzhiyun 	err = crypto_shash_update(&desc.shash, buffer, len);
78*4882a593Smuzhiyun 	rcu_read_unlock();
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	BUG_ON(err);
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	return desc.crc;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun EXPORT_SYMBOL(crc_t10dif_update);
85*4882a593Smuzhiyun 
crc_t10dif(const unsigned char * buffer,size_t len)86*4882a593Smuzhiyun __u16 crc_t10dif(const unsigned char *buffer, size_t len)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun 	return crc_t10dif_update(0, buffer, len);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun EXPORT_SYMBOL(crc_t10dif);
91*4882a593Smuzhiyun 
crc_t10dif_mod_init(void)92*4882a593Smuzhiyun static int __init crc_t10dif_mod_init(void)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	INIT_WORK(&crct10dif_rehash_work, crc_t10dif_rehash);
95*4882a593Smuzhiyun 	crypto_register_notifier(&crc_t10dif_nb);
96*4882a593Smuzhiyun 	crc_t10dif_rehash(&crct10dif_rehash_work);
97*4882a593Smuzhiyun 	return 0;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun 
crc_t10dif_mod_fini(void)100*4882a593Smuzhiyun static void __exit crc_t10dif_mod_fini(void)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun 	crypto_unregister_notifier(&crc_t10dif_nb);
103*4882a593Smuzhiyun 	cancel_work_sync(&crct10dif_rehash_work);
104*4882a593Smuzhiyun 	crypto_free_shash(rcu_dereference_protected(crct10dif_tfm, 1));
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun module_init(crc_t10dif_mod_init);
108*4882a593Smuzhiyun module_exit(crc_t10dif_mod_fini);
109*4882a593Smuzhiyun 
crc_t10dif_transform_show(char * buffer,const struct kernel_param * kp)110*4882a593Smuzhiyun static int crc_t10dif_transform_show(char *buffer, const struct kernel_param *kp)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	struct crypto_shash *tfm;
113*4882a593Smuzhiyun 	int len;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	if (static_branch_unlikely(&crct10dif_fallback))
116*4882a593Smuzhiyun 		return sprintf(buffer, "fallback\n");
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	rcu_read_lock();
119*4882a593Smuzhiyun 	tfm = rcu_dereference(crct10dif_tfm);
120*4882a593Smuzhiyun 	len = snprintf(buffer, PAGE_SIZE, "%s\n",
121*4882a593Smuzhiyun 		       crypto_shash_driver_name(tfm));
122*4882a593Smuzhiyun 	rcu_read_unlock();
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	return len;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun module_param_call(transform, NULL, crc_t10dif_transform_show, NULL, 0444);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun MODULE_DESCRIPTION("T10 DIF CRC calculation (library API)");
130*4882a593Smuzhiyun MODULE_LICENSE("GPL");
131*4882a593Smuzhiyun MODULE_SOFTDEP("pre: crct10dif");
132