]> Pileus Git - ~andy/linux/blobdiff - drivers/tty/tty_io.c
Merge tag 'arc-v3.10-rc1-part2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[~andy/linux] / drivers / tty / tty_io.c
index b0452688308c6605bfc64168c790a442bef1407d..6464029e4860968945840c252ad632b556f9f947 100644 (file)
@@ -532,6 +532,60 @@ void tty_wakeup(struct tty_struct *tty)
 
 EXPORT_SYMBOL_GPL(tty_wakeup);
 
+/**
+ *     tty_signal_session_leader       - sends SIGHUP to session leader
+ *     @tty            controlling tty
+ *     @exit_session   if non-zero, signal all foreground group processes
+ *
+ *     Send SIGHUP and SIGCONT to the session leader and its process group.
+ *     Optionally, signal all processes in the foreground process group.
+ *
+ *     Returns the number of processes in the session with this tty
+ *     as their controlling terminal. This value is used to drop
+ *     tty references for those processes.
+ */
+static int tty_signal_session_leader(struct tty_struct *tty, int exit_session)
+{
+       struct task_struct *p;
+       int refs = 0;
+       struct pid *tty_pgrp = NULL;
+
+       read_lock(&tasklist_lock);
+       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;
+                               /* We defer the dereferences outside fo
+                                  the tasklist lock */
+                               refs++;
+                       }
+                       if (!p->signal->leader) {
+                               spin_unlock_irq(&p->sighand->siglock);
+                               continue;
+                       }
+                       __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
+                       __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
+                       put_pid(p->signal->tty_old_pgrp);  /* A noop */
+                       spin_lock(&tty->ctrl_lock);
+                       tty_pgrp = get_pid(tty->pgrp);
+                       if (tty->pgrp)
+                               p->signal->tty_old_pgrp = get_pid(tty->pgrp);
+                       spin_unlock(&tty->ctrl_lock);
+                       spin_unlock_irq(&p->sighand->siglock);
+               } while_each_pid_task(tty->session, PIDTYPE_SID, p);
+       }
+       read_unlock(&tasklist_lock);
+
+       if (tty_pgrp) {
+               if (exit_session)
+                       kill_pgrp(tty_pgrp, SIGHUP, exit_session);
+               put_pid(tty_pgrp);
+       }
+
+       return refs;
+}
+
 /**
  *     __tty_hangup            -       actual handler for hangup events
  *     @work: tty device
@@ -554,15 +608,13 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
  *               tasklist_lock to walk task list for hangup event
  *                 ->siglock to protect ->signal/->sighand
  */
-static void __tty_hangup(struct tty_struct *tty)
+static void __tty_hangup(struct tty_struct *tty, int exit_session)
 {
        struct file *cons_filp = NULL;
        struct file *filp, *f = NULL;
-       struct task_struct *p;
        struct tty_file_private *priv;
        int    closecount = 0, n;
-       unsigned long flags;
-       int refs = 0;
+       int refs;
 
        if (!tty)
                return;
@@ -599,39 +651,18 @@ static void __tty_hangup(struct tty_struct *tty)
        }
        spin_unlock(&tty_files_lock);
 
+       refs = tty_signal_session_leader(tty, exit_session);
+       /* Account for the p->signal references we killed */
+       while (refs--)
+               tty_kref_put(tty);
+
        /*
         * it drops BTM and thus races with reopen
         * we protect the race by TTY_HUPPING
         */
        tty_ldisc_hangup(tty);
 
-       read_lock(&tasklist_lock);
-       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;
-                               /* We defer the dereferences outside fo
-                                  the tasklist lock */
-                               refs++;
-                       }
-                       if (!p->signal->leader) {
-                               spin_unlock_irq(&p->sighand->siglock);
-                               continue;
-                       }
-                       __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
-                       __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
-                       put_pid(p->signal->tty_old_pgrp);  /* A noop */
-                       spin_lock_irqsave(&tty->ctrl_lock, flags);
-                       if (tty->pgrp)
-                               p->signal->tty_old_pgrp = get_pid(tty->pgrp);
-                       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-                       spin_unlock_irq(&p->sighand->siglock);
-               } while_each_pid_task(tty->session, PIDTYPE_SID, p);
-       }
-       read_unlock(&tasklist_lock);
-
-       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       spin_lock_irq(&tty->ctrl_lock);
        clear_bit(TTY_THROTTLED, &tty->flags);
        clear_bit(TTY_PUSH, &tty->flags);
        clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
@@ -640,11 +671,7 @@ static void __tty_hangup(struct tty_struct *tty)
        tty->session = NULL;
        tty->pgrp = NULL;
        tty->ctrl_status = 0;
-       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-
-       /* Account for the p->signal references we killed */
-       while (refs--)
-               tty_kref_put(tty);
+       spin_unlock_irq(&tty->ctrl_lock);
 
        /*
         * If one of the devices matches a console pointer, we
@@ -666,7 +693,6 @@ static void __tty_hangup(struct tty_struct *tty)
         */
        set_bit(TTY_HUPPED, &tty->flags);
        clear_bit(TTY_HUPPING, &tty->flags);
