mirror of
https://github.com/torvalds/linux.git
synced 2024-11-12 15:11:50 +00:00
pty: Rework the pty layer to use the normal buffering logic
This fixes the ppp problems and various other issues with call locking caused by one side of a pty called in one locking context trying to match another with differing rules on the other side. We also get a big slack space to work with that means we can bury the flow control deadlock case for any conceivable real world situation. Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
b4b21cac88
commit
d945cb9cce
@ -75,114 +75,88 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
|
|||||||
*/
|
*/
|
||||||
static void pty_unthrottle(struct tty_struct *tty)
|
static void pty_unthrottle(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_struct *o_tty = tty->link;
|
tty_wakeup(tty->link);
|
||||||
|
|
||||||
if (!o_tty)
|
|
||||||
return;
|
|
||||||
|
|
||||||
tty_wakeup(o_tty);
|
|
||||||
set_bit(TTY_THROTTLED, &tty->flags);
|
set_bit(TTY_THROTTLED, &tty->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* WSH 05/24/97: modified to
|
* pty_space - report space left for writing
|
||||||
* (1) use space in tty->flip instead of a shared temp buffer
|
* @to: tty we are writing into
|
||||||
* The flip buffers aren't being used for a pty, so there's lots
|
|
||||||
* of space available. The buffer is protected by a per-pty
|
|
||||||
* semaphore that should almost never come under contention.
|
|
||||||
* (2) avoid redundant copying for cases where count >> receive_room
|
|
||||||
* N.B. Calls from user space may now return an error code instead of
|
|
||||||
* a count.
|
|
||||||
*
|
*
|
||||||
* FIXME: Our pty_write method is called with our ldisc lock held but
|
* The tty buffers allow 64K but we sneak a peak and clip at 8K this
|
||||||
* not our partners. We can't just wait on the other one blindly without
|
* allows a lot of overspill room for echo and other fun messes to
|
||||||
* risking deadlocks. At some point when everything has settled down we need
|
* be handled properly
|
||||||
* to look into making pty_write at least able to sleep over an ldisc change.
|
|
||||||
*
|
|
||||||
* The return on no ldisc is a bit counter intuitive but the logic works
|
|
||||||
* like this. During an ldisc change the other end will flush its buffers. We
|
|
||||||
* thus return the full length which is identical to the case where we had
|
|
||||||
* proper locking and happened to queue the bytes just before the flush during
|
|
||||||
* the ldisc change.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static int pty_space(struct tty_struct *to)
|
||||||
|
{
|
||||||
|
int n = 8192 - to->buf.memory_used;
|
||||||
|
if (n < 0)
|
||||||
|
return 0;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pty_write - write to a pty
|
||||||
|
* @tty: the tty we write from
|
||||||
|
* @buf: kernel buffer of data
|
||||||
|
* @count: bytes to write
|
||||||
|
*
|
||||||
|
* Our "hardware" write method. Data is coming from the ldisc which
|
||||||
|
* may be in a non sleeping state. We simply throw this at the other
|
||||||
|
* end of the link as if we were an IRQ handler receiving stuff for
|
||||||
|
* the other side of the pty/tty pair.
|
||||||
|
*/
|
||||||
|
|
||||||
static int pty_write(struct tty_struct *tty, const unsigned char *buf,
|
static int pty_write(struct tty_struct *tty, const unsigned char *buf,
|
||||||
int count)
|
int count)
|
||||||
{
|
{
|
||||||
struct tty_struct *to = tty->link;
|
struct tty_struct *to = tty->link;
|
||||||
struct tty_ldisc *ld;
|
int c;
|
||||||
int c = count;
|
|
||||||
|
|
||||||
if (!to || tty->stopped)
|
if (tty->stopped)
|
||||||
return 0;
|
return 0;
|
||||||
ld = tty_ldisc_ref(to);
|
|
||||||
|
|
||||||
if (ld) {
|
/* This isn't locked but our 8K is quite sloppy so no
|
||||||
c = to->receive_room;
|
big deal */
|
||||||
if (c > count)
|
|
||||||
c = count;
|
c = pty_space(to);
|
||||||
ld->ops->receive_buf(to, buf, NULL, c);
|
if (c > count)
|
||||||
tty_ldisc_deref(ld);
|
c = count;
|
||||||
|
if (c > 0) {
|
||||||
|
/* Stuff the data into the input queue of the other end */
|
||||||
|
c = tty_insert_flip_string(to, buf, c);
|
||||||
|
/* And shovel */
|
||||||
|
tty_flip_buffer_push(to);
|
||||||
|
tty_wakeup(tty);
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pty_write_room - write space
|
||||||
|
* @tty: tty we are writing from
|
||||||
|
*
|
||||||
|
* Report how many bytes the ldisc can send into the queue for
|
||||||
|
* the other device.
|
||||||
|
*/
|
||||||
|
|
||||||
static int pty_write_room(struct tty_struct *tty)
|
static int pty_write_room(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_struct *to = tty->link;
|
return pty_space(tty->link);
|
||||||
|
|
||||||
if (!to || tty->stopped)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return to->receive_room;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior
|
* pty_chars_in_buffer - characters currently in our tx queue
|
||||||
* The chars_in_buffer() value is used by the ldisc select() function
|
* @tty: our tty
|
||||||
* to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256).
|
|
||||||
* The pty driver chars_in_buffer() Master/Slave must behave differently:
|
|
||||||
*
|
*
|
||||||
* The Master side needs to allow typed-ahead commands to accumulate
|
* Report how much we have in the transmit queue. As everything is
|
||||||
* while being canonicalized, so we report "our buffer" as empty until
|
* instantly at the other end this is easy to implement.
|
||||||
* some threshold is reached, and then report the count. (Any count >
|
|
||||||
* WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock
|
|
||||||
* the count returned must be 0 if no canonical data is available to be
|
|
||||||
* read. (The N_TTY ldisc.chars_in_buffer now knows this.)
|
|
||||||
*
|
|
||||||
* The Slave side passes all characters in raw mode to the Master side's
|
|
||||||
* buffer where they can be read immediately, so in this case we can
|
|
||||||
* return the true count in the buffer.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int pty_chars_in_buffer(struct tty_struct *tty)
|
static int pty_chars_in_buffer(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_struct *to = tty->link;
|
return 0;
|
||||||
struct tty_ldisc *ld;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
/* We should get the line discipline lock for "tty->link" */
|
|
||||||
if (!to)
|
|
||||||
return 0;
|
|
||||||
/* We cannot take a sleeping reference here without deadlocking with
|
|
||||||
an ldisc change - but it doesn't really matter */
|
|
||||||
ld = tty_ldisc_ref(to);
|
|
||||||
if (ld == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* The ldisc must report 0 if no characters available to be read */
|
|
||||||
if (ld->ops->chars_in_buffer)
|
|
||||||
count = ld->ops->chars_in_buffer(to);
|
|
||||||
|
|
||||||
tty_ldisc_deref(ld);
|
|
||||||
|
|
||||||
if (tty->driver->subtype == PTY_TYPE_SLAVE)
|
|
||||||
return count;
|
|
||||||
|
|
||||||
/* Master side driver ... if the other side's read buffer is less than
|
|
||||||
* half full, return 0 to allow writers to proceed; otherwise return
|
|
||||||
* the count. This leaves a comfortable margin to avoid overflow,
|
|
||||||
* and still allows half a buffer's worth of typed-ahead commands.
|
|
||||||
*/
|
|
||||||
return (count < N_TTY_BUF_SIZE/2) ? 0 : count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the lock flag on a pty */
|
/* Set the lock flag on a pty */
|
||||||
@ -202,20 +176,10 @@ static void pty_flush_buffer(struct tty_struct *tty)
|
|||||||
{
|
{
|
||||||
struct tty_struct *to = tty->link;
|
struct tty_struct *to = tty->link;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct tty_ldisc *ld;
|
|
||||||
|
|
||||||
if (!to)
|
if (!to)
|
||||||
return;
|
return;
|
||||||
ld = tty_ldisc_ref(to);
|
/* tty_buffer_flush(to); FIXME */
|
||||||
|
|
||||||
/* The other end is changing discipline */
|
|
||||||
if (!ld)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ld->ops->flush_buffer)
|
|
||||||
to->ldisc->ops->flush_buffer(to);
|
|
||||||
tty_ldisc_deref(ld);
|
|
||||||
|
|
||||||
if (to->packet) {
|
if (to->packet) {
|
||||||
spin_lock_irqsave(&tty->ctrl_lock, flags);
|
spin_lock_irqsave(&tty->ctrl_lock, flags);
|
||||||
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
|
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
|
||||||
|
Loading…
Reference in New Issue
Block a user