Pull percpu-dtc into release branch
[linux-drm-fsl-dcu.git] / drivers / char / tty_io.c
index 47a6eacb10bc4c0c49dc54357be1f7adf9e36fd7..7a32df59490750775b6df3b718e0b33be2ee8514 100644 (file)
@@ -154,7 +154,9 @@ static int tty_release(struct inode *, struct file *);
 int tty_ioctl(struct inode * inode, struct file * file,
              unsigned int cmd, unsigned long arg);
 static int tty_fasync(int fd, struct file * filp, int on);
-static void release_mem(struct tty_struct *tty, int idx);
+static void release_tty(struct tty_struct *tty, int idx);
+static struct pid *__proc_set_tty(struct task_struct *tsk,
+                               struct tty_struct *tty);
 
 /**
  *     alloc_tty_struct        -       allocate a tty object
@@ -1109,17 +1111,17 @@ int tty_check_change(struct tty_struct * tty)
 {
        if (current->signal->tty != tty)
                return 0;
-       if (tty->pgrp <= 0) {
-               printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n");
+       if (!tty->pgrp) {
+               printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n");
                return 0;
        }
-       if (process_group(current) == tty->pgrp)
+       if (task_pgrp(current) == tty->pgrp)
                return 0;
        if (is_ignored(SIGTTOU))
                return 0;
-       if (is_orphaned_pgrp(process_group(current)))
+       if (is_current_pgrp_orphaned())
                return -EIO;
-       (void) kill_pg(process_group(current), SIGTTOU, 1);
+       (void) kill_pgrp(task_pgrp(current), SIGTTOU, 1);
        return -ERESTARTSYS;
 }
 
@@ -1354,8 +1356,8 @@ static void do_tty_hangup(struct work_struct *work)
          tty_release is called */
        
        read_lock(&tasklist_lock);
-       if (tty->session > 0) {
-               do_each_task_pid(tty->session, PIDTYPE_SID, p) {
+       if (tty->session) {
+               do_each_pid_task(tty->session, PIDTYPE_SID, p) {
                        spin_lock_irq(&p->sighand->siglock);
                        if (p->signal->tty == tty)
                                p->signal->tty = NULL;
@@ -1365,16 +1367,19 @@ static void do_tty_hangup(struct work_struct *work)
                        }
                        __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
                        __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
-                       if (tty->pgrp > 0)
-                               p->signal->tty_old_pgrp = tty->pgrp;
+                       put_pid(p->signal->tty_old_pgrp);  /* A noop */
+                       if (tty->pgrp)
+                               p->signal->tty_old_pgrp = get_pid(tty->pgrp);
                        spin_unlock_irq(&p->sighand->siglock);
-               } while_each_task_pid(tty->session, PIDTYPE_SID, p);
+               } while_each_pid_task(tty->session, PIDTYPE_SID, p);
        }
        read_unlock(&tasklist_lock);
 
        tty->flags = 0;
-       tty->session = 0;
-       tty->pgrp = -1;
+       put_pid(tty->session);
+       put_pid(tty->pgrp);
+       tty->session = NULL;
+       tty->pgrp = NULL;
        tty->ctrl_status = 0;
        /*
         *      If one of the devices matches a console pointer, we
@@ -1459,12 +1464,12 @@ int tty_hung_up_p(struct file * filp)
 
 EXPORT_SYMBOL(tty_hung_up_p);
 
-static void session_clear_tty(pid_t session)
+static void session_clear_tty(struct pid *session)
 {
        struct task_struct *p;
-       do_each_task_pid(session, PIDTYPE_SID, p) {
+       do_each_pid_task(session, PIDTYPE_SID, p) {
                proc_clear_tty(p);
-       } while_each_task_pid(session, PIDTYPE_SID, p);
+       } while_each_pid_task(session, PIDTYPE_SID, p);
 }
 
 /**
@@ -1494,46 +1499,54 @@ static void session_clear_tty(pid_t session)
 void disassociate_ctty(int on_exit)
 {
        struct tty_struct *tty;
-       int tty_pgrp = -1;
-       int session;
+       struct pid *tty_pgrp = NULL;
 
        lock_kernel();
 
        mutex_lock(&tty_mutex);
        tty = get_current_tty();
        if (tty) {
-               tty_pgrp = tty->pgrp;
+               tty_pgrp = get_pid(tty->pgrp);
                mutex_unlock(&tty_mutex);
                /* XXX: here we race, there is nothing protecting tty */
                if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
                        tty_vhangup(tty);
-       } else {
-               pid_t old_pgrp = current->signal->tty_old_pgrp;
+       } else if (on_exit) {
+               struct pid *old_pgrp;
+               spin_lock_irq(&current->sighand->siglock);
+               old_pgrp = current->signal->tty_old_pgrp;
+               current->signal->tty_old_pgrp = NULL;
+               spin_unlock_irq(&current->sighand->siglock);
                if (old_pgrp) {
-                       kill_pg(old_pgrp, SIGHUP, on_exit);
-                       kill_pg(old_pgrp, SIGCONT, on_exit);
+                       kill_pgrp(old_pgrp, SIGHUP, on_exit);
+                       kill_pgrp(old_pgrp, SIGCONT, on_exit);
+                       put_pid(old_pgrp);
                }
                mutex_unlock(&tty_mutex);
                unlock_kernel();        
                return;
        }
-       if (tty_pgrp > 0) {
-               kill_pg(tty_pgrp, SIGHUP, on_exit);
+       if (tty_pgrp) {
+               kill_pgrp(tty_pgrp, SIGHUP, on_exit);
                if (!on_exit)
-                       kill_pg(tty_pgrp, SIGCONT, on_exit);
+                       kill_pgrp(tty_pgrp, SIGCONT, on_exit);
+               put_pid(tty_pgrp);
        }
 
        spin_lock_irq(&current->sighand->siglock);
-       current->signal->tty_old_pgrp = 0;
-       session = process_session(current);
+       tty_pgrp = current->signal->tty_old_pgrp;
+       current->signal->tty_old_pgrp = NULL;
        spin_unlock_irq(&current->sighand->siglock);
+       put_pid(tty_pgrp);
 
        mutex_lock(&tty_mutex);
        /* It is possible that do_tty_hangup has free'd this tty */
        tty = get_current_tty();
        if (tty) {
-               tty->session = 0;
-               tty->pgrp = 0;
+               put_pid(tty->session);
+               put_pid(tty->pgrp);
+               tty->session = NULL;
+               tty->pgrp = NULL;
        } else {
 #ifdef TTY_DEBUG_HANGUP
                printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
@@ -1544,7 +1557,7 @@ void disassociate_ctty(int on_exit)
 
        /* Now clear signal->tty under the lock */
        read_lock(&tasklist_lock);
-       session_clear_tty(session);
+       session_clear_tty(task_session(current));
        read_unlock(&tasklist_lock);
        unlock_kernel();
 }
@@ -1612,7 +1625,6 @@ void start_tty(struct tty_struct *tty)
 
        /* If we have a running line discipline it may need kicking */
        tty_wakeup(tty);
-       wake_up_interruptible(&tty->write_wait);
 }
 
 EXPORT_SYMBOL(start_tty);
