Changes since second version:
- use an own workqueue per open file in order to retain system
  interactivity.
- Have a variable list of items in the job queue (increasing one by one
  until some arbitrary upper limit).
- Locking fixup: synchronising on the list items only is not possible.
  Instead lock the whole list, but minimise locking time.
- Have an asynchronous version of examples/speed.c to compare
  efficiency.

Still ugly as hell (and therefore TODO):
- having to pass task_struct and mm_struct from the calling process
  around
- need to copy the IV right at write() time (copy_from_user seems to
  work in process context only)

Depending on the chunk size, the throughput drops in a range between 80%
and 7%. So if one can afford to wait for cryptodev, it's adviceable to
also do so.

This will be the version I will be using for a while, since solving the
TODO items from above is rather non-trivial and time is money right now.
So please consider this a release candidate for upstream inclusion. I
guess for now this is as good as it gets (speaking of asynchronism).
Unless, of course, someone finds somethig to improve it! :)
---
 .gitignore              |    2 +
 cryptodev.h             |    4 +
 cryptodev_int.h         |    4 +-
 cryptodev_main.c        |  296 +++++++++++++++++++++++++++++++++++++++-----
 examples/Makefile       |    8 +-
 examples/async_cipher.c |  313 +++++++++++++++++++++++++++++++++++++++++++++++
 examples/async_speed.c  |  197 +++++++++++++++++++++++++++++
 7 files changed, 788 insertions(+), 36 deletions(-)
 create mode 100644 examples/async_cipher.c
 create mode 100644 examples/async_speed.c

diff --git a/.gitignore b/.gitignore
index 8b6dce9..c825daa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,8 @@ modules.order
 examples/cipher
 examples/hmac
 examples/speed
+examples/async_cipher
+examples/async_speed
 releases
 scripts
 version.h
diff --git a/cryptodev.h b/cryptodev.h
index 8cee01b..adfc374 100644
--- a/cryptodev.h
+++ b/cryptodev.h
@@ -164,4 +164,8 @@ enum cryptodev_crk_op_t {
  */
 #define CRIOGET_NOT_NEEDED 1
 
+/* additional ioctls for asynchronous  operation */
+#define CIOCASYNCCRYPT    _IOW('c', 107, struct crypt_op)
+#define CIOCASYNCFETCH    _IOR('c', 108, struct crypt_op)
+
 #endif /* L_CRYPTODEV_H */
diff --git a/cryptodev_int.h b/cryptodev_int.h
index 15eb5bf..2697cb5 100644
--- a/cryptodev_int.h
+++ b/cryptodev_int.h
@@ -26,7 +26,7 @@ extern int cryptodev_verbosity;
 
 /* For zero copy */
 int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
-               int pgcount, struct page **pg, struct scatterlist *sg);
+               int pgcount, struct page **pg, struct scatterlist *sg, struct 
task_struct *task, struct mm_struct *mm);
 void release_user_pages(struct page **pg, int pagecount);
 
 /* last page - first page + 1 */
@@ -114,6 +114,8 @@ struct compat_session_op {
 /* compat ioctls, defined for the above structs */
 #define COMPAT_CIOCGSESSION    _IOWR('c', 102, struct compat_session_op)
 #define COMPAT_CIOCCRYPT       _IOWR('c', 104, struct compat_crypt_op)
+#define COMPAT_CIOCASYNCCRYPT  _IOW('c', 107, struct compat_crypt_op)
+#define COMPAT_CIOCASYNCFETCH  _IOR('c', 108, struct compat_crypt_op)
 
 #endif /* CONFIG_COMPAT */
 
diff --git a/cryptodev_main.c b/cryptodev_main.c
index f3ccc06..9563764 100644
--- a/cryptodev_main.c
+++ b/cryptodev_main.c
@@ -39,6 +39,7 @@
 #include <linux/random.h>
 #include <linux/syscalls.h>
 #include <linux/pagemap.h>
+#include <linux/poll.h>
 #include <linux/uaccess.h>
 #include "cryptodev.h"
 #include <linux/scatterlist.h>
@@ -53,6 +54,11 @@ MODULE_LICENSE("GPL");
 
 #define CRYPTODEV_STATS
 
+/* Default (pre-allocated) and maximum size of the job queue.
+ * These are free, pending and done items all together. */
+#define DEF_COP_RINGSIZE 16
+#define MAX_COP_RINGSIZE 64
+
 /* ====== Module parameters ====== */
 
 int cryptodev_verbosity;
@@ -71,8 +77,26 @@ struct fcrypt {
        struct mutex sem;
 };
 
+struct todo_list_item {
+       struct list_head __hook;
+       struct crypt_op cop;
+       uint8_t iv[EALG_MAX_BLOCK_LEN];
+       struct task_struct *task;
+       struct mm_struct *mm;
+       int result;
+};
+
+struct locked_list {
+       struct list_head list;
+       struct mutex lock;
+};
+
 struct crypt_priv {
        struct fcrypt fcrypt;
+       struct locked_list free, todo, done;
+       int itemcount;
+       struct work_struct cryptask;
+       wait_queue_head_t user_waiter;
 };
 
 #define FILL_SG(sg, ptr, len)                                  \
@@ -101,6 +125,9 @@ struct csession {
        struct scatterlist *sg;
 };
 
+/* cryptodev's own workqueue, keeps crypto tasks from disturbing the force */
+static struct workqueue_struct *cryptodev_wq;
+
 /* Prepare session for future use. */
 static int
 crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
@@ -521,15 +548,15 @@ void release_user_pages(struct page **pg, int pagecount)
 
 /* fetch the pages addr resides in into pg and initialise sg with them */
 int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
-               int pgcount, struct page **pg, struct scatterlist *sg)
+               int pgcount, struct page **pg, struct scatterlist *sg, struct 
task_struct *task, struct mm_struct *mm)
 {
        int ret, pglen, i = 0;
        struct scatterlist *sgp;
 
-       down_write(&current->mm->mmap_sem);
-       ret = get_user_pages(current, current->mm,
+       down_write(&mm->mmap_sem);
+       ret = get_user_pages(task, mm,
                        (unsigned long)addr, pgcount, write, 0, pg, NULL);
-       up_write(&current->mm->mmap_sem);
+       up_write(&mm->mmap_sem);
        if (ret != pgcount)
                return -EINVAL;
 
@@ -549,8 +576,8 @@ int __get_userbuf(uint8_t __user *addr, uint32_t len, int 
write,
 }
 
 /* make cop->src and cop->dst available in scatterlists */
-static int get_userbuf(struct csession *ses,
-               struct crypt_op *cop, struct scatterlist **src_sg,
+static int get_userbuf(struct csession *ses, struct crypt_op *cop,
+               struct task_struct *task, struct mm_struct *mm, struct 
scatterlist **src_sg,
                struct scatterlist **dst_sg, int *tot_pages)
 {
        int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1;
@@ -595,7 +622,7 @@ static int get_userbuf(struct csession *ses,
        }
 
        if (__get_userbuf(cop->src, cop->len, write_src,
-                       src_pagecount, ses->pages, ses->sg)) {
+                       src_pagecount, ses->pages, ses->sg, task, mm)) {
                dprintk(1, KERN_ERR,
                        "failed to get user pages for data input\n");
                return -EINVAL;
@@ -606,7 +633,7 @@ static int get_userbuf(struct csession *ses,
                (*dst_sg) = ses->sg + src_pagecount;
 
                if (__get_userbuf(cop->dst, cop->len, 1, dst_pagecount,
-                                       ses->pages + src_pagecount, *dst_sg)) {
+                                       ses->pages + src_pagecount, *dst_sg, 
task, mm)) {
                        dprintk(1, KERN_ERR,
                                "failed to get user pages for data output\n");
                        release_user_pages(ses->pages, src_pagecount);
@@ -618,12 +645,12 @@ static int get_userbuf(struct csession *ses,
 
 /* This is the main crypto function - zero-copy edition */
 static int
-__crypto_run_zc(struct csession *ses_ptr, struct crypt_op *cop)
+__crypto_run_zc(struct csession *ses_ptr, struct crypt_op *cop, struct 
task_struct *task, struct mm_struct *mm)
 {
        struct scatterlist *src_sg, *dst_sg;
        int ret = 0, pagecount;
 
-       ret = get_userbuf(ses_ptr, cop, &src_sg, &dst_sg, &pagecount);
+       ret = get_userbuf(ses_ptr, cop, task, mm, &src_sg, &dst_sg, &pagecount);
        if (unlikely(ret)) {
                dprintk(1, KERN_ERR, "Error getting user pages. \
                                        Falling back to non zero copy.\n");
@@ -636,7 +663,7 @@ __crypto_run_zc(struct csession *ses_ptr, struct crypt_op 
*cop)
        return ret;
 }
 
-static int crypto_run(struct fcrypt *fcr, struct crypt_op *cop)
+static int crypto_run(struct fcrypt *fcr, struct crypt_op *cop, struct 
task_struct *task, struct mm_struct *mm, u8 *user_iv)
 {
        struct csession *ses_ptr;
        uint8_t hash_output[AALG_MAX_RESULT_LEN];
@@ -676,27 +703,29 @@ static int crypto_run(struct fcrypt *fcr, struct crypt_op 
*cop)
                        goto out_unlock;
                }
 
-               if (cop->iv) {
+               if (cop->iv || user_iv) {
                        uint8_t iv[EALG_MAX_BLOCK_LEN];
-
-                       ret = copy_from_user(iv, cop->iv,
-                               min((int)sizeof(iv), (ses_ptr->cdata.ivsize)));
-                       if (unlikely(ret)) {
-                               dprintk(1, KERN_ERR,
-                                       "error copying IV (%d bytes)\n",
-                                       min((int)sizeof(iv),
-                                       (ses_ptr->cdata.ivsize)));
-                               ret = -EFAULT;
-                               goto out_unlock;
+                       if (!user_iv) {
+
+                               ret = copy_from_user(iv, cop->iv,
+                                       min((int)sizeof(iv), 
(ses_ptr->cdata.ivsize)));
+                               if (unlikely(ret)) {
+                                       dprintk(1, KERN_ERR,
+                                               "error copying IV (%d bytes), 
copy_from_user returned %d for address %lx\n",
+                                               min((int)sizeof(iv),
+                                               (ses_ptr->cdata.ivsize)), ret, 
(unsigned long)cop->iv);
+                                       ret = -EFAULT;
+                                       goto out_unlock;
+                               }
                        }
 
-                       cryptodev_cipher_set_iv(&ses_ptr->cdata, iv,
+                       cryptodev_cipher_set_iv(&ses_ptr->cdata, user_iv ? 
user_iv : iv,
                                                ses_ptr->cdata.ivsize);
                }
        }
 
        if (cop->len != 0) {
-               ret = __crypto_run_zc(ses_ptr, cop);
+               ret = __crypto_run_zc(ses_ptr, cop, task, mm);
                if (unlikely(ret))
                        goto out_unlock;
        }
@@ -732,12 +761,39 @@ out_unlock:
        return ret;
 }
 
+static void cryptask_routine(struct work_struct *work)
+{
+       struct crypt_priv *pcr = container_of(work, struct crypt_priv, 
cryptask);
+       struct todo_list_item *item;
+       LIST_HEAD(tmp);
+
+       /* fetch all pending jobs into the temporary list */
+       mutex_lock(&pcr->todo.lock);
+       list_cut_position(&tmp, &pcr->todo.list, pcr->todo.list.prev);
+       mutex_unlock(&pcr->todo.lock);
+
+       /* handle each job locklessly */
+       list_for_each_entry(item, &tmp, __hook) {
+               if ((item->result = crypto_run(&pcr->fcrypt, &item->cop, 
item->task, item->mm, item->iv)))
+                       dprintk(0, KERN_ERR, "%s: crypto_run() failed: %d\n", 
__func__, item->result);
+       }
+
+       /* push all handled jobs to the done list at once */
+       mutex_lock(&pcr->done.lock);
+       list_splice_tail(&tmp, &pcr->done.list);
+       mutex_unlock(&pcr->done.lock);
+
+       /* wake for POLLIN */
+       wake_up_interruptible(&pcr->user_waiter);
+}
+
 /* ====== /dev/crypto ====== */
 
 static int
 cryptodev_open(struct inode *inode, struct file *filp)
 {
        struct crypt_priv *pcr;
+       int i;
 
        pcr = kmalloc(sizeof(*pcr), GFP_KERNEL);
        if (!pcr)
@@ -747,7 +803,24 @@ cryptodev_open(struct inode *inode, struct file *filp)
        mutex_init(&pcr->fcrypt.sem);
        INIT_LIST_HEAD(&pcr->fcrypt.list);
 
+       INIT_LIST_HEAD(&pcr->free.list);
+       INIT_LIST_HEAD(&pcr->todo.list);
+       INIT_LIST_HEAD(&pcr->done.list);
+       INIT_WORK(&pcr->cryptask, cryptask_routine);
+       mutex_init(&pcr->free.lock);
+       mutex_init(&pcr->todo.lock);
+       mutex_init(&pcr->done.lock);
+       init_waitqueue_head(&pcr->user_waiter);
+
+       for (i = 0; i < DEF_COP_RINGSIZE; i++) {
+               struct todo_list_item *tmp = kzalloc(sizeof(struct 
todo_list_item), GFP_KERNEL);
+               pcr->itemcount++;
+               dprintk(2, KERN_DEBUG, "%s: allocated new item at %lx\n", 
__func__, (unsigned long)tmp);
+               list_add(&tmp->__hook, &pcr->free.list);
+       }
+
        filp->private_data = pcr;
+       dprintk(2, KERN_DEBUG, "Cryptodev handle initialised, %d elements in 
queue\n", DEF_COP_RINGSIZE);
        return 0;
 }
 
@@ -755,13 +828,36 @@ static int
 cryptodev_release(struct inode *inode, struct file *filp)
 {
        struct crypt_priv *pcr = filp->private_data;
+       struct todo_list_item *item, *item_safe;
+       int items_freed = 0;
 
-       if (pcr) {
-               crypto_finish_all_sessions(&pcr->fcrypt);
-               kfree(pcr);
-               filp->private_data = NULL;
+       if (!pcr)
+               return 0;
+
+       cancel_work_sync(&pcr->cryptask);
+
+       mutex_destroy(&pcr->todo.lock);
+       mutex_destroy(&pcr->done.lock);
+       mutex_destroy(&pcr->free.lock);
+
+       list_splice_tail(&pcr->todo.list, &pcr->free.list);
+       list_splice_tail(&pcr->done.list, &pcr->free.list);
+
+       list_for_each_entry_safe(item, item_safe, &pcr->free.list, __hook) {
+               dprintk(2, KERN_DEBUG, "%s: freeing item at %lx\n", __func__, 
(unsigned long)item);
+               list_del(&item->__hook);
+               kfree(item);
+               items_freed++;
        }
+       if (items_freed != pcr->itemcount)
+               dprintk(0, KERN_ERR, "%s: freed %d items, but %d should 
exist!\n",
+                               __func__, items_freed, pcr->itemcount);
+
+       crypto_finish_all_sessions(&pcr->fcrypt);
+       kfree(pcr);
+       filp->private_data = NULL;
 
+       dprintk(2, KERN_DEBUG, "Cryptodev handle deinitialised, %d elements 
freed\n", items_freed);
        return 0;
 }
 
@@ -778,6 +874,83 @@ clonefd(struct file *filp)
        return ret;
 }
 
+/* enqueue a job for asynchronous completion
+ *
+ * returns:
+ * -EBUSY when there are no free queue slots left
+ * -EFAULT on memory access error
+ * 0 on success */
+static int crypto_async_run(struct crypt_priv *pcr, struct crypt_op *cop)
+{
+       struct todo_list_item *item;
+
+       mutex_lock(&pcr->free.lock);
+       if (list_empty(&pcr->free.list)) {
+               if (pcr->itemcount >= MAX_COP_RINGSIZE) {
+                       dprintk(1, KERN_INFO, "%s: run out of free items\n", 
__func__);
+                       mutex_unlock(&pcr->free.lock);
+                       return -EBUSY;
+               }
+               item = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL);
+               pcr->itemcount++;
+               dprintk(1, KERN_INFO, "%s: increasing item count to %d\n",
+                               __func__, pcr->itemcount);
+       } else {
+               item = list_first_entry(&pcr->free.list, struct todo_list_item, 
__hook);
+               list_del(&item->__hook);
+       }
+       mutex_unlock(&pcr->free.lock);
+
+       memcpy(&item->cop, cop, sizeof(struct crypt_op));
+       if (copy_from_user(item->iv, item->cop.iv, sizeof(item->cop.iv))) {
+               dprintk(0, KERN_ERR, "%s: copy_from_user for IV failed!\n", 
__func__);
+               mutex_lock(&pcr->free.lock);
+               list_add_tail(&item->__hook, &pcr->free.list);
+               mutex_unlock(&pcr->free.lock);
+               return -EFAULT;
+       }
+       item->task = current;
+       item->mm = current->mm;
+
+       mutex_lock(&pcr->todo.lock);
+       list_add_tail(&item->__hook, &pcr->todo.list);
+       mutex_unlock(&pcr->todo.lock);
+
+       queue_work(cryptodev_wq, &pcr->cryptask);
+       return 0;
+}
+
+/* get the first completed job from the "done" queue
+ *
+ * returns:
+ * -EBUSY if no completed jobs are ready (yet)
+ * the return value of crypto_run() otherwise */
+static int crypto_async_fetch(struct crypt_priv *pcr, struct crypt_op *cop)
+{
+       struct todo_list_item *item;
+       int retval;
+
+       mutex_lock(&pcr->done.lock);
+       if (list_empty(&pcr->done.list)) {
+               mutex_unlock(&pcr->done.lock);
+               return -EBUSY;
+       }
+       item = list_first_entry(&pcr->done.list, struct todo_list_item, __hook);
+       list_del(&item->__hook);
+       mutex_unlock(&pcr->done.lock);
+
+       memcpy(cop, &item->cop, sizeof(struct crypt_op));
+       retval = item->result;
+
+       mutex_lock(&pcr->free.lock);
+       list_add_tail(&item->__hook, &pcr->free.list);
+       mutex_unlock(&pcr->free.lock);
+
+       /* wake for POLLOUT */
+       wake_up_interruptible(&pcr->user_waiter);
+       return retval;
+}
+
 static long
 cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
 {
@@ -798,6 +971,7 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, 
unsigned long arg_)
        switch (cmd) {
        case CIOCASYMFEAT:
                return put_user(0, p);
+
        case CRIOGET:
                fd = clonefd(filp);
                ret = put_user(fd, p);
@@ -806,6 +980,7 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, 
unsigned long arg_)
                        return ret;
                }
                return ret;
+
        case CIOCGSESSION:
                if (unlikely(copy_from_user(&sop, arg, sizeof(sop))))
                        return -EFAULT;
@@ -819,23 +994,36 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, 
unsigned long arg_)
                        return -EFAULT;
                }
                return ret;
+
        case CIOCFSESSION:
                ret = get_user(ses, (uint32_t __user *)arg);
                if (unlikely(ret))
                        return ret;
                ret = crypto_finish_session(fcr, ses);
                return ret;
+
        case CIOCCRYPT:
                if (unlikely(copy_from_user(&cop, arg, sizeof(cop))))
                        return -EFAULT;
 
-               ret = crypto_run(fcr, &cop);
+               ret = crypto_run(fcr, &cop, current, 0, 0);
                if (unlikely(ret))
                        return ret;
                if (unlikely(copy_to_user(arg, &cop, sizeof(cop))))
                        return -EFAULT;
                return 0;
 
+       case CIOCASYNCCRYPT:
+               if (unlikely(copy_from_user(&cop, arg, sizeof(cop))))
+                       return -EFAULT;
+               return crypto_async_run(pcr, &cop);
+
+       case CIOCASYNCFETCH:
+               ret = crypto_async_fetch(pcr, &cop);
+               if (copy_to_user(arg, &cop, sizeof(cop)))
+                       return -EFAULT;
+               return ret;
+
        default:
                return -EINVAL;
        }
@@ -902,15 +1090,17 @@ static long
 cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_)
 {
        void __user *arg = (void __user *)arg_;
-       struct fcrypt *fcr = file->private_data;
+       struct crypt_priv *pcr = file->private_data;
+       struct fcrypt *fcr;
        struct session_op sop;
        struct compat_session_op compat_sop;
        struct crypt_op cop;
        struct compat_crypt_op compat_cop;
        int ret;
 
-       if (unlikely(!fcr))
+       if (unlikely(!pcr))
                BUG();
+       fcr = &pcr->fcrypt;
 
        switch (cmd) {
        case CIOCASYMFEAT:
@@ -943,7 +1133,7 @@ cryptodev_compat_ioctl(struct file *file, unsigned int 
cmd, unsigned long arg_)
 
                compat_to_crypt_op(&compat_cop, &cop);
 
-               ret = crypto_run(fcr, &cop);
+               ret = crypto_run(fcr, &cop, current, current->mm, 0);
                if (unlikely(ret))
                        return ret;
 
@@ -953,6 +1143,19 @@ cryptodev_compat_ioctl(struct file *file, unsigned int 
cmd, unsigned long arg_)
                        return -EFAULT;
                return 0;
 
+       case COMPAT_CIOCASYNCCRYPT:
+               if (unlikely(copy_from_user(&compat_cop, arg, 
sizeof(compat_cop))))
+                       return -EFAULT;
+               compat_to_crypt_op(&compat_cop, &cop);
+               return crypto_async_run(pcr, &cop);
+
+       case COMPAT_CIOCASYNCFETCH:
+               ret = crypto_async_fetch(pcr, &cop);
+               crypt_op_to_compat(&cop, &compat_cop);
+               if (copy_to_user(arg, &compat_cop, sizeof(compat_cop)))
+                       return -EFAULT;
+               return ret;
+
        default:
                return -EINVAL;
        }
@@ -960,6 +1163,21 @@ cryptodev_compat_ioctl(struct file *file, unsigned int 
cmd, unsigned long arg_)
 
 #endif /* CONFIG_COMPAT */
 
+static unsigned int cryptodev_poll(struct file *file, poll_table *wait)
+{
+       struct crypt_priv *pcr = file->private_data;
+       int ret = 0;
+
+       poll_wait(file, &pcr->user_waiter, wait);
+
+       if (!list_empty_careful(&pcr->done.list))
+               ret |= POLLIN | POLLRDNORM;
+       if (!list_empty_careful(&pcr->free.list) || pcr->itemcount < 
MAX_COP_RINGSIZE)
+               ret |= POLLOUT | POLLWRNORM;
+
+       return ret;
+}
+
 static const struct file_operations cryptodev_fops = {
        .owner = THIS_MODULE,
        .open = cryptodev_open,
@@ -968,6 +1186,7 @@ static const struct file_operations cryptodev_fops = {
 #ifdef CONFIG_COMPAT
        .compat_ioctl = cryptodev_compat_ioctl,
 #endif /* CONFIG_COMPAT */
+       .poll = cryptodev_poll,
 };
 
 static struct miscdevice cryptodev = {
@@ -1001,9 +1220,17 @@ static int __init init_cryptodev(void)
 {
        int rc;
 
+       cryptodev_wq = create_workqueue("cryptodev_queue");
+       if (unlikely(!cryptodev_wq)) {
+               printk(KERN_ERR PFX "failed to allocate the cryptodev 
workqueue\n");
+               return -EFAULT;
+       }
+
        rc = cryptodev_register();
-       if (unlikely(rc))
+       if (unlikely(rc)) {
+               destroy_workqueue(cryptodev_wq);
                return rc;
+       }
 
        printk(KERN_INFO PFX "driver %s loaded.\n", VERSION);
 
@@ -1012,6 +1239,9 @@ static int __init init_cryptodev(void)
 
 static void __exit exit_cryptodev(void)
 {
+       flush_workqueue(cryptodev_wq);
+       destroy_workqueue(cryptodev_wq);
+
        cryptodev_deregister();
        printk(KERN_INFO PFX "driver unloaded.\n");
 }
diff --git a/examples/Makefile b/examples/Makefile
index 937eb6d..0796508 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -1,13 +1,17 @@
 KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
 
-hostprogs := cipher hmac speed
+CFLAGS=-m32
+
+hostprogs := cipher hmac speed async_cipher async_speed
 example-cipher-objs := cipher.o
 example-hmac-objs := hmac.o
 example-speed-objs := speed.c
+example-async_cipher-objs := async_cipher.o
+example-async_speed-objs := async_speed.o
 
 check: $(hostprogs)
        ./cipher
        ./hmac
 
 clean:
-       rm -f *.o *~ hmac cipher speed
+       rm -f *.o *~ hmac cipher speed async_cipher async_speed
diff --git a/examples/async_cipher.c b/examples/async_cipher.c
new file mode 100644
index 0000000..1e68770
--- /dev/null
+++ b/examples/async_cipher.c
@@ -0,0 +1,313 @@
+/*
+ * Demo on how to use /dev/crypto device for ciphering.
+ *
+ * Placed under public domain.
+ *
+ */
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <crypto/cryptodev.h>
+
+#define        DATA_SIZE       8*1024
+#define        BLOCK_SIZE      16
+#define        KEY_SIZE        16
+
+static int
+test_crypto(int cfd)
+{
+       char plaintext[DATA_SIZE];
+       char ciphertext[DATA_SIZE];
+       char iv[BLOCK_SIZE];
+       char key[KEY_SIZE];
+
+       struct session_op sess;
+       struct crypt_op cryp;
+
+       printf("running %s\n", __func__);
+
+       memset(&sess, 0, sizeof(sess));
+       memset(&cryp, 0, sizeof(cryp));
+
+       memset(plaintext, 0x15,  sizeof(plaintext));
+       memset(key, 0x33,  sizeof(key));
+       memset(iv, 0x03,  sizeof(iv));
+
+       /* Get crypto session for AES128 */
+       sess.cipher = CRYPTO_AES_CBC;
+       sess.keylen = KEY_SIZE;
+       sess.key = key;
+       if (ioctl(cfd, CIOCGSESSION, &sess)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+
+       printf("%s: got the session\n", __func__);
+
+
+       /* Encrypt data.in to data.encrypted */
+       cryp.ses = sess.ses;
+       cryp.len = sizeof(plaintext);
+       cryp.src = plaintext;
+       cryp.dst = ciphertext;
+       cryp.iv = iv;
+       cryp.op = COP_ENCRYPT;
+       if (ioctl(cfd, CIOCCRYPT, &cryp)) {
+               perror("ioctl(CIOCCRYPT)");
+               return 1;
+       }
+       printf("%s: data encrypted\n", __func__);
+
+       if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+               perror("ioctl(CIOCFSESSION)");
+               return 1;
+       }
+       printf("%s: session finished\n", __func__);
+
+       if (ioctl(cfd, CIOCGSESSION, &sess)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+       printf("%s: got new session\n", __func__);
+
+       /* Decrypt data.encrypted to data.decrypted */
+       cryp.ses = sess.ses;
+       cryp.len = sizeof(plaintext);
+       cryp.src = ciphertext;
+       cryp.dst = ciphertext;
+       cryp.iv = iv;
+       cryp.op = COP_DECRYPT;
+       if (ioctl(cfd, CIOCCRYPT, &cryp)) {
+               perror("ioctl(CIOCCRYPT)");
+               return 1;
+       }
+       printf("%s: data encrypted\n", __func__);
+
+       /* Verify the result */
+       if (memcmp(plaintext, ciphertext, sizeof(plaintext)) != 0) {
+               fprintf(stderr,
+                       "FAIL: Decrypted data are different from the input 
data.\n");
+               return 1;
+       } else
+               printf("Test passed\n");
+
+       /* Finish crypto session */
+       if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+               perror("ioctl(CIOCFSESSION)");
+               return 1;
+       }
+
+       return 0;
+}
+
+static int test_aes(int cfd)
+{
+       char plaintext1[BLOCK_SIZE];
+       char ciphertext1[BLOCK_SIZE] = { 0xdf, 0x55, 0x6a, 0x33, 0x43, 0x8d, 
0xb8, 0x7b, 0xc4, 0x1b, 0x17, 0x52, 0xc5, 0x5e, 0x5e, 0x49 };
+       char iv1[BLOCK_SIZE];
+       char key1[KEY_SIZE] = { 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       char plaintext2[BLOCK_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 };
+       char ciphertext2[BLOCK_SIZE] = { 0xb7, 0x97, 0x2b, 0x39, 0x41, 0xc4, 
0x4b, 0x90, 0xaf, 0xa7, 0xb2, 0x64, 0xbf, 0xba, 0x73, 0x87 };
+       char iv2[BLOCK_SIZE];
+       char key2[KEY_SIZE];
+
+       struct session_op sess1, sess2;
+       struct crypt_op cryp1, cryp2;
+       struct pollfd pfd;
+
+       printf("running %s\n", __func__);
+
+       memset(&sess1, 0, sizeof(sess1));
+       memset(&sess2, 0, sizeof(sess2));
+       memset(&cryp1, 0, sizeof(cryp1));
+       memset(&cryp2, 0, sizeof(cryp2));
+
+       memset(plaintext1, 0x0, sizeof(plaintext1));
+       memset(iv1, 0x0, sizeof(iv1));
+
+       /* Get crypto session for AES128 */
+       sess1.cipher = CRYPTO_AES_CBC;
+       sess1.keylen = KEY_SIZE;
+       sess1.key = key1;
+       if (ioctl(cfd, CIOCGSESSION, &sess1)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+
+       memset(key2, 0x0, sizeof(key2));
+
+       /* Get second crypto session for AES128 */
+       sess2.cipher = CRYPTO_AES_CBC;
+       sess2.keylen = KEY_SIZE;
+       sess2.key = key2;
+       if (ioctl(cfd, CIOCGSESSION, &sess2)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+
+       pfd.fd = cfd;
+       pfd.events = POLLOUT;
+       if (poll(&pfd, 1, -1) < 1) {
+               perror("poll()");
+               return 1;
+       }
+       printf("poll() returned good\n");
+
+       /* Encrypt data.in to data.encrypted */
+       cryp1.ses = sess1.ses;
+       cryp1.len = sizeof(plaintext1);
+       cryp1.src = plaintext1;
+       cryp1.dst = plaintext1;
+       cryp1.iv = iv1;
+       cryp1.op = COP_ENCRYPT;
+       if (ioctl(cfd, CIOCASYNCCRYPT, &cryp1)) {
+               perror("ioctl(CIOCASYNCCRYPT)");
+               return 1;
+       }
+
+       printf("cryp1 written out\n");
+
+       memset(iv2, 0x0, sizeof(iv2));
+
+       /* Encrypt data.in to data.encrypted */
+       cryp2.ses = sess2.ses;
+       cryp2.len = sizeof(plaintext2);
+       cryp2.src = plaintext2;
+       cryp2.dst = plaintext2;
+       cryp2.iv = iv2;
+       cryp2.op = COP_ENCRYPT;
+
+       pfd.fd = cfd;
+       pfd.events = POLLOUT;
+       if (poll(&pfd, 1, -1) < 1) {
+               perror("poll()");
+               return 1;
+       }
+       printf("poll() returned good\n");
+
+       if (ioctl(cfd, CIOCASYNCCRYPT, &cryp2)) {
+               perror("ioctl(CIOCASYNCCRYPT)");
+               return 1;
+       }
+       printf("cryp2 written out\n");
+
+       pfd.fd = cfd;
+       pfd.events = POLLIN;
+       if (poll(&pfd, 1, -1) < 1) {
+               perror("poll()");
+               return 1;
+       }
+       printf("poll() returned good\n");
+
+       if (ioctl(cfd, CIOCASYNCFETCH, &cryp1) ||
+           ioctl(cfd, CIOCASYNCFETCH, &cryp2)) {
+               perror("ioctl(CIOCASYNCFETCH)");
+               return 1;
+       }
+       printf("cryp1 + cryp2 successfully read\n");
+
+       /* Verify the result */
+       if (memcmp(plaintext1, ciphertext1, sizeof(plaintext1)) != 0) {
+               int i;
+               fprintf(stderr,
+                       "FAIL: Decrypted data are different from the input 
data.\n");
+               printf("cipher text should be:\t");
+               for (i = 0; i < BLOCK_SIZE; i++) {
+                       printf("%02x ", (unsigned char)ciphertext1[i]);
+               }
+               printf("\nbut really is:\t\t");
+               for (i = 0; i < BLOCK_SIZE; i++) {
+                       printf("%02x ", (unsigned char)plaintext1[i]);
+               }
+               printf("\n");
+               return 1;
+       } else {
+               printf("result 1 passed\n");
+       }
+
+       /* Test 2 */
+
+       /* Verify the result */
+       if (memcmp(plaintext2, ciphertext2, sizeof(plaintext2)) != 0) {
+               int i;
+               fprintf(stderr,
+                       "FAIL: Decrypted data are different from the input 
data.\n");
+               printf("cipher text should be:\t");
+               for (i = 0; i < BLOCK_SIZE; i++) {
+                       printf("%02x ", (unsigned char)ciphertext2[i]);
+               }
+               printf("\nbut really is:\t\t");
+               for (i = 0; i < BLOCK_SIZE; i++) {
+                       printf("%02x ", (unsigned char)plaintext2[i]);
+               }
+               printf("\n");
+               return 1;
+       } else {
+               printf("result 2 passed\n");
+       }
+
+       printf("AES Test passed\n");
+
+       /* Finish crypto session */
+       if (ioctl(cfd, CIOCFSESSION, &sess1.ses)) {
+               perror("ioctl(CIOCFSESSION)");
+               return 1;
+       }
+       if (ioctl(cfd, CIOCFSESSION, &sess2.ses)) {
+               perror("ioctl(CIOCFSESSION)");
+               return 1;
+       }
+
+       return 0;
+}
+
+int
+main()
+{
+       int fd = -1, cfd = -1;
+
+       /* Open the crypto device */
+       fd = open("/dev/crypto", O_RDWR, 0);
+       if (fd < 0) {
+               perror("open(/dev/crypto)");
+               return 1;
+       }
+
+       /* Clone file descriptor */
+       if (ioctl(fd, CRIOGET, &cfd)) {
+               perror("ioctl(CRIOGET)");
+               return 1;
+       }
+
+       /* Set close-on-exec (not really neede here) */
+       if (fcntl(cfd, F_SETFD, 1) == -1) {
+               perror("fcntl(F_SETFD)");
+               return 1;
+       }
+
+       /* Run the test itself */
+       if (test_aes(cfd))
+               return 1;
+
+       if (test_crypto(cfd))
+               return 1;
+
+       /* Close cloned descriptor */
+       if (close(cfd)) {
+               perror("close(cfd)");
+               return 1;
+       }
+
+       /* Close the original descriptor */
+       if (close(fd)) {
+               perror("close(fd)");
+               return 1;
+       }
+
+       return 0;
+}
+
diff --git a/examples/async_speed.c b/examples/async_speed.c
new file mode 100644
index 0000000..0a11f05
--- /dev/null
+++ b/examples/async_speed.c
@@ -0,0 +1,197 @@
+/*  cryptodev_test - simple benchmark tool for cryptodev
+ *
+ *    Copyright (C) 2010 by Phil Sutter <phil.sut...@viprinet.com>
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <signal.h>
+#include "../cryptodev.h"
+
+static double udifftimeval(struct timeval start, struct timeval end)
+{
+       return (double)(end.tv_usec - start.tv_usec) +
+              (double)(end.tv_sec - start.tv_sec) * 1000 * 1000;
+}
+
+static int must_finish = 0;
+static struct pollfd pfd;
+
+static void alarm_handler(int signo)
+{
+        must_finish = 1;
+       pfd.events = POLLIN;
+}
+
+static void value2human(double bytes, double time, double* data, double* 
speed,char* metric)
+{
+        if (bytes > 1000 && bytes < 1000*1000) {
+                *data = ((double)bytes)/1000;
+                *speed = *data/time;
+                strcpy(metric, "Kb");
+                return;
+        } else if (bytes >= 1000*1000 && bytes < 1000*1000*1000) {
+                *data = ((double)bytes)/(1000*1000);
+                *speed = *data/time;
+                strcpy(metric, "Mb");
+                return;
+        } else if (bytes >= 1000*1000*1000) {
+                *data = ((double)bytes)/(1000*1000*1000);
+                *speed = *data/time;
+                strcpy(metric, "Gb");
+                return;
+        } else {
+                *data = (double)bytes;
+                *speed = *data/time;
+                strcpy(metric, "bytes");
+                return;
+        }
+}
+
+
+int encrypt_data(struct session_op *sess, int fdc, int chunksize, int flags)
+{
+       struct crypt_op cop;
+       char *buffer[256], iv[32];
+       static int val = 23;
+       struct timeval start, end;
+       double total = 0;
+       double secs, ddata, dspeed;
+       char metric[16];
+       int rc, wqueue = 0, bufidx = 0;
+
+       memset(iv, 0x23, 32);
+
+       printf("\tEncrypting in chunks of %d bytes: ", chunksize);
+       fflush(stdout);
+
+       for (rc = 0; rc < 256; rc++) {
+               buffer[rc] = malloc(chunksize);
+               memset(buffer[rc], val++, chunksize);
+       }
+       pfd.fd = fdc;
+       pfd.events = POLLOUT | POLLIN;
+
+       must_finish = 0;
+       alarm(5);
+
+       gettimeofday(&start, NULL);
+       do {
+               if ((rc = poll(&pfd, 1, 100)) < 0) {
+                       if (errno & (ERESTART | EINTR))
+                               continue;
+                       fprintf(stderr, "errno = %d ", errno);
+                       perror("poll()");
+                       return 1;
+               }
+
+               if (pfd.revents & POLLOUT) {
+                       memset(&cop, 0, sizeof(cop));
+                       cop.ses = sess->ses;
+                       cop.len = chunksize;
+                       cop.iv = (unsigned char *)iv;
+                       cop.op = COP_ENCRYPT;
+                       cop.flags = flags;
+                       cop.src = cop.dst = (unsigned char *)buffer[bufidx];
+                       bufidx = (bufidx + 1) % 256;
+
+                       if (ioctl(fdc, CIOCASYNCCRYPT, &cop)) {
+                               perror("ioctl(CIOCASYNCCRYPT)");
+                               return 1;
+                       }
+                       wqueue++;
+               }
+               if (pfd.revents & POLLIN) {
+                       if (ioctl(fdc, CIOCASYNCFETCH, &cop)) {
+                               perror("ioctl(CIOCASYNCFETCH)");
+                               return 1;
+                       }
+                       wqueue--;
+                       total += cop.len;
+               }
+       } while(!must_finish || wqueue);
+       gettimeofday(&end, NULL);
+
+       secs = udifftimeval(start, end)/ 1000000.0;
+
+       value2human(total, secs, &ddata, &dspeed, metric);
+       printf ("done. %.2f %s in %.2f secs: ", ddata, metric, secs);
+       printf ("%.2f %s/sec\n", dspeed, metric);
+
+       for (rc = 0; rc < 256; rc++)
+               free(buffer[rc]);
+       return 0;
+}
+
+int main(void)
+{
+       int fd, i, fdc = -1;
+       struct session_op sess;
+       char keybuf[32];
+
+       signal(SIGALRM, alarm_handler);
+
+       if ((fd = open("/dev/crypto", O_RDWR, 0)) < 0) {
+               perror("open()");
+               return 1;
+       }
+       if (ioctl(fd, CRIOGET, &fdc)) {
+               perror("ioctl(CRIOGET)");
+               return 1;
+       }
+
+       fprintf(stderr, "Testing NULL cipher: \n");
+       memset(&sess, 0, sizeof(sess));
+       sess.cipher = CRYPTO_NULL;
+       sess.keylen = 0;
+       sess.key = (unsigned char *)keybuf;
+       if (ioctl(fdc, CIOCGSESSION, &sess)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+
+       for (i = 256; i <= (64 * 4096); i *= 2) {
+               if (encrypt_data(&sess, fdc, i, 0))
+                       break;
+       }
+
+       fprintf(stderr, "\nTesting AES-128-CBC cipher: \n");
+       memset(&sess, 0, sizeof(sess));
+       sess.cipher = CRYPTO_AES_CBC;
+       sess.keylen = 16;
+       memset(keybuf, 0x42, 16);
+       sess.key = (unsigned char *)keybuf;
+       if (ioctl(fdc, CIOCGSESSION, &sess)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+
+       for (i = 256; i <= (64 * 1024); i *= 2) {
+               if (encrypt_data(&sess, fdc, i, 0))
+                       break;
+       }
+
+       close(fdc);
+       close(fd);
+       return 0;
+}
-- 
1.7.2.2



_______________________________________________
Cryptodev-linux-devel mailing list
Cryptodev-linux-devel@gna.org
https://mail.gna.org/listinfo/cryptodev-linux-devel

Reply via email to