tty_io.c
来自「linux 内核源代码」· C语言 代码 · 共 2,498 行 · 第 1/5 页
C
2,498 行
if (tty->ldisc.num == ldisc) { tty_ldisc_put(ldisc); return 0; } /* * No more input please, we are switching. The new ldisc * will update this value in the ldisc open function */ tty->receive_room = 0; o_ldisc = tty->ldisc; o_tty = tty->link; /* * Make sure we don't change while someone holds a * reference to the line discipline. The TTY_LDISC bit * prevents anyone taking a reference once it is clear. * We need the lock to avoid racing reference takers. */ spin_lock_irqsave(&tty_ldisc_lock, flags); if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { if(tty->ldisc.refcount) { /* Free the new ldisc we grabbed. Must drop the lock first. */ spin_unlock_irqrestore(&tty_ldisc_lock, flags); tty_ldisc_put(ldisc); /* * There are several reasons we may be busy, including * random momentary I/O traffic. We must therefore * retry. We could distinguish between blocking ops * and retries if we made tty_ldisc_wait() smarter. That * is up for discussion. */ if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) return -ERESTARTSYS; goto restart; } if(o_tty && o_tty->ldisc.refcount) { spin_unlock_irqrestore(&tty_ldisc_lock, flags); tty_ldisc_put(ldisc); if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) return -ERESTARTSYS; goto restart; } } /* if the TTY_LDISC bit is set, then we are racing against another ldisc change */ if (!test_bit(TTY_LDISC, &tty->flags)) { spin_unlock_irqrestore(&tty_ldisc_lock, flags); tty_ldisc_put(ldisc); ld = tty_ldisc_ref_wait(tty); tty_ldisc_deref(ld); goto restart; } clear_bit(TTY_LDISC, &tty->flags); if (o_tty) clear_bit(TTY_LDISC, &o_tty->flags); spin_unlock_irqrestore(&tty_ldisc_lock, flags); /* * From this point on we know nobody has an ldisc * usage reference, nor can they obtain one until * we say so later on. */ work = cancel_delayed_work(&tty->buf.work); /* * Wait for ->hangup_work and ->buf.work handlers to terminate */ flush_scheduled_work(); /* Shutdown the current discipline. */ if (tty->ldisc.close) (tty->ldisc.close)(tty); /* Now set up the new line discipline. */ tty_ldisc_assign(tty, ld); tty_set_termios_ldisc(tty, ldisc); if (tty->ldisc.open) retval = (tty->ldisc.open)(tty); if (retval < 0) { tty_ldisc_put(ldisc); /* There is an outstanding reference here so this is safe */ tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num)); tty_set_termios_ldisc(tty, tty->ldisc.num); if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) { tty_ldisc_put(o_ldisc.num); /* This driver is always present */ tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); tty_set_termios_ldisc(tty, N_TTY); if (tty->ldisc.open) { int r = tty->ldisc.open(tty); if (r < 0) panic("Couldn't open N_TTY ldisc for " "%s --- error %d.", tty_name(tty, buf), r); } } } /* At this point we hold a reference to the new ldisc and a a reference to the old ldisc. If we ended up flipping back to the existing ldisc we have two references to it */ if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc) tty->driver->set_ldisc(tty); tty_ldisc_put(o_ldisc.num); /* * Allow ldisc referencing to occur as soon as the driver * ldisc callback completes. */ tty_ldisc_enable(tty); if (o_tty) tty_ldisc_enable(o_tty); /* Restart it in case no characters kick it off. Safe if already running */ if (work) schedule_delayed_work(&tty->buf.work, 1); return retval;}/** * get_tty_driver - find device of a tty * @dev_t: device identifier * @index: returns the index of the tty * * This routine returns a tty driver structure, given a device number * and also passes back the index number. * * Locking: caller must hold tty_mutex */static struct tty_driver *get_tty_driver(dev_t device, int *index){ struct tty_driver *p; list_for_each_entry(p, &tty_drivers, tty_drivers) { dev_t base = MKDEV(p->major, p->minor_start); if (device < base || device >= base + p->num) continue; *index = device - base; return p; } return NULL;}/** * tty_check_change - check for POSIX terminal changes * @tty: tty to check * * If we try to write to, or set the state of, a terminal and we're * not in the foreground, send a SIGTTOU. If the signal is blocked or * ignored, go ahead and perform the operation. (POSIX 7.2) * * Locking: none */int tty_check_change(struct tty_struct * tty){ if (current->signal->tty != tty) return 0; if (!tty->pgrp) { printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n"); return 0; } if (task_pgrp(current) == tty->pgrp) return 0; if (is_ignored(SIGTTOU)) return 0; if (is_current_pgrp_orphaned()) return -EIO; kill_pgrp(task_pgrp(current), SIGTTOU, 1); set_thread_flag(TIF_SIGPENDING); return -ERESTARTSYS;}EXPORT_SYMBOL(tty_check_change);static ssize_t hung_up_tty_read(struct file * file, char __user * buf, size_t count, loff_t *ppos){ return 0;}static ssize_t hung_up_tty_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ return -EIO;}/* No kernel lock held - none needed ;) */static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait){ return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;}static int hung_up_tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ return cmd == TIOCSPGRP ? -ENOTTY : -EIO;}static long hung_up_tty_compat_ioctl(struct file * file, unsigned int cmd, unsigned long arg){ return cmd == TIOCSPGRP ? -ENOTTY : -EIO;}static const struct file_operations tty_fops = { .llseek = no_llseek, .read = tty_read, .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync,};#ifdef CONFIG_UNIX98_PTYSstatic const struct file_operations ptmx_fops = { .llseek = no_llseek, .read = tty_read, .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, .open = ptmx_open, .release = tty_release, .fasync = tty_fasync,};#endifstatic const struct file_operations console_fops = { .llseek = no_llseek, .read = tty_read, .write = redirected_tty_write, .poll = tty_poll, .ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync,};static const struct file_operations hung_up_tty_fops = { .llseek = no_llseek, .read = hung_up_tty_read, .write = hung_up_tty_write, .poll = hung_up_tty_poll, .ioctl = hung_up_tty_ioctl, .compat_ioctl = hung_up_tty_compat_ioctl, .release = tty_release,};static DEFINE_SPINLOCK(redirect_lock);static struct file *redirect;/** * tty_wakeup - request more data * @tty: terminal * * Internal and external helper for wakeups of tty. This function * informs the line discipline if present that the driver is ready * to receive more output data. */ void tty_wakeup(struct tty_struct *tty){ struct tty_ldisc *ld; if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) { ld = tty_ldisc_ref(tty); if(ld) { if(ld->write_wakeup) ld->write_wakeup(tty); tty_ldisc_deref(ld); } } wake_up_interruptible(&tty->write_wait);}EXPORT_SYMBOL_GPL(tty_wakeup);/** * tty_ldisc_flush - flush line discipline queue * @tty: tty * * Flush the line discipline queue (if any) for this tty. If there * is no line discipline active this is a no-op. */ void tty_ldisc_flush(struct tty_struct *tty){ struct tty_ldisc *ld = tty_ldisc_ref(tty); if(ld) { if(ld->flush_buffer) ld->flush_buffer(tty); tty_ldisc_deref(ld); } tty_buffer_flush(tty);}EXPORT_SYMBOL_GPL(tty_ldisc_flush);/** * tty_reset_termios - reset terminal state * @tty: tty to reset * * Restore a terminal to the driver default state */static void tty_reset_termios(struct tty_struct *tty){ mutex_lock(&tty->termios_mutex); *tty->termios = tty->driver->init_termios; tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); mutex_unlock(&tty->termios_mutex);} /** * do_tty_hangup - actual handler for hangup events * @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 * have the appropriate locks for what we're doing. * * The hangup event clears any pending redirections onto the hung up * device. It ensures future writes will error and it does the needed * line discipline hangup and signal delivery. The tty object itself * remains intact. * * Locking: * BKL * 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(struct work_struct *work){ 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; struct tty_ldisc *ld; int closecount = 0, n; if (!tty) return; /* inuse_filps is protected by the single kernel lock */ lock_kernel(); spin_lock(&redirect_lock); if (redirect && redirect->private_data == tty) { f = redirect; redirect = NULL; } spin_unlock(&redirect_lock); check_tty_count(tty, "do_tty_hangup"); file_list_lock(); /* This breaks for file handles being sent over AF_UNIX sockets ? */ list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) { if (filp->f_op->write == redirected_tty_write) cons_filp = filp; if (filp->f_op->write != tty_write) continue; closecount++; tty_fasync(-1, filp, 0); /* can't block */ filp->f_op = &hung_up_tty_fops; } file_list_unlock(); /* FIXME! What are the locking issues here? This may me overdoing things.. * this question is especially important now that we've removed the irqlock. */ ld = tty_ldisc_ref(tty); if(ld != NULL) /* We may have no line discipline at this point */ { if (ld->flush_buffer) ld->flush_buffer(tty); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && ld->write_wakeup) ld->write_wakeup(tty); if (ld->hangup) ld->hangup(tty); } /* FIXME: Once we trust the LDISC code better we can wait here for ldisc completion and fix the driver call race */ wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); /* * Shutdown the current line discipline, and reset it to * N_TTY. */ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) tty_reset_termios(tty); /* Defer ldisc switch */ /* tty_deferred_ldisc_switch(N_TTY); This should get done automatically when the port closes and tty_release is called */ 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; 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 */ if (tty->pgrp) p->signal->tty_old_pgrp = get_pid(tty->pgrp); spin_unlock_irq(&p->sighand->siglock); } while_each_pid_task(tty->session, PIDTYPE_SID, p); } read_unlock(&tasklist_lock); tty->flags = 0; 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 * cannot just call hangup() because that will cause * tty->count and state->count to go out of sync. * So we just call close() the right number of times. */ if (cons_filp) { if (tty->driver->close) for (n = 0; n < closecount; n++) tty->driver->close(tty, cons_filp); } else if (tty->driver->hangup) (tty->driver->hangup)(tty); /* We don't want to have driver/ldisc interactions beyond the ones we did here. The driver layer expects no calls after ->hangup() from the ldisc side. However we can't yet guarantee all that */ set_bit(TTY_HUPPED, &tty->flags); if (ld) { tty_ldisc_enable(tty); tty_ldisc_deref(ld); } unlock_kernel(); if (f) fput(f);}/** * tty_hangup - trigger a hangup event * @tty: tty to hangup * * A carrier loss (virtual or otherwise) has occurred on this like * schedule a hangup sequence to run after this event. */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 schedule_work(&tty->hangup_work);}EXPORT_SYMBOL(tty_hangup);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?