diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2013-03-11 16:44:40 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-03-18 19:48:42 -0400 |
commit | ebc9baed42e42f9b51cf61672b7afb72f068d523 (patch) | |
tree | 2438eccbfddbfde6ff8a388f2dcf049913b4db8e | |
parent | 8842dda2366d3d0c97646102768831f9b0ffd712 (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>
-rw-r--r-- | drivers/tty/tty_ldisc.c | 69 |
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 | ||
52 | static 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 | ||
364 | void tty_ldisc_deref(struct tty_ldisc *ld) | 333 | void 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 | } |
368 | EXPORT_SYMBOL_GPL(tty_ldisc_deref); | 351 | EXPORT_SYMBOL_GPL(tty_ldisc_deref); |
369 | 352 | ||
353 | /** | ||
354 | * tty_ldisc_put - release the ldisc | ||
355 | * | ||
356 | * Complement of tty_ldisc_get(). | ||
357 | */ | ||
370 | static inline void tty_ldisc_put(struct tty_ldisc *ld) | 358 | static 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 | */ |
1002 | void tty_ldisc_deinit(struct tty_struct *tty) | 1003 | void 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 | ||