staging: lustre: Use ahash
[cascardo/linux.git] / drivers / staging / lustre / lustre / libcfs / linux / linux-crypto.c
1 /* GPL HEADER START
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 only,
7  * as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License version 2 for more details (a copy is included
13  * in the LICENSE file that accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License
16  * version 2 along with this program; If not, see http://www.gnu.org/licenses
17  *
18  * Please  visit http://www.xyratex.com/contact if you need additional
19  * information or have any questions.
20  *
21  * GPL HEADER END
22  */
23
24 /*
25  * Copyright 2012 Xyratex Technology Limited
26  *
27  * Copyright (c) 2012, Intel Corporation.
28  */
29
30 #include <crypto/hash.h>
31 #include <linux/scatterlist.h>
32 #include "../../../include/linux/libcfs/libcfs.h"
33 #include "linux-crypto.h"
34 /**
35  *  Array of  hash algorithm speed in MByte per second
36  */
37 static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX];
38
39 static int cfs_crypto_hash_alloc(unsigned char alg_id,
40                                  const struct cfs_crypto_hash_type **type,
41                                  struct ahash_request **req,
42                                  unsigned char *key,
43                                  unsigned int key_len)
44 {
45         struct crypto_ahash *tfm;
46         int     err = 0;
47
48         *type = cfs_crypto_hash_type(alg_id);
49
50         if (*type == NULL) {
51                 CWARN("Unsupported hash algorithm id = %d, max id is %d\n",
52                       alg_id, CFS_HASH_ALG_MAX);
53                 return -EINVAL;
54         }
55         tfm = crypto_alloc_ahash((*type)->cht_name, 0, CRYPTO_ALG_ASYNC);
56
57         if (IS_ERR(tfm)) {
58                 CDEBUG(D_INFO, "Failed to alloc crypto hash %s\n",
59                        (*type)->cht_name);
60                 return PTR_ERR(tfm);
61         }
62
63         *req = ahash_request_alloc(tfm, GFP_KERNEL);
64         if (!*req) {
65                 CDEBUG(D_INFO, "Failed to alloc ahash_request for %s\n",
66                        (*type)->cht_name);
67                 crypto_free_ahash(tfm);
68                 return -ENOMEM;
69         }
70
71         ahash_request_set_callback(*req, 0, NULL, NULL);
72
73         /** Shash have different logic for initialization then digest
74          * shash: crypto_hash_setkey, crypto_hash_init
75          * digest: crypto_digest_init, crypto_digest_setkey
76          * Skip this function for digest, because we use shash logic at
77          * cfs_crypto_hash_alloc.
78          */
79         if (key != NULL)
80                 err = crypto_ahash_setkey(tfm, key, key_len);
81         else if ((*type)->cht_key != 0)
82                 err = crypto_ahash_setkey(tfm,
83                                          (unsigned char *)&((*type)->cht_key),
84                                          (*type)->cht_size);
85
86         if (err != 0) {
87                 crypto_free_ahash(tfm);
88                 return err;
89         }
90
91         CDEBUG(D_INFO, "Using crypto hash: %s (%s) speed %d MB/s\n",
92                crypto_ahash_alg_name(tfm), crypto_ahash_driver_name(tfm),
93                cfs_crypto_hash_speeds[alg_id]);
94
95         err = crypto_ahash_init(*req);
96         if (err) {
97                 ahash_request_free(*req);
98                 crypto_free_ahash(tfm);
99         }
100         return err;
101 }
102
103 int cfs_crypto_hash_digest(unsigned char alg_id,
104                            const void *buf, unsigned int buf_len,
105                            unsigned char *key, unsigned int key_len,
106                            unsigned char *hash, unsigned int *hash_len)
107 {
108         struct scatterlist      sl;
109         struct ahash_request *req;
110         int                     err;
111         const struct cfs_crypto_hash_type       *type;
112
113         if (buf == NULL || buf_len == 0 || hash_len == NULL)
114                 return -EINVAL;
115
116         err = cfs_crypto_hash_alloc(alg_id, &type, &req, key, key_len);
117         if (err != 0)
118                 return err;
119
120         if (hash == NULL || *hash_len < type->cht_size) {
121                 *hash_len = type->cht_size;
122                 crypto_free_ahash(crypto_ahash_reqtfm(req));
123                 ahash_request_free(req);
124                 return -ENOSPC;
125         }
126         sg_init_one(&sl, buf, buf_len);
127
128         ahash_request_set_crypt(req, &sl, hash, sl.length);
129         err = crypto_ahash_digest(req);
130         crypto_free_ahash(crypto_ahash_reqtfm(req));
131         ahash_request_free(req);
132
133         return err;
134 }
135 EXPORT_SYMBOL(cfs_crypto_hash_digest);
136
137 struct cfs_crypto_hash_desc *
138         cfs_crypto_hash_init(unsigned char alg_id,
139                              unsigned char *key, unsigned int key_len)
140 {
141         struct ahash_request *req;
142         int                  err;
143         const struct cfs_crypto_hash_type       *type;
144
145         err = cfs_crypto_hash_alloc(alg_id, &type, &req, key, key_len);
146
147         if (err)
148                 return ERR_PTR(err);
149         return (struct cfs_crypto_hash_desc *)req;
150 }
151 EXPORT_SYMBOL(cfs_crypto_hash_init);
152
153 int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *hdesc,
154                                 struct page *page, unsigned int offset,
155                                 unsigned int len)
156 {
157         struct ahash_request *req = (void *)hdesc;
158         struct scatterlist sl;
159
160         sg_init_table(&sl, 1);
161         sg_set_page(&sl, page, len, offset & ~CFS_PAGE_MASK);
162
163         ahash_request_set_crypt(req, &sl, NULL, sl.length);
164         return crypto_ahash_update(req);
165 }
166 EXPORT_SYMBOL(cfs_crypto_hash_update_page);
167
168 int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *hdesc,
169                            const void *buf, unsigned int buf_len)
170 {
171         struct ahash_request *req = (void *)hdesc;
172         struct scatterlist sl;
173
174         sg_init_one(&sl, buf, buf_len);
175
176         ahash_request_set_crypt(req, &sl, NULL, sl.length);
177         return crypto_ahash_update(req);
178 }
179 EXPORT_SYMBOL(cfs_crypto_hash_update);
180
181 /*      If hash_len pointer is NULL - destroy descriptor. */
182 int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *hdesc,
183                           unsigned char *hash, unsigned int *hash_len)
184 {
185         int     err;
186         struct ahash_request *req = (void *)hdesc;
187         int size = crypto_ahash_digestsize(crypto_ahash_reqtfm(req));
188
189         if (hash_len == NULL) {
190                 crypto_free_ahash(crypto_ahash_reqtfm(req));
191                 ahash_request_free(req);
192                 return 0;
193         }
194         if (hash == NULL || *hash_len < size) {
195                 *hash_len = size;
196                 return -ENOSPC;
197         }
198         ahash_request_set_crypt(req, NULL, hash, 0);
199         err = crypto_ahash_final(req);
200
201         if (err < 0) {
202                 /* May be caller can fix error */
203                 return err;
204         }
205         crypto_free_ahash(crypto_ahash_reqtfm(req));
206         ahash_request_free(req);
207         return err;
208 }
209 EXPORT_SYMBOL(cfs_crypto_hash_final);
210
211 static void cfs_crypto_performance_test(unsigned char alg_id,
212                                         const unsigned char *buf,
213                                         unsigned int buf_len)
214 {
215         unsigned long              start, end;
216         int                          bcount, err = 0;
217         int                          sec = 1; /* do test only 1 sec */
218         unsigned char              hash[64];
219         unsigned int                hash_len = 64;
220
221         for (start = jiffies, end = start + sec * HZ, bcount = 0;
222              time_before(jiffies, end); bcount++) {
223                 err = cfs_crypto_hash_digest(alg_id, buf, buf_len, NULL, 0,
224                                              hash, &hash_len);
225                 if (err)
226                         break;
227
228         }
229         end = jiffies;
230
231         if (err) {
232                 cfs_crypto_hash_speeds[alg_id] =  -1;
233                 CDEBUG(D_INFO, "Crypto hash algorithm %s, err = %d\n",
234                        cfs_crypto_hash_name(alg_id), err);
235         } else {
236                 unsigned long   tmp;
237
238                 tmp = ((bcount * buf_len / jiffies_to_msecs(end - start)) *
239                        1000) / (1024 * 1024);
240                 cfs_crypto_hash_speeds[alg_id] = (int)tmp;
241         }
242         CDEBUG(D_INFO, "Crypto hash algorithm %s speed = %d MB/s\n",
243                cfs_crypto_hash_name(alg_id), cfs_crypto_hash_speeds[alg_id]);
244 }
245
246 int cfs_crypto_hash_speed(unsigned char hash_alg)
247 {
248         if (hash_alg < CFS_HASH_ALG_MAX)
249                 return cfs_crypto_hash_speeds[hash_alg];
250         else
251                 return -1;
252 }
253 EXPORT_SYMBOL(cfs_crypto_hash_speed);
254
255 /**
256  * Do performance test for all hash algorithms.
257  */
258 static int cfs_crypto_test_hashes(void)
259 {
260         unsigned char      i;
261         unsigned char      *data;
262         unsigned int        j;
263         /* Data block size for testing hash. Maximum
264          * kmalloc size for 2.6.18 kernel is 128K */
265         unsigned int        data_len = 1 * 128 * 1024;
266
267         data = kmalloc(data_len, 0);
268         if (data == NULL) {
269                 CERROR("Failed to allocate mem\n");
270                 return -ENOMEM;
271         }
272
273         for (j = 0; j < data_len; j++)
274                 data[j] = j & 0xff;
275
276         for (i = 0; i < CFS_HASH_ALG_MAX; i++)
277                 cfs_crypto_performance_test(i, data, data_len);
278
279         kfree(data);
280         return 0;
281 }
282
283 static int adler32;
284
285 int cfs_crypto_register(void)
286 {
287         request_module("crc32c");
288
289         adler32 = cfs_crypto_adler32_register();
290
291         /* check all algorithms and do performance test */
292         cfs_crypto_test_hashes();
293         return 0;
294 }
295
296 void cfs_crypto_unregister(void)
297 {
298         if (adler32 == 0)
299                 cfs_crypto_adler32_unregister();
300
301         return;
302 }