aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/tty_ldisc.c
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2013-03-11 16:44:40 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-18 19:48:42 -0400
commitebc9baed42e42f9b51cf61672b7afb72f068d523 (patch)
tree2438eccbfddbfde6ff8a388f2dcf049913b4db8e /drivers/tty/tty_ldisc.c
parent8842dda2366d3d0c97646102768831f9b0ffd712 (diff)
tty: Separate release semantics of ldisc reference
tty_ldisc_ref()/tty_ldisc_unref() have usage semantics equivalent to down_read_trylock()/up_read(). Only callers of tty_ldisc_put() are performing the additional operations necessary for proper ldisc teardown, and then only after ensuring no outstanding 'read lock' remains. Thus, tty_ldisc_unref() should never be the last reference; WARN if it is. Conversely, tty_ldisc_put() should never be destructing if the use count != 1. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/tty_ldisc.c')
-rw-r--r--drivers/tty/tty_ldisc.c69
1 files changed, 35 insertions, 34 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 328ff5b544a5..9362a1030c95 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -49,37 +49,6 @@ static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld)
49 return ld; 49 return ld;
50} 50}
51 51
52static void put_ldisc(struct tty_ldisc *ld)
53{
54 unsigned long flags;
55
56 if (WARN_ON_ONCE(!ld))
57 return;
58
59 /*
60 * If this is the last user, free the ldisc, and
61 * release the ldisc ops.
62 *
63 * We really want an "atomic_dec_and_raw_lock_irqsave()",
64 * but we don't have it, so this does it by hand.
65 */
66 raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
67 if (atomic_dec_and_test(&ld->users)) {
68 struct tty_ldisc_ops *ldo = ld->ops;
69
70 ldo->refcount--;
71 module_put(ldo->owner);
72 raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
73
74 kfree(ld);
75 return;
76 }
77 raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
78
79 if (waitqueue_active(&ld->wq_idle))
80 wake_up(&ld->wq_idle);
81}
82
83/** 52/**
84 * tty_register_ldisc - install a line discipline 53 * tty_register_ldisc - install a line discipline
85 * @disc: ldisc number 54 * @disc: ldisc number
@@ -363,13 +332,45 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref);
363 332
364void tty_ldisc_deref(struct tty_ldisc *ld) 333void tty_ldisc_deref(struct tty_ldisc *ld)
365{ 334{
366 put_ldisc(ld); 335 unsigned long flags;
336
337 if (WARN_ON_ONCE(!ld))
338 return;
339
340 raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
341 /*
342 * WARNs if one-too-many reader references were released
343 * - the last reference must be released with tty_ldisc_put
344 */
345 WARN_ON(atomic_dec_and_test(&ld->users));
346 raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
347
348 if (waitqueue_active(&ld->wq_idle))
349 wake_up(&ld->wq_idle);
367} 350}
368EXPORT_SYMBOL_GPL(tty_ldisc_deref); 351EXPORT_SYMBOL_GPL(tty_ldisc_deref);
369 352
353/**
354 * tty_ldisc_put - release the ldisc
355 *
356 * Complement of tty_ldisc_get().
357 */
370static inline void tty_ldisc_put(struct tty_ldisc *ld) 358static inline void tty_ldisc_put(struct tty_ldisc *ld)
371{ 359{
372 put_ldisc(ld); 360 unsigned long flags;
361
362 if (WARN_ON_ONCE(!ld))
363 return;
364
365 raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
366
367 /* unreleased reader reference(s) will cause this WARN */
368 WARN_ON(!atomic_dec_and_test(&ld->users));
369
370 ld->ops->refcount--;
371 module_put(ld->ops->owner);
372 kfree(ld);
373 raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
373} 374}
374 375
375/** 376/**
@@ -1001,7 +1002,7 @@ void tty_ldisc_init(struct tty_struct *tty)
1001 */ 1002 */
1002void tty_ldisc_deinit(struct tty_struct *tty) 1003void tty_ldisc_deinit(struct tty_struct *tty)
1003{ 1004{
1004 put_ldisc(tty->ldisc); 1005 tty_ldisc_put(tty->ldisc);
1005 tty_ldisc_assign(tty, NULL); 1006 tty_ldisc_assign(tty, NULL);
1006} 1007}
1007 1008