crypto: omap-sham - error handling improved
Introduces DMA error handling.
DMA error is returned as a result code of the hash request.
Clients needs to handle error codes and may repeat hash calculation attempt.
Also in the case of DMA error, SHAM module is set to be re-initialized again.
It significantly improves stability against possible HW failures.
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c
index 9dfbc4a..db20628 100644
--- a/drivers/crypto/omap-sham.c
+++ b/drivers/crypto/omap-sham.c
@@ -83,6 +83,7 @@
#define FLAGS_INIT 0x0100
#define FLAGS_CPU 0x0200
#define FLAGS_HMAC 0x0400
+#define FLAGS_ERROR 0x0800
/* 3rd byte */
#define FLAGS_BUSY 16
@@ -137,6 +138,7 @@
int irq;
struct clk *iclk;
spinlock_t lock;
+ int err;
int dma;
int dma_lch;
struct tasklet_struct done_task;
@@ -234,10 +236,12 @@
SHA_REG_MASK_SOFTRESET, SHA_REG_MASK_SOFTRESET);
if (omap_sham_wait(dd, SHA_REG_SYSSTATUS,
- SHA_REG_SYSSTATUS_RESETDONE))
+ SHA_REG_SYSSTATUS_RESETDONE)) {
+ clk_disable(dd->iclk);
return -ETIMEDOUT;
-
+ }
dd->flags |= FLAGS_INIT;
+ dd->err = 0;
}
} else {
omap_sham_write(dd, SHA_REG_DIGCNT, ctx->digcnt);
@@ -279,11 +283,12 @@
if (err)
return err;
+ /* should be non-zero before next lines to disable clocks later */
+ ctx->digcnt += length;
+
if (omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY))
return -ETIMEDOUT;
- ctx->digcnt += length;
-
if (final)
ctx->flags |= FLAGS_FINAL; /* catch last interrupt */
@@ -303,7 +308,6 @@
dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n",
ctx->digcnt, length, final);
-
/* flush cache entries related to our page */
if (dma_addr == ctx->dma_addr)
dma_sync_single_for_device(dd->dev, dma_addr, length,
@@ -411,6 +415,7 @@
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
unsigned int length;
+ int err;
ctx->flags |= FLAGS_FAST;
@@ -424,7 +429,11 @@
ctx->total -= length;
- return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, 1);
+ err = omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, 1);
+ if (err != -EINPROGRESS)
+ dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE);
+
+ return err;
}
static int omap_sham_update_cpu(struct omap_sham_dev *dd)
@@ -580,9 +589,6 @@
ctx->bufcnt = 0;
- if (err != -EINPROGRESS)
- omap_sham_cleanup(req);
-
dev_dbg(dd->dev, "final_req: err: %d\n", err);
return err;
@@ -616,9 +622,11 @@
omap_sham_copy_hash(ctx->dd->req, 1);
if (ctx->flags & FLAGS_HMAC)
err = omap_sham_finish_req_hmac(req);
+ } else {
+ ctx->flags |= FLAGS_ERROR;
}
- if (ctx->flags & FLAGS_FINAL)
+ if ((ctx->flags & FLAGS_FINAL) || err)
omap_sham_cleanup(req);
clear_bit(FLAGS_BUSY, &ctx->dd->flags);
@@ -776,12 +784,14 @@
ctx->flags |= FLAGS_FINUP;
- /* OMAP HW accel works only with buffers >= 9 */
- /* HMAC is always >= 9 because of ipad */
- if ((ctx->digcnt + ctx->bufcnt) < 9)
- err = omap_sham_final_shash(req);
- else if (ctx->bufcnt)
- return omap_sham_enqueue(req, OP_FINAL);
+ if (!(ctx->flags & FLAGS_ERROR)) {
+ /* OMAP HW accel works only with buffers >= 9 */
+ /* HMAC is always >= 9 because of ipad */
+ if ((ctx->digcnt + ctx->bufcnt) < 9)
+ err = omap_sham_final_shash(req);
+ else if (ctx->bufcnt)
+ return omap_sham_enqueue(req, OP_FINAL);
+ }
omap_sham_cleanup(req);
@@ -851,6 +861,8 @@
struct omap_sham_ctx *tctx = crypto_tfm_ctx(tfm);
const char *alg_name = crypto_tfm_alg_name(tfm);
+ pr_info("enter\n");
+
/* Allocate a fallback and abort if it failed. */
tctx->fallback = crypto_alloc_shash(alg_name, 0,
CRYPTO_ALG_NEED_FALLBACK);
@@ -1008,7 +1020,7 @@
struct omap_sham_dev *dd = (struct omap_sham_dev *)data;
struct ahash_request *req = dd->req;
struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
- int ready = 1;
+ int ready = 0, err = 0;
if (ctx->flags & FLAGS_OUTPUT_READY) {
ctx->flags &= ~FLAGS_OUTPUT_READY;
@@ -1018,13 +1030,16 @@
if (dd->flags & FLAGS_DMA_ACTIVE) {
dd->flags &= ~FLAGS_DMA_ACTIVE;
omap_sham_update_dma_stop(dd);
- omap_sham_update_dma_slow(dd);
+ if (!dd->err)
+ err = omap_sham_update_dma_slow(dd);
}
- if (ready && !(dd->flags & FLAGS_DMA_ACTIVE)) {
- dev_dbg(dd->dev, "update done\n");
+ err = dd->err ? : err;
+
+ if (err != -EINPROGRESS && (ready || err)) {
+ dev_dbg(dd->dev, "update done: err: %d\n", err);
/* finish curent request */
- omap_sham_finish_req(req, 0);
+ omap_sham_finish_req(req, err);
/* start new request */
omap_sham_handle_queue(dd);
}
@@ -1056,6 +1071,7 @@
omap_sham_read(dd, SHA_REG_CTRL);
ctx->flags |= FLAGS_OUTPUT_READY;
+ dd->err = 0;
tasklet_schedule(&dd->done_task);
return IRQ_HANDLED;
@@ -1065,8 +1081,13 @@
{
struct omap_sham_dev *dd = data;
- if (likely(lch == dd->dma_lch))
- tasklet_schedule(&dd->done_task);
+ if (ch_status != OMAP_DMA_BLOCK_IRQ) {
+ pr_err("omap-sham DMA error status: 0x%hx\n", ch_status);
+ dd->err = -EIO;
+ dd->flags &= ~FLAGS_INIT; /* request to re-initialize */
+ }
+
+ tasklet_schedule(&dd->done_task);
}
static int omap_sham_dma_init(struct omap_sham_dev *dd)