2008-10-13 13:37:07 +04:00
/*
* Tty port functions
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
2009-01-02 16:45:26 +03:00
# include <linux/serial.h>
2008-10-13 13:37:07 +04:00
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/sched.h>
# include <linux/wait.h>
# include <linux/bitops.h>
# include <linux/delay.h>
# include <linux/module.h>
void tty_port_init ( struct tty_port * port )
{
memset ( port , 0 , sizeof ( * port ) ) ;
2012-10-19 00:26:47 +04:00
tty_buffer_init ( port ) ;
2008-10-13 13:37:07 +04:00
init_waitqueue_head ( & port - > open_wait ) ;
init_waitqueue_head ( & port - > close_wait ) ;
2009-09-20 00:13:31 +04:00
init_waitqueue_head ( & port - > delta_msr_wait ) ;
2008-10-13 13:37:07 +04:00
mutex_init ( & port - > mutex ) ;
2009-11-30 16:16:41 +03:00
mutex_init ( & port - > buf_mutex ) ;
2008-10-13 13:39:46 +04:00
spin_lock_init ( & port - > lock ) ;
2008-10-13 13:37:07 +04:00
port - > close_delay = ( 50 * HZ ) / 100 ;
port - > closing_wait = ( 3000 * HZ ) / 100 ;
2009-11-30 16:17:14 +03:00
kref_init ( & port - > kref ) ;
2008-10-13 13:37:07 +04:00
}
EXPORT_SYMBOL ( tty_port_init ) ;
2012-08-07 23:47:50 +04:00
/**
* tty_port_link_device - link tty and tty_port
* @ port : tty_port of the device
* @ driver : tty_driver for this device
* @ index : index of the tty
*
* Provide the tty layer wit ha link from a tty ( specified by @ index ) to a
* tty_port ( @ port ) . Use this only if neither tty_port_register_device nor
* tty_port_install is used in the driver . If used , this has to be called before
* tty_register_driver .
*/
void tty_port_link_device ( struct tty_port * port ,
struct tty_driver * driver , unsigned index )
{
if ( WARN_ON ( index > = driver - > num ) )
return ;
driver - > ports [ index ] = port ;
}
EXPORT_SYMBOL_GPL ( tty_port_link_device ) ;
2012-08-07 23:47:49 +04:00
/**
* tty_port_register_device - register tty device
* @ port : tty_port of the device
* @ driver : tty_driver for this device
* @ index : index of the tty
* @ device : parent if exists , otherwise NULL
*
* It is the same as tty_register_device except the provided @ port is linked to
* a concrete tty specified by @ index . Use this or tty_port_install ( or both ) .
* Call tty_port_link_device as a last resort .
*/
2012-06-04 15:35:37 +04:00
struct device * tty_port_register_device ( struct tty_port * port ,
struct tty_driver * driver , unsigned index ,
struct device * device )
{
2012-08-07 23:47:50 +04:00
tty_port_link_device ( port , driver , index ) ;
2012-06-04 15:35:37 +04:00
return tty_register_device ( driver , index , device ) ;
}
EXPORT_SYMBOL_GPL ( tty_port_register_device ) ;
2012-09-07 01:17:47 +04:00
/**
* tty_port_register_device_attr - register tty device
* @ port : tty_port of the device
* @ driver : tty_driver for this device
* @ index : index of the tty
* @ device : parent if exists , otherwise NULL
* @ drvdata : Driver data to be set to device .
* @ attr_grp : Attribute group to be set on device .
*
* It is the same as tty_register_device_attr except the provided @ port is
* linked to a concrete tty specified by @ index . Use this or tty_port_install
* ( or both ) . Call tty_port_link_device as a last resort .
*/
struct device * tty_port_register_device_attr ( struct tty_port * port ,
struct tty_driver * driver , unsigned index ,
struct device * device , void * drvdata ,
const struct attribute_group * * attr_grp )
{
tty_port_link_device ( port , driver , index ) ;
return tty_register_device_attr ( driver , index , device , drvdata ,
attr_grp ) ;
}
EXPORT_SYMBOL_GPL ( tty_port_register_device_attr ) ;
2008-10-13 13:37:07 +04:00
int tty_port_alloc_xmit_buf ( struct tty_port * port )
{
/* We may sleep in get_zeroed_page() */
2009-11-30 16:16:41 +03:00
mutex_lock ( & port - > buf_mutex ) ;
2008-10-13 13:37:07 +04:00
if ( port - > xmit_buf = = NULL )
port - > xmit_buf = ( unsigned char * ) get_zeroed_page ( GFP_KERNEL ) ;
2009-11-30 16:16:41 +03:00
mutex_unlock ( & port - > buf_mutex ) ;
2008-10-13 13:37:07 +04:00
if ( port - > xmit_buf = = NULL )
return - ENOMEM ;
return 0 ;
}
EXPORT_SYMBOL ( tty_port_alloc_xmit_buf ) ;
void tty_port_free_xmit_buf ( struct tty_port * port )
{
2009-11-30 16:16:41 +03:00
mutex_lock ( & port - > buf_mutex ) ;
2008-10-13 13:37:07 +04:00
if ( port - > xmit_buf ! = NULL ) {
free_page ( ( unsigned long ) port - > xmit_buf ) ;
port - > xmit_buf = NULL ;
}
2009-11-30 16:16:41 +03:00
mutex_unlock ( & port - > buf_mutex ) ;
2008-10-13 13:37:07 +04:00
}
EXPORT_SYMBOL ( tty_port_free_xmit_buf ) ;
2012-11-15 12:49:54 +04:00
/**
* tty_port_destroy - - destroy inited port
* @ port : tty port to be doestroyed
*
* When a port was initialized using tty_port_init , one has to destroy the
* port by this function . Either indirectly by using tty_port refcounting
* ( tty_port_put ) or directly if refcounting is not used .
*/
void tty_port_destroy ( struct tty_port * port )
{
2013-03-12 00:44:34 +04:00
cancel_work_sync ( & port - > buf . work ) ;
2012-11-15 12:49:54 +04:00
tty_buffer_free_all ( port ) ;
}
EXPORT_SYMBOL ( tty_port_destroy ) ;
2009-11-30 16:17:14 +03:00
static void tty_port_destructor ( struct kref * kref )
{
struct tty_port * port = container_of ( kref , struct tty_port , kref ) ;
2013-09-19 04:42:39 +04:00
/* check if last port ref was dropped before tty release */
if ( WARN_ON ( port - > itty ) )
return ;
2009-11-30 16:17:14 +03:00
if ( port - > xmit_buf )
free_page ( ( unsigned long ) port - > xmit_buf ) ;
2012-11-15 12:49:54 +04:00
tty_port_destroy ( port ) ;
2012-11-15 12:49:49 +04:00
if ( port - > ops & & port - > ops - > destruct )
2009-11-30 16:17:14 +03:00
port - > ops - > destruct ( port ) ;
else
kfree ( port ) ;
}
void tty_port_put ( struct tty_port * port )
{
if ( port )
kref_put ( & port - > kref , tty_port_destructor ) ;
}
EXPORT_SYMBOL ( tty_port_put ) ;
2008-10-13 13:37:07 +04:00
2008-10-13 13:39:46 +04:00
/**
* tty_port_tty_get - get a tty reference
* @ port : tty port
*
* Return a refcount protected tty instance or NULL if the port is not
* associated with a tty ( eg due to close or hangup )
*/
struct tty_struct * tty_port_tty_get ( struct tty_port * port )
{
unsigned long flags ;
struct tty_struct * tty ;
spin_lock_irqsave ( & port - > lock , flags ) ;
tty = tty_kref_get ( port - > tty ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return tty ;
}
EXPORT_SYMBOL ( tty_port_tty_get ) ;
/**
* tty_port_tty_set - set the tty of a port
* @ port : tty port
* @ tty : the tty
*
* Associate the port and tty pair . Manages any internal refcounts .
* Pass NULL to deassociate a port
*/
void tty_port_tty_set ( struct tty_port * port , struct tty_struct * tty )
{
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
2014-11-21 15:42:29 +03:00
tty_kref_put ( port - > tty ) ;
2008-10-21 16:47:44 +04:00
port - > tty = tty_kref_get ( tty ) ;
2008-10-13 13:39:46 +04:00
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
EXPORT_SYMBOL ( tty_port_tty_set ) ;
2009-01-02 16:45:05 +03:00
2013-03-07 18:55:51 +04:00
static void tty_port_shutdown ( struct tty_port * port , struct tty_struct * tty )
2009-09-20 00:13:20 +04:00
{
2009-10-06 19:06:11 +04:00
mutex_lock ( & port - > mutex ) ;
2013-03-07 18:55:48 +04:00
if ( port - > console )
goto out ;
if ( test_and_clear_bit ( ASYNCB_INITIALIZED , & port - > flags ) ) {
2013-03-07 18:55:51 +04:00
/*
* Drop DTR / RTS if HUPCL is set . This causes any attached
* modem to hang up the line .
*/
if ( tty & & C_HUPCL ( tty ) )
tty_port_lower_dtr_rts ( port ) ;
2013-03-07 18:55:48 +04:00
if ( port - > ops - > shutdown )
2009-09-20 00:13:20 +04:00
port - > ops - > shutdown ( port ) ;
2013-03-07 18:55:48 +04:00
}
out :
2009-10-06 19:06:11 +04:00
mutex_unlock ( & port - > mutex ) ;
2009-09-20 00:13:20 +04:00
}
2009-01-02 16:45:26 +03:00
/**
* tty_port_hangup - hangup helper
* @ port : tty port
*
* Perform port level tty hangup flag and count changes . Drop the tty
* reference .
2014-06-16 17:17:02 +04:00
*
* Caller holds tty lock .
2009-01-02 16:45:26 +03:00
*/
void tty_port_hangup ( struct tty_port * port )
{
2013-03-07 18:55:51 +04:00
struct tty_struct * tty ;
2009-01-02 16:45:26 +03:00
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
port - > count = 0 ;
port - > flags & = ~ ASYNC_NORMAL_ACTIVE ;
2013-03-07 18:55:51 +04:00
tty = port - > tty ;
if ( tty )
set_bit ( TTY_IO_ERROR , & tty - > flags ) ;
2009-01-02 16:45:26 +03:00
port - > tty = NULL ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2013-03-07 18:55:51 +04:00
tty_port_shutdown ( port , tty ) ;
tty_kref_put ( tty ) ;
2009-01-02 16:45:26 +03:00
wake_up_interruptible ( & port - > open_wait ) ;
2009-09-20 00:13:31 +04:00
wake_up_interruptible ( & port - > delta_msr_wait ) ;
2009-01-02 16:45:26 +03:00
}
EXPORT_SYMBOL ( tty_port_hangup ) ;
2013-03-07 16:12:30 +04:00
/**
* tty_port_tty_hangup - helper to hang up a tty
*
* @ port : tty port
* @ check_clocal : hang only ttys with CLOCAL unset ?
*/
void tty_port_tty_hangup ( struct tty_port * port , bool check_clocal )
{
struct tty_struct * tty = tty_port_tty_get ( port ) ;
2013-07-25 09:26:16 +04:00
if ( tty & & ( ! check_clocal | | ! C_CLOCAL ( tty ) ) )
2013-03-07 16:12:30 +04:00
tty_hangup ( tty ) ;
2013-07-25 09:26:16 +04:00
tty_kref_put ( tty ) ;
2013-03-07 16:12:30 +04:00
}
EXPORT_SYMBOL_GPL ( tty_port_tty_hangup ) ;
2013-03-07 16:12:29 +04:00
/**
* tty_port_tty_wakeup - helper to wake up a tty
*
* @ port : tty port
*/
void tty_port_tty_wakeup ( struct tty_port * port )
{
struct tty_struct * tty = tty_port_tty_get ( port ) ;
if ( tty ) {
tty_wakeup ( tty ) ;
tty_kref_put ( tty ) ;
}
}
EXPORT_SYMBOL_GPL ( tty_port_tty_wakeup ) ;
2009-01-02 16:45:05 +03:00
/**
* tty_port_carrier_raised - carrier raised check
* @ port : tty port
*
* Wrapper for the carrier detect logic . For the moment this is used
* to hide some internal details . This will eventually become entirely
* internal to the tty port .
*/
int tty_port_carrier_raised ( struct tty_port * port )
{
if ( port - > ops - > carrier_raised = = NULL )
return 1 ;
return port - > ops - > carrier_raised ( port ) ;
}
EXPORT_SYMBOL ( tty_port_carrier_raised ) ;
2009-01-02 16:45:19 +03:00
/**
2009-06-11 15:24:17 +04:00
* tty_port_raise_dtr_rts - Raise DTR / RTS
2009-01-02 16:45:19 +03:00
* @ port : tty port
*
* Wrapper for the DTR / RTS raise logic . For the moment this is used
* to hide some internal details . This will eventually become entirely
* internal to the tty port .
*/
void tty_port_raise_dtr_rts ( struct tty_port * port )
{
2009-06-11 15:24:17 +04:00
if ( port - > ops - > dtr_rts )
port - > ops - > dtr_rts ( port , 1 ) ;
2009-01-02 16:45:19 +03:00
}
EXPORT_SYMBOL ( tty_port_raise_dtr_rts ) ;
2009-01-02 16:46:10 +03:00
2009-06-11 15:24:17 +04:00
/**
* tty_port_lower_dtr_rts - Lower DTR / RTS
* @ port : tty port
*
* Wrapper for the DTR / RTS raise logic . For the moment this is used
* to hide some internal details . This will eventually become entirely
* internal to the tty port .
*/
void tty_port_lower_dtr_rts ( struct tty_port * port )
{
if ( port - > ops - > dtr_rts )
port - > ops - > dtr_rts ( port , 0 ) ;
}
EXPORT_SYMBOL ( tty_port_lower_dtr_rts ) ;
2009-01-02 16:46:10 +03:00
/**
* tty_port_block_til_ready - Waiting logic for tty open
* @ port : the tty port being opened
* @ tty : the tty device being bound
* @ filp : the file pointer of the opener
*
* Implement the core POSIX / SuS tty behaviour when opening a tty device .
* Handles :
* - hangup ( both before and during )
* - non blocking open
* - rts / dtr / dcd
* - signals
* - port flags and counts
*
* The passed tty_port must implement the carrier_raised method if it can
2009-06-11 15:24:17 +04:00
* do carrier detect and the dtr_rts method if it supports software
2009-01-02 16:46:10 +03:00
* management of these lines . Note that the dtr / rts raise is done each
* iteration as a hangup may have previously dropped them while we wait .
2014-06-16 17:17:01 +04:00
*
* Caller holds tty lock .
*
* NB : May drop and reacquire tty lock when blocking , so tty and tty_port
* may have changed state ( eg . , may have been hung up ) .
2009-01-02 16:46:10 +03:00
*/
2009-10-06 19:06:21 +04:00
2009-01-02 16:46:10 +03:00
int tty_port_block_til_ready ( struct tty_port * port ,
struct tty_struct * tty , struct file * filp )
{
int do_clocal = 0 , retval ;
unsigned long flags ;
2009-06-24 21:35:05 +04:00
DEFINE_WAIT ( wait ) ;
2009-01-02 16:46:10 +03:00
/* block if port is in the process of being closed */
tty: Remove tty_hung_up_p() tests from tty drivers' open()
Since at least before 2.6.30, it has not been possible to observe
a hung up file pointer in a tty driver's open() method unless/until
the driver open() releases the tty_lock() (eg., before blocking).
This is because tty_open() adds the file pointer while holding
the tty_lock() _and_ doesn't release the lock until after calling
the tty driver's open() method. [ Before tty_lock(), this was
lock_kernel(). ]
Since __tty_hangup() first waits on the tty_lock() before
enumerating and hanging up the open file pointers, either
__tty_hangup() will wait for the tty_lock() or tty_open() will
not yet have added the file pointer. For example,
CPU 0 | CPU 1
|
tty_open | __tty_hangup
.. | ..
tty_lock | ..
tty_reopen | tty_lock / blocks
.. |
tty_add_file(tty, filp) |
.. |
tty->ops->open(tty, filp) |
tty_port_open |
tty_port_block_til_ready |
.. |
while (1) |
.. |
tty_unlock | / unblocks
schedule | for each filp on tty->tty_files
| f_ops = tty_hung_up_fops;
| ..
| tty_unlock
tty_lock |
.. |
tty_unlock |
Note that since tty_port_block_til_ready() and similar drop
the tty_lock while blocking, when woken, the file pointer
must then be tested for having been hung up.
Also, fix bit-rotted drivers that used extra_count to track the
port->count bump.
CC: Mikael Starvik <starvik@axis.com>
CC: Samuel Ortiz <samuel@sortiz.org>
CC: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-06-16 17:17:06 +04:00
if ( port - > flags & ASYNC_CLOSING ) {
2012-08-08 19:30:13 +04:00
wait_event_interruptible_tty ( tty , port - > close_wait ,
2009-06-11 17:32:42 +04:00
! ( port - > flags & ASYNC_CLOSING ) ) ;
2009-01-02 16:46:10 +03:00
if ( port - > flags & ASYNC_HUP_NOTIFY )
return - EAGAIN ;
else
return - ERESTARTSYS ;
}
/* if non-blocking mode is set we can pass directly to open unless
the port has just hung up or is in another error state */
2009-11-18 17:12:58 +03:00
if ( tty - > flags & ( 1 < < TTY_IO_ERROR ) ) {
port - > flags | = ASYNC_NORMAL_ACTIVE ;
return 0 ;
}
if ( filp - > f_flags & O_NONBLOCK ) {
2009-10-28 23:12:32 +03:00
/* Indicate we are open */
2012-07-14 18:31:47 +04:00
if ( tty - > termios . c_cflag & CBAUD )
2009-10-28 23:12:32 +03:00
tty_port_raise_dtr_rts ( port ) ;
2009-01-02 16:46:10 +03:00
port - > flags | = ASYNC_NORMAL_ACTIVE ;
return 0 ;
}
if ( C_CLOCAL ( tty ) )
do_clocal = 1 ;
/* Block waiting until we can proceed. We may need to wait for the
carrier , but we must also wait for any close that is in progress
before the next open may complete */
retval = 0 ;
/* The port lock protects the port counts */
spin_lock_irqsave ( & port - > lock , flags ) ;
tty: Remove tty_hung_up_p() tests from tty drivers' open()
Since at least before 2.6.30, it has not been possible to observe
a hung up file pointer in a tty driver's open() method unless/until
the driver open() releases the tty_lock() (eg., before blocking).
This is because tty_open() adds the file pointer while holding
the tty_lock() _and_ doesn't release the lock until after calling
the tty driver's open() method. [ Before tty_lock(), this was
lock_kernel(). ]
Since __tty_hangup() first waits on the tty_lock() before
enumerating and hanging up the open file pointers, either
__tty_hangup() will wait for the tty_lock() or tty_open() will
not yet have added the file pointer. For example,
CPU 0 | CPU 1
|
tty_open | __tty_hangup
.. | ..
tty_lock | ..
tty_reopen | tty_lock / blocks
.. |
tty_add_file(tty, filp) |
.. |
tty->ops->open(tty, filp) |
tty_port_open |
tty_port_block_til_ready |
.. |
while (1) |
.. |
tty_unlock | / unblocks
schedule | for each filp on tty->tty_files
| f_ops = tty_hung_up_fops;
| ..
| tty_unlock
tty_lock |
.. |
tty_unlock |
Note that since tty_port_block_til_ready() and similar drop
the tty_lock while blocking, when woken, the file pointer
must then be tested for having been hung up.
Also, fix bit-rotted drivers that used extra_count to track the
port->count bump.
CC: Mikael Starvik <starvik@axis.com>
CC: Samuel Ortiz <samuel@sortiz.org>
CC: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-06-16 17:17:06 +04:00
port - > count - - ;
2009-01-02 16:46:10 +03:00
port - > blocked_open + + ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
while ( 1 ) {
/* Indicate we are open */
2013-03-07 18:55:50 +04:00
if ( C_BAUD ( tty ) & & test_bit ( ASYNCB_INITIALIZED , & port - > flags ) )
2009-01-02 16:46:43 +03:00
tty_port_raise_dtr_rts ( port ) ;
2009-01-02 16:46:10 +03:00
2009-06-11 17:33:37 +04:00
prepare_to_wait ( & port - > open_wait , & wait , TASK_INTERRUPTIBLE ) ;
2009-10-06 19:06:21 +04:00
/* Check for a hangup or uninitialised port.
Return accordingly */
2009-01-02 16:46:10 +03:00
if ( tty_hung_up_p ( filp ) | | ! ( port - > flags & ASYNC_INITIALIZED ) ) {
if ( port - > flags & ASYNC_HUP_NOTIFY )
retval = - EAGAIN ;
else
retval = - ERESTARTSYS ;
break ;
}
2012-01-13 01:55:15 +04:00
/*
* Probe the carrier . For devices with no carrier detect
* tty_port_carrier_raised will always return true .
* Never ask drivers if CLOCAL is set , this causes troubles
* on some hardware .
*/
2009-01-02 16:46:10 +03:00
if ( ! ( port - > flags & ASYNC_CLOSING ) & &
2012-01-13 01:55:15 +04:00
( do_clocal | | tty_port_carrier_raised ( port ) ) )
2009-01-02 16:46:10 +03:00
break ;
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
break ;
}
2012-08-08 19:30:13 +04:00
tty_unlock ( tty ) ;
2009-01-02 16:46:10 +03:00
schedule ( ) ;
2012-08-08 19:30:13 +04:00
tty_lock ( tty ) ;
2009-01-02 16:46:10 +03:00
}
2009-06-11 17:33:37 +04:00
finish_wait ( & port - > open_wait , & wait ) ;
2009-01-02 16:46:10 +03:00
/* Update counts. A parallel hangup will have set count to zero and
we must not mess that up further */
spin_lock_irqsave ( & port - > lock , flags ) ;
if ( ! tty_hung_up_p ( filp ) )
port - > count + + ;
port - > blocked_open - - ;
if ( retval = = 0 )
port - > flags | = ASYNC_NORMAL_ACTIVE ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2009-07-17 19:17:26 +04:00
return retval ;
2009-01-02 16:46:10 +03:00
}
EXPORT_SYMBOL ( tty_port_block_til_ready ) ;
2013-03-07 18:55:52 +04:00
static void tty_port_drain_delay ( struct tty_port * port , struct tty_struct * tty )
{
unsigned int bps = tty_get_baud_rate ( tty ) ;
long timeout ;
if ( bps > 1200 ) {
timeout = ( HZ * 10 * port - > drain_delay ) / bps ;
timeout = max_t ( long , timeout , HZ / 10 ) ;
} else {
timeout = 2 * HZ ;
}
schedule_timeout_interruptible ( timeout ) ;
}
tty: Document locking for tty_port_close{,start,end}()
The tty lock is held when the tty driver's .close method is called
(from the two lone call-sites of tty_release() and __tty_hangup()).
The call-tree audit[1] of tty_port_close(), tty_port_close_start,
and tty_port_close_end() is a closed graph of the callers of these
3 functions; ie., all callers originate from only tty_release()
or __tty_hangup().
Of these callers, none drop the tty lock.
Also, document tty_port_close_start() may drop and reacquire the
tty lock in tty_wait_until_sent_from_close(), which means the tty
or tty_port may have changed state (but not reopened or hung up).
[1]
Call-tree audit of tty_port_close, tty_port_close_start, and tty_port_close_end()
tty_release()
tty->ops->close() --+
|
__tty_hangup() |
tty->ops->close() --+
|
+- rp_close():drivers/tty/rocket.c -------------------+
+- uart_close():drivers/tty/serial/serial_core.c -----+
| +- tty_port_close_start()
|
|
+- close():drivers/tty/synclinkmp.c ------------------+
+- rs_close():drivers/tty/amiserial.c ----------------+
+- gsmtty_close():drivers/tty/n_gsm.c ----------------+
+- mxser_close():drivers/tty/mxser.c -----------------+
+- close():drivers/tty/synclink_gt.c -----------------+
+- mgsl_close():drivers/tty/synclink.c ---------------+
+- isdn_tty_close():drivers/isdn/i4l/isdn_tty.c ------+
+- mgslpc_close():drivers/char/pcmcia/synclink_cs.c --+
+- ircomm_tty_close():net/irda/ircomm/ircomm_tty.c ---+
| |
rs_close():arch/ia64/hp/sim/simserial.c |
*line_close():arch/um/drivers/line.c |
gdm_tty_close():drivers/staging/gdm724x/gdm_tty.c
fwtty_close():drivers/staging/fwserial/fwserial.c
acm_tty_close():drivers/usb/class/cdc-acm.c
serial_close():drivers/usb/serial/usb-serial.c
pti_tty_driver_close():drivers/misc/pti.c
ipoctal_close():drivers/ipack/devices/ipoctal.c
cy_close():drivers/tty/cyclades.c
isicom_close():drivers/tty/isicom.c
dashtty_close():drivers/tty/metag_da.c
moxa_close():drivers/tty/moxa.c
goldfish_tty_close():drivers/tty/goldfish.c
ehv_bc_tty_close():drivers/tty/ehv_bytechan.c
kgdb_nmi_tty_close():drivers/tty/serial/kgdb_nmi.c
ifx_spi_close():drivers/tty/serial/ifx6x60.c
smd_tty_close():drivers/tty/serial/msm_smd_tty.c
ntty_close():drivers/tty/nozomi.c
capinc_tty_close():drivers/isdn/capi/capi.c
tpk_close():drivers/char/ttyprintk.c
sdio_uart_close():drivers/mmc/card/sdio_uart.c |
rfcomm_tty_close():net/bluetooth/rfcomm/tty.c |
| |
+- tty_port_close():drivers/tty/tty_port.c -----------+
|
+- tty_port_close_start()
+- tty_port_close_end()
* line_close() is the .close method for 2 um drivers,
declared in ./arch/um/drivers/stdio_console.c and
in ./arch/um/drivers/ssl.c, and not called directly
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-06-16 17:16:59 +04:00
/* Caller holds tty lock.
* NB : may drop and reacquire tty lock ( in tty_wait_until_sent_from_close ( ) )
* so tty and tty port may have changed state ( but not hung up or reopened ) .
*/
2009-10-06 19:06:21 +04:00
int tty_port_close_start ( struct tty_port * port ,
struct tty_struct * tty , struct file * filp )
2009-01-02 16:46:50 +03:00
{
unsigned long flags ;
2014-11-05 20:40:03 +03:00
if ( tty_hung_up_p ( filp ) )
2009-01-02 16:46:50 +03:00
return 0 ;
2014-11-05 20:40:03 +03:00
spin_lock_irqsave ( & port - > lock , flags ) ;
2009-10-06 19:06:21 +04:00
if ( tty - > count = = 1 & & port - > count ! = 1 ) {
2009-01-02 16:46:50 +03:00
printk ( KERN_WARNING
" tty_port_close_start: tty->count = 1 port count = %d. \n " ,
port - > count ) ;
port - > count = 1 ;
}
if ( - - port - > count < 0 ) {
printk ( KERN_WARNING " tty_port_close_start: count = %d \n " ,
port - > count ) ;
port - > count = 0 ;
}
if ( port - > count ) {
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return 0 ;
}
2009-08-20 23:23:47 +04:00
set_bit ( ASYNCB_CLOSING , & port - > flags ) ;
2009-01-02 16:46:50 +03:00
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2013-03-07 18:55:53 +04:00
2014-06-16 17:17:03 +04:00
tty - > closing = 1 ;
2013-03-07 18:55:53 +04:00
if ( test_bit ( ASYNCB_INITIALIZED , & port - > flags ) ) {
/* Don't block on a stalled port, just pull the chain */
if ( tty - > flow_stopped )
tty_driver_flush_buffer ( tty ) ;
if ( port - > closing_wait ! = ASYNC_CLOSING_WAIT_NONE )
tty_wait_until_sent_from_close ( tty , port - > closing_wait ) ;
if ( port - > drain_delay )
tty_port_drain_delay ( port , tty ) ;
}
2009-11-05 16:27:57 +03:00
/* Flush the ldisc buffering */
tty_ldisc_flush ( tty ) ;
2013-09-19 04:47:06 +04:00
/* Report to caller this is the last port reference */
2009-01-02 16:46:50 +03:00
return 1 ;
}
EXPORT_SYMBOL ( tty_port_close_start ) ;
tty: Document locking for tty_port_close{,start,end}()
The tty lock is held when the tty driver's .close method is called
(from the two lone call-sites of tty_release() and __tty_hangup()).
The call-tree audit[1] of tty_port_close(), tty_port_close_start,
and tty_port_close_end() is a closed graph of the callers of these
3 functions; ie., all callers originate from only tty_release()
or __tty_hangup().
Of these callers, none drop the tty lock.
Also, document tty_port_close_start() may drop and reacquire the
tty lock in tty_wait_until_sent_from_close(), which means the tty
or tty_port may have changed state (but not reopened or hung up).
[1]
Call-tree audit of tty_port_close, tty_port_close_start, and tty_port_close_end()
tty_release()
tty->ops->close() --+
|
__tty_hangup() |
tty->ops->close() --+
|
+- rp_close():drivers/tty/rocket.c -------------------+
+- uart_close():drivers/tty/serial/serial_core.c -----+
| +- tty_port_close_start()
|
|
+- close():drivers/tty/synclinkmp.c ------------------+
+- rs_close():drivers/tty/amiserial.c ----------------+
+- gsmtty_close():drivers/tty/n_gsm.c ----------------+
+- mxser_close():drivers/tty/mxser.c -----------------+
+- close():drivers/tty/synclink_gt.c -----------------+
+- mgsl_close():drivers/tty/synclink.c ---------------+
+- isdn_tty_close():drivers/isdn/i4l/isdn_tty.c ------+
+- mgslpc_close():drivers/char/pcmcia/synclink_cs.c --+
+- ircomm_tty_close():net/irda/ircomm/ircomm_tty.c ---+
| |
rs_close():arch/ia64/hp/sim/simserial.c |
*line_close():arch/um/drivers/line.c |
gdm_tty_close():drivers/staging/gdm724x/gdm_tty.c
fwtty_close():drivers/staging/fwserial/fwserial.c
acm_tty_close():drivers/usb/class/cdc-acm.c
serial_close():drivers/usb/serial/usb-serial.c
pti_tty_driver_close():drivers/misc/pti.c
ipoctal_close():drivers/ipack/devices/ipoctal.c
cy_close():drivers/tty/cyclades.c
isicom_close():drivers/tty/isicom.c
dashtty_close():drivers/tty/metag_da.c
moxa_close():drivers/tty/moxa.c
goldfish_tty_close():drivers/tty/goldfish.c
ehv_bc_tty_close():drivers/tty/ehv_bytechan.c
kgdb_nmi_tty_close():drivers/tty/serial/kgdb_nmi.c
ifx_spi_close():drivers/tty/serial/ifx6x60.c
smd_tty_close():drivers/tty/serial/msm_smd_tty.c
ntty_close():drivers/tty/nozomi.c
capinc_tty_close():drivers/isdn/capi/capi.c
tpk_close():drivers/char/ttyprintk.c
sdio_uart_close():drivers/mmc/card/sdio_uart.c |
rfcomm_tty_close():net/bluetooth/rfcomm/tty.c |
| |
+- tty_port_close():drivers/tty/tty_port.c -----------+
|
+- tty_port_close_start()
+- tty_port_close_end()
* line_close() is the .close method for 2 um drivers,
declared in ./arch/um/drivers/stdio_console.c and
in ./arch/um/drivers/ssl.c, and not called directly
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-06-16 17:16:59 +04:00
/* Caller holds tty lock */
2009-01-02 16:46:50 +03:00
void tty_port_close_end ( struct tty_port * port , struct tty_struct * tty )
{
unsigned long flags ;
2014-11-05 20:40:05 +03:00
tty_ldisc_flush ( tty ) ;
2009-01-02 16:46:50 +03:00
tty - > closing = 0 ;
2014-06-16 17:17:03 +04:00
spin_lock_irqsave ( & port - > lock , flags ) ;
2009-01-02 16:46:50 +03:00
if ( port - > blocked_open ) {
spin_unlock_irqrestore ( & port - > lock , flags ) ;
if ( port - > close_delay ) {
msleep_interruptible (
jiffies_to_msecs ( port - > close_delay ) ) ;
}
spin_lock_irqsave ( & port - > lock , flags ) ;
wake_up_interruptible ( & port - > open_wait ) ;
}
port - > flags & = ~ ( ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING ) ;
wake_up_interruptible ( & port - > close_wait ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
EXPORT_SYMBOL ( tty_port_close_end ) ;
2009-09-20 00:13:20 +04:00
tty: Document locking for tty_port_close{,start,end}()
The tty lock is held when the tty driver's .close method is called
(from the two lone call-sites of tty_release() and __tty_hangup()).
The call-tree audit[1] of tty_port_close(), tty_port_close_start,
and tty_port_close_end() is a closed graph of the callers of these
3 functions; ie., all callers originate from only tty_release()
or __tty_hangup().
Of these callers, none drop the tty lock.
Also, document tty_port_close_start() may drop and reacquire the
tty lock in tty_wait_until_sent_from_close(), which means the tty
or tty_port may have changed state (but not reopened or hung up).
[1]
Call-tree audit of tty_port_close, tty_port_close_start, and tty_port_close_end()
tty_release()
tty->ops->close() --+
|
__tty_hangup() |
tty->ops->close() --+
|
+- rp_close():drivers/tty/rocket.c -------------------+
+- uart_close():drivers/tty/serial/serial_core.c -----+
| +- tty_port_close_start()
|
|
+- close():drivers/tty/synclinkmp.c ------------------+
+- rs_close():drivers/tty/amiserial.c ----------------+
+- gsmtty_close():drivers/tty/n_gsm.c ----------------+
+- mxser_close():drivers/tty/mxser.c -----------------+
+- close():drivers/tty/synclink_gt.c -----------------+
+- mgsl_close():drivers/tty/synclink.c ---------------+
+- isdn_tty_close():drivers/isdn/i4l/isdn_tty.c ------+
+- mgslpc_close():drivers/char/pcmcia/synclink_cs.c --+
+- ircomm_tty_close():net/irda/ircomm/ircomm_tty.c ---+
| |
rs_close():arch/ia64/hp/sim/simserial.c |
*line_close():arch/um/drivers/line.c |
gdm_tty_close():drivers/staging/gdm724x/gdm_tty.c
fwtty_close():drivers/staging/fwserial/fwserial.c
acm_tty_close():drivers/usb/class/cdc-acm.c
serial_close():drivers/usb/serial/usb-serial.c
pti_tty_driver_close():drivers/misc/pti.c
ipoctal_close():drivers/ipack/devices/ipoctal.c
cy_close():drivers/tty/cyclades.c
isicom_close():drivers/tty/isicom.c
dashtty_close():drivers/tty/metag_da.c
moxa_close():drivers/tty/moxa.c
goldfish_tty_close():drivers/tty/goldfish.c
ehv_bc_tty_close():drivers/tty/ehv_bytechan.c
kgdb_nmi_tty_close():drivers/tty/serial/kgdb_nmi.c
ifx_spi_close():drivers/tty/serial/ifx6x60.c
smd_tty_close():drivers/tty/serial/msm_smd_tty.c
ntty_close():drivers/tty/nozomi.c
capinc_tty_close():drivers/isdn/capi/capi.c
tpk_close():drivers/char/ttyprintk.c
sdio_uart_close():drivers/mmc/card/sdio_uart.c |
rfcomm_tty_close():net/bluetooth/rfcomm/tty.c |
| |
+- tty_port_close():drivers/tty/tty_port.c -----------+
|
+- tty_port_close_start()
+- tty_port_close_end()
* line_close() is the .close method for 2 um drivers,
declared in ./arch/um/drivers/stdio_console.c and
in ./arch/um/drivers/ssl.c, and not called directly
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-06-16 17:16:59 +04:00
/**
* tty_port_close
*
* Caller holds tty lock
*
* NB : may drop and reacquire tty lock ( in tty_port_close_start ( ) - >
* tty_wait_until_sent_from_close ( ) ) so tty and tty_port may have changed
* state ( but not hung up or reopened ) .
*/
2009-09-20 00:13:20 +04:00
void tty_port_close ( struct tty_port * port , struct tty_struct * tty ,
struct file * filp )
{
if ( tty_port_close_start ( port , tty , filp ) = = 0 )
return ;
2013-03-07 18:55:51 +04:00
tty_port_shutdown ( port , tty ) ;
2009-11-30 16:16:52 +03:00
set_bit ( TTY_IO_ERROR , & tty - > flags ) ;
2009-09-20 00:13:20 +04:00
tty_port_close_end ( port , tty ) ;
tty_port_tty_set ( port , NULL ) ;
}
EXPORT_SYMBOL ( tty_port_close ) ;
2009-10-06 19:06:11 +04:00
2012-08-07 23:47:49 +04:00
/**
* tty_port_install - generic tty - > ops - > install handler
* @ port : tty_port of the device
* @ driver : tty_driver for this device
* @ tty : tty to be installed
*
* It is the same as tty_standard_install except the provided @ port is linked
* to a concrete tty specified by @ tty . Use this or tty_port_register_device
* ( or both ) . Call tty_port_link_device as a last resort .
*/
2012-06-04 15:35:32 +04:00
int tty_port_install ( struct tty_port * port , struct tty_driver * driver ,
struct tty_struct * tty )
{
tty - > port = port ;
return tty_standard_install ( driver , tty ) ;
}
EXPORT_SYMBOL_GPL ( tty_port_install ) ;
2014-06-16 17:17:00 +04:00
/**
* tty_port_open
*
* Caller holds tty lock .
*
* NB : may drop and reacquire tty lock ( in tty_port_block_til_ready ( ) ) so
* tty and tty_port may have changed state ( eg . , may be hung up now )
*/
2009-10-06 19:06:11 +04:00
int tty_port_open ( struct tty_port * port , struct tty_struct * tty ,
2009-10-06 19:06:21 +04:00
struct file * filp )
2009-10-06 19:06:11 +04:00
{
spin_lock_irq ( & port - > lock ) ;
tty: Remove tty_hung_up_p() tests from tty drivers' open()
Since at least before 2.6.30, it has not been possible to observe
a hung up file pointer in a tty driver's open() method unless/until
the driver open() releases the tty_lock() (eg., before blocking).
This is because tty_open() adds the file pointer while holding
the tty_lock() _and_ doesn't release the lock until after calling
the tty driver's open() method. [ Before tty_lock(), this was
lock_kernel(). ]
Since __tty_hangup() first waits on the tty_lock() before
enumerating and hanging up the open file pointers, either
__tty_hangup() will wait for the tty_lock() or tty_open() will
not yet have added the file pointer. For example,
CPU 0 | CPU 1
|
tty_open | __tty_hangup
.. | ..
tty_lock | ..
tty_reopen | tty_lock / blocks
.. |
tty_add_file(tty, filp) |
.. |
tty->ops->open(tty, filp) |
tty_port_open |
tty_port_block_til_ready |
.. |
while (1) |
.. |
tty_unlock | / unblocks
schedule | for each filp on tty->tty_files
| f_ops = tty_hung_up_fops;
| ..
| tty_unlock
tty_lock |
.. |
tty_unlock |
Note that since tty_port_block_til_ready() and similar drop
the tty_lock while blocking, when woken, the file pointer
must then be tested for having been hung up.
Also, fix bit-rotted drivers that used extra_count to track the
port->count bump.
CC: Mikael Starvik <starvik@axis.com>
CC: Samuel Ortiz <samuel@sortiz.org>
CC: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-06-16 17:17:06 +04:00
+ + port - > count ;
2009-10-06 19:06:11 +04:00
spin_unlock_irq ( & port - > lock ) ;
tty_port_tty_set ( port , tty ) ;
/*
* Do the device - specific open only if the hardware isn ' t
* already initialized . Serialize open and shutdown using the
* port mutex .
*/
mutex_lock ( & port - > mutex ) ;
if ( ! test_bit ( ASYNCB_INITIALIZED , & port - > flags ) ) {
2009-11-30 16:16:57 +03:00
clear_bit ( TTY_IO_ERROR , & tty - > flags ) ;
2009-10-06 19:06:11 +04:00
if ( port - > ops - > activate ) {
int retval = port - > ops - > activate ( port , tty ) ;
if ( retval ) {
2009-10-06 19:06:21 +04:00
mutex_unlock ( & port - > mutex ) ;
return retval ;
}
}
2009-10-06 19:06:11 +04:00
set_bit ( ASYNCB_INITIALIZED , & port - > flags ) ;
}
mutex_unlock ( & port - > mutex ) ;
return tty_port_block_til_ready ( port , tty , filp ) ;
}
EXPORT_SYMBOL ( tty_port_open ) ;