Re: tty's use of file_list_lock and file_move

From: Jon Smirl
Date: Tue Jul 11 2006 - 08:28:16 EST


On 7/11/06, Alan Cox <alan@xxxxxxxxxxxxxxxxxxx> wrote:
Ar Llu, 2006-07-10 am 22:16 -0400, ysgrifennodd Jon Smirl:
> I'm not looking for performance gains, I'm looking more to isolate the
> tty code down to a minimal set of interactions with the rest of the
> kernel. RIght now it is all intertwined.

That might be tricky given that hangup and SAK have to directly interact
with the VFS locking.

I haven't got a working patch yet but I think there are a couple of
functions that should live over in the kernel code and have tty call
them. For example, make the walk every thread loop into a kernel
function named revoke (hopefully it will get fully fleshed out at some
point) and move it over to /kernel.

The code that manipulates job control belongs in /kernel too with tty
calling it. Right now the job control code is spread over three
directories. It could be collected into a single file which would make
it more understandable.

The general idea is that the current tty code is not really a device
driver, it has some kernel functions smeared into it. I'm looking at
extracting those kernel functions and making an API for them. Doing
that should make the locking needs more obvious and understandable.

Of course I am going to need help decoding all of the current locking.
Every time one of those global locks is taken I have worry about
spooky quantum interaction at a distance.

Have either of you looked at the refcount patch I posted earlier? I'll
attach it again so you don't need to search.

--
Jon Smirl
jonsmirl@xxxxxxxxx
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index bfdb902..5c673e0 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -126,9 +126,8 @@ 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
- vt.c for deeply disgusting hack reasons */
-DEFINE_MUTEX(tty_mutex);
+/* Spinlock to protect creating and releasing a tty. */
+static DEFINE_SPINLOCK(tty_lock);

#ifdef CONFIG_UNIX98_PTYS
extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
@@ -158,21 +157,21 @@ static struct tty_struct *alloc_tty_stru
{
struct tty_struct *tty;

- tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
- if (tty)
- memset(tty, 0, sizeof(struct tty_struct));
+ tty = kmalloc(sizeof(*tty), GFP_KERNEL);
return tty;
}

static void tty_buffer_free_all(struct tty_struct *);

-static inline void free_tty_struct(struct tty_struct *tty)
+void free_tty_struct(struct tty_struct *tty)
{
kfree(tty->write_buf);
tty_buffer_free_all(tty);
kfree(tty);
}

+EXPORT_SYMBOL(free_tty_struct);
+
#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base)

char *tty_name(struct tty_struct *tty, char *buf)
@@ -1090,7 +1089,7 @@ static void do_tty_hangup(void *data)
if (tty->session > 0) {
do_each_task_pid(tty->session, PIDTYPE_SID, p) {
if (p->signal->tty == tty)
- p->signal->tty = NULL;
+ tty_put(&p->signal->tty, 0, __FILE__, __LINE__);
if (!p->signal->leader)
continue;
group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
@@ -1184,11 +1183,9 @@ void disassociate_ctty(int on_exit)

lock_kernel();

- mutex_lock(&tty_mutex);
tty = current->signal->tty;
if (tty) {
tty_pgrp = tty->pgrp;
- mutex_unlock(&tty_mutex);
if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
tty_vhangup(tty);
} else {
@@ -1196,7 +1193,6 @@ void disassociate_ctty(int on_exit)
kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
}
- mutex_unlock(&tty_mutex);
unlock_kernel();
return;
}
@@ -1206,8 +1202,6 @@ void disassociate_ctty(int on_exit)
kill_pg(tty_pgrp, SIGCONT, on_exit);
}

