aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/vt
diff options
context:
space:
mode:
authorAlan Cox <alan@linux.intel.com>2012-07-17 12:06:41 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-17 12:13:37 -0400
commit3db1ddb725dcd9a2bb32be2b64d0688c3e1c4579 (patch)
tree4cd65e810725baf91b1dd27c91288388eb28c798 /drivers/tty/vt
parentce7240e445303de3ca66e6d08f17a2ec278a5bf6 (diff)
vt: fix the keyboard/led locking
We touch the LED from both keyboard callback and direct paths. In one case we've got the lock held way up the call chain and in the other we haven't. This leads to complete insanity so fix it by giving the LED bits their own lock. Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/vt')
-rw-r--r--drivers/tty/vt/keyboard.c41
1 files changed, 23 insertions, 18 deletions
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 9b4f60a6ab0e..681765baef69 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -119,6 +119,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
119 119
120static struct input_handler kbd_handler; 120static struct input_handler kbd_handler;
121static DEFINE_SPINLOCK(kbd_event_lock); 121static DEFINE_SPINLOCK(kbd_event_lock);
122static DEFINE_SPINLOCK(led_lock);
122static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ 123static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
123static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ 124static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
124static bool dead_key_next; 125static bool dead_key_next;
@@ -984,7 +985,7 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
984 * or (ii) whatever pattern of lights people want to show using KDSETLED, 985 * or (ii) whatever pattern of lights people want to show using KDSETLED,
985 * or (iii) specified bits of specified words in kernel memory. 986 * or (iii) specified bits of specified words in kernel memory.
986 */ 987 */
987unsigned char getledstate(void) 988static unsigned char getledstate(void)
988{ 989{
989 return ledstate; 990 return ledstate;
990} 991}
@@ -992,7 +993,7 @@ unsigned char getledstate(void)
992void setledstate(struct kbd_struct *kbd, unsigned int led) 993void setledstate(struct kbd_struct *kbd, unsigned int led)
993{ 994{
994 unsigned long flags; 995 unsigned long flags;
995 spin_lock_irqsave(&kbd_event_lock, flags); 996 spin_lock_irqsave(&led_lock, flags);
996 if (!(led & ~7)) { 997 if (!(led & ~7)) {
997 ledioctl = led; 998 ledioctl = led;
998 kbd->ledmode = LED_SHOW_IOCTL; 999 kbd->ledmode = LED_SHOW_IOCTL;
@@ -1000,7 +1001,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led)
1000 kbd->ledmode = LED_SHOW_FLAGS; 1001 kbd->ledmode = LED_SHOW_FLAGS;
1001 1002
1002 set_leds(); 1003 set_leds();
1003 spin_unlock_irqrestore(&kbd_event_lock, flags); 1004 spin_unlock_irqrestore(&led_lock, flags);
1004} 1005}
1005 1006
1006static inline unsigned char getleds(void) 1007static inline unsigned char getleds(void)
@@ -1051,8 +1052,11 @@ int vt_get_leds(int console, int flag)
1051{ 1052{
1052 struct kbd_struct * kbd = kbd_table + console; 1053 struct kbd_struct * kbd = kbd_table + console;
1053 int ret; 1054 int ret;
1055 unsigned long flags;
1054 1056
1057 spin_lock_irqsave(&led_lock, flags);
1055 ret = vc_kbd_led(kbd, flag); 1058 ret = vc_kbd_led(kbd, flag);
1059 spin_unlock_irqrestore(&led_lock, flags);
1056 1060
1057 return ret; 1061 return ret;
1058} 1062}
@@ -1088,11 +1092,11 @@ void vt_set_led_state(int console, int leds)
1088void vt_kbd_con_start(int console) 1092void vt_kbd_con_start(int console)
1089{ 1093{
1090 struct kbd_struct * kbd = kbd_table + console; 1094 struct kbd_struct * kbd = kbd_table + console;
1091/* unsigned long flags; */ 1095 unsigned long flags;
1092/* spin_lock_irqsave(&kbd_event_lock, flags); */ 1096 spin_lock_irqsave(&led_lock, flags);
1093 clr_vc_kbd_led(kbd, VC_SCROLLOCK); 1097 clr_vc_kbd_led(kbd, VC_SCROLLOCK);
1094 set_leds(); 1098 set_leds();
1095/* spin_unlock_irqrestore(&kbd_event_lock, flags); */ 1099 spin_unlock_irqrestore(&led_lock, flags);
1096} 1100}
1097 1101
1098/** 1102/**
@@ -1101,21 +1105,15 @@ void vt_kbd_con_start(int console)
1101 * 1105 *
1102 * Handle console stop. This is a wrapper for the VT layer 1106 * Handle console stop. This is a wrapper for the VT layer
1103 * so that we can keep kbd knowledge internal 1107 * so that we can keep kbd knowledge internal
1104 *
1105 * FIXME: We eventually need to hold the kbd lock here to protect
1106 * the LED updating. We can't do it yet because fn_hold calls stop_tty
1107 * and start_tty under the kbd_event_lock, while normal tty paths
1108 * don't hold the lock. We probably need to split out an LED lock
1109 * but not during an -rc release!
1110 */ 1108 */
1111void vt_kbd_con_stop(int console) 1109void vt_kbd_con_stop(int console)
1112{ 1110{
1113 struct kbd_struct * kbd = kbd_table + console; 1111 struct kbd_struct * kbd = kbd_table + console;
1114/* unsigned long flags; */ 1112 unsigned long flags;
1115/* spin_lock_irqsave(&kbd_event_lock, flags); */ 1113 spin_lock_irqsave(&led_lock, flags);
1116 set_vc_kbd_led(kbd, VC_SCROLLOCK); 1114 set_vc_kbd_led(kbd, VC_SCROLLOCK);
1117 set_leds(); 1115 set_leds();
1118/* spin_unlock_irqrestore(&kbd_event_lock, flags); */ 1116 spin_unlock_irqrestore(&led_lock, flags);
1119} 1117}
1120 1118
1121/* 1119/*
@@ -1127,7 +1125,12 @@ void vt_kbd_con_stop(int console)
1127 */ 1125 */
1128static void kbd_bh(unsigned long dummy) 1126static void kbd_bh(unsigned long dummy)
1129{ 1127{
1130 unsigned char leds = getleds(); 1128 unsigned char leds;
1129 unsigned long flags;
1130
1131 spin_lock_irqsave(&led_lock, flags);
1132 leds = getleds();
1133 spin_unlock_irqrestore(&led_lock, flags);
1131 1134
1132 if (leds != ledstate) { 1135 if (leds != ledstate) {
1133 input_handler_for_each_handle(&kbd_handler, &leds, 1136 input_handler_for_each_handle(&kbd_handler, &leds,
@@ -2032,11 +2035,11 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm)
2032 return -EPERM; 2035 return -EPERM;
2033 if (arg & ~0x77) 2036 if (arg & ~0x77)
2034 return -EINVAL; 2037 return -EINVAL;
2035 spin_lock_irqsave(&kbd_event_lock, flags); 2038 spin_lock_irqsave(&led_lock, flags);
2036 kbd->ledflagstate = (arg & 7); 2039 kbd->ledflagstate = (arg & 7);
2037 kbd->default_ledflagstate = ((arg >> 4) & 7); 2040 kbd->default_ledflagstate = ((arg >> 4) & 7);
2038 set_leds(); 2041 set_leds();
2039 spin_unlock_irqrestore(&kbd_event_lock, flags); 2042 spin_unlock_irqrestore(&led_lock, flags);
2040 return 0; 2043 return 0;
2041 2044
2042 /* the ioctls below only set the lights, not the functions */ 2045 /* the ioctls below only set the lights, not the functions */
@@ -2131,8 +2134,10 @@ void vt_reset_keyboard(int console)
2131 clr_vc_kbd_mode(kbd, VC_CRLF); 2134 clr_vc_kbd_mode(kbd, VC_CRLF);
2132 kbd->lockstate = 0; 2135 kbd->lockstate = 0;
2133 kbd->slockstate = 0; 2136 kbd->slockstate = 0;
2137 spin_lock(&led_lock);
2134 kbd->ledmode = LED_SHOW_FLAGS; 2138 kbd->ledmode = LED_SHOW_FLAGS;
2135 kbd->ledflagstate = kbd->default_ledflagstate; 2139 kbd->ledflagstate = kbd->default_ledflagstate;
2140 spin_unlock(&led_lock);
2136 /* do not do set_leds here because this causes an endless tasklet loop 2141 /* do not do set_leds here because this causes an endless tasklet loop
2137 when the keyboard hasn't been initialized yet */ 2142 when the keyboard hasn't been initialized yet */
2138 spin_unlock_irqrestore(&kbd_event_lock, flags); 2143 spin_unlock_irqrestore(&kbd_event_lock, flags);