[095/136] usb-serial: change logic of serial lookups

From: Greg KH
Date: Thu Oct 01 2009 - 21:44:09 EST


2.6.31-stable review patch. If anyone has any objections, please let us know.

------------------
From: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>

commit 8bc2c1b2daf95029658868cb1427baea2da87139 upstream.

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 <stern@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>

---
drivers/usb/serial/usb-serial.c | 31 +++++++++++++++++++++++--------
1 file changed, 23 insertions(+), 8 deletions(-)

--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -66,6 +66,11 @@ static struct usb_serial *serial_table[S
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_ind
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_ser

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 *

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_dri
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_struc
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_f

seq_putc(m, '\n');
usb_serial_put(serial);
+ mutex_unlock(&serial->disc_mutex);
}
return 0;
}


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/