Commit cbe9352f authored by Linus Torvalds's avatar Linus Torvalds Committed by Greg Kroah-Hartman

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: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Tested-by: default avatarSergey Senozhatsky <sergey.senozhatsky@mail.by>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 65b77046
...@@ -55,25 +55,32 @@ static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld) ...@@ -55,25 +55,32 @@ static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld)
return ld; return ld;
} }
static inline void put_ldisc(struct tty_ldisc *ld) static void put_ldisc(struct tty_ldisc *ld)
{ {
unsigned long flags;
if (WARN_ON_ONCE(!ld)) if (WARN_ON_ONCE(!ld))
return; return;
/* /*
* If this is the last user, free the ldisc, and * If this is the last user, free the ldisc, and
* release the ldisc ops. * release the ldisc ops.
*
* We really want an "atomic_dec_and_lock_irqsave()",
* but we don't have it, so this does it by hand.
*/ */
if (atomic_dec_and_test(&ld->users)) { local_irq_save(flags);
unsigned long flags; if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) {
struct tty_ldisc_ops *ldo = ld->ops; struct tty_ldisc_ops *ldo = ld->ops;
kfree(ld);
spin_lock_irqsave(&tty_ldisc_lock, flags);
ldo->refcount--; ldo->refcount--;
module_put(ldo->owner); module_put(ldo->owner);
spin_unlock_irqrestore(&tty_ldisc_lock, flags); spin_unlock_irqrestore(&tty_ldisc_lock, flags);
kfree(ld);
return;
} }
local_irq_restore(flags);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment