Whamcloud - gitweb
LU-4976 doc: comments to osp_precreate.c
[fs/lustre-release.git] / libcfs / libcfs / user-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
28 /*
29  * Libcfs crypto hash interfaces for user mode.
30  */
31
32 #include <libcfs/libcfs.h>
33 #include <libcfs/libcfs_crypto.h>
34 #include <libcfs/posix/posix-crypto.h>
35 #include <libcfs/user-crypto.h>
36
37 /**
38  *  Array of hash algorithm speed in MByte per second
39  */
40 static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX];
41
42 struct __hash_alg {
43         /**
44          * Initialization of algorithm
45          */
46         int (*init)(void);
47         /**
48          * Start function for the hash instance
49          */
50         int (*start)(void *ctx, unsigned char *p, unsigned int len);
51         /**
52          * Partial update checksum
53          */
54         int (*update)(void *ctx, const unsigned char *p, unsigned int len);
55         /**
56          * Final function for the instance destroy context and copy digest
57          */
58         int (*final)(void *ctx, unsigned char *p, unsigned int len);
59         /**
60          * Destroy algorithm
61          */
62         void (*fini)(void);
63         unsigned int    ha_ctx_size;    /**< size of context */
64         unsigned int    ha_priority;    /**< implementation priority
65                                              defined by developer
66                                              to get one from equal algorithm */
67         unsigned char   ha_id;    /**< algorithm identifier */
68 };
69
70 struct hash_desc {
71         const struct __hash_alg *hd_hash;
72         unsigned char   hd_ctx[0];
73 };
74
75 static int crc32_update_wrapper(void *ctx, const unsigned char *p,
76                                 unsigned int len)
77 {
78         unsigned int crc = *(unsigned int *)ctx;
79
80         crc = crc32_le(crc, p, len);
81
82         *(unsigned int *)ctx = crc;
83         return 0;
84 }
85
86 static int adler_wrapper(void *ctx, const unsigned char *p,
87                                 unsigned int len)
88 {
89         unsigned int cksum = *(unsigned int *)ctx;
90
91         cksum = zlib_adler32(cksum, p, len);
92
93         *(unsigned int *)ctx = cksum;
94         return 0;
95 }
96
97 #if defined(HAVE_PCLMULQDQ) && defined(NEED_CRC32_ACCEL)
98 static int crc32_pclmul_wrapper(void *ctx, const unsigned char *p,
99                                 unsigned int len)
100 {
101         unsigned int cksum = *(unsigned int *)ctx;
102
103         cksum = crc32_pclmul_le(cksum, p, len);
104
105         *(unsigned int *)ctx = cksum;
106         return 0;
107 }
108 #endif
109
110 static int start_generic(void *ctx, unsigned char *key,
111                          unsigned int key_len)
112 {
113         const struct cfs_crypto_hash_type       *type;
114         struct hash_desc        *hd = container_of(ctx, struct hash_desc,
115                                                    hd_ctx);
116         type = cfs_crypto_hash_type(hd->hd_hash->ha_id);
117         LASSERT(type != NULL);
118
119         /* copy key to context */
120         if (key && key_len == hd->hd_hash->ha_ctx_size) {
121                 memcpy(ctx, key, key_len);
122         } else if (type->cht_key != 0) {
123                 memcpy(ctx, &type->cht_key, type->cht_size);
124         } else {
125                 CWARN("Invalid key or key_len, zero context\n");
126                 memset(ctx, 0, hd->hd_hash->ha_ctx_size);
127         }
128         return 0;
129 }
130
131 static int final_generic(void *ctx, unsigned char *hash,
132                          unsigned int hash_len)
133 {
134         const struct cfs_crypto_hash_type       *type;
135         struct hash_desc        *hd = container_of(ctx, struct hash_desc,
136                                                    hd_ctx);
137         type = cfs_crypto_hash_type(hd->hd_hash->ha_id);
138         LASSERT(type != NULL);
139          /* copy context to out hash */
140         LASSERT(hd->hd_hash->ha_ctx_size == type->cht_size);
141         memcpy(hash, ctx, hd->hd_hash->ha_ctx_size);
142
143
144         return 0;
145 }
146
147 static struct __hash_alg crypto_hash[] = {
148                                           {.ha_id = CFS_HASH_ALG_CRC32,
149                                            .ha_ctx_size = sizeof(unsigned int),
150                                            .ha_priority = 10,
151                                            .init = crc32init_le,
152                                            .update = crc32_update_wrapper,
153                                            .start = start_generic,
154                                            .final = final_generic,
155                                            .fini = NULL},
156                                           {.ha_id = CFS_HASH_ALG_ADLER32,
157                                            .ha_ctx_size = sizeof(unsigned int),
158                                            .ha_priority = 10,
159                                            .init = NULL,
160                                            .update = adler_wrapper,
161                                            .start = start_generic,
162                                            .final = final_generic,
163                                            .fini = NULL},
164 #if defined(HAVE_PCLMULQDQ) && defined(NEED_CRC32_ACCEL)
165                                           {.ha_id = CFS_HASH_ALG_CRC32,
166                                            .ha_ctx_size = sizeof(unsigned int),
167                                            .ha_priority = 100,
168                                            .init = crc32_pclmul_init,
169                                            .update = crc32_pclmul_wrapper,
170                                            .start = start_generic,
171                                            .final = final_generic,
172                                            .fini = NULL},
173 #endif
174                                         };
175
176 /**
177  * Go through hashes to find the hash with max priority for the hash_alg
178  * algorithm. This is done for different implementation of the same
179  * algorithm. Priority is staticaly defined by developer, and can be zeroed
180  * if initialization of algo is unsuccessful.
181  */
182 static const struct __hash_alg
183 *cfs_crypto_hash_best_alg(enum cfs_crypto_hash_alg hash_alg)
184 {
185         int max_priority = 0;
186         const struct __hash_alg *alg = NULL;
187         int i;
188
189         for (i = 0; i < ARRAY_SIZE(crypto_hash); i++) {
190                 if (hash_alg == crypto_hash[i].ha_id &&
191                     max_priority < crypto_hash[i].ha_priority) {
192                         max_priority = crypto_hash[i].ha_priority;
193                         alg = &crypto_hash[i];
194                 }
195         }
196
197         return alg;
198 }
199
200 struct cfs_crypto_hash_desc
201 *cfs_crypto_hash_init(enum cfs_crypto_hash_alg hash_alg,
202                       unsigned char *key, unsigned int key_len)
203 {
204         struct hash_desc                        *hdesc = NULL;
205         const struct cfs_crypto_hash_type       *type;
206         const struct __hash_alg                 *ha = NULL;
207         int                                     err;
208
209         type = cfs_crypto_hash_type(hash_alg);
210         if (type == NULL) {
211                 CWARN("Unsupported hash algorithm id = %d, max id is %d\n",
212                       hash_alg, CFS_HASH_ALG_MAX);
213                 return ERR_PTR(-EINVAL);
214         }
215
216         ha = cfs_crypto_hash_best_alg(hash_alg);
217         if (ha == NULL) {
218                 CERROR("Failed to get hash algorithm\n");
219                 return ERR_PTR(-ENODEV);
220         }
221
222         hdesc = kmalloc(sizeof(*hdesc) + ha->ha_ctx_size, 0);
223         if (hdesc == NULL)
224                 return ERR_PTR(-ENOMEM);
225
226         hdesc->hd_hash = ha;
227
228         if (ha->start != NULL) {
229                 err = ha->start(hdesc->hd_ctx, key, key_len);
230                 if (err == 0) {
231                         return (struct cfs_crypto_hash_desc *) hdesc;
232                 } else {
233                         kfree(hdesc);
234                         return ERR_PTR(err);
235                 }
236         }
237
238         return (struct cfs_crypto_hash_desc *) hdesc;
239 }
240
241 int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *desc, const void *buf,
242                            unsigned int buf_len)
243 {
244         struct hash_desc *d = (struct hash_desc *)desc;
245         return d->hd_hash->update(d->hd_ctx, buf, buf_len);
246 }
247
248 int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *desc,
249                                 struct page *page, unsigned int offset,
250                                 unsigned int len)
251 {
252         const void *p = page->addr + offset;
253
254         return cfs_crypto_hash_update(desc, p, len);
255 }
256
257 /**
258  *      To get final hash and destroy cfs_crypto_hash_desc, caller
259  *      should use valid hash buffer with enougth len for hash.
260  *      If hash_len pointer is NULL - destroy descriptor.
261  */
262 int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *desc,
263                           unsigned char *hash, unsigned int *hash_len)
264 {
265         const struct cfs_crypto_hash_type *type;
266         struct hash_desc        *d = (struct hash_desc *)desc;
267         int                     size;
268         int                     err;
269
270         LASSERT(d != NULL);
271         type = cfs_crypto_hash_type(d->hd_hash->ha_id);
272         LASSERT(type != NULL);
273         size = type->cht_size;
274
275         if (hash == NULL || hash_len == NULL) {
276                 err = 0;
277                 goto free;
278         }
279         if (*hash_len < size) {
280                 err = -EOVERFLOW;
281                 goto free;
282         }
283
284         LASSERT(d->hd_hash->final != NULL);
285         err = d->hd_hash->final(d->hd_ctx, hash, *hash_len);
286 free:
287         kfree(d);
288
289         return err;
290 }
291
292 int cfs_crypto_hash_digest(enum cfs_crypto_hash_alg hash_alg,
293                            const void *buf, unsigned int buf_len,
294                            unsigned char *key, unsigned int key_len,
295                            unsigned char *hash, unsigned int *hash_len)
296 {
297         struct cfs_crypto_hash_desc      *desc;
298         int                          err, err2;
299
300         desc = cfs_crypto_hash_init(hash_alg, key, key_len);
301
302         if (IS_ERR(desc))
303                 return PTR_ERR(desc);
304
305         err = cfs_crypto_hash_update(desc, buf, buf_len);
306         if (err != 0)
307                 hash_len = NULL;
308
309         err2 = cfs_crypto_hash_final(desc, hash, hash_len);
310         if (err2 != 0 && err == 0)
311                 err = err2;
312
313         return err;
314 }
315
316
317 static void cfs_crypto_start_timer(struct timeval *start)
318 {
319         gettimeofday(start, NULL);
320         return;
321 }
322
323 /** return usec */
324 static long cfs_crypto_get_sec(struct timeval *start)
325 {
326         struct timeval  end;
327
328         gettimeofday(&end, NULL);
329
330         return cfs_timeval_sub(&end, start, NULL);
331 }
332
333 static void cfs_crypto_performance_test(enum cfs_crypto_hash_alg hash_alg,
334                                         const unsigned char *buf,
335                                         unsigned int buf_len)
336 {
337         struct timeval            start;
338         int                          bcount, err, msec;
339         int                          iteration = 400; /* do test 400 times */
340         unsigned char              hash[64];
341         unsigned int                hash_len = 64;
342
343         cfs_crypto_start_timer(&start);
344         for (bcount = 0; bcount < iteration; bcount++) {
345                 err = cfs_crypto_hash_digest(hash_alg, buf, buf_len, NULL, 0,
346                                              hash, &hash_len);
347                 if (err)
348                         break;
349
350         }
351
352         msec = (int)(cfs_crypto_get_sec(&start) / 1000.0);
353         if (err) {
354                 cfs_crypto_hash_speeds[hash_alg] =  -1;
355                 CDEBUG(D_INFO, "Crypto hash algorithm err = %d\n", err);
356         } else {
357                 long tmp;
358                 tmp =  ((bcount * buf_len / msec) * 1000) / (1024 * 1024);
359                 cfs_crypto_hash_speeds[hash_alg] = (int)tmp;
360         }
361         CDEBUG(D_CONFIG, "Crypto hash algorithm %s speed = %d MB/s\n",
362                cfs_crypto_hash_name(hash_alg),
363                cfs_crypto_hash_speeds[hash_alg]);
364 }
365
366 int cfs_crypto_hash_speed(enum cfs_crypto_hash_alg hash_alg)
367 {
368         if (hash_alg < CFS_HASH_ALG_MAX)
369                 return cfs_crypto_hash_speeds[hash_alg];
370         else
371                 return -1;
372 }
373
374 /**
375  * Do performance test for all hash algorithms.
376  */
377 static int cfs_crypto_test_hashes(void)
378 {
379         unsigned char      i;
380         unsigned char      *data;
381         unsigned int        j, data_len = 1024 * 1024;
382
383         data = kmalloc(data_len, 0);
384         if (data == NULL) {
385                 CERROR("Failed to allocate mem\n");
386                 return -ENOMEM;
387         }
388         for (j = 0; j < data_len; j++)
389                 data[j] = j & 0xff;
390
391         for (i = 0; i < CFS_HASH_ALG_MAX; i++)
392                 cfs_crypto_performance_test(i, data, data_len);
393
394         kfree(data);
395         return 0;
396 }
397
398 /**
399  *      Register crypto hash algorithms
400  */
401 int cfs_crypto_register(void)
402 {
403         int i, err;
404         for (i = 0; i < ARRAY_SIZE(crypto_hash); i++) {
405                 if (crypto_hash[i].init == NULL)
406                         continue;
407                 err = crypto_hash[i].init();
408                 if (err < 0) {
409                         crypto_hash[i].ha_priority = 0;
410                         CWARN("Failed to initialize hash %s, error %d\n",
411                               cfs_crypto_hash_name(crypto_hash[i].ha_id), err);
412                 }
413         }
414
415         cfs_crypto_test_hashes();
416         return 0;
417 }
418
419 /**
420  *      Unregister
421  */
422 void cfs_crypto_unregister(void)
423 {
424         int i;
425         for (i = 0; i < ARRAY_SIZE(crypto_hash); i++) {
426                 if (crypto_hash[i].fini == NULL)
427                         continue;
428                 if (crypto_hash[i].ha_priority > 0)
429                         crypto_hash[i].fini();
430         }
431 }