Re: tty_mutex and tty_old_pgrp

From: Jon Smirl
Date: Thu Jun 29 2006 - 01:40:42 EST


Here's what I have done so far. These new functions need better names
and probably some reorganization. They move the job control code out
of tty_io.c over to exit.c (or is there a better place?) to make it
easier to see what is going on. I haven't tried removing tty_mutex
yet.

void set_controlling_tty(struct tty_struct *tty);
void reset_controlling_tty(struct task_struct *task);
void reset_session_tty(pid_t session);
extern void clear_controlling_tty(int on_exit);
extern void clear_session_tty(pid_t session, struct tty_struct *tty,
pid_t pgrp);

--
Jon Smirl
jonsmirl@xxxxxxxxx


diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 8b2a599..c677402 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -161,14 +161,16 @@ static struct tty_struct *alloc_tty_stru
struct tty_struct *tty;

tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
- if (tty)
+ if (tty) {
memset(tty, 0, sizeof(struct tty_struct));
+ atomic_set(&tty->ref_count, 1);
+ }
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);
@@ -1026,7 +1028,6 @@ static void do_tty_hangup(void *data)
struct tty_struct *tty = (struct tty_struct *) data;
struct file * cons_filp = NULL;
struct file *filp, *f = NULL;
- struct task_struct *p;
struct tty_ldisc *ld;
int closecount = 0, n;

@@ -1096,22 +1097,9 @@ static void do_tty_hangup(void *data)

This should get done automatically when the port closes and
tty_release is called */
+
+ clear_session_tty(tty->session, tty, tty->pgrp);

- read_lock(&tasklist_lock);
- if (tty->session > 0) {
- do_each_task_pid(tty->session, PIDTYPE_SID, p) {
- if (p->signal->tty == tty)
- p->signal->tty = NULL;
- if (!p->signal->leader)
- continue;
- group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
- group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
- if (tty->pgrp > 0)
- p->signal->tty_old_pgrp = tty->pgrp;
- } while_each_task_pid(tty->session, PIDTYPE_SID, p);
- }
- read_unlock(&tasklist_lock);
-
tty->flags = 0;
tty->session = 0;
tty->pgrp = -1;
@@ -1174,65 +1162,6 @@ int tty_hung_up_p(struct file * filp)

EXPORT_SYMBOL(tty_hung_up_p);

-/*
- * This function is typically called only by the session leader, when
- * it wants to disassociate itself from its controlling tty.
- *
- * It performs the following functions:
- * (1) Sends a SIGHUP and SIGCONT to the foreground process group
- * (2) Clears the tty from being controlling the session
- * (3) Clears the controlling tty for all processes in the
- * session group.
- *
- * The argument on_exit is set to 1 if called when a process is
- * exiting; it is 0 if called by the ioctl TIOCNOTTY.
- */
-void disassociate_ctty(int on_exit)
-{
- struct tty_struct *tty;
- struct task_struct *p;
- int tty_pgrp = -1;
-
- 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 {
- if (current->signal->tty_old_pgrp) {
- 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;
- }
- if (tty_pgrp > 0) {
- kill_pg(tty_pgrp, SIGHUP, on_exit);
- if (!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;
-
- /* 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;
- } while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
- read_unlock(&tasklist_lock);
- mutex_unlock(&tty_mutex);
- unlock_kernel();
-}
-
void stop_tty(struct tty_struct *tty)
{
if (tty->stopped)
@@ -1931,10 +1860,12 @@ #endif

read_lock(&tasklist_lock);
do_each_task_pid(tty->session, PIDTYPE_SID, p) {
+ tty_put(p->signal->tty);
p->signal->tty = NULL;
} while_each_task_pid(tty->session, PIDTYPE_SID, p);
if (o_tty)
do_each_task_pid(o_tty->session, PIDTYPE_SID, p) {
+ tty_put(p->signal->tty);
p->signal->tty = NULL;
} while_each_task_pid(o_tty->session, PIDTYPE_SID, p);
read_unlock(&tasklist_lock);
@@ -2133,10 +2064,7 @@ #endif
current->signal->leader &&
!current->signal->tty &&
tty->session == 0) {
- task_lock(current);
- current->signal->tty = tty;
- task_unlock(current);
- current->signal->tty_old_pgrp = 0;
+ set_controlling_tty(tty);
tty->session = current->signal->session;
tty->pgrp = process_group(current);
}
@@ -2348,8 +2276,6 @@ static int fionbio(struct file *file, in

static int tiocsctty(struct tty_struct *tty, int arg)
{
- task_t *p;
-
if (current->signal->leader &&
(current->signal->session == tty->session))
return 0;
@@ -2368,19 +2294,11 @@ static int tiocsctty(struct tty_struct *
/*
* Steal it away
*/
-
- read_lock(&tasklist_lock);
- do_each_task_pid(tty->session, PIDTYPE_SID, p) {
- p->signal->tty = NULL;
- } while_each_task_pid(tty->session, PIDTYPE_SID, p);
- read_unlock(&tasklist_lock);
+ reset_session_tty(tty->session);
} else
return -EPERM;
}
- task_lock(current);
- current->signal->tty = tty;
- task_unlock(current);
- current->signal->tty_old_pgrp = 0;
+ set_controlling_tty(tty);
tty->session = current->signal->session;
tty->pgrp = process_group(current);
return 0;
@@ -2588,8 +2506,9 @@ int tty_ioctl(struct inode * inode, stru
if (current->signal->tty != tty)
return -ENOTTY;
if (current->signal->leader)
- disassociate_ctty(0);
+ clear_controlling_tty(0);
task_lock(current);
+ tty_put(current->signal->tty);
current->signal->tty = NULL;
task_unlock(current);
return 0;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 8d11d93..181f036 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1181,6 +1181,12 @@ extern void exit_itimers(struct signal_s

extern NORET_TYPE void do_group_exit(int);

+void set_controlling_tty(struct tty_struct *tty);
+void reset_controlling_tty(struct task_struct *task);
+void reset_session_tty(pid_t session);
+extern void clear_controlling_tty(int on_exit);
+extern void clear_session_tty(pid_t session, struct tty_struct *tty,
pid_t pgrp);
+
extern void daemonize(const char *, ...);
extern int allow_signal(int);
extern int disallow_signal(int);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index cb35ca5..3937a4e 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -182,6 +182,7 @@ struct device;
*/
struct tty_struct {
int magic;
+ atomic_t ref_count;
struct tty_driver *driver;
int index;
struct tty_ldisc ldisc;
@@ -269,6 +270,22 @@ #define TTY_HUPPED 18 /* Post driver->

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

+static inline struct tty_struct * tty_get(struct tty_struct * tty)
+{
+ if (tty) {
+ WARN_ON(!atomic_read(&tty->ref_count));
+ atomic_inc(&tty->ref_count);
+ }
+ return tty;
+}
+
+extern void free_tty_struct(struct tty_struct *tty);
+static inline void tty_put(struct tty_struct * tty)
+{
+ if (tty && atomic_dec_and_test(&tty->ref_count))
+ free_tty_struct(tty);
+}
+
extern void tty_write_flush(struct tty_struct *);

extern struct termios tty_std_termios;
@@ -306,7 +323,6 @@ extern void tty_vhangup(struct tty_struc
extern void tty_unhangup(struct file *filp);
extern int tty_hung_up_p(struct file * filp);
extern void do_SAK(struct tty_struct *tty);
-extern void disassociate_ctty(int priv);
extern void tty_flip_buffer_push(struct tty_struct *tty);
extern int tty_get_baud_rate(struct tty_struct *tty);
extern int tty_termios_baud_rate(struct termios *termios);
diff --git a/kernel/exit.c b/kernel/exit.c
index e76bd02..ea09822 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -379,6 +379,122 @@ EXPORT_SYMBOL(disallow_signal);
* attached user resources in one place where it belongs.
*/

+void set_controlling_tty(struct tty_struct *tty)
+{
+ task_lock(current);
+ current->signal->tty = tty_get(tty);
+ task_unlock(current);
+ current->signal->tty_old_pgrp = 0;
+}
+
+EXPORT_SYMBOL(set_controlling_tty);
+
+void reset_session_tty(pid_t session)
+{
+ struct task_struct *p;
+
+ read_lock(&tasklist_lock);
+ do_each_task_pid(session, PIDTYPE_SID, p) {
+ tty_put(p->signal->tty);
+ p->signal->tty = NULL;
+ } while_each_task_pid(session, PIDTYPE_SID, p);
+ read_unlock(&tasklist_lock);
+}
+
+EXPORT_SYMBOL(reset_session_tty);
+
+void reset_controlling_tty(struct task_struct *task)
+{
+ /* Reset controlling tty. */
+ mutex_lock(&tty_mutex);
+ tty_put(task->signal->tty);
+ task->signal->tty = NULL;
+ task->signal->tty_old_pgrp = 0;
+ mutex_unlock(&tty_mutex);
+}
+
+EXPORT_SYMBOL(reset_controlling_tty);
+
+/*
+ * This function is typically called only by the session leader, when
+ * it wants to disassociate itself from its controlling tty.
+ *
+ * It performs the following functions:
+ * (1) Sends a SIGHUP and SIGCONT to the foreground process group
+ * (2) Clears the tty from being controlling the session
+ * (3) Clears the controlling tty for all processes in the
+ * session group.
+ *
+ * The argument on_exit is set to 1 if called when a process is
+ * exiting; it is 0 if called by the ioctl TIOCNOTTY.
+ */
+void clear_controlling_tty(int on_exit)
+{
+ struct tty_struct *tty;
+ int tty_pgrp = -1;
+
+ 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 {
+ if (current->signal->tty_old_pgrp) {
+ 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;
+ }
+ if (tty_pgrp > 0) {
+ kill_pg(tty_pgrp, SIGHUP, on_exit);
+ if (!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;
+
+ /* Now clear signal->tty under the lock */
+ reset_session_tty(current->signal->session);
+ mutex_unlock(&tty_mutex);
+ unlock_kernel();
+}
+
+EXPORT_SYMBOL(clear_controlling_tty);
+
+void clear_session_tty(pid_t session, struct tty_struct *tty, pid_t pgrp)
+{
+ struct task_struct *p;
+
+ read_lock(&tasklist_lock);
+ if (session > 0) {
+ do_each_task_pid(session, PIDTYPE_SID, p) {
+ if (p->signal->tty == tty) {
+ tty_put(p->signal->tty);
+ p->signal->tty = NULL;
+ }
+ if (!p->signal->leader)
+ continue;
+ group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
+ group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
+ if (pgrp > 0)
+ p->signal->tty_old_pgrp = tty->pgrp;
+ } while_each_task_pid(session, PIDTYPE_SID, p);
+ }
+ read_unlock(&tasklist_lock);
+}
+
+EXPORT_SYMBOL(clear_session_tty);
+
void daemonize(const char *name, ...)
{
va_list args;
@@ -398,6 +514,7 @@ void daemonize(const char *name, ...)

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

@@ -917,7 +1034,7 @@ #endif
exit_keys(tsk);

if (group_dead && tsk->signal->leader)
- disassociate_ctty(1);
+ clear_controlling_tty(1);

module_put(task_thread_info(tsk)->exec_domain->module);
if (tsk->binfmt)
diff --git a/kernel/fork.c b/kernel/fork.c
index dfd10cb..34e7797 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -44,6 +44,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>
@@ -1201,7 +1202,7 @@ #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);
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 2d5179c..c97370f 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1400,8 +1400,7 @@ asmlinkage long sys_setsid(void)

group_leader->signal->leader = 1;
__set_special_pids(session, session);
- group_leader->signal->tty = NULL;
- group_leader->signal->tty_old_pgrp = 0;
+ reset_controlling_tty(group_leader);
err = process_group(group_leader);
out:
write_unlock_irq(&tasklist_lock);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 79c16e3..f3e128a 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1624,9 +1624,7 @@ static inline void flush_unauthorized_fi
struct inode *inode = file->f_dentry->d_inode;
if (inode_has_perm(current, inode,
FILE__READ | FILE__WRITE, NULL)) {
- /* Reset controlling tty. */
- current->signal->tty = NULL;
- current->signal->tty_old_pgrp = 0;
+ reset_controlling_tty(current);
}
}
file_list_unlock();
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/