From: James S. <jsi...@us...> - 2005-03-30 03:23:23
|
Update of /cvsroot/linuxconsole/ruby/ruby-2.6/drivers/char In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1059 Modified Files: tty_io.c Log Message: Synced to linus tree Index: tty_io.c =================================================================== RCS file: /cvsroot/linuxconsole/ruby/ruby-2.6/drivers/char/tty_io.c,v retrieving revision 1.13 retrieving revision 1.14 diff -u -d -r1.13 -r1.14 --- tty_io.c 22 Oct 2004 07:46:16 -0000 1.13 +++ tty_io.c 30 Mar 2005 03:23:03 -0000 1.14 @@ -81,7 +81,6 @@ #include <linux/console.h> #include <linux/timer.h> #include <linux/ctype.h> -#include <linux/kd.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/slab.h> @@ -93,14 +92,12 @@ #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> -#include <linux/selection.h> #include <linux/devfs_fs_kernel.h> #include <linux/kmod.h> @@ -136,6 +133,7 @@ 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); @@ -147,7 +145,6 @@ 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); @@ -168,6 +165,7 @@ static inline void free_tty_struct(struct tty_struct *tty) { + kfree(tty->write_buf); kfree(tty); } @@ -248,7 +246,7 @@ * callers who will do ldisc lookups and cannot sleep. */ -static spinlock_t tty_ldisc_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(tty_ldisc_lock); static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); static struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */ @@ -326,7 +324,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_put); -void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) +static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) { tty->ldisc = *ld; tty->ldisc.refcount = 0; @@ -582,7 +580,7 @@ /* * This routine returns a tty driver structure, given a device number */ -struct tty_driver *get_tty_driver(dev_t device, int *index) +static struct tty_driver *get_tty_driver(dev_t device, int *index) { struct tty_driver *p; @@ -689,7 +687,7 @@ .release = tty_release, }; -static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(redirect_lock); static struct file *redirect; /** @@ -743,7 +741,7 @@ * but doesn't hold any locks, so we need to make sure we have the appropriate * locks for what we're doing.. */ -void do_tty_hangup(void *data) +static void do_tty_hangup(void *data) { struct tty_struct *tty = (struct tty_struct *) data; struct file * cons_filp = NULL; @@ -917,9 +915,11 @@ 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 { @@ -927,6 +927,7 @@ 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; } @@ -936,15 +937,19 @@ 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(); } @@ -1010,7 +1015,7 @@ tty_ldisc_deref(ld); unlock_kernel(); if (i > 0) - inode->i_atime = CURRENT_TIME; + inode->i_atime = current_fs_time(inode->i_sb); return i; } @@ -1019,44 +1024,81 @@ * denial-of-service type attacks */ static inline ssize_t do_tty_write( - ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char __user *, size_t), + ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t), struct tty_struct *tty, struct file *file, - const unsigned char __user *buf, + const char __user *buf, size_t count) { ssize_t ret = 0, written = 0; + unsigned int chunk; if (down_interruptible(&tty->atomic_write)) { return -ERESTARTSYS; } - if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) { + + /* + * 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.. + * + * The default chunk-size is 2kB, because the NTTY + * layer has problems with bigger chunks. It will + * claim to be able to handle more characters than + * it actually does. + */ + chunk = 2048; + 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; lock_kernel(); - written = write(tty, file, buf, count); + ret = write(tty, file, tty->write_buf, size); unlock_kernel(); - } 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 (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; + struct inode *inode = file->f_dentry->d_inode; + inode->i_mtime = current_fs_time(inode->i_sb); ret = written; } up(&tty->atomic_write); @@ -1082,8 +1124,7 @@ if (!ld->write) ret = -EIO; else - ret = do_tty_write(ld->write, tty, file, - (const unsigned char __user *)buf, count); + ret = do_tty_write(ld->write, tty, file, buf, count); tty_ldisc_deref(ld); return ret; } @@ -1140,12 +1181,6 @@ 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); @@ -1334,7 +1369,6 @@ /* All paths come through here to release the semaphore */ end_init: - up(&tty_sem); return retval; /* Release locally allocated memory ... nothing placed in slots */ @@ -1530,9 +1564,14 @@ * 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) { @@ -1568,6 +1607,8 @@ * 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 " @@ -1581,7 +1622,8 @@ 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 @@ -1728,10 +1770,14 @@ noctty = filp->f_flags & O_NOCTTY; index = -1; retval = 0; + + down(&tty_sem); if (device == MKDEV(TTYAUX_MAJOR,0)) { - if (!current->signal->tty) + if (!current->signal->tty) { + up(&tty_sem); return -ENXIO; + } driver = current->signal->tty->driver; index = current->signal->tty->index; filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ @@ -1765,14 +1811,18 @@ noctty = 1; goto got_driver; } + up(&tty_sem); return -ENODEV; } driver = get_tty_driver(device, &index); - if (!driver) + if (!driver) { + up(&tty_sem); return -ENODEV; + } got_driver: retval = init_dev(driver, index, &tty); + up(&tty_sem); if (retval) return retval; @@ -1810,7 +1860,7 @@ /* * Need to reset f_op in case a hangup happened. */ - /*if (filp->f_op == &hung_up_tty_fops) FIXME brave ruby*/ + if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; goto retry_open; } @@ -1858,7 +1908,10 @@ } up(&allocated_ptys_lock); + down(&tty_sem); retval = init_dev(ptm_driver, index, &tty); + up(&tty_sem); + if (retval) goto out; @@ -1969,14 +2022,11 @@ return 0; #ifdef CONFIG_VT if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) { - struct vc_data *vc = (struct vc_data *) tty->driver_data; - int rc = 0; + int rc; - if (!vc) { - acquire_console_sem(); - rc = vc_resize(vc, tmp_ws.ws_col, tmp_ws.ws_row); - release_console_sem(); - } + acquire_console_sem(); + rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row); + release_console_sem(); if (rc) return -ENXIO; } @@ -1992,10 +2042,10 @@ static int tioccons(struct file *file) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; if (file->f_op->write == redirected_tty_write) { struct file *f; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; spin_lock(&redirect_lock); f = redirect; redirect = NULL; @@ -2128,11 +2178,11 @@ static int send_break(struct tty_struct *tty, int duration) { - set_current_state(TASK_INTERRUPTIBLE); - tty->driver->break_ctl(tty, -1); - if (!signal_pending(current)) + if (!signal_pending(current)) { + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(duration); + } tty->driver->break_ctl(tty, 0); if (signal_pending(current)) return -EINTR; @@ -2484,28 +2534,6 @@ } /* - * Call the ldisc flush directly from a driver. This function may - * return an error and need retrying by the user. - */ - -int tty_push_data(struct tty_struct *tty, unsigned char *cp, unsigned char *fp, int count) -{ - int ret = 0; - struct tty_ldisc *disc; - - disc = tty_ldisc_ref(tty); - if(test_bit(TTY_DONT_FLIP, &tty->flags)) - ret = -EAGAIN; - else if(disc == NULL) - ret = -EIO; - else - disc->receive_buf(tty, cp, fp, count); - tty_ldisc_deref(disc); - return ret; - -} - -/* * Routine which returns the baud rate of the tty * * Note that the baud_table needs to be kept in sync with the @@ -2609,6 +2637,7 @@ tty->magic = TTY_MAGIC; tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); tty->pgrp = -1; + tty->overrun_time = jiffies; tty->flip.char_buf_ptr = tty->flip.char_buf; tty->flip.flag_buf_ptr = tty->flip.flag_buf; INIT_WORK(&tty->flip.work, flush_to_ldisc, tty); @@ -2629,7 +2658,7 @@ */ static void tty_default_put_char(struct tty_struct *tty, unsigned char ch) { - tty->driver->write(tty, 0, &ch, 1); + tty->driver->write(tty, &ch, 1); } static struct class_simple *tty_class; @@ -2883,8 +2912,8 @@ So I haven't moved it. dwmw2 */ rs_360_init(); #endif - call = &__con_initcall_start; - while (call < &__con_initcall_end) { + call = __con_initcall_start; + while (call < __con_initcall_end) { (*call)(); call++; } |