diff options
author | Alexey Korolev <akorolev@infradead.org> | 2007-10-22 12:55:20 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-10-23 07:07:52 -0400 |
commit | 5a37cf19efcceae14c2078449e35b9f4eb3e63e4 (patch) | |
tree | 4d4bb74499d093aa9a921e72875d223f15506657 | |
parent | 2a754b51aacb122cec25c849e3cf7f5503cc3ec6 (diff) |
[MTD] [NOR] Fix deadlock in Intel chip driver caused by get_chip recursion
This patch solves kernel deadlock issue seen on JFFF2 simultaneous
operations. Detailed investigation of the issue showed that the kernel
deadlock is caused by tons of recursive get_chip calls.
Signed-off-by: Alexey Korolev <akorolev@infradead.org>
Acked-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0001.c | 146 |
1 files changed, 77 insertions, 69 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 3aa3dca56ae6..a9eb1c516247 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c | |||
@@ -85,6 +85,7 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, | |||
85 | static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, | 85 | static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, |
86 | size_t len); | 86 | size_t len); |
87 | 87 | ||
88 | static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode); | ||
88 | static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); | 89 | static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); |
89 | static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); | 90 | static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); |
90 | #include "fwh_lock.h" | 91 | #include "fwh_lock.h" |
@@ -641,73 +642,13 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, | |||
641 | /* | 642 | /* |
642 | * *********** CHIP ACCESS FUNCTIONS *********** | 643 | * *********** CHIP ACCESS FUNCTIONS *********** |
643 | */ | 644 | */ |
644 | 645 | static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode) | |
645 | static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) | ||
646 | { | 646 | { |
647 | DECLARE_WAITQUEUE(wait, current); | 647 | DECLARE_WAITQUEUE(wait, current); |
648 | struct cfi_private *cfi = map->fldrv_priv; | 648 | struct cfi_private *cfi = map->fldrv_priv; |
649 | map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01); | 649 | map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01); |
650 | unsigned long timeo; | ||
651 | struct cfi_pri_intelext *cfip = cfi->cmdset_priv; | 650 | struct cfi_pri_intelext *cfip = cfi->cmdset_priv; |
652 | 651 | unsigned long timeo = jiffies + HZ; | |
653 | resettime: | ||
654 | timeo = jiffies + HZ; | ||
655 | retry: | ||
656 | if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE || mode == FL_SHUTDOWN)) { | ||
657 | /* | ||
658 | * OK. We have possibility for contension on the write/erase | ||
659 | * operations which are global to the real chip and not per | ||
660 | * partition. So let's fight it over in the partition which | ||
661 | * currently has authority on the operation. | ||
662 | * | ||
663 | * The rules are as follows: | ||
664 | * | ||
665 | * - any write operation must own shared->writing. | ||
666 | * | ||
667 | * - any erase operation must own _both_ shared->writing and | ||
668 | * shared->erasing. | ||
669 | * | ||
670 | * - contension arbitration is handled in the owner's context. | ||
671 | * | ||
672 | * The 'shared' struct can be read and/or written only when | ||
673 | * its lock is taken. | ||
674 | */ | ||
675 | struct flchip_shared *shared = chip->priv; | ||
676 | struct flchip *contender; | ||
677 | spin_lock(&shared->lock); | ||
678 | contender = shared->writing; | ||
679 | if (contender && contender != chip) { | ||
680 | /* | ||
681 | * The engine to perform desired operation on this | ||
682 | * partition is already in use by someone else. | ||
683 | * Let's fight over it in the context of the chip | ||
684 | * currently using it. If it is possible to suspend, | ||
685 | * that other partition will do just that, otherwise | ||
686 | * it'll happily send us to sleep. In any case, when | ||
687 | * get_chip returns success we're clear to go ahead. | ||
688 | */ | ||
689 | int ret = spin_trylock(contender->mutex); | ||
690 | spin_unlock(&shared->lock); | ||
691 | if (!ret) | ||
692 | goto retry; | ||
693 | spin_unlock(chip->mutex); | ||
694 | ret = get_chip(map, contender, contender->start, mode); | ||
695 | spin_lock(chip->mutex); | ||
696 | if (ret) { | ||
697 | spin_unlock(contender->mutex); | ||
698 | return ret; | ||
699 | } | ||
700 | timeo = jiffies + HZ; | ||
701 | spin_lock(&shared->lock); | ||
702 | spin_unlock(contender->mutex); | ||
703 | } | ||
704 | |||
705 | /* We now own it */ | ||
706 | shared->writing = chip; | ||
707 | if (mode == FL_ERASING) | ||
708 | shared->erasing = chip; | ||
709 | spin_unlock(&shared->lock); | ||
710 | } | ||
711 | 652 | ||
712 | switch (chip->state) { | 653 | switch (chip->state) { |
713 | 654 | ||
@@ -722,16 +663,11 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr | |||
722 | if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) | 663 | if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) |
723 | break; | 664 | break; |
724 | 665 | ||
725 | if (time_after(jiffies, timeo)) { | ||
726 | printk(KERN_ERR "%s: Waiting for chip to be ready timed out. Status %lx\n", | ||
727 | map->name, status.x[0]); | ||
728 | return -EIO; | ||
729 | } | ||
730 | spin_unlock(chip->mutex); | 666 | spin_unlock(chip->mutex); |
731 | cfi_udelay(1); | 667 | cfi_udelay(1); |
732 | spin_lock(chip->mutex); | 668 | spin_lock(chip->mutex); |
733 | /* Someone else might have been playing with it. */ | 669 | /* Someone else might have been playing with it. */ |
734 | goto retry; | 670 | return -EAGAIN; |
735 | } | 671 | } |
736 | 672 | ||
737 | case FL_READY: | 673 | case FL_READY: |
@@ -809,10 +745,82 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr | |||
809 | schedule(); | 745 | schedule(); |
810 | remove_wait_queue(&chip->wq, &wait); | 746 | remove_wait_queue(&chip->wq, &wait); |
811 | spin_lock(chip->mutex); | 747 | spin_lock(chip->mutex); |
812 | goto resettime; | 748 | return -EAGAIN; |
813 | } | 749 | } |
814 | } | 750 | } |
815 | 751 | ||
752 | static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) | ||
753 | { | ||
754 | int ret; | ||
755 | |||
756 | retry: | ||
757 | if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING | ||
758 | || mode == FL_OTP_WRITE || mode == FL_SHUTDOWN)) { | ||
759 | /* | ||
760 | * OK. We have possibility for contention on the write/erase | ||
761 | * operations which are global to the real chip and not per | ||
762 | * partition. So let's fight it over in the partition which | ||
763 | * currently has authority on the operation. | ||
764 | * | ||
765 | * The rules are as follows: | ||
766 | * | ||
767 | * - any write operation must own shared->writing. | ||
768 | * | ||
769 | * - any erase operation must own _both_ shared->writing and | ||
770 | * shared->erasing. | ||
771 | * | ||
772 | * - contention arbitration is handled in the owner's context. | ||
773 | * | ||
774 | * The 'shared' struct can be read and/or written only when | ||
775 | * its lock is taken. | ||
776 | */ | ||
777 | struct flchip_shared *shared = chip->priv; | ||
778 | struct flchip *contender; | ||
779 | spin_lock(&shared->lock); | ||
780 | contender = shared->writing; | ||
781 | if (contender && contender != chip) { | ||
782 | /* | ||
783 | * The engine to perform desired operation on this | ||
784 | * partition is already in use by someone else. | ||
785 | * Let's fight over it in the context of the chip | ||
786 | * currently using it. If it is possible to suspend, | ||
787 | * that other partition will do just that, otherwise | ||
788 | * it'll happily send us to sleep. In any case, when | ||
789 | * get_chip returns success we're clear to go ahead. | ||
790 | */ | ||
791 | ret = spin_trylock(contender->mutex); | ||
792 | spin_unlock(&shared->lock); | ||
793 | if (!ret) | ||
794 | goto retry; | ||
795 | spin_unlock(chip->mutex); | ||
796 | ret = chip_ready(map, contender, contender->start, mode); | ||
797 | spin_lock(chip->mutex); | ||
798 | |||
799 | if (ret == -EAGAIN) { | ||
800 | spin_unlock(contender->mutex); | ||
801 | goto retry; | ||
802 | } | ||
803 | if (ret) { | ||
804 | spin_unlock(contender->mutex); | ||
805 | return ret; | ||
806 | } | ||
807 | spin_lock(&shared->lock); | ||
808 | spin_unlock(contender->mutex); | ||
809 | } | ||
810 | |||
811 | /* We now own it */ | ||
812 | shared->writing = chip; | ||
813 | if (mode == FL_ERASING) | ||
814 | shared->erasing = chip; | ||
815 | spin_unlock(&shared->lock); | ||
816 | } | ||
817 | ret = chip_ready(map, chip, adr, mode); | ||
818 | if (ret == -EAGAIN) | ||
819 | goto retry; | ||
820 | |||
821 | return ret; | ||
822 | } | ||
823 | |||
816 | static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) | 824 | static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) |
817 | { | 825 | { |
818 | struct cfi_private *cfi = map->fldrv_priv; | 826 | struct cfi_private *cfi = map->fldrv_priv; |