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 )
{
2017-03-04 15:46:12 +03:00
struct tty_ldisc * ld ;
2013-06-15 15:04:48 +04:00
ldsem_down_read ( & tty - > ldisc_sem , MAX_SCHEDULE_TIMEOUT ) ;
2017-03-04 15:46:12 +03:00
ld = tty - > ldisc ;
if ( ! ld )
2016-01-11 09:41:02 +03:00
ldsem_up_read ( & tty - > ldisc_sem ) ;
2017-03-04 15:46:12 +03:00
return ld ;
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
2017-04-14 11:57:52 +03: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 )
{
struct tty_ldisc * new_ldisc ;
int r ;
/* There is an outstanding reference here so this is safe */
old = tty_ldisc_get ( tty , old - > ops - > num ) ;
WARN_ON ( IS_ERR ( old ) ) ;
tty - > ldisc = old ;
tty_set_termios_ldisc ( tty , old - > ops - > num ) ;
if ( tty_ldisc_open ( tty , old ) < 0 ) {
tty_ldisc_put ( old ) ;
/* This driver is always present */
new_ldisc = tty_ldisc_get ( tty , N_TTY ) ;
if ( IS_ERR ( new_ldisc ) )
panic ( " n_tty: get " ) ;
tty - > ldisc = new_ldisc ;
tty_set_termios_ldisc ( tty , N_TTY ) ;
r = tty_ldisc_open ( tty , new_ldisc ) ;
if ( r < 0 )
panic ( " Couldn't open N_TTY ldisc for "
" %s --- error %d. " ,
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
{
2017-04-14 11:57:52 +03:00
int retval ;
struct tty_ldisc * old_ldisc , * new_ldisc ;
new_ldisc = tty_ldisc_get ( tty , disc ) ;
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 */
2017-04-14 11:57:52 +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
}
2017-04-14 11:57:52 +03:00
old_ldisc = tty - > ldisc ;
/* Shutdown the old discipline. */
tty_ldisc_close ( tty , old_ldisc ) ;
/* Now set up the new line discipline. */
tty - > ldisc = new_ldisc ;
tty_set_termios_ldisc ( tty , disc ) ;
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 */
2017-04-14 11:57:52 +03:00
tty_ldisc_put ( new_ldisc ) ;
tty_ldisc_restore ( tty , old_ldisc ) ;
2008-07-22 14:16:55 +04:00
}
2009-06-11 15:50:12 +04:00
2017-04-14 11:57:52 +03:00
if ( tty - > ldisc - > ops - > num ! = old_ldisc - > ops - > num & & tty - > ops - > set_ldisc ) {
2014-11-05 21:11:41 +03:00
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
2017-04-14 11:57:52 +03: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 . */
new_ldisc = old_ldisc ;
2015-11-08 17:29:38 +03:00
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 :
2017-04-14 11:57:52 +03:00
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 ) ;
2017-04-14 11:57:52 +03:00
if ( IS_ERR ( ld ) ) {
BUG_ON ( disc = = N_TTY ) ;
2016-01-11 09:41:05 +03:00
return PTR_ERR ( ld ) ;
2017-04-14 11:57:52 +03:00
}
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 ) {
2017-04-14 11:57:52 +03:00
if ( ! WARN_ON ( disc = = N_TTY ) ) {
tty_ldisc_put ( tty - > ldisc ) ;
tty - > ldisc = NULL ;
}
2016-01-11 09:41:05 +03:00
}
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
}