OSDN Git Service

[CRYPTO] api: Added spawns
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / crypto / algapi.c
1 /*
2  * Cryptographic API for algorithms (i.e., low-level API).
3  *
4  * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  */
12
13 #include <linux/err.h>
14 #include <linux/errno.h>
15 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/list.h>
18 #include <linux/module.h>
19 #include <linux/string.h>
20
21 #include "internal.h"
22
23 static LIST_HEAD(crypto_template_list);
24
25 void crypto_larval_error(const char *name, u32 type, u32 mask)
26 {
27         struct crypto_alg *alg;
28
29         down_read(&crypto_alg_sem);
30         alg = __crypto_alg_lookup(name, type, mask);
31         up_read(&crypto_alg_sem);
32
33         if (alg) {
34                 if (crypto_is_larval(alg)) {
35                         struct crypto_larval *larval = (void *)alg;
36                         complete(&larval->completion);
37                 }
38                 crypto_mod_put(alg);
39         }
40 }
41 EXPORT_SYMBOL_GPL(crypto_larval_error);
42
43 static inline int crypto_set_driver_name(struct crypto_alg *alg)
44 {
45         static const char suffix[] = "-generic";
46         char *driver_name = alg->cra_driver_name;
47         int len;
48
49         if (*driver_name)
50                 return 0;
51
52         len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
53         if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME)
54                 return -ENAMETOOLONG;
55
56         memcpy(driver_name + len, suffix, sizeof(suffix));
57         return 0;
58 }
59
60 static int crypto_check_alg(struct crypto_alg *alg)
61 {
62         if (alg->cra_alignmask & (alg->cra_alignmask + 1))
63                 return -EINVAL;
64
65         if (alg->cra_alignmask & alg->cra_blocksize)
66                 return -EINVAL;
67
68         if (alg->cra_blocksize > PAGE_SIZE / 8)
69                 return -EINVAL;
70
71         if (alg->cra_priority < 0)
72                 return -EINVAL;
73
74         return crypto_set_driver_name(alg);
75 }
76
77 static void crypto_destroy_instance(struct crypto_alg *alg)
78 {
79         struct crypto_instance *inst = (void *)alg;
80         struct crypto_template *tmpl = inst->tmpl;
81
82         tmpl->free(inst);
83         crypto_tmpl_put(tmpl);
84 }
85
86 static void crypto_remove_spawns(struct list_head *spawns,
87                                  struct list_head *list)
88 {
89         struct crypto_spawn *spawn, *n;
90
91         list_for_each_entry_safe(spawn, n, spawns, list) {
92                 struct crypto_instance *inst = spawn->inst;
93                 struct crypto_template *tmpl = inst->tmpl;
94
95                 list_del_init(&spawn->list);
96                 spawn->alg = NULL;
97
98                 if (crypto_is_dead(&inst->alg))
99                         continue;
100
101                 inst->alg.cra_flags |= CRYPTO_ALG_DEAD;
102                 if (!tmpl || !crypto_tmpl_get(tmpl))
103                         continue;
104
105                 crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, &inst->alg);
106                 list_move(&inst->alg.cra_list, list);
107                 hlist_del(&inst->list);
108                 inst->alg.cra_destroy = crypto_destroy_instance;
109
110                 if (!list_empty(&inst->alg.cra_users)) {
111                         if (&n->list == spawns)
112                                 n = list_entry(inst->alg.cra_users.next,
113                                                typeof(*n), list);
114                         __list_splice(&inst->alg.cra_users, spawns->prev);
115                 }
116         }
117 }
118
119 static int __crypto_register_alg(struct crypto_alg *alg,
120                                  struct list_head *list)
121 {
122         struct crypto_alg *q;
123         int ret = -EAGAIN;
124
125         if (crypto_is_dead(alg))
126                 goto out;
127
128         INIT_LIST_HEAD(&alg->cra_users);
129
130         ret = -EEXIST;
131
132         atomic_set(&alg->cra_refcnt, 1);
133         list_for_each_entry(q, &crypto_alg_list, cra_list) {
134                 if (q == alg)
135                         goto out;
136
137                 if (crypto_is_moribund(q))
138                         continue;
139
140                 if (crypto_is_larval(q)) {
141                         struct crypto_larval *larval = (void *)q;
142
143                         if (strcmp(alg->cra_name, q->cra_name) &&
144                             strcmp(alg->cra_driver_name, q->cra_name))
145                                 continue;
146
147                         if (larval->adult)
148                                 continue;
149                         if ((q->cra_flags ^ alg->cra_flags) & larval->mask)
150                                 continue;
151                         if (!crypto_mod_get(alg))
152                                 continue;
153
154                         larval->adult = alg;
155                         complete(&larval->completion);
156                         continue;
157                 }
158
159                 if (strcmp(alg->cra_name, q->cra_name))
160                         continue;
161
162                 if (strcmp(alg->cra_driver_name, q->cra_driver_name) &&
163                     q->cra_priority > alg->cra_priority)
164                         continue;
165
166                 crypto_remove_spawns(&q->cra_users, list);
167         }
168         
169         list_add(&alg->cra_list, &crypto_alg_list);
170
171         crypto_notify(CRYPTO_MSG_ALG_REGISTER, alg);
172         ret = 0;
173
174 out:    
175         return ret;
176 }
177
178 static void crypto_remove_final(struct list_head *list)
179 {
180         struct crypto_alg *alg;
181         struct crypto_alg *n;
182
183         list_for_each_entry_safe(alg, n, list, cra_list) {
184                 list_del_init(&alg->cra_list);
185                 crypto_alg_put(alg);
186         }
187 }
188
189 int crypto_register_alg(struct crypto_alg *alg)
190 {
191         LIST_HEAD(list);
192         int err;
193
194         err = crypto_check_alg(alg);
195         if (err)
196                 return err;
197
198         down_write(&crypto_alg_sem);
199         err = __crypto_register_alg(alg, &list);
200         up_write(&crypto_alg_sem);
201
202         crypto_remove_final(&list);
203         return err;
204 }
205 EXPORT_SYMBOL_GPL(crypto_register_alg);
206
207 static int crypto_remove_alg(struct crypto_alg *alg, struct list_head *list)
208 {
209         if (unlikely(list_empty(&alg->cra_list)))
210                 return -ENOENT;
211
212         alg->cra_flags |= CRYPTO_ALG_DEAD;
213
214         crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, alg);
215         list_del_init(&alg->cra_list);
216         crypto_remove_spawns(&alg->cra_users, list);
217
218         return 0;
219 }
220
221 int crypto_unregister_alg(struct crypto_alg *alg)
222 {
223         int ret;
224         LIST_HEAD(list);
225         
226         down_write(&crypto_alg_sem);
227         ret = crypto_remove_alg(alg, &list);
228         up_write(&crypto_alg_sem);
229
230         if (ret)
231                 return ret;
232
233         BUG_ON(atomic_read(&alg->cra_refcnt) != 1);
234         if (alg->cra_destroy)
235                 alg->cra_destroy(alg);
236
237         crypto_remove_final(&list);
238         return 0;
239 }
240 EXPORT_SYMBOL_GPL(crypto_unregister_alg);
241
242 int crypto_register_template(struct crypto_template *tmpl)
243 {
244         struct crypto_template *q;
245         int err = -EEXIST;
246
247         down_write(&crypto_alg_sem);
248
249         list_for_each_entry(q, &crypto_template_list, list) {
250                 if (q == tmpl)
251                         goto out;
252         }
253
254         list_add(&tmpl->list, &crypto_template_list);
255         crypto_notify(CRYPTO_MSG_TMPL_REGISTER, tmpl);
256         err = 0;
257 out:
258         up_write(&crypto_alg_sem);
259         return err;
260 }
261 EXPORT_SYMBOL_GPL(crypto_register_template);
262
263 void crypto_unregister_template(struct crypto_template *tmpl)
264 {
265         struct crypto_instance *inst;
266         struct hlist_node *p, *n;
267         struct hlist_head *list;
268         LIST_HEAD(users);
269
270         down_write(&crypto_alg_sem);
271
272         BUG_ON(list_empty(&tmpl->list));
273         list_del_init(&tmpl->list);
274
275         list = &tmpl->instances;
276         hlist_for_each_entry(inst, p, list, list) {
277                 int err = crypto_remove_alg(&inst->alg, &users);
278                 BUG_ON(err);
279         }
280
281         crypto_notify(CRYPTO_MSG_TMPL_UNREGISTER, tmpl);
282
283         up_write(&crypto_alg_sem);
284
285         hlist_for_each_entry_safe(inst, p, n, list, list) {
286                 BUG_ON(atomic_read(&inst->alg.cra_refcnt) != 1);
287                 tmpl->free(inst);
288         }
289         crypto_remove_final(&users);
290 }
291 EXPORT_SYMBOL_GPL(crypto_unregister_template);
292
293 static struct crypto_template *__crypto_lookup_template(const char *name)
294 {
295         struct crypto_template *q, *tmpl = NULL;
296
297         down_read(&crypto_alg_sem);
298         list_for_each_entry(q, &crypto_template_list, list) {
299                 if (strcmp(q->name, name))
300                         continue;
301                 if (unlikely(!crypto_tmpl_get(q)))
302                         continue;
303
304                 tmpl = q;
305                 break;
306         }
307         up_read(&crypto_alg_sem);
308
309         return tmpl;
310 }
311
312 struct crypto_template *crypto_lookup_template(const char *name)
313 {
314         return try_then_request_module(__crypto_lookup_template(name), name);
315 }
316 EXPORT_SYMBOL_GPL(crypto_lookup_template);
317
318 int crypto_register_instance(struct crypto_template *tmpl,
319                              struct crypto_instance *inst)
320 {
321         LIST_HEAD(list);
322         int err = -EINVAL;
323
324         if (inst->alg.cra_destroy)
325                 goto err;
326
327         err = crypto_check_alg(&inst->alg);
328         if (err)
329                 goto err;
330
331         inst->alg.cra_module = tmpl->module;
332
333         down_write(&crypto_alg_sem);
334
335         err = __crypto_register_alg(&inst->alg, &list);
336         if (err)
337                 goto unlock;
338
339         hlist_add_head(&inst->list, &tmpl->instances);
340         inst->tmpl = tmpl;
341
342 unlock:
343         up_write(&crypto_alg_sem);
344
345         crypto_remove_final(&list);
346
347 err:
348         return err;
349 }
350 EXPORT_SYMBOL_GPL(crypto_register_instance);
351
352 int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg,
353                       struct crypto_instance *inst)
354 {
355         int err = -EAGAIN;
356
357         spawn->inst = inst;
358
359         down_write(&crypto_alg_sem);
360         if (!crypto_is_moribund(alg)) {
361                 list_add(&spawn->list, &alg->cra_users);
362                 spawn->alg = alg;
363                 err = 0;
364         }
365         up_write(&crypto_alg_sem);
366
367         return err;
368 }
369 EXPORT_SYMBOL_GPL(crypto_init_spawn);
370
371 void crypto_drop_spawn(struct crypto_spawn *spawn)
372 {
373         down_write(&crypto_alg_sem);
374         list_del(&spawn->list);
375         up_write(&crypto_alg_sem);
376 }
377 EXPORT_SYMBOL_GPL(crypto_drop_spawn);
378
379 struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn)
380 {
381         struct crypto_alg *alg;
382         struct crypto_alg *alg2;
383         struct crypto_tfm *tfm;
384
385         down_read(&crypto_alg_sem);
386         alg = spawn->alg;
387         alg2 = alg;
388         if (alg2)
389                 alg2 = crypto_mod_get(alg2);
390         up_read(&crypto_alg_sem);
391
392         if (!alg2) {
393                 if (alg)
394                         crypto_shoot_alg(alg);
395                 return ERR_PTR(-EAGAIN);
396         }
397
398         tfm = __crypto_alloc_tfm(alg, 0);
399         if (IS_ERR(tfm))
400                 crypto_mod_put(alg);
401
402         return tfm;
403 }
404 EXPORT_SYMBOL_GPL(crypto_spawn_tfm);
405
406 int crypto_register_notifier(struct notifier_block *nb)
407 {
408         return blocking_notifier_chain_register(&crypto_chain, nb);
409 }
410 EXPORT_SYMBOL_GPL(crypto_register_notifier);
411
412 int crypto_unregister_notifier(struct notifier_block *nb)
413 {
414         return blocking_notifier_chain_unregister(&crypto_chain, nb);
415 }
416 EXPORT_SYMBOL_GPL(crypto_unregister_notifier);
417
418 static int __init crypto_algapi_init(void)
419 {
420         crypto_init_proc();
421         return 0;
422 }
423
424 static void __exit crypto_algapi_exit(void)
425 {
426         crypto_exit_proc();
427 }
428
429 module_init(crypto_algapi_init);
430 module_exit(crypto_algapi_exit);
431
432 MODULE_LICENSE("GPL");
433 MODULE_DESCRIPTION("Cryptographic algorithms API");