crypto: mxs-dcp - Fix wait logic on chan threads
authorLeonard Crestez <leonard.crestez@nxp.com>
Fri, 21 Sep 2018 15:03:18 +0000 (18:03 +0300)
committerHerbert Xu <herbert@gondor.apana.org.au>
Fri, 28 Sep 2018 04:45:37 +0000 (12:45 +0800)
When compiling with CONFIG_DEBUG_ATOMIC_SLEEP=y the mxs-dcp driver
prints warnings such as:

WARNING: CPU: 0 PID: 120 at kernel/sched/core.c:7736 __might_sleep+0x98/0x9c
do not call blocking ops when !TASK_RUNNING; state=1 set at [<8081978c>] dcp_chan_thread_sha+0x3c/0x2ec

The problem is that blocking ops will manipulate current->state
themselves so it is not allowed to call them between
set_current_state(TASK_INTERRUPTIBLE) and schedule().

Fix this by converting the per-chan mutex to a spinlock (it only
protects tiny list ops anyway) and rearranging the wait logic so that
callbacks are called current->state as TASK_RUNNING. Those callbacks
will indeed call blocking ops themselves so this is required.

Cc: <stable@vger.kernel.org>
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
drivers/crypto/mxs-dcp.c

index a10c418d4e5c60b7aba530f5ce7a9a0865930489..56bd28174f5251c11c8996a959fc7e96160ee6ac 100644 (file)
@@ -63,7 +63,7 @@ struct dcp {
        struct dcp_coherent_block       *coh;
 
        struct completion               completion[DCP_MAX_CHANS];
-       struct mutex                    mutex[DCP_MAX_CHANS];
+       spinlock_t                      lock[DCP_MAX_CHANS];
        struct task_struct              *thread[DCP_MAX_CHANS];
        struct crypto_queue             queue[DCP_MAX_CHANS];
 };
@@ -349,13 +349,20 @@ static int dcp_chan_thread_aes(void *data)
 
        int ret;
 
-       do {
-               __set_current_state(TASK_INTERRUPTIBLE);
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
 
-               mutex_lock(&sdcp->mutex[chan]);
+               spin_lock(&sdcp->lock[chan]);
                backlog = crypto_get_backlog(&sdcp->queue[chan]);
                arq = crypto_dequeue_request(&sdcp->queue[chan]);
-               mutex_unlock(&sdcp->mutex[chan]);
+               spin_unlock(&sdcp->lock[chan]);
+
+               if (!backlog && !arq) {
+                       schedule();
+                       continue;
+               }
+
+               set_current_state(TASK_RUNNING);
 
                if (backlog)
                        backlog->complete(backlog, -EINPROGRESS);
@@ -363,11 +370,8 @@ static int dcp_chan_thread_aes(void *data)
                if (arq) {
                        ret = mxs_dcp_aes_block_crypt(arq);
                        arq->complete(arq, ret);
-                       continue;
                }
-
-               schedule();
-       } while (!kthread_should_stop());
+       }
 
        return 0;
 }
@@ -409,9 +413,9 @@ static int mxs_dcp_aes_enqueue(struct ablkcipher_request *req, int enc, int ecb)
        rctx->ecb = ecb;
        actx->chan = DCP_CHAN_CRYPTO;
 
-       mutex_lock(&sdcp->mutex[actx->chan]);
+       spin_lock(&sdcp->lock[actx->chan]);
        ret = crypto_enqueue_request(&sdcp->queue[actx->chan], &req->base);
-       mutex_unlock(&sdcp->mutex[actx->chan]);
+       spin_unlock(&sdcp->lock[actx->chan]);
 
        wake_up_process(sdcp->thread[actx->chan]);
 
@@ -640,13 +644,20 @@ static int dcp_chan_thread_sha(void *data)
        struct ahash_request *req;
        int ret, fini;
 
-       do {
-               __set_current_state(TASK_INTERRUPTIBLE);
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
 
-               mutex_lock(&sdcp->mutex[chan]);
+               spin_lock(&sdcp->lock[chan]);
                backlog = crypto_get_backlog(&sdcp->queue[chan]);
                arq = crypto_dequeue_request(&sdcp->queue[chan]);
-               mutex_unlock(&sdcp->mutex[chan]);
+               spin_unlock(&sdcp->lock[chan]);
+
+               if (!backlog && !arq) {
+                       schedule();
+                       continue;
+               }
+
+               set_current_state(TASK_RUNNING);
 
                if (backlog)
                        backlog->complete(backlog, -EINPROGRESS);
@@ -658,12 +669,8 @@ static int dcp_chan_thread_sha(void *data)
                        ret = dcp_sha_req_to_buf(arq);
                        fini = rctx->fini;
                        arq->complete(arq, ret);
-                       if (!fini)
-                               continue;
                }
-
-               schedule();
-       } while (!kthread_should_stop());
+       }
 
        return 0;
 }
@@ -721,9 +728,9 @@ static int dcp_sha_update_fx(struct ahash_request *req, int fini)
                rctx->init = 1;
        }
 
-       mutex_lock(&sdcp->mutex[actx->chan]);
+       spin_lock(&sdcp->lock[actx->chan]);
        ret = crypto_enqueue_request(&sdcp->queue[actx->chan], &req->base);
-       mutex_unlock(&sdcp->mutex[actx->chan]);
+       spin_unlock(&sdcp->lock[actx->chan]);
 
        wake_up_process(sdcp->thread[actx->chan]);
        mutex_unlock(&actx->mutex);
@@ -997,7 +1004,7 @@ static int mxs_dcp_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, sdcp);
 
        for (i = 0; i < DCP_MAX_CHANS; i++) {
-               mutex_init(&sdcp->mutex[i]);
+               spin_lock_init(&sdcp->lock[i]);
                init_completion(&sdcp->completion[i]);
                crypto_init_queue(&sdcp->queue[i], 50);
        }