crytpo: tegra-aes: make aes_handle_req aynschronous
Varun Wadekar [Tue, 28 Dec 2010 09:31:18 +0000 (14:31 +0530)]
the encrypt/decrypt callbacks have to return with -EINPROGRESS
error code and the request complete callback needs to be
called from handle_req for aynchronous block ciphers. use
work queue to make the driver asynchronous.

Change-Id: I0dec1185c31e5de7ba039c39d6bd87c8b3487b2a
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>

drivers/crypto/tegra-aes.c

index 1d47758..d94046a 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/interrupt.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/workqueue.h>
 
 #include <mach/arb_sema.h>
 #include <mach/clk.h>
@@ -144,8 +145,7 @@ struct tegra_aes_reqctx {
        unsigned long mode;
 };
 
-#define TEGRA_AES_QUEUE_LENGTH 1
-#define TEGRA_AES_CACHE_SIZE 0
+#define TEGRA_AES_QUEUE_LENGTH 50
 
 struct tegra_aes_dev {
        struct device *dev;
@@ -197,6 +197,9 @@ static LIST_HEAD(dev_list);
 static DEFINE_SPINLOCK(list_lock);
 static DEFINE_MUTEX(aes_lock);
 
+static void aes_workqueue_handler(struct work_struct *work);
+static DECLARE_WORK(aes_wq, aes_workqueue_handler);
+
 extern unsigned long long tegra_chip_uid(void);
 
 static inline u32 aes_readl(struct tegra_aes_dev *dd, u32 offset)
@@ -350,7 +353,6 @@ static void aes_release_key_slot(struct tegra_aes_dev *dd)
        spin_lock(&list_lock);
        dd->ctx->slot->available = true;
        dd->ctx->slot = NULL;
-       dd->ctx = NULL;
        spin_unlock(&list_lock);
 }
 
@@ -380,6 +382,11 @@ static int aes_set_key(struct tegra_aes_dev *dd)
        int i, eng_busy, icq_empty, dma_busy, ret = 0;
        bool use_ssk = false;
 
+       if (!ctx) {
+               dev_err(dd->dev, "%s: context invalid\n", __func__);
+               return -EINVAL;
+       }
+
        /* use ssk? */
        if (!dd->ctx->slot) {
                dev_dbg(dd->dev, "using ssk");
@@ -472,10 +479,8 @@ static int tegra_aes_handle_req(struct tegra_aes_dev *dd)
                clear_bit(FLAGS_BUSY, &dd->flags);
        spin_unlock_irqrestore(&dd->lock, flags);
 
-       if (!async_req) {
-               dev_err(dd->dev, "no request");
-               return 0;
-       }
+       if (!async_req)
+               return -ENODATA;
 
        if (backlog)
                backlog->complete(backlog, -EINPROGRESS);
@@ -484,6 +489,9 @@ static int tegra_aes_handle_req(struct tegra_aes_dev *dd)
 
        dev_dbg(dd->dev, "%s: get new req\n", __func__);
 
+       /* take mutex to access the aes hw */
+       mutex_lock(&aes_lock);
+
        /* assign new request to device */
        dd->req = req;
        dd->total = req->nbytes;
@@ -492,6 +500,15 @@ static int tegra_aes_handle_req(struct tegra_aes_dev *dd)
        dd->out_offset = 0;
        dd->out_sg = req->dst;
 
+       in_sg = dd->in_sg;
+       out_sg = dd->out_sg;
+
+       if (!in_sg || !out_sg) {
+               mutex_unlock(&aes_lock);
+               return -EINVAL;
+       }
+
+       total = dd->total;
        rctx = ablkcipher_request_ctx(req);
        ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
        rctx->mode &= FLAGS_MODE_MASK;
@@ -512,9 +529,6 @@ static int tegra_aes_handle_req(struct tegra_aes_dev *dd)
                ctx->flags |= FLAGS_NEW_KEY;
        }
 
-       /* take mutex to access the aes hw */
-       mutex_lock(&aes_lock);
-
        /* take the hardware semaphore */
        if (tegra_arb_mutex_lock_timeout(dd->res_id, ARB_SEMA_TIMEOUT) < 0) {
                dev_err(dd->dev, "aes hardware not available\n");
@@ -522,15 +536,17 @@ static int tegra_aes_handle_req(struct tegra_aes_dev *dd)
                return -EBUSY;
        }
 
-       total = dd->total;
-       in_sg = dd->in_sg;
-       out_sg = dd->out_sg;
-
        aes_set_key(dd);
 
        /* set iv to the aes hw slot */
        memset(dd->buf_in, 0 , AES_BLOCK_SIZE);