-       tty_ldisc_enable(tty);
 
        tty_unlock(tty);
 
@@ -679,7 +705,7 @@ static void do_tty_hangup(struct work_struct *work)
        struct tty_struct *tty =
                container_of(work, struct tty_struct, hangup_work);
 
-       __tty_hangup(tty);
+       __tty_hangup(tty, 0);
 }
 
 /**
@@ -717,7 +743,7 @@ void tty_vhangup(struct tty_struct *tty)
 
        printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
 #endif
-       __tty_hangup(tty);
+       __tty_hangup(tty, 0);
 }
 
 EXPORT_SYMBOL(tty_vhangup);
@@ -740,6 +766,27 @@ void tty_vhangup_self(void)
        }
 }
 
+/**
+ *     tty_vhangup_session             -       hangup session leader exit
+ *     @tty: tty to hangup
+ *
+ *     The session leader is exiting and hanging up its controlling terminal.
+ *     Every process in the foreground process group is signalled SIGHUP.
+ *
+ *     We do this synchronously so that when the syscall returns the process
+ *     is complete. That guarantee is necessary for security reasons.
+ */
+
+static void tty_vhangup_session(struct tty_struct *tty)
+{
+#ifdef TTY_DEBUG_HANGUP
+       char    buf[64];
+
+       printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty, buf));
+#endif
+       __tty_hangup(tty, 1);
+}
+
 /**
  *     tty_hung_up_p           -       was tty hung up
  *     @filp: file pointer of tty
@@ -797,18 +844,18 @@ void disassociate_ctty(int on_exit)
 
        tty = get_current_tty();
        if (tty) {
-               struct pid *tty_pgrp = get_pid(tty->pgrp);
-               if (on_exit) {
-                       if (tty->driver->type != TTY_DRIVER_TYPE_PTY)
-                               tty_vhangup(tty);
-               }
-               tty_kref_put(tty);
-               if (tty_pgrp) {
-                       kill_pgrp(tty_pgrp, SIGHUP, on_exit);
-                       if (!on_exit)
+               if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) {
+                       tty_vhangup_session(tty);
+               } else {
+                       struct pid *tty_pgrp = tty_get_pgrp(tty);
+                       if (tty_pgrp) {
+                               kill_pgrp(tty_pgrp, SIGHUP, on_exit);
                                kill_pgrp(tty_pgrp, SIGCONT, on_exit);
-                       put_pid(tty_pgrp);
+                               put_pid(tty_pgrp);
+                       }
                }
+               tty_kref_put(tty);
+
        } else if (on_exit) {
                struct pid *old_pgrp;
                spin_lock_irq(&current->sighand->siglock);
@@ -941,10 +988,10 @@ void start_tty(struct tty_struct *tty)
 
 EXPORT_SYMBOL(start_tty);
 
+/* We limit tty time update visibility to every 8 seconds or so. */
 static void tty_update_time(struct timespec *time)
 {
-       unsigned long sec = get_seconds();
-       sec -= sec % 60;
+       unsigned long sec = get_seconds() & ~7;
        if ((long)(sec - time->tv_sec) > 0)
                time->tv_sec = sec;
 }
@@ -1358,9 +1405,7 @@ static int tty_reopen(struct tty_struct *tty)
        }
        tty->count++;
 
-       mutex_lock(&tty->ldisc_mutex);
        WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
-       mutex_unlock(&tty->ldisc_mutex);
 
        return 0;
 }
@@ -1477,6 +1522,17 @@ void tty_free_termios(struct tty_struct *tty)
 }
 EXPORT_SYMBOL(tty_free_termios);
 
+/**
+ *     tty_flush_works         -       flush all works of a tty
+ *     @tty: tty device to flush works for
+ *
+ *     Sync flush all works belonging to @tty.
+ */
+static void tty_flush_works(struct tty_struct *tty)
+{
+       flush_work(&tty->SAK_work);
+       flush_work(&tty->hangup_work);
+}
 
 /**
  *     release_one_tty         -       release tty structure memory
@@ -1562,6 +1618,7 @@ static void release_tty(struct tty_struct *tty, int idx)
        tty_free_termios(tty);
        tty_driver_remove_tty(tty->driver, tty);
        tty->port->itty = NULL;
+       cancel_work_sync(&tty->port->buf.work);
 
        if (tty->link)
                tty_kref_put(tty->link);
@@ -1791,12 +1848,21 @@ int tty_release(struct inode *inode, struct file *filp)
                return 0;
 
 #ifdef TTY_DEBUG_HANGUP
-       printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__);
+       printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty, buf));
 #endif
        /*
         * Ask the line discipline code to release its structures
         */
        tty_ldisc_release(tty, o_tty);
+
+       /* Wait for pending work before tty destruction commmences */
+       tty_flush_works(tty);
+       if (o_tty)
+               tty_flush_works(o_tty);
+
+#ifdef TTY_DEBUG_HANGUP
+       printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf));
+#endif
        /*
         * The release_tty function takes care of the details of clearing
         * the slots and preserving the termios structure. The tty_unlock_pair