This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / char / tty_io.c
index 9935d1c..118dc73 100644 (file)
 #include <linux/device.h>
 #include <linux/idr.h>
 #include <linux/wait.h>
-#include <linux/bitops.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <asm/bitops.h>
 
 #include <linux/kbd_kern.h>
 #include <linux/vt_kern.h>
@@ -137,7 +137,6 @@ extern struct tty_driver *ptm_driver;       /* Unix98 pty masters; for /dev/ptmx */
 extern int pty_limit;          /* Config limit on Unix98 ptys */
 static DEFINE_IDR(allocated_ptys);
 static DECLARE_MUTEX(allocated_ptys_lock);
-static int ptmx_open(struct inode *, struct file *);
 #endif
 
 extern void disable_early_printk(void);
@@ -149,6 +148,7 @@ static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
 ssize_t redirected_tty_write(struct file *, const char __user *, size_t, loff_t *);
 static unsigned int tty_poll(struct file *, poll_table *);
 static int tty_open(struct inode *, struct file *);
+static int ptmx_open(struct inode *, struct file *);
 static int tty_release(struct inode *, struct file *);
 int tty_ioctl(struct inode * inode, struct file * file,
              unsigned int cmd, unsigned long arg);
@@ -169,7 +169,6 @@ static struct tty_struct *alloc_tty_struct(void)
 
 static inline void free_tty_struct(struct tty_struct *tty)
 {
-       kfree(tty->write_buf);
        kfree(tty);
 }
 
@@ -919,11 +918,9 @@ void disassociate_ctty(int on_exit)
 
        lock_kernel();
 
-       down(&tty_sem);
        tty = current->signal->tty;
        if (tty) {
                tty_pgrp = tty->pgrp;
-               up(&tty_sem);
                if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
                        tty_vhangup(tty);
        } else {
@@ -931,7 +928,6 @@ void disassociate_ctty(int on_exit)
                        kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
                        kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
                }
-               up(&tty_sem);
                unlock_kernel();        
                return;
        }
@@ -941,19 +937,15 @@ void disassociate_ctty(int on_exit)
                        kill_pg(tty_pgrp, SIGCONT, on_exit);
        }
 
-       /* Must lock changes to tty_old_pgrp */
-       down(&tty_sem);
        current->signal->tty_old_pgrp = 0;
        tty->session = 0;
        tty->pgrp = -1;
 
-       /* Now clear signal->tty under the lock */
        read_lock(&tasklist_lock);
        do_each_task_pid(current->signal->session, PIDTYPE_SID, p) {
                p->signal->tty = NULL;
        } while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
        read_unlock(&tasklist_lock);
-       up(&tty_sem);
        unlock_kernel();
 }
 
@@ -1028,72 +1020,41 @@ static ssize_t tty_read(struct file * file, char __user * buf, size_t count,
  * denial-of-service type attacks
  */
 static inline ssize_t do_tty_write(
-       ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
+       ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char __user *, size_t),
        struct tty_struct *tty,
        struct file *file,
-       const char __user *buf,
+       const unsigned char __user *buf,
        size_t count)
 {
        ssize_t ret = 0, written = 0;
-       unsigned int chunk;
        
        if (down_interruptible(&tty->atomic_write)) {
                return -ERESTARTSYS;
        }
-
-       /*
-        * We chunk up writes into a temporary buffer. This
-        * simplifies low-level drivers immensely, since they
-        * don't have locking issues and user mode accesses.
-        *
-        * But if TTY_NO_WRITE_SPLIT is set, we should use a
-        * big chunk-size..
-        */
-       chunk = 4096;
-       if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
-               chunk = 65536;
-       if (count < chunk)
-               chunk = count;
-
-       /* write_buf/write_cnt is protected by the atomic_write semaphore */
-       if (tty->write_cnt < chunk) {
-               unsigned char *buf;
-
-               if (chunk < 1024)
-                       chunk = 1024;
-
-               buf = kmalloc(chunk, GFP_KERNEL);
-               if (!buf) {
-                       up(&tty->atomic_write);
-                       return -ENOMEM;
-               }
-               kfree(tty->write_buf);
-               tty->write_cnt = chunk;
-               tty->write_buf = buf;
-       }
-
-       /* Do the write .. */
-       for (;;) {
-               size_t size = count;
-               if (size > chunk)
-                       size = chunk;
-               ret = -EFAULT;
-               if (copy_from_user(tty->write_buf, buf, size))
-                       break;
+       if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) {
                lock_kernel();
-               ret = write(tty, file, tty->write_buf, size);
+               written = write(tty, file, buf, count);
                unlock_kernel();
-               if (ret <= 0)
-                       break;
-               written += ret;
-               buf += ret;
-               count -= ret;
-               if (!count)
-                       break;
-               ret = -ERESTARTSYS;
-               if (signal_pending(current))
-                       break;
-               cond_resched();
+       } else {
+               for (;;) {
+                       unsigned long size = max((unsigned long)PAGE_SIZE*2, 16384UL);
+                       if (size > count)
+                               size = count;
+                       lock_kernel();
+                       ret = write(tty, file, buf, size);
+                       unlock_kernel();
+                       if (ret <= 0)
+                               break;
+                       written += ret;
+                       buf += ret;
+                       count -= ret;
+                       if (!count)
+                               break;
+                       ret = -ERESTARTSYS;
+                       if (signal_pending(current))
+                               break;
+                       cond_resched();
+               }
        }
        if (written) {
                file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
@@ -1122,7 +1083,8 @@ static ssize_t tty_write(struct file * file, const char __user * buf, size_t cou
        if (!ld->write)
                ret = -EIO;
        else
-               ret = do_tty_write(ld->write, tty, file, buf, count);
+               ret = do_tty_write(ld->write, tty, file,
+                           (const unsigned char __user *)buf, count);
        tty_ldisc_deref(ld);
        return ret;
 }
@@ -1156,8 +1118,8 @@ static inline void pty_line_name(struct tty_driver *driver, int index, char *p)
        int i = index + driver->name_base;
        /* ->name is initialized to "ttyp", but "tty" is expected */
        sprintf(p, "%s%c%x",
-               driver->subtype == PTY_TYPE_SLAVE ? "pty" : driver->name,
-               ptychar[i >> 4 & 0xf], i & 0xf);
+                       driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
+                       ptychar[i >> 4 & 0xf], i & 0xf);
 }
 
 static inline void tty_line_name(struct tty_driver *driver, int index, char *p)
@@ -1179,6 +1141,12 @@ static int init_dev(struct tty_driver *driver, int idx,
        struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
        int retval=0;
 
+       /* 
+        * Check whether we need to acquire the tty semaphore to avoid
+        * race conditions.  For now, play it safe.
+        */
+       down(&tty_sem);
+
        /* check whether we're reopening an existing tty */
        if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
                tty = devpts_get_tty(idx);
@@ -1367,6 +1335,7 @@ success:
        
        /* All paths come through here to release the semaphore */
 end_init:
+       up(&tty_sem);
        return retval;
 
        /* Release locally allocated memory ... nothing placed in slots */
@@ -1562,14 +1531,9 @@ static void release_dev(struct file * filp)
         * each iteration we avoid any problems.
         */
        while (1) {
-               /* Guard against races with tty->count changes elsewhere and
-                  opens on /dev/tty */
-                  
-               down(&tty_sem);
                tty_closing = tty->count <= 1;
                o_tty_closing = o_tty &&
                        (o_tty->count <= (pty_master ? 1 : 0));
-               up(&tty_sem);
                do_sleep = 0;
 
                if (tty_closing) {
@@ -1605,8 +1569,6 @@ static void release_dev(struct file * filp)
         * both sides, and we've completed the last operation that could 
         * block, so it's safe to proceed with closing.
         */
-        
-       down(&tty_sem);
        if (pty_master) {
                if (--o_tty->count < 0) {
                        printk(KERN_WARNING "release_dev: bad pty slave count "
@@ -1620,8 +1582,7 @@ static void release_dev(struct file * filp)
                       tty->count, tty_name(tty, buf));
                tty->count = 0;
        }
-       up(&tty_sem);
-       
+
        /*
         * We've decremented tty->count, so we need to remove this file
         * descriptor off the tty->tty_files list; this serves two
@@ -1768,14 +1729,10 @@ retry_open:
        noctty = filp->f_flags & O_NOCTTY;
        index  = -1;
        retval = 0;
-       
-       down(&tty_sem);
 
        if (device == MKDEV(TTYAUX_MAJOR,0)) {
-               if (!current->signal->tty) {
-                       up(&tty_sem);
+               if (!current->signal->tty)
                        return -ENXIO;
-               }
                driver = current->signal->tty->driver;
                index = current->signal->tty->index;
                filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
@@ -1800,18 +1757,14 @@ retry_open:
                        noctty = 1;
                        goto got_driver;
                }
-               up(&tty_sem);
                return -ENODEV;
        }
 
        driver = get_tty_driver(device, &index);
-       if (!driver) {
-               up(&tty_sem);
+       if (!driver)
                return -ENODEV;
-       }
 got_driver:
        retval = init_dev(driver, index, &tty);
-       up(&tty_sem);
        if (retval)
                return retval;
 
@@ -1897,10 +1850,7 @@ static int ptmx_open(struct inode * inode, struct file * filp)
        }
        up(&allocated_ptys_lock);
 
-       down(&tty_sem);
        retval = init_dev(ptm_driver, index, &tty);
-       up(&tty_sem);
-       
        if (retval)
                goto out;
 
@@ -2674,7 +2624,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
  */
 static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
 {
-       tty->driver->write(tty, &ch, 1);
+       tty->driver->write(tty, 0, &ch, 1);
 }
 
 static struct class_simple *tty_class;