Commit 3db1ddb7 authored by Alan Cox's avatar Alan Cox Committed by Greg Kroah-Hartman

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: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ce7240e4
......@@ -119,6 +119,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
static struct input_handler kbd_handler;
static DEFINE_SPINLOCK(kbd_event_lock);
static DEFINE_SPINLOCK(led_lock);
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
static bool dead_key_next;
......@@ -984,7 +985,7 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
* or (ii) whatever pattern of lights people want to show using KDSETLED,
* or (iii) specified bits of specified words in kernel memory.
*/
unsigned char getledstate(void)
static unsigned char getledstate(void)
{
return ledstate;
}
......@@ -992,7 +993,7 @@ unsigned char getledstate(void)
void setledstate(struct kbd_struct *kbd, unsigned int led)
{
unsigned long flags;
spin_lock_irqsave(&kbd_event_lock, flags);
spin_lock_irqsave(&led_lock, flags);
if (!(led & ~7)) {
ledioctl = led;
kbd->ledmode = LED_SHOW_IOCTL;
......@@ -1000,7 +1001,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led)
kbd->ledmode = LED_SHOW_FLAGS;
set_leds();
spin_unlock_irqrestore(&kbd_event_lock, flags);
spin_unlock_irqrestore(&led_lock, flags);
}
static inline unsigned char getleds(void)
......@@ -1051,8 +1052,11 @@ int vt_get_leds(int console, int flag)
{
struct kbd_struct * kbd = kbd_table + console;
int ret;
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
ret = vc_kbd_led(kbd, flag);
spin_unlock_irqrestore(&led_lock, flags);
return ret;
}
......@@ -1088,11 +1092,11 @@ void vt_set_led_state(int console, int leds)
void vt_kbd_con_start(int console)
{
struct kbd_struct * kbd = kbd_table + console;
/* unsigned long flags; */
/* spin_lock_irqsave(&kbd_event_lock, flags); */
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
clr_vc_kbd_led(kbd, VC_SCROLLOCK);
set_leds();
/* spin_unlock_irqrestore(&kbd_event_lock, flags); */
spin_unlock_irqrestore(&led_lock, flags);
}
/**
......@@ -1101,21 +1105,15 @@ void vt_kbd_con_start(int console)
*
* Handle console stop. This is a wrapper for the VT layer
* so that we can keep kbd knowledge internal
*
* FIXME: We eventually need to hold the kbd lock here to protect
* the LED updating. We can't do it yet because fn_hold calls stop_tty
* and start_tty under the kbd_event_lock, while normal tty paths
* don't hold the lock. We probably need to split out an LED lock
* but not during an -rc release!
*/
void vt_kbd_con_stop(int console)
{
struct kbd_struct * kbd = kbd_table + console;
/* unsigned long flags; */
/* spin_lock_irqsave(&kbd_event_lock, flags); */
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
set_vc_kbd_led(kbd, VC_SCROLLOCK);
set_leds();
/* spin_unlock_irqrestore(&kbd_event_lock, flags); */
spin_unlock_irqrestore(&led_lock, flags);
}
/*
......@@ -1127,7 +1125,12 @@ void vt_kbd_con_stop(int console)
*/
static void kbd_bh(unsigned long dummy)
{
unsigned char leds = getleds();
unsigned char leds;
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
leds = getleds();
spin_unlock_irqrestore(&led_lock, flags);
if (leds != ledstate) {
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)
return -EPERM;
if (arg & ~0x77)
return -EINVAL;
spin_lock_irqsave(&kbd_event_lock, flags);
spin_lock_irqsave(&led_lock, flags);
kbd->ledflagstate = (arg & 7);
kbd->default_ledflagstate = ((arg >> 4) & 7);
set_leds();
spin_unlock_irqrestore(&kbd_event_lock, flags);
spin_unlock_irqrestore(&led_lock, flags);
return 0;
/* the ioctls below only set the lights, not the functions */
......@@ -2131,8 +2134,10 @@ void vt_reset_keyboard(int console)
clr_vc_kbd_mode(kbd, VC_CRLF);
kbd->lockstate = 0;
kbd->slockstate = 0;
spin_lock(&led_lock);
kbd->ledmode = LED_SHOW_FLAGS;
kbd->ledflagstate = kbd->default_ledflagstate;
spin_unlock(&led_lock);
/* do not do set_leds here because this causes an endless tasklet loop
when the keyboard hasn't been initialized yet */
spin_unlock_irqrestore(&kbd_event_lock, flags);
......
......@@ -65,7 +65,6 @@ struct kbd_struct {
extern int kbd_init(void);
extern unsigned char getledstate(void);
extern void setledstate(struct kbd_struct *kbd, unsigned int led);
extern int do_poke_blanked_console;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment