aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/tty_ldisc.c
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2013-06-15 07:04:47 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-23 19:38:34 -0400
commitd2c438905f9f718b3d9f5d89ce163fc22bd33995 (patch)
tree60ff9ab6543fe72fb1225e660a53dc2f147faecb /drivers/tty/tty_ldisc.c
parent137084bbaddf4f6dde948ef3a14e18ba0754cc0d (diff)
tty: Add lock/unlock ldisc pair functions
Just as the tty pair must be locked in a stable sequence (ie, independent of which is consider the 'other' tty), so must the ldisc pair be locked in a stable sequence as well. 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.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 8166260aa839..418c9f64a9fd 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -31,6 +31,13 @@
31#define tty_ldisc_debug(tty, f, args...) 31#define tty_ldisc_debug(tty, f, args...)
32#endif 32#endif
33 33
34/* lockdep nested classes for tty->ldisc_sem */
35enum {
36 LDISC_SEM_NORMAL,
37 LDISC_SEM_OTHER,
38};
39
40
34/* 41/*
35 * This guards the refcounted line discipline lists. The lock 42 * This guards the refcounted line discipline lists. The lock
36 * must be taken with irqs off because there are hangup path 43 * must be taken with irqs off because there are hangup path
@@ -351,6 +358,86 @@ void tty_ldisc_deref(struct tty_ldisc *ld)
351} 358}
352EXPORT_SYMBOL_GPL(tty_ldisc_deref); 359EXPORT_SYMBOL_GPL(tty_ldisc_deref);
353 360
361
362static inline int __lockfunc
363tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
364{
365 return ldsem_down_write(&tty->ldisc_sem, timeout);
366}
367
368static inline int __lockfunc
369tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout)
370{
371 return ldsem_down_write_nested(&tty->ldisc_sem,
372 LDISC_SEM_OTHER, timeout);
373}
374
375static inline void tty_ldisc_unlock(struct tty_struct *tty)
376{
377 return ldsem_up_write(&tty->ldisc_sem);
378}
379
380static int __lockfunc
381tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2,
382 unsigned long timeout)
383{
384 int ret;
385
386 if (tty < tty2) {
387 ret = tty_ldisc_lock(tty, timeout);
388 if (ret) {
389 ret = tty_ldisc_lock_nested(tty2, timeout);
390 if (!ret)
391 tty_ldisc_unlock(tty);
392 }
393 } else {
394 /* if this is possible, it has lots of implications */
395 WARN_ON_ONCE(tty == tty2);
396 if (tty2 && tty != tty2) {
397 ret = tty_ldisc_lock(tty2, timeout);
398 if (ret) {
399 ret = tty_ldisc_lock_nested(tty, timeout);
400 if (!ret)
401 tty_ldisc_unlock(tty2);
402 }
403 } else
404 ret = tty_ldisc_lock(tty, timeout);
405 }
406
407 if (!ret)
408 return -EBUSY;
409
410 set_bit(TTY_LDISC_HALTED, &tty->flags);
411 if (tty2)
412 set_bit(TTY_LDISC_HALTED, &tty2->flags);
413 return 0;
414}
415
416static void __lockfunc
417tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2)
418{
419 tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT);
420}
421
422static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty,
423 struct tty_struct *tty2)
424{
425 tty_ldisc_unlock(tty);
426 if (tty2)
427 tty_ldisc_unlock(tty2);
428}
429
430static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty,
431 struct tty_struct *tty2)
432{
433 clear_bit(TTY_LDISC_HALTED, &tty->flags);
434 if (tty2)
435 clear_bit(TTY_LDISC_HALTED, &tty2->flags);
436
437 tty_ldisc_unlock_pair(tty, tty2);
438}
439
440
354/** 441/**
355 * tty_ldisc_enable - allow ldisc use 442 * tty_ldisc_enable - allow ldisc use
356 * @tty: terminal to activate ldisc on 443 * @tty: terminal to activate ldisc on