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/posix/posix-crypto.h>
34 #include <libcfs/user-crypto.h>
36 static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX];
40 * Initialization of algorithm
44 * Start function for the hash instance
46 int (*start)(void *ctx, unsigned char *p, unsigned int len);
48 * Partial update checksum
50 int (*update)(void *ctx, const unsigned char *p, unsigned int len);
52 * Final function for the instance destroy context and copy digest
54 int (*final)(void *ctx, unsigned char *p, unsigned int len);
59 unsigned int ha_ctx_size; /**< size of context */
60 unsigned int ha_priority; /**< implementation priority
62 to get one from equal algorithm */
63 unsigned char ha_id; /**< algorithm identifier */
67 const struct __hash_alg *hd_hash;
68 unsigned char hd_ctx[0];
71 static int crc32_update_wrapper(void *ctx, const unsigned char *p,
74 unsigned int crc = *(unsigned int *)ctx;
76 crc = crc32_le(crc, p, len);
78 *(unsigned int *)ctx = crc;
82 static int adler_wrapper(void *ctx, const unsigned char *p,
85 unsigned int cksum = *(unsigned int *)ctx;
87 cksum = zlib_adler32(cksum, p, len);
89 *(unsigned int *)ctx = cksum;
93 #if (defined i386) || (defined __amd64__)
94 static int crc32_pclmul_wrapper(void *ctx, const unsigned char *p,
97 unsigned int cksum = *(unsigned int *)ctx;
99 cksum = crc32_pclmul_le(cksum, p, len);
101 *(unsigned int *)ctx = cksum;
106 static int start_generic(void *ctx, unsigned char *key,
107 unsigned int key_len)
109 const struct cfs_crypto_hash_type *type;
110 struct hash_desc *hd = container_of(ctx, struct hash_desc,
112 type = cfs_crypto_hash_type(hd->hd_hash->ha_id);
113 LASSERT(type != NULL);
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);
121 CWARN("Invalid key or key_len, zero context\n");
122 memset(ctx, 0, hd->hd_hash->ha_ctx_size);
127 static int final_generic(void *ctx, unsigned char *hash,
128 unsigned int hash_len)
130 const struct cfs_crypto_hash_type *type;
131 struct hash_desc *hd = container_of(ctx, struct hash_desc,
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);
143 static struct __hash_alg crypto_hash[] = {
144 {.ha_id = CFS_HASH_ALG_CRC32,
145 .ha_ctx_size = sizeof(unsigned int),
147 .init = crc32init_le,
148 .update = crc32_update_wrapper,
149 .start = start_generic,
150 .final = final_generic,
152 {.ha_id = CFS_HASH_ALG_ADLER32,
153 .ha_ctx_size = sizeof(unsigned int),
156 .update = adler_wrapper,
157 .start = start_generic,
158 .final = final_generic,
160 #if (defined i386) || (defined __amd64__)
161 {.ha_id = CFS_HASH_ALG_CRC32,
162 .ha_ctx_size = sizeof(unsigned int),
164 .init = crc32_pclmul_init,
165 .update = crc32_pclmul_wrapper,
166 .start = start_generic,
167 .final = final_generic,
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.
178 static const struct __hash_alg *cfs_crypto_hash_best_alg(unsigned char alg_id)
180 int max_priority = 0;
181 const struct __hash_alg *alg = NULL;
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];
195 struct cfs_crypto_hash_desc
196 *cfs_crypto_hash_init(unsigned char alg,
197 unsigned char *key, unsigned int key_len)
199 struct hash_desc *hdesc = NULL;
200 const struct cfs_crypto_hash_type *type;
201 const struct __hash_alg *ha = NULL;
204 type = cfs_crypto_hash_type(alg);
206 CWARN("Unsupported hash algorithm id = %d, max id is %d\n",
207 alg, CFS_HASH_ALG_MAX);
208 return ERR_PTR(-EINVAL);
211 ha = cfs_crypto_hash_best_alg(alg);
213 CERROR("Failed to get hash algorithm\n");
214 return ERR_PTR(-ENODEV);
217 hdesc = cfs_alloc(sizeof(*hdesc) + ha->ha_ctx_size, 0);
219 return ERR_PTR(-ENOMEM);
223 if (ha->start != NULL) {
224 err = ha->start(hdesc->hd_ctx, key, key_len);
226 return (struct cfs_crypto_hash_desc *) hdesc;
233 return (struct cfs_crypto_hash_desc *) hdesc;
236 int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *desc, const void *buf,
237 unsigned int buf_len)
239 struct hash_desc *d = (struct hash_desc *)desc;
240 return d->hd_hash->update(d->hd_ctx, buf, buf_len);
243 int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *desc,
244 cfs_page_t *page, unsigned int offset,
247 const void *p = page->addr + offset;
249 return cfs_crypto_hash_update(desc, p, len);
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.
257 int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *desc,
258 unsigned char *hash, unsigned int *hash_len)
260 struct hash_desc *d = (struct hash_desc *)desc;
261 int size = (cfs_crypto_hash_type(d->hd_hash->ha_id))->cht_size;
264 if (hash_len == NULL) {
268 if (hash == NULL || *hash_len < size) {
269 *hash_len = d->hd_hash->ha_ctx_size;
273 LASSERT(d->hd_hash->final != NULL);
274 err = d->hd_hash->final(d->hd_ctx, hash, *hash_len);
276 /* If get final digest success free hash descriptor */
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)
288 struct cfs_crypto_hash_desc *desc;
291 desc = cfs_crypto_hash_init(alg, key, key_len);
294 return PTR_ERR(desc);
296 err = cfs_crypto_hash_update(desc, buf, buf_len);
298 cfs_crypto_hash_final(desc, NULL, NULL);
301 err = cfs_crypto_hash_final(desc, hash, hash_len);
303 cfs_crypto_hash_final(desc, NULL, NULL);
308 static void cfs_crypto_start_timer(struct timeval *start)
310 gettimeofday(start, NULL);
315 static long cfs_crypto_get_sec(struct timeval *start)
319 gettimeofday(&end, NULL);
321 return cfs_timeval_sub(&end, start, NULL);
324 static void cfs_crypto_performance_test(unsigned char alg_id,
325 const unsigned char *buf,
326 unsigned int buf_len)
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;
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,
343 msec = (int)(cfs_crypto_get_sec(&start) / 1000.0);
345 cfs_crypto_hash_speeds[alg_id] = -1;
346 CDEBUG(D_INFO, "Crypto hash algorithm err = %d\n", err);
349 tmp = ((bcount * buf_len / msec) * 1000) / (1024 * 1024);
350 cfs_crypto_hash_speeds[alg_id] = (int)tmp;
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]);
356 int cfs_crypto_hash_speed(unsigned char hash_alg)
358 if (hash_alg < CFS_HASH_ALG_MAX)
359 return cfs_crypto_hash_speeds[hash_alg];
365 * Do performance test for all hash algorithms.
367 static int cfs_crypto_test_hashes(void)
371 unsigned int j, data_len = 1024 * 1024;
373 data = cfs_alloc(data_len, 0);
375 CERROR("Failed to allocate mem\n");
378 for (j = 0; j < data_len; j++)
381 for (i = 0; i < CFS_HASH_ALG_MAX; i++)
382 cfs_crypto_performance_test(i, data, data_len);
389 * Register crypto hash algorithms
391 int cfs_crypto_register(void)
394 for (i = 0; i < ARRAY_SIZE(crypto_hash); i++) {
395 if (crypto_hash[i].init == NULL)
397 err = crypto_hash[i].init();
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);
405 cfs_crypto_test_hashes();
412 void cfs_crypto_unregister(void)
415 for (i = 0; i < ARRAY_SIZE(crypto_hash); i++) {
416 if (crypto_hash[i].fini == NULL)
418 if (crypto_hash[i].ha_priority > 0)
419 crypto_hash[i].fini();