--- zzzz-none-000/linux-3.10.107/drivers/tty/tty_io.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/tty/tty_io.c 2021-02-04 17:41:59.000000000 +0000 @@ -106,6 +106,11 @@ #include #undef TTY_DEBUG_HANGUP +#ifdef TTY_DEBUG_HANGUP +# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args) +#else +# define tty_debug_hangup(tty, f, args...) do { } while (0) +#endif #define TTY_PARANOIA_CHECK 1 #define CHECK_TTY_COUNT 1 @@ -153,22 +158,6 @@ static int __tty_fasync(int fd, struct file *filp, int on); static int tty_fasync(int fd, struct file *filp, int on); static void release_tty(struct tty_struct *tty, int idx); -static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); -static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); - -/** - * alloc_tty_struct - allocate a tty object - * - * Return a new empty tty structure. The data fields have not - * been initialized in any way but has been zeroed - * - * Locking: none - */ - -struct tty_struct *alloc_tty_struct(void) -{ - return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); -} /** * free_tty_struct - free a disused tty @@ -183,8 +172,7 @@ { if (!tty) return; - if (tty->dev) - put_device(tty->dev); + put_device(tty->dev); kfree(tty->write_buf); tty->magic = 0xDEADDEAD; kfree(tty); @@ -252,7 +240,6 @@ /** * tty_name - return tty naming * @tty: tty structure - * @buf: buffer for output * * Convert a tty structure into a name. The name reflects the kernel * naming policy and if udev is in use may not reflect user space @@ -260,13 +247,11 @@ * Locking: none */ -char *tty_name(struct tty_struct *tty, char *buf) +const char *tty_name(const struct tty_struct *tty) { if (!tty) /* Hmm. NULL pointer. That's fun. */ - strcpy(buf, "NULL tty"); - else - strcpy(buf, tty->name); - return buf; + return "NULL tty"; + return tty->name; } EXPORT_SYMBOL(tty_name); @@ -291,6 +276,7 @@ return 0; } +/* Caller must hold tty_lock */ static int check_tty_count(struct tty_struct *tty, const char *routine) { #ifdef CHECK_TTY_COUNT @@ -404,39 +390,48 @@ * Locking: ctrl_lock */ -int tty_check_change(struct tty_struct *tty) +int __tty_check_change(struct tty_struct *tty, int sig) { unsigned long flags; + struct pid *pgrp, *tty_pgrp; int ret = 0; if (current->signal->tty != tty) return 0; + rcu_read_lock(); + pgrp = task_pgrp(current); + spin_lock_irqsave(&tty->ctrl_lock, flags); + tty_pgrp = tty->pgrp; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); - if (!tty->pgrp) { - printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n"); - goto out_unlock; + if (tty_pgrp && pgrp != tty->pgrp) { + if (is_ignored(sig)) { + if (sig == SIGTTIN) + ret = -EIO; + } else if (is_current_pgrp_orphaned()) + ret = -EIO; + else { + kill_pgrp(pgrp, sig, 1); + set_thread_flag(TIF_SIGPENDING); + ret = -ERESTARTSYS; + } } - if (task_pgrp(current) == tty->pgrp) - goto out_unlock; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - if (is_ignored(SIGTTOU)) - goto out; - if (is_current_pgrp_orphaned()) { - ret = -EIO; - goto out; + rcu_read_unlock(); + + if (!tty_pgrp) { + pr_warn("%s: tty_check_change: sig=%d, tty->pgrp == NULL!\n", + tty_name(tty), sig); } - kill_pgrp(task_pgrp(current), SIGTTOU, 1); - set_thread_flag(TIF_SIGPENDING); - ret = -ERESTARTSYS; -out: - return ret; -out_unlock: - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + return ret; } +int tty_check_change(struct tty_struct *tty) +{ + return __tty_check_change(tty, SIGTTOU); +} EXPORT_SYMBOL(tty_check_change); static ssize_t hung_up_tty_read(struct file *file, char __user *buf, @@ -506,6 +501,79 @@ static DEFINE_SPINLOCK(redirect_lock); static struct file *redirect; + +void proc_clear_tty(struct task_struct *p) +{ + unsigned long flags; + struct tty_struct *tty; + spin_lock_irqsave(&p->sighand->siglock, flags); + tty = p->signal->tty; + p->signal->tty = NULL; + spin_unlock_irqrestore(&p->sighand->siglock, flags); + tty_kref_put(tty); +} + +/** + * proc_set_tty - set the controlling terminal + * + * Only callable by the session leader and only if it does not already have + * a controlling terminal. + * + * Caller must hold: tty_lock() + * a readlock on tasklist_lock + * sighand lock + */ +static void __proc_set_tty(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + /* + * The session and fg pgrp references will be non-NULL if + * tiocsctty() is stealing the controlling tty + */ + put_pid(tty->session); + put_pid(tty->pgrp); + tty->pgrp = get_pid(task_pgrp(current)); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty->session = get_pid(task_session(current)); + if (current->signal->tty) { + tty_debug(tty, "current tty %s not NULL!!\n", + current->signal->tty->name); + tty_kref_put(current->signal->tty); + } + put_pid(current->signal->tty_old_pgrp); + current->signal->tty = tty_kref_get(tty); + current->signal->tty_old_pgrp = NULL; +} + +static void proc_set_tty(struct tty_struct *tty) +{ + spin_lock_irq(¤t->sighand->siglock); + __proc_set_tty(tty); + spin_unlock_irq(¤t->sighand->siglock); +} + +struct tty_struct *get_current_tty(void) +{ + struct tty_struct *tty; + unsigned long flags; + + spin_lock_irqsave(¤t->sighand->siglock, flags); + tty = tty_kref_get(current->signal->tty); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + return tty; +} +EXPORT_SYMBOL_GPL(get_current_tty); + +static void session_clear_tty(struct pid *session) +{ + struct task_struct *p; + do_each_pid_task(session, PIDTYPE_SID, p) { + proc_clear_tty(p); + } while_each_pid_task(session, PIDTYPE_SID, p); +} + /** * tty_wakeup - request more data * @tty: terminal @@ -603,8 +671,8 @@ * BTM * redirect lock for undoing redirection * file list lock for manipulating list of ttys - * tty_ldisc_lock from called functions - * termios_mutex resetting termios data + * tty_ldiscs_lock from called functions + * termios_rwsem resetting termios data * tasklist_lock to walk task list for hangup event * ->siglock to protect ->signal/->sighand */ @@ -629,8 +697,10 @@ tty_lock(tty); - /* some functions below drop BTM, so we need this bit */ - set_bit(TTY_HUPPING, &tty->flags); + if (test_bit(TTY_HUPPED, &tty->flags)) { + tty_unlock(tty); + return; + } /* inuse_filps is protected by the single tty lock, this really needs to change if we want to flush the @@ -656,15 +726,10 @@ 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); 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); put_pid(tty->session); put_pid(tty->pgrp); @@ -684,7 +749,7 @@ for (n = 0; n < closecount; n++) tty->ops->close(tty, cons_filp); } else if (tty->ops->hangup) - (tty->ops->hangup)(tty); + tty->ops->hangup(tty); /* * We don't want to have driver/ldisc interactions beyond * the ones we did here. The driver layer expects no @@ -692,8 +757,6 @@ * can't yet guarantee all that. */ set_bit(TTY_HUPPED, &tty->flags); - clear_bit(TTY_HUPPING, &tty->flags); - tty_unlock(tty); if (f) @@ -718,10 +781,7 @@ void tty_hangup(struct tty_struct *tty) { -#ifdef TTY_DEBUG_HANGUP - char buf[64]; - printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf)); -#endif + tty_debug_hangup(tty, "\n"); schedule_work(&tty->hangup_work); } @@ -738,11 +798,7 @@ void tty_vhangup(struct tty_struct *tty) { -#ifdef TTY_DEBUG_HANGUP - char buf[64]; - - printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); -#endif + tty_debug_hangup(tty, "\n"); __tty_hangup(tty, 0); } @@ -779,11 +835,7 @@ 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_debug_hangup(tty, "\n"); __tty_hangup(tty, 1); } @@ -802,14 +854,6 @@ EXPORT_SYMBOL(tty_hung_up_p); -static void session_clear_tty(struct pid *session) -{ - struct task_struct *p; - do_each_pid_task(session, PIDTYPE_SID, p) { - proc_clear_tty(p); - } while_each_pid_task(session, PIDTYPE_SID, p); -} - /** * disassociate_ctty - disconnect controlling tty * @on_exit: true if exiting so need to "hang up" the session @@ -874,9 +918,8 @@ spin_lock_irq(¤t->sighand->siglock); put_pid(current->signal->tty_old_pgrp); current->signal->tty_old_pgrp = NULL; - spin_unlock_irq(¤t->sighand->siglock); - tty = get_current_tty(); + tty = tty_kref_get(current->signal->tty); if (tty) { unsigned long flags; spin_lock_irqsave(&tty->ctrl_lock, flags); @@ -886,13 +929,10 @@ tty->pgrp = NULL; spin_unlock_irqrestore(&tty->ctrl_lock, flags); tty_kref_put(tty); - } else { -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "error attempted to write to tty [0x%p]" - " = NULL", tty); -#endif - } + } else + tty_debug_hangup(tty, "no current tty\n"); + spin_unlock_irq(¤t->sighand->siglock); /* Now clear signal->tty under the lock */ read_lock(&tasklist_lock); session_clear_tty(task_session(current)); @@ -918,8 +958,7 @@ * stop_tty - propagate flow control * @tty: tty to stop * - * Perform flow control to the driver. For PTY/TTY pairs we - * must also propagate the TIOCKPKT status. May be called + * Perform flow control to the driver. May be called * on an already stopped device and will not re-call the driver * method. * @@ -929,71 +968,71 @@ * but not always. * * Locking: - * Uses the tty control lock internally + * flow_lock */ -void stop_tty(struct tty_struct *tty) +void __stop_tty(struct tty_struct *tty) { - unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->stopped) { - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (tty->stopped) return; - } tty->stopped = 1; - if (tty->link && tty->link->packet) { - tty->ctrl_status &= ~TIOCPKT_START; - tty->ctrl_status |= TIOCPKT_STOP; - wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (tty->ops->stop) - (tty->ops->stop)(tty); + tty->ops->stop(tty); } +void stop_tty(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->flow_lock, flags); + __stop_tty(tty); + spin_unlock_irqrestore(&tty->flow_lock, flags); +} EXPORT_SYMBOL(stop_tty); /** * start_tty - propagate flow control * @tty: tty to start * - * Start a tty that has been stopped if at all possible. Perform - * any necessary wakeups and propagate the TIOCPKT status. If this - * is the tty was previous stopped and is being started then the - * driver start method is invoked and the line discipline woken. + * Start a tty that has been stopped if at all possible. If this + * tty was previous stopped and is now being started, the driver + * start method is invoked and the line discipline woken. * * Locking: - * ctrl_lock + * flow_lock */ -void start_tty(struct tty_struct *tty) +void __start_tty(struct tty_struct *tty) { - unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (!tty->stopped || tty->flow_stopped) { - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (!tty->stopped || tty->flow_stopped) return; - } tty->stopped = 0; - if (tty->link && tty->link->packet) { - tty->ctrl_status &= ~TIOCPKT_STOP; - tty->ctrl_status |= TIOCPKT_START; - wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (tty->ops->start) - (tty->ops->start)(tty); - /* If we have a running line discipline it may need kicking */ + tty->ops->start(tty); tty_wakeup(tty); } +void start_tty(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->flow_lock, flags); + __start_tty(tty); + spin_unlock_irqrestore(&tty->flow_lock, flags); +} 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(); - if (abs(sec - time->tv_sec) & ~7) + + /* + * We only care if the two values differ in anything other than the + * lower three bits (i.e every 8 seconds). If so, then we can update + * the time of the tty device, otherwise it could be construded as a + * security leak to let userspace know the exact timing of the tty. + */ + if ((sec ^ time->tv_sec) & ~7) time->tv_sec = sec; } @@ -1029,7 +1068,7 @@ situation */ ld = tty_ldisc_ref_wait(tty); if (ld->ops->read) - i = (ld->ops->read)(tty, file, buf, count); + i = ld->ops->read(tty, file, buf, count); else i = -EIO; tty_ldisc_deref(ld); @@ -1040,15 +1079,13 @@ return i; } -void tty_write_unlock(struct tty_struct *tty) - __releases(&tty->atomic_write_lock) +static void tty_write_unlock(struct tty_struct *tty) { mutex_unlock(&tty->atomic_write_lock); wake_up_interruptible_poll(&tty->write_wait, POLLOUT); } -int tty_write_lock(struct tty_struct *tty, int ndelay) - __acquires(&tty->atomic_write_lock) +static int tty_write_lock(struct tty_struct *tty, int ndelay) { if (!mutex_trylock(&tty->atomic_write_lock)) { if (ndelay) @@ -1163,11 +1200,9 @@ if (tty) { mutex_lock(&tty->atomic_write_lock); tty_lock(tty); - if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(tty); + if (tty->ops->write && tty->count > 0) tty->ops->write(tty, msg, strlen(msg)); - } else - tty_unlock(tty); + tty_unlock(tty); tty_write_unlock(tty); } return; @@ -1234,6 +1269,39 @@ return tty_write(file, buf, count, ppos); } +/** + * tty_send_xchar - send priority character + * + * Send a high priority character to the tty even if stopped + * + * Locking: none for xchar method, write ordering for write method. + */ + +int tty_send_xchar(struct tty_struct *tty, char ch) +{ + int was_stopped = tty->stopped; + + if (tty->ops->send_xchar) { + down_read(&tty->termios_rwsem); + tty->ops->send_xchar(tty, ch); + up_read(&tty->termios_rwsem); + return 0; + } + + if (tty_write_lock(tty, 0) < 0) + return -ERESTARTSYS; + + down_read(&tty->termios_rwsem); + if (was_stopped) + start_tty(tty); + tty->ops->write(tty, &ch, 1); + if (was_stopped) + stop_tty(tty); + up_read(&tty->termios_rwsem); + tty_write_unlock(tty); + return 0; +} + static char ptychar[] = "pqrstuvwxyzabcde"; /** @@ -1281,19 +1349,24 @@ * @driver: the driver for the tty * @idx: the minor number * - * Return the tty, if found or ERR_PTR() otherwise. + * Return the tty, if found. If not found, return NULL or ERR_PTR() if the + * driver lookup() method returns an error. * - * Locking: tty_mutex must be held. If tty is found, the mutex must - * be held until the 'fast-open' is also done. Will change once we - * have refcounting in the driver and per driver locking + * Locking: tty_mutex must be held. If the tty is found, bump the tty kref. */ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, struct inode *inode, int idx) { + struct tty_struct *tty; + if (driver->ops->lookup) - return driver->ops->lookup(driver, inode, idx); + tty = driver->ops->lookup(driver, inode, idx); + else + tty = driver->ttys[idx]; - return driver->ttys[idx]; + if (!IS_ERR(tty)) + tty_kref_get(tty); + return tty; } /** @@ -1381,33 +1454,27 @@ * @tty - the tty to open * * Return 0 on success, -errno on error. + * Re-opens on master ptys are not allowed and return -EIO. * - * Locking: tty_mutex must be held from the time the tty was found - * till this open completes. + * Locking: Caller must hold tty_lock */ static int tty_reopen(struct tty_struct *tty) { struct tty_driver *driver = tty->driver; - if (test_bit(TTY_CLOSING, &tty->flags) || - test_bit(TTY_HUPPING, &tty->flags) || - test_bit(TTY_LDISC_CHANGING, &tty->flags)) + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) return -EIO; - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER) { - /* - * special case for PTY masters: only one open permitted, - * and the slave side open count is incremented as well. - */ - if (tty->count) - return -EIO; + if (!tty->count) + return -EAGAIN; + + if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) + return -EBUSY; - tty->link->count++; - } tty->count++; - WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); + WARN_ON(!tty->ldisc); return 0; } @@ -1452,12 +1519,11 @@ if (!try_module_get(driver->owner)) return ERR_PTR(-ENODEV); - tty = alloc_tty_struct(); + tty = alloc_tty_struct(driver, idx); if (!tty) { retval = -ENOMEM; goto err_module_put; } - initialize_tty_struct(tty, driver, idx); tty_lock(tty); retval = tty_driver_install_tty(driver, tty); @@ -1525,15 +1591,19 @@ EXPORT_SYMBOL(tty_free_termios); /** - * tty_flush_works - flush all works of a tty - * @tty: tty device to flush works for + * tty_flush_works - flush all works of a tty/pty pair + * @tty: tty device to flush works for (or either end of a pty pair) * - * Sync flush all works belonging to @tty. + * Sync flush all works belonging to @tty (and the 'other' tty). */ static void tty_flush_works(struct tty_struct *tty) { flush_work(&tty->SAK_work); flush_work(&tty->hangup_work); + if (tty->link) { + flush_work(&tty->link->SAK_work); + flush_work(&tty->link->hangup_work); + } } /** @@ -1556,13 +1626,14 @@ struct tty_struct *tty = container_of(work, struct tty_struct, hangup_work); struct tty_driver *driver = tty->driver; + struct module *owner = driver->owner; if (tty->ops->cleanup) tty->ops->cleanup(tty); tty->magic = 0; tty_driver_kref_put(driver); - module_put(driver->owner); + module_put(owner); spin_lock(&tty_files_lock); list_del_init(&tty->tty_files); @@ -1622,10 +1693,9 @@ tty->port->itty = NULL; if (tty->link) tty->link->port->itty = NULL; - cancel_work_sync(&tty->port->buf.work); + tty_buffer_cancel_work(tty->port); - if (tty->link) - tty_kref_put(tty->link); + tty_kref_put(tty->link); tty_kref_put(tty); } @@ -1638,13 +1708,11 @@ * Performs some paranoid checking before true release of the @tty. * This is a no-op unless TTY_PARANOIA_CHECK is defined. */ -static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty, - int idx) +static int tty_release_checks(struct tty_struct *tty, int idx) { #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver->num) { - printk(KERN_DEBUG "%s: bad idx when trying to free (%s)\n", - __func__, tty->name); + tty_debug(tty, "bad idx %d\n", idx); return -1; } @@ -1653,18 +1721,20 @@ return 0; if (tty != tty->driver->ttys[idx]) { - printk(KERN_DEBUG "%s: driver.table[%d] not tty for (%s)\n", - __func__, idx, tty->name); + tty_debug(tty, "bad driver table[%d] = %p\n", + idx, tty->driver->ttys[idx]); return -1; } if (tty->driver->other) { + struct tty_struct *o_tty = tty->link; + if (o_tty != tty->driver->other->ttys[idx]) { - printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n", - __func__, idx, tty->name); + tty_debug(tty, "bad other table[%d] = %p\n", + idx, tty->driver->other->ttys[idx]); return -1; } if (o_tty->link != tty) { - printk(KERN_DEBUG "%s: bad pty pointers\n", __func__); + tty_debug(tty, "bad link = %p\n", o_tty->link); return -1; } } @@ -1694,11 +1764,11 @@ int tty_release(struct inode *inode, struct file *filp) { struct tty_struct *tty = file_tty(filp); - struct tty_struct *o_tty; - int pty_master, tty_closing, o_tty_closing, do_sleep; + struct tty_struct *o_tty = NULL; + int do_sleep, final; int idx; - char buf[64]; long timeout = 0; + int once = 1; if (tty_paranoia_check(tty, inode, __func__)) return 0; @@ -1709,25 +1779,23 @@ __tty_fasync(-1, filp, 0); idx = tty->index; - pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER); - /* Review: parallel close */ - o_tty = tty->link; + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && + tty->driver->subtype == PTY_TYPE_MASTER) + o_tty = tty->link; - if (tty_release_checks(tty, o_tty, idx)) { + if (tty_release_checks(tty, idx)) { tty_unlock(tty); return 0; } -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__, - tty_name(tty, buf), tty->count); -#endif + tty_debug_hangup(tty, "(tty count=%d)...\n", tty->count); if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(tty); + /* If tty is pty master, lock the slave pty (stable lock order) */ + tty_lock_slave(o_tty); + /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1738,25 +1806,13 @@ * The test for the o_tty closing is necessary, since the master and * slave sides may close in any order. If the slave side closes out * first, its count will be one, since the master side holds an open. - * Thus this test wouldn't be triggered at the time the slave closes, + * Thus this test wouldn't be triggered at the time the slave closed, * so we do it now. - * - * Note that it's possible for the tty to be opened again while we're - * flushing out waiters. By recalculating the closing flags before - * each iteration we avoid any problems. */ while (1) { - /* Guard against races with tty->count changes elsewhere and - opens on /dev/tty */ - - mutex_lock(&tty_mutex); - tty_lock_pair(tty, o_tty); - tty_closing = tty->count <= 1; - o_tty_closing = o_tty && - (o_tty->count <= (pty_master ? 1 : 0)); do_sleep = 0; - if (tty_closing) { + if (tty->count <= 1) { if (waitqueue_active(&tty->read_wait)) { wake_up_poll(&tty->read_wait, POLLIN); do_sleep++; @@ -1766,7 +1822,7 @@ do_sleep++; } } - if (o_tty_closing) { + if (o_tty && o_tty->count <= 1) { if (waitqueue_active(&o_tty->read_wait)) { wake_up_poll(&o_tty->read_wait, POLLIN); do_sleep++; @@ -1779,10 +1835,11 @@ if (!do_sleep) break; - printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", - __func__, tty_name(tty, buf)); - tty_unlock_pair(tty, o_tty); - mutex_unlock(&tty_mutex); + if (once) { + once = 0; + printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", + __func__, tty_name(tty)); + } schedule_timeout_killable(timeout); if (timeout < 120 * HZ) timeout = 2 * timeout + 1; @@ -1790,24 +1847,16 @@ timeout = MAX_SCHEDULE_TIMEOUT; } - /* - * The closing flags are now consistent with the open counts on - * both sides, and we've completed the last operation that could - * block, so it's safe to proceed with closing. - * - * We must *not* drop the tty_mutex until we ensure that a further - * entry into tty_open can not pick up this tty. - */ - if (pty_master) { + if (o_tty) { if (--o_tty->count < 0) { printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", - __func__, o_tty->count, tty_name(o_tty, buf)); + __func__, o_tty->count, tty_name(o_tty)); o_tty->count = 0; } } if (--tty->count < 0) { printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n", - __func__, tty->count, tty_name(tty, buf)); + __func__, tty->count, tty_name(tty)); tty->count = 0; } @@ -1825,21 +1874,11 @@ /* * Perform some housekeeping before deciding whether to return. * - * Set the TTY_CLOSING flag if this was the last open. In the - * case of a pty we may have to wait around for the other side - * to close, and TTY_CLOSING makes sure we can't be reopened. - */ - if (tty_closing) - set_bit(TTY_CLOSING, &tty->flags); - if (o_tty_closing) - set_bit(TTY_CLOSING, &o_tty->flags); - - /* * If _either_ side is closing, make sure there aren't any * processes that still think tty or o_tty is their controlling * tty. */ - if (tty_closing || o_tty_closing) { + if (!tty->count) { read_lock(&tasklist_lock); session_clear_tty(tty->session); if (o_tty) @@ -1847,31 +1886,28 @@ read_unlock(&tasklist_lock); } - mutex_unlock(&tty_mutex); - tty_unlock_pair(tty, o_tty); - /* At this point the TTY_CLOSING flag should ensure a dead tty + /* check whether both sides are closing ... */ + final = !tty->count && !(o_tty && o_tty->count); + + tty_unlock_slave(o_tty); + tty_unlock(tty); + + /* At this point, the tty->count == 0 should ensure a dead tty cannot be re-opened by a racing opener */ - /* check whether both sides are closing ... */ - if (!tty_closing || (o_tty && !o_tty_closing)) + if (!final) return 0; -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty, buf)); -#endif + tty_debug_hangup(tty, "final close\n"); /* * Ask the line discipline code to release its structures */ - tty_ldisc_release(tty, o_tty); + tty_ldisc_release(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 + tty_debug_hangup(tty, "freeing structure...\n"); /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. The tty_unlock_pair @@ -1886,20 +1922,20 @@ } /** - * tty_open_current_tty - get tty of current task for open + * tty_open_current_tty - get locked tty of current task * @device: device number * @filp: file pointer to tty - * @return: tty of the current task iff @device is /dev/tty + * @return: locked tty of the current task iff @device is /dev/tty + * + * Performs a re-open of the current task's controlling tty. * * We cannot return driver and index like for the other nodes because * devpts will not work then. It expects inodes to be from devpts FS. - * - * We need to move to returning a refcounted object from all the lookup - * paths including this one. */ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) { struct tty_struct *tty; + int retval; if (device != MKDEV(TTYAUX_MAJOR, 0)) return NULL; @@ -1910,9 +1946,14 @@ filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ - tty_kref_put(tty); - /* FIXME: we put a reference and return a TTY! */ - /* This is only safe because the caller holds tty_mutex */ + tty_lock(tty); + tty_kref_put(tty); /* safe to drop the kref now */ + + retval = tty_reopen(tty); + if (retval < 0) { + tty_unlock(tty); + tty = ERR_PTR(retval); + } return tty; } @@ -2010,13 +2051,9 @@ index = -1; retval = 0; - mutex_lock(&tty_mutex); - /* This is protected by the tty_mutex */ tty = tty_open_current_tty(device, filp); - if (IS_ERR(tty)) { - retval = PTR_ERR(tty); - goto err_unlock; - } else if (!tty) { + if (!tty) { + mutex_lock(&tty_mutex); driver = tty_lookup_driver(device, filp, &noctty, &index); if (IS_ERR(driver)) { retval = PTR_ERR(driver); @@ -2029,24 +2066,37 @@ retval = PTR_ERR(tty); goto err_unlock; } - } - if (tty) { - tty_lock(tty); - retval = tty_reopen(tty); - if (retval < 0) { - tty_unlock(tty); - tty = ERR_PTR(retval); + if (tty) { + mutex_unlock(&tty_mutex); + retval = tty_lock_interruptible(tty); + if (retval) { + if (retval == -EINTR) + retval = -ERESTARTSYS; + goto err_unref; + } + /* safe to drop the kref from tty_driver_lookup_tty() */ + tty_kref_put(tty); + retval = tty_reopen(tty); + if (retval < 0) { + tty_unlock(tty); + tty = ERR_PTR(retval); + } + } else { /* Returns with the tty_lock held for now */ + tty = tty_init_dev(driver, index); + mutex_unlock(&tty_mutex); } - } else /* Returns with the tty_lock held for now */ - tty = tty_init_dev(driver, index); - mutex_unlock(&tty_mutex); - if (driver) tty_driver_kref_put(driver); + } + if (IS_ERR(tty)) { retval = PTR_ERR(tty); - goto err_file; + if (retval != -EAGAIN || signal_pending(current)) + goto err_file; + tty_free_file(filp); + schedule(); + goto retry_open; } tty_add_file(tty, filp); @@ -2055,24 +2105,18 @@ if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) noctty = 1; -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: opening %s...\n", __func__, tty->name); -#endif + + tty_debug_hangup(tty, "(tty count=%d)\n", tty->count); + if (tty->ops->open) retval = tty->ops->open(tty, filp); else retval = -ENODEV; filp->f_flags = saved_flags; - if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && - !capable(CAP_SYS_ADMIN)) - retval = -EBUSY; - if (retval) { -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, - retval, tty->name); -#endif + tty_debug_hangup(tty, "error %d, releasing...\n", retval); + tty_unlock(tty); /* need to call tty_release without BTM */ tty_release(inode, filp); if (retval != -ERESTARTSYS) @@ -2085,27 +2129,43 @@ /* * Need to reset f_op in case a hangup happened. */ - if (filp->f_op == &hung_up_tty_fops) + if (tty_hung_up_p(filp)) filp->f_op = &tty_fops; goto retry_open; } - tty_unlock(tty); + clear_bit(TTY_HUPPED, &tty->flags); - mutex_lock(&tty_mutex); - tty_lock(tty); + read_lock(&tasklist_lock); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && !current->signal->tty && - tty->session == NULL) - __proc_set_tty(current, tty); + tty->session == NULL) { + /* + * Don't let a process that only has write access to the tty + * obtain the privileges associated with having a tty as + * controlling terminal (being able to reopen it with full + * access through /dev/tty, being able to perform pushback). + * Many distributions set the group of all ttys to "tty" and + * grant write-only access to all terminals for setgid tty + * binaries, which should not imply full privileges on all ttys. + * + * This could theoretically break old code that performs open() + * on a write-only file descriptor. In that case, it might be + * necessary to also permit this if + * inode_permission(inode, MAY_READ) == 0. + */ + if (filp->f_mode & FMODE_READ) + __proc_set_tty(tty); + } spin_unlock_irq(¤t->sighand->siglock); + read_unlock(&tasklist_lock); tty_unlock(tty); - mutex_unlock(&tty_mutex); return 0; err_unlock: mutex_unlock(&tty_mutex); +err_unref: /* after locks to avoid deadlock */ if (!IS_ERR_OR_NULL(driver)) tty_driver_kref_put(driver); @@ -2139,7 +2199,7 @@ ld = tty_ldisc_ref_wait(tty); if (ld->ops->poll) - ret = (ld->ops->poll)(tty, filp, wait); + ret = ld->ops->poll(tty, filp, wait); tty_ldisc_deref(ld); return ret; } @@ -2147,6 +2207,7 @@ static int __tty_fasync(int fd, struct file *filp, int on) { struct tty_struct *tty = file_tty(filp); + struct tty_ldisc *ldisc; unsigned long flags; int retval = 0; @@ -2157,11 +2218,17 @@ if (retval <= 0) goto out; + ldisc = tty_ldisc_ref(tty); + if (ldisc) { + if (ldisc->ops->fasync) + ldisc->ops->fasync(tty, on); + tty_ldisc_deref(ldisc); + } + if (on) { enum pid_type type; struct pid *pid; - if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = 1; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->pgrp) { pid = tty->pgrp; @@ -2172,15 +2239,10 @@ } get_pid(pid); spin_unlock_irqrestore(&tty->ctrl_lock, flags); - retval = __f_setown(filp, pid, type, 0); + __f_setown(filp, pid, type, 0); put_pid(pid); - if (retval) - goto out; - } else { - if (!tty->fasync && !waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = N_TTY_BUF_SIZE; + retval = 0; } - retval = 0; out: return retval; } @@ -2208,7 +2270,7 @@ * FIXME: does not honour flow control ?? * * Locking: - * Called functions take tty_ldisc_lock + * Called functions take tty_ldiscs_lock * current->signal->tty check is safe without locks * * FIXME: may race normal receive processing @@ -2237,7 +2299,7 @@ * * Copies the kernel idea of the window size into the user buffer. * - * Locking: tty->termios_mutex is taken to ensure the winsize data + * Locking: tty->winsize_mutex is taken to ensure the winsize data * is consistent. */ @@ -2245,9 +2307,9 @@ { int err; - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->winsize_mutex); err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->winsize_mutex); return err ? -EFAULT: 0; } @@ -2265,25 +2327,21 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) { struct pid *pgrp; - unsigned long flags; /* Lock the tty */ - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->winsize_mutex); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; - /* Get the PID values and reference them so we can - avoid holding the tty ctrl lock while sending signals */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + /* Signal the foreground process group */ + pgrp = tty_get_pgrp(tty); if (pgrp) kill_pgrp(pgrp, SIGWINCH, 1); put_pid(pgrp); tty->winsize = *ws; done: - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->winsize_mutex); return 0; } EXPORT_SYMBOL(tty_do_resize); @@ -2385,18 +2443,21 @@ * leader to set this tty as the controlling tty for the session. * * Locking: - * Takes tty_mutex() to protect tty instance + * Takes tty_lock() to serialize proc_set_tty() for this tty * Takes tasklist_lock internally to walk sessions * Takes ->siglock() when updating signal->tty */ -static int tiocsctty(struct tty_struct *tty, int arg) +static int tiocsctty(struct tty_struct *tty, struct file *file, int arg) { int ret = 0; + + tty_lock(tty); + read_lock(&tasklist_lock); + if (current->signal->leader && (task_session(current) == tty->session)) - return ret; + goto unlock; - mutex_lock(&tty_mutex); /* * The process must be a session leader and * not have a controlling tty already. @@ -2415,17 +2476,23 @@ /* * Steal it away */ - read_lock(&tasklist_lock); session_clear_tty(tty->session); - read_unlock(&tasklist_lock); } else { ret = -EPERM; goto unlock; } } - proc_set_tty(current, tty); + + /* See the comment in tty_open(). */ + if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto unlock; + } + + proc_set_tty(tty); unlock: - mutex_unlock(&tty_mutex); + read_unlock(&tasklist_lock); + tty_unlock(tty); return ret; } @@ -2450,6 +2517,27 @@ } EXPORT_SYMBOL_GPL(tty_get_pgrp); +/* + * This checks not only the pgrp, but falls back on the pid if no + * satisfactory pgrp is found. I dunno - gdb doesn't work correctly + * without this... + * + * The caller must hold rcu lock or the tasklist lock. + */ +static struct pid *session_of_pgrp(struct pid *pgrp) +{ + struct task_struct *p; + struct pid *sid = NULL; + + p = pid_task(pgrp, PIDTYPE_PGID); + if (p == NULL) + p = pid_task(pgrp, PIDTYPE_PID); + if (p != NULL) + sid = task_session(p); + + return sid; +} + /** * tiocgpgrp - get process group * @tty: tty passed by user @@ -2495,7 +2583,6 @@ struct pid *pgrp; pid_t pgrp_nr; int retval = tty_check_change(real_tty); - unsigned long flags; if (retval == -EIO) return -ENOTTY; @@ -2518,10 +2605,10 @@ if (session_of_pgrp(pgrp) != task_session(current)) goto out_unlock; retval = 0; - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock_irq(&tty->ctrl_lock); put_pid(real_tty->pgrp); real_tty->pgrp = get_pid(pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + spin_unlock_irq(&tty->ctrl_lock); out_unlock: rcu_read_unlock(); return retval; @@ -2718,23 +2805,35 @@ return 0; } -struct tty_struct *tty_pair_get_tty(struct tty_struct *tty) +static void tty_warn_deprecated_flags(struct serial_struct __user *ss) { - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER) - tty = tty->link; - return tty; + static DEFINE_RATELIMIT_STATE(depr_flags, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + char comm[TASK_COMM_LEN]; + int flags; + + if (get_user(flags, &ss->flags)) + return; + + flags &= ASYNC_DEPRECATED; + + if (flags && __ratelimit(&depr_flags)) + pr_warning("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", + __func__, get_task_comm(comm, current), flags); } -EXPORT_SYMBOL(tty_pair_get_tty); -struct tty_struct *tty_pair_get_pty(struct tty_struct *tty) +/* + * if pty, return the slave side (real_tty) + * otherwise, return self + */ +static struct tty_struct *tty_pair_get_tty(struct tty_struct *tty) { if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) - return tty; - return tty->link; + tty = tty->link; + return tty; } -EXPORT_SYMBOL(tty_pair_get_pty); /* * Split this up, as gcc can choke on it otherwise.. @@ -2803,7 +2902,7 @@ no_tty(); return 0; case TIOCSCTTY: - return tiocsctty(tty, arg); + return tiocsctty(tty, file, arg); case TIOCGPGRP: return tiocgpgrp(tty, real_tty, p); case TIOCSPGRP: @@ -2863,13 +2962,16 @@ case TCIFLUSH: case TCIOFLUSH: /* flush tty buffer and allow ldisc to process ioctl */ - tty_buffer_flush(tty); + tty_buffer_flush(tty, NULL); break; } break; + case TIOCSSERIAL: + tty_warn_deprecated_flags(p); + break; } if (tty->ops->ioctl) { - retval = (tty->ops->ioctl)(tty, cmd, arg); + retval = tty->ops->ioctl(tty, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } @@ -2896,7 +2998,7 @@ return -EINVAL; if (tty->ops->compat_ioctl) { - retval = (tty->ops->compat_ioctl)(tty, cmd, arg); + retval = tty->ops->compat_ioctl(tty, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } @@ -3025,32 +3127,37 @@ /** - * initialize_tty_struct - * @tty: tty to initialize + * alloc_tty_struct * - * This subroutine initializes a tty structure that has been newly - * allocated. + * This subroutine allocates and initializes a tty structure. * - * Locking: none - tty in question must not be exposed at this point + * Locking: none - tty in question is not exposed at this point */ -void initialize_tty_struct(struct tty_struct *tty, - struct tty_driver *driver, int idx) +struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) { - memset(tty, 0, sizeof(struct tty_struct)); + struct tty_struct *tty; + + tty = kzalloc(sizeof(*tty), GFP_KERNEL); + if (!tty) + return NULL; + kref_init(&tty->kref); tty->magic = TTY_MAGIC; tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); - mutex_init(&tty->termios_mutex); - mutex_init(&tty->ldisc_mutex); + mutex_init(&tty->throttle_mutex); + init_rwsem(&tty->termios_rwsem); + mutex_init(&tty->winsize_mutex); + init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); mutex_init(&tty->atomic_write_lock); spin_lock_init(&tty->ctrl_lock); + spin_lock_init(&tty->flow_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); @@ -3059,6 +3166,8 @@ tty->index = idx; tty_line_name(driver, idx, tty->name); tty->dev = tty_get_device(tty); + + return tty; } /** @@ -3100,10 +3209,18 @@ static int tty_cdev_add(struct tty_driver *driver, dev_t dev, unsigned int index, unsigned int count) { + int err; + /* init here, since reused cdevs cause crashes */ - cdev_init(&driver->cdevs[index], &tty_fops); - driver->cdevs[index].owner = driver->owner; - return cdev_add(&driver->cdevs[index], dev, count); + driver->cdevs[index] = cdev_alloc(); + if (!driver->cdevs[index]) + return -ENOMEM; + driver->cdevs[index]->ops = &tty_fops; + driver->cdevs[index]->owner = driver->owner; + err = cdev_add(driver->cdevs[index], dev, count); + if (err) + kobject_put(&driver->cdevs[index]->kobj); + return err; } /** @@ -3209,8 +3326,10 @@ error: put_device(dev); - if (cdev) - cdev_del(&driver->cdevs[index]); + if (cdev) { + cdev_del(driver->cdevs[index]); + driver->cdevs[index] = NULL; + } return ERR_PTR(retval); } EXPORT_SYMBOL_GPL(tty_register_device_attr); @@ -3230,8 +3349,10 @@ { device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index); - if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) - cdev_del(&driver->cdevs[index]); + if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { + cdev_del(driver->cdevs[index]); + driver->cdevs[index] = NULL; + } } EXPORT_SYMBOL(tty_unregister_device); @@ -3296,6 +3417,7 @@ kfree(driver->ports); kfree(driver->ttys); kfree(driver->termios); + kfree(driver->cdevs); kfree(driver); return ERR_PTR(err); } @@ -3324,7 +3446,7 @@ } proc_tty_unregister_driver(driver); if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) - cdev_del(&driver->cdevs[0]); + cdev_del(driver->cdevs[0]); } kfree(driver->cdevs); kfree(driver->ports); @@ -3440,59 +3562,6 @@ } EXPORT_SYMBOL(tty_devnum); -void proc_clear_tty(struct task_struct *p) -{ - unsigned long flags; - struct tty_struct *tty; - spin_lock_irqsave(&p->sighand->siglock, flags); - tty = p->signal->tty; - p->signal->tty = NULL; - spin_unlock_irqrestore(&p->sighand->siglock, flags); - tty_kref_put(tty); -} - -/* Called under the sighand lock */ - -static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) -{ - if (tty) { - unsigned long flags; - /* We should not have a session or pgrp to put here but.... */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - put_pid(tty->session); - put_pid(tty->pgrp); - tty->pgrp = get_pid(task_pgrp(tsk)); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - tty->session = get_pid(task_session(tsk)); - if (tsk->signal->tty) { - printk(KERN_DEBUG "tty not NULL!!\n"); - tty_kref_put(tsk->signal->tty); - } - } - put_pid(tsk->signal->tty_old_pgrp); - tsk->signal->tty = tty_kref_get(tty); - tsk->signal->tty_old_pgrp = NULL; -} - -static 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; - unsigned long flags; - - spin_lock_irqsave(¤t->sighand->siglock, flags); - tty = tty_kref_get(current->signal->tty); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - return tty; -} -EXPORT_SYMBOL_GPL(get_current_tty); - void tty_default_fops(struct file_operations *fops) { *fops = tty_fops; @@ -3585,6 +3654,13 @@ } static DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL); +static struct attribute *cons_dev_attrs[] = { + &dev_attr_active.attr, + NULL +}; + +ATTRIBUTE_GROUPS(cons_dev); + static struct device *consdev; void console_sysfs_notify(void) @@ -3609,12 +3685,11 @@ 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"); - consdev = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, - "console"); + consdev = device_create_with_groups(tty_class, NULL, + MKDEV(TTYAUX_MAJOR, 1), NULL, + cons_dev_groups, "console"); if (IS_ERR(consdev)) consdev = NULL; - else - WARN_ON(device_create_file(consdev, &dev_attr_active) < 0); #ifdef CONFIG_VT vty_init(&console_fops);