[PATCH] USB: gadget-serial: fix a deadlock when closing the serial device
When closing the device, the driver acquires/release twice the port lock before/after waiting for the data to be completely sent. Therefore it will dead lock. This patch fixes it and also uses the generic scheduler services for waiting for an event. Signed-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
b059c81af7
commit
943e1b4d60
1 changed files with 17 additions and 78 deletions
|
@ -51,82 +51,10 @@
|
|||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/* Wait Cond */
|
||||
|
||||
#define __wait_cond_interruptible(wq, condition, lock, flags, ret) \
|
||||
do { \
|
||||
wait_queue_t __wait; \
|
||||
init_waitqueue_entry(&__wait, current); \
|
||||
\
|
||||
add_wait_queue(&wq, &__wait); \
|
||||
for (;;) { \
|
||||
set_current_state(TASK_INTERRUPTIBLE); \
|
||||
if (condition) \
|
||||
break; \
|
||||
if (!signal_pending(current)) { \
|
||||
spin_unlock_irqrestore(lock, flags); \
|
||||
schedule(); \
|
||||
spin_lock_irqsave(lock, flags); \
|
||||
continue; \
|
||||
} \
|
||||
ret = -ERESTARTSYS; \
|
||||
break; \
|
||||
} \
|
||||
current->state = TASK_RUNNING; \
|
||||
remove_wait_queue(&wq, &__wait); \
|
||||
} while (0)
|
||||
|
||||
#define wait_cond_interruptible(wq, condition, lock, flags) \
|
||||
({ \
|
||||
int __ret = 0; \
|
||||
if (!(condition)) \
|
||||
__wait_cond_interruptible(wq, condition, lock, flags, \
|
||||
__ret); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define __wait_cond_interruptible_timeout(wq, condition, lock, flags, \
|
||||
timeout, ret) \
|
||||
do { \
|
||||
signed long __timeout = timeout; \
|
||||
wait_queue_t __wait; \
|
||||
init_waitqueue_entry(&__wait, current); \
|
||||
\
|
||||
add_wait_queue(&wq, &__wait); \
|
||||
for (;;) { \
|
||||
set_current_state(TASK_INTERRUPTIBLE); \
|
||||
if (__timeout == 0) \
|
||||
break; \
|
||||
if (condition) \
|
||||
break; \
|
||||
if (!signal_pending(current)) { \
|
||||
spin_unlock_irqrestore(lock, flags); \
|
||||
__timeout = schedule_timeout(__timeout); \
|
||||
spin_lock_irqsave(lock, flags); \
|
||||
continue; \
|
||||
} \
|
||||
ret = -ERESTARTSYS; \
|
||||
break; \
|
||||
} \
|
||||
current->state = TASK_RUNNING; \
|
||||
remove_wait_queue(&wq, &__wait); \
|
||||
} while (0)
|
||||
|
||||
#define wait_cond_interruptible_timeout(wq, condition, lock, flags, \
|
||||
timeout) \
|
||||
({ \
|
||||
int __ret = 0; \
|
||||
if (!(condition)) \
|
||||
__wait_cond_interruptible_timeout(wq, condition, lock, \
|
||||
flags, timeout, __ret); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
|
||||
/* Defines */
|
||||
|
||||
#define GS_VERSION_STR "v2.0"
|
||||
#define GS_VERSION_NUM 0x0200
|
||||
#define GS_VERSION_STR "v2.1"
|
||||
#define GS_VERSION_NUM 0x0201
|
||||
|
||||
#define GS_LONG_NAME "Gadget Serial"
|
||||
#define GS_SHORT_NAME "g_serial"
|
||||
|
@ -843,6 +771,18 @@ static int gs_open(struct tty_struct *tty, struct file *file)
|
|||
/*
|
||||
* gs_close
|
||||
*/
|
||||
|
||||
#define GS_WRITE_FINISHED_EVENT_SAFELY(p) \
|
||||
({ \
|
||||
unsigned long flags; \
|
||||
int cond; \
|
||||
\
|
||||
spin_lock_irqsave(&(p)->port_lock, flags); \
|
||||
cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf); \
|
||||
spin_unlock_irqrestore(&(p)->port_lock, flags); \
|
||||
cond; \
|
||||
})
|
||||
|
||||
static void gs_close(struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -888,10 +828,9 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
|||
/* at most GS_CLOSE_TIMEOUT seconds */
|
||||
if (gs_buf_data_avail(port->port_write_buf) > 0) {
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
wait_cond_interruptible_timeout(port->port_write_wait,
|
||||
port->port_dev == NULL
|
||||
|| gs_buf_data_avail(port->port_write_buf) == 0,
|
||||
&port->port_lock, flags, GS_CLOSE_TIMEOUT * HZ);
|
||||
wait_event_interruptible_timeout(port->port_write_wait,
|
||||
GS_WRITE_FINISHED_EVENT_SAFELY(port),
|
||||
GS_CLOSE_TIMEOUT * HZ);
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue