/* * 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, 10000}, {CRYPTO_OP_DECRYPT, CRYPTO_TYPE_AES_128, CRYPTO_MODE_ECB, 10000}, }; 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; 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, req->q=%p.\n", __func__, req, req->q); (unsigned long)req->special--; if ((unsigned long)req->special == 0 && req->q) { spinlock_t *l = req->q->queue_lock; dprintk("Finishing request for req=%p [%lu].\n", req, (unsigned long)req->special); spin_lock_irq(l); (unsigned long)req->special = 0; if (!end_that_request_first(req, 1, req->nr_sectors)); 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, u64 size, void *priv) { struct crypto_data data; struct crypto_session *sp; async_prepare(&data, src, dst, size, priv); ci.operation = CRYPTO_OP_ENCRYPT; sp = crypto_session_alloc(&ci, &data); BUG_ON(!sp); wake_up_interruptible(&async_wait_queue); } void bd_decrypt(void *src, void *dst, u64 size, void *priv) { struct crypto_data data; struct crypto_session *sp; async_prepare(&data, src, dst, size, priv); ci.operation = CRYPTO_OP_DECRYPT; sp = crypto_session_alloc(&ci, &data); BUG_ON(!sp); wake_up_interruptible(&async_wait_queue); } static void prov_data_ready(struct crypto_device *dev) { wake_up_interruptible(&async_wait_queue); } static int async_thread(void *data) { struct crypto_device *dev = (struct crypto_device *)data; struct crypto_session *s, *n; daemonize("%s", dev->name); allow_signal(SIGTERM); while (!need_exit) { int num, pnum; interruptible_sleep_on_timeout(&async_wait_queue, 100); if (need_exit) break; num = pnum = 0; list_for_each_entry_safe(s, n, &dev->session_list, dev_queue_entry) { num++; if (session_completed(s)) continue; pnum++; start_process_session(s); if (s->ci.operation == CRYPTO_OP_ENCRYPT) crypto_cipher_encrypt(tfm, &s->data.sg_dst, &s->data.sg_src, s->data.sg_src.length); else crypto_cipher_decrypt(tfm, &s->data.sg_dst, &s->data.sg_src, s->data.sg_src.length); dprintk("%lu: Completing session %llu [%llu] in %s.\n", jiffies, s->ci.id, s->ci.dev_id, pdev.name); crypto_stat_complete_inc(s); complete_session(s); stop_process_session(s); } dprintk("%lu: %s: %d sessions, %d processed.\n", jiffies, dev->name, num, pnum); } complete_and_exit(&thread_exited, 0); } int prov_init(void) { int err, pid; tfm = crypto_alloc_tfm(async_algo, 0); if (!tfm) { dprintk(KERN_ERR "Failed to allocate %s tfm.\n", async_algo); 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);