Commit c948129d authored by Dmitry Torokhov's avatar Dmitry Torokhov

[PATCH] Input: add serio_[un]register_port_delayed to fix deadlock

Add serio_[un]register_port_delayed to allow delayed execution of
register/unregister code (via kseriod) when it is not clear whether
serio_sem has been taken or not.  Use in i8042.c to avoid deadlock
parent c5a67891
...@@ -283,7 +283,7 @@ static int i8042_open(struct serio *port) ...@@ -283,7 +283,7 @@ static int i8042_open(struct serio *port)
irq_fail: irq_fail:
values->exists = 0; values->exists = 0;
serio_unregister_port(port); serio_unregister_port_delayed(port);
return -1; return -1;
} }
......
...@@ -49,8 +49,10 @@ MODULE_LICENSE("GPL"); ...@@ -49,8 +49,10 @@ MODULE_LICENSE("GPL");
EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(serio_interrupt);
EXPORT_SYMBOL(serio_register_port); EXPORT_SYMBOL(serio_register_port);
EXPORT_SYMBOL(serio_register_port_delayed);
EXPORT_SYMBOL(__serio_register_port); EXPORT_SYMBOL(__serio_register_port);
EXPORT_SYMBOL(serio_unregister_port); EXPORT_SYMBOL(serio_unregister_port);
EXPORT_SYMBOL(serio_unregister_port_delayed);
EXPORT_SYMBOL(__serio_unregister_port); EXPORT_SYMBOL(__serio_unregister_port);
EXPORT_SYMBOL(serio_register_device); EXPORT_SYMBOL(serio_register_device);
EXPORT_SYMBOL(serio_unregister_device); EXPORT_SYMBOL(serio_unregister_device);
...@@ -83,8 +85,10 @@ static void serio_find_dev(struct serio *serio) ...@@ -83,8 +85,10 @@ static void serio_find_dev(struct serio *serio)
} }
} }
#define SERIO_RESCAN 1 #define SERIO_RESCAN 1
#define SERIO_RECONNECT 2 #define SERIO_RECONNECT 2
#define SERIO_REGISTER_PORT 3
#define SERIO_UNREGISTER_PORT 4
static DECLARE_WAIT_QUEUE_HEAD(serio_wait); static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
static DECLARE_COMPLETION(serio_exited); static DECLARE_COMPLETION(serio_exited);
...@@ -111,6 +115,14 @@ void serio_handle_events(void) ...@@ -111,6 +115,14 @@ void serio_handle_events(void)
goto event_done; goto event_done;
switch (event->type) { switch (event->type) {
case SERIO_REGISTER_PORT :
__serio_register_port(event->serio);
break;
case SERIO_UNREGISTER_PORT :
__serio_unregister_port(event->serio);
break;
case SERIO_RECONNECT : case SERIO_RECONNECT :
if (event->serio->dev && event->serio->dev->reconnect) if (event->serio->dev && event->serio->dev->reconnect)
if (event->serio->dev->reconnect(event->serio) == 0) if (event->serio->dev->reconnect(event->serio) == 0)
...@@ -197,6 +209,16 @@ void serio_register_port(struct serio *serio) ...@@ -197,6 +209,16 @@ void serio_register_port(struct serio *serio)
up(&serio_sem); up(&serio_sem);
} }
/*
* Submits register request to kseriod for subsequent execution.
* Can be used when it is not obvious whether the serio_sem is
* taken or not and when delayed execution is feasible.
*/
void serio_register_port_delayed(struct serio *serio)
{
serio_queue_event(serio, SERIO_REGISTER_PORT);
}
/* /*
* Should only be called directly if serio_sem has already been taken, * Should only be called directly if serio_sem has already been taken,
* for example when unregistering a serio from other input device's * for example when unregistering a serio from other input device's
...@@ -215,6 +237,16 @@ void serio_unregister_port(struct serio *serio) ...@@ -215,6 +237,16 @@ void serio_unregister_port(struct serio *serio)
up(&serio_sem); up(&serio_sem);
} }
/*
* Submits unregister request to kseriod for subsequent execution.
* Can be used when it is not obvious whether the serio_sem is
* taken or not and when delayed execution is feasible.
*/
void serio_unregister_port_delayed(struct serio *serio)
{
serio_queue_event(serio, SERIO_UNREGISTER_PORT);
}
/* /*
* Should only be called directly if serio_sem has already been taken, * Should only be called directly if serio_sem has already been taken,
* for example when unregistering a serio from other input device's * for example when unregistering a serio from other input device's
......
...@@ -63,8 +63,10 @@ void serio_reconnect(struct serio *serio); ...@@ -63,8 +63,10 @@ void serio_reconnect(struct serio *serio);
irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs); irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs);
void serio_register_port(struct serio *serio); void serio_register_port(struct serio *serio);
void serio_register_port_delayed(struct serio *serio);
void __serio_register_port(struct serio *serio); void __serio_register_port(struct serio *serio);
void serio_unregister_port(struct serio *serio); void serio_unregister_port(struct serio *serio);
void serio_unregister_port_delayed(struct serio *serio);
void __serio_unregister_port(struct serio *serio); void __serio_unregister_port(struct serio *serio);
void serio_register_device(struct serio_dev *dev); void serio_register_device(struct serio_dev *dev);
void serio_unregister_device(struct serio_dev *dev); void serio_unregister_device(struct serio_dev *dev);
......
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