Skip to content

Commit

Permalink
Revert "tty: make receive_buf() return the amout of bytes received"
Browse files Browse the repository at this point in the history
This reverts commit b1c43f8.

It was broken in so many ways, and results in random odd pty issues.

It re-introduced the buggy schedule_work() in flush_to_ldisc() that can
cause endless work-loops (see commit a5660b4: "tty: fix endless
work loop when the buffer fills up").

It also used an "unsigned int" return value fo the ->receive_buf()
function, but then made multiple functions return a negative error code,
and didn't actually check for the error in the caller.

And it didn't actually work at all.  BenH bisected down odd tty behavior
to it:
  "It looks like the patch is causing some major malfunctions of the X
   server for me, possibly related to PTYs.  For example, cat'ing a
   large file in a gnome terminal hangs the kernel for -minutes- in a
   loop of what looks like flush_to_ldisc/workqueue code, (some ftrace
   data in the quoted bits further down).

   ...

   Some more data: It -looks- like what happens is that the
   flush_to_ldisc work queue entry constantly re-queues itself (because
   the PTY is full ?) and the workqueue thread will basically loop
   forver calling it without ever scheduling, thus starving the consumer
   process that could have emptied the PTY."

which is pretty much exactly the problem we fixed in a5660b4.

Milton Miller pointed out the 'unsigned int' issue.

Reported-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Reported-by: Milton Miller <miltonm@bga.com>
Cc: Stefan Bigler <stefan.bigler@keymile.com>
Cc: Toby Gray <toby.gray@realvnc.com>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Linus Torvalds committed Jun 3, 2011
1 parent 1fa7b6a commit 55db4c6
Show file tree
Hide file tree
Showing 20 changed files with 115 additions and 128 deletions.
17 changes: 6 additions & 11 deletions drivers/bluetooth/hci_ldisc.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,29 +355,24 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
* flags pointer to flags for data
* count count of received data in bytes
*
* Return Value: Number of bytes received
* Return Value: None
*/
static unsigned int hci_uart_tty_receive(struct tty_struct *tty,
const u8 *data, char *flags, int count)
static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
{
struct hci_uart *hu = (void *)tty->disc_data;
int received;

if (!hu || tty != hu->tty)
return -ENODEV;
return;

if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
return -EINVAL;
return;

spin_lock(&hu->rx_lock);
received = hu->proto->recv(hu, (void *) data, count);
if (received > 0)
hu->hdev->stat.byte_rx += received;
hu->proto->recv(hu, (void *) data, count);
hu->hdev->stat.byte_rx += count;
spin_unlock(&hu->rx_lock);

tty_unthrottle(tty);

return received;
}

static int hci_uart_register_dev(struct hci_uart *hu)
Expand Down
10 changes: 2 additions & 8 deletions drivers/input/serio/serport.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,21 +120,17 @@ static void serport_ldisc_close(struct tty_struct *tty)
* 'interrupt' routine.
*/

static unsigned int serport_ldisc_receive(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
{
struct serport *serport = (struct serport*) tty->disc_data;
unsigned long flags;
unsigned int ch_flags;
int ret = 0;
int i;

spin_lock_irqsave(&serport->lock, flags);

if (!test_bit(SERPORT_ACTIVE, &serport->flags)) {
ret = -EINVAL;
if (!test_bit(SERPORT_ACTIVE, &serport->flags))
goto out;
}

for (i = 0; i < count; i++) {
switch (fp[i]) {
Expand All @@ -156,8 +152,6 @@ static unsigned int serport_ldisc_receive(struct tty_struct *tty,

out:
spin_unlock_irqrestore(&serport->lock, flags);

return ret == 0 ? count : ret;
}

/*
Expand Down
8 changes: 3 additions & 5 deletions drivers/isdn/gigaset/ser-gigaset.c
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file,
* cflags buffer containing error flags for received characters (ignored)
* count number of received characters
*/
static unsigned int
static void
gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
char *cflags, int count)
{
Expand All @@ -683,12 +683,12 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
struct inbuf_t *inbuf;

if (!cs)
return -ENODEV;
return;
inbuf = cs->inbuf;
if (!inbuf) {
dev_err(cs->dev, "%s: no inbuf\n", __func__);
cs_put(cs);
return -EINVAL;
return;
}

tail = inbuf->tail;
Expand Down Expand Up @@ -725,8 +725,6 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
gigaset_schedule_event(cs);
cs_put(cs);

return count;
}

/*
Expand Down
6 changes: 2 additions & 4 deletions drivers/misc/ti-st/st_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -747,8 +747,8 @@ static void st_tty_close(struct tty_struct *tty)
pr_debug("%s: done ", __func__);
}

static unsigned int st_tty_receive(struct tty_struct *tty,
const unsigned char *data, char *tty_flags, int count)
static void st_tty_receive(struct tty_struct *tty, const unsigned char *data,
char *tty_flags, int count)
{
#ifdef VERBOSE
print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE,
Expand All @@ -761,8 +761,6 @@ static unsigned int st_tty_receive(struct tty_struct *tty,
*/
st_recv(tty->disc_data, data, count);
pr_debug("done %s", __func__);

return count;
}

/* wake-up function called in from the TTY layer
Expand Down
6 changes: 2 additions & 4 deletions drivers/net/caif/caif_serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size)

#endif

static unsigned int ldisc_receive(struct tty_struct *tty,
const u8 *data, char *flags, int count)
static void ldisc_receive(struct tty_struct *tty, const u8 *data,
char *flags, int count)
{
struct sk_buff *skb = NULL;
struct ser_device *ser;
Expand Down Expand Up @@ -215,8 +215,6 @@ static unsigned int ldisc_receive(struct tty_struct *tty,
} else
++ser->dev->stats.rx_dropped;
update_tty_status(ser);

return count;
}

static int handle_tx(struct ser_device *ser)
Expand Down
9 changes: 3 additions & 6 deletions drivers/net/can/slcan.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,17 +425,16 @@ static void slc_setup(struct net_device *dev)
* in parallel
*/

static unsigned int slcan_receive_buf(struct tty_struct *tty,
static void slcan_receive_buf(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
{
struct slcan *sl = (struct slcan *) tty->disc_data;
int bytes = count;

if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev))
return -ENODEV;
return;

/* Read the characters out of the buffer */
while (bytes--) {
while (count--) {
if (fp && *fp++) {
if (!test_and_set_bit(SLF_ERROR, &sl->flags))
sl->dev->stats.rx_errors++;
Expand All @@ -444,8 +443,6 @@ static unsigned int slcan_receive_buf(struct tty_struct *tty,
}
slcan_unesc(sl, *cp++);
}

return count;
}

/************************************
Expand Down
8 changes: 3 additions & 5 deletions drivers/net/hamradio/6pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -456,19 +456,19 @@ static void sixpack_write_wakeup(struct tty_struct *tty)
* a block of 6pack data has been received, which can now be decapsulated
* and sent on to some IP layer for further processing.
*/
static unsigned int sixpack_receive_buf(struct tty_struct *tty,
static void sixpack_receive_buf(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
{
struct sixpack *sp;
unsigned char buf[512];
int count1;

if (!count)
return 0;
return;

sp = sp_get(tty);
if (!sp)
return -ENODEV;
return;

memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf));

Expand All @@ -487,8 +487,6 @@ static unsigned int sixpack_receive_buf(struct tty_struct *tty,

sp_put(sp);
tty_unthrottle(tty);

return count1;
}

/*
Expand Down
11 changes: 4 additions & 7 deletions drivers/net/hamradio/mkiss.c
Original file line number Diff line number Diff line change
Expand Up @@ -923,14 +923,13 @@ static long mkiss_compat_ioctl(struct tty_struct *tty, struct file *file,
* a block of data has been received, which can now be decapsulated
* and sent on to the AX.25 layer for further processing.
*/
static unsigned int mkiss_receive_buf(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
struct mkiss *ax = mkiss_get(tty);
int bytes = count;

if (!ax)
return -ENODEV;
return;

/*
* Argh! mtu change time! - costs us the packet part received
Expand All @@ -940,7 +939,7 @@ static unsigned int mkiss_receive_buf(struct tty_struct *tty,
ax_changedmtu(ax);

/* Read the characters out of the buffer */
while (bytes--) {
while (count--) {
if (fp != NULL && *fp++) {
if (!test_and_set_bit(AXF_ERROR, &ax->flags))
ax->dev->stats.rx_errors++;
Expand All @@ -953,8 +952,6 @@ static unsigned int mkiss_receive_buf(struct tty_struct *tty,

mkiss_put(ax);
tty_unthrottle(tty);

return count;
}

/*
Expand Down
16 changes: 7 additions & 9 deletions drivers/net/irda/irtty-sir.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,23 +216,23 @@ static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t
* usbserial: urb-complete-interrupt / softint
*/

static unsigned int irtty_receive_buf(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
struct sir_dev *dev;
struct sirtty_cb *priv = tty->disc_data;
int i;

IRDA_ASSERT(priv != NULL, return -ENODEV;);
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EINVAL;);
IRDA_ASSERT(priv != NULL, return;);
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);

if (unlikely(count==0)) /* yes, this happens */
return 0;
return;

dev = priv->dev;
if (!dev) {
IRDA_WARNING("%s(), not ready yet!\n", __func__);
return -ENODEV;
return;
}

for (i = 0; i < count; i++) {
Expand All @@ -242,13 +242,11 @@ static unsigned int irtty_receive_buf(struct tty_struct *tty,
if (fp && *fp++) {
IRDA_DEBUG(0, "Framing or parity error!\n");
sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */
return -EINVAL;
return;
}
}

sirdev_receive(dev, cp, count);

return count;
}

/*
Expand Down
6 changes: 2 additions & 4 deletions drivers/net/ppp_async.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,24 +340,22 @@ ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
}

/* May sleep, don't call from interrupt level or with interrupts disabled */
static unsigned int
static void
ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
char *cflags, int count)
{
struct asyncppp *ap = ap_get(tty);
unsigned long flags;

if (!ap)
return -ENODEV;
return;
spin_lock_irqsave(&ap->recv_lock, flags);
ppp_async_input(ap, buf, cflags, count);
spin_unlock_irqrestore(&ap->recv_lock, flags);
if (!skb_queue_empty(&ap->rqueue))
tasklet_schedule(&ap->tsk);
ap_put(ap);
tty_unthrottle(tty);

return count;
}

static void
Expand Down
6 changes: 2 additions & 4 deletions drivers/net/ppp_synctty.c
Original file line number Diff line number Diff line change
Expand Up @@ -381,24 +381,22 @@ ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
}

/* May sleep, don't call from interrupt level or with interrupts disabled */
static unsigned int
static void
ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
char *cflags, int count)
{
struct syncppp *ap = sp_get(tty);
unsigned long flags;

if (!ap)
return -ENODEV;
return;
spin_lock_irqsave(&ap->recv_lock, flags);
ppp_sync_input(ap, buf, cflags, count);
spin_unlock_irqrestore(&ap->recv_lock, flags);
if (!skb_queue_empty(&ap->rqueue))
tasklet_schedule(&ap->tsk);
sp_put(ap);
tty_unthrottle(tty);

return count;
}

static void
Expand Down
11 changes: 4 additions & 7 deletions drivers/net/slip.c
Original file line number Diff line number Diff line change
Expand Up @@ -670,17 +670,16 @@ static void sl_setup(struct net_device *dev)
* in parallel
*/

static unsigned int slip_receive_buf(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
struct slip *sl = tty->disc_data;
int bytes = count;

if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev))
return -ENODEV;
return;

/* Read the characters out of the buffer */
while (bytes--) {
while (count--) {
if (fp && *fp++) {
if (!test_and_set_bit(SLF_ERROR, &sl->flags))
sl->dev->stats.rx_errors++;
Expand All @@ -694,8 +693,6 @@ static unsigned int slip_receive_buf(struct tty_struct *tty,
#endif
slip_unesc(sl, *cp++);
}

return count;
}

/************************************
Expand Down
7 changes: 2 additions & 5 deletions drivers/net/wan/x25_asy.c
Original file line number Diff line number Diff line change
Expand Up @@ -517,18 +517,17 @@ static int x25_asy_close(struct net_device *dev)
* and sent on to some IP layer for further processing.
*/

static unsigned int x25_asy_receive_buf(struct tty_struct *tty,
static void x25_asy_receive_buf(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
{
struct x25_asy *sl = tty->disc_data;
int bytes = count;

if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev))
return;


/* Read the characters out of the buffer */
while (bytes--) {
while (count--) {
if (fp && *fp++) {
if (!test_and_set_bit(SLF_ERROR, &sl->flags))
sl->dev->stats.rx_errors++;
Expand All @@ -537,8 +536,6 @@ static unsigned int x25_asy_receive_buf(struct tty_struct *tty,
}
x25_asy_unesc(sl, *cp++);
}

return count;
}

/*
Expand Down
Loading

0 comments on commit 55db4c6

Please sign in to comment.