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