crypto: sahara - replace tasklets with kthread
In preparation for SHA support, replace the tasklets with a kthread that manages one crypto_queue for the core. As the Sahara can only process one AES or SHA request at a time, we make sure that the queue serializes all requests from userspace. Instead of a watchdog timer we now use a completion mechanism in the queue manager thread. This makes the control flow more obvious and guarantees, that only one request is dequeued until the completion is completed. Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
parent
5ed903b3f5
commit
c0c3c89ae3
|
@ -22,7 +22,9 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/kthread.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -38,7 +40,6 @@
|
||||||
#define FLAGS_ENCRYPT BIT(0)
|
#define FLAGS_ENCRYPT BIT(0)
|
||||||
#define FLAGS_CBC BIT(1)
|
#define FLAGS_CBC BIT(1)
|
||||||
#define FLAGS_NEW_KEY BIT(3)
|
#define FLAGS_NEW_KEY BIT(3)
|
||||||
#define FLAGS_BUSY 4
|
|
||||||
|
|
||||||
#define SAHARA_HDR_BASE 0x00800000
|
#define SAHARA_HDR_BASE 0x00800000
|
||||||
#define SAHARA_HDR_SKHA_ALG_AES 0
|
#define SAHARA_HDR_SKHA_ALG_AES 0
|
||||||
|
@ -119,7 +120,6 @@ struct sahara_hw_link {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sahara_ctx {
|
struct sahara_ctx {
|
||||||
struct sahara_dev *dev;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int keylen;
|
int keylen;
|
||||||
u8 key[AES_KEYSIZE_128];
|
u8 key[AES_KEYSIZE_128];
|
||||||
|
@ -136,15 +136,15 @@ struct sahara_dev {
|
||||||
void __iomem *regs_base;
|
void __iomem *regs_base;
|
||||||
struct clk *clk_ipg;
|
struct clk *clk_ipg;
|
||||||
struct clk *clk_ahb;
|
struct clk *clk_ahb;
|
||||||
|
struct mutex queue_mutex;
|
||||||
|
struct task_struct *kthread;
|
||||||
|
struct completion dma_completion;
|
||||||
|
|
||||||
struct sahara_ctx *ctx;
|
struct sahara_ctx *ctx;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct crypto_queue queue;
|
struct crypto_queue queue;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
struct tasklet_struct done_task;
|
|
||||||
struct tasklet_struct queue_task;
|
|
||||||
|
|
||||||
struct sahara_hw_desc *hw_desc[SAHARA_MAX_HW_DESC];
|
struct sahara_hw_desc *hw_desc[SAHARA_MAX_HW_DESC];
|
||||||
dma_addr_t hw_phys_desc[SAHARA_MAX_HW_DESC];
|
dma_addr_t hw_phys_desc[SAHARA_MAX_HW_DESC];
|
||||||
|
|
||||||
|
@ -157,7 +157,6 @@ struct sahara_dev {
|
||||||
struct sahara_hw_link *hw_link[SAHARA_MAX_HW_LINK];
|
struct sahara_hw_link *hw_link[SAHARA_MAX_HW_LINK];
|
||||||
dma_addr_t hw_phys_link[SAHARA_MAX_HW_LINK];
|
dma_addr_t hw_phys_link[SAHARA_MAX_HW_LINK];
|
||||||
|
|
||||||
struct ablkcipher_request *req;
|
|
||||||
size_t total;
|
size_t total;
|
||||||
struct scatterlist *in_sg;
|
struct scatterlist *in_sg;
|
||||||
unsigned int nb_in_sg;
|
unsigned int nb_in_sg;
|
||||||
|
@ -165,7 +164,6 @@ struct sahara_dev {
|
||||||
unsigned int nb_out_sg;
|
unsigned int nb_out_sg;
|
||||||
|
|
||||||
u32 error;
|
u32 error;
|
||||||
struct timer_list watchdog;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sahara_dev *dev_ptr;
|
static struct sahara_dev *dev_ptr;
|
||||||
|
@ -404,34 +402,6 @@ static void sahara_dump_links(struct sahara_dev *dev)
|
||||||
dev_dbg(dev->device, "\n");
|
dev_dbg(dev->device, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sahara_aes_done_task(unsigned long data)
|
|
||||||
{
|
|
||||||
struct sahara_dev *dev = (struct sahara_dev *)data;
|
|
||||||
|
|
||||||
dma_unmap_sg(dev->device, dev->out_sg, dev->nb_out_sg,
|
|
||||||
DMA_TO_DEVICE);
|
|
||||||
dma_unmap_sg(dev->device, dev->in_sg, dev->nb_in_sg,
|
|
||||||
DMA_FROM_DEVICE);
|
|
||||||
|
|
||||||
spin_lock(&dev->lock);
|
|
||||||
clear_bit(FLAGS_BUSY, &dev->flags);
|
|
||||||
spin_unlock(&dev->lock);
|
|
||||||
|
|
||||||
dev->req->base.complete(&dev->req->base, dev->error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sahara_watchdog(unsigned long data)
|
|
||||||
{
|
|
||||||
struct sahara_dev *dev = (struct sahara_dev *)data;
|
|
||||||
unsigned int err = sahara_read(dev, SAHARA_REG_ERRSTATUS);
|
|
||||||
unsigned int stat = sahara_read(dev, SAHARA_REG_STATUS);
|
|
||||||
|
|
||||||
sahara_decode_status(dev, stat);
|
|
||||||
sahara_decode_error(dev, err);
|
|
||||||
dev->error = -ETIMEDOUT;
|
|
||||||
sahara_aes_done_task(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sahara_hw_descriptor_create(struct sahara_dev *dev)
|
static int sahara_hw_descriptor_create(struct sahara_dev *dev)
|
||||||
{
|
{
|
||||||
struct sahara_ctx *ctx = dev->ctx;
|
struct sahara_ctx *ctx = dev->ctx;
|
||||||
|
@ -515,9 +485,6 @@ static int sahara_hw_descriptor_create(struct sahara_dev *dev)
|
||||||
sahara_dump_descriptors(dev);
|
sahara_dump_descriptors(dev);
|
||||||
sahara_dump_links(dev);
|
sahara_dump_links(dev);
|
||||||
|
|
||||||
/* Start processing descriptor chain. */
|
|
||||||
mod_timer(&dev->watchdog,
|
|
||||||
jiffies + msecs_to_jiffies(SAHARA_TIMEOUT_MS));
|
|
||||||
sahara_write(dev, dev->hw_phys_desc[0], SAHARA_REG_DAR);
|
sahara_write(dev, dev->hw_phys_desc[0], SAHARA_REG_DAR);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -532,37 +499,19 @@ unmap_in:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sahara_aes_queue_task(unsigned long data)
|
static int sahara_aes_process(struct ablkcipher_request *req)
|
||||||
{
|
{
|
||||||
struct sahara_dev *dev = (struct sahara_dev *)data;
|
struct sahara_dev *dev = dev_ptr;
|
||||||
struct crypto_async_request *async_req, *backlog;
|
|
||||||
struct sahara_ctx *ctx;
|
struct sahara_ctx *ctx;
|
||||||
struct sahara_aes_reqctx *rctx;
|
struct sahara_aes_reqctx *rctx;
|
||||||
struct ablkcipher_request *req;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock(&dev->lock);
|
|
||||||
backlog = crypto_get_backlog(&dev->queue);
|
|
||||||
async_req = crypto_dequeue_request(&dev->queue);
|
|
||||||
if (!async_req)
|
|
||||||
clear_bit(FLAGS_BUSY, &dev->flags);
|
|
||||||
spin_unlock(&dev->lock);
|
|
||||||
|
|
||||||
if (!async_req)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (backlog)
|
|
||||||
backlog->complete(backlog, -EINPROGRESS);
|
|
||||||
|
|
||||||
req = ablkcipher_request_cast(async_req);
|
|
||||||
|
|
||||||
/* Request is ready to be dispatched by the device */
|
/* Request is ready to be dispatched by the device */
|
||||||
dev_dbg(dev->device,
|
dev_dbg(dev->device,
|
||||||
"dispatch request (nbytes=%d, src=%p, dst=%p)\n",
|
"dispatch request (nbytes=%d, src=%p, dst=%p)\n",
|
||||||
req->nbytes, req->src, req->dst);
|
req->nbytes, req->src, req->dst);
|
||||||
|
|
||||||
/* assign new request to device */
|
/* assign new request to device */
|
||||||
dev->req = req;
|
|
||||||
dev->total = req->nbytes;
|
dev->total = req->nbytes;
|
||||||
dev->in_sg = req->src;
|
dev->in_sg = req->src;
|
||||||
dev->out_sg = req->dst;
|
dev->out_sg = req->dst;
|
||||||
|
@ -576,16 +525,25 @@ static void sahara_aes_queue_task(unsigned long data)
|
||||||
memcpy(dev->iv_base, req->info, AES_KEYSIZE_128);
|
memcpy(dev->iv_base, req->info, AES_KEYSIZE_128);
|
||||||
|
|
||||||
/* assign new context to device */
|
/* assign new context to device */
|
||||||
ctx->dev = dev;
|
|
||||||
dev->ctx = ctx;
|
dev->ctx = ctx;
|
||||||
|
|
||||||
|
reinit_completion(&dev->dma_completion);
|
||||||
|
|
||||||
ret = sahara_hw_descriptor_create(dev);
|
ret = sahara_hw_descriptor_create(dev);
|
||||||
if (ret < 0) {
|
|
||||||
spin_lock(&dev->lock);
|
ret = wait_for_completion_timeout(&dev->dma_completion,
|
||||||
clear_bit(FLAGS_BUSY, &dev->flags);
|
msecs_to_jiffies(SAHARA_TIMEOUT_MS));
|
||||||
spin_unlock(&dev->lock);
|
if (!ret) {
|
||||||
dev->req->base.complete(&dev->req->base, ret);
|
dev_err(dev->device, "AES timeout\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dma_unmap_sg(dev->device, dev->out_sg, dev->nb_out_sg,
|
||||||
|
DMA_TO_DEVICE);
|
||||||
|
dma_unmap_sg(dev->device, dev->in_sg, dev->nb_in_sg,
|
||||||
|
DMA_FROM_DEVICE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sahara_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
|
static int sahara_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
|
||||||
|
@ -627,12 +585,9 @@ static int sahara_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
|
||||||
|
|
||||||
static int sahara_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
|
static int sahara_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
|
||||||
{
|
{
|
||||||
struct sahara_ctx *ctx = crypto_ablkcipher_ctx(
|
|
||||||
crypto_ablkcipher_reqtfm(req));
|
|
||||||
struct sahara_aes_reqctx *rctx = ablkcipher_request_ctx(req);
|
struct sahara_aes_reqctx *rctx = ablkcipher_request_ctx(req);
|
||||||
struct sahara_dev *dev = dev_ptr;
|
struct sahara_dev *dev = dev_ptr;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
int busy;
|
|
||||||
|
|
||||||
dev_dbg(dev->device, "nbytes: %d, enc: %d, cbc: %d\n",
|
dev_dbg(dev->device, "nbytes: %d, enc: %d, cbc: %d\n",
|
||||||
req->nbytes, !!(mode & FLAGS_ENCRYPT), !!(mode & FLAGS_CBC));
|
req->nbytes, !!(mode & FLAGS_ENCRYPT), !!(mode & FLAGS_CBC));
|
||||||
|
@ -643,16 +598,13 @@ static int sahara_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->dev = dev;
|
|
||||||
|
|
||||||
rctx->mode = mode;
|
rctx->mode = mode;
|
||||||
spin_lock_bh(&dev->lock);
|
|
||||||
err = ablkcipher_enqueue_request(&dev->queue, req);
|
|
||||||
busy = test_and_set_bit(FLAGS_BUSY, &dev->flags);
|
|
||||||
spin_unlock_bh(&dev->lock);
|
|
||||||
|
|
||||||
if (!busy)
|
mutex_lock(&dev->queue_mutex);
|
||||||
tasklet_schedule(&dev->queue_task);
|
err = ablkcipher_enqueue_request(&dev->queue, req);
|
||||||
|
mutex_unlock(&dev->queue_mutex);
|
||||||
|
|
||||||
|
wake_up_process(dev->kthread);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -755,6 +707,36 @@ static void sahara_aes_cra_exit(struct crypto_tfm *tfm)
|
||||||
ctx->fallback = NULL;
|
ctx->fallback = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sahara_queue_manage(void *data)
|
||||||
|
{
|
||||||
|
struct sahara_dev *dev = (struct sahara_dev *)data;
|
||||||
|
struct crypto_async_request *async_req;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
__set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
|
||||||
|
mutex_lock(&dev->queue_mutex);
|
||||||
|
async_req = crypto_dequeue_request(&dev->queue);
|
||||||
|
mutex_unlock(&dev->queue_mutex);
|
||||||
|
|
||||||
|
if (async_req) {
|
||||||
|
struct ablkcipher_request *req =
|
||||||
|
ablkcipher_request_cast(async_req);
|
||||||
|
|
||||||
|
ret = sahara_aes_process(req);
|
||||||
|
|
||||||
|
async_req->complete(async_req, ret);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule();
|
||||||
|
} while (!kthread_should_stop());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct crypto_alg aes_algs[] = {
|
static struct crypto_alg aes_algs[] = {
|
||||||
{
|
{
|
||||||
.cra_name = "ecb(aes)",
|
.cra_name = "ecb(aes)",
|
||||||
|
@ -806,8 +788,6 @@ static irqreturn_t sahara_irq_handler(int irq, void *data)
|
||||||
unsigned int stat = sahara_read(dev, SAHARA_REG_STATUS);
|
unsigned int stat = sahara_read(dev, SAHARA_REG_STATUS);
|
||||||
unsigned int err = sahara_read(dev, SAHARA_REG_ERRSTATUS);
|
unsigned int err = sahara_read(dev, SAHARA_REG_ERRSTATUS);
|
||||||
|
|
||||||
del_timer(&dev->watchdog);
|
|
||||||
|
|
||||||
sahara_write(dev, SAHARA_CMD_CLEAR_INT | SAHARA_CMD_CLEAR_ERR,
|
sahara_write(dev, SAHARA_CMD_CLEAR_INT | SAHARA_CMD_CLEAR_ERR,
|
||||||
SAHARA_REG_CMD);
|
SAHARA_REG_CMD);
|
||||||
|
|
||||||
|
@ -822,7 +802,7 @@ static irqreturn_t sahara_irq_handler(int irq, void *data)
|
||||||
dev->error = -EINVAL;
|
dev->error = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tasklet_schedule(&dev->done_task);
|
complete(&dev->dma_completion);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -961,17 +941,17 @@ static int sahara_probe(struct platform_device *pdev)
|
||||||
crypto_init_queue(&dev->queue, SAHARA_QUEUE_LENGTH);
|
crypto_init_queue(&dev->queue, SAHARA_QUEUE_LENGTH);
|
||||||
|
|
||||||
spin_lock_init(&dev->lock);
|
spin_lock_init(&dev->lock);
|
||||||
|
mutex_init(&dev->queue_mutex);
|
||||||
|
|
||||||
dev_ptr = dev;
|
dev_ptr = dev;
|
||||||
|
|
||||||
tasklet_init(&dev->queue_task, sahara_aes_queue_task,
|
dev->kthread = kthread_run(sahara_queue_manage, dev, "sahara_crypto");
|
||||||
(unsigned long)dev);
|
if (IS_ERR(dev->kthread)) {
|
||||||
tasklet_init(&dev->done_task, sahara_aes_done_task,
|
err = PTR_ERR(dev->kthread);
|
||||||
(unsigned long)dev);
|
goto err_link;
|
||||||
|
}
|
||||||
|
|
||||||
init_timer(&dev->watchdog);
|
init_completion(&dev->dma_completion);
|
||||||
dev->watchdog.function = &sahara_watchdog;
|
|
||||||
dev->watchdog.data = (unsigned long)dev;
|
|
||||||
|
|
||||||
clk_prepare_enable(dev->clk_ipg);
|
clk_prepare_enable(dev->clk_ipg);
|
||||||
clk_prepare_enable(dev->clk_ahb);
|
clk_prepare_enable(dev->clk_ahb);
|
||||||
|
@ -1016,6 +996,7 @@ err_algs:
|
||||||
dev->hw_link[0], dev->hw_phys_link[0]);
|
dev->hw_link[0], dev->hw_phys_link[0]);
|
||||||
clk_disable_unprepare(dev->clk_ipg);
|
clk_disable_unprepare(dev->clk_ipg);
|
||||||
clk_disable_unprepare(dev->clk_ahb);
|
clk_disable_unprepare(dev->clk_ahb);
|
||||||
|
kthread_stop(dev->kthread);
|
||||||
dev_ptr = NULL;
|
dev_ptr = NULL;
|
||||||
err_link:
|
err_link:
|
||||||
dma_free_coherent(&pdev->dev,
|
dma_free_coherent(&pdev->dev,
|
||||||
|
@ -1043,8 +1024,7 @@ static int sahara_remove(struct platform_device *pdev)
|
||||||
SAHARA_MAX_HW_DESC * sizeof(struct sahara_hw_desc),
|
SAHARA_MAX_HW_DESC * sizeof(struct sahara_hw_desc),
|
||||||
dev->hw_desc[0], dev->hw_phys_desc[0]);
|
dev->hw_desc[0], dev->hw_phys_desc[0]);
|
||||||
|
|
||||||
tasklet_kill(&dev->done_task);
|
kthread_stop(dev->kthread);
|
||||||
tasklet_kill(&dev->queue_task);
|
|
||||||
|
|
||||||
sahara_unregister_algs(dev);
|
sahara_unregister_algs(dev);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue