aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/tty_ldisc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-08-03 17:54:56 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-08-04 16:46:30 -0400
commitcbe9352fa08f90aa03b4dbf1bbabfc95d196e562 (patch)
tree960187b610e13e5ef7f461397bd0846a36221436 /drivers/char/tty_ldisc.c
parent65b770468e98941e45e19780dff9283e663e6b8b (diff)
tty-ldisc: be more careful in 'put_ldisc' locking
Use 'atomic_dec_and_lock()' to make sure that we always hold the tty_ldisc_lock when the ldisc count goes to zero. That way we can never race against 'tty_ldisc_try()' increasing the count again. Reported-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Tested-by: Sergey Senozhatsky <sergey.senozhatsky@mail.by> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/char/tty_ldisc.c')
-rw-r--r--drivers/char/tty_ldisc.c17
1 files changed, 12 insertions, 5 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index be55dfcf59ac..1733d3439ad2 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -55,25 +55,32 @@ static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld)
55 return ld; 55 return ld;
56} 56}
57 57
58static inline void put_ldisc(struct tty_ldisc *ld) 58static void put_ldisc(struct tty_ldisc *ld)
59{ 59{
60 unsigned long flags;
61
60 if (WARN_ON_ONCE(!ld)) 62 if (WARN_ON_ONCE(!ld))
61 return; 63 return;
62 64
63 /* 65 /*
64 * If this is the last user, free the ldisc, and 66 * If this is the last user, free the ldisc, and
65 * release the ldisc ops. 67 * release the ldisc ops.
68 *
69 * We really want an "atomic_dec_and_lock_irqsave()",
70 * but we don't have it, so this does it by hand.
66 */ 71 */
67 if (atomic_dec_and_test(&ld->users)) { 72 local_irq_save(flags);
68 unsigned long flags; 73 if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) {
69 struct tty_ldisc_ops *ldo = ld->ops; 74 struct tty_ldisc_ops *ldo = ld->ops;
70 75
71 kfree(ld);
72 spin_lock_irqsave(&tty_ldisc_lock, flags);
73 ldo->refcount--; 76 ldo->refcount--;
74 module_put(ldo->owner); 77 module_put(ldo->owner);
75 spin_unlock_irqrestore(&tty_ldisc_lock, flags); 78 spin_unlock_irqrestore(&tty_ldisc_lock, flags);
79
80 kfree(ld);
81 return;
76 } 82 }
83 local_irq_restore(flags);
77} 84}
78 85
79/** 86/**