diff options
| author | Alan Cox <alan@linux.intel.com> | 2009-10-06 11:06:11 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 18:18:04 -0500 |
| commit | 64bc397914265a9ead8d73b63bb31ab3bdd25f67 (patch) | |
| tree | dc96d1737d3d70bbdebaea7e0f0aedd1377e1000 | |
| parent | 894cb91770f7794f1a17db4df2d83999b197da24 (diff) | |
tty_port: add "tty_port_open" helper
For the moment this just moves the USB logic over and fixes the 'what if
we open and hangup at the same time' race noticed by Oliver Neukum.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <oliver@neukum.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/char/tty_port.c | 36 | ||||
| -rw-r--r-- | drivers/usb/serial/usb-serial.c | 49 | ||||
| -rw-r--r-- | include/linux/tty.h | 10 |
3 files changed, 64 insertions, 31 deletions
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index c63f3d33914a..b22a61a4fbe5 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c | |||
| @@ -99,10 +99,11 @@ EXPORT_SYMBOL(tty_port_tty_set); | |||
| 99 | 99 | ||
| 100 | static void tty_port_shutdown(struct tty_port *port) | 100 | static void tty_port_shutdown(struct tty_port *port) |
| 101 | { | 101 | { |
| 102 | mutex_lock(&port->mutex); | ||
| 102 | if (port->ops->shutdown && | 103 | if (port->ops->shutdown && |
| 103 | test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) | 104 | test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) |
| 104 | port->ops->shutdown(port); | 105 | port->ops->shutdown(port); |
| 105 | 106 | mutex_unlock(&port->mutex); | |
| 106 | } | 107 | } |
| 107 | 108 | ||
| 108 | /** | 109 | /** |
| @@ -381,3 +382,36 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, | |||
| 381 | tty_port_tty_set(port, NULL); | 382 | tty_port_tty_set(port, NULL); |
| 382 | } | 383 | } |
| 383 | EXPORT_SYMBOL(tty_port_close); | 384 | EXPORT_SYMBOL(tty_port_close); |
| 385 | |||
| 386 | int tty_port_open(struct tty_port *port, struct tty_struct *tty, | ||
| 387 | struct file *filp) | ||
| 388 | { | ||
| 389 | spin_lock_irq(&port->lock); | ||
| 390 | if (!tty_hung_up_p(filp)) | ||
| 391 | ++port->count; | ||
| 392 | spin_unlock_irq(&port->lock); | ||
| 393 | tty_port_tty_set(port, tty); | ||
| 394 | |||
| 395 | /* | ||
| 396 | * Do the device-specific open only if the hardware isn't | ||
| 397 | * already initialized. Serialize open and shutdown using the | ||
| 398 | * port mutex. | ||
| 399 | */ | ||
| 400 | |||
| 401 | mutex_lock(&port->mutex); | ||
| 402 | |||
| 403 | if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { | ||
| 404 | if (port->ops->activate) { | ||
| 405 | int retval = port->ops->activate(port, tty); | ||
| 406 | if (retval) { | ||
| 407 | mutex_unlock(&port->mutex); | ||
| 408 | return retval; | ||
| 409 | } | ||
| 410 | } | ||
| 411 | set_bit(ASYNCB_INITIALIZED, &port->flags); | ||
| 412 | } | ||
| 413 | mutex_unlock(&port->mutex); | ||
| 414 | return tty_port_block_til_ready(port, tty, filp); | ||
| 415 | } | ||
| 416 | |||
| 417 | EXPORT_SYMBOL(tty_port_open); | ||
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index bd3fa7ff15b1..b0649d92251f 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c | |||
| @@ -247,41 +247,31 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) | |||
| 247 | return retval; | 247 | return retval; |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | static int serial_open(struct tty_struct *tty, struct file *filp) | 250 | static int serial_activate(struct tty_port *tport, struct tty_struct *tty) |
| 251 | { | 251 | { |
| 252 | struct usb_serial_port *port = tty->driver_data; | 252 | struct usb_serial_port *port = |
| 253 | container_of(tport, struct usb_serial_port, port); | ||
| 253 | struct usb_serial *serial = port->serial; | 254 | struct usb_serial *serial = port->serial; |
| 254 | int retval; | 255 | int retval; |
| 255 | 256 | ||
| 256 | dbg("%s - port %d", __func__, port->number); | 257 | if (mutex_lock_interruptible(&port->mutex)) |
| 257 | 258 | return -ERESTARTSYS; | |
| 258 | spin_lock_irq(&port->port.lock); | 259 | mutex_lock(&serial->disc_mutex); |
| 259 | if (!tty_hung_up_p(filp)) | 260 | if (serial->disconnected) |
| 260 | ++port->port.count; | 261 | retval = -ENODEV; |
| 261 | spin_unlock_irq(&port->port.lock); | 262 | else |
| 262 | tty_port_tty_set(&port->port, tty); | 263 | retval = port->serial->type->open(tty, port); |
| 264 | mutex_unlock(&serial->disc_mutex); | ||
| 265 | mutex_unlock(&port->mutex); | ||
| 266 | return retval; | ||
| 267 | } | ||
| 263 | 268 | ||
| 264 | /* Do the device-specific open only if the hardware isn't | 269 | static int serial_open(struct tty_struct *tty, struct file *filp) |
| 265 | * already initialized. | 270 | { |
| 266 | */ | 271 | struct usb_serial_port *port = tty->driver_data; |
| 267 | if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) { | ||
| 268 | if (mutex_lock_interruptible(&port->mutex)) | ||
| 269 | return -ERESTARTSYS; | ||
| 270 | mutex_lock(&serial->disc_mutex); | ||
| 271 | if (serial->disconnected) | ||
| 272 | retval = -ENODEV; | ||
| 273 | else | ||
| 274 | retval = port->serial->type->open(tty, port); | ||
| 275 | mutex_unlock(&serial->disc_mutex); | ||
| 276 | mutex_unlock(&port->mutex); | ||
| 277 | if (retval) | ||
| 278 | return retval; | ||
| 279 | set_bit(ASYNCB_INITIALIZED, &port->port.flags); | ||
| 280 | } | ||
| 281 | 272 | ||
| 282 | /* Now do the correct tty layer semantics */ | 273 | dbg("%s - port %d", __func__, port->number); |
| 283 | retval = tty_port_block_til_ready(&port->port, tty, filp); | 274 | return tty_port_open(&port->port, tty, filp); |
| 284 | return retval; | ||
| 285 | } | 275 | } |
| 286 | 276 | ||
| 287 | /** | 277 | /** |
| @@ -725,6 +715,7 @@ static void serial_dtr_rts(struct tty_port *port, int on) | |||
| 725 | static const struct tty_port_operations serial_port_ops = { | 715 | static const struct tty_port_operations serial_port_ops = { |
| 726 | .carrier_raised = serial_carrier_raised, | 716 | .carrier_raised = serial_carrier_raised, |
| 727 | .dtr_rts = serial_dtr_rts, | 717 | .dtr_rts = serial_dtr_rts, |
| 718 | .activate = serial_activate, | ||
| 728 | }; | 719 | }; |
| 729 | 720 | ||
| 730 | int usb_serial_probe(struct usb_interface *interface, | 721 | int usb_serial_probe(struct usb_interface *interface, |
diff --git a/include/linux/tty.h b/include/linux/tty.h index f0f43d08d8b8..6352ac257fcb 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
| @@ -190,9 +190,15 @@ struct tty_port_operations { | |||
| 190 | /* Control the DTR line */ | 190 | /* Control the DTR line */ |
| 191 | void (*dtr_rts)(struct tty_port *port, int raise); | 191 | void (*dtr_rts)(struct tty_port *port, int raise); |
| 192 | /* Called when the last close completes or a hangup finishes | 192 | /* Called when the last close completes or a hangup finishes |
| 193 | IFF the port was initialized. Do not use to free resources */ | 193 | IFF the port was initialized. Do not use to free resources. Called |
| 194 | under the port mutex to serialize against activate/shutdowns */ | ||
| 194 | void (*shutdown)(struct tty_port *port); | 195 | void (*shutdown)(struct tty_port *port); |
| 195 | void (*drop)(struct tty_port *port); | 196 | void (*drop)(struct tty_port *port); |
| 197 | /* Called under the port mutex from tty_port_open, serialized using | ||
| 198 | the port mutex */ | ||
| 199 | /* FIXME: long term getting the tty argument *out* of this would be | ||
| 200 | good for consoles */ | ||
| 201 | int (*activate)(struct tty_port *port, struct tty_struct *tty); | ||
| 196 | }; | 202 | }; |
| 197 | 203 | ||
| 198 | struct tty_port { | 204 | struct tty_port { |
| @@ -467,6 +473,8 @@ extern int tty_port_close_start(struct tty_port *port, | |||
| 467 | extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty); | 473 | extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty); |
| 468 | extern void tty_port_close(struct tty_port *port, | 474 | extern void tty_port_close(struct tty_port *port, |
| 469 | struct tty_struct *tty, struct file *filp); | 475 | struct tty_struct *tty, struct file *filp); |
| 476 | extern int tty_port_open(struct tty_port *port, | ||
| 477 | struct tty_struct *tty, struct file *filp); | ||
| 470 | extern inline int tty_port_users(struct tty_port *port) | 478 | extern inline int tty_port_users(struct tty_port *port) |
| 471 | { | 479 | { |
| 472 | return port->count + port->blocked_open; | 480 | return port->count + port->blocked_open; |
