tty_io.c
来自「linux 内核源代码」· C语言 代码 · 共 2,498 行 · 第 1/5 页
C
2,498 行
static int init_dev(struct tty_driver *driver, int idx, struct tty_struct **ret_tty){ struct tty_struct *tty, *o_tty; struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; int retval = 0; /* check whether we're reopening an existing tty */ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { tty = devpts_get_tty(idx); /* * If we don't have a tty here on a slave open, it's because * the master already started the close process and there's * no relation between devpts file and tty anymore. */ if (!tty && driver->subtype == PTY_TYPE_SLAVE) { retval = -EIO; goto end_init; } /* * It's safe from now on because init_dev() is called with * tty_mutex held and release_dev() won't change tty->count * or tty->flags without having to grab tty_mutex */ if (tty && driver->subtype == PTY_TYPE_MASTER) tty = tty->link; } else { tty = driver->ttys[idx]; } if (tty) goto fast_track; /* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated * and the allocated memory released. (Except that the termios * and locked termios may be retained.) */ if (!try_module_get(driver->owner)) { retval = -ENODEV; goto end_init; } o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; tty = alloc_tty_struct(); if(!tty) goto fail_no_mem; initialize_tty_struct(tty); tty->driver = driver; tty->index = idx; tty_line_name(driver, idx, tty->name); if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { tp_loc = &tty->termios; ltp_loc = &tty->termios_locked; } else { tp_loc = &driver->termios[idx]; ltp_loc = &driver->termios_locked[idx]; } if (!*tp_loc) { tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); if (!tp) goto free_mem_out; *tp = driver->init_termios; } if (!*ltp_loc) { ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); if (!ltp) goto free_mem_out; } if (driver->type == TTY_DRIVER_TYPE_PTY) { o_tty = alloc_tty_struct(); if (!o_tty) goto free_mem_out; initialize_tty_struct(o_tty); o_tty->driver = driver->other; o_tty->index = idx; tty_line_name(driver->other, idx, o_tty->name); if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { o_tp_loc = &o_tty->termios; o_ltp_loc = &o_tty->termios_locked; } else { o_tp_loc = &driver->other->termios[idx]; o_ltp_loc = &driver->other->termios_locked[idx]; } if (!*o_tp_loc) { o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); if (!o_tp) goto free_mem_out; *o_tp = driver->other->init_termios; } if (!*o_ltp_loc) { o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); if (!o_ltp) goto free_mem_out; } /* * Everything allocated ... set up the o_tty structure. */ if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) { driver->other->ttys[idx] = o_tty; } if (!*o_tp_loc) *o_tp_loc = o_tp; if (!*o_ltp_loc) *o_ltp_loc = o_ltp; o_tty->termios = *o_tp_loc; o_tty->termios_locked = *o_ltp_loc; driver->other->refcount++; if (driver->subtype == PTY_TYPE_MASTER) o_tty->count++; /* Establish the links in both directions */ tty->link = o_tty; o_tty->link = tty; } /* * All structures have been allocated, so now we install them. * Failures after this point use release_tty to clean up, so * there's no need to null out the local pointers. */ if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) { driver->ttys[idx] = tty; } if (!*tp_loc) *tp_loc = tp; if (!*ltp_loc) *ltp_loc = ltp; tty->termios = *tp_loc; tty->termios_locked = *ltp_loc; /* Compatibility until drivers always set this */ tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); driver->refcount++; tty->count++; /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ if (tty->ldisc.open) { retval = (tty->ldisc.open)(tty); if (retval) goto release_mem_out; } if (o_tty && o_tty->ldisc.open) { retval = (o_tty->ldisc.open)(o_tty); if (retval) { if (tty->ldisc.close) (tty->ldisc.close)(tty); goto release_mem_out; } tty_ldisc_enable(o_tty); } tty_ldisc_enable(tty); goto success; /* * This fast open can be used if the tty is already open. * No memory is allocated, and the only failures are from * attempting to open a closing tty or attempting multiple * opens on a pty master. */fast_track: if (test_bit(TTY_CLOSING, &tty->flags)) { retval = -EIO; goto end_init; } 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) { retval = -EIO; goto end_init; } tty->link->count++; } tty->count++; tty->driver = driver; /* N.B. why do this every time?? */ /* FIXME */ if(!test_bit(TTY_LDISC, &tty->flags)) printk(KERN_ERR "init_dev but no ldisc\n");success: *ret_tty = tty; /* All paths come through here to release the mutex */end_init: return retval; /* Release locally allocated memory ... nothing placed in slots */free_mem_out: kfree(o_tp); if (o_tty) free_tty_struct(o_tty); kfree(ltp); kfree(tp); free_tty_struct(tty);fail_no_mem: module_put(driver->owner); retval = -ENOMEM; goto end_init; /* call the tty release_tty routine to clean out this slot */release_mem_out: if (printk_ratelimit()) printk(KERN_INFO "init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); goto end_init;}/** * release_one_tty - release tty structure memory * * Releases memory associated with a tty structure, and clears out the * driver table slots. This function is called when a device is no longer * in use. It also gets called when setup of a device fails. * * Locking: * tty_mutex - sometimes only * takes the file list lock internally when working on the list * of ttys that the driver keeps. * FIXME: should we require tty_mutex is held here ?? */static void release_one_tty(struct tty_struct *tty, int idx){ int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; struct ktermios *tp; if (!devpts) tty->driver->ttys[idx] = NULL; if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { tp = tty->termios; if (!devpts) tty->driver->termios[idx] = NULL; kfree(tp); tp = tty->termios_locked; if (!devpts) tty->driver->termios_locked[idx] = NULL; kfree(tp); } tty->magic = 0; tty->driver->refcount--; file_list_lock(); list_del_init(&tty->tty_files); file_list_unlock(); free_tty_struct(tty);}/** * release_tty - release tty structure memory * * Release both @tty and a possible linked partner (think pty pair), * and decrement the refcount of the backing module. * * Locking: * tty_mutex - sometimes only * takes the file list lock internally when working on the list * of ttys that the driver keeps. * FIXME: should we require tty_mutex is held here ?? */static void release_tty(struct tty_struct *tty, int idx){ struct tty_driver *driver = tty->driver; if (tty->link) release_one_tty(tty->link, idx); release_one_tty(tty, idx); module_put(driver->owner);}/* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the * same time, as interrupts might otherwise get the wrong pointers. * * WSH 09/09/97: rewritten to avoid some nasty race conditions that could * lead to double frees or releasing memory still in use. */static void release_dev(struct file * filp){ struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts; int idx; char buf[64]; unsigned long flags; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "release_dev")) return; check_tty_count(tty, "release_dev"); tty_fasync(-1, filp, 0); idx = tty->index; pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; o_tty = tty->link;#ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver->num) { printk(KERN_DEBUG "release_dev: bad idx when trying to " "free (%s)\n", tty->name); return; } if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { if (tty != tty->driver->ttys[idx]) { printk(KERN_DEBUG "release_dev: driver.table[%d] not tty " "for (%s)\n", idx, tty->name); return; } if (tty->termios != tty->driver->termios[idx]) { printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios " "for (%s)\n", idx, tty->name); return; } if (tty->termios_locked != tty->driver->termios_locked[idx]) { printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not " "termios_locked for (%s)\n", idx, tty->name); return; } }#endif#ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "release_dev of %s (tty count=%d)...", tty_name(tty, buf), tty->count);#endif#ifdef TTY_PARANOIA_CHECK if (tty->driver->other && !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { if (o_tty != tty->driver->other->ttys[idx]) { printk(KERN_DEBUG "release_dev: other->table[%d] " "not o_tty for (%s)\n", idx, tty->name); return; } if (o_tty->termios != tty->driver->other->termios[idx]) { printk(KERN_DEBUG "release_dev: other->termios[%d] " "not o_termios for (%s)\n", idx, tty->name); return; } if (o_tty->termios_locked != tty->driver->other->termios_locked[idx]) { printk(KERN_DEBUG "release_dev: other->termios_locked[" "%d] not o_termios_locked for (%s)\n", idx, tty->name); return; } if (o_tty->link != tty) { printk(KERN_DEBUG "release_dev: bad pty pointers\n"); return; } }#endif if (tty->driver->close) tty->driver->close(tty, filp); /* * 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 * wait queues and kick everyone out _before_ actually starting to * close. This ensures that we won't block while releasing the tty * structure. * * 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, * 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_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); do_sleep = 0; if (tty_closing) { if (waitqueue_active(&tty->read_wait)) { wake_up(&tty->read_wait); do_sleep++; } if (waitqueue_active(&tty->write_wait)) { wake_up(&tty->write_wait); do_sleep++; } } if (o_tty_closing) { if (waitqueue_active(&o_tty->read_wait)) { wake_up(&o_tty->read_wait); do_sleep++; } if (waitqueue_active(&o_tty->write_wait)) { wake_up(&o_tty->write_wait); do_sleep++; } } if (!do_sleep) break; printk(KERN_WARNING "release_dev: %s: read/write wait queue " "active!\n", tty_name(tty, buf)); mutex_unlock(&tty_mutex); schedule(); } /* * 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. */ if (pty_master) { if (--o_tty->count < 0) { printk(KERN_WARNING "release_dev: bad pty slave count " "(%d) for %s\n", o_tty->count, tty_name(o_tty, buf)); o_tty->count = 0; } } if (--tty->count < 0) { printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n", tty->count, tty_name(tty, buf)); tty->count = 0; } /* * We've decremented tty->count, so we need to remove this file * descriptor off the tty->tty_files list; this serves two * purposes: * - check_tty_count sees the correct number of file descriptors * associated with this tty. * - do_tty_hangup no longer sees this file descriptor as * something that needs to be handled for hangups. */ file_kill(filp); filp->private_data = NULL; /* * 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) { rea
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?