aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty/tty_ldisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/tty_ldisc.c')
-rw-r--r--drivers/tty/tty_ldisc.c61
1 files changed, 34 insertions, 27 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 0f2a2c5e704..d794087c327 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -26,7 +26,7 @@
* callers who will do ldisc lookups and cannot sleep.
*/
-static DEFINE_SPINLOCK(tty_ldisc_lock);
+static DEFINE_RAW_SPINLOCK(tty_ldisc_lock);
static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
/* Line disc dispatch table */
static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
@@ -49,22 +49,24 @@ static void put_ldisc(struct tty_ldisc *ld)
* If this is the last user, free the ldisc, and
* release the ldisc ops.
*
- * We really want an "atomic_dec_and_lock_irqsave()",
+ * We really want an "atomic_dec_and_raw_lock_irqsave()",
* but we don't have it, so this does it by hand.
*/
- local_irq_save(flags);
- if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) {
+ raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
+ if (atomic_dec_and_test(&ld->users)) {
struct tty_ldisc_ops *ldo = ld->ops;
ldo->refcount--;
module_put(ldo->owner);
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
kfree(ld);
return;
}
- local_irq_restore(flags);
- wake_up(&ld->wq_idle);
+ raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+
+ if (waitqueue_active(&ld->wq_idle))
+ wake_up(&ld->wq_idle);
}
/**
@@ -88,11 +90,11 @@ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
if (disc < N_TTY || disc >= NR_LDISCS)
return -EINVAL;
- spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
tty_ldiscs[disc] = new_ldisc;
new_ldisc->num = disc;
new_ldisc->refcount = 0;
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
return ret;
}
@@ -118,12 +120,12 @@ int tty_unregister_ldisc(int disc)
if (disc < N_TTY || disc >= NR_LDISCS)
return -EINVAL;
- spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
if (tty_ldiscs[disc]->refcount)
ret = -EBUSY;
else
tty_ldiscs[disc] = NULL;
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
return ret;
}
@@ -134,7 +136,7 @@ static struct tty_ldisc_ops *get_ldops(int disc)
unsigned long flags;
struct tty_ldisc_ops *ldops, *ret;
- spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
ret = ERR_PTR(-EINVAL);
ldops = tty_ldiscs[disc];
if (ldops) {
@@ -144,7 +146,7 @@ static struct tty_ldisc_ops *get_ldops(int disc)
ret = ldops;
}
}
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
return ret;
}
@@ -152,10 +154,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops)
{
unsigned long flags;
- spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
ldops->refcount--;
module_put(ldops->owner);
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
}
/**
@@ -287,11 +289,11 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty)
unsigned long flags;
struct tty_ldisc *ld;
- spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
ld = NULL;
if (test_bit(TTY_LDISC, &tty->flags))
ld = get_ldisc(tty->ldisc);
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
return ld;
}
@@ -512,7 +514,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
static int tty_ldisc_halt(struct tty_struct *tty)
{
clear_bit(TTY_LDISC, &tty->flags);
- return cancel_work_sync(&tty->buf.work);
+ return cancel_work_sync(&tty->port->buf.work);
}
/**
@@ -525,7 +527,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty)
{
flush_work(&tty->hangup_work);
flush_work(&tty->SAK_work);
- flush_work(&tty->buf.work);
+ flush_work(&tty->port->buf.work);
}
/**
@@ -704,9 +706,9 @@ enable:
/* Restart the work queue in case no characters kick it off. Safe if
already running */
if (work)
- schedule_work(&tty->buf.work);
+ schedule_work(&tty->port->buf.work);
if (o_work)
- schedule_work(&o_tty->buf.work);
+ schedule_work(&o_tty->port->buf.work);
mutex_unlock(&tty->ldisc_mutex);
tty_unlock(tty);
return retval;
@@ -817,7 +819,7 @@ void tty_ldisc_hangup(struct tty_struct *tty)
*/
clear_bit(TTY_LDISC, &tty->flags);
tty_unlock(tty);
- cancel_work_sync(&tty->buf.work);
+ cancel_work_sync(&tty->port->buf.work);
mutex_unlock(&tty->ldisc_mutex);
retry:
tty_lock(tty);
@@ -897,6 +899,11 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
static void tty_ldisc_kill(struct tty_struct *tty)
{
+ /* There cannot be users from userspace now. But there still might be
+ * drivers holding a reference via tty_ldisc_ref. Do not steal them the
+ * ldisc until they are done. */
+ tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT);
+
mutex_lock(&tty->ldisc_mutex);
/*
* Now kill off the ldisc
@@ -929,17 +936,17 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
* race with the set_ldisc code path.
*/
- tty_lock_pair(tty, o_tty);
tty_ldisc_halt(tty);
- tty_ldisc_flush_works(tty);
- if (o_tty) {
+ if (o_tty)
tty_ldisc_halt(o_tty);
+
+ tty_ldisc_flush_works(tty);
+ if (o_tty)
tty_ldisc_flush_works(o_tty);
- }
+ tty_lock_pair(tty, o_tty);
/* This will need doing differently if we need to lock */
tty_ldisc_kill(tty);
-
if (o_tty)
tty_ldisc_kill(o_tty);