diff options
author | Dave Jiang <dave.jiang@intel.com> | 2016-07-20 16:13:55 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2016-08-07 22:41:42 -0400 |
commit | 9546d4cdc8445acdea415f70a330bbfbd016a0f0 (patch) | |
tree | deed065e3d0e0f1d78acd80e9a0d42cc31b0a581 | |
parent | f067025bc676ba8d18fba5f959598339e39b86db (diff) |
dmaengine: ioatdma: Add error handling to ioat driver
Adding error handling to the ioatdma driver so that when a
read/write error occurs the error results are reported back and
all the remaining descriptors are aborted. This utilizes the new
dmaengine callback function that allows reporting of results.
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
-rw-r--r-- | drivers/dma/ioat/dma.c | 140 | ||||
-rw-r--r-- | drivers/dma/ioat/registers.h | 2 |
2 files changed, 126 insertions, 16 deletions
diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 6499de4b8e79..251f8be639c2 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c | |||
@@ -568,10 +568,14 @@ static void __cleanup(struct ioatdma_chan *ioat_chan, dma_addr_t phys_complete) | |||
568 | 568 | ||
569 | tx = &desc->txd; | 569 | tx = &desc->txd; |
570 | if (tx->cookie) { | 570 | if (tx->cookie) { |
571 | struct dmaengine_result res; | ||
572 | |||
571 | dma_cookie_complete(tx); | 573 | dma_cookie_complete(tx); |
572 | dma_descriptor_unmap(tx); | 574 | dma_descriptor_unmap(tx); |
575 | res.result = DMA_TRANS_NOERROR; | ||
573 | dmaengine_desc_get_callback_invoke(tx, NULL); | 576 | dmaengine_desc_get_callback_invoke(tx, NULL); |
574 | tx->callback = NULL; | 577 | tx->callback = NULL; |
578 | tx->callback_result = NULL; | ||
575 | } | 579 | } |
576 | 580 | ||
577 | if (tx->phys == phys_complete) | 581 | if (tx->phys == phys_complete) |
@@ -620,7 +624,8 @@ static void ioat_cleanup(struct ioatdma_chan *ioat_chan) | |||
620 | if (is_ioat_halted(*ioat_chan->completion)) { | 624 | if (is_ioat_halted(*ioat_chan->completion)) { |
621 | u32 chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); | 625 | u32 chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); |
622 | 626 | ||
623 | if (chanerr & IOAT_CHANERR_HANDLE_MASK) { | 627 | if (chanerr & |
628 | (IOAT_CHANERR_HANDLE_MASK | IOAT_CHANERR_RECOVER_MASK)) { | ||
624 | mod_timer(&ioat_chan->timer, jiffies + IDLE_TIMEOUT); | 629 | mod_timer(&ioat_chan->timer, jiffies + IDLE_TIMEOUT); |
625 | ioat_eh(ioat_chan); | 630 | ioat_eh(ioat_chan); |
626 | } | 631 | } |
@@ -650,6 +655,61 @@ static void ioat_restart_channel(struct ioatdma_chan *ioat_chan) | |||
650 | __ioat_restart_chan(ioat_chan); | 655 | __ioat_restart_chan(ioat_chan); |
651 | } | 656 | } |
652 | 657 | ||
658 | |||
659 | static void ioat_abort_descs(struct ioatdma_chan *ioat_chan) | ||
660 | { | ||
661 | struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; | ||
662 | struct ioat_ring_ent *desc; | ||
663 | u16 active; | ||
664 | int idx = ioat_chan->tail, i; | ||
665 | |||
666 | /* | ||
667 | * We assume that the failed descriptor has been processed. | ||
668 | * Now we are just returning all the remaining submitted | ||
669 | * descriptors to abort. | ||
670 | */ | ||
671 | active = ioat_ring_active(ioat_chan); | ||
672 | |||
673 | /* we skip the failed descriptor that tail points to */ | ||
674 | for (i = 1; i < active; i++) { | ||
675 | struct dma_async_tx_descriptor *tx; | ||
676 | |||
677 | smp_read_barrier_depends(); | ||
678 | prefetch(ioat_get_ring_ent(ioat_chan, idx + i + 1)); | ||
679 | desc = ioat_get_ring_ent(ioat_chan, idx + i); | ||
680 | |||
681 | tx = &desc->txd; | ||
682 | if (tx->cookie) { | ||
683 | struct dmaengine_result res; | ||
684 | |||
685 | dma_cookie_complete(tx); | ||
686 | dma_descriptor_unmap(tx); | ||
687 | res.result = DMA_TRANS_ABORTED; | ||
688 | dmaengine_desc_get_callback_invoke(tx, &res); | ||
689 | tx->callback = NULL; | ||
690 | tx->callback_result = NULL; | ||
691 | } | ||
692 | |||
693 | /* skip extended descriptors */ | ||
694 | if (desc_has_ext(desc)) { | ||
695 | WARN_ON(i + 1 >= active); | ||
696 | i++; | ||
697 | } | ||
698 | |||
699 | /* cleanup super extended descriptors */ | ||
700 | if (desc->sed) { | ||
701 | ioat_free_sed(ioat_dma, desc->sed); | ||
702 | desc->sed = NULL; | ||
703 | } | ||
704 | } | ||
705 | |||
706 | smp_mb(); /* finish all descriptor reads before incrementing tail */ | ||
707 | ioat_chan->tail = idx + active; | ||
708 | |||
709 | desc = ioat_get_ring_ent(ioat_chan, ioat_chan->tail); | ||
710 | ioat_chan->last_completion = *ioat_chan->completion = desc->txd.phys; | ||
711 | } | ||
712 | |||
653 | static void ioat_eh(struct ioatdma_chan *ioat_chan) | 713 | static void ioat_eh(struct ioatdma_chan *ioat_chan) |
654 | { | 714 | { |
655 | struct pci_dev *pdev = to_pdev(ioat_chan); | 715 | struct pci_dev *pdev = to_pdev(ioat_chan); |
@@ -660,6 +720,8 @@ static void ioat_eh(struct ioatdma_chan *ioat_chan) | |||
660 | u32 err_handled = 0; | 720 | u32 err_handled = 0; |
661 | u32 chanerr_int; | 721 | u32 chanerr_int; |
662 | u32 chanerr; | 722 | u32 chanerr; |
723 | bool abort = false; | ||
724 | struct dmaengine_result res; | ||
663 | 725 | ||
664 | /* cleanup so tail points to descriptor that caused the error */ | 726 | /* cleanup so tail points to descriptor that caused the error */ |
665 | if (ioat_cleanup_preamble(ioat_chan, &phys_complete)) | 727 | if (ioat_cleanup_preamble(ioat_chan, &phys_complete)) |
@@ -695,28 +757,50 @@ static void ioat_eh(struct ioatdma_chan *ioat_chan) | |||
695 | break; | 757 | break; |
696 | } | 758 | } |
697 | 759 | ||
760 | if (chanerr & IOAT_CHANERR_RECOVER_MASK) { | ||
761 | if (chanerr & IOAT_CHANERR_READ_DATA_ERR) { | ||
762 | res.result = DMA_TRANS_READ_FAILED; | ||
763 | err_handled |= IOAT_CHANERR_READ_DATA_ERR; | ||
764 | } else if (chanerr & IOAT_CHANERR_WRITE_DATA_ERR) { | ||
765 | res.result = DMA_TRANS_WRITE_FAILED; | ||
766 | err_handled |= IOAT_CHANERR_WRITE_DATA_ERR; | ||
767 | } | ||
768 | |||
769 | abort = true; | ||
770 | } else | ||
771 | res.result = DMA_TRANS_NOERROR; | ||
772 | |||
698 | /* fault on unhandled error or spurious halt */ | 773 | /* fault on unhandled error or spurious halt */ |
699 | if (chanerr ^ err_handled || chanerr == 0) { | 774 | if (chanerr ^ err_handled || chanerr == 0) { |
700 | dev_err(to_dev(ioat_chan), "%s: fatal error (%x:%x)\n", | 775 | dev_err(to_dev(ioat_chan), "%s: fatal error (%x:%x)\n", |
701 | __func__, chanerr, err_handled); | 776 | __func__, chanerr, err_handled); |
702 | BUG(); | 777 | BUG(); |
703 | } else { /* cleanup the faulty descriptor */ | ||
704 | tx = &desc->txd; | ||
705 | if (tx->cookie) { | ||
706 | dma_cookie_complete(tx); | ||
707 | dma_descriptor_unmap(tx); | ||
708 | dmaengine_desc_get_callback_invoke(tx, NULL); | ||
709 | tx->callback = NULL; | ||
710 | } | ||
711 | } | 778 | } |
712 | 779 | ||
713 | writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); | 780 | /* cleanup the faulty descriptor since we are continuing */ |
714 | pci_write_config_dword(pdev, IOAT_PCI_CHANERR_INT_OFFSET, chanerr_int); | 781 | tx = &desc->txd; |
782 | if (tx->cookie) { | ||
783 | dma_cookie_complete(tx); | ||
784 | dma_descriptor_unmap(tx); | ||
785 | dmaengine_desc_get_callback_invoke(tx, &res); | ||
786 | tx->callback = NULL; | ||
787 | tx->callback_result = NULL; | ||
788 | } | ||
715 | 789 | ||
716 | /* mark faulting descriptor as complete */ | 790 | /* mark faulting descriptor as complete */ |
717 | *ioat_chan->completion = desc->txd.phys; | 791 | *ioat_chan->completion = desc->txd.phys; |
718 | 792 | ||
719 | spin_lock_bh(&ioat_chan->prep_lock); | 793 | spin_lock_bh(&ioat_chan->prep_lock); |
794 | /* we need abort all descriptors */ | ||
795 | if (abort) { | ||
796 | ioat_abort_descs(ioat_chan); | ||
797 | /* clean up the channel, we could be in weird state */ | ||
798 | ioat_reset_hw(ioat_chan); | ||
799 | } | ||
800 | |||
801 | writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); | ||
802 | pci_write_config_dword(pdev, IOAT_PCI_CHANERR_INT_OFFSET, chanerr_int); | ||
803 | |||
720 | ioat_restart_channel(ioat_chan); | 804 | ioat_restart_channel(ioat_chan); |
721 | spin_unlock_bh(&ioat_chan->prep_lock); | 805 | spin_unlock_bh(&ioat_chan->prep_lock); |
722 | } | 806 | } |
@@ -749,10 +833,25 @@ void ioat_timer_event(unsigned long data) | |||
749 | chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); | 833 | chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); |
750 | dev_err(to_dev(ioat_chan), "%s: Channel halted (%x)\n", | 834 | dev_err(to_dev(ioat_chan), "%s: Channel halted (%x)\n", |
751 | __func__, chanerr); | 835 | __func__, chanerr); |
752 | if (test_bit(IOAT_RUN, &ioat_chan->state)) | 836 | if (test_bit(IOAT_RUN, &ioat_chan->state)) { |
753 | BUG_ON(is_ioat_bug(chanerr)); | 837 | spin_lock_bh(&ioat_chan->cleanup_lock); |
754 | else /* we never got off the ground */ | 838 | spin_lock_bh(&ioat_chan->prep_lock); |
755 | return; | 839 | set_bit(IOAT_CHAN_DOWN, &ioat_chan->state); |
840 | spin_unlock_bh(&ioat_chan->prep_lock); | ||
841 | |||
842 | ioat_abort_descs(ioat_chan); | ||
843 | dev_warn(to_dev(ioat_chan), "Reset channel...\n"); | ||
844 | ioat_reset_hw(ioat_chan); | ||
845 | dev_warn(to_dev(ioat_chan), "Restart channel...\n"); | ||
846 | ioat_restart_channel(ioat_chan); | ||
847 | |||
848 | spin_lock_bh(&ioat_chan->prep_lock); | ||
849 | clear_bit(IOAT_CHAN_DOWN, &ioat_chan->state); | ||
850 | spin_unlock_bh(&ioat_chan->prep_lock); | ||
851 | spin_unlock_bh(&ioat_chan->cleanup_lock); | ||
852 | } | ||
853 | |||
854 | return; | ||
756 | } | 855 | } |
757 | 856 | ||
758 | spin_lock_bh(&ioat_chan->cleanup_lock); | 857 | spin_lock_bh(&ioat_chan->cleanup_lock); |
@@ -776,14 +875,23 @@ void ioat_timer_event(unsigned long data) | |||
776 | u32 chanerr; | 875 | u32 chanerr; |
777 | 876 | ||
778 | chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); | 877 | chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); |
779 | dev_warn(to_dev(ioat_chan), "Restarting channel...\n"); | ||
780 | dev_warn(to_dev(ioat_chan), "CHANSTS: %#Lx CHANERR: %#x\n", | 878 | dev_warn(to_dev(ioat_chan), "CHANSTS: %#Lx CHANERR: %#x\n", |
781 | status, chanerr); | 879 | status, chanerr); |
782 | dev_warn(to_dev(ioat_chan), "Active descriptors: %d\n", | 880 | dev_warn(to_dev(ioat_chan), "Active descriptors: %d\n", |
783 | ioat_ring_active(ioat_chan)); | 881 | ioat_ring_active(ioat_chan)); |
784 | 882 | ||
785 | spin_lock_bh(&ioat_chan->prep_lock); | 883 | spin_lock_bh(&ioat_chan->prep_lock); |
884 | set_bit(IOAT_CHAN_DOWN, &ioat_chan->state); | ||
885 | spin_unlock_bh(&ioat_chan->prep_lock); | ||
886 | |||
887 | ioat_abort_descs(ioat_chan); | ||
888 | dev_warn(to_dev(ioat_chan), "Resetting channel...\n"); | ||
889 | ioat_reset_hw(ioat_chan); | ||
890 | dev_warn(to_dev(ioat_chan), "Restarting channel...\n"); | ||
786 | ioat_restart_channel(ioat_chan); | 891 | ioat_restart_channel(ioat_chan); |
892 | |||
893 | spin_lock_bh(&ioat_chan->prep_lock); | ||
894 | clear_bit(IOAT_CHAN_DOWN, &ioat_chan->state); | ||
787 | spin_unlock_bh(&ioat_chan->prep_lock); | 895 | spin_unlock_bh(&ioat_chan->prep_lock); |
788 | spin_unlock_bh(&ioat_chan->cleanup_lock); | 896 | spin_unlock_bh(&ioat_chan->cleanup_lock); |
789 | return; | 897 | return; |
diff --git a/drivers/dma/ioat/registers.h b/drivers/dma/ioat/registers.h index 70534981a49b..48fa4cf9f64a 100644 --- a/drivers/dma/ioat/registers.h +++ b/drivers/dma/ioat/registers.h | |||
@@ -240,6 +240,8 @@ | |||
240 | #define IOAT_CHANERR_DESCRIPTOR_COUNT_ERR 0x40000 | 240 | #define IOAT_CHANERR_DESCRIPTOR_COUNT_ERR 0x40000 |
241 | 241 | ||
242 | #define IOAT_CHANERR_HANDLE_MASK (IOAT_CHANERR_XOR_P_OR_CRC_ERR | IOAT_CHANERR_XOR_Q_ERR) | 242 | #define IOAT_CHANERR_HANDLE_MASK (IOAT_CHANERR_XOR_P_OR_CRC_ERR | IOAT_CHANERR_XOR_Q_ERR) |
243 | #define IOAT_CHANERR_RECOVER_MASK (IOAT_CHANERR_READ_DATA_ERR | \ | ||
244 | IOAT_CHANERR_WRITE_DATA_ERR) | ||
243 | 245 | ||
244 | #define IOAT_CHANERR_MASK_OFFSET 0x2C /* 32-bit Channel Error Register */ | 246 | #define IOAT_CHANERR_MASK_OFFSET 0x2C /* 32-bit Channel Error Register */ |
245 | 247 | ||