From: Dmitry Torokhov 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 -puN drivers/input/serio/i8042.c~input-06-serio_unregister_port_delayed drivers/input/serio/i8042.c --- 25/drivers/input/serio/i8042.c~input-06-serio_unregister_port_delayed 2003-12-16 22:47:41.000000000 -0800 +++ 25-akpm/drivers/input/serio/i8042.c 2003-12-16 22:47:41.000000000 -0800 @@ -283,7 +283,7 @@ activate_fail: irq_fail: values->exists = 0; - serio_unregister_port(port); + serio_unregister_port_delayed(port); return -1; } diff -puN drivers/input/serio/serio.c~input-06-serio_unregister_port_delayed drivers/input/serio/serio.c --- 25/drivers/input/serio/serio.c~input-06-serio_unregister_port_delayed 2003-12-16 22:47:41.000000000 -0800 +++ 25-akpm/drivers/input/serio/serio.c 2003-12-16 22:47:41.000000000 -0800 @@ -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 } } -#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) @@ -198,6 +210,16 @@ void serio_register_port(struct serio *s } /* + * 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 * connect() function. @@ -216,6 +238,16 @@ void serio_unregister_port(struct serio } /* + * 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 * disconnect() function. diff -puN include/linux/serio.h~input-06-serio_unregister_port_delayed include/linux/serio.h --- 25/include/linux/serio.h~input-06-serio_unregister_port_delayed 2003-12-16 22:47:41.000000000 -0800 +++ 25-akpm/include/linux/serio.h 2003-12-16 22:47:41.000000000 -0800 @@ -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); _