-       memcpy(dd->buf_in, dd->iv, dd->ivlen);
+       ret = copy_from_user((void *)dd->buf_in, (void __user *)dd->iv,
+               dd->ivlen);
+       if (ret < 0) {
+               dev_err(dd->dev, "copy_from_user fail(%d)\n", ret);
+               goto out;
+       }
+
        ret = aes_start_crypt(dd, (u32)dd->dma_buf_in,
          (u32)dd->dma_buf_out, 1, FLAGS_CBC, false);
        if (ret < 0) {
@@ -586,45 +602,16 @@ out:
        /* release the hardware semaphore */
        tegra_arb_mutex_unlock(dd->res_id);
 
-       /* release the mutex */
-       mutex_unlock(&aes_lock);
-
        dd->total = total;
-       if (!dd->total) {
-               clear_bit(FLAGS_BUSY, &dd->flags);
-               aes_release_key_slot(dd);
-       }
-
-       dev_dbg(dd->dev, "exit\n");
-       return ret;
-}
-
-static int tegra_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
-{
-       struct tegra_aes_reqctx *rctx = ablkcipher_request_ctx(req);
-       struct tegra_aes_dev *dd = aes_dev;
-       unsigned long flags;
-       int err = 0;
-
-       dev_dbg(dd->dev, "nbytes: %d, enc: %d, cbc: %d\n", req->nbytes,
-               !!(mode & FLAGS_ENCRYPT),
-               !!(mode & FLAGS_CBC));
-
-       rctx->mode = mode;
 
-       spin_lock_irqsave(&dd->lock, flags);
-       err = ablkcipher_enqueue_request(&dd->queue, req);
-       spin_unlock_irqrestore(&dd->lock, flags);
-
-       if (!test_and_set_bit(FLAGS_BUSY, &dd->flags))
-               err = tegra_aes_handle_req(dd);
-       else
-               err = -EBUSY;
+       /* release the mutex */
+       mutex_unlock(&aes_lock);
 
        if (dd->req->base.complete)
-               dd->req->base.complete(&dd->req->base, err);
+               dd->req->base.complete(&dd->req->base, ret);
 
-       return err;
+       dev_dbg(dd->dev, "%s: exit\n", __func__);
+       return ret;
 }
 
 static int tegra_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
@@ -648,15 +635,18 @@ static int tegra_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
 
        dev_dbg(dd->dev, "keylen: %d\n", keylen);
 
+       ctx->dd = dd;
+       dd->ctx = ctx;
+
+       if (ctx->slot)
+               aes_release_key_slot(dd);
+
        key_slot = aes_find_key_slot(dd);
        if (!key_slot) {
                dev_err(dd->dev, "no empty slot\n");
                return -ENOMEM;
        }
 
-       ctx->dd = dd;
-       dd->ctx = ctx;
-
        ctx->slot = key_slot;
        ctx->keylen = keylen;
        ctx->flags |= FLAGS_NEW_KEY;
@@ -669,6 +659,55 @@ static int tegra_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
        return 0;
 }
 
+static void aes_workqueue_handler(struct work_struct *work)
+{
+       struct tegra_aes_dev *dd = aes_dev;
+       int ret;
+
+       set_bit(FLAGS_BUSY, &dd->flags);
+
+       do {
+               ret = tegra_aes_handle_req(dd);
+       } while (!ret);
+}
+
+static irqreturn_t aes_irq(int irq, void *dev_id)
+{
+       struct tegra_aes_dev *dd = (struct tegra_aes_dev *)dev_id;
+       u32 value = aes_readl(dd, INTR_STATUS);
+
+       dev_dbg(dd->dev, "irq_stat: 0x%x", value);
+       if (!((value & ENGINE_BUSY_FIELD) & !(value & ICQ_EMPTY_FIELD)))
+               complete(&dd->op_complete);
+
+       return IRQ_HANDLED;
+}
+
+static int tegra_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
+{
+       struct tegra_aes_reqctx *rctx = ablkcipher_request_ctx(req);
+       struct tegra_aes_dev *dd = aes_dev;
+       unsigned long flags;
+       int err = 0;
+       int busy;
+
+       dev_dbg(dd->dev, "nbytes: %d, enc: %d, cbc: %d\n", req->nbytes,
+               !!(mode & FLAGS_ENCRYPT),
+               !!(mode & FLAGS_CBC));
+
+       rctx->mode = mode;
+
+       spin_lock_irqsave(&dd->lock, flags);
+       err = ablkcipher_enqueue_request(&dd->queue, req);
+       busy = test_and_set_bit(FLAGS_BUSY, &dd->flags);
+       spin_unlock_irqrestore(&dd->lock, flags);
+
+       if (!busy)
+               schedule_work(&aes_wq);
+
+       return err;
+}
+
 static int tegra_aes_ecb_encrypt(struct ablkcipher_request *req)
 {
        return tegra_aes_crypt(req, FLAGS_ENCRYPT);
@@ -893,18 +932,6 @@ static struct crypto_alg algs[] = {
        }
 };
 
-static irqreturn_t aes_irq(int irq, void *dev_id)
-{
-       struct tegra_aes_dev *dd = (struct tegra_aes_dev *)dev_id;
-       u32 value = aes_readl(dd, INTR_STATUS);
-
-       dev_dbg(dd->dev, "irq_stat: 0x%x", value);
-       if (!((value & ENGINE_BUSY_FIELD) & !(value & ICQ_EMPTY_FIELD)))
-               complete(&dd->op_complete);
-
-       return IRQ_HANDLED;
-}
-
 static int tegra_aes_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -1067,6 +1094,7 @@ static int __devexit tegra_aes_remove(struct platform_device *pdev)
        if (!dd)
                return -ENODEV;
 
+       cancel_work_sync(&aes_wq);
        free_irq(INT_VDE_BSE_V, dd);
        spin_lock(&list_lock);
        list_del(&dev_list);