| From 0618764cb25f6fa9fb31152995de42a8a0496475 Mon Sep 17 00:00:00 2001 |
| From: Ben Collins <ben.c@servergy.com> |
| Date: Fri, 3 Apr 2015 16:09:46 +0000 |
| Subject: dm crypt: fix deadlock when async crypto algorithm returns -EBUSY |
| |
| From: Ben Collins <ben.c@servergy.com> |
| |
| commit 0618764cb25f6fa9fb31152995de42a8a0496475 upstream. |
| |
| I suspect this doesn't show up for most anyone because software |
| algorithms typically don't have a sense of being too busy. However, |
| when working with the Freescale CAAM driver it will return -EBUSY on |
| occasion under heavy -- which resulted in dm-crypt deadlock. |
| |
| After checking the logic in some other drivers, the scheme for |
| crypt_convert() and it's callback, kcryptd_async_done(), were not |
| correctly laid out to properly handle -EBUSY or -EINPROGRESS. |
| |
| Fix this by using the completion for both -EBUSY and -EINPROGRESS. Now |
| crypt_convert()'s use of completion is comparable to |
| af_alg_wait_for_completion(). Similarly, kcryptd_async_done() follows |
| the pattern used in af_alg_complete(). |
| |
| Before this fix dm-crypt would lockup within 1-2 minutes running with |
| the CAAM driver. Fix was regression tested against software algorithms |
| on PPC32 and x86_64, and things seem perfectly happy there as well. |
| |
| Signed-off-by: Ben Collins <ben.c@servergy.com> |
| Signed-off-by: Mike Snitzer <snitzer@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/md/dm-crypt.c | 12 ++++++------ |
| 1 file changed, 6 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/md/dm-crypt.c |
| +++ b/drivers/md/dm-crypt.c |
| @@ -925,11 +925,10 @@ static int crypt_convert(struct crypt_co |
| |
| switch (r) { |
| /* async */ |
| + case -EINPROGRESS: |
| case -EBUSY: |
| wait_for_completion(&ctx->restart); |
| reinit_completion(&ctx->restart); |
| - /* fall through*/ |
| - case -EINPROGRESS: |
| ctx->req = NULL; |
| ctx->cc_sector++; |
| continue; |
| @@ -1346,10 +1345,8 @@ static void kcryptd_async_done(struct cr |
| struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx); |
| struct crypt_config *cc = io->cc; |
| |
| - if (error == -EINPROGRESS) { |
| - complete(&ctx->restart); |
| + if (error == -EINPROGRESS) |
| return; |
| - } |
| |
| if (!error && cc->iv_gen_ops && cc->iv_gen_ops->post) |
| error = cc->iv_gen_ops->post(cc, iv_of_dmreq(cc, dmreq), dmreq); |
| @@ -1360,12 +1357,15 @@ static void kcryptd_async_done(struct cr |
| crypt_free_req(cc, req_of_dmreq(cc, dmreq), io->base_bio); |
| |
| if (!atomic_dec_and_test(&ctx->cc_pending)) |
| - return; |
| + goto done; |
| |
| if (bio_data_dir(io->base_bio) == READ) |
| kcryptd_crypt_read_done(io); |
| else |
| kcryptd_crypt_write_io_submit(io, 1); |
| +done: |
| + if (!completion_done(&ctx->restart)) |
| + complete(&ctx->restart); |
| } |
| |
| static void kcryptd_crypt(struct work_struct *work) |