From 8bc2c1b2daf95029658868cb1427baea2da87139 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 1 Sep 2009 11:38:59 -0400 Subject: usb-serial: change logic of serial lookups This patch (as1286) changes usb_serial_get_by_index(). Now the routine will check whether the serial device has been disconnected; if it has then the return value will be NULL. If the device hasn't been disconnected then the routine will return with serial->disc_mutex held, so that the caller can use the structure without fear of racing against driver unloads. This permits the scope of table_mutex in destroy_serial() to be reduced. Instead of protecting the entire function, it suffices to protect the part that actually uses serial_table[], i.e., the call to return_serial(). There's no longer any danger of the refcount being incremented after it reaches 0 (which was the reason for having the large scope previously), because it can't reach 0 until the serial device has been disconnected. Also, the patch makes serial_install() check that serial is non-NULL before attempting to use it. Signed-off-by: Alan Stern Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 266dc583867..87802ea8bbc 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -66,6 +66,11 @@ static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; static DEFINE_MUTEX(table_lock); static LIST_HEAD(usb_serial_driver_list); +/* + * Look up the serial structure. If it is found and it hasn't been + * disconnected, return with its disc_mutex held and its refcount + * incremented. Otherwise return NULL. + */ struct usb_serial *usb_serial_get_by_index(unsigned index) { struct usb_serial *serial; @@ -73,8 +78,15 @@ struct usb_serial *usb_serial_get_by_index(unsigned index) mutex_lock(&table_lock); serial = serial_table[index]; - if (serial) - kref_get(&serial->kref); + if (serial) { + mutex_lock(&serial->disc_mutex); + if (serial->disconnected) { + mutex_unlock(&serial->disc_mutex); + serial = NULL; + } else { + kref_get(&serial->kref); + } + } mutex_unlock(&table_lock); return serial; } @@ -123,8 +135,10 @@ static void return_serial(struct usb_serial *serial) dbg("%s", __func__); + mutex_lock(&table_lock); for (i = 0; i < serial->num_ports; ++i) serial_table[serial->minor + i] = NULL; + mutex_unlock(&table_lock); } static void destroy_serial(struct kref *kref) @@ -158,9 +172,7 @@ static void destroy_serial(struct kref *kref) void usb_serial_put(struct usb_serial *serial) { - mutex_lock(&table_lock); kref_put(&serial->kref, destroy_serial); - mutex_unlock(&table_lock); } /***************************************************************************** @@ -190,9 +202,12 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) return retval; /* allow the driver to update it */ serial = usb_serial_get_by_index(tty->index); - if (serial->type->init_termios) - serial->type->init_termios(tty); - usb_serial_put(serial); + if (serial) { + if (serial->type->init_termios) + serial->type->init_termios(tty); + usb_serial_put(serial); + mutex_unlock(&serial->disc_mutex); + } } /* Final install (we use the default method) */ tty_driver_kref_get(driver); @@ -218,7 +233,6 @@ static int serial_open (struct tty_struct *tty, struct file *filp) return -ENODEV; } - mutex_lock(&serial->disc_mutex); portNumber = tty->index - serial->minor; port = serial->port[portNumber]; if (!port || serial->disconnected) @@ -529,6 +543,7 @@ static int serial_proc_show(struct seq_file *m, void *v) seq_putc(m, '\n'); usb_serial_put(serial); + mutex_unlock(&serial->disc_mutex); } return 0; } -- cgit v1.2.3-70-g09d2