patch-2.1.44 linux/drivers/char/tty_io.c
Next file: linux/drivers/char/vc_screen.c
Previous file: linux/drivers/char/sysrq.c
Back to the patch index
Back to the overall index
- Lines: 756
- Date:
Sat Jun 28 10:31:36 1997
- Orig file:
v2.1.43/linux/drivers/char/tty_io.c
- Orig date:
Mon Jun 16 16:35:55 1997
diff -u --recursive --new-file v2.1.43/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
@@ -45,9 +45,12 @@
* Restrict vt switching via ioctl()
* -- grif@cs.ucr.edu, 5-Dec-95
*
- * Move console and virtual terminal code to more apropriate files,
+ * Move console and virtual terminal code to more appropriate files,
* implement CONFIG_VT and generalize console device interface.
* -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
+ *
+ * Rewrote init_dev and release_dev to eliminate races.
+ * -- Bill Hawes <whawes@star.net>, June 97
*/
#include <linux/config.h>
@@ -90,8 +93,8 @@
#undef TTY_DEBUG_HANGUP
-#define TTY_PARANOIA_CHECK
-#define CHECK_TTY_COUNT
+#define TTY_PARANOIA_CHECK 1
+#define CHECK_TTY_COUNT 1
struct termios tty_std_termios; /* for the benefit of tty drivers */
struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */
@@ -651,18 +654,31 @@
(unsigned int)count);
}
+/* Semaphore to protect creating and releasing a tty */
+static struct semaphore tty_sem = MUTEX;
+static void down_tty_sem(int index)
+{
+ down(&tty_sem);
+}
+static void up_tty_sem(int index)
+{
+ up(&tty_sem);
+}
+static void release_mem(struct tty_struct *tty, int idx);
+
/*
- * This is so ripe with races that you should *really* not touch this
- * unless you know exactly what you are doing. All the changes have to be
- * made atomically, or there may be incorrect pointers all over the place.
+ * WSH 06/09/97: Rewritten to remove races and properly clean up after a
+ * failed open. The new code protects the open with a semaphore, so it's
+ * really quite straightforward. The semaphore locking can probably be
+ * relaxed for the (most common) case of reopening a tty.
*/
static int init_dev(kdev_t device, struct tty_struct **ret_tty)
{
- struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
+ struct tty_struct *tty, *o_tty;
struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
struct tty_driver *driver;
- int retval;
+ int retval=0;
int idx;
driver = get_tty_driver(device);
@@ -670,175 +686,236 @@
return -ENODEV;
idx = MINOR(device) - driver->minor_start;
- tty = o_tty = NULL;
+ tty = driver->table[idx];
+
+ /*
+ * Check whether we need to acquire the tty semaphore to avoid
+ * race conditions. For now, play it safe.
+ */
+ down_tty_sem(idx);
+
+ /* check whether we're reopening an existing tty */
+ 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.)
+ */
+
+ o_tty = NULL;
tp = o_tp = NULL;
ltp = o_ltp = NULL;
- o_tty_loc = NULL;
- o_tp_loc = o_ltp_loc = NULL;
- tty_loc = &driver->table[idx];
- tp_loc = &driver->termios[idx];
- ltp_loc = &driver->termios_locked[idx];
+ tty = (struct tty_struct*) get_free_page(GFP_KERNEL);
+ if(!tty)
+ goto fail_no_mem;
+ initialize_tty_struct(tty);
+ tty->device = device;
+ tty->driver = *driver;
-repeat:
- retval = -EIO;
- if (driver->type == TTY_DRIVER_TYPE_PTY &&
- driver->subtype == PTY_TYPE_MASTER &&
- *tty_loc && (*tty_loc)->count)
- goto end_init;
- retval = -ENOMEM;
- if (!*tty_loc && !tty) {
- if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
- goto end_init;
- initialize_tty_struct(tty);
- tty->device = device;
- tty->driver = *driver;
- goto repeat;
- }
- if (!*tp_loc && !tp) {
+ tp_loc = &driver->termios[idx];
+ if (!*tp_loc) {
tp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!tp)
- goto end_init;
+ goto free_mem_out;
*tp = driver->init_termios;
- goto repeat;
}
- if (!*ltp_loc && !ltp) {
+
+ ltp_loc = &driver->termios_locked[idx];
+ if (!*ltp_loc) {
ltp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!ltp)
- goto end_init;
+ goto free_mem_out;
memset(ltp, 0, sizeof(struct termios));
- goto repeat;
}
+
if (driver->type == TTY_DRIVER_TYPE_PTY) {
- o_tty_loc = &driver->other->table[idx];
- o_tp_loc = &driver->other->termios[idx];
- o_ltp_loc = &driver->other->termios_locked[idx];
+ o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL);
+ if (!o_tty)
+ goto free_mem_out;
+ initialize_tty_struct(o_tty);
+ o_tty->device = (kdev_t) MKDEV(driver->other->major,
+ driver->other->minor_start + idx);
+ o_tty->driver = *driver->other;
- if (!*o_tty_loc && !o_tty) {
- kdev_t o_device;
-
- o_tty = (struct tty_struct *)
- get_free_page(GFP_KERNEL);
- if (!o_tty)
- goto end_init;
- o_device = MKDEV(driver->other->major,
- driver->other->minor_start + idx);
- initialize_tty_struct(o_tty);
- o_tty->device = o_device;
- o_tty->driver = *driver->other;
- goto repeat;
- }
- if (!*o_tp_loc && !o_tp) {
+ o_tp_loc = &driver->other->termios[idx];
+ if (!*o_tp_loc) {
o_tp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_tp)
- goto end_init;
+ goto free_mem_out;
*o_tp = driver->other->init_termios;
- goto repeat;
}
- if (!*o_ltp_loc && !o_ltp) {
+
+ o_ltp_loc = &driver->other->termios_locked[idx];
+ if (!*o_ltp_loc) {
o_ltp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_ltp)
- goto end_init;
+ goto free_mem_out;
memset(o_ltp, 0, sizeof(struct termios));
- goto repeat;
}
-
+
+ /*
+ * Everything allocated ... set up the o_tty structure.
+ */
+ driver->other->table[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;
}
- /* Now we have allocated all the structures: update all the pointers.. */
- if (!*tp_loc) {
+
+ /*
+ * All structures have been allocated, so now we install them.
+ * Failures after this point use release_mem to clean up, so
+ * there's no need to null out the local pointers.
+ */
+ driver->table[idx] = tty;
+ if (!*tp_loc)
*tp_loc = tp;
- tp = NULL;
- }
- if (!*ltp_loc) {
+ if (!*ltp_loc)
*ltp_loc = ltp;
- ltp = NULL;
+ tty->termios = *tp_loc;
+ tty->termios_locked = *ltp_loc;
+ (*driver->refcount)++;
+ tty->count++;
+
+ /*
+ * Structures all installed ... call the ldisc open routines.
+ * If we fail here just call release_mem to clean up. No need
+ * to decrement the use counts, as release_mem doesn't care.
+ */
+ if (tty->ldisc.open) {
+ retval = (tty->ldisc.open)(tty);
+ if (retval)
+ goto release_mem_out;
}
- if (!*tty_loc) {
- tty->termios = *tp_loc;
- tty->termios_locked = *ltp_loc;
- *tty_loc = tty;
- (*driver->refcount)++;
- (*tty_loc)->count++;
- if (tty->ldisc.open) {
- retval = (tty->ldisc.open)(tty);
- if (retval < 0) {
- (*tty_loc)->count--;
- tty = NULL;
- goto end_init;
- }
- }
- tty = NULL;
- } else {
- if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
- printk("Attempt to open closing tty %s.\n",
- tty_name(*tty_loc));
- printk("Ack!!!! This should never happen!!\n");
- return -EINVAL;
+ 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_loc)->count++;
}
- if (driver->type == TTY_DRIVER_TYPE_PTY) {
- if (!*o_tp_loc) {
- *o_tp_loc = o_tp;
- o_tp = NULL;
- }
- if (!*o_ltp_loc) {
- *o_ltp_loc = o_ltp;
- o_ltp = NULL;
- }
- if (!*o_tty_loc) {
- o_tty->termios = *o_tp_loc;
- o_tty->termios_locked = *o_ltp_loc;
- *o_tty_loc = o_tty;
- (*driver->other->refcount)++;
- if (o_tty->ldisc.open) {
- retval = (o_tty->ldisc.open)(o_tty);
- if (retval < 0) {
- (*tty_loc)->count--;
- o_tty = NULL;
- goto end_init;
- }
- }
- o_tty = NULL;
+ 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 (tty->flags & (1 << TTY_CLOSING)) {
+ 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_loc)->link = *o_tty_loc;
- (*o_tty_loc)->link = *tty_loc;
- if (driver->subtype == PTY_TYPE_MASTER)
- (*o_tty_loc)->count++;
+ tty->link->count++;
}
- (*tty_loc)->driver = *driver;
- *ret_tty = *tty_loc;
- retval = 0;
+ tty->count++;
+ tty->driver = *driver; /* N.B. why do this every time?? */
+
+success:
+ *ret_tty = tty;
+
+ /* All paths come through here to release the semaphore */
end_init:
- if (tty)
- free_page((unsigned long) tty);
- if (o_tty)
- free_page((unsigned long) o_tty);
- if (tp)
- kfree_s(tp, sizeof(struct termios));
+ up_tty_sem(idx);
+ return retval;
+
+ /* Release locally allocated memory ... nothing placed in slots */
+free_mem_out:
if (o_tp)
kfree_s(o_tp, sizeof(struct termios));
+ if (o_tty)
+ free_page((unsigned long) o_tty);
if (ltp)
kfree_s(ltp, sizeof(struct termios));
- if (o_ltp)
- kfree_s(o_ltp, sizeof(struct termios));
- return retval;
+ if (tp)
+ kfree_s(tp, sizeof(struct termios));
+ free_page((unsigned long) tty);
+
+fail_no_mem:
+ retval = -ENOMEM;
+ goto end_init;
+
+ /* call the tty release_mem routine to clean out this slot */
+release_mem_out:
+ printk("init_dev: ldisc open failed, clearing slot %d\n", idx);
+ release_mem(tty, idx);
+ goto end_init;
+}
+
+/*
+ * Releases memory associated with a tty structure, and clears out the
+ * driver table slots.
+ */
+static void release_mem(struct tty_struct *tty, int idx)
+{
+ struct tty_struct *o_tty;
+ struct termios *tp;
+
+ if ((o_tty = tty->link) != NULL) {
+ o_tty->driver.table[idx] = NULL;
+ if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+ tp = o_tty->driver.termios[idx];
+ o_tty->driver.termios[idx] = NULL;
+ kfree_s(tp, sizeof(struct termios));
+ }
+ o_tty->magic = 0;
+ (*o_tty->driver.refcount)--;
+ free_page((unsigned long) o_tty);
+ }
+
+ tty->driver.table[idx] = NULL;
+ if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+ tp = tty->driver.termios[idx];
+ tty->driver.termios[idx] = NULL;
+ kfree_s(tp, sizeof(struct termios));
+ }
+ tty->magic = 0;
+ (*tty->driver.refcount)--;
+ free_page((unsigned long) tty);
}
/*
* 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;
- struct termios *tp, *o_tp, *ltp, *o_ltp;
- struct task_struct *p;
+ int pty_master, tty_closing, o_tty_closing, do_sleep;
int idx;
tty = (struct tty_struct *)filp->private_data;
@@ -849,10 +926,11 @@
tty_fasync(filp->f_inode, filp, 0);
- tp = tty->termios;
- ltp = tty->termios_locked;
-
idx = MINOR(tty->device) - tty->driver.minor_start;
+ pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver.subtype == PTY_TYPE_MASTER);
+ o_tty = tty->link;
+
#ifdef TTY_PARANOIA_CHECK
if (idx < 0 || idx >= tty->driver.num) {
printk("release_dev: bad idx when trying to free (%s)\n",
@@ -864,15 +942,15 @@
idx, kdevname(tty->device));
return;
}
- if (tp != tty->driver.termios[idx]) {
- printk("release_dev: driver.termios[%d] not termios for ("
- "%s)\n",
+ if (tty->termios != tty->driver.termios[idx]) {
+ printk("release_dev: driver.termios[%d] not termios "
+ "for (%s)\n",
idx, kdevname(tty->device));
return;
}
- if (ltp != tty->driver.termios_locked[idx]) {
- printk("release_dev: driver.termios_locked[%d] not termios_locked for ("
- "%s)\n",
+ if (tty->termios_locked != tty->driver.termios_locked[idx]) {
+ printk("release_dev: driver.termios_locked[%d] not "
+ "termios_locked for (%s)\n",
idx, kdevname(tty->device));
return;
}
@@ -883,10 +961,6 @@
tty->count);
#endif
- o_tty = tty->link;
- o_tp = (o_tty) ? o_tty->termios : NULL;
- o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
-
#ifdef TTY_PARANOIA_CHECK
if (tty->driver.other) {
if (o_tty != tty->driver.other->table[idx]) {
@@ -895,34 +969,90 @@
idx, kdevname(tty->device));
return;
}
- if (o_tp != tty->driver.other->termios[idx]) {
- printk("release_dev: other->termios[%d] not o_termios for ("
- "%s)\n",
+ if (o_tty->termios != tty->driver.other->termios[idx]) {
+ printk("release_dev: other->termios[%d] not o_termios "
+ "for (%s)\n",
idx, kdevname(tty->device));
return;
}
- if (o_ltp != tty->driver.other->termios_locked[idx]) {
- printk("release_dev: other->termios_locked[%d] not o_termios_locked for ("
- "%s)\n",
+ if (o_tty->termios_locked !=
+ tty->driver.other->termios_locked[idx]) {
+ printk("release_dev: other->termios_locked[%d] not "
+ "o_termios_locked for (%s)\n",
idx, kdevname(tty->device));
return;
}
-
if (o_tty->link != tty) {
printk("release_dev: bad pty pointers\n");
return;
}
}
#endif
-
+ /*
+ * 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) {
+ 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("release_dev: %s: read/write wait queue active!\n",
+ tty_name(tty));
+ 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 (tty->driver.close)
tty->driver.close(tty, filp);
- if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- tty->driver.subtype == PTY_TYPE_MASTER) {
- if (--tty->link->count < 0) {
+
+ if (pty_master) {
+ if (--o_tty->count < 0) {
printk("release_dev: bad pty slave count (%d) for %s\n",
- tty->count, tty_name(tty));
- tty->link->count = 0;
+ o_tty->count, tty_name(o_tty));
+ o_tty->count = 0;
}
}
if (--tty->count < 0) {
@@ -930,60 +1060,50 @@
tty->count, tty_name(tty));
tty->count = 0;
}
- if (tty->count)
- return;
/*
- * Sanity check --- if tty->count is zero, there shouldn't be
- * any waiters on tty->read_wait or tty->write_wait. But just
- * in case....
+ * 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.
*/
- while (1) {
- if (waitqueue_active(&tty->read_wait)) {
- printk("release_dev: %s: read_wait active?!?\n",
- tty_name(tty));
- wake_up(&tty->read_wait);
- } else if (waitqueue_active(&tty->write_wait)) {
- printk("release_dev: %s: write_wait active?!?\n",
- tty_name(tty));
- wake_up(&tty->write_wait);
- } else
- break;
- schedule();
- }
-
+ if(tty_closing)
+ tty->flags |= (1 << TTY_CLOSING);
+ if(o_tty_closing)
+ o_tty->flags |= (1 << TTY_CLOSING);
+
/*
- * We're committed; at this point, we must not block!
+ * If _either_ side is closing, make sure there aren't any
+ * processes that still think tty or o_tty is their controlling
+ * tty. Also, clear redirect if it points to either tty.
*/
- if (o_tty) {
- if (o_tty->count)
- return;
- tty->driver.other->table[idx] = NULL;
- tty->driver.other->termios[idx] = NULL;
- kfree_s(o_tp, sizeof(struct termios));
+ if (tty_closing || o_tty_closing) {
+ struct task_struct *p;
+
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ if (p->tty == tty || (o_tty && p->tty == o_tty))
+ p->tty = NULL;
+ }
+ read_unlock(&tasklist_lock);
+
+ if (redirect == tty || (o_tty && redirect == o_tty))
+ redirect = NULL;
}
+
+ /* check whether both sides are closing ... */
+ if (!tty_closing || (o_tty && !o_tty_closing))
+ return;
+ filp->private_data = 0;
#ifdef TTY_DEBUG_HANGUP
printk("freeing tty structure...");
#endif
- tty->flags |= (1 << TTY_CLOSING);
/*
- * Make sure there aren't any processes that still think this
- * tty is their controlling tty.
- */
- read_lock(&tasklist_lock);
- for_each_task(p) {
- if (p->tty == tty)
- p->tty = NULL;
- if (o_tty && p->tty == o_tty)
- p->tty = NULL;
- }
- read_unlock(&tasklist_lock);
-
- /*
- * Shutdown the current line discipline, and reset it to
- * N_TTY.
+ * Shutdown the current line discipline, and reset it to N_TTY.
+ * N.B. why reset ldisc when we're releasing the memory??
*/
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
@@ -995,41 +1115,34 @@
o_tty->ldisc = ldiscs[N_TTY];
}
- tty->driver.table[idx] = NULL;
- if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
- tty->driver.termios[idx] = NULL;
- kfree_s(tp, sizeof(struct termios));
- }
- if (tty == redirect || o_tty == redirect)
- redirect = NULL;
/*
* Make sure that the tty's task queue isn't activated. If it
- * is, take it out of the linked list.
+ * is, take it out of the linked list. The tqueue isn't used by
+ * pty's, so skip the test for them.
*/
- spin_lock_irq(&tqueue_lock);
- if (tty->flip.tqueue.sync) {
- struct tq_struct *tq, *prev;
-
- for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- if (tq == &tty->flip.tqueue) {
- if (prev)
- prev->next = tq->next;
- else
- tq_timer = tq->next;
- break;
+ if (tty->driver.type != TTY_DRIVER_TYPE_PTY) {
+ spin_lock_irq(&tqueue_lock);
+ if (tty->flip.tqueue.sync) {
+ struct tq_struct *tq, *prev;
+
+ for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
+ if (tq == &tty->flip.tqueue) {
+ if (prev)
+ prev->next = tq->next;
+ else
+ tq_timer = tq->next;
+ break;
+ }
}
}
+ spin_unlock_irq(&tqueue_lock);
}
- spin_unlock_irq(&tqueue_lock);
- tty->magic = 0;
- (*tty->driver.refcount)--;
- free_page((unsigned long) tty);
- filp->private_data = 0;
- if (o_tty) {
- o_tty->magic = 0;
- (*o_tty->driver.refcount)--;
- free_page((unsigned long) o_tty);
- }
+
+ /*
+ * The release_mem function takes care of the details of clearing
+ * the slots and preserving the termios structure.
+ */
+ release_mem(tty, idx);
}
/*
@@ -1077,6 +1190,7 @@
retval = init_dev(device, &tty);
if (retval)
return retval;
+ /* N.B. this error exit may leave filp->f_flags with O_NONBLOCK set */
filp->private_data = tty;
check_tty_count(tty, "tty_open");
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
@@ -1123,11 +1237,6 @@
return 0;
}
-/*
- * Note that releasing a pty master also releases the child, so
- * we have to make the redirection checks after that and on both
- * sides of a pty.
- */
static int tty_release(struct inode * inode, struct file * filp)
{
release_dev(filp);
@@ -1545,6 +1654,7 @@
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
+ tty->flip.pty_sem = MUTEX;
}
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov