diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2009-09-01 11:38:59 -0400 |
---|---|---|
committer | Live-CD User <linux@linux.site> | 2009-09-19 16:13:40 -0400 |
commit | 8bc2c1b2daf95029658868cb1427baea2da87139 (patch) | |
tree | 9895248e23de85d55a4d4ec9234aec7e58de285b /drivers/usb/serial/usb-serial.c | |
parent | f5b0953a89fa3407fb293cc54ead7d8feec489e4 (diff) |
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 <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/serial/usb-serial.c')
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 31 |
1 files changed, 23 insertions, 8 deletions
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 266dc583867b..87802ea8bbc8 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]; | |||
66 | static DEFINE_MUTEX(table_lock); | 66 | static DEFINE_MUTEX(table_lock); |
67 | static LIST_HEAD(usb_serial_driver_list); | 67 | static LIST_HEAD(usb_serial_driver_list); |
68 | 68 | ||
69 | /* | ||
70 | * Look up the serial structure. If it is found and it hasn't been | ||
71 | * disconnected, return with its disc_mutex held and its refcount | ||
72 | * incremented. Otherwise return NULL. | ||
73 | */ | ||
69 | struct usb_serial *usb_serial_get_by_index(unsigned index) | 74 | struct usb_serial *usb_serial_get_by_index(unsigned index) |
70 | { | 75 | { |
71 | struct usb_serial *serial; | 76 | struct usb_serial *serial; |
@@ -73,8 +78,15 @@ struct usb_serial *usb_serial_get_by_index(unsigned index) | |||
73 | mutex_lock(&table_lock); | 78 | mutex_lock(&table_lock); |
74 | serial = serial_table[index]; | 79 | serial = serial_table[index]; |
75 | 80 | ||
76 | if (serial) | 81 | if (serial) { |
77 | kref_get(&serial->kref); | 82 | mutex_lock(&serial->disc_mutex); |
83 | if (serial->disconnected) { | ||
84 | mutex_unlock(&serial->disc_mutex); | ||
85 | serial = NULL; | ||
86 | } else { | ||
87 | kref_get(&serial->kref); | ||
88 | } | ||
89 | } | ||
78 | mutex_unlock(&table_lock); | 90 | mutex_unlock(&table_lock); |
79 | return serial; | 91 | return serial; |
80 | } | 92 | } |
@@ -123,8 +135,10 @@ static void return_serial(struct usb_serial *serial) | |||
123 | 135 | ||
124 | dbg("%s", __func__); | 136 | dbg("%s", __func__); |
125 | 137 | ||
138 | mutex_lock(&table_lock); | ||
126 | for (i = 0; i < serial->num_ports; ++i) | 139 | for (i = 0; i < serial->num_ports; ++i) |
127 | serial_table[serial->minor + i] = NULL; | 140 | serial_table[serial->minor + i] = NULL; |
141 | mutex_unlock(&table_lock); | ||
128 | } | 142 | } |
129 | 143 | ||
130 | static void destroy_serial(struct kref *kref) | 144 | static void destroy_serial(struct kref *kref) |
@@ -158,9 +172,7 @@ static void destroy_serial(struct kref *kref) | |||
158 | 172 | ||
159 | void usb_serial_put(struct usb_serial *serial) | 173 | void usb_serial_put(struct usb_serial *serial) |
160 | { | 174 | { |
161 | mutex_lock(&table_lock); | ||
162 | kref_put(&serial->kref, destroy_serial); | 175 | kref_put(&serial->kref, destroy_serial); |
163 | mutex_unlock(&table_lock); | ||
164 | } | 176 | } |
165 | 177 | ||
166 | /***************************************************************************** | 178 | /***************************************************************************** |
@@ -190,9 +202,12 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) | |||
190 | return retval; | 202 | return retval; |
191 | /* allow the driver to update it */ | 203 | /* allow the driver to update it */ |
192 | serial = usb_serial_get_by_index(tty->index); | 204 | serial = usb_serial_get_by_index(tty->index); |
193 | if (serial->type->init_termios) | 205 | if (serial) { |
194 | serial->type->init_termios(tty); | 206 | if (serial->type->init_termios) |
195 | usb_serial_put(serial); | 207 | serial->type->init_termios(tty); |
208 | usb_serial_put(serial); | ||
209 | mutex_unlock(&serial->disc_mutex); | ||
210 | } | ||
196 | } | 211 | } |
197 | /* Final install (we use the default method) */ | 212 | /* Final install (we use the default method) */ |
198 | tty_driver_kref_get(driver); | 213 | tty_driver_kref_get(driver); |
@@ -218,7 +233,6 @@ static int serial_open (struct tty_struct *tty, struct file *filp) | |||
218 | return -ENODEV; | 233 | return -ENODEV; |
219 | } | 234 | } |
220 | 235 | ||
221 | mutex_lock(&serial->disc_mutex); | ||
222 | portNumber = tty->index - serial->minor; | 236 | portNumber = tty->index - serial->minor; |
223 | port = serial->port[portNumber]; | 237 | port = serial->port[portNumber]; |
224 | if (!port || serial->disconnected) | 238 | if (!port || serial->disconnected) |
@@ -529,6 +543,7 @@ static int serial_proc_show(struct seq_file *m, void *v) | |||
529 | 543 | ||
530 | seq_putc(m, '\n'); | 544 | seq_putc(m, '\n'); |
531 | usb_serial_put(serial); | 545 | usb_serial_put(serial); |
546 | mutex_unlock(&serial->disc_mutex); | ||
532 | } | 547 | } |
533 | return 0; | 548 | return 0; |
534 | } | 549 | } |