From c948129dd11035b467d3fa0d1ab34d995a41e4ea Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 18 Dec 2003 05:35:36 -0800 Subject: [PATCH] [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 --- drivers/input/serio/i8042.c | 2 +- drivers/input/serio/serio.c | 36 ++++++++++++++++++++++++++++++++++-- include/linux/serio.h | 2 ++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index b603a4db0efe8..1b0e7a5fd16b4 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -283,7 +283,7 @@ activate_fail: irq_fail: values->exists = 0; - serio_unregister_port(port); + serio_unregister_port_delayed(port); return -1; } diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 105a5d59a4e7d..53e77f40e2fe5 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -49,8 +49,10 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(serio_register_port); +EXPORT_SYMBOL(serio_register_port_delayed); EXPORT_SYMBOL(__serio_register_port); EXPORT_SYMBOL(serio_unregister_port); +EXPORT_SYMBOL(serio_unregister_port_delayed); EXPORT_SYMBOL(__serio_unregister_port); EXPORT_SYMBOL(serio_register_device); EXPORT_SYMBOL(serio_unregister_device); @@ -83,8 +85,10 @@ static void serio_find_dev(struct serio *serio) } } -#define SERIO_RESCAN 1 -#define SERIO_RECONNECT 2 +#define SERIO_RESCAN 1 +#define SERIO_RECONNECT 2 +#define SERIO_REGISTER_PORT 3 +#define SERIO_UNREGISTER_PORT 4 static DECLARE_WAIT_QUEUE_HEAD(serio_wait); static DECLARE_COMPLETION(serio_exited); @@ -111,6 +115,14 @@ void serio_handle_events(void) goto event_done; 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 : if (event->serio->dev && event->serio->dev->reconnect) if (event->serio->dev->reconnect(event->serio) == 0) @@ -197,6 +209,16 @@ void serio_register_port(struct serio *serio) 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, * for example when unregistering a serio from other input device's @@ -215,6 +237,16 @@ void serio_unregister_port(struct serio *serio) 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, * for example when unregistering a serio from other input device's diff --git a/include/linux/serio.h b/include/linux/serio.h index 8248b882e2827..d99e973302de9 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -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); void serio_register_port(struct serio *serio); +void serio_register_port_delayed(struct serio *serio); void __serio_register_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_register_device(struct serio_dev *dev); void serio_unregister_device(struct serio_dev *dev); -- 2.53.0