Commit ecb88b25 authored by Vojtech Pavlik's avatar Vojtech Pavlik

input: Fixes in serio locking. We need per-serio lock for passthrough

       ports, some locks were missing, and spin_lock_irq was wishful
       thinking in serio_interrupt. There is no guarantee
       that serio_interrupt won't be called twice at the same time.
Signed-off-by: default avatarVojtech Pavlik <vojtech@suse.cz>
parent c00aa0db
...@@ -68,8 +68,8 @@ struct serio_event { ...@@ -68,8 +68,8 @@ struct serio_event {
}; };
spinlock_t serio_lock = SPIN_LOCK_UNLOCKED; /* protects serio_event_list and serio->dev */ spinlock_t serio_event_lock = SPIN_LOCK_UNLOCKED; /* protects serio_event_list */
static DECLARE_MUTEX(serio_sem); /* protects serio_list and serio_dev_list */ static DECLARE_MUTEX(serio_sem); /* protects serio_list and serio_dev_list */
static LIST_HEAD(serio_list); static LIST_HEAD(serio_list);
static LIST_HEAD(serio_dev_list); static LIST_HEAD(serio_dev_list);
static LIST_HEAD(serio_event_list); static LIST_HEAD(serio_event_list);
...@@ -104,13 +104,13 @@ static void serio_invalidate_pending_events(struct serio *serio) ...@@ -104,13 +104,13 @@ static void serio_invalidate_pending_events(struct serio *serio)
struct serio_event *event; struct serio_event *event;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&serio_lock, flags); spin_lock_irqsave(&serio_event_lock, flags);
list_for_each_entry(event, &serio_event_list, node) list_for_each_entry(event, &serio_event_list, node)
if (event->serio == serio) if (event->serio == serio)
event->serio = NULL; event->serio = NULL;
spin_unlock_irqrestore(&serio_lock, flags); spin_unlock_irqrestore(&serio_event_lock, flags);
} }
void serio_handle_events(void) void serio_handle_events(void)
...@@ -119,13 +119,13 @@ void serio_handle_events(void) ...@@ -119,13 +119,13 @@ void serio_handle_events(void)
struct serio_event *event; struct serio_event *event;
unsigned long flags; unsigned long flags;
while (1) { while (1) {
spin_lock_irqsave(&serio_lock, flags); spin_lock_irqsave(&serio_event_lock, flags);
if (list_empty(&serio_event_list)) { if (list_empty(&serio_event_list)) {
spin_unlock_irqrestore(&serio_lock, flags); spin_unlock_irqrestore(&serio_event_lock, flags);
break; break;
} }
...@@ -133,7 +133,7 @@ void serio_handle_events(void) ...@@ -133,7 +133,7 @@ void serio_handle_events(void)
event = container_of(node, struct serio_event, node); event = container_of(node, struct serio_event, node);
list_del_init(node); list_del_init(node);
spin_unlock_irqrestore(&serio_lock, flags); spin_unlock_irqrestore(&serio_event_lock, flags);
down(&serio_sem); down(&serio_sem);
...@@ -190,8 +190,11 @@ static int serio_thread(void *nothing) ...@@ -190,8 +190,11 @@ static int serio_thread(void *nothing)
static void serio_queue_event(struct serio *serio, int event_type) static void serio_queue_event(struct serio *serio, int event_type)
{ {
unsigned long flags;
struct serio_event *event; struct serio_event *event;
spin_lock_irqsave(&serio_event_lock, flags);
if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) { if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
event->type = event_type; event->type = event_type;
event->serio = serio; event->serio = serio;
...@@ -199,44 +202,41 @@ static void serio_queue_event(struct serio *serio, int event_type) ...@@ -199,44 +202,41 @@ static void serio_queue_event(struct serio *serio, int event_type)
list_add_tail(&event->node, &serio_event_list); list_add_tail(&event->node, &serio_event_list);
wake_up(&serio_wait); wake_up(&serio_wait);
} }
spin_unlock_irqrestore(&serio_event_lock, flags);
} }
void serio_rescan(struct serio *serio) void serio_rescan(struct serio *serio)
{ {
unsigned long flags;
spin_lock_irqsave(&serio_lock, flags);
serio_queue_event(serio, SERIO_RESCAN); serio_queue_event(serio, SERIO_RESCAN);
spin_unlock_irqrestore(&serio_lock, flags);
} }
void serio_reconnect(struct serio *serio) void serio_reconnect(struct serio *serio)
{ {
unsigned long flags;
spin_lock_irqsave(&serio_lock, flags);
serio_queue_event(serio, SERIO_RECONNECT); serio_queue_event(serio, SERIO_RECONNECT);
spin_unlock_irqrestore(&serio_lock, flags);
} }
irqreturn_t serio_interrupt(struct serio *serio, irqreturn_t serio_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs) unsigned char data, unsigned int dfl, struct pt_regs *regs)
{ {
unsigned long flags;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
spin_lock_irq(&serio_lock); spin_lock_irqsave(&serio->lock, flags);
if (serio->dev && serio->dev->interrupt) { if (serio->dev && serio->dev->interrupt) {
ret = serio->dev->interrupt(serio, data, flags, regs); ret = serio->dev->interrupt(serio, data, dfl, regs);
} else { } else {
if (!flags) { if (!dfl) {
if ((serio->type != SERIO_8042 && if ((serio->type != SERIO_8042 &&
serio->type != SERIO_8042_XL) || (data == 0xaa)) { serio->type != SERIO_8042_XL) || (data == 0xaa)) {
serio_queue_event(serio, SERIO_RESCAN); serio_rescan(serio);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
} }
} }
} }
spin_unlock_irq(&serio_lock); spin_unlock_irqrestore(&serio->lock, flags);
return ret; return ret;
} }
...@@ -265,6 +265,7 @@ void serio_register_port_delayed(struct serio *serio) ...@@ -265,6 +265,7 @@ void serio_register_port_delayed(struct serio *serio)
*/ */
void __serio_register_port(struct serio *serio) void __serio_register_port(struct serio *serio)
{ {
spin_lock_init(&serio->lock);
list_add_tail(&serio->node, &serio_list); list_add_tail(&serio->node, &serio_list);
serio_find_dev(serio); serio_find_dev(serio);
} }
...@@ -330,11 +331,13 @@ int serio_open(struct serio *serio, struct serio_dev *dev) ...@@ -330,11 +331,13 @@ int serio_open(struct serio *serio, struct serio_dev *dev)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&serio_lock, flags); spin_lock_irqsave(&serio->lock, flags);
serio->dev = dev; serio->dev = dev;
spin_unlock_irqrestore(&serio_lock, flags); spin_unlock_irqrestore(&serio->lock, flags);
if (serio->open && serio->open(serio)) { if (serio->open && serio->open(serio)) {
spin_lock_irqsave(&serio->lock, flags);
serio->dev = NULL; serio->dev = NULL;
spin_unlock_irqrestore(&serio->lock, flags);
return -1; return -1;
} }
return 0; return 0;
...@@ -347,9 +350,9 @@ void serio_close(struct serio *serio) ...@@ -347,9 +350,9 @@ void serio_close(struct serio *serio)
if (serio->close) if (serio->close)
serio->close(serio); serio->close(serio);
spin_lock_irqsave(&serio_lock, flags); spin_lock_irqsave(&serio->lock, flags);
serio->dev = NULL; serio->dev = NULL;
spin_unlock_irqrestore(&serio_lock, flags); spin_unlock_irqrestore(&serio->lock, flags);
} }
static int __init serio_init(void) static int __init serio_init(void)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/list.h> #include <linux/list.h>
#include <linux/spinlock.h>
struct serio { struct serio {
void *private; void *private;
...@@ -32,6 +33,8 @@ struct serio { ...@@ -32,6 +33,8 @@ struct serio {
unsigned long type; unsigned long type;
unsigned long event; unsigned long event;
spinlock_t lock;
int (*write)(struct serio *, unsigned char); int (*write)(struct serio *, unsigned char);
int (*open)(struct serio *); int (*open)(struct serio *);
void (*close)(struct serio *); void (*close)(struct serio *);
......
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