aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pcmcia
diff options
context:
space:
mode:
authorDominik Brodowski <linux@dominikbrodowski.net>2010-01-17 12:13:31 -0500
committerDominik Brodowski <linux@dominikbrodowski.net>2010-02-17 11:48:25 -0500
commitf971dbd5da4e2fbf756d07b938a9c65a9c75178b (patch)
treed21b138ac2ea3ada0e27910425cfd988f72dbede /drivers/pcmcia
parentcfe5d809518eda3d5e2da87c5ccbe8647143573a (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')
-rw-r--r--drivers/pcmcia/cs.c177
-rw-r--r--drivers/pcmcia/cs_internal.h10
-rw-r--r--drivers/pcmcia/pcmcia_ioctl.c8
-rw-r--r--drivers/pcmcia/socket_sysfs.c26
4 files changed, 81 insertions, 140 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 */
746EXPORT_SYMBOL(pcmcia_parse_events); 776EXPORT_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 */
788void 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}
800EXPORT_SYMBOL(pcmcia_parse_uevents);
801
748 802
749/* register pcmcia_callback */ 803/* register pcmcia_callback */
750int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) 804int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
@@ -828,121 +882,6 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
828EXPORT_SYMBOL(pcmcia_reset_card); 882EXPORT_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 */
834int 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 */
861EXPORT_SYMBOL(pcmcia_suspend_card);
862
863
864int 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 */
888EXPORT_SYMBOL(pcmcia_resume_card);
889
890
891/* These handle user requests to eject or insert a card. */
892int 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 */
918EXPORT_SYMBOL(pcmcia_eject_card);
919
920
921int 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 */
943EXPORT_SYMBOL(pcmcia_insert_card);
944
945
946static int pcmcia_socket_uevent(struct device *dev, 885static int pcmcia_socket_uevent(struct device *dev,
947 struct kobj_uevent_env *env) 886 struct kobj_uevent_env *env)
948{ 887{
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index bd386d77845e..127c97acf849 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -124,11 +124,11 @@ extern struct class pcmcia_socket_class;
124int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c); 124int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
125struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr); 125struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr);
126 126
127int pcmcia_suspend_card(struct pcmcia_socket *skt); 127void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events);
128int pcmcia_resume_card(struct pcmcia_socket *skt); 128#define PCMCIA_UEVENT_EJECT 0x0001
129 129#define PCMCIA_UEVENT_INSERT 0x0002
130int pcmcia_eject_card(struct pcmcia_socket *skt); 130#define PCMCIA_UEVENT_SUSPEND 0x0004
131int pcmcia_insert_card(struct pcmcia_socket *skt); 131#define PCMCIA_UEVENT_RESUME 0x0008
132 132
133struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); 133struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt);
134void pcmcia_put_socket(struct pcmcia_socket *skt); 134void pcmcia_put_socket(struct pcmcia_socket *skt);
diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c
index 96fd236f52a9..13a7132cf688 100644
--- a/drivers/pcmcia/pcmcia_ioctl.c
+++ b/drivers/pcmcia/pcmcia_ioctl.c
@@ -925,16 +925,16 @@ static int ds_ioctl(struct inode *inode, struct file *file,
925 ret = pccard_validate_cis(s, &buf->cisinfo.Chains); 925 ret = pccard_validate_cis(s, &buf->cisinfo.Chains);
926 break; 926 break;
927 case DS_SUSPEND_CARD: 927 case DS_SUSPEND_CARD:
928 ret = pcmcia_suspend_card(s); 928 pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND);
929 break; 929 break;
930 case DS_RESUME_CARD: 930 case DS_RESUME_CARD:
931 ret = pcmcia_resume_card(s); 931 pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME);
932 break; 932 break;
933 case DS_EJECT_CARD: 933 case DS_EJECT_CARD:
934 err = pcmcia_eject_card(s); 934 pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT);
935 break; 935 break;
936 case DS_INSERT_CARD: 936 case DS_INSERT_CARD:
937 err = pcmcia_insert_card(s); 937 pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT);
938 break; 938 break;
939 case DS_ACCESS_CONFIGURATION_REGISTER: 939 case DS_ACCESS_CONFIGURATION_REGISTER:
940 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { 940 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
index e8826df00a36..fba0e30183f4 100644
--- a/drivers/pcmcia/socket_sysfs.c
+++ b/drivers/pcmcia/socket_sysfs.c
@@ -88,15 +88,14 @@ static DEVICE_ATTR(card_vcc, 0444, pccard_show_vcc, NULL);
88static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr, 88static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr,
89 const char *buf, size_t count) 89 const char *buf, size_t count)
90{ 90{
91 ssize_t ret;
92 struct pcmcia_socket *s = to_socket(dev); 91 struct pcmcia_socket *s = to_socket(dev);
93 92
94 if (!count) 93 if (!count)
95 return -EINVAL; 94 return -EINVAL;
96 95
97 ret = pcmcia_insert_card(s); 96 pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT);
98 97
99 return ret ? ret : count; 98 return count;
100} 99}
101static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert); 100static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert);
102 101
@@ -113,18 +112,22 @@ static ssize_t pccard_store_card_pm_state(struct device *dev,
113 struct device_attribute *attr, 112 struct device_attribute *attr,
114 const char *buf, size_t count) 113 const char *buf, size_t count)
115{ 114{
116 ssize_t ret = -EINVAL;
117 struct pcmcia_socket *s = to_socket(dev); 115 struct pcmcia_socket *s = to_socket(dev);
116 ssize_t ret = count;
118 117
119 if (!count) 118 if (!count)
120 return -EINVAL; 119 return -EINVAL;
121 120
122 if (!(s->state & SOCKET_SUSPEND) && !strncmp(buf, "off", 3)) 121 if (!strncmp(buf, "off", 3))
123 ret = pcmcia_suspend_card(s); 122 pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND);
124 else if ((s->state & SOCKET_SUSPEND) && !strncmp(buf, "on", 2)) 123 else {
125 ret = pcmcia_resume_card(s); 124 if (!strncmp(buf, "on", 2))
125 pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME);
126 else
127 ret = -EINVAL;
128 }
126 129
127 return ret ? -ENODEV : count; 130 return ret;
128} 131}
129static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state); 132static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state);
130 133
@@ -132,15 +135,14 @@ static ssize_t pccard_store_eject(struct device *dev,
132 struct device_attribute *attr, 135 struct device_attribute *attr,
133 const char *buf, size_t count) 136 const char *buf, size_t count)
134{ 137{
135 ssize_t ret;
136 struct pcmcia_socket *s = to_socket(dev); 138 struct pcmcia_socket *s = to_socket(dev);
137 139
138 if (!count) 140 if (!count)
139 return -EINVAL; 141 return -EINVAL;
140 142
141 ret = pcmcia_eject_card(s); 143 pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT);
142 144
143 return ret ? ret : count; 145 return count;
144} 146}
145static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject); 147static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject);
146 148