/* * async_provider.c * * Copyright (c) 2004 Evgeniy Polyakov * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../crypto/acrypto.h" #include "../crypto/crypto_stat.h" #include "../crypto/crypto_def.h" static void prov_data_ready(struct crypto_device *); static void async_callback(struct crypto_session_initializer *, struct crypto_data *); static struct crypto_capability prov_caps[] = { {CRYPTO_OP_ENCRYPT, CRYPTO_TYPE_AES_128, CRYPTO_MODE_ECB, 1000}, {CRYPTO_OP_DECRYPT, CRYPTO_TYPE_AES_128, CRYPTO_MODE_ECB, 1000}, }; static int prov_cap_number = sizeof(prov_caps)/sizeof(prov_caps[0]); static struct completion thread_exited; static DECLARE_WAIT_QUEUE_HEAD(async_wait_queue); static int need_exit; static struct crypto_tfm *tfm; struct crypto_session *sp; static char async_algo[] = "aes"; static char async_key[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; static struct crypto_session_initializer ci = { .operation = CRYPTO_OP_ENCRYPT, .mode = CRYPTO_MODE_ECB, .type = CRYPTO_TYPE_AES_128, .callback = async_callback, }; static struct crypto_device pdev = { .name = "async_provider", .data_ready = prov_data_ready, .cap = &prov_caps[0], }; static void async_callback(struct crypto_session_initializer *ci, struct crypto_data *data) { struct request *req = data->priv; dprintk("%s: req=%p\n", __func__, req); (unsigned long)req->special--; if (((unsigned long)req->special) == 0) { spinlock_t *l = req->q->queue_lock; dprintk("Finishing request for req=%p.\n", req); spin_lock_irq(l); end_that_request_last(req); spin_unlock_irq(l); } } static __inline__ void async_prepare(struct crypto_data *data, void *src, void *dst, unsigned long size, void *priv) { struct request *req = priv; (unsigned long)req->special++; dprintk("%s: req=%p, refcnt=%lu.\n", __func__, req, (unsigned long)req->special); data->sg_src.page = virt_to_page(src); data->sg_src.offset = offset_in_page(src); data->sg_src.length = size; data->sg_dst.page = virt_to_page(dst); data->sg_dst.offset = offset_in_page(dst); data->sg_dst.length = size; data->sg_key.page = virt_to_page(async_key); data->sg_key.offset = offset_in_page(async_key); data->sg_key.length = sizeof(async_key); data->priv = priv; data->priv_size = 0; } void bd_encrypt(void *src, void *dst, unsigned long size, void *priv) { struct crypto_session *session; struct crypto_data data; async_prepare(&data, src, dst, size, priv); ci.operation = CRYPTO_OP_ENCRYPT; session = crypto_session_alloc(&ci, &data); if (!session) WARN_ON(1); wake_up(&async_wait_queue); } void bd_decrypt(void *src, void *dst, unsigned long size, void *priv) { struct crypto_session *session; struct crypto_data data; async_prepare(&data, src, dst, size, priv); ci.operation = CRYPTO_OP_DECRYPT; session = crypto_session_alloc(&ci, &data); if (!session) WARN_ON(1); wake_up(&async_wait_queue); } static void prov_data_ready(struct crypto_device *dev) { struct crypto_session *s, *n; list_for_each_entry_safe(s, n, &dev->session_list, dev_queue_entry) { if (!session_completed(s)) { sp = s; wake_up(&async_wait_queue); break; } } } static int async_thread(void *data) { struct crypto_device *dev = (struct crypto_device *)data; daemonize("%s", dev->name); allow_signal(SIGTERM); while (!need_exit) { interruptible_sleep_on(&async_wait_queue); if (need_exit) break; if (!sp) continue; start_process_session(sp); if (sp->ci.operation == CRYPTO_OP_ENCRYPT) crypto_cipher_encrypt(tfm, &sp->data.sg_dst, &sp->data.sg_src, sp->data.sg_src.length); else crypto_cipher_decrypt(tfm, &sp->data.sg_dst, &sp->data.sg_src, sp->data.sg_src.length); dprintk("Completing session %llu [%llu] in %s.\n", sp->ci.id, sp->ci.dev_id, pdev.name); //crypto_stat_complete_inc(sp); complete_session(sp); stop_process_session(sp); prov_data_ready(dev); sp = NULL; } complete_and_exit(&thread_exited, 0); } int prov_init(void) { int err, pid; tfm = crypto_alloc_tfm(async_algo, 0); if (!tfm) { printk(KERN_ERR "Failed to allocate SHA1 tfm.\n"); return -EINVAL; } err = crypto_cipher_setkey(tfm, async_key, sizeof(async_key)); if (err) { dprintk("Failed to set key [keylen=%d]: err=%d.\n", sizeof(async_key), err); goto err_out_free_tfm; } init_completion(&thread_exited); pid = kernel_thread(async_thread, &pdev, CLONE_FS | CLONE_FILES); if (IS_ERR((void *)pid)) { err = -EINVAL; dprintk(KERN_ERR "Failed to create kernel load balancing thread.\n"); goto err_out_free_tfm; } pdev.cap_number = prov_cap_number; err = crypto_device_add(&pdev); if (err) goto err_out_remove_thread; dprintk(KERN_INFO "Test crypto provider module %s is loaded.\n", pdev.name); return 0; err_out_remove_thread: need_exit = 1; wake_up(&async_wait_queue); wait_for_completion(&thread_exited); err_out_free_tfm: crypto_free_tfm(tfm); return err; } void prov_fini(void) { need_exit = 1; wake_up(&async_wait_queue); wait_for_completion(&thread_exited); crypto_device_remove(&pdev); crypto_free_tfm(tfm); dprintk(KERN_INFO "Test crypto provider module %s is unloaded.\n", pdev.name); } module_init(prov_init); module_exit(prov_fini); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_DESCRIPTION("Test crypto module provider."); EXPORT_SYMBOL(bd_encrypt); EXPORT_SYMBOL(bd_decrypt);