tty_port: add "tty_port_open" helper
authorAlan Cox <alan@linux.intel.com>
Tue, 6 Oct 2009 15:06:11 +0000 (16:06 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Dec 2009 23:18:04 +0000 (15:18 -0800)
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>
drivers/char/tty_port.c
drivers/usb/serial/usb-serial.c
include/linux/tty.h

index c63f3d33914a7def6b4e49d2f3d33a25dbf8fc0c..b22a61a4fbe58c0f32a6df3cbc429655c6f2a4d9 100644 (file)
@@ -99,10 +99,11 @@ EXPORT_SYMBOL(tty_port_tty_set);
 
 static void tty_port_shutdown(struct tty_port *port)
 {
+       mutex_lock(&port->mutex);
        if (port->ops->shutdown &&
                test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags))
                        port->ops->shutdown(port);
-
+       mutex_unlock(&port->mutex);
 }
 
 /**
@@ -381,3 +382,36 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty,
        tty_port_tty_set(port, NULL);
 }
 EXPORT_SYMBOL(tty_port_close);
+
+int tty_port_open(struct tty_port *port, struct tty_struct *tty,
+                                                        struct file *filp)
+{
+       spin_lock_irq(&port->lock);
+       if (!tty_hung_up_p(filp))
+               ++port->count;
+       spin_unlock_irq(&port->lock);
+       tty_port_tty_set(port, tty);
+
+       /*
+        * Do the device-specific open only if the hardware isn't
+        * already initialized. Serialize open and shutdown using the
+        * port mutex.
+        */
+
+       mutex_lock(&port->mutex);
+
+       if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+               if (port->ops->activate) {
+                       int retval = port->ops->activate(port, tty);
+                       if (retval) {
+                               mutex_unlock(&port->mutex);
+                               return retval;
+                       }
+                }
+               set_bit(ASYNCB_INITIALIZED, &port->flags);
+       }
+       mutex_unlock(&port->mutex);
+       return tty_port_block_til_ready(port, tty, filp);
+}
+
+EXPORT_SYMBOL(tty_port_open);
index bd3fa7ff15b12166bf0cf2ad86f3214144f914c0..b0649d92251ff77baf8fcb4be7be1209b119b5cc 100644 (file)
@@ -247,41 +247,31 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
        return retval;
 }
 
-static int serial_open(struct tty_struct *tty, struct file *filp)
+static int serial_activate(struct tty_port *tport, struct tty_struct *tty)
 {
-       struct usb_serial_port *port = tty->driver_data;
+       struct usb_serial_port *port =
+               container_of(tport, struct usb_serial_port, port);
        struct usb_serial *serial = port->serial;
        int retval;
 
-       dbg("%s - port %d", __func__, port->number);
-
-       spin_lock_irq(&port->port.lock);
-       if (!tty_hung_up_p(filp))
-               ++port->port.count;
-       spin_unlock_irq(&port->port.lock);
-       tty_port_tty_set(&port->port, tty);
+       if (mutex_lock_interruptible(&port->mutex))
+               return -ERESTARTSYS;
+       mutex_lock(&serial->disc_mutex);
+       if (serial->disconnected)
+               retval = -ENODEV;
+       else
+               retval = port->serial->type->open(tty, port);
+       mutex_unlock(&serial->disc_mutex);
+       mutex_unlock(&port->mutex);
+       return retval;
+}
 
-       /* Do the device-specific open only if the hardware isn't
-        * already initialized.
-        */
-       if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
-               if (mutex_lock_interruptible(&port->mutex))
-                       return -ERESTARTSYS;
-               mutex_lock(&serial->disc_mutex);
-               if (serial->disconnected)
-                       retval = -ENODEV;
-               else
-                       retval = port->serial->type->open(tty, port);
-               mutex_unlock(&serial->disc_mutex);
-               mutex_unlock(&port->mutex);
-               if (retval)
-                       return retval;
-               set_bit(ASYNCB_INITIALIZED, &port->port.flags);
-       }
+static int serial_open(struct tty_struct *tty, struct file *filp)
+{
+       struct usb_serial_port *port = tty->driver_data;
 
-       /* Now do the correct tty layer semantics */
-       retval = tty_port_block_til_ready(&port->port, tty, filp);
-       return retval;
+       dbg("%s - port %d", __func__, port->number);
+       return tty_port_open(&port->port, tty, filp);
 }
 
 /**
@@ -725,6 +715,7 @@ static void serial_dtr_rts(struct tty_port *port, int on)
 static const struct tty_port_operations serial_port_ops = {
        .carrier_raised = serial_carrier_raised,
        .dtr_rts = serial_dtr_rts,
+       .activate = serial_activate,
 };
 
 int usb_serial_probe(struct usb_interface *interface,
index f0f43d08d8b8765911d51f681034841a73b1c239..6352ac257fcb78163adf8d8954b92c9ae5bbe4b0 100644 (file)
@@ -190,9 +190,15 @@ struct tty_port_operations {
        /* Control the DTR line */
        void (*dtr_rts)(struct tty_port *port, int raise);
        /* Called when the last close completes or a hangup finishes
-          IFF the port was initialized. Do not use to free resources */
+          IFF the port was initialized. Do not use to free resources. Called
+          under the port mutex to serialize against activate/shutdowns */
        void (*shutdown)(struct tty_port *port);
        void (*drop)(struct tty_port *port);
+       /* Called under the port mutex from tty_port_open, serialized using
+          the port mutex */
+        /* FIXME: long term getting the tty argument *out* of this would be
+           good for consoles */
+       int (*activate)(struct tty_port *port, struct tty_struct *tty);
 };
        
 struct tty_port {
@@ -467,6 +473,8 @@ extern int tty_port_close_start(struct tty_port *port,
 extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty);
 extern void tty_port_close(struct tty_port *port,
                                struct tty_struct *tty, struct file *filp);
+extern int tty_port_open(struct tty_port *port,
+                               struct tty_struct *tty, struct file *filp);
 extern inline int tty_port_users(struct tty_port *port)
 {
        return port->count + port->blocked_open;