aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/crypto/s5p-sss.c
diff options
context:
space:
mode:
authorKrzysztof Kozlowski <k.kozlowski@samsung.com>2016-04-22 08:15:23 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2016-04-25 07:14:54 -0400
commit79152e8d085fd64484afd473ef6830b45518acba (patch)
tree5aa57f4ad000bb19bfef13f52a71381ef8b13a13 /drivers/crypto/s5p-sss.c
parent5e00c6040dfd367e35bdc7b8ef28861ff8b1dd64 (diff)
crypto: s5p-sss - Fix missed interrupts when working with 8 kB blocks
The tcrypt testing module on Exynos5422-based Odroid XU3/4 board failed on testing 8 kB size blocks: $ sudo modprobe tcrypt sec=1 mode=500 testing speed of async ecb(aes) (ecb-aes-s5p) encryption test 0 (128 bit key, 16 byte blocks): 21971 operations in 1 seconds (351536 bytes) test 1 (128 bit key, 64 byte blocks): 21731 operations in 1 seconds (1390784 bytes) test 2 (128 bit key, 256 byte blocks): 21932 operations in 1 seconds (5614592 bytes) test 3 (128 bit key, 1024 byte blocks): 21685 operations in 1 seconds (22205440 bytes) test 4 (128 bit key, 8192 byte blocks): This was caused by a race issue of missed BRDMA_DONE ("Block cipher Receiving DMA") interrupt. Device starts processing the data in DMA mode immediately after setting length of DMA block: receiving (FCBRDMAL) or transmitting (FCBTDMAL). The driver sets these lengths from interrupt handler through s5p_set_dma_indata() function (or xxx_setdata()). However the interrupt handler was first dealing with receive buffer (dma-unmap old, dma-map new, set receive block length which starts the operation), then with transmit buffer and finally was clearing pending interrupts (FCINTPEND). Because of the time window between setting receive buffer length and clearing pending interrupts, the operation on receive buffer could end already and driver would miss new interrupt. User manual for Exynos5422 confirms in example code that setting DMA block lengths should be the last operation. The tcrypt hang could be also observed in following blocked-task dmesg: INFO: task modprobe:258 blocked for more than 120 seconds. Not tainted 4.6.0-rc4-next-20160419-00005-g9eac8b7b7753-dirty #42 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. modprobe D c06b09d8 0 258 256 0x00000000 [<c06b09d8>] (__schedule) from [<c06b0f24>] (schedule+0x40/0xac) [<c06b0f24>] (schedule) from [<c06b49f8>] (schedule_timeout+0x124/0x178) [<c06b49f8>] (schedule_timeout) from [<c06b17fc>] (wait_for_common+0xb8/0x144) [<c06b17fc>] (wait_for_common) from [<bf0013b8>] (test_acipher_speed+0x49c/0x740 [tcrypt]) [<bf0013b8>] (test_acipher_speed [tcrypt]) from [<bf003e8c>] (do_test+0x2240/0x30ec [tcrypt]) [<bf003e8c>] (do_test [tcrypt]) from [<bf008048>] (tcrypt_mod_init+0x48/0xa4 [tcrypt]) [<bf008048>] (tcrypt_mod_init [tcrypt]) from [<c010177c>] (do_one_initcall+0x3c/0x16c) [<c010177c>] (do_one_initcall) from [<c0191ff0>] (do_init_module+0x5c/0x1ac) [<c0191ff0>] (do_init_module) from [<c0185610>] (load_module+0x1a30/0x1d08) [<c0185610>] (load_module) from [<c0185ab0>] (SyS_finit_module+0x8c/0x98) [<c0185ab0>] (SyS_finit_module) from [<c01078c0>] (ret_fast_syscall+0x0/0x3c) Fixes: a49e490c7a8a ("crypto: s5p-sss - add S5PV210 advanced crypto engine support") Cc: <stable@vger.kernel.org> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/crypto/s5p-sss.c')
-rw-r--r--drivers/crypto/s5p-sss.c53
1 files changed, 39 insertions, 14 deletions
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c
index b96532078d0c..ac6d62b3be07 100644
--- a/drivers/crypto/s5p-sss.c
+++ b/drivers/crypto/s5p-sss.c
@@ -367,43 +367,55 @@ exit:
367 return err; 367 return err;
368} 368}
369 369
370static void s5p_aes_tx(struct s5p_aes_dev *dev) 370/*
371 * Returns true if new transmitting (output) data is ready and its
372 * address+length have to be written to device (by calling
373 * s5p_set_dma_outdata()). False otherwise.
374 */
375static bool s5p_aes_tx(struct s5p_aes_dev *dev)
371{ 376{
372 int err = 0; 377 int err = 0;
378 bool ret = false;
373 379
374 s5p_unset_outdata(dev); 380 s5p_unset_outdata(dev);
375 381
376 if (!sg_is_last(dev->sg_dst)) { 382 if (!sg_is_last(dev->sg_dst)) {
377 err = s5p_set_outdata(dev, sg_next(dev->sg_dst)); 383 err = s5p_set_outdata(dev, sg_next(dev->sg_dst));
378 if (err) { 384 if (err)
379 s5p_aes_complete(dev, err); 385 s5p_aes_complete(dev, err);
380 return; 386 else
381 } 387 ret = true;
382
383 s5p_set_dma_outdata(dev, dev->sg_dst);
384 } else { 388 } else {
385 s5p_aes_complete(dev, err); 389 s5p_aes_complete(dev, err);
386 390
387 dev->busy = true; 391 dev->busy = true;
388 tasklet_schedule(&dev->tasklet); 392 tasklet_schedule(&dev->tasklet);
389 } 393 }
394
395 return ret;
390} 396}
391 397
392static void s5p_aes_rx(struct s5p_aes_dev *dev) 398/*
399 * Returns true if new receiving (input) data is ready and its
400 * address+length have to be written to device (by calling
401 * s5p_set_dma_indata()). False otherwise.
402 */
403static bool s5p_aes_rx(struct s5p_aes_dev *dev)
393{ 404{
394 int err; 405 int err;
406 bool ret = false;
395 407
396 s5p_unset_indata(dev); 408 s5p_unset_indata(dev);
397 409
398 if (!sg_is_last(dev->sg_src)) { 410 if (!sg_is_last(dev->sg_src)) {
399 err = s5p_set_indata(dev, sg_next(dev->sg_src)); 411 err = s5p_set_indata(dev, sg_next(dev->sg_src));
400 if (err) { 412 if (err)
401 s5p_aes_complete(dev, err); 413 s5p_aes_complete(dev, err);
402 return; 414 else
403 } 415 ret = true;
404
405 s5p_set_dma_indata(dev, dev->sg_src);
406 } 416 }
417
418 return ret;
407} 419}
408 420
409static irqreturn_t s5p_aes_interrupt(int irq, void *dev_id) 421static irqreturn_t s5p_aes_interrupt(int irq, void *dev_id)
@@ -412,17 +424,30 @@ static irqreturn_t s5p_aes_interrupt(int irq, void *dev_id)
412 struct s5p_aes_dev *dev = platform_get_drvdata(pdev); 424 struct s5p_aes_dev *dev = platform_get_drvdata(pdev);
413 uint32_t status; 425 uint32_t status;
414 unsigned long flags; 426 unsigned long flags;
427 bool set_dma_tx = false;
428 bool set_dma_rx = false;
415 429
416 spin_lock_irqsave(&dev->lock, flags); 430 spin_lock_irqsave(&dev->lock, flags);
417 431
418 status = SSS_READ(dev, FCINTSTAT); 432 status = SSS_READ(dev, FCINTSTAT);
419 if (status & SSS_FCINTSTAT_BRDMAINT) 433 if (status & SSS_FCINTSTAT_BRDMAINT)
420 s5p_aes_rx(dev); 434 set_dma_rx = s5p_aes_rx(dev);
421 if (status & SSS_FCINTSTAT_BTDMAINT) 435 if (status & SSS_FCINTSTAT_BTDMAINT)
422 s5p_aes_tx(dev); 436 set_dma_tx = s5p_aes_tx(dev);
423 437
424 SSS_WRITE(dev, FCINTPEND, status); 438 SSS_WRITE(dev, FCINTPEND, status);
425 439
440 /*
441 * Writing length of DMA block (either receiving or transmitting)
442 * will start the operation immediately, so this should be done
443 * at the end (even after clearing pending interrupts to not miss the
444 * interrupt).
445 */
446 if (set_dma_tx)
447 s5p_set_dma_outdata(dev, dev->sg_dst);
448 if (set_dma_rx)
449 s5p_set_dma_indata(dev, dev->sg_src);
450
426 spin_unlock_irqrestore(&dev->lock, flags); 451 spin_unlock_irqrestore(&dev->lock, flags);
427 452
428 return IRQ_HANDLED; 453 return IRQ_HANDLED;