Subject: [CRYPTO] [1/1] skeleton for async crypto drivers

This skeleton should be a good start for everyone who is still looking for
the documentation :)
The code is based mainly on Herbert's crypto/cryptd.c. The obvious
difference is, that it does not wrap around the complete argument.
As mentioned in the code, process_requests_thread() could be optimized to
call blkcipher_crypt() directly with the parameter (and remove the
function pointer).

Sebastian
Index: linux/crypto/async_drv_skeleton.c
===================================================================
--- /dev/null
+++ linux/crypto/async_drv_skeleton.c
@@ -0,0 +1,402 @@
+/*
+ * Template for async crypto HW
+ *
+ * Code is based mainly on the crypto/cryptd.c code
+ */
+
+#include <crypto/algapi.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <asm/byteorder.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/kthread.h>
+
+
+#define CRYPTO_MAX_QLEN 101
+
+/* not to use the HW twice at the same time */
+struct hw_work_ctx {
+ struct hw_context *hw_ctx;
+ struct mutex mutex;
+};
+
+static struct hw_work_ctx work_ctx;
+
+struct algo_ctx_t {
+ char key[42];
+ int keylen;
+};
+
+#define ALGO_MIN_KEY_SIZE 16
+#define ALGO_MAX_KEY_SIZE 32
+#define ALGO_BLOCKSIZE 16
+
+/* hardware functions */
+#define HW_DECRYPT_ECB 0xbeef
+#define HW_ENCRYPT_ECB 0xbabe
+
+static int hw_set_key(struct hw_context *hw_ctx, const char *inkey, int keylen,
+ char *outkey)
+{
+ return 1;
+}
+
+static int hw_crypt_process(int operation_type, const char *key, const char *inbuf,
+ char *outbuf, unsigned long size)
+{
+ return 2;
+}
+static void *get_hw_ctx(void)
+{
+ return NULL;
+}
+
+static void free_hw_ctx(struct hw_context *hw_ctx)
+{
+ return;
+}
+
+/* algo -> HW */
+static int _algo_set_key(struct algo_ctx_t *ctx, const u8 *in_key,
+ unsigned int key_len)
+{
+ int ret;
+
+ switch (key_len) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&work_ctx.mutex);
+
+ ctx->keylen = key_len / 4;
+ ret = hw_set_key(work_ctx.hw_ctx, in_key, key_len, ctx->key);
+ if (ret < 0) {
+ printk("%s() failed\n", __FUNCTION__);
+ }
+
+ mutex_unlock(&work_ctx.mutex);
+ return ret < 0 ? ret : 0;
+}
+
+/*
+ * this is from s390's AES
+ */
+static int mode_algo_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes, int op_type)
+{
+ struct algo_ctx_t *algo_ctx = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ u8 *out, *in;
+ int ret;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ ret = blkcipher_walk_virt(desc, &walk);
+
+ while ((nbytes = walk.nbytes)) {
+ /* only use complete blocks */
+ unsigned int n = nbytes & ~(15);
+
+ out = walk.dst.virt.addr;
+ in = walk.src.virt.addr;
+
+ printk("%s(): processing data from %p to %p (%d bytes)\n", __FUNCTION__,
+ in, out, n);
+
+ mutex_lock(&work_ctx.mutex);
+
+ ret = hw_crypt_process(op_type, algo_ctx->key, in, out, n);
+ mutex_unlock(&work_ctx.mutex);
+
+ if (ret < 0) {
+ printk("hw_crypt error: %d\n", ret);
+ }
+
+ nbytes &= 15;
+ ret = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+ return ret;
+}
+
+/* per ablk cipher data */
+struct async_algo_state {
+ spinlock_t queue_spinlock;
+ struct mutex queue_mutex;
+ struct crypto_queue requests_queue;
+ struct task_struct *requests_thread;
+};
+
+/* per request struct */
+struct async_d_request {
+ crypto_completion_t crypto_complete;
+};
+
+static struct async_algo_state algo_state;
+
+/* set key is NOT async */
+static int algo_set_key_async(struct crypto_ablkcipher *parent,
+ const u8 *key, unsigned int keylen)
+{
+ struct algo_ctx_t *ctx = crypto_ablkcipher_ctx(parent);
+
+ return _algo_set_key(ctx, key, keylen);
+}
+
+static void blkcipher_crypt(struct ablkcipher_request *req,
+ struct algo_ctx_t *ctx,
+ int err,
+ int op_type)
+{
+ struct blkcipher_desc desc;
+
+ printk("%s()\n", __FUNCTION__);
+ if (unlikely(err == -EINPROGRESS)) {
+ printk("err = -EINPROGRESS in %s\n", __FUNCTION__);
+ req->base.complete(&req->base, err);
+ return;
+ }
+
+ /*
+ * desc is required by crypto's scatter code.
+ * info is the IV.
+ */
+ desc.tfm = crypto_blkcipher_cast(req->base.tfm);
+ desc.info = req->info;
+ desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ err = mode_algo_crypt(&desc, req->dst, req->src, req->nbytes, op_type);
+
+ local_bh_disable();
+ /*
+ * call the crypto user and inform him about his request
+ */
+ req->base.complete(&req->base, err);
+ local_bh_enable();
+}
+
+/*
+ * the next two are called from the working thread
+ * ([a_d_ctx|a_d_backreq]->crypto_complete). This could be optimized by calling
+ * blkcipher_crypt() directly with the special parameter
+ */
+static void algo_request_encrypt (struct crypto_async_request *req, int err)
+{
+ struct algo_ctx_t *ctx = crypto_tfm_ctx(req->tfm);
+
+ blkcipher_crypt(ablkcipher_request_cast(req), ctx, err, HW_ENCRYPT_ECB);
+}
+
+static void algo_request_decrypt (struct crypto_async_request *req, int err)
+{
+ struct algo_ctx_t *ctx = crypto_tfm_ctx(req->tfm);
+
+ blkcipher_crypt(ablkcipher_request_cast(req), ctx, err, HW_DECRYPT_ECB);
+}
+
+/*
+ * queue one request and wakeup work thread
+ */
+static int enqueue_request(struct ablkcipher_request *req,
+ crypto_completion_t complete)
+{
+ struct async_d_request *asy_d_ctx = ablkcipher_request_ctx(req);
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ int ret;
+
+ asy_d_ctx->crypto_complete = complete;
+
+ spin_lock_bh(&algo_state.queue_spinlock);
+ ret = ablkcipher_enqueue_request(crypto_ablkcipher_alg(tfm), req);
+ spin_unlock_bh(&algo_state.queue_spinlock);
+
+ wake_up_process(algo_state.requests_thread);
+ return ret;
+}
+
+/*
+ * called from crypto user. We queue the crypto function + request.
+ * Crypto request is stored in our per request struct.
+ */
+static int algo_encrypt_ecb_async(struct ablkcipher_request *req)
+{
+
+ return enqueue_request(req, algo_request_encrypt);
+}
+
+static int algo_decrypt_ecb_async(struct ablkcipher_request *req)
+{
+
+ return enqueue_request(req, algo_request_decrypt);
+}
+
+static int async_d_init(struct crypto_tfm *tfm)
+{
+ /* important to save request specific data */
+ printk("currently in %s()\n", __FUNCTION__);
+ tfm->crt_ablkcipher.reqsize = sizeof(struct async_d_request);
+ return 0;
+}
+
+static void async_d_exit(struct crypto_tfm *tfm)
+{
+ printk("currently in %s()\n", __FUNCTION__);
+}
+
+static void async_d_destory(struct crypto_alg *alg)
+{
+ printk("currently in %s()\n", __FUNCTION__);
+}
+
+static struct crypto_alg algo_ecb_alg_async = {
+ .cra_name = "ecb(algo)",
+ .cra_driver_name = "ecb-algohw-async",
+ .cra_priority = 123,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = ALGO_BLOCKSIZE,
+ .cra_alignmask = 15,
+ .cra_ctxsize = sizeof(struct algo_ctx_t),
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(algo_ecb_alg_async.cra_list),
+ .cra_init = async_d_init,
+ .cra_exit = async_d_exit,
+ .cra_destroy = async_d_destory,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = ALGO_MIN_KEY_SIZE,
+ .max_keysize = ALGO_MAX_KEY_SIZE,
+ .ivsize = 0,
+ .setkey = algo_set_key_async,
+ .encrypt = algo_encrypt_ecb_async,
+ .decrypt = algo_decrypt_ecb_async,
+ .queue = &algo_state.requests_queue,
+ }
+ }
+};
+
+static int process_requests_thread(void *data) {
+
+ struct async_algo_state *state = data;
+ int stop;
+ struct crypto_async_request *req, *backlog;
+ struct ablkcipher_request *ablk_req;
+ struct async_d_request *a_d_ctx, *a_d_backreq;
+
+ printk("thread active\n");
+ do {
+ printk("%s(): Starting request\n", __FUNCTION__);
+ mutex_lock(&state->queue_mutex);
+ __set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_bh(&state->queue_spinlock);
+ backlog = crypto_get_backlog(&state->requests_queue);
+ req = crypto_dequeue_request(&state->requests_queue);
+ spin_unlock_bh(&state->queue_spinlock);
+
+ printk("backlog: %p, req: %p\n", backlog, req);
+
+ stop = kthread_should_stop();
+
+ if (stop || req) {
+
+ __set_current_state(TASK_RUNNING);
+ if (req) {
+ if (backlog) {
+ ablk_req = ablkcipher_request_cast(backlog);
+ a_d_backreq = ablkcipher_request_ctx(ablk_req);
+ a_d_backreq->crypto_complete(backlog, -EINPROGRESS);
+ }
+
+ ablk_req = ablkcipher_request_cast(req);
+ a_d_ctx = ablkcipher_request_ctx(ablk_req);
+ a_d_ctx->crypto_complete(req, 0);
+ }
+ }
+
+ mutex_unlock(&state->queue_mutex);
+ schedule();
+
+ } while(!stop);
+
+ return 0;
+}
+
+static int async_api_init(void) {
+ int ret;
+
+ crypto_init_queue(&algo_state.requests_queue, CRYPTO_MAX_QLEN);
+ mutex_init(&algo_state.queue_mutex);
+ spin_lock_init(&algo_state.queue_spinlock);
+
+ algo_state.requests_thread = kthread_create(process_requests_thread, &algo_state, "cryptoHW");
+ if (IS_ERR(algo_state.requests_thread)) {
+ printk("kthread_create() failed: %ld\n", PTR_ERR(algo_state.requests_thread));
+ return PTR_ERR(algo_state.requests_thread);
+ }
+
+ ret = crypto_register_alg(&algo_ecb_alg_async);
+ if (ret) {
+ printk("crypto_register_alg() failed: %d\n", ret);
+ kthread_stop(algo_state.requests_thread);
+ return ret;
+ }
+
+ return 0;
+}
+static int async_api_fini(void) {
+
+ /*
+ * thread should not be busy anymore, nor should receive any
+ * further requests
+ */
+ BUG_ON(algo_state.requests_queue.qlen);
+ crypto_unregister_alg(&algo_ecb_alg_async);
+ kthread_stop(algo_state.requests_thread);
+ return 0;
+}
+
+static int __init algo_init(void)
+{
+ unsigned int ret;
+
+ work_ctx.hw_ctx = get_hw_ctx();
+
+ if (IS_ERR(work_ctx.hw_ctx)) {
+ ret = PTR_ERR(work_ctx.hw_ctx);
+ printk("Can't get HW: %d\n", ret);
+ return ret;
+ }
+ mutex_init(&work_ctx.mutex);
+
+ ret = async_api_init();
+ if (ret) {
+ printk("async_api_init() failed\n");
+ goto out_ctx;
+ }
+ return 0;
+out_ctx:
+ free_hw_ctx(work_ctx.hw_ctx);
+ return ret;
+}
+
+static void __exit algo_fini(void)
+{
+ async_api_fini();
+ free_hw_ctx(work_ctx.hw_ctx);
+}
+
+module_init(algo_init);
+module_exit(algo_fini);
+
+MODULE_DESCRIPTION("Skeleton for ASYNC crypto driver");
+MODULE_LICENSE("GPL");