@@ -1891,6 +1903,20 @@ static int init_dev(struct tty_driver *driver, int idx,
        /* check whether we're reopening an existing tty */
        if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
                tty = devpts_get_tty(idx);
+               /*
+                * If we don't have a tty here on a slave open, it's because
+                * the master already started the close process and there's
+                * no relation between devpts file and tty anymore.
+                */
+               if (!tty && driver->subtype == PTY_TYPE_SLAVE) {
+                       retval = -EIO;
+                       goto end_init;
+               }
+               /*
+                * It's safe from now on because init_dev() is called with
+                * tty_mutex held and release_dev() won't change tty->count
+                * or tty->flags without having to grab tty_mutex
+                */
                if (tty && driver->subtype == PTY_TYPE_MASTER)
                        tty = tty->link;
        } else {
@@ -2003,7 +2029,7 @@ static int init_dev(struct tty_driver *driver, int idx,
 
        /* 
         * All structures have been allocated, so now we install them.
-        * Failures after this point use release_mem to clean up, so 
+        * Failures after this point use release_tty to clean up, so
         * there's no need to null out the local pointers.
         */
        if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
@@ -2024,8 +2050,8 @@ static int init_dev(struct tty_driver *driver, int idx,
 
        /* 
         * Structures all installed ... call the ldisc open routines.
-        * If we fail here just call release_mem to clean up.  No need
-        * to decrement the use counts, as release_mem doesn't care.
+        * If we fail here just call release_tty to clean up.  No need
+        * to decrement the use counts, as release_tty doesn't care.
         */
 
        if (tty->ldisc.open) {
@@ -2095,17 +2121,17 @@ fail_no_mem:
        retval = -ENOMEM;
        goto end_init;
 
-       /* call the tty release_mem routine to clean out this slot */
+       /* call the tty release_tty routine to clean out this slot */
 release_mem_out:
        if (printk_ratelimit())
                printk(KERN_INFO "init_dev: ldisc open failed, "
                                 "clearing slot %d\n", idx);
-       release_mem(tty, idx);
+       release_tty(tty, idx);
        goto end_init;
 }
 
 /**
- *     release_mem             -       release tty structure memory
+ *     release_one_tty         -       release tty structure memory
  *
  *     Releases memory associated with a tty structure, and clears out the
  *     driver table slots. This function is called when a device is no longer
@@ -2117,37 +2143,14 @@ release_mem_out:
  *     of ttys that the driver keeps.
  *             FIXME: should we require tty_mutex is held here ??
  */
-
-static void release_mem(struct tty_struct *tty, int idx)
+static void release_one_tty(struct tty_struct *tty, int idx)
 {
-       struct tty_struct *o_tty;
-       struct ktermios *tp;
        int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
-
-       if ((o_tty = tty->link) != NULL) {
-               if (!devpts)
-                       o_tty->driver->ttys[idx] = NULL;
-               if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-                       tp = o_tty->termios;
-                       if (!devpts)
-                               o_tty->driver->termios[idx] = NULL;
-                       kfree(tp);
-
-                       tp = o_tty->termios_locked;
-                       if (!devpts)
-                               o_tty->driver->termios_locked[idx] = NULL;
-                       kfree(tp);
-               }
-               o_tty->magic = 0;
-               o_tty->driver->refcount--;
-               file_list_lock();
-               list_del_init(&o_tty->tty_files);
-               file_list_unlock();
-               free_tty_struct(o_tty);
-       }
+       struct ktermios *tp;
 
        if (!devpts)
                tty->driver->ttys[idx] = NULL;
+
        if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
                tp = tty->termios;
                if (!devpts)
@@ -2160,15 +2163,39 @@ static void release_mem(struct tty_struct *tty, int idx)
                kfree(tp);
        }
 
+
        tty->magic = 0;
        tty->driver->refcount--;
+
        file_list_lock();
        list_del_init(&tty->tty_files);
        file_list_unlock();
-       module_put(tty->driver->owner);
+
        free_tty_struct(tty);
 }
 
+/**
+ *     release_tty             -       release tty structure memory
+ *
+ *     Release both @tty and a possible linked partner (think pty pair),
+ *     and decrement the refcount of the backing module.
+ *
+ *     Locking:
+ *             tty_mutex - sometimes only
+ *             takes the file list lock internally when working on the list
+ *     of ttys that the driver keeps.
+ *             FIXME: should we require tty_mutex is held here ??
+ */
+static void release_tty(struct tty_struct *tty, int idx)
+{
+       struct tty_driver *driver = tty->driver;
+
+       if (tty->link)
+               release_one_tty(tty->link, idx);
+       release_one_tty(tty, idx);
+       module_put(driver->owner);
+}
+
 /*
  * Even releasing the tty structures is a tricky business.. We have
  * to be very careful that the structures are all released at the
@@ -2436,10 +2463,10 @@ static void release_dev(struct file * filp)
                tty_set_termios_ldisc(o_tty,N_TTY); 
        }
        /*
-        * The release_mem function takes care of the details of clearing
+        * The release_tty function takes care of the details of clearing
         * the slots and preserving the termios structure.
         */
-       release_mem(tty, idx);
+       release_tty(tty, idx);
 
 #ifdef CONFIG_UNIX98_PTYS
        /* Make this pty number available for reallocation */
@@ -2481,6 +2508,7 @@ static int tty_open(struct inode * inode, struct file * filp)
        int index;
        dev_t device = inode->i_rdev;
        unsigned short saved_flags = filp->f_flags;
+       struct pid *old_pgrp;
 
        nonseekable_open(inode, filp);
        
@@ -2574,15 +2602,17 @@ got_driver:
                goto retry_open;
        }
 
+       old_pgrp = NULL;
        mutex_lock(&tty_mutex);
        spin_lock_irq(&current->sighand->siglock);
        if (!noctty &&
            current->signal->leader &&
            !current->signal->tty &&
-           tty->session == 0)
-               __proc_set_tty(current, tty);
+           tty->session == NULL)
+               old_pgrp = __proc_set_tty(current, tty);
        spin_unlock_irq(&current->sighand->siglock);
        mutex_unlock(&tty_mutex);
+       put_pid(old_pgrp);
        return 0;
 }
 
@@ -2721,9 +2751,18 @@ static int tty_fasync(int fd, struct file * filp, int on)
                return retval;
 
        if (on) {
+               enum pid_type type;
+               struct pid *pid;
                if (!waitqueue_active(&tty->read_wait))
                        tty->minimum_to_wake = 1;
-               retval = f_setown(filp, (-tty->pgrp) ? : current->pid, 0);
+               if (tty->pgrp) {
+                       pid = tty->pgrp;
+                       type = PIDTYPE_PGID;
+               } else {
+                       pid = task_pid(current);
+                       type = PIDTYPE_PID;
+               }
+               retval = __f_setown(filp, pid, type, 0);
                if (retval)
                        return retval;
        } else {
@@ -2825,10 +2864,10 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
                }
        }
 #endif
-       if (tty->pgrp > 0)
-               kill_pg(tty->pgrp, SIGWINCH, 1);
-       if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0))
-               kill_pg(real_tty->pgrp, SIGWINCH, 1);
+       if (tty->pgrp)
+               kill_pgrp(tty->pgrp, SIGWINCH, 1);
+       if ((real_tty->pgrp != tty->pgrp) && real_tty->pgrp)
+               kill_pgrp(real_tty->pgrp, SIGWINCH, 1);
        tty->winsize = tmp_ws;
        real_tty->winsize = tmp_ws;
 done:
@@ -2913,8 +2952,7 @@ static int fionbio(struct file *file, int __user *p)
 static int tiocsctty(struct tty_struct *tty, int arg)
 {
        int ret = 0;
-       if (current->signal->leader &&
-                       (process_session(current) == tty->session))
+       if (current->signal->leader && (task_session(current) == tty->session))
                return ret;
 
        mutex_lock(&tty_mutex);
@@ -2927,7 +2965,7 @@ static int tiocsctty(struct tty_struct *tty, int arg)
                goto unlock;
        }
 
-       if (tty->session > 0) {
+       if (tty->session) {
                /*
                 * This tty is already the controlling
                 * tty for another session group!
@@ -2970,7 +3008,7 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
         */
        if (tty == real_tty && current->signal->tty != real_tty)
                return -ENOTTY;
-       return put_user(real_tty->pgrp, p);
+       return put_user(pid_nr(real_tty->pgrp), p);
 }
 
 /**
@@ -2987,7 +3025,8 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
 
 static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
 {
-       pid_t pgrp;
+       struct pid *pgrp;
+       pid_t pgrp_nr;
        int retval = tty_check_change(real_tty);
 
        if (retval == -EIO)
@@ -2996,16 +3035,26 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
                return retval;
        if (!current->signal->tty ||
            (current->signal->tty != real_tty) ||
-           (real_tty->session != process_session(current)))
+           (real_tty->session != task_session(current)))
                return -ENOTTY;
-       if (get_user(pgrp, p))
+       if (get_user(pgrp_nr, p))
                return -EFAULT;
-       if (pgrp < 0)
+       if (pgrp_nr < 0)
                return -EINVAL;
-       if (session_of_pgrp(pgrp) != process_session(current))
-               return -EPERM;
-       real_tty->pgrp = pgrp;
-       return 0;
+       rcu_read_lock();
+       pgrp = find_pid(pgrp_nr);
+       retval = -ESRCH;
+       if (!pgrp)
+               goto out_unlock;
+       retval = -EPERM;
+       if (session_of_pgrp(pgrp) != task_session(current))
+               goto out_unlock;
+       retval = 0;
+       put_pid(real_tty->pgrp);
+       real_tty->pgrp = get_pid(pgrp);
+out_unlock:
+       rcu_read_unlock();
+       return retval;
 }
 
 /**
@@ -3028,9 +3077,9 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _
        */
        if (tty == real_tty && current->signal->tty != real_tty)
                return -ENOTTY;
-       if (real_tty->session <= 0)
+       if (!real_tty->session)
                return -ENOTTY;
-       return put_user(real_tty->session, p);
+       return put_user(pid_nr(real_tty->session), p);
 }
 
 /**
@@ -3324,15 +3373,13 @@ int tty_ioctl(struct inode * inode, struct file * file,
  * Nasty bug: do_SAK is being called in interrupt context.  This can
  * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
  */
-static void __do_SAK(struct work_struct *work)
+void __do_SAK(struct tty_struct *tty)
 {
-       struct tty_struct *tty =
-               container_of(work, struct tty_struct, SAK_work);
 #ifdef TTY_SOFT_SAK
        tty_hangup(tty);
 #else
        struct task_struct *g, *p;
-       int session;
+       struct pid *session;
        int             i;
        struct file     *filp;
        struct fdtable *fdt;
@@ -3348,12 +3395,12 @@ static void __do_SAK(struct work_struct *work)
        
        read_lock(&tasklist_lock);
        /* Kill the entire session */
-       do_each_task_pid(session, PIDTYPE_SID, p) {
+       do_each_pid_task(session, PIDTYPE_SID, p) {
                printk(KERN_NOTICE "SAK: killed process %d"
                        " (%s): process_session(p)==tty->session\n",
                        p->pid, p->comm);
                send_sig(SIGKILL, p, 1);
-       } while_each_task_pid(session, PIDTYPE_SID, p);
+       } while_each_pid_task(session, PIDTYPE_SID, p);
        /* Now kill any processes that happen to have the
         * tty open.
         */
@@ -3394,6 +3441,13 @@ static void __do_SAK(struct work_struct *work)
 #endif
 }
 
+static void do_SAK_work(struct work_struct *work)
+{
+       struct tty_struct *tty =
+               container_of(work, struct tty_struct, SAK_work);
+       __do_SAK(tty);
+}
+
 /*
  * The tq handling here is a little racy - tty->SAK_work may already be queued.
  * Fortunately we don't need to worry, because if ->SAK_work is already queued,
@@ -3404,7 +3458,6 @@ void do_SAK(struct tty_struct *tty)
 {
        if (!tty)
                return;
-       PREPARE_WORK(&tty->SAK_work, __do_SAK);
        schedule_work(&tty->SAK_work);
 }
 
@@ -3515,7 +3568,8 @@ static void initialize_tty_struct(struct tty_struct *tty)
        memset(tty, 0, sizeof(struct tty_struct));
        tty->magic = TTY_MAGIC;
        tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
-       tty->pgrp = -1;
+       tty->session = NULL;
+       tty->pgrp = NULL;
        tty->overrun_time = jiffies;
        tty->buf.head = tty->buf.tail = NULL;
        tty_buffer_init(tty);
@@ -3529,7 +3583,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
        mutex_init(&tty->atomic_write_lock);
        spin_lock_init(&tty->read_lock);
        INIT_LIST_HEAD(&tty->tty_files);
-       INIT_WORK(&tty->SAK_work, NULL);
+       INIT_WORK(&tty->SAK_work, do_SAK_work);
 }
 
 /*
@@ -3675,15 +3729,14 @@ int tty_register_driver(struct tty_driver *driver)
 
        if (!driver->major) {
                error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
-                                               (char*)driver->name);
+                                               driver->name);
                if (!error) {
                        driver->major = MAJOR(dev);
                        driver->minor_start = MINOR(dev);
                }
        } else {
                dev = MKDEV(driver->major, driver->minor_start);
-               error = register_chrdev_region(dev, driver->num,
-                                               (char*)driver->name);
+               error = register_chrdev_region(dev, driver->num, driver->name);
        }
        if (error < 0) {
                kfree(p);
@@ -3786,21 +3839,31 @@ void proc_clear_tty(struct task_struct *p)
 }
 EXPORT_SYMBOL(proc_clear_tty);
 
-void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
+static struct pid *__proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
 {
+       struct pid *old_pgrp;
        if (tty) {
-               tty->session = process_session(tsk);
-               tty->pgrp = process_group(tsk);
+               /* We should not have a session or pgrp to here but.... */
+               put_pid(tty->session);
+               put_pid(tty->pgrp);
+               tty->session = get_pid(task_session(tsk));
+               tty->pgrp = get_pid(task_pgrp(tsk));
        }
+       old_pgrp = tsk->signal->tty_old_pgrp;
        tsk->signal->tty = tty;
-       tsk->signal->tty_old_pgrp = 0;
+       tsk->signal->tty_old_pgrp = NULL;
+       return old_pgrp;
 }
 
 void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
 {
+       struct pid *old_pgrp;
+
        spin_lock_irq(&tsk->sighand->siglock);
-       __proc_set_tty(tsk, tty);
+       old_pgrp = __proc_set_tty(tsk, tty);
        spin_unlock_irq(&tsk->sighand->siglock);
+
+       put_pid(old_pgrp);
 }
 
 struct tty_struct *get_current_tty(void)