Commit 8bc2c1b2 authored by Alan Stern's avatar Alan Stern Committed by Live-CD User

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent f5b0953a
...@@ -66,6 +66,11 @@ static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; ...@@ -66,6 +66,11 @@ static struct usb_serial *serial_table[SERIAL_TTY_MINORS];
static DEFINE_MUTEX(table_lock); static DEFINE_MUTEX(table_lock);
static LIST_HEAD(usb_serial_driver_list); 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 *usb_serial_get_by_index(unsigned index)
{ {
struct usb_serial *serial; struct usb_serial *serial;
...@@ -73,8 +78,15 @@ struct usb_serial *usb_serial_get_by_index(unsigned index) ...@@ -73,8 +78,15 @@ struct usb_serial *usb_serial_get_by_index(unsigned index)
mutex_lock(&table_lock); mutex_lock(&table_lock);
serial = serial_table[index]; serial = serial_table[index];
if (serial) if (serial) {
kref_get(&serial->kref); mutex_lock(&serial->disc_mutex);
if (serial->disconnected) {
mutex_unlock(&serial->disc_mutex);
serial = NULL;
} else {
kref_get(&serial->kref);
}
}
mutex_unlock(&table_lock); mutex_unlock(&table_lock);
return serial; return serial;
} }
...@@ -123,8 +135,10 @@ static void return_serial(struct usb_serial *serial) ...@@ -123,8 +135,10 @@ static void return_serial(struct usb_serial *serial)
dbg("%s", __func__); dbg("%s", __func__);
mutex_lock(&table_lock);
for (i = 0; i < serial->num_ports; ++i) for (i = 0; i < serial->num_ports; ++i)
serial_table[serial->minor + i] = NULL; serial_table[serial->minor + i] = NULL;
mutex_unlock(&table_lock);
} }
static void destroy_serial(struct kref *kref) static void destroy_serial(struct kref *kref)
...@@ -158,9 +172,7 @@ 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) void usb_serial_put(struct usb_serial *serial)
{ {
mutex_lock(&table_lock);
kref_put(&serial->kref, destroy_serial); 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) ...@@ -190,9 +202,12 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
return retval; return retval;
/* allow the driver to update it */ /* allow the driver to update it */
serial = usb_serial_get_by_index(tty->index); serial = usb_serial_get_by_index(tty->index);
if (serial->type->init_termios) if (serial) {
serial->type->init_termios(tty); if (serial->type->init_termios)
usb_serial_put(serial); serial->type->init_termios(tty);
usb_serial_put(serial);
mutex_unlock(&serial->disc_mutex);
}
} }
/* Final install (we use the default method) */ /* Final install (we use the default method) */
tty_driver_kref_get(driver); tty_driver_kref_get(driver);
...@@ -218,7 +233,6 @@ static int serial_open (struct tty_struct *tty, struct file *filp) ...@@ -218,7 +233,6 @@ static int serial_open (struct tty_struct *tty, struct file *filp)
return -ENODEV; return -ENODEV;
} }
mutex_lock(&serial->disc_mutex);
portNumber = tty->index - serial->minor; portNumber = tty->index - serial->minor;
port = serial->port[portNumber]; port = serial->port[portNumber];
if (!port || serial->disconnected) if (!port || serial->disconnected)
...@@ -529,6 +543,7 @@ static int serial_proc_show(struct seq_file *m, void *v) ...@@ -529,6 +543,7 @@ static int serial_proc_show(struct seq_file *m, void *v)
seq_putc(m, '\n'); seq_putc(m, '\n');
usb_serial_put(serial); usb_serial_put(serial);
mutex_unlock(&serial->disc_mutex);
} }
return 0; return 0;
} }
......
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