3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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
18 * Please visit http://www.xyratex.com/contact if you need additional
19 * information or have any questions.
25 * Copyright 2012 Xyratex Technology Limited
29 * Libcfs crypto hash interfaces for user mode.
32 #include <libcfs/libcfs.h>
33 #include <libcfs/libcfs_crypto.h>
34 #include <libcfs/posix/posix-crypto.h>
35 #include <libcfs/user-crypto.h>
38 * Array of hash algorithm speed in MByte per second
40 static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX];
44 * Initialization of algorithm
48 * Start function for the hash instance
50 int (*start)(void *ctx, unsigned char *p, unsigned int len);
52 * Partial update checksum
54 int (*update)(void *ctx, const unsigned char *p, unsigned int len);
56 * Final function for the instance destroy context and copy digest
58 int (*final)(void *ctx, unsigned char *p, unsigned int len);
63 unsigned int ha_ctx_size; /**< size of context */
64 unsigned int ha_priority; /**< implementation priority
66 to get one from equal algorithm */
67 unsigned char ha_id; /**< algorithm identifier */
71 const struct __hash_alg *hd_hash;
72 unsigned char hd_ctx[0];
75 static int crc32_update_wrapper(void *ctx, const unsigned char *p,
78 unsigned int crc = *(unsigned int *)ctx;
80 crc = crc32_le(crc, p, len);
82 *(unsigned int *)ctx = crc;
86 static int adler_wrapper(void *ctx, const unsigned char *p,
89 unsigned int cksum = *(unsigned int *)ctx;
91 cksum = zlib_adler32(cksum, p, len);
93 *(unsigned int *)ctx = cksum;
97 #if defined(HAVE_PCLMULQDQ) && defined(NEED_CRC32_ACCEL)
98 static int crc32_pclmul_wrapper(void *ctx, const unsigned char *p,
101 unsigned int cksum = *(unsigned int *)ctx;
103 cksum = crc32_pclmul_le(cksum, p, len);
105 *(unsigned int *)ctx = cksum;
110 static int start_generic(void *ctx, unsigned char *key,
111 unsigned int key_len)
113 const struct cfs_crypto_hash_type *type;
114 struct hash_desc *hd = container_of(ctx, struct hash_desc,
116 type = cfs_crypto_hash_type(hd->hd_hash->ha_id);
117 LASSERT(type != NULL);
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);
125 CWARN("Invalid key or key_len, zero context\n");
126 memset(ctx, 0, hd->hd_hash->ha_ctx_size);
131 static int final_generic(void *ctx, unsigned char *hash,
132 unsigned int hash_len)
134 const struct cfs_crypto_hash_type *type;
135 struct hash_desc *hd = container_of(ctx, struct hash_desc,
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);
147 static struct __hash_alg crypto_hash[] = {
148 {.ha_id = CFS_HASH_ALG_CRC32,
149 .ha_ctx_size = sizeof(unsigned int),
151 .init = crc32init_le,
152 .update = crc32_update_wrapper,
153 .start = start_generic,
154 .final = final_generic,
156 {.ha_id = CFS_HASH_ALG_ADLER32,
157 .ha_ctx_size = sizeof(unsigned int),
160 .update = adler_wrapper,
161 .start = start_generic,
162 .final = final_generic,
164 #if defined(HAVE_PCLMULQDQ) && defined(NEED_CRC32_ACCEL)
165 {.ha_id = CFS_HASH_ALG_CRC32,
166 .ha_ctx_size = sizeof(unsigned int),
168 .init = crc32_pclmul_init,
169 .update = crc32_pclmul_wrapper,
170 .start = start_generic,
171 .final = final_generic,
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.
182 static const struct __hash_alg
183 *cfs_crypto_hash_best_alg(enum cfs_crypto_hash_alg hash_alg)
185 int max_priority = 0;
186 const struct __hash_alg *alg = NULL;
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];
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)
204 struct hash_desc *hdesc = NULL;
205 const struct cfs_crypto_hash_type *type;
206 const struct __hash_alg *ha = NULL;
209 type = cfs_crypto_hash_type(hash_alg);
211 CWARN("Unsupported hash algorithm id = %d, max id is %d\n",
212 hash_alg, CFS_HASH_ALG_MAX);
213 return ERR_PTR(-EINVAL);
216 ha = cfs_crypto_hash_best_alg(hash_alg);
218 CERROR("Failed to get hash algorithm\n");
219 return ERR_PTR(-ENODEV);
222 hdesc = kmalloc(sizeof(*hdesc) + ha->ha_ctx_size, 0);
224 return ERR_PTR(-ENOMEM);
228 if (ha->start != NULL) {
229 err = ha->start(hdesc->hd_ctx, key, key_len);
231 return (struct cfs_crypto_hash_desc *) hdesc;
238 return (struct cfs_crypto_hash_desc *) hdesc;
241 int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *desc, const void *buf,
242 unsigned int buf_len)
244 struct hash_desc *d = (struct hash_desc *)desc;
245 return d->hd_hash->update(d->hd_ctx, buf, buf_len);
248 int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *desc,
249 struct page *page, unsigned int offset,
252 const void *p = page->addr + offset;
254 return cfs_crypto_hash_update(desc, p, len);
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.
262 int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *desc,
263 unsigned char *hash, unsigned int *hash_len)
265 const struct cfs_crypto_hash_type *type;
266 struct hash_desc *d = (struct hash_desc *)desc;
271 type = cfs_crypto_hash_type(d->hd_hash->ha_id);
272 LASSERT(type != NULL);
273 size = type->cht_size;
275 if (hash == NULL || hash_len == NULL) {
279 if (*hash_len < size) {
284 LASSERT(d->hd_hash->final != NULL);
285 err = d->hd_hash->final(d->hd_ctx, hash, *hash_len);
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)
297 struct cfs_crypto_hash_desc *desc;
300 desc = cfs_crypto_hash_init(hash_alg, key, key_len);
303 return PTR_ERR(desc);
305 err = cfs_crypto_hash_update(desc, buf, buf_len);
309 err2 = cfs_crypto_hash_final(desc, hash, hash_len);
310 if (err2 != 0 && err == 0)
317 static void cfs_crypto_start_timer(struct timeval *start)
319 gettimeofday(start, NULL);
324 static long cfs_crypto_get_sec(struct timeval *start)
328 gettimeofday(&end, NULL);
330 return cfs_timeval_sub(&end, start, NULL);
333 static void cfs_crypto_performance_test(enum cfs_crypto_hash_alg hash_alg,
334 const unsigned char *buf,
335 unsigned int buf_len)
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;
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,
352 msec = (int)(cfs_crypto_get_sec(&start) / 1000.0);
354 cfs_crypto_hash_speeds[hash_alg] = -1;
355 CDEBUG(D_INFO, "Crypto hash algorithm err = %d\n", err);
358 tmp = ((bcount * buf_len / msec) * 1000) / (1024 * 1024);
359 cfs_crypto_hash_speeds[hash_alg] = (int)tmp;
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]);
366 int cfs_crypto_hash_speed(enum cfs_crypto_hash_alg hash_alg)
368 if (hash_alg < CFS_HASH_ALG_MAX)
369 return cfs_crypto_hash_speeds[hash_alg];
375 * Do performance test for all hash algorithms.
377 static int cfs_crypto_test_hashes(void)
381 unsigned int j, data_len = 1024 * 1024;
383 data = kmalloc(data_len, 0);
385 CERROR("Failed to allocate mem\n");
388 for (j = 0; j < data_len; j++)
391 for (i = 0; i < CFS_HASH_ALG_MAX; i++)
392 cfs_crypto_performance_test(i, data, data_len);
399 * Register crypto hash algorithms
401 int cfs_crypto_register(void)
404 for (i = 0; i < ARRAY_SIZE(crypto_hash); i++) {
405 if (crypto_hash[i].init == NULL)
407 err = crypto_hash[i].init();
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);
415 cfs_crypto_test_hashes();
422 void cfs_crypto_unregister(void)
425 for (i = 0; i < ARRAY_SIZE(crypto_hash); i++) {
426 if (crypto_hash[i].fini == NULL)
428 if (crypto_hash[i].ha_priority > 0)
429 crypto_hash[i].fini();