Subject: Re: [CRYPTO] [1/1] skeleton for async crypto drivers

* Sebastian Siewior | 2007-05-22 17:16:57 [+0200]:

>+static int async_d_init(struct crypto_tfm *tfm)
>+{
>+ /* important to save request specific data */
>+ tfm->crt_ablkcipher.reqsize = sizeof(struct async_d_request);
>+ return 0;
>+}
..
>+static int process_requests_thread(void *data) {
..
>+ if (backlog) {
>+ ablk_req = ablkcipher_request_cast(backlog);
>+ a_d_backreq = ablkcipher_request_ctx(ablk_req);
>+ a_d_backreq->crypto_complete(backlog, -EINPROGRESS);
>+ }
>+
>+ ablk_req = ablkcipher_request_cast(req);
>+ a_d_ctx = ablkcipher_request_ctx(ablk_req);
>+ a_d_ctx->crypto_complete(req, 0);
>+ }
...

Currently the crypto driver has to allocate a structure in order to
remember later whether encrypt or decrypt is requested.
Isn't it better to add a operation field into the request struct which
contains the requested operation (set but the crypto user)? This would
then replace encrypt & decrypt function with a crypt function which
queues the request.

Sebastian

2007-05-25 06:49:22

by Herbert Xu

[permalink] [raw]
Subject: Re: [CRYPTO] [1/1] skeleton for async crypto drivers

Sebastian Siewior <[email protected]> wrote:
>
> Currently the crypto driver has to allocate a structure in order to
> remember later whether encrypt or decrypt is requested.
> Isn't it better to add a operation field into the request struct which
> contains the requested operation (set but the crypto user)? This would
> then replace encrypt & decrypt function with a crypt function which
> queues the request.

No doubt that you have to allocate it one way or another. Not
allocating it explicitly means that you have the freedom of
representing it in the most convenient fashion. For example,
the cryptd module represents it using a function pointer.

Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt