public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Jeff Dike <jdike@addtoit.com>
To: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: LKML <linux-kernel@vger.kernel.org>
Subject: tcsetattr(fd, TCSAFLUSH) on an O_ASYNC pts device always fails
Date: Tue, 27 Nov 2007 13:53:21 -0500	[thread overview]
Message-ID: <20071127185321.GA8546@c2.user-mode-linux.org> (raw)

tcsetattr(fd, TCSAFLUSH) will always return -EINTR on a pts device
when O_ASYNC has been set.  This is demonstrated by the test program
below - just compile and follow the instructions.  You'll get an
infinite stream of SIGIOs and -EINTRs.

The underlying reason is that the pty driver keeps its TTY_THROTTLED
flag set all the time:

static void check_unthrottle(struct tty_struct * tty)
{
	if (tty->count &&
	    test_and_clear_bit(TTY_THROTTLED, &tty->flags) && 
	    tty->driver->unthrottle)
		tty->driver->unthrottle(tty);
}

tty->driver->unthrottle is pty_unthrottle in this case:

static void pty_unthrottle(struct tty_struct * tty)
{
	...
	tty_wakeup(o_tty);
	set_bit(TTY_THROTTLED, &tty->flags);
}

tty_wakeup naturally wakes up any processes sleeping on the device
and also queues a SIGIO to any processes wanting async I/O
notifications.  However, the fact that a SIGIO is queued means that
the ioctl underneath tcsetattr returns -EINTR.  When userspace sees
this and retries the call, we go around the same loop:
	make sure that output is flushed
	call the unthrottle routine because TTY_THROTTLED is set
	deliver another SIGIO
	set TTY_THROTTLED
	return -EINTR

The call stack looks like this:

	kill_fasync
	tty_wakeup
	pty_unthrottle
	check_unthrottle
	n_tty_flush_buffer
	set_termios
	n_tty_ioctl
	tty_ioctl

I'd love to get rid of the set_bit(TTY_THROTTLED, &tty->flags) in
pty_unthrottle, but it's protected by this comment:

/* For PTY's, the TTY_THROTTLED
 * flag is always set, to force the line discipline to always call the
 * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE 
 * characters in the queue.  This is necessary since each time this
 * happens, we need to wake up any sleeping processes that could be
 * (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
 * for the pty buffer to be drained.
 */

Failing that, there should be a relevant state change in the device
before it will deliver a SIGIO.  I just have no idea where to put it.

				Jeff

-- 
Work email - jdike at linux dot intel dot com


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define __USE_GNU /* Needed to get F_SETSIG */
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <termios.h>

int pts_fd;

void sigio(int sig)
{
	struct pollfd fd;
	int err;

	printf("SIGIO\n");

	fd.fd = pts_fd;
	fd.events = POLLIN | POLLPRI | POLLOUT | POLLERR | POLLHUP | POLLNVAL;
	fd.revents = 0;

	err = poll(&fd, 1, 0);
	if (err < 0) {
		perror("poll");
		exit(1);
	}

	printf("poll returns revents = 0x%x\n", fd.revents);
}

int main(int argc, char **argv)
{
	struct termios save;
	int err;
	char c;

	pts_fd = open("/dev/ptmx", O_RDWR);
	if (pts_fd < 0) {
		perror("Opening /dev/ptmx");
		exit(1);
	}

	err = grantpt(pts_fd);
	if (err) {
		perror("grantpt");
		exit(1);
	}

	if (unlockpt(pts_fd) < 0) {
		perror("unlockpt");
		exit(1);
	}

	if((fcntl(pts_fd, F_SETFL, O_ASYNC | O_NONBLOCK) < 0) ||
	   (fcntl(pts_fd, F_SETSIG, SIGIO) < 0) ||
	   (fcntl(pts_fd, F_SETOWN, getpid()) < 0)){
		perror("F_SETFL, F_SETSIG, or F_SETOWN");
		exit(1);
	}

	signal(SIGIO, sigio);

	err = tcgetattr(pts_fd, &save);
	if (err) {
		perror("tcgetattr");
		exit(1);
	}

	printf("Attach screen to %s and hit return here\n", ptsname(pts_fd));

	read(0, &c, 1);

	do {
		err = tcsetattr(pts_fd, TCSAFLUSH, &save);
		if (!err)
			break;
		err = errno;
		perror("tcsetattr");
	} while(err == EINTR);
}

             reply	other threads:[~2007-11-27 18:53 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-11-27 18:53 Jeff Dike [this message]
2007-11-29  0:12 ` tcsetattr(fd, TCSAFLUSH) on an O_ASYNC pts device always fails Alan Cox

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20071127185321.GA8546@c2.user-mode-linux.org \
    --to=jdike@addtoit.com \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox