diff options
author | Alan Cox <alan@redhat.com> | 2008-10-13 05:37:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-13 12:51:40 -0400 |
commit | 9c9f4ded90a59eee84e15f5fd38c03d60184e112 (patch) | |
tree | 925a7f7c30136477b3f8551123fd86b355fd60fb | |
parent | 348eb12e5598be97400c749d3d93a71856ae0b2b (diff) |
tty: Add a kref count
Introduce a kref to the tty structure and use it to protect the tty->signal
tty references. For now we don't introduce it for anything else.
Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/char/tty_io.c | 54 | ||||
-rw-r--r-- | include/linux/tty.h | 18 | ||||
-rw-r--r-- | kernel/fork.c | 5 | ||||
-rw-r--r-- | kernel/sys.c | 4 |
4 files changed, 71 insertions, 10 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 732316899ca4..310e0703e4a1 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c | |||
@@ -559,6 +559,7 @@ static void do_tty_hangup(struct work_struct *work) | |||
559 | struct tty_ldisc *ld; | 559 | struct tty_ldisc *ld; |
560 | int closecount = 0, n; | 560 | int closecount = 0, n; |
561 | unsigned long flags; | 561 | unsigned long flags; |
562 | int refs = 0; | ||
562 | 563 | ||
563 | if (!tty) | 564 | if (!tty) |
564 | return; | 565 | return; |
@@ -625,8 +626,12 @@ static void do_tty_hangup(struct work_struct *work) | |||
625 | if (tty->session) { | 626 | if (tty->session) { |
626 | do_each_pid_task(tty->session, PIDTYPE_SID, p) { | 627 | do_each_pid_task(tty->session, PIDTYPE_SID, p) { |
627 | spin_lock_irq(&p->sighand->siglock); | 628 | spin_lock_irq(&p->sighand->siglock); |
628 | if (p->signal->tty == tty) | 629 | if (p->signal->tty == tty) { |
629 | p->signal->tty = NULL; | 630 | p->signal->tty = NULL; |
631 | /* We defer the dereferences outside fo | ||
632 | the tasklist lock */ | ||
633 | refs++; | ||
634 | } | ||
630 | if (!p->signal->leader) { | 635 | if (!p->signal->leader) { |
631 | spin_unlock_irq(&p->sighand->siglock); | 636 | spin_unlock_irq(&p->sighand->siglock); |
632 | continue; | 637 | continue; |
@@ -652,6 +657,10 @@ static void do_tty_hangup(struct work_struct *work) | |||
652 | tty->ctrl_status = 0; | 657 | tty->ctrl_status = 0; |
653 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | 658 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); |
654 | 659 | ||
660 | /* Account for the p->signal references we killed */ | ||
661 | while (refs--) | ||
662 | tty_kref_put(tty); | ||
663 | |||
655 | /* | 664 | /* |
656 | * If one of the devices matches a console pointer, we | 665 | * If one of the devices matches a console pointer, we |
657 | * cannot just call hangup() because that will cause | 666 | * cannot just call hangup() because that will cause |
@@ -1424,6 +1433,7 @@ release_mem_out: | |||
1424 | 1433 | ||
1425 | /** | 1434 | /** |
1426 | * release_one_tty - release tty structure memory | 1435 | * release_one_tty - release tty structure memory |
1436 | * @kref: kref of tty we are obliterating | ||
1427 | * | 1437 | * |
1428 | * Releases memory associated with a tty structure, and clears out the | 1438 | * Releases memory associated with a tty structure, and clears out the |
1429 | * driver table slots. This function is called when a device is no longer | 1439 | * driver table slots. This function is called when a device is no longer |
@@ -1433,17 +1443,19 @@ release_mem_out: | |||
1433 | * tty_mutex - sometimes only | 1443 | * tty_mutex - sometimes only |
1434 | * takes the file list lock internally when working on the list | 1444 | * takes the file list lock internally when working on the list |
1435 | * of ttys that the driver keeps. | 1445 | * of ttys that the driver keeps. |
1436 | * FIXME: should we require tty_mutex is held here ?? | ||
1437 | */ | 1446 | */ |
1438 | static void release_one_tty(struct tty_struct *tty, int idx) | 1447 | static void release_one_tty(struct kref *kref) |
1439 | { | 1448 | { |
1449 | struct tty_struct *tty = container_of(kref, struct tty_struct, kref); | ||
1440 | int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; | 1450 | int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; |
1441 | struct ktermios *tp; | 1451 | struct ktermios *tp; |
1452 | int idx = tty->index; | ||
1442 | 1453 | ||
1443 | if (!devpts) | 1454 | if (!devpts) |
1444 | tty->driver->ttys[idx] = NULL; | 1455 | tty->driver->ttys[idx] = NULL; |
1445 | 1456 | ||
1446 | if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { | 1457 | if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { |
1458 | /* FIXME: Locking on ->termios array */ | ||
1447 | tp = tty->termios; | 1459 | tp = tty->termios; |
1448 | if (!devpts) | 1460 | if (!devpts) |
1449 | tty->driver->termios[idx] = NULL; | 1461 | tty->driver->termios[idx] = NULL; |
@@ -1457,6 +1469,7 @@ static void release_one_tty(struct tty_struct *tty, int idx) | |||
1457 | 1469 | ||
1458 | 1470 | ||
1459 | tty->magic = 0; | 1471 | tty->magic = 0; |
1472 | /* FIXME: locking on tty->driver->refcount */ | ||
1460 | tty->driver->refcount--; | 1473 | tty->driver->refcount--; |
1461 | 1474 | ||
1462 | file_list_lock(); | 1475 | file_list_lock(); |
@@ -1467,6 +1480,21 @@ static void release_one_tty(struct tty_struct *tty, int idx) | |||
1467 | } | 1480 | } |
1468 | 1481 | ||
1469 | /** | 1482 | /** |
1483 | * tty_kref_put - release a tty kref | ||
1484 | * @tty: tty device | ||
1485 | * | ||
1486 | * Release a reference to a tty device and if need be let the kref | ||
1487 | * layer destruct the object for us | ||
1488 | */ | ||
1489 | |||
1490 | void tty_kref_put(struct tty_struct *tty) | ||
1491 | { | ||
1492 | if (tty) | ||
1493 | kref_put(&tty->kref, release_one_tty); | ||
1494 | } | ||
1495 | EXPORT_SYMBOL(tty_kref_put); | ||
1496 | |||
1497 | /** | ||
1470 | * release_tty - release tty structure memory | 1498 | * release_tty - release tty structure memory |
1471 | * | 1499 | * |
1472 | * Release both @tty and a possible linked partner (think pty pair), | 1500 | * Release both @tty and a possible linked partner (think pty pair), |
@@ -1477,14 +1505,20 @@ static void release_one_tty(struct tty_struct *tty, int idx) | |||
1477 | * takes the file list lock internally when working on the list | 1505 | * takes the file list lock internally when working on the list |
1478 | * of ttys that the driver keeps. | 1506 | * of ttys that the driver keeps. |
1479 | * FIXME: should we require tty_mutex is held here ?? | 1507 | * FIXME: should we require tty_mutex is held here ?? |
1508 | * | ||
1509 | * FIXME: We want to defer the module put of the driver to the | ||
1510 | * destructor. | ||
1480 | */ | 1511 | */ |
1481 | static void release_tty(struct tty_struct *tty, int idx) | 1512 | static void release_tty(struct tty_struct *tty, int idx) |
1482 | { | 1513 | { |
1483 | struct tty_driver *driver = tty->driver; | 1514 | struct tty_driver *driver = tty->driver; |
1484 | 1515 | ||
1516 | /* This should always be true but check for the moment */ | ||
1517 | WARN_ON(tty->index != idx); | ||
1518 | |||
1485 | if (tty->link) | 1519 | if (tty->link) |
1486 | release_one_tty(tty->link, idx); | 1520 | tty_kref_put(tty->link); |
1487 | release_one_tty(tty, idx); | 1521 | tty_kref_put(tty); |
1488 | module_put(driver->owner); | 1522 | module_put(driver->owner); |
1489 | } | 1523 | } |
1490 | 1524 | ||
@@ -2798,6 +2832,7 @@ EXPORT_SYMBOL(do_SAK); | |||
2798 | static void initialize_tty_struct(struct tty_struct *tty) | 2832 | static void initialize_tty_struct(struct tty_struct *tty) |
2799 | { | 2833 | { |
2800 | memset(tty, 0, sizeof(struct tty_struct)); | 2834 | memset(tty, 0, sizeof(struct tty_struct)); |
2835 | kref_init(&tty->kref); | ||
2801 | tty->magic = TTY_MAGIC; | 2836 | tty->magic = TTY_MAGIC; |
2802 | tty_ldisc_init(tty); | 2837 | tty_ldisc_init(tty); |
2803 | tty->session = NULL; | 2838 | tty->session = NULL; |
@@ -3053,9 +3088,12 @@ EXPORT_SYMBOL(tty_devnum); | |||
3053 | 3088 | ||
3054 | void proc_clear_tty(struct task_struct *p) | 3089 | void proc_clear_tty(struct task_struct *p) |
3055 | { | 3090 | { |
3091 | struct tty_struct *tty; | ||
3056 | spin_lock_irq(&p->sighand->siglock); | 3092 | spin_lock_irq(&p->sighand->siglock); |
3093 | tty = p->signal->tty; | ||
3057 | p->signal->tty = NULL; | 3094 | p->signal->tty = NULL; |
3058 | spin_unlock_irq(&p->sighand->siglock); | 3095 | spin_unlock_irq(&p->sighand->siglock); |
3096 | tty_kref_put(tty); | ||
3059 | } | 3097 | } |
3060 | 3098 | ||
3061 | /* Called under the sighand lock */ | 3099 | /* Called under the sighand lock */ |
@@ -3071,9 +3109,13 @@ static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) | |||
3071 | tty->pgrp = get_pid(task_pgrp(tsk)); | 3109 | tty->pgrp = get_pid(task_pgrp(tsk)); |
3072 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | 3110 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); |
3073 | tty->session = get_pid(task_session(tsk)); | 3111 | tty->session = get_pid(task_session(tsk)); |
3112 | if (tsk->signal->tty) { | ||
3113 | printk(KERN_DEBUG "tty not NULL!!\n"); | ||
3114 | tty_kref_put(tsk->signal->tty); | ||
3115 | } | ||
3074 | } | 3116 | } |
3075 | put_pid(tsk->signal->tty_old_pgrp); | 3117 | put_pid(tsk->signal->tty_old_pgrp); |
3076 | tsk->signal->tty = tty; | 3118 | tsk->signal->tty = tty_kref_get(tty); |
3077 | tsk->signal->tty_old_pgrp = NULL; | 3119 | tsk->signal->tty_old_pgrp = NULL; |
3078 | } | 3120 | } |
3079 | 3121 | ||
diff --git a/include/linux/tty.h b/include/linux/tty.h index e3612c3ac194..b6e6c26883ee 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -209,6 +209,7 @@ struct tty_operations; | |||
209 | 209 | ||
210 | struct tty_struct { | 210 | struct tty_struct { |
211 | int magic; | 211 | int magic; |
212 | struct kref kref; | ||
212 | struct tty_driver *driver; | 213 | struct tty_driver *driver; |
213 | const struct tty_operations *ops; | 214 | const struct tty_operations *ops; |
214 | int index; | 215 | int index; |
@@ -311,6 +312,23 @@ extern int kmsg_redirect; | |||
311 | extern void console_init(void); | 312 | extern void console_init(void); |
312 | extern int vcs_init(void); | 313 | extern int vcs_init(void); |
313 | 314 | ||
315 | /** | ||
316 | * tty_kref_get - get a tty reference | ||
317 | * @tty: tty device | ||
318 | * | ||
319 | * Return a new reference to a tty object. The caller must hold | ||
320 | * sufficient locks/counts to ensure that their existing reference cannot | ||
321 | * go away | ||
322 | */ | ||
323 | |||
324 | extern inline struct tty_struct *tty_kref_get(struct tty_struct *tty) | ||
325 | { | ||
326 | if (tty) | ||
327 | kref_get(&tty->kref); | ||
328 | return tty; | ||
329 | } | ||
330 | extern void tty_kref_put(struct tty_struct *tty); | ||
331 | |||
314 | extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, | 332 | extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, |
315 | const char *routine); | 333 | const char *routine); |
316 | extern char *tty_name(struct tty_struct *tty, char *buf); | 334 | extern char *tty_name(struct tty_struct *tty, char *buf); |
diff --git a/kernel/fork.c b/kernel/fork.c index 7ce2ebe84796..30de644a40c4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -802,6 +802,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) | |||
802 | 802 | ||
803 | sig->leader = 0; /* session leadership doesn't inherit */ | 803 | sig->leader = 0; /* session leadership doesn't inherit */ |
804 | sig->tty_old_pgrp = NULL; | 804 | sig->tty_old_pgrp = NULL; |
805 | sig->tty = NULL; | ||
805 | 806 | ||
806 | sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero; | 807 | sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero; |
807 | sig->gtime = cputime_zero; | 808 | sig->gtime = cputime_zero; |
@@ -838,6 +839,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) | |||
838 | void __cleanup_signal(struct signal_struct *sig) | 839 | void __cleanup_signal(struct signal_struct *sig) |
839 | { | 840 | { |
840 | exit_thread_group_keys(sig); | 841 | exit_thread_group_keys(sig); |
842 | tty_kref_put(sig->tty); | ||
841 | kmem_cache_free(signal_cachep, sig); | 843 | kmem_cache_free(signal_cachep, sig); |
842 | } | 844 | } |
843 | 845 | ||
@@ -1227,7 +1229,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1227 | p->nsproxy->pid_ns->child_reaper = p; | 1229 | p->nsproxy->pid_ns->child_reaper = p; |
1228 | 1230 | ||
1229 | p->signal->leader_pid = pid; | 1231 | p->signal->leader_pid = pid; |
1230 | p->signal->tty = current->signal->tty; | 1232 | tty_kref_put(p->signal->tty); |
1233 | p->signal->tty = tty_kref_get(current->signal->tty); | ||
1231 | set_task_pgrp(p, task_pgrp_nr(current)); | 1234 | set_task_pgrp(p, task_pgrp_nr(current)); |
1232 | set_task_session(p, task_session_nr(current)); | 1235 | set_task_session(p, task_session_nr(current)); |
1233 | attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); | 1236 | attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); |
diff --git a/kernel/sys.c b/kernel/sys.c index 038a7bc0901d..234d9454294e 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -1060,9 +1060,7 @@ asmlinkage long sys_setsid(void) | |||
1060 | group_leader->signal->leader = 1; | 1060 | group_leader->signal->leader = 1; |
1061 | __set_special_pids(sid); | 1061 | __set_special_pids(sid); |
1062 | 1062 | ||
1063 | spin_lock(&group_leader->sighand->siglock); | 1063 | proc_clear_tty(group_leader); |
1064 | group_leader->signal->tty = NULL; | ||
1065 | spin_unlock(&group_leader->sighand->siglock); | ||
1066 | 1064 | ||
1067 | err = session; | 1065 | err = session; |
1068 | out: | 1066 | out: |