2008-07-22 14:16:55 +04:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/errno.h>
|
2011-11-16 19:27:10 +04:00
|
|
|
#include <linux/kmod.h>
|
2008-07-22 14:16:55 +04:00
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/tty_driver.h>
|
|
|
|
#include <linux/file.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/wait.h>
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/uaccess.h>
|
2011-11-16 19:27:09 +04:00
|
|
|
#include <linux/ratelimit.h>
|
2008-07-22 14:16:55 +04:00
|
|
|
|
2013-03-12 00:44:38 +04:00
|
|
|
#undef LDISC_DEBUG_HANGUP
|
|
|
|
|
|
|
|
#ifdef LDISC_DEBUG_HANGUP
|
2015-07-13 05:49:10 +03:00
|
|
|
#define tty_ldisc_debug(tty, f, args...) tty_debug(tty, f, ##args)
|
2013-03-12 00:44:38 +04:00
|
|
|
#else
|
|
|
|
#define tty_ldisc_debug(tty, f, args...)
|
|
|
|
#endif
|
|
|
|
|
2013-06-15 15:04:47 +04:00
|
|
|
/* lockdep nested classes for tty->ldisc_sem */
|
|
|
|
enum {
|
|
|
|
LDISC_SEM_NORMAL,
|
|
|
|
LDISC_SEM_OTHER,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-07-22 14:16:55 +04:00
|
|
|
/*
|
|
|
|
* This guards the refcounted line discipline lists. The lock
|
|
|
|
* must be taken with irqs off because there are hangup path
|
|
|
|
* callers who will do ldisc lookups and cannot sleep.
|
|
|
|
*/
|
|
|
|
|
2013-06-15 15:04:46 +04:00
|
|
|
static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock);
|
2008-07-22 14:16:55 +04:00
|
|
|
/* Line disc dispatch table */
|
|
|
|
static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_register_ldisc - install a line discipline
|
|
|
|
* @disc: ldisc number
|
|
|
|
* @new_ldisc: pointer to the ldisc object
|
|
|
|
*
|
|
|
|
* Installs a new line discipline into the kernel. The discipline
|
|
|
|
* is set up as unreferenced and then made available to the kernel
|
|
|
|
* from this point onwards.
|
|
|
|
*
|
|
|
|
* Locking:
|
2013-06-15 15:04:46 +04:00
|
|
|
* takes tty_ldiscs_lock to guard against ldisc races
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (disc < N_TTY || disc >= NR_LDISCS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-06-15 15:04:46 +04:00
|
|
|
raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
|
2008-07-22 14:16:55 +04:00
|
|
|
tty_ldiscs[disc] = new_ldisc;
|
|
|
|
new_ldisc->num = disc;
|
|
|
|
new_ldisc->refcount = 0;
|
2013-06-15 15:04:46 +04:00
|
|
|
raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
|
2008-07-22 14:16:55 +04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tty_register_ldisc);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_unregister_ldisc - unload a line discipline
|
|
|
|
* @disc: ldisc number
|
|
|
|
* @new_ldisc: pointer to the ldisc object
|
|
|
|
*
|
|
|
|
* Remove a line discipline from the kernel providing it is not
|
|
|
|
* currently in use.
|
|
|
|
*
|
|
|
|
* Locking:
|
2013-06-15 15:04:46 +04:00
|
|
|
* takes tty_ldiscs_lock to guard against ldisc races
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int tty_unregister_ldisc(int disc)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (disc < N_TTY || disc >= NR_LDISCS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-06-15 15:04:46 +04:00
|
|
|
raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
|
2008-07-22 14:16:55 +04:00
|
|
|
if (tty_ldiscs[disc]->refcount)
|
|
|
|
ret = -EBUSY;
|
|
|
|
else
|
|
|
|
tty_ldiscs[disc] = NULL;
|
2013-06-15 15:04:46 +04:00
|
|
|
raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
|
2008-07-22 14:16:55 +04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tty_unregister_ldisc);
|
|
|
|
|
2009-08-04 03:00:15 +04:00
|
|
|
static struct tty_ldisc_ops *get_ldops(int disc)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
struct tty_ldisc_ops *ldops, *ret;
|
|
|
|
|
2013-06-15 15:04:46 +04:00
|
|
|
raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
|
2009-08-04 03:00:15 +04:00
|
|
|
ret = ERR_PTR(-EINVAL);
|
|
|
|
ldops = tty_ldiscs[disc];
|
|
|
|
if (ldops) {
|
|
|
|
ret = ERR_PTR(-EAGAIN);
|
|
|
|
if (try_module_get(ldops->owner)) {
|
|
|
|
ldops->refcount++;
|
|
|
|
ret = ldops;
|
|
|
|
}
|
|
|
|
}
|
2013-06-15 15:04:46 +04:00
|
|
|
raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
|
2009-08-04 03:00:15 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void put_ldops(struct tty_ldisc_ops *ldops)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
2013-06-15 15:04:46 +04:00
|
|
|
raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
|
2009-08-04 03:00:15 +04:00
|
|
|
ldops->refcount--;
|
|
|
|
module_put(ldops->owner);
|
2013-06-15 15:04:46 +04:00
|
|
|
raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
|
2009-08-04 03:00:15 +04:00
|
|
|
}
|
2008-07-22 14:16:55 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_get - take a reference to an ldisc
|
|
|
|
* @disc: ldisc number
|
|
|
|
*
|
|
|
|
* Takes a reference to a line discipline. Deals with refcounts and
|
2016-01-11 09:40:59 +03:00
|
|
|
* module locking counts.
|
|
|
|
*
|
|
|
|
* Returns: -EINVAL if the discipline index is not [N_TTY..NR_LDISCS] or
|
|
|
|
* if the discipline is not registered
|
|
|
|
* -EAGAIN if request_module() failed to load or register the
|
|
|
|
* the discipline
|
|
|
|
* -ENOMEM if allocation failure
|
|
|
|
*
|
|
|
|
* Otherwise, returns a pointer to the discipline and bumps the
|
|
|
|
* ref count
|
2008-07-22 14:16:55 +04:00
|
|
|
*
|
|
|
|
* Locking:
|
2013-06-15 15:04:46 +04:00
|
|
|
* takes tty_ldiscs_lock to guard against ldisc races
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
2013-06-15 15:04:48 +04:00
|
|
|
static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
|
2008-07-22 14:16:55 +04:00
|
|
|
{
|
2009-06-11 15:50:12 +04:00
|
|
|
struct tty_ldisc *ld;
|
2009-08-04 03:01:28 +04:00
|
|
|
struct tty_ldisc_ops *ldops;
|
2008-07-22 14:16:55 +04:00
|
|
|
|
|
|
|
if (disc < N_TTY || disc >= NR_LDISCS)
|
2009-06-11 15:50:12 +04:00
|
|
|
return ERR_PTR(-EINVAL);
|
2009-08-04 03:01:28 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the ldisc ops - we may need to request them to be loaded
|
|
|
|
* dynamically and try again.
|
|
|
|
*/
|
|
|
|
ldops = get_ldops(disc);
|
|
|
|
if (IS_ERR(ldops)) {
|
2008-07-22 14:16:55 +04:00
|
|
|
request_module("tty-ldisc-%d", disc);
|
2009-08-04 03:01:28 +04:00
|
|
|
ldops = get_ldops(disc);
|
|
|
|
if (IS_ERR(ldops))
|
|
|
|
return ERR_CAST(ldops);
|
|
|
|
}
|
|
|
|
|
|
|
|
ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
|
|
|
|
if (ld == NULL) {
|
|
|
|
put_ldops(ldops);
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
2009-08-04 03:01:28 +04:00
|
|
|
|
|
|
|
ld->ops = ldops;
|
2013-06-15 15:04:48 +04:00
|
|
|
ld->tty = tty;
|
2012-05-03 16:37:43 +04:00
|
|
|
|
2009-06-11 15:50:12 +04:00
|
|
|
return ld;
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
|
2013-03-12 00:44:43 +04:00
|
|
|
/**
|
|
|
|
* tty_ldisc_put - release the ldisc
|
|
|
|
*
|
|
|
|
* Complement of tty_ldisc_get().
|
|
|
|
*/
|
2015-10-27 19:40:02 +03:00
|
|
|
static void tty_ldisc_put(struct tty_ldisc *ld)
|
2013-03-12 00:44:43 +04:00
|
|
|
{
|
|
|
|
if (WARN_ON_ONCE(!ld))
|
|
|
|
return;
|
|
|
|
|
2013-06-15 15:04:48 +04:00
|
|
|
put_ldops(ld->ops);
|
2013-03-12 00:44:43 +04:00
|
|
|
kfree(ld);
|
|
|
|
}
|
|
|
|
|
2009-06-11 15:51:41 +04:00
|
|
|
static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
|
2008-07-22 14:16:55 +04:00
|
|
|
{
|
|
|
|
return (*pos < NR_LDISCS) ? pos : NULL;
|
|
|
|
}
|
|
|
|
|
2009-06-11 15:51:41 +04:00
|
|
|
static void *tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos)
|
2008-07-22 14:16:55 +04:00
|
|
|
{
|
|
|
|
(*pos)++;
|
|
|
|
return (*pos < NR_LDISCS) ? pos : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tty_ldiscs_seq_stop(struct seq_file *m, void *v)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tty_ldiscs_seq_show(struct seq_file *m, void *v)
|
|
|
|
{
|
|
|
|
int i = *(loff_t *)v;
|
2009-08-04 03:00:15 +04:00
|
|
|
struct tty_ldisc_ops *ldops;
|
2009-06-11 15:51:41 +04:00
|
|
|
|
2009-08-04 03:00:15 +04:00
|
|
|
ldops = get_ldops(i);
|
|
|
|
if (IS_ERR(ldops))
|
2008-07-22 14:16:55 +04:00
|
|
|
return 0;
|
2009-08-04 03:00:15 +04:00
|
|
|
seq_printf(m, "%-10s %2d\n", ldops->name ? ldops->name : "???", i);
|
|
|
|
put_ldops(ldops);
|
2008-07-22 14:16:55 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct seq_operations tty_ldiscs_seq_ops = {
|
|
|
|
.start = tty_ldiscs_seq_start,
|
|
|
|
.next = tty_ldiscs_seq_next,
|
|
|
|
.stop = tty_ldiscs_seq_stop,
|
|
|
|
.show = tty_ldiscs_seq_show,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int proc_tty_ldiscs_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return seq_open(file, &tty_ldiscs_seq_ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct file_operations tty_ldiscs_proc_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = proc_tty_ldiscs_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
.release = seq_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_ref_wait - wait for the tty ldisc
|
|
|
|
* @tty: tty device
|
|
|
|
*
|
|
|
|
* Dereference the line discipline for the terminal and take a
|
|
|
|
* reference to it. If the line discipline is in flux then
|
|
|
|
* wait patiently until it changes.
|
|
|
|
*
|
tty: Destroy ldisc instance on hangup
Currently, when the tty is hungup, the ldisc is re-instanced; ie., the
current instance is destroyed and a new instance is created. The purpose
of this design was to guarantee a valid, open ldisc for the lifetime of
the tty.
However, now that tty buffers are owned by and have lifetime equivalent
to the tty_port (since v3.10), any data received immediately after the
ldisc is re-instanced may cause continued driver i/o operations
concurrently with the driver's hangup() operation. For drivers that
shutdown h/w on hangup, this is unexpected and usually bad. For example,
the serial core may free the xmit buffer page concurrently with an
in-progress write() operation (triggered by echo).
With the existing stable and robust ldisc reference handling, the
cleaned-up tty_reopen(), the straggling unsafe ldisc use cleaned up, and
the preparation to properly handle a NULL tty->ldisc, the ldisc instance
can be destroyed and only re-instanced when the tty is re-opened.
If the tty was opened as /dev/console or /dev/tty0, the original behavior
of re-instancing the ldisc is retained (the 'reinit' parameter to
tty_ldisc_hangup() is true). This is required since those file descriptors
are never hungup.
This patch has neglible impact on userspace; the tty file_operations ptr
is changed to point to the hungup file operations _before_ the ldisc
instance is destroyed, so only racing file operations might now retrieve
a NULL ldisc reference (which is simply handled as if the hungup file
operation had been called instead -- see "tty: Prepare for destroying
line discipline on hangup").
This resolves a long-standing FIXME and several crash reports.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-01-11 09:41:06 +03:00
|
|
|
* Returns: NULL if the tty has been hungup and not re-opened with
|
|
|
|
* a new file descriptor, otherwise valid ldisc reference
|
|
|
|
*
|
2008-07-22 14:16:55 +04:00
|
|
|
* Note: Must not be called from an IRQ/timer context. The caller
|
|
|
|
* must also be careful not to hold other locks that will deadlock
|
|
|
|
* against a discipline change, such as an existing ldisc reference
|
|
|
|
* (which we check for)
|
|
|
|
*
|
2016-01-11 09:41:01 +03:00
|
|
|
* Note: a file_operations routine (read/poll/write) should use this
|
|
|
|
* function to wait for any ldisc lifetime events to finish.
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
|
|
|
|
{
|
2013-06-15 15:04:48 +04:00
|
|
|
ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT);
|
2016-01-11 09:41:02 +03:00
|
|
|
if (!tty->ldisc)
|
|
|
|
ldsem_up_read(&tty->ldisc_sem);
|
2013-06-15 15:04:48 +04:00
|
|
|
return tty->ldisc;
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_ref - get the tty ldisc
|
|
|
|
* @tty: tty device
|
|
|
|
*
|
|
|
|
* Dereference the line discipline for the terminal and take a
|
|
|
|
* reference to it. If the line discipline is in flux then
|
|
|
|
* return NULL. Can be called from IRQ and timer functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
|
|
|
|
{
|
2013-06-15 15:04:48 +04:00
|
|
|
struct tty_ldisc *ld = NULL;
|
|
|
|
|
|
|
|
if (ldsem_down_read_trylock(&tty->ldisc_sem)) {
|
|
|
|
ld = tty->ldisc;
|
|
|
|
if (!ld)
|
|
|
|
ldsem_up_read(&tty->ldisc_sem);
|
|
|
|
}
|
|
|
|
return ld;
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tty_ldisc_ref);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_deref - free a tty ldisc reference
|
|
|
|
* @ld: reference to free up
|
|
|
|
*
|
|
|
|
* Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
|
|
|
|
* be called in IRQ context.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void tty_ldisc_deref(struct tty_ldisc *ld)
|
|
|
|
{
|
2013-06-15 15:04:48 +04:00
|
|
|
ldsem_up_read(&ld->tty->ldisc_sem);
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tty_ldisc_deref);
|
|
|
|
|
2013-06-15 15:04:47 +04:00
|
|
|
|
2016-01-10 08:13:51 +03:00
|
|
|
static inline int
|
2014-11-05 20:13:06 +03:00
|
|
|
__tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
|
2013-06-15 15:04:47 +04:00
|
|
|
{
|
|
|
|
return ldsem_down_write(&tty->ldisc_sem, timeout);
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:13:51 +03:00
|
|
|
static inline int
|
2014-11-05 20:13:06 +03:00
|
|
|
__tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout)
|
2013-06-15 15:04:47 +04:00
|
|
|
{
|
|
|
|
return ldsem_down_write_nested(&tty->ldisc_sem,
|
|
|
|
LDISC_SEM_OTHER, timeout);
|
|
|
|
}
|
|
|
|
|
2014-11-05 20:13:06 +03:00
|
|
|
static inline void __tty_ldisc_unlock(struct tty_struct *tty)
|
2013-06-15 15:04:47 +04:00
|
|
|
{
|
2015-10-04 22:19:18 +03:00
|
|
|
ldsem_up_write(&tty->ldisc_sem);
|
2013-06-15 15:04:47 +04:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:13:51 +03:00
|
|
|
static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
|
2014-11-05 20:13:07 +03:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = __tty_ldisc_lock(tty, timeout);
|
|
|
|
if (!ret)
|
|
|
|
return -EBUSY;
|
|
|
|
set_bit(TTY_LDISC_HALTED, &tty->flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tty_ldisc_unlock(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
clear_bit(TTY_LDISC_HALTED, &tty->flags);
|
|
|
|
__tty_ldisc_unlock(tty);
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:13:51 +03:00
|
|
|
static int
|
2013-06-15 15:04:47 +04:00
|
|
|
tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2,
|
|
|
|
unsigned long timeout)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (tty < tty2) {
|
2014-11-05 20:13:06 +03:00
|
|
|
ret = __tty_ldisc_lock(tty, timeout);
|
2013-06-15 15:04:47 +04:00
|
|
|
if (ret) {
|
2014-11-05 20:13:06 +03:00
|
|
|
ret = __tty_ldisc_lock_nested(tty2, timeout);
|
2013-06-15 15:04:47 +04:00
|
|
|
if (!ret)
|
2014-11-05 20:13:06 +03:00
|
|
|
__tty_ldisc_unlock(tty);
|
2013-06-15 15:04:47 +04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* if this is possible, it has lots of implications */
|
|
|
|
WARN_ON_ONCE(tty == tty2);
|
|
|
|
if (tty2 && tty != tty2) {
|
2014-11-05 20:13:06 +03:00
|
|
|
ret = __tty_ldisc_lock(tty2, timeout);
|
2013-06-15 15:04:47 +04:00
|
|
|
if (ret) {
|
2014-11-05 20:13:06 +03:00
|
|
|
ret = __tty_ldisc_lock_nested(tty, timeout);
|
2013-06-15 15:04:47 +04:00
|
|
|
if (!ret)
|
2014-11-05 20:13:06 +03:00
|
|
|
__tty_ldisc_unlock(tty2);
|
2013-06-15 15:04:47 +04:00
|
|
|
}
|
|
|
|
} else
|
2014-11-05 20:13:06 +03:00
|
|
|
ret = __tty_ldisc_lock(tty, timeout);
|
2013-06-15 15:04:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
set_bit(TTY_LDISC_HALTED, &tty->flags);
|
|
|
|
if (tty2)
|
|
|
|
set_bit(TTY_LDISC_HALTED, &tty2->flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:13:51 +03:00
|
|
|
static void tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2)
|
2013-06-15 15:04:47 +04:00
|
|
|
{
|
|
|
|
tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:13:51 +03:00
|
|
|
static void tty_ldisc_unlock_pair(struct tty_struct *tty,
|
|
|
|
struct tty_struct *tty2)
|
2013-06-15 15:04:47 +04:00
|
|
|
{
|
2014-11-05 20:13:06 +03:00
|
|
|
__tty_ldisc_unlock(tty);
|
2013-06-15 15:04:47 +04:00
|
|
|
if (tty2)
|
2014-11-05 20:13:06 +03:00
|
|
|
__tty_ldisc_unlock(tty2);
|
2013-06-15 15:04:47 +04:00
|
|
|
}
|
|
|
|
|
2009-06-11 15:50:58 +04:00
|
|
|
/**
|
|
|
|
* tty_ldisc_flush - flush line discipline queue
|
|
|
|
* @tty: tty
|
|
|
|
*
|
2014-11-05 20:13:09 +03:00
|
|
|
* Flush the line discipline queue (if any) and the tty flip buffers
|
|
|
|
* for this tty.
|
2009-06-11 15:50:58 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
void tty_ldisc_flush(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct tty_ldisc *ld = tty_ldisc_ref(tty);
|
2014-11-05 20:13:09 +03:00
|
|
|
|
|
|
|
tty_buffer_flush(tty, ld);
|
|
|
|
if (ld)
|
2009-06-11 15:50:58 +04:00
|
|
|
tty_ldisc_deref(ld);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tty_ldisc_flush);
|
|
|
|
|
2008-07-22 14:16:55 +04:00
|
|
|
/**
|
|
|
|
* tty_set_termios_ldisc - set ldisc field
|
|
|
|
* @tty: tty structure
|
2016-01-11 09:41:04 +03:00
|
|
|
* @disc: line discipline number
|
2008-07-22 14:16:55 +04:00
|
|
|
*
|
|
|
|
* This is probably overkill for real world processors but
|
|
|
|
* they are not on hot paths so a little discipline won't do
|
|
|
|
* any harm.
|
|
|
|
*
|
2015-11-27 22:30:21 +03:00
|
|
|
* The line discipline-related tty_struct fields are reset to
|
|
|
|
* prevent the ldisc driver from re-using stale information for
|
|
|
|
* the new ldisc instance.
|
|
|
|
*
|
2013-06-15 17:14:23 +04:00
|
|
|
* Locking: takes termios_rwsem
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
2016-01-11 09:41:04 +03:00
|
|
|
static void tty_set_termios_ldisc(struct tty_struct *tty, int disc)
|
2008-07-22 14:16:55 +04:00
|
|
|
{
|
2013-06-15 17:14:23 +04:00
|
|
|
down_write(&tty->termios_rwsem);
|
2016-01-11 09:41:04 +03:00
|
|
|
tty->termios.c_line = disc;
|
2013-06-15 17:14:23 +04:00
|
|
|
up_write(&tty->termios_rwsem);
|
2015-11-27 22:30:21 +03:00
|
|
|
|
|
|
|
tty->disc_data = NULL;
|
|
|
|
tty->receive_room = 0;
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
|
2009-06-11 15:50:12 +04:00
|
|
|
/**
|
|
|
|
* tty_ldisc_open - open a line discipline
|
|
|
|
* @tty: tty we are opening the ldisc on
|
|
|
|
* @ld: discipline to open
|
|
|
|
*
|
|
|
|
* A helper opening method. Also a convenient debugging and check
|
|
|
|
* point.
|
2010-06-02 00:53:01 +04:00
|
|
|
*
|
|
|
|
* Locking: always called with BTM already held.
|
2009-06-11 15:50:12 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
|
|
|
|
{
|
|
|
|
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
|
2009-11-30 16:18:35 +03:00
|
|
|
if (ld->ops->open) {
|
|
|
|
int ret;
|
2010-06-02 00:53:01 +04:00
|
|
|
/* BTM here locks versus a hangup event */
|
2009-11-30 16:18:35 +03:00
|
|
|
ret = ld->ops->open(tty);
|
2010-11-25 02:27:54 +03:00
|
|
|
if (ret)
|
|
|
|
clear_bit(TTY_LDISC_OPEN, &tty->flags);
|
2015-07-13 05:49:12 +03:00
|
|
|
|
2016-01-11 09:41:02 +03:00
|
|
|
tty_ldisc_debug(tty, "%p: opened\n", ld);
|
2009-11-30 16:18:35 +03:00
|
|
|
return ret;
|
|
|
|
}
|
2009-06-11 15:50:12 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_close - close a line discipline
|
|
|
|
* @tty: tty we are opening the ldisc on
|
|
|
|
* @ld: discipline to close
|
|
|
|
*
|
|
|
|
* A helper close method. Also a convenient debugging and check
|
|
|
|
* point.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
|
|
|
|
{
|
|
|
|
WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags));
|
|
|
|
clear_bit(TTY_LDISC_OPEN, &tty->flags);
|
|
|
|
if (ld->ops->close)
|
|
|
|
ld->ops->close(tty);
|
2016-01-11 09:41:02 +03:00
|
|
|
tty_ldisc_debug(tty, "%p: closed\n", ld);
|
2009-06-11 15:50:12 +04:00
|
|
|
}
|
2008-07-22 14:16:55 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_restore - helper for tty ldisc change
|
|
|
|
* @tty: tty to recover
|
|
|
|
* @old: previous ldisc
|
|
|
|
*
|
|
|
|
* Restore the previous line discipline or N_TTY when a line discipline
|
|
|
|
* change fails due to an open error
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
|
|
|
|
{
|
2009-06-11 15:50:12 +04:00
|
|
|
struct tty_ldisc *new_ldisc;
|
|
|
|
int r;
|
2008-07-22 14:16:55 +04:00
|
|
|
|
|
|
|
/* There is an outstanding reference here so this is safe */
|
2013-06-15 15:04:48 +04:00
|
|
|
old = tty_ldisc_get(tty, old->ops->num);
|
2009-06-11 15:50:12 +04:00
|
|
|
WARN_ON(IS_ERR(old));
|
2013-03-12 00:44:42 +04:00
|
|
|
tty->ldisc = old;
|
2008-07-22 14:16:55 +04:00
|
|
|
tty_set_termios_ldisc(tty, old->ops->num);
|
2009-06-11 15:50:12 +04:00
|
|
|
if (tty_ldisc_open(tty, old) < 0) {
|
|
|
|
tty_ldisc_put(old);
|
2008-07-22 14:16:55 +04:00
|
|
|
/* This driver is always present */
|
2013-06-15 15:04:48 +04:00
|
|
|
new_ldisc = tty_ldisc_get(tty, N_TTY);
|
2009-06-11 15:50:12 +04:00
|
|
|
if (IS_ERR(new_ldisc))
|
2008-07-22 14:16:55 +04:00
|
|
|
panic("n_tty: get");
|
2013-03-12 00:44:42 +04:00
|
|
|
tty->ldisc = new_ldisc;
|
2008-07-22 14:16:55 +04:00
|
|
|
tty_set_termios_ldisc(tty, N_TTY);
|
2009-06-11 15:50:12 +04:00
|
|
|
r = tty_ldisc_open(tty, new_ldisc);
|
|
|
|
if (r < 0)
|
|
|
|
panic("Couldn't open N_TTY ldisc for "
|
|
|
|
"%s --- error %d.",
|
2015-03-31 16:55:59 +03:00
|
|
|
tty_name(tty), r);
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_set_ldisc - set line discipline
|
|
|
|
* @tty: the terminal to set
|
|
|
|
* @ldisc: the line discipline
|
|
|
|
*
|
|
|
|
* Set the discipline of a tty line. Must be called from a process
|
2009-06-11 15:50:12 +04:00
|
|
|
* context. The ldisc change logic has to protect itself against any
|
|
|
|
* overlapping ldisc change (including on the other end of pty pairs),
|
|
|
|
* the close of one side of a tty/pty pair, and eventually hangup.
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
2016-01-11 09:41:04 +03:00
|
|
|
int tty_set_ldisc(struct tty_struct *tty, int disc)
|
2008-07-22 14:16:55 +04:00
|
|
|
{
|
|
|
|
int retval;
|
2013-06-15 15:04:49 +04:00
|
|
|
struct tty_ldisc *old_ldisc, *new_ldisc;
|
2008-07-22 14:16:55 +04:00
|
|
|
|
2016-01-11 09:41:04 +03:00
|
|
|
new_ldisc = tty_ldisc_get(tty, disc);
|
2009-06-11 15:50:12 +04:00
|
|
|
if (IS_ERR(new_ldisc))
|
|
|
|
return PTR_ERR(new_ldisc);
|
2008-07-22 14:16:55 +04:00
|
|
|
|
2014-11-05 20:12:45 +03:00
|
|
|
tty_lock(tty);
|
2014-11-05 20:13:08 +03:00
|
|
|
retval = tty_ldisc_lock(tty, 5 * HZ);
|
2015-11-08 17:29:38 +03:00
|
|
|
if (retval)
|
|
|
|
goto err;
|
2008-07-22 14:16:55 +04:00
|
|
|
|
2016-01-11 09:41:02 +03:00
|
|
|
if (!tty->ldisc) {
|
|
|
|
retval = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-11-08 17:29:38 +03:00
|
|
|
/* Check the no-op case */
|
2016-01-11 09:41:04 +03:00
|
|
|
if (tty->ldisc->ops->num == disc)
|
2015-11-08 17:29:38 +03:00
|
|
|
goto out;
|
2009-06-11 15:50:12 +04:00
|
|
|
|
2015-11-08 17:29:38 +03:00
|
|
|
if (test_bit(TTY_HUPPED, &tty->flags)) {
|
|
|
|
/* We were raced by hangup */
|
|
|
|
retval = -EIO;
|
|
|
|
goto out;
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
|
2013-06-15 15:04:49 +04:00
|
|
|
old_ldisc = tty->ldisc;
|
TTY: restore tty_ldisc_wait_idle
It was removed in 65b770468e98 (tty-ldisc: turn ldisc user count into
a proper refcount), but we need to wait for last user to quit the
ldisc before we close it in tty_set_ldisc.
Otherwise weird things start to happen. There might be processes
waiting in tty_read->n_tty_read on tty->read_wait for input to appear
and at that moment, a change of ldisc is fatal. n_tty_close is called,
it frees read_buf and the waiting process is still in the middle of
reading and goes nuts after it is woken.
Previously we prevented close to happen when others are in ldisc ops
by tty_ldisc_wait_idle in tty_set_ldisc. But the commit above removed
that. So revoke the change and test whether there is 1 user (=we), and
allow the close then.
We can do that without ldisc/tty locks, because nobody else can open
the device due to TTY_LDISC_CHANGING bit set, so we in fact wait for
everybody to leave.
I don't understand why tty_ldisc_lock would be needed either when the
counter is an atomic variable, so this is a lockless
tty_ldisc_wait_idle.
On the other hand, if we fail to wait (timeout or signal), we have to
reenable the halted ldiscs, so we take ldisc lock and reuse the setup
path at the end of tty_set_ldisc.
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Tested-by: Sebastian Andrzej Siewior <bigeasy@breakpoint.cc>
LKML-Reference: <20101031104136.GA511@Chamillionaire.breakpoint.cc>
LKML-Reference: <1287669539-22644-1-git-send-email-jslaby@suse.cz>
Cc: Alan Cox <alan@linux.intel.com>
Cc: stable@kernel.org [32, 33, 36]
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-11-01 01:17:51 +03:00
|
|
|
|
2013-06-15 15:04:49 +04:00
|
|
|
/* Shutdown the old discipline. */
|
|
|
|
tty_ldisc_close(tty, old_ldisc);
|
2008-07-22 14:16:55 +04:00
|
|
|
|
|
|
|
/* Now set up the new line discipline. */
|
2013-03-12 00:44:42 +04:00
|
|
|
tty->ldisc = new_ldisc;
|
2016-01-11 09:41:04 +03:00
|
|
|
tty_set_termios_ldisc(tty, disc);
|
2009-06-11 15:50:12 +04:00
|
|
|
|
|
|
|
retval = tty_ldisc_open(tty, new_ldisc);
|
2008-07-22 14:16:55 +04:00
|
|
|
if (retval < 0) {
|
2009-06-11 15:50:12 +04:00
|
|
|
/* Back to the old one or N_TTY if we can't */
|
|
|
|
tty_ldisc_put(new_ldisc);
|
2013-06-15 15:04:49 +04:00
|
|
|
tty_ldisc_restore(tty, old_ldisc);
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
2009-06-11 15:50:12 +04:00
|
|
|
|
2014-11-05 21:11:41 +03:00
|
|
|
if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) {
|
|
|
|
down_read(&tty->termios_rwsem);
|
2008-07-22 14:16:55 +04:00
|
|
|
tty->ops->set_ldisc(tty);
|
2014-11-05 21:11:41 +03:00
|
|
|
up_read(&tty->termios_rwsem);
|
|
|
|
}
|
2008-07-22 14:16:55 +04:00
|
|
|
|
2013-06-15 15:04:51 +04:00
|
|
|
/* At this point we hold a reference to the new ldisc and a
|
|
|
|
reference to the old ldisc, or we hold two references to
|
|
|
|
the old ldisc (if it was restored as part of error cleanup
|
|
|
|
above). In either case, releasing a single reference from
|
|
|
|
the old ldisc is correct. */
|
2015-11-08 17:29:38 +03:00
|
|
|
new_ldisc = old_ldisc;
|
|
|
|
out:
|
2014-11-05 20:13:08 +03:00
|
|
|
tty_ldisc_unlock(tty);
|
2008-07-22 14:16:55 +04:00
|
|
|
|
2009-06-11 15:50:12 +04:00
|
|
|
/* Restart the work queue in case no characters kick it off. Safe if
|
2008-07-22 14:16:55 +04:00
|
|
|
already running */
|
2015-11-08 15:53:06 +03:00
|
|
|
tty_buffer_restart_work(tty->port);
|
2015-11-08 17:29:38 +03:00
|
|
|
err:
|
|
|
|
tty_ldisc_put(new_ldisc); /* drop the extra reference */
|
2012-08-08 19:30:13 +04:00
|
|
|
tty_unlock(tty);
|
2008-07-22 14:16:55 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2016-01-11 09:41:03 +03:00
|
|
|
/**
|
|
|
|
* tty_ldisc_kill - teardown ldisc
|
|
|
|
* @tty: tty being released
|
|
|
|
*
|
|
|
|
* Perform final close of the ldisc and reset tty->ldisc
|
|
|
|
*/
|
|
|
|
static void tty_ldisc_kill(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
if (!tty->ldisc)
|
|
|
|
return;
|
|
|
|
/*
|
|
|
|
* Now kill off the ldisc
|
|
|
|
*/
|
|
|
|
tty_ldisc_close(tty, tty->ldisc);
|
|
|
|
tty_ldisc_put(tty->ldisc);
|
|
|
|
/* Force an oops if we mess this up */
|
|
|
|
tty->ldisc = NULL;
|
|
|
|
}
|
|
|
|
|
2009-06-11 15:50:12 +04:00
|
|
|
/**
|
|
|
|
* tty_reset_termios - reset terminal state
|
|
|
|
* @tty: tty to reset
|
|
|
|
*
|
|
|
|
* Restore a terminal to the driver default state.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void tty_reset_termios(struct tty_struct *tty)
|
|
|
|
{
|
2013-06-15 17:14:23 +04:00
|
|
|
down_write(&tty->termios_rwsem);
|
2012-07-14 18:31:47 +04:00
|
|
|
tty->termios = tty->driver->init_termios;
|
|
|
|
tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios);
|
|
|
|
tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios);
|
2013-06-15 17:14:23 +04:00
|
|
|
up_write(&tty->termios_rwsem);
|
2009-06-11 15:50:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_reinit - reinitialise the tty ldisc
|
|
|
|
* @tty: tty to reinit
|
2016-01-11 09:41:04 +03:00
|
|
|
* @disc: line discipline to reinitialize
|
2009-06-11 15:50:12 +04:00
|
|
|
*
|
2016-01-11 09:41:05 +03:00
|
|
|
* Completely reinitialize the line discipline state, by closing the
|
tty: Destroy ldisc instance on hangup
Currently, when the tty is hungup, the ldisc is re-instanced; ie., the
current instance is destroyed and a new instance is created. The purpose
of this design was to guarantee a valid, open ldisc for the lifetime of
the tty.
However, now that tty buffers are owned by and have lifetime equivalent
to the tty_port (since v3.10), any data received immediately after the
ldisc is re-instanced may cause continued driver i/o operations
concurrently with the driver's hangup() operation. For drivers that
shutdown h/w on hangup, this is unexpected and usually bad. For example,
the serial core may free the xmit buffer page concurrently with an
in-progress write() operation (triggered by echo).
With the existing stable and robust ldisc reference handling, the
cleaned-up tty_reopen(), the straggling unsafe ldisc use cleaned up, and
the preparation to properly handle a NULL tty->ldisc, the ldisc instance
can be destroyed and only re-instanced when the tty is re-opened.
If the tty was opened as /dev/console or /dev/tty0, the original behavior
of re-instancing the ldisc is retained (the 'reinit' parameter to
tty_ldisc_hangup() is true). This is required since those file descriptors
are never hungup.
This patch has neglible impact on userspace; the tty file_operations ptr
is changed to point to the hungup file operations _before_ the ldisc
instance is destroyed, so only racing file operations might now retrieve
a NULL ldisc reference (which is simply handled as if the hungup file
operation had been called instead -- see "tty: Prepare for destroying
line discipline on hangup").
This resolves a long-standing FIXME and several crash reports.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-01-11 09:41:06 +03:00
|
|
|
* current instance, if there is one, and opening a new instance. If
|
|
|
|
* an error occurs opening the new non-N_TTY instance, the instance
|
|
|
|
* is dropped and tty->ldisc reset to NULL. The caller can then retry
|
|
|
|
* with N_TTY instead.
|
2016-01-11 09:41:05 +03:00
|
|
|
*
|
|
|
|
* Returns 0 if successful, otherwise error code < 0
|
2009-06-11 15:50:12 +04:00
|
|
|
*/
|
|
|
|
|
tty: Destroy ldisc instance on hangup
Currently, when the tty is hungup, the ldisc is re-instanced; ie., the
current instance is destroyed and a new instance is created. The purpose
of this design was to guarantee a valid, open ldisc for the lifetime of
the tty.
However, now that tty buffers are owned by and have lifetime equivalent
to the tty_port (since v3.10), any data received immediately after the
ldisc is re-instanced may cause continued driver i/o operations
concurrently with the driver's hangup() operation. For drivers that
shutdown h/w on hangup, this is unexpected and usually bad. For example,
the serial core may free the xmit buffer page concurrently with an
in-progress write() operation (triggered by echo).
With the existing stable and robust ldisc reference handling, the
cleaned-up tty_reopen(), the straggling unsafe ldisc use cleaned up, and
the preparation to properly handle a NULL tty->ldisc, the ldisc instance
can be destroyed and only re-instanced when the tty is re-opened.
If the tty was opened as /dev/console or /dev/tty0, the original behavior
of re-instancing the ldisc is retained (the 'reinit' parameter to
tty_ldisc_hangup() is true). This is required since those file descriptors
are never hungup.
This patch has neglible impact on userspace; the tty file_operations ptr
is changed to point to the hungup file operations _before_ the ldisc
instance is destroyed, so only racing file operations might now retrieve
a NULL ldisc reference (which is simply handled as if the hungup file
operation had been called instead -- see "tty: Prepare for destroying
line discipline on hangup").
This resolves a long-standing FIXME and several crash reports.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-01-11 09:41:06 +03:00
|
|
|
int tty_ldisc_reinit(struct tty_struct *tty, int disc)
|
2009-06-11 15:50:12 +04:00
|
|
|
{
|
2016-01-11 09:41:05 +03:00
|
|
|
struct tty_ldisc *ld;
|
|
|
|
int retval;
|
2010-10-27 19:13:21 +04:00
|
|
|
|
2016-01-11 09:41:05 +03:00
|
|
|
ld = tty_ldisc_get(tty, disc);
|
|
|
|
if (IS_ERR(ld)) {
|
|
|
|
BUG_ON(disc == N_TTY);
|
|
|
|
return PTR_ERR(ld);
|
|
|
|
}
|
2009-06-11 15:50:12 +04:00
|
|
|
|
2016-01-11 09:41:05 +03:00
|
|
|
if (tty->ldisc) {
|
|
|
|
tty_ldisc_close(tty, tty->ldisc);
|
|
|
|
tty_ldisc_put(tty->ldisc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* switch the line discipline */
|
2013-03-12 00:44:42 +04:00
|
|
|
tty->ldisc = ld;
|
2016-01-11 09:41:04 +03:00
|
|
|
tty_set_termios_ldisc(tty, disc);
|
2016-01-11 09:41:05 +03:00
|
|
|
retval = tty_ldisc_open(tty, tty->ldisc);
|
|
|
|
if (retval) {
|
|
|
|
if (!WARN_ON(disc == N_TTY)) {
|
|
|
|
tty_ldisc_put(tty->ldisc);
|
|
|
|
tty->ldisc = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
2009-06-11 15:50:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_hangup - hangup ldisc reset
|
|
|
|
* @tty: tty being hung up
|
|
|
|
*
|
|
|
|
* Some tty devices reset their termios when they receive a hangup
|
|
|
|
* event. In that situation we must also switch back to N_TTY properly
|
|
|
|
* before we reset the termios data.
|
|
|
|
*
|
|
|
|
* Locking: We can take the ldisc mutex as the rest of the code is
|
|
|
|
* careful to allow for this.
|
|
|
|
*
|
|
|
|
* In the pty pair case this occurs in the close() path of the
|
|
|
|
* tty itself so we must be careful about locking rules.
|
|
|
|
*/
|
|
|
|
|
tty: Destroy ldisc instance on hangup
Currently, when the tty is hungup, the ldisc is re-instanced; ie., the
current instance is destroyed and a new instance is created. The purpose
of this design was to guarantee a valid, open ldisc for the lifetime of
the tty.
However, now that tty buffers are owned by and have lifetime equivalent
to the tty_port (since v3.10), any data received immediately after the
ldisc is re-instanced may cause continued driver i/o operations
concurrently with the driver's hangup() operation. For drivers that
shutdown h/w on hangup, this is unexpected and usually bad. For example,
the serial core may free the xmit buffer page concurrently with an
in-progress write() operation (triggered by echo).
With the existing stable and robust ldisc reference handling, the
cleaned-up tty_reopen(), the straggling unsafe ldisc use cleaned up, and
the preparation to properly handle a NULL tty->ldisc, the ldisc instance
can be destroyed and only re-instanced when the tty is re-opened.
If the tty was opened as /dev/console or /dev/tty0, the original behavior
of re-instancing the ldisc is retained (the 'reinit' parameter to
tty_ldisc_hangup() is true). This is required since those file descriptors
are never hungup.
This patch has neglible impact on userspace; the tty file_operations ptr
is changed to point to the hungup file operations _before_ the ldisc
instance is destroyed, so only racing file operations might now retrieve
a NULL ldisc reference (which is simply handled as if the hungup file
operation had been called instead -- see "tty: Prepare for destroying
line discipline on hangup").
This resolves a long-standing FIXME and several crash reports.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-01-11 09:41:06 +03:00
|
|
|
void tty_ldisc_hangup(struct tty_struct *tty, bool reinit)
|
2009-06-11 15:50:12 +04:00
|
|
|
{
|
|
|
|
struct tty_ldisc *ld;
|
|
|
|
|
2016-01-11 09:41:02 +03:00
|
|
|
tty_ldisc_debug(tty, "%p: hangup\n", tty->ldisc);
|
2013-03-12 00:44:38 +04:00
|
|
|
|
2009-06-11 15:50:12 +04:00
|
|
|
ld = tty_ldisc_ref(tty);
|
|
|
|
if (ld != NULL) {
|
|
|
|
if (ld->ops->flush_buffer)
|
|
|
|
ld->ops->flush_buffer(tty);
|
|
|
|
tty_driver_flush_buffer(tty);
|
|
|
|
if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
|
|
|
|
ld->ops->write_wakeup)
|
|
|
|
ld->ops->write_wakeup(tty);
|
|
|
|
if (ld->ops->hangup)
|
|
|
|
ld->ops->hangup(tty);
|
|
|
|
tty_ldisc_deref(ld);
|
|
|
|
}
|
2013-06-15 15:04:48 +04:00
|
|
|
|
2009-06-11 15:50:12 +04:00
|
|
|
wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
|
|
|
|
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
|
2013-06-15 15:04:48 +04:00
|
|
|
|
2009-06-11 15:50:12 +04:00
|
|
|
/*
|
|
|
|
* Shutdown the current line discipline, and reset it to
|
2010-02-08 13:09:26 +03:00
|
|
|
* N_TTY if need be.
|
|
|
|
*
|
|
|
|
* Avoid racing set_ldisc or tty_ldisc_release
|
2009-06-11 15:50:12 +04:00
|
|
|
*/
|
2014-11-05 20:13:07 +03:00
|
|
|
tty_ldisc_lock(tty, MAX_SCHEDULE_TIMEOUT);
|
2010-06-02 00:53:06 +04:00
|
|
|
|
tty: Destroy ldisc instance on hangup
Currently, when the tty is hungup, the ldisc is re-instanced; ie., the
current instance is destroyed and a new instance is created. The purpose
of this design was to guarantee a valid, open ldisc for the lifetime of
the tty.
However, now that tty buffers are owned by and have lifetime equivalent
to the tty_port (since v3.10), any data received immediately after the
ldisc is re-instanced may cause continued driver i/o operations
concurrently with the driver's hangup() operation. For drivers that
shutdown h/w on hangup, this is unexpected and usually bad. For example,
the serial core may free the xmit buffer page concurrently with an
in-progress write() operation (triggered by echo).
With the existing stable and robust ldisc reference handling, the
cleaned-up tty_reopen(), the straggling unsafe ldisc use cleaned up, and
the preparation to properly handle a NULL tty->ldisc, the ldisc instance
can be destroyed and only re-instanced when the tty is re-opened.
If the tty was opened as /dev/console or /dev/tty0, the original behavior
of re-instancing the ldisc is retained (the 'reinit' parameter to
tty_ldisc_hangup() is true). This is required since those file descriptors
are never hungup.
This patch has neglible impact on userspace; the tty file_operations ptr
is changed to point to the hungup file operations _before_ the ldisc
instance is destroyed, so only racing file operations might now retrieve
a NULL ldisc reference (which is simply handled as if the hungup file
operation had been called instead -- see "tty: Prepare for destroying
line discipline on hangup").
This resolves a long-standing FIXME and several crash reports.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-01-11 09:41:06 +03:00
|
|
|
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
|
|
|
|
tty_reset_termios(tty);
|
2013-03-12 00:44:36 +04:00
|
|
|
|
tty: Destroy ldisc instance on hangup
Currently, when the tty is hungup, the ldisc is re-instanced; ie., the
current instance is destroyed and a new instance is created. The purpose
of this design was to guarantee a valid, open ldisc for the lifetime of
the tty.
However, now that tty buffers are owned by and have lifetime equivalent
to the tty_port (since v3.10), any data received immediately after the
ldisc is re-instanced may cause continued driver i/o operations
concurrently with the driver's hangup() operation. For drivers that
shutdown h/w on hangup, this is unexpected and usually bad. For example,
the serial core may free the xmit buffer page concurrently with an
in-progress write() operation (triggered by echo).
With the existing stable and robust ldisc reference handling, the
cleaned-up tty_reopen(), the straggling unsafe ldisc use cleaned up, and
the preparation to properly handle a NULL tty->ldisc, the ldisc instance
can be destroyed and only re-instanced when the tty is re-opened.
If the tty was opened as /dev/console or /dev/tty0, the original behavior
of re-instancing the ldisc is retained (the 'reinit' parameter to
tty_ldisc_hangup() is true). This is required since those file descriptors
are never hungup.
This patch has neglible impact on userspace; the tty file_operations ptr
is changed to point to the hungup file operations _before_ the ldisc
instance is destroyed, so only racing file operations might now retrieve
a NULL ldisc reference (which is simply handled as if the hungup file
operation had been called instead -- see "tty: Prepare for destroying
line discipline on hangup").
This resolves a long-standing FIXME and several crash reports.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-01-11 09:41:06 +03:00
|
|
|
if (tty->ldisc) {
|
|
|
|
if (reinit) {
|
|
|
|
if (tty_ldisc_reinit(tty, tty->termios.c_line) < 0)
|
|
|
|
tty_ldisc_reinit(tty, N_TTY);
|
|
|
|
} else
|
|
|
|
tty_ldisc_kill(tty);
|
2009-06-11 15:50:12 +04:00
|
|
|
}
|
2014-11-05 20:13:07 +03:00
|
|
|
tty_ldisc_unlock(tty);
|
2009-06-11 15:50:12 +04:00
|
|
|
}
|
2008-07-22 14:16:55 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_setup - open line discipline
|
|
|
|
* @tty: tty being shut down
|
|
|
|
* @o_tty: pair tty for pty/tty pairs
|
|
|
|
*
|
|
|
|
* Called during the initial open of a tty/pty pair in order to set up the
|
2009-06-11 15:50:12 +04:00
|
|
|
* line disciplines and bind them to the tty. This has no locking issues
|
|
|
|
* as the device isn't yet active.
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
|
|
|
|
{
|
2016-01-11 09:41:08 +03:00
|
|
|
int retval = tty_ldisc_open(tty, tty->ldisc);
|
2009-06-11 15:50:12 +04:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
if (o_tty) {
|
|
|
|
retval = tty_ldisc_open(o_tty, o_tty->ldisc);
|
2008-07-22 14:16:55 +04:00
|
|
|
if (retval) {
|
2016-01-11 09:41:08 +03:00
|
|
|
tty_ldisc_close(tty, tty->ldisc);
|
2008-07-22 14:16:55 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-08-08 19:30:13 +04:00
|
|
|
|
2008-07-22 14:16:55 +04:00
|
|
|
/**
|
|
|
|
* tty_ldisc_release - release line discipline
|
2014-11-05 20:12:58 +03:00
|
|
|
* @tty: tty being shut down (or one end of pty pair)
|
2014-11-05 20:12:44 +03:00
|
|
|
*
|
2014-11-05 20:12:58 +03:00
|
|
|
* Called during the final close of a tty or a pty pair in order to shut
|
2016-01-11 09:41:00 +03:00
|
|
|
* down the line discpline layer. On exit, each tty's ldisc is NULL.
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
2014-11-05 20:12:58 +03:00
|
|
|
void tty_ldisc_release(struct tty_struct *tty)
|
2008-07-22 14:16:55 +04:00
|
|
|
{
|
2014-11-05 20:12:58 +03:00
|
|
|
struct tty_struct *o_tty = tty->link;
|
|
|
|
|
2008-07-22 14:16:55 +04:00
|
|
|
/*
|
2013-03-12 00:44:35 +04:00
|
|
|
* Shutdown this line discipline. As this is the final close,
|
|
|
|
* it does not race with the set_ldisc code path.
|
2008-07-22 14:16:55 +04:00
|
|
|
*/
|
|
|
|
|
2013-06-15 15:04:48 +04:00
|
|
|
tty_ldisc_lock_pair(tty, o_tty);
|
2012-08-08 19:30:13 +04:00
|
|
|
tty_ldisc_kill(tty);
|
2009-06-11 15:50:12 +04:00
|
|
|
if (o_tty)
|
2012-08-08 19:30:13 +04:00
|
|
|
tty_ldisc_kill(o_tty);
|
2013-06-15 15:04:48 +04:00
|
|
|
tty_ldisc_unlock_pair(tty, o_tty);
|
|
|
|
|
2009-06-29 18:21:47 +04:00
|
|
|
/* And the memory resources remaining (buffers, termios) will be
|
|
|
|
disposed of when the kref hits zero */
|
2013-03-12 00:44:38 +04:00
|
|
|
|
2015-07-13 05:49:12 +03:00
|
|
|
tty_ldisc_debug(tty, "released\n");
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tty_ldisc_init - ldisc setup for new tty
|
|
|
|
* @tty: tty being allocated
|
|
|
|
*
|
|
|
|
* Set up the line discipline objects for a newly allocated tty. Note that
|
|
|
|
* the tty structure is not completely set up when this call is made.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void tty_ldisc_init(struct tty_struct *tty)
|
|
|
|
{
|
2013-06-15 15:04:48 +04:00
|
|
|
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
|
2009-06-11 15:50:12 +04:00
|
|
|
if (IS_ERR(ld))
|
2008-07-22 14:16:55 +04:00
|
|
|
panic("n_tty: init_tty");
|
2013-03-12 00:44:42 +04:00
|
|
|
tty->ldisc = ld;
|
2008-07-22 14:16:55 +04:00
|
|
|
}
|
|
|
|
|
2011-03-23 12:48:35 +03:00
|
|
|
/**
|
2016-01-10 08:13:46 +03:00
|
|
|
* tty_ldisc_deinit - ldisc cleanup for new tty
|
2011-03-23 12:48:35 +03:00
|
|
|
* @tty: tty that was allocated recently
|
|
|
|
*
|
|
|
|
* The tty structure must not becompletely set up (tty_ldisc_setup) when
|
|
|
|
* this call is made.
|
|
|
|
*/
|
|
|
|
void tty_ldisc_deinit(struct tty_struct *tty)
|
|
|
|
{
|
2016-01-10 08:13:46 +03:00
|
|
|
if (tty->ldisc)
|
|
|
|
tty_ldisc_put(tty->ldisc);
|
2013-03-12 00:44:42 +04:00
|
|
|
tty->ldisc = NULL;
|
2011-03-23 12:48:35 +03:00
|
|
|
}
|