- /* Must lock changes to tty_old_pgrp */
- mutex_lock(&tty_mutex);
current->signal->tty_old_pgrp = 0;
tty->session = 0;
tty->pgrp = -1;
@@ -1215,10 +1209,9 @@ void disassociate_ctty(int on_exit)
/* 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;
+ tty_put(&p->signal->tty, 0, __FILE__, __LINE__);
} while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
read_unlock(&tasklist_lock);
- mutex_unlock(&tty_mutex);
unlock_kernel();
}

@@ -1546,7 +1539,8 @@ static int init_dev(struct tty_driver *d
* Everything allocated ... set up the o_tty structure.
*/
if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) {
- driver->other->ttys[idx] = o_tty;
+ driver->other->ttys[idx] = tty_get(o_tty,
+ __FILE__, __LINE__);
}
if (!*o_tp_loc)
*o_tp_loc = o_tp;
@@ -1559,8 +1553,8 @@ static int init_dev(struct tty_driver *d
o_tty->count++;

/* Establish the links in both directions */
- tty->link = o_tty;
- o_tty->link = tty;
+ tty->link = tty_get(o_tty, __FILE__, __LINE__);
+ o_tty->link = tty_get(tty, __FILE__, __LINE__);
}

/*
@@ -1569,7 +1563,7 @@ static int init_dev(struct tty_driver *d
* there's no need to null out the local pointers.
*/
if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
- driver->ttys[idx] = tty;
+ driver->ttys[idx] = tty_get(tty, __FILE__, __LINE__);
}

if (!*tp_loc)
@@ -1636,7 +1630,6 @@ fast_track:
success:
*ret_tty = tty;

- /* All paths come through here to release the mutex */
end_init:
return retval;

@@ -1644,10 +1637,10 @@ end_init:
free_mem_out:
kfree(o_tp);
if (o_tty)
- free_tty_struct(o_tty);
+ tty_put(&o_tty, 1, __FILE__, __LINE__);
kfree(ltp);
kfree(tp);
- free_tty_struct(tty);
+ tty_put(&tty, 1, __FILE__, __LINE__);

fail_no_mem:
module_put(driver->owner);
@@ -1674,7 +1667,8 @@ static void release_mem(struct tty_struc

if ((o_tty = tty->link) != NULL) {
if (!devpts)
- o_tty->driver->ttys[idx] = NULL;
+ tty_put(&o_tty->driver->ttys[idx], 0,
+ __FILE__, __LINE__);
if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
tp = o_tty->termios;
if (!devpts)
@@ -1691,11 +1685,14 @@ static void release_mem(struct tty_struc
file_list_lock();
list_del_init(&o_tty->tty_files);
file_list_unlock();
- free_tty_struct(o_tty);
+ tty_put(&o_tty->link, 0, __FILE__, __LINE__);
+ tty_put(&tty->link, 0, __FILE__, __LINE__);
+ tty_put(&o_tty, 1, __FILE__, __LINE__);
}

if (!devpts)
- tty->driver->ttys[idx] = NULL;
+ tty_put(&tty->driver->ttys[idx], 0,
+ __FILE__, __LINE__);
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
tp = tty->termios;
if (!devpts)
@@ -1714,7 +1711,7 @@ static void release_mem(struct tty_struc
list_del_init(&tty->tty_files);
file_list_unlock();
module_put(tty->driver->owner);
- free_tty_struct(tty);
+ tty_put(&tty, 1, __FILE__, __LINE__);
}

/*
@@ -1832,7 +1829,7 @@ #endif
/* Guard against races with tty->count changes elsewhere and
opens on /dev/tty */

- mutex_lock(&tty_mutex);
+ spin_lock(&tty_lock);
tty_closing = tty->count <= 1;
o_tty_closing = o_tty &&
(o_tty->count <= (pty_master ? 1 : 0));
@@ -1861,9 +1858,9 @@ #endif
if (!do_sleep)
break;

+ spin_unlock(&tty_lock);
printk(KERN_WARNING "release_dev: %s: read/write wait queue "
"active!\n", tty_name(tty, buf));
- mutex_unlock(&tty_mutex);
schedule();
}

@@ -1896,7 +1893,8 @@ #endif
* something that needs to be handled for hangups.
*/
file_kill(filp);
- filp->private_data = NULL;
+ tty_put((struct tty_struct **)(&filp->private_data),
+ 0, __FILE__, __LINE__);

/*
* Perform some housekeeping before deciding whether to return.
@@ -1920,16 +1918,16 @@ #endif

read_lock(&tasklist_lock);
do_each_task_pid(tty->session, PIDTYPE_SID, p) {
- p->signal->tty = NULL;
+ tty_put(&p->signal->tty, 0, __FILE__, __LINE__);
} while_each_task_pid(tty->session, PIDTYPE_SID, p);
if (o_tty)
do_each_task_pid(o_tty->session, PIDTYPE_SID, p) {
- p->signal->tty = NULL;
+ tty_put(&p->signal->tty, 0, __FILE__, __LINE__);
} while_each_task_pid(o_tty->session, PIDTYPE_SID, p);
read_unlock(&tasklist_lock);
}

- mutex_unlock(&tty_mutex);
+ spin_unlock(&tty_lock);

/* check whether both sides are closing ... */
if (!tty_closing || (o_tty && !o_tty_closing))
@@ -2034,11 +2032,11 @@ retry_open:
index = -1;
retval = 0;

- mutex_lock(&tty_mutex);
-
+ spin_lock(&tty_lock);
+
if (device == MKDEV(TTYAUX_MAJOR,0)) {
if (!current->signal->tty) {
- mutex_unlock(&tty_mutex);
+ spin_unlock(&tty_lock);
return -ENXIO;
}
driver = current->signal->tty->driver;
@@ -2064,22 +2062,22 @@ #endif
noctty = 1;
goto got_driver;
}
- mutex_unlock(&tty_mutex);
+ spin_unlock(&tty_lock);
return -ENODEV;
}

driver = get_tty_driver(device, &index);
if (!driver) {
- mutex_unlock(&tty_mutex);
+ spin_unlock(&tty_lock);
return -ENODEV;
}
got_driver:
retval = init_dev(driver, index, &tty);
- mutex_unlock(&tty_mutex);
+ spin_unlock(&tty_lock);
if (retval)
return retval;

- filp->private_data = tty;
+ filp->private_data = tty_get(tty, __FILE__, __LINE__);
file_move(filp, &tty->tty_files);
check_tty_count(tty, "tty_open");
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
@@ -2122,7 +2120,7 @@ #endif
!current->signal->tty &&
tty->session == 0) {
task_lock(current);
- current->signal->tty = tty;
+ current->signal->tty = tty_get(tty, __FILE__, __LINE__);
task_unlock(current);
current->signal->tty_old_pgrp = 0;
tty->session = current->signal->session;
@@ -2161,15 +2159,15 @@ static int ptmx_open(struct inode * inod
}
up(&allocated_ptys_lock);

- mutex_lock(&tty_mutex);
+ spin_lock(&tty_lock);
retval = init_dev(ptm_driver, index, &tty);
- mutex_unlock(&tty_mutex);
+ spin_unlock(&tty_lock);

if (retval)
goto out;

set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
- filp->private_data = tty;
+ filp->private_data = tty_get(tty, __FILE__, __LINE__);
file_move(filp, &tty->tty_files);

retval = -ENOMEM;
@@ -2359,14 +2357,14 @@ static int tiocsctty(struct tty_struct *

read_lock(&tasklist_lock);
do_each_task_pid(tty->session, PIDTYPE_SID, p) {
- p->signal->tty = NULL;
+ tty_put(&p->signal->tty, 0, __FILE__, __LINE__);
} while_each_task_pid(tty->session, PIDTYPE_SID, p);
read_unlock(&tasklist_lock);
} else
return -EPERM;
}
task_lock(current);
- current->signal->tty = tty;
+ current->signal->tty = tty_get(tty, __FILE__, __LINE__);
task_unlock(current);
current->signal->tty_old_pgrp = 0;
tty->session = current->signal->session;
@@ -2578,7 +2576,7 @@ int tty_ioctl(struct inode * inode, stru
if (current->signal->leader)
disassociate_ctty(0);
task_lock(current);
- current->signal->tty = NULL;
+ tty_put(&current->signal->tty, 0, __FILE__, __LINE__);
task_unlock(current);
return 0;
case TIOCSCTTY:
@@ -2670,7 +2668,7 @@ #ifdef TTY_SOFT_SAK
#else
struct tty_struct *tty = arg;
struct task_struct *g, *p;
- int session;
+ pid_t session;
int i;
struct file *filp;
struct tty_ldisc *disc;
@@ -2914,6 +2912,11 @@ static void initialize_tty_struct(struct
{
memset(tty, 0, sizeof(struct tty_struct));
tty->magic = TTY_MAGIC;
+ atomic_set(&tty->ref_count, 1);
+#ifdef DEBUG_TTY_REFCOUNT
+ printk("TTY ref count alloc: %p count %d\n",
+ tty, atomic_read(&tty->ref_count));
+#endif
tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
tty->pgrp = -1;
tty->overrun_time = jiffies;
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index a9247b5..97c36fc 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -474,14 +474,14 @@ static const struct file_operations vcs_

static struct class *vc_class;

-void vcs_make_devfs(struct tty_struct *tty)
+void vcs_make_sysfs(struct tty_struct *tty)
{
class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1),
NULL, "vcs%u", tty->index + 1);
class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129),
NULL, "vcsa%u", tty->index + 1);
}
-void vcs_remove_devfs(struct tty_struct *tty)
+void vcs_remove_sysfs(struct tty_struct *tty)
{
class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 1));
class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 129));
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index da7e66a..dd39020 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -128,8 +128,8 @@ #define CTRL_ALWAYS 0x0800f501 /* Cannot
#define DEFAULT_BELL_PITCH 750
#define DEFAULT_BELL_DURATION (HZ/8)

-extern void vcs_make_devfs(struct tty_struct *tty);
-extern void vcs_remove_devfs(struct tty_struct *tty);
+extern void vcs_make_sysfs(struct tty_struct *tty);
+extern void vcs_remove_sysfs(struct tty_struct *tty);

extern void console_map_init(void);
#ifdef CONFIG_PROM_CONSOLE
@@ -2491,14 +2491,14 @@ static int con_open(struct tty_struct *t
if (ret == 0) {
struct vc_data *vc = vc_cons[currcons].d;
tty->driver_data = vc;
- vc->vc_tty = tty;
+ vc->vc_tty = tty_get(tty, __FILE__, __LINE__);

if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
tty->winsize.ws_col = vc_cons[currcons].d->vc_cols;
}
release_console_sem();
- vcs_make_devfs(tty);
+ vcs_make_sysfs(tty);
return ret;
}
}
@@ -2510,30 +2510,25 @@ static int con_open(struct tty_struct *t
* We take tty_mutex in here to prevent another thread from coming in via init_dev
* and taking a ref against the tty while we're in the process of forgetting
* about it and cleaning things up.
- *
- * This is because vcs_remove_devfs() can sleep and will drop the BKL.
*/
static void con_close(struct tty_struct *tty, struct file *filp)
{
- mutex_lock(&tty_mutex);
acquire_console_sem();
- if (tty && tty->count == 1) {
+ if (tty && (tty->count == 1)) {
struct vc_data *vc = tty->driver_data;

if (vc)
- vc->vc_tty = NULL;
+ tty_put(&vc->vc_tty, 0, __FILE__, __LINE__);
tty->driver_data = NULL;
release_console_sem();
- vcs_remove_devfs(tty);
- mutex_unlock(&tty_mutex);
+ vcs_remove_sysfs(tty);
/*
- * tty_mutex is released, but we still hold BKL, so there is
+ * We still hold BKL, so there is
* still exclusion against init_dev()
*/
return;
}
release_console_sem();
- mutex_unlock(&tty_mutex);
}

static void vc_init(struct vc_data *vc, unsigned int rows,
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 23659fd..d0e804c 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -165,7 +165,7 @@ ppp_asynctty_open(struct tty_struct *tty

/* initialize the asyncppp structure */
memset(ap, 0, sizeof(*ap));
- ap->tty = tty;
+ ap->tty = tty_get(tty, __FILE__, __LINE__);
ap->mru = PPP_MRU;
spin_lock_init(&ap->xmit_lock);
spin_lock_init(&ap->recv_lock);
@@ -218,6 +218,7 @@ ppp_asynctty_close(struct tty_struct *tt
write_unlock_irq(&disc_data_lock);
if (ap == 0)
return;
+ tty_put(&ap->tty, 0, __FILE__, __LINE__);

/*
* We have now ensured that nobody can start using ap from now
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index d5f636f..0b6e4e4 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -1279,7 +1279,7 @@ static void uart_close(struct tty_struct
tty_ldisc_flush(tty);

tty->closing = 0;
- state->info->tty = NULL;
+ tty_put(&state->info->tty, 0, __FILE__, __LINE__);

if (state->info->blocked_open) {
if (state->close_delay)
@@ -1375,6 +1375,7 @@ static void uart_hangup(struct tty_struc
uart_shutdown(state);
state->count = 0;
state->info->flags &= ~UIF_NORMAL_ACTIVE;
+ tty_put(&state->info->tty, 0, __FILE__, __LINE__);
state->info->tty = NULL;
wake_up_interruptible(&state->info->open_wait);
wake_up_interruptible(&state->info->delta_msr_wait);
@@ -1598,7 +1599,7 @@ static int uart_open(struct tty_struct *
tty->driver_data = state;
tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 0;
- state->info->tty = tty;
+ state->info->tty = tty_get(tty, __FILE__, __LINE__);

/*
* If the port is in the middle of closing, bail out now.
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 3670d77..06e96df 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -451,7 +451,7 @@ static int acm_tty_open(struct tty_struc
rv = 0;

tty->driver_data = acm;
- acm->tty = tty;
+ acm->tty = tty_get(tty, __FILE__, __LINE__);

/* force low_latency on so that our tty_push actually forces the data through,
otherwise it is scheduled, and with high data rates data can get lost. */
@@ -519,6 +519,7 @@ static void acm_tty_close(struct tty_str
return;

nr = acm->rx_buflimit;
+ tty_put(&acm->tty, 0, __FILE__, __LINE__);
mutex_lock(&open_mutex);
if (!--acm->used) {
if (acm->dev) {
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index f7aef5b..74eb9de 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -177,7 +177,7 @@ int devpts_pty_new(struct tty_struct *tt
inode->i_gid = config.setgid ? config.gid : current->fsgid;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR|config.mode, device);
- inode->u.generic_ip = tty;
+ inode->u.generic_ip = tty_get(tty, __FILE__, __LINE__);

dentry = get_node(number);
if (!IS_ERR(dentry) && !dentry->d_inode)
@@ -212,6 +212,8 @@ void devpts_pty_kill(int number)
if (!IS_ERR(dentry)) {
struct inode *inode = dentry->d_inode;
if (inode) {
+ tty_put((struct tty_struct **)(&inode->u.generic_ip),
+ 0, __FILE__, __LINE__);
inode->i_nlink--;
d_delete(dentry);
dput(dentry);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index b3b807e..f159a12 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -181,14 +181,15 @@ struct device;
*/
struct tty_struct {
int magic;
+ atomic_t ref_count;
struct tty_driver *driver;
int index;
struct tty_ldisc ldisc;
struct semaphore termios_sem;
struct termios *termios, *termios_locked;
char name[64];
- int pgrp;
- int session;
+ pid_t pgrp;
+ pid_t session;
unsigned long flags;
int count;
struct winsize winsize;
@@ -267,6 +268,44 @@ #define TTY_HUPPED 18 /* Post driver->

#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))

+#undef DEBUG_TTY_REFCOUNT
+static inline struct tty_struct *
+tty_get(struct tty_struct * tty, char *file, int line)
+{
+ if (tty) {
+ WARN_ON(!atomic_read(&tty->ref_count));
+ atomic_inc(&tty->ref_count);
+#ifdef DEBUG_TTY_REFCOUNT
+ printk("TTY ref count get: %s:%d %s %p count %d\n", file,
+ line, tty->name, tty, atomic_read(&tty->ref_count));
+#endif
+ }
+ return tty;
+}
+
+extern void free_tty_struct(struct tty_struct *tty);
+static inline void
+tty_put(struct tty_struct ** tty, int free_me, char *file, int line)
+{
+ int count;
+
+ if (*tty) {
+ count = atomic_dec_return(&(*tty)->ref_count);
+ if (count == 0) {
+ WARN_ON(!free_me);
+ free_tty_struct(*tty);
+#ifdef DEBUG_TTY_REFCOUNT
+ printk("TTY ref count put freed: %s:%d %s %p\n",
+ file, line, (*tty)->name, *tty);
+ } else {
+ printk("TTY ref count put: %s:%d %s %p count %d\n",
+ file, line, (*tty)->name, *tty, count);
+#endif
+ }
+ *tty = NULL;
+ }
+}
+
extern void tty_write_flush(struct tty_struct *);

extern struct termios tty_std_termios;
@@ -319,8 +358,6 @@ extern void tty_ldisc_put(int);
extern void tty_wakeup(struct tty_struct *tty);
extern void tty_ldisc_flush(struct tty_struct *tty);

-extern struct mutex tty_mutex;
-
/* n_tty.c */
extern struct tty_ldisc tty_ldisc_N_TTY;

diff --git a/kernel/exit.c b/kernel/exit.c
index 6664c08..a6cc755 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -108,6 +108,7 @@ static void __exit_signal(struct task_st
sig->nvcsw += tsk->nvcsw;
sig->nivcsw += tsk->nivcsw;
sig->sched_time += tsk->sched_time;
+ tty_put(&sig->tty, 0, __FILE__, __LINE__);
sig = NULL; /* Marker for below. */
}

@@ -391,9 +392,7 @@ void daemonize(const char *name, ...)
exit_mm(current);

set_special_pids(1, 1);
- mutex_lock(&tty_mutex);
- current->signal->tty = NULL;
- mutex_unlock(&tty_mutex);
+ tty_put(&current->signal->tty, 0, __FILE__, __LINE__);

/* Block and flush all signals */
sigfillset(&blocked);
diff --git a/kernel/fork.c b/kernel/fork.c
index 56e4e07..90cc4e3 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -43,6 +43,7 @@ #include <linux/profile.h>
#include <linux/rmap.h>
#include <linux/acct.h>
#include <linux/cn_proc.h>
+#include <linux/tty.h>

#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -856,6 +857,7 @@ static inline int copy_signal(unsigned l

sig->leader = 0; /* session leadership doesn't inherit */
sig->tty_old_pgrp = 0;
+ sig->tty = NULL;

sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero;
sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0;
@@ -885,6 +887,7 @@ static inline int copy_signal(unsigned l
void __cleanup_signal(struct signal_struct *sig)
{
exit_thread_group_keys(sig);
+ tty_put(&sig->tty, 0, __FILE__, __LINE__);
kmem_cache_free(signal_cachep, sig);
}

@@ -1227,7 +1230,8 @@ #endif
__ptrace_link(p, current->parent);

if (thread_group_leader(p)) {
- p->signal->tty = current->signal->tty;
+ p->signal->tty = tty_get(current->signal->tty,
+ __FILE__, __LINE__);
p->signal->pgrp = process_group(current);
p->signal->session = current->signal->session;
attach_pid(p, PIDTYPE_PGID, process_group(p));
diff --git a/kernel/sys.c b/kernel/sys.c
index dbb3b9c..31f486a 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1379,7 +1379,6 @@ asmlinkage long sys_setsid(void)
pid_t session;
int err = -EPERM;

- mutex_lock(&tty_mutex);
write_lock_irq(&tasklist_lock);

/* Fail if I am already a session leader */
@@ -1399,12 +1398,11 @@ asmlinkage long sys_setsid(void)

group_leader->signal->leader = 1;
__set_special_pids(session, session);
- group_leader->signal->tty = NULL;
+ tty_put(&group_leader->signal->tty, 0, __FILE__, __LINE__);
group_leader->signal->tty_old_pgrp = 0;
err = process_group(group_leader);
out:
write_unlock_irq(&tasklist_lock);
- mutex_unlock(&tty_mutex);
return err;
}

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 24caaee..db53c4e 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1638,7 +1638,8 @@ static inline void flush_unauthorized_fi
if (inode_has_perm(current, inode,
FILE__READ | FILE__WRITE, NULL)) {
/* Reset controlling tty. */
- current->signal->tty = NULL;
+ tty_put(&current->signal->tty, 0,
+ __FILE__, __LINE__);
current->signal->tty_old_pgrp = 0;
}
}