diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-01-17 12:13:31 -0500 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-02-17 11:48:25 -0500 |
commit | f971dbd5da4e2fbf756d07b938a9c65a9c75178b (patch) | |
tree | d21b138ac2ea3ada0e27910425cfd988f72dbede /drivers/pcmcia/cs.c | |
parent | cfe5d809518eda3d5e2da87c5ccbe8647143573a (diff) |
pcmcia: use pccardd to handle eject, insert, suspend and resume requests
This avoids any sysfs-related deadlock (or lockdep warning), such
as reported at http://lkml.org/lkml/2010/1/17/88 .
Reported-by: Ming Lei <tom.leiming@gmail.com>
Tested-by: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'drivers/pcmcia/cs.c')
-rw-r--r-- | drivers/pcmcia/cs.c | 177 |
1 files changed, 58 insertions, 119 deletions
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 7ba45b0cca6b..823ecda32216 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c | |||
@@ -505,7 +505,10 @@ static int socket_insert(struct pcmcia_socket *skt) | |||
505 | dev_dbg(&skt->dev, "insert\n"); | 505 | dev_dbg(&skt->dev, "insert\n"); |
506 | 506 | ||
507 | mutex_lock(&skt->ops_mutex); | 507 | mutex_lock(&skt->ops_mutex); |
508 | WARN_ON(skt->state & SOCKET_INUSE); | 508 | if (skt->state & SOCKET_INUSE) { |
509 | mutex_unlock(&skt->ops_mutex); | ||
510 | return -EINVAL; | ||
511 | } | ||
509 | skt->state |= SOCKET_INUSE; | 512 | skt->state |= SOCKET_INUSE; |
510 | 513 | ||
511 | ret = socket_setup(skt, setup_delay); | 514 | ret = socket_setup(skt, setup_delay); |
@@ -682,16 +685,19 @@ static int pccardd(void *__skt) | |||
682 | for (;;) { | 685 | for (;;) { |
683 | unsigned long flags; | 686 | unsigned long flags; |
684 | unsigned int events; | 687 | unsigned int events; |
688 | unsigned int sysfs_events; | ||
685 | 689 | ||
686 | set_current_state(TASK_INTERRUPTIBLE); | 690 | set_current_state(TASK_INTERRUPTIBLE); |
687 | 691 | ||
688 | spin_lock_irqsave(&skt->thread_lock, flags); | 692 | spin_lock_irqsave(&skt->thread_lock, flags); |
689 | events = skt->thread_events; | 693 | events = skt->thread_events; |
690 | skt->thread_events = 0; | 694 | skt->thread_events = 0; |
695 | sysfs_events = skt->sysfs_events; | ||
696 | skt->sysfs_events = 0; | ||
691 | spin_unlock_irqrestore(&skt->thread_lock, flags); | 697 | spin_unlock_irqrestore(&skt->thread_lock, flags); |
692 | 698 | ||
699 | mutex_lock(&skt->skt_mutex); | ||
693 | if (events) { | 700 | if (events) { |
694 | mutex_lock(&skt->skt_mutex); | ||
695 | if (events & SS_DETECT) | 701 | if (events & SS_DETECT) |
696 | socket_detect_change(skt); | 702 | socket_detect_change(skt); |
697 | if (events & SS_BATDEAD) | 703 | if (events & SS_BATDEAD) |
@@ -700,10 +706,34 @@ static int pccardd(void *__skt) | |||
700 | send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); | 706 | send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); |
701 | if (events & SS_READY) | 707 | if (events & SS_READY) |
702 | send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); | 708 | send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); |
703 | mutex_unlock(&skt->skt_mutex); | ||
704 | continue; | ||
705 | } | 709 | } |
706 | 710 | ||
711 | if (sysfs_events) { | ||
712 | if (sysfs_events & PCMCIA_UEVENT_EJECT) | ||
713 | socket_remove(skt); | ||
714 | if (sysfs_events & PCMCIA_UEVENT_INSERT) | ||
715 | socket_insert(skt); | ||
716 | if ((sysfs_events & PCMCIA_UEVENT_RESUME) && | ||
717 | !(skt->state & SOCKET_CARDBUS)) { | ||
718 | ret = socket_resume(skt); | ||
719 | if (!ret && skt->callback) | ||
720 | skt->callback->resume(skt); | ||
721 | } | ||
722 | if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) && | ||
723 | !(skt->state & SOCKET_CARDBUS)) { | ||
724 | if (skt->callback) | ||
725 | ret = skt->callback->suspend(skt); | ||
726 | else | ||
727 | ret = 0; | ||
728 | if (!ret) | ||
729 | socket_suspend(skt); | ||
730 | } | ||
731 | } | ||
732 | mutex_unlock(&skt->skt_mutex); | ||
733 | |||
734 | if (events || sysfs_events) | ||
735 | continue; | ||
736 | |||
707 | if (kthread_should_stop()) | 737 | if (kthread_should_stop()) |
708 | break; | 738 | break; |
709 | 739 | ||
@@ -745,6 +775,30 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) | |||
745 | } /* pcmcia_parse_events */ | 775 | } /* pcmcia_parse_events */ |
746 | EXPORT_SYMBOL(pcmcia_parse_events); | 776 | EXPORT_SYMBOL(pcmcia_parse_events); |
747 | 777 | ||
778 | /** | ||
779 | * pcmcia_parse_uevents() - tell pccardd to issue manual commands | ||
780 | * @s: the PCMCIA socket we wan't to command | ||
781 | * @events: events to pass to pccardd | ||
782 | * | ||
783 | * userspace-issued insert, eject, suspend and resume commands must be | ||
784 | * handled by pccardd to avoid any sysfs-related deadlocks. Valid events | ||
785 | * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), | ||
786 | * PCMCIA_UEVENT_RESUME (for resume) and PCMCIA_UEVENT_SUSPEND (for suspend). | ||
787 | */ | ||
788 | void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) | ||
789 | { | ||
790 | unsigned long flags; | ||
791 | dev_dbg(&s->dev, "parse_uevents: events %08x\n", events); | ||
792 | if (s->thread) { | ||
793 | spin_lock_irqsave(&s->thread_lock, flags); | ||
794 | s->sysfs_events |= events; | ||
795 | spin_unlock_irqrestore(&s->thread_lock, flags); | ||
796 | |||
797 | wake_up_process(s->thread); | ||
798 | } | ||
799 | } | ||
800 | EXPORT_SYMBOL(pcmcia_parse_uevents); | ||
801 | |||
748 | 802 | ||
749 | /* register pcmcia_callback */ | 803 | /* register pcmcia_callback */ |
750 | int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) | 804 | int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) |
@@ -828,121 +882,6 @@ int pcmcia_reset_card(struct pcmcia_socket *skt) | |||
828 | EXPORT_SYMBOL(pcmcia_reset_card); | 882 | EXPORT_SYMBOL(pcmcia_reset_card); |
829 | 883 | ||
830 | 884 | ||
831 | /* These shut down or wake up a socket. They are sort of user | ||
832 | * initiated versions of the APM suspend and resume actions. | ||
833 | */ | ||
834 | int pcmcia_suspend_card(struct pcmcia_socket *skt) | ||
835 | { | ||
836 | int ret; | ||
837 | |||
838 | dev_dbg(&skt->dev, "suspending socket\n"); | ||
839 | |||
840 | mutex_lock(&skt->skt_mutex); | ||
841 | do { | ||
842 | if (!(skt->state & SOCKET_PRESENT)) { | ||
843 | ret = -ENODEV; | ||
844 | break; | ||
845 | } | ||
846 | if (skt->state & SOCKET_CARDBUS) { | ||
847 | ret = -EPERM; | ||
848 | break; | ||
849 | } | ||
850 | if (skt->callback) { | ||
851 | ret = skt->callback->suspend(skt); | ||
852 | if (ret) | ||
853 | break; | ||
854 | } | ||
855 | ret = socket_suspend(skt); | ||
856 | } while (0); | ||
857 | mutex_unlock(&skt->skt_mutex); | ||
858 | |||
859 | return ret; | ||
860 | } /* suspend_card */ | ||
861 | EXPORT_SYMBOL(pcmcia_suspend_card); | ||
862 | |||
863 | |||
864 | int pcmcia_resume_card(struct pcmcia_socket *skt) | ||
865 | { | ||
866 | int ret; | ||
867 | |||
868 | dev_dbg(&skt->dev, "waking up socket\n"); | ||
869 | |||
870 | mutex_lock(&skt->skt_mutex); | ||
871 | do { | ||
872 | if (!(skt->state & SOCKET_PRESENT)) { | ||
873 | ret = -ENODEV; | ||
874 | break; | ||
875 | } | ||
876 | if (skt->state & SOCKET_CARDBUS) { | ||
877 | ret = -EPERM; | ||
878 | break; | ||
879 | } | ||
880 | ret = socket_resume(skt); | ||
881 | if (!ret && skt->callback) | ||
882 | skt->callback->resume(skt); | ||
883 | } while (0); | ||
884 | mutex_unlock(&skt->skt_mutex); | ||
885 | |||
886 | return ret; | ||
887 | } /* resume_card */ | ||
888 | EXPORT_SYMBOL(pcmcia_resume_card); | ||
889 | |||
890 | |||
891 | /* These handle user requests to eject or insert a card. */ | ||
892 | int pcmcia_eject_card(struct pcmcia_socket *skt) | ||
893 | { | ||
894 | int ret; | ||
895 | |||
896 | dev_dbg(&skt->dev, "user eject request\n"); | ||
897 | |||
898 | mutex_lock(&skt->skt_mutex); | ||
899 | do { | ||
900 | if (!(skt->state & SOCKET_PRESENT)) { | ||
901 | ret = -ENODEV; | ||
902 | break; | ||
903 | } | ||
904 | |||
905 | ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW); | ||
906 | if (ret != 0) { | ||
907 | ret = -EINVAL; | ||
908 | break; | ||
909 | } | ||
910 | |||
911 | socket_remove(skt); | ||
912 | ret = 0; | ||
913 | } while (0); | ||
914 | mutex_unlock(&skt->skt_mutex); | ||
915 | |||
916 | return ret; | ||
917 | } /* eject_card */ | ||
918 | EXPORT_SYMBOL(pcmcia_eject_card); | ||
919 | |||
920 | |||
921 | int pcmcia_insert_card(struct pcmcia_socket *skt) | ||
922 | { | ||
923 | int ret; | ||
924 | |||
925 | dev_dbg(&skt->dev, "user insert request\n"); | ||
926 | |||
927 | mutex_lock(&skt->skt_mutex); | ||
928 | do { | ||
929 | if (skt->state & SOCKET_PRESENT) { | ||
930 | ret = -EBUSY; | ||
931 | break; | ||
932 | } | ||
933 | if (socket_insert(skt) == -ENODEV) { | ||
934 | ret = -ENODEV; | ||
935 | break; | ||
936 | } | ||
937 | ret = 0; | ||
938 | } while (0); | ||
939 | mutex_unlock(&skt->skt_mutex); | ||
940 | |||
941 | return ret; | ||
942 | } /* insert_card */ | ||
943 | EXPORT_SYMBOL(pcmcia_insert_card); | ||
944 | |||
945 | |||
946 | static int pcmcia_socket_uevent(struct device *dev, | 885 | static int pcmcia_socket_uevent(struct device *dev, |
947 | struct kobj_uevent_env *env) | 886 | struct kobj_uevent_env *env) |
948 | { | 887 | { |