Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / char / pty.c
index 4abc4d9..34dd4c3 100644 (file)
@@ -11,7 +11,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/module.h>      /* For EXPORT_SYMBOL */
 
 #include <linux/errno.h>
 #include <linux/major.h>
 #include <linux/mm.h>
 #include <linux/init.h>
-#include <linux/devfs_fs_kernel.h>
 #include <linux/sysctl.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
 #include <linux/devpts_fs.h>
 
-#if defined(CONFIG_LEGACY_PTYS) || defined(CONFIG_UNIX98_PTYS)
-
-#ifdef CONFIG_LEGACY_PTYS
-static struct tty_driver *pty_driver, *pty_slave_driver;
-#endif
-
 /* These are global because they are accessed in tty_io.c */
 #ifdef CONFIG_UNIX98_PTYS
 struct tty_driver *ptm_driver;
-struct tty_driver *pts_driver;
+static struct tty_driver *pts_driver;
 #endif
 
 static void pty_close(struct tty_struct * tty, struct file * filp)
@@ -61,9 +53,9 @@ static void pty_close(struct tty_struct * tty, struct file * filp)
        if (!tty->link)
                return;
        tty->link->packet = 0;
+       set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
        wake_up_interruptible(&tty->link->read_wait);
        wake_up_interruptible(&tty->link->write_wait);
-       set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
        if (tty->driver->subtype == PTY_TYPE_MASTER) {
                set_bit(TTY_OTHER_CLOSED, &tty->flags);
 #ifdef CONFIG_UNIX98_PTYS
@@ -91,10 +83,7 @@ static void pty_unthrottle(struct tty_struct * tty)
        if (!o_tty)
                return;
 
-       if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-           o_tty->ldisc.write_wakeup)
-               (o_tty->ldisc.write_wakeup)(o_tty);
-       wake_up_interruptible(&o_tty->write_wait);
+       tty_wakeup(o_tty);
        set_bit(TTY_THROTTLED, &tty->flags);
 }
 
@@ -107,52 +96,23 @@ static void pty_unthrottle(struct tty_struct * tty)
  *   (2) avoid redundant copying for cases where count >> receive_room
  * N.B. Calls from user space may now return an error code instead of
  * a count.
+ *
+ * FIXME: Our pty_write method is called with our ldisc lock held but
+ * not our partners. We can't just take the other one blindly without
+ * risking deadlocks.
  */
-static int pty_write(struct tty_struct * tty, int from_user,
-                      const unsigned char *buf, int count)
+static int pty_write(struct tty_struct * tty, const unsigned char *buf, int count)
 {
        struct tty_struct *to = tty->link;
-       int     c=0, n, room;
-       char    *temp_buffer;
+       int     c;
 
        if (!to || tty->stopped)
                return 0;
 
-       if (from_user) {
-               down(&tty->flip.pty_sem);
-               temp_buffer = &tty->flip.char_buf[0];
-               while (count > 0) {
-                       /* check space so we don't copy needlessly */ 
-                       n = to->ldisc.receive_room(to);
-                       if (n > count)
-                               n = count;
-                       if (!n) break;
-
-                       n  = min(n, PTY_BUF_SIZE);
-                       n -= copy_from_user(temp_buffer, buf, n);
-                       if (!n) {
-                               if (!c)
-                                       c = -EFAULT;
-                               break;
-                       }
-
-                       /* check again in case the buffer filled up */
-                       room = to->ldisc.receive_room(to);
-                       if (n > room)
-                               n = room;
-                       if (!n) break;
-                       buf   += n; 
-                       c     += n;
-                       count -= n;
-                       to->ldisc.receive_buf(to, temp_buffer, 0, n);
-               }
-               up(&tty->flip.pty_sem);
-       } else {
-               c = to->ldisc.receive_room(to);
-               if (c > count)
-                       c = count;
-               to->ldisc.receive_buf(to, buf, 0, c);
-       }
+       c = to->receive_room;
+       if (c > count)
+               c = count;
+       to->ldisc.receive_buf(to, buf, NULL, c);
        
        return c;
 }
@@ -164,7 +124,7 @@ static int pty_write_room(struct tty_struct *tty)
        if (!to || tty->stopped)
                return 0;
 
-       return to->ldisc.receive_room(to);
+       return to->receive_room;
 }
 
 /*
@@ -189,6 +149,7 @@ static int pty_chars_in_buffer(struct tty_struct *tty)
        struct tty_struct *to = tty->link;
        int count;
 
+       /* We should get the line discipline lock for "tty->link" */
        if (!to || !to->ldisc.chars_in_buffer)
                return 0;
 
@@ -205,21 +166,8 @@ static int pty_chars_in_buffer(struct tty_struct *tty)
        return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
 }
 
-/* 
- * Return the device number of a Unix98 PTY (only!).  This lets us open a
- * master pty with the multi-headed ptmx device, then find out which
- * one we got after it is open, with an ioctl.
- */
-#ifdef CONFIG_UNIX98_PTYS
-static int pty_get_device_number(struct tty_struct *tty, unsigned int *value)
-{
-       unsigned int result = tty->index;
-       return put_user(result, value);
-}
-#endif
-
 /* Set the lock flag on a pty */
-static int pty_set_lock(struct tty_struct *tty, int * arg)
+static int pty_set_lock(struct tty_struct *tty, int __user * arg)
 {
        int val;
        if (get_user(val,arg))
@@ -231,41 +179,6 @@ static int pty_set_lock(struct tty_struct *tty, int * arg)
        return 0;
 }
 
-#ifdef CONFIG_LEGACY_PTYS
-static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file,
-                        unsigned int cmd, unsigned long arg)
-{
-       if (!tty) {
-               printk("pty_ioctl called with NULL tty!\n");
-               return -EIO;
-       }
-       switch(cmd) {
-       case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
-               return pty_set_lock(tty, (int *) arg);
-       }
-       return -ENOIOCTLCMD;
-}
-#endif
-
-#ifdef CONFIG_UNIX98_PTYS
-static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
-                           unsigned int cmd, unsigned long arg)
-{
-       if (!tty) {
-               printk("pty_unix98_ioctl called with NULL tty!\n");
-               return -EIO;
-       }
-       switch(cmd) {
-       case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
-               return pty_set_lock(tty, (int *)arg);
-       case TIOCGPTN: /* Get PT Number */
-               return pty_get_device_number(tty, (unsigned int *)arg);
-       }
-
-       return -ENOIOCTLCMD;
-}
-#endif
-
 static void pty_flush_buffer(struct tty_struct *tty)
 {
        struct tty_struct *to = tty->link;
@@ -322,42 +235,22 @@ static struct tty_operations pty_ops = {
        .set_termios = pty_set_termios,
 };
 
-/* sysctl support for setting limits on the number of Unix98 ptys allocated.
-   Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. */
-#ifdef CONFIG_UNIX98_PTYS
-int pty_limit = NR_UNIX98_PTY_DEFAULT;
-static int pty_limit_min = 0;
-static int pty_limit_max = NR_UNIX98_PTY_MAX;
+/* Traditional BSD devices */
+#ifdef CONFIG_LEGACY_PTYS
+static struct tty_driver *pty_driver, *pty_slave_driver;
 
-ctl_table pty_table[] = {
-       {
-               .ctl_name       = PTY_MAX,
-               .procname       = "max",
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .data           = &pty_limit,
-               .proc_handler   = &proc_dointvec_minmax,
-               .strategy       = &sysctl_intvec,
-               .extra1         = &pty_limit_min,
-               .extra2         = &pty_limit_max,
-       }, {
-               .ctl_name       = PTY_NR,
-               .procname       = "nr",
-               .maxlen         = sizeof(int),
-               .mode           = 0444,
-               .proc_handler   = &proc_dointvec,
-       }, {
-               .ctl_name       = 0
+static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
+               return pty_set_lock(tty, (int __user *) arg);
        }
-};
-#endif
-
-/* Initialization */
+       return -ENOIOCTLCMD;
+}
 
-static int __init pty_init(void)
+static void __init legacy_pty_init(void)
 {
-#ifdef CONFIG_LEGACY_PTYS
-       /* Traditional BSD devices */
 
        pty_driver = alloc_tty_driver(NR_PTYS);
        if (!pty_driver)
@@ -370,7 +263,6 @@ static int __init pty_init(void)
        pty_driver->owner = THIS_MODULE;
        pty_driver->driver_name = "pty_master";
        pty_driver->name = "pty";
-       pty_driver->devfs_name = "pty/m";
        pty_driver->major = PTY_MASTER_MAJOR;
        pty_driver->minor_start = 0;
        pty_driver->type = TTY_DRIVER_TYPE_PTY;
@@ -388,7 +280,6 @@ static int __init pty_init(void)
        pty_slave_driver->owner = THIS_MODULE;
        pty_slave_driver->driver_name = "pty_slave";
        pty_slave_driver->name = "ttyp";
-       pty_slave_driver->devfs_name = "pty/s";
        pty_slave_driver->major = PTY_SLAVE_MAJOR;
        pty_slave_driver->minor_start = 0;
        pty_slave_driver->type = TTY_DRIVER_TYPE_PTY;
@@ -404,12 +295,58 @@ static int __init pty_init(void)
                panic("Couldn't register pty driver");
        if (tty_register_driver(pty_slave_driver))
                panic("Couldn't register pty slave driver");
+}
+#else
+static inline void legacy_pty_init(void) { }
+#endif
 
-#endif /* CONFIG_LEGACY_PTYS */
-
+/* Unix98 devices */
 #ifdef CONFIG_UNIX98_PTYS
-       /* Unix98 devices */
-       devfs_mk_dir("pts");
+/*
+ * sysctl support for setting limits on the number of Unix98 ptys allocated.
+ * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly.
+ */
+int pty_limit = NR_UNIX98_PTY_DEFAULT;
+static int pty_limit_min = 0;
+static int pty_limit_max = NR_UNIX98_PTY_MAX;
+
+ctl_table pty_table[] = {
+       {
+               .ctl_name       = PTY_MAX,
+               .procname       = "max",
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .data           = &pty_limit,
+               .proc_handler   = &proc_dointvec_minmax,
+               .strategy       = &sysctl_intvec,
+               .extra1         = &pty_limit_min,
+               .extra2         = &pty_limit_max,
+       }, {
+               .ctl_name       = PTY_NR,
+               .procname       = "nr",
+               .maxlen         = sizeof(int),
+               .mode           = 0444,
+               .proc_handler   = &proc_dointvec,
+       }, {
+               .ctl_name       = 0
+       }
+};
+
+static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
+                           unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
+               return pty_set_lock(tty, (int __user *)arg);
+       case TIOCGPTN: /* Get PT Number */
+               return put_user(tty->index, (unsigned int __user *)arg);
+       }
+
+       return -ENOIOCTLCMD;
+}
+
+static void __init unix98_pty_init(void)
+{
        ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
        if (!ptm_driver)
                panic("Couldn't allocate Unix98 ptm driver");
@@ -430,7 +367,7 @@ static int __init pty_init(void)
        ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
        ptm_driver->init_termios.c_lflag = 0;
        ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
-               TTY_DRIVER_NO_DEVFS | TTY_DRIVER_DEVPTS_MEM;
+               TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM;
        ptm_driver->other = pts_driver;
        tty_set_operations(ptm_driver, &pty_ops);
        ptm_driver->ioctl = pty_unix98_ioctl;
@@ -445,7 +382,7 @@ static int __init pty_init(void)
        pts_driver->init_termios = tty_std_termios;
        pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
        pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
-               TTY_DRIVER_NO_DEVFS | TTY_DRIVER_DEVPTS_MEM;
+               TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM;
        pts_driver->other = ptm_driver;
        tty_set_operations(pts_driver, &pty_ops);
        
@@ -455,10 +392,15 @@ static int __init pty_init(void)
                panic("Couldn't register Unix98 pts driver");
 
        pty_table[1].data = &ptm_driver->refcount;
-#endif /* CONFIG_UNIX98_PTYS */
+}
+#else
+static inline void unix98_pty_init(void) { }
+#endif
 
+static int __init pty_init(void)
+{
+       legacy_pty_init();
+       unix98_pty_init();
        return 0;
 }
 module_init(pty_init);
-
-#endif /* CONFIG_LEGACY_PTYS || CONFIG_UNIX98_PTYS */