[PATCH] tty: ->signal->tty locking
[powerpc.git] / drivers / char / tty_io.c
index b4f37c6..0c856a6 100644 (file)
@@ -126,9 +126,10 @@ EXPORT_SYMBOL(tty_std_termios);
    
 LIST_HEAD(tty_drivers);                        /* linked list of tty drivers */
 
-/* Semaphore to protect creating and releasing a tty. This is shared with
+/* Mutex to protect creating and releasing a tty. This is shared with
    vt.c for deeply disgusting hack reasons */
 DEFINE_MUTEX(tty_mutex);
+EXPORT_SYMBOL(tty_mutex);
 
 #ifdef CONFIG_UNIX98_PTYS
 extern struct tty_driver *ptm_driver;  /* Unix98 pty masters; for /dev/ptmx */
@@ -249,7 +250,7 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
                                    "!= #fd's(%d) in %s\n",
                       tty->name, tty->count, count, routine);
                return count;
-       }       
+       }
 #endif
        return 0;
 }
@@ -258,18 +259,6 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
  * Tty buffer allocation management
  */
 
-
-/**
- *     tty_buffer_free_all             -       free buffers used by a tty
- *     @tty: tty to free from
- *
- *     Remove all the buffers pending on a tty whether queued with data
- *     or in the free ring. Must be called when the tty is no longer in use
- *
- *     Locking: none
- */
-
-
 /**
  *     tty_buffer_free_all             -       free buffers used by a tty
  *     @tty: tty to free from
@@ -613,14 +602,14 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
  *     they are not on hot paths so a little discipline won't do 
  *     any harm.
  *
- *     Locking: takes termios_sem
+ *     Locking: takes termios_mutex
  */
  
 static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
 {
-       down(&tty->termios_sem);
+       mutex_lock(&tty->termios_mutex);
        tty->termios->c_line = num;
-       up(&tty->termios_sem);
+       mutex_unlock(&tty->termios_mutex);
 }
 
 /*
@@ -914,7 +903,7 @@ static void tty_ldisc_enable(struct tty_struct *tty)
  *     context.
  *
  *     Locking: takes tty_ldisc_lock.
- *             called functions take termios_sem
+ *              called functions take termios_mutex
  */
  
 static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
@@ -1253,7 +1242,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush);
        
 /**
  *     do_tty_hangup           -       actual handler for hangup events
- *     @data: tty device
+ *     @work: tty device
  *
  *     This can be called by the "eventd" kernel thread.  That is process
  *     synchronous but doesn't hold any locks, so we need to make sure we
@@ -1266,16 +1255,17 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush);
  *
  *     Locking:
  *             BKL
- *             redirect lock for undoing redirection
- *             file list lock for manipulating list of ttys
- *             tty_ldisc_lock from called functions
- *             termios_sem resetting termios data
- *             tasklist_lock to walk task list for hangup event
- *
+ *               redirect lock for undoing redirection
+ *               file list lock for manipulating list of ttys
+ *               tty_ldisc_lock from called functions
+ *               termios_mutex resetting termios data
+ *               tasklist_lock to walk task list for hangup event
+ *                 ->siglock to protect ->signal/->sighand
  */
-static void do_tty_hangup(void *data)
+static void do_tty_hangup(struct work_struct *work)
 {
-       struct tty_struct *tty = (struct tty_struct *) data;
+       struct tty_struct *tty =
+               container_of(work, struct tty_struct, hangup_work);
        struct file * cons_filp = NULL;
        struct file *filp, *f = NULL;
        struct task_struct *p;
@@ -1338,9 +1328,9 @@ static void do_tty_hangup(void *data)
         */
        if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
        {
-               down(&tty->termios_sem);
+               mutex_lock(&tty->termios_mutex);
                *tty->termios = tty->driver->init_termios;
-               up(&tty->termios_sem);
+               mutex_unlock(&tty->termios_mutex);
        }
        
        /* Defer ldisc switch */
@@ -1352,14 +1342,18 @@ static void do_tty_hangup(void *data)
        read_lock(&tasklist_lock);
        if (tty->session > 0) {
                do_each_task_pid(tty->session, PIDTYPE_SID, p) {
+                       spin_lock_irq(&p->sighand->siglock);
                        if (p->signal->tty == tty)
                                p->signal->tty = NULL;
-                       if (!p->signal->leader)
+                       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);
+                       }
+                       __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;
+                       spin_unlock_irq(&p->sighand->siglock);
                } while_each_task_pid(tty->session, PIDTYPE_SID, p);
        }
        read_unlock(&tasklist_lock);
@@ -1432,7 +1426,7 @@ void tty_vhangup(struct tty_struct * tty)
 
        printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
 #endif
-       do_tty_hangup((void *) tty);
+       do_tty_hangup(&tty->hangup_work);
 }
 EXPORT_SYMBOL(tty_vhangup);
 
@@ -1451,6 +1445,14 @@ int tty_hung_up_p(struct file * filp)
 
 EXPORT_SYMBOL(tty_hung_up_p);
 
+static void session_clear_tty(pid_t session)
+{
+       struct task_struct *p;
+       do_each_task_pid(session, PIDTYPE_SID, p) {
+               proc_clear_tty(p);
+       } while_each_task_pid(session, PIDTYPE_SID, p);
+}
+
 /**
  *     disassociate_ctty       -       disconnect controlling tty
  *     @on_exit: true if exiting so need to "hang up" the session
@@ -1467,31 +1469,35 @@ EXPORT_SYMBOL(tty_hung_up_p);
  *     The argument on_exit is set to 1 if called when a process is
  *     exiting; it is 0 if called by the ioctl TIOCNOTTY.
  *
- *     Locking: tty_mutex is taken to protect current->signal->tty
+ *     Locking:
  *             BKL is taken for hysterical raisins
- *             Tasklist lock is taken (under tty_mutex) to walk process
- *             lists for the session.
+ *               tty_mutex is taken to protect tty
+ *               ->siglock is taken to protect ->signal/->sighand
+ *               tasklist_lock is taken to walk process list for sessions
+ *                 ->siglock is taken to protect ->signal/->sighand
  */
 
 void disassociate_ctty(int on_exit)
 {
        struct tty_struct *tty;
-       struct task_struct *p;
        int tty_pgrp = -1;
+       int session;
 
        lock_kernel();
 
        mutex_lock(&tty_mutex);
-       tty = current->signal->tty;
+       tty = get_current_tty();
        if (tty) {
                tty_pgrp = 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 {
-               if (current->signal->tty_old_pgrp) {
-                       kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
-                       kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
+               pid_t old_pgrp = current->signal->tty_old_pgrp;
+               if (old_pgrp) {
+                       kill_pg(old_pgrp, SIGHUP, on_exit);
+                       kill_pg(old_pgrp, SIGCONT, on_exit);
                }
                mutex_unlock(&tty_mutex);
                unlock_kernel();        
@@ -1503,19 +1509,29 @@ void disassociate_ctty(int on_exit)
                        kill_pg(tty_pgrp, SIGCONT, on_exit);
        }
 
-       /* Must lock changes to tty_old_pgrp */
-       mutex_lock(&tty_mutex);
+       spin_lock_irq(&current->sighand->siglock);
        current->signal->tty_old_pgrp = 0;
-       tty->session = 0;
-       tty->pgrp = -1;
+       session = current->signal->session;
+       spin_unlock_irq(&current->sighand->siglock);
+
+       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;
+       } else {
+#ifdef TTY_DEBUG_HANGUP
+               printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
+                      " = NULL", tty);
+#endif
+       }
+       mutex_unlock(&tty_mutex);
 
        /* 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);
+       session_clear_tty(session);
        read_unlock(&tasklist_lock);
-       mutex_unlock(&tty_mutex);
        unlock_kernel();
 }
 
@@ -2064,8 +2080,9 @@ fail_no_mem:
 
        /* call the tty release_mem routine to clean out this slot */
 release_mem_out:
-       printk(KERN_INFO "init_dev: ldisc open failed, "
-                        "clearing slot %d\n", idx);
+       if (printk_ratelimit())
+               printk(KERN_INFO "init_dev: ldisc open failed, "
+                                "clearing slot %d\n", idx);
        release_mem(tty, idx);
        goto end_init;
 }
@@ -2334,16 +2351,10 @@ static void release_dev(struct file * filp)
         * tty.
         */
        if (tty_closing || o_tty_closing) {
-               struct task_struct *p;
-
                read_lock(&tasklist_lock);
-               do_each_task_pid(tty->session, PIDTYPE_SID, p) {
-                       p->signal->tty = NULL;
-               } while_each_task_pid(tty->session, PIDTYPE_SID, p);
+               session_clear_tty(tty->session);
                if (o_tty)
-                       do_each_task_pid(o_tty->session, PIDTYPE_SID, p) {
-                               p->signal->tty = NULL;
-                       } while_each_task_pid(o_tty->session, PIDTYPE_SID, p);
+                       session_clear_tty(o_tty->session);
                read_unlock(&tasklist_lock);
        }
 
@@ -2440,9 +2451,9 @@ static void release_dev(struct file * filp)
  *     The termios state of a pty is reset on first open so that
  *     settings don't persist across reuse.
  *
- *     Locking: tty_mutex protects current->signal->tty, get_tty_driver and
- *             init_dev work. tty->count should protect the rest.
- *             task_lock is held to update task details for sessions
+ *     Locking: tty_mutex protects tty, get_tty_driver and init_dev work.
+ *              tty->count should protect the rest.
+ *              ->siglock protects ->signal/->sighand
  */
 
 static int tty_open(struct inode * inode, struct file * filp)
@@ -2464,12 +2475,13 @@ retry_open:
        mutex_lock(&tty_mutex);
 
        if (device == MKDEV(TTYAUX_MAJOR,0)) {
-               if (!current->signal->tty) {
+               tty = get_current_tty();
+               if (!tty) {
                        mutex_unlock(&tty_mutex);
                        return -ENXIO;
                }
-               driver = current->signal->tty->driver;
-               index = current->signal->tty->index;
+               driver = tty->driver;
+               index = tty->index;
                filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
                /* noctty = 1; */
                goto got_driver;
@@ -2544,17 +2556,16 @@ got_driver:
                        filp->f_op = &tty_fops;
                goto retry_open;
        }
+
+       mutex_lock(&tty_mutex);
+       spin_lock_irq(&current->sighand->siglock);
        if (!noctty &&
            current->signal->leader &&
            !current->signal->tty &&
-           tty->session == 0) {
-               task_lock(current);
-               current->signal->tty = tty;
-               task_unlock(current);
-               current->signal->tty_old_pgrp = 0;
-               tty->session = current->signal->session;
-               tty->pgrp = process_group(current);
-       }
+           tty->session == 0)
+               __proc_set_tty(current, tty);
+       spin_unlock_irq(&current->sighand->siglock);
+       mutex_unlock(&tty_mutex);
        return 0;
 }
 
@@ -2718,6 +2729,8 @@ static int tty_fasync(int fd, struct file * filp, int on)
  *     Locking:
  *             Called functions take tty_ldisc_lock
  *             current->signal->tty check is safe without locks
+ *
+ *     FIXME: may race normal receive processing
  */
 
 static int tiocsti(struct tty_struct *tty, char __user *p)
@@ -2742,7 +2755,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
  *
  *     Copies the kernel idea of the window size into the user buffer.
  *
- *     Locking: tty->termios_sem is taken to ensure the winsize data
+ *     Locking: tty->termios_mutex is taken to ensure the winsize data
  *             is consistent.
  */
 
@@ -2750,9 +2763,9 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
 {
        int err;
 
-       down(&tty->termios_sem);
+       mutex_lock(&tty->termios_mutex);
        err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
-       up(&tty->termios_sem);
+       mutex_unlock(&tty->termios_mutex);
 
        return err ? -EFAULT: 0;
 }
@@ -2769,8 +2782,8 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
  *     Locking:
  *             Called function use the console_sem is used to ensure we do
  *     not try and resize the console twice at once.
- *             The tty->termios_sem is used to ensure we don't double
- *     resize and get confused. Lock order - tty->termios.sem before
+ *             The tty->termios_mutex is used to ensure we don't double
+ *     resize and get confused. Lock order - tty->termios_mutex before
  *     console sem
  */
 
@@ -2782,14 +2795,15 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
        if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
                return -EFAULT;
 
-       down(&tty->termios_sem);
+       mutex_lock(&tty->termios_mutex);
        if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
                goto done;
 
 #ifdef CONFIG_VT
        if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
-               if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row)) {
-                       up(&tty->termios_sem);
+               if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col,
+                                       tmp_ws.ws_row)) {
+                       mutex_unlock(&tty->termios_mutex);
                        return -ENXIO;
                }
        }
@@ -2801,7 +2815,7 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
        tty->winsize = tmp_ws;
        real_tty->winsize = tmp_ws;
 done:
-       up(&tty->termios_sem);
+       mutex_unlock(&tty->termios_mutex);
        return 0;
 }
 
@@ -2874,27 +2888,28 @@ static int fionbio(struct file *file, int __user *p)
  *     leader to set this tty as the controlling tty for the session.
  *
  *     Locking:
- *             Takes tasklist lock internally to walk sessions
- *             Takes task_lock() when updating signal->tty
- *
- *     FIXME: tty_mutex is needed to protect signal->tty references.
- *     FIXME: why task_lock on the signal->tty reference ??
- *
+ *             Takes tty_mutex() to protect tty instance
+ *             Takes tasklist_lock internally to walk sessions
+ *             Takes ->siglock() when updating signal->tty
  */
 
 static int tiocsctty(struct tty_struct *tty, int arg)
 {
-       struct task_struct *p;
-
+       int ret = 0;
        if (current->signal->leader &&
            (current->signal->session == tty->session))
-               return 0;
+               return ret;
+
+       mutex_lock(&tty_mutex);
        /*
         * The process must be a session leader and
         * not have a controlling tty already.
         */
-       if (!current->signal->leader || current->signal->tty)
-               return -EPERM;
+       if (!current->signal->leader || current->signal->tty) {
+               ret = -EPERM;
+               goto unlock;
+       }
+
        if (tty->session > 0) {
                /*
                 * This tty is already the controlling
@@ -2904,22 +2919,18 @@ static int tiocsctty(struct tty_struct *tty, int arg)
                        /*
                         * Steal it away
                         */
-
                        read_lock(&tasklist_lock);
-                       do_each_task_pid(tty->session, PIDTYPE_SID, p) {
-                               p->signal->tty = NULL;
-                       } while_each_task_pid(tty->session, PIDTYPE_SID, p);
+                       session_clear_tty(tty->session);
                        read_unlock(&tasklist_lock);
-               } else
-                       return -EPERM;
+               } else {
+                       ret = -EPERM;
+                       goto unlock;
+               }
        }
-       task_lock(current);
-       current->signal->tty = tty;
-       task_unlock(current);
-       current->signal->tty_old_pgrp = 0;
-       tty->session = current->signal->session;
-       tty->pgrp = process_group(current);
-       return 0;
+       proc_set_tty(current, tty);
+unlock:
+       mutex_unlock(&tty_mutex);
+       return ret;
 }
 
 /**
@@ -2931,7 +2942,7 @@ static int tiocsctty(struct tty_struct *tty, int arg)
  *     Obtain the process group of the tty. If there is no process group
  *     return an error.
  *
- *     Locking: none. Reference to ->signal->tty is safe.
+ *     Locking: none. Reference to current->signal->tty is safe.
  */
 
 static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
@@ -2955,8 +2966,6 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
  *     permitted where the tty session is our session.
  *
  *     Locking: None
- *
- *     FIXME: current->signal->tty referencing is unsafe.
  */
 
 static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
@@ -2991,7 +3000,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
  *     Obtain the session id of the tty. If there is no session
  *     return an error.
  *
- *     Locking: none. Reference to ->signal->tty is safe.
+ *     Locking: none. Reference to current->signal->tty is safe.
  */
 
 static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
@@ -3035,19 +3044,20 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
  *     timed break functionality.
  *
  *     Locking:
- *             None
+ *             atomic_write_lock serializes
  *
- *     FIXME:
- *             What if two overlap
  */
 
 static int send_break(struct tty_struct *tty, unsigned int duration)
 {
+       if (mutex_lock_interruptible(&tty->atomic_write_lock))
+               return -EINTR;
        tty->driver->break_ctl(tty, -1);
        if (!signal_pending(current)) {
                msleep_interruptible(duration);
        }
        tty->driver->break_ctl(tty, 0);
+       mutex_unlock(&tty->atomic_write_lock);
        if (signal_pending(current))
                return -EINTR;
        return 0;
@@ -3140,6 +3150,8 @@ int tty_ioctl(struct inode * inode, struct file * file,
        if (tty_paranoia_check(tty, inode, "tty_ioctl"))
                return -EINVAL;
 
+       /* CHECKME: is this safe as one end closes ? */
+
        real_tty = tty;
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
            tty->driver->subtype == PTY_TYPE_MASTER)
@@ -3207,14 +3219,11 @@ int tty_ioctl(struct inode * inode, struct file * file,
                        clear_bit(TTY_EXCLUSIVE, &tty->flags);
                        return 0;
                case TIOCNOTTY:
-                       /* FIXME: taks lock or tty_mutex ? */
                        if (current->signal->tty != tty)
                                return -ENOTTY;
                        if (current->signal->leader)
                                disassociate_ctty(0);
-                       task_lock(current);
-                       current->signal->tty = NULL;
-                       task_unlock(current);
+                       proc_clear_tty(current);
                        return 0;
                case TIOCSCTTY:
                        return tiocsctty(tty, arg);
@@ -3298,12 +3307,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(void *arg)
+static void __do_SAK(struct work_struct *work)
 {
+       struct tty_struct *tty =
+               container_of(work, struct tty_struct, SAK_work);
 #ifdef TTY_SOFT_SAK
        tty_hangup(tty);
 #else
-       struct tty_struct *tty = arg;
        struct task_struct *g, *p;
        int session;
        int             i;
@@ -3313,7 +3323,7 @@ static void __do_SAK(void *arg)
        
        if (!tty)
                return;
-       session  = tty->session;
+       session = tty->session;
        
        /* We don't want an ldisc switch during this */
        disc = tty_ldisc_ref(tty);
@@ -3382,7 +3392,7 @@ void do_SAK(struct tty_struct *tty)
 {
        if (!tty)
                return;
-       PREPARE_WORK(&tty->SAK_work, __do_SAK, tty);
+       PREPARE_WORK(&tty->SAK_work, __do_SAK);
        schedule_work(&tty->SAK_work);
 }
 
@@ -3390,7 +3400,7 @@ EXPORT_SYMBOL(do_SAK);
 
 /**
  *     flush_to_ldisc
- *     @private_: tty structure passed from work queue.
+ *     @work: tty structure passed from work queue.
  *
  *     This routine is called out of the software interrupt to flush data
  *     from the buffer chain to the line discipline.
@@ -3400,9 +3410,10 @@ EXPORT_SYMBOL(do_SAK);
  *     receive_buf method is single threaded for each tty instance.
  */
  
-static void flush_to_ldisc(void *private_)
+static void flush_to_ldisc(struct work_struct *work)
 {
-       struct tty_struct *tty = (struct tty_struct *) private_;
+       struct tty_struct *tty =
+               container_of(work, struct tty_struct, buf.work.work);
        unsigned long   flags;
        struct tty_ldisc *disc;
        struct tty_buffer *tbuf, *head;
@@ -3547,7 +3558,7 @@ void tty_flip_buffer_push(struct tty_struct *tty)
        spin_unlock_irqrestore(&tty->buf.lock, flags);
 
        if (tty->low_latency)
-               flush_to_ldisc((void *) tty);
+               flush_to_ldisc(&tty->buf.work.work);
        else
                schedule_delayed_work(&tty->buf.work, 1);
 }
@@ -3574,17 +3585,17 @@ static void initialize_tty_struct(struct tty_struct *tty)
        tty->overrun_time = jiffies;
        tty->buf.head = tty->buf.tail = NULL;
        tty_buffer_init(tty);
-       INIT_WORK(&tty->buf.work, flush_to_ldisc, tty);
+       INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
        init_MUTEX(&tty->buf.pty_sem);
-       init_MUTEX(&tty->termios_sem);
+       mutex_init(&tty->termios_mutex);
        init_waitqueue_head(&tty->write_wait);
        init_waitqueue_head(&tty->read_wait);
-       INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
+       INIT_WORK(&tty->hangup_work, do_tty_hangup);
        mutex_init(&tty->atomic_read_lock);
        mutex_init(&tty->atomic_write_lock);
        spin_lock_init(&tty->read_lock);
        INIT_LIST_HEAD(&tty->tty_files);
-       INIT_WORK(&tty->SAK_work, NULL, NULL);
+       INIT_WORK(&tty->SAK_work, NULL);
 }
 
 /*
@@ -3606,7 +3617,8 @@ static struct class *tty_class;
  *             This field is optional, if there is no known struct device
  *             for this tty device it can be set to NULL safely.
  *
- *     Returns a pointer to the class device (or ERR_PTR(-EFOO) on error).
+ *     Returns a pointer to the struct device for this tty device
+ *     (or ERR_PTR(-EFOO) on error).
  *
  *     This call is required to be made to register an individual tty device
  *     if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set.  If
@@ -3616,8 +3628,8 @@ static struct class *tty_class;
  *     Locking: ??
  */
 
-struct class_device *tty_register_device(struct tty_driver *driver,
-                                        unsigned index, struct device *device)
+struct device *tty_register_device(struct tty_driver *driver, unsigned index,
+                                  struct device *device)
 {
        char name[64];
        dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
@@ -3633,7 +3645,7 @@ struct class_device *tty_register_device(struct tty_driver *driver,
        else
                tty_line_name(driver, index, name);
 
-       return class_device_create(tty_class, NULL, dev, device, "%s", name);
+       return device_create(tty_class, device, dev, name);
 }
 
 /**
@@ -3649,7 +3661,7 @@ struct class_device *tty_register_device(struct tty_driver *driver,
 
 void tty_unregister_device(struct tty_driver *driver, unsigned index)
 {
-       class_device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index);
+       device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index);
 }
 
 EXPORT_SYMBOL(tty_register_device);
@@ -3674,7 +3686,8 @@ void put_tty_driver(struct tty_driver *driver)
        kfree(driver);
 }
 
-void tty_set_operations(struct tty_driver *driver, struct tty_operations *op)
+void tty_set_operations(struct tty_driver *driver,
+                       const struct tty_operations *op)
 {
        driver->open = op->open;
        driver->close = op->close;
@@ -3823,9 +3836,52 @@ int tty_unregister_driver(struct tty_driver *driver)
        cdev_del(&driver->cdev);
        return 0;
 }
-
 EXPORT_SYMBOL(tty_unregister_driver);
 
+dev_t tty_devnum(struct tty_struct *tty)
+{
+       return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
+}
+EXPORT_SYMBOL(tty_devnum);
+
+void proc_clear_tty(struct task_struct *p)
+{
+       spin_lock_irq(&p->sighand->siglock);
+       p->signal->tty = NULL;
+       spin_unlock_irq(&p->sighand->siglock);
+}
+EXPORT_SYMBOL(proc_clear_tty);
+
+void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
+{
+       if (tty) {
+               tty->session = tsk->signal->session;
+               tty->pgrp = process_group(tsk);
+       }
+       tsk->signal->tty = tty;
+       tsk->signal->tty_old_pgrp = 0;
+}
+
+void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
+{
+       spin_lock_irq(&tsk->sighand->siglock);
+       __proc_set_tty(tsk, tty);
+       spin_unlock_irq(&tsk->sighand->siglock);
+}
+
+struct tty_struct *get_current_tty(void)
+{
+       struct tty_struct *tty;
+       WARN_ON_ONCE(!mutex_is_locked(&tty_mutex));
+       tty = current->signal->tty;
+       /*
+        * session->tty can be changed/cleared from under us, make sure we
+        * issue the load. The obtained pointer, when not NULL, is valid as
+        * long as we hold tty_mutex.
+        */
+       barrier();
+       return tty;
+}
 
 /*
  * Initialize the console device. This is called *early*, so
@@ -3888,20 +3944,20 @@ static int __init tty_init(void)
        if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
                panic("Couldn't register /dev/tty driver\n");
-       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
+       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), "tty");
 
        cdev_init(&console_cdev, &console_fops);
        if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
                panic("Couldn't register /dev/console driver\n");
-       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console");
+       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), "console");
 
 #ifdef CONFIG_UNIX98_PTYS
        cdev_init(&ptmx_cdev, &ptmx_fops);
        if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
                panic("Couldn't register /dev/ptmx driver\n");
-       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
+       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), "ptmx");
 #endif
 
 #ifdef CONFIG_VT
@@ -3909,7 +3965,7 @@ static int __init tty_init(void)
        if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
            register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
                panic("Couldn't register /dev/tty0 driver\n");
-       class_device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
+       device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), "tty0");
 
        vty_init();
 #endif