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(¤t->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(¤t->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