* TTY loosing data with u_serial gadget @ 2011-03-16 20:47 Stefan Bigler 2011-03-17 0:04 ` Greg KH 0 siblings, 1 reply; 19+ messages in thread From: Stefan Bigler @ 2011-03-16 20:47 UTC (permalink / raw) To: linux-kernel Hi all I'm facing a problem with TTY loosing data using u_serial gadget. I have a MPC8247 based system running 2.6.33. Data transfer is done bidirectional to see the problem more often. I use tty in raw mode and do some delayed reads on the receiver side to stress the throttling / un-throttling. I tracked down the problem and was able to see that n_tty_receive_buf() is not able to store all data in the read queue. The function flush_to_ldisc() use the values of tty->receive_room to schedule_delayed_work()or to pass data to receive_buf() and finally n_tty_receive_buf(). Also the number of passed bytes rely on variable receive_room. I added debug code to re-calculate the real free space. left_space = N_TTY_BUF_SIZE - tty->read_cnt - 1; Comparing receive_room and left_space showed in some cases big differences up to 1600 bytes. left_space was always smaller and sometimes zero. receive_room is calculated in n_tty_set_room() without read locking. There are various "FIXME does n_tty_set_room need locking ?" My test showed, adding a read looking solves the problem. I asked me why is the locking not done? How big is the risk for dead-locks? To minimize this risk, I reduced the changes to a minimum (see the patch below). The code in the patch only re-calculates the receive_room for raw mode right before it is used in flush_to_ldisc(). Can somebody give me an advice, what is the best way to solve this problem? Thanks for your help. Regards Stefan From 91b04c875cecc890139ccdd101cfff6b02b5f22c Mon Sep 17 00:00:00 2001 From: Stefan Bigler <stefan.bigler@keymile.com> Date: Wed, 16 Mar 2011 15:20:03 +0100 Subject: [PATCH 1/1] n_tty: fix race condition with receive_room Do an additional update of receive_room with locking for raw tty. The n_tty_set_room is done without locking what is known as a potential problem. In case of heavy load and raw tty and flush_to_ldisc() determine the number of char to send with receive_buf(). If there is not enough space available in receive_buf() the data is lost. This additional update of receive_room should reduce the risk of have invalid receive_room. It is not a clean fix but the risk of risk a dead-lock with clean protection is too high Signed-off-by: Stefan Bigler <stefan.bigler@keymile.com> --- drivers/char/tty_buffer.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-) diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index f27c4d6..5db07fe 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c @@ -431,6 +431,22 @@ static void flush_to_ldisc(struct work_struct *work) line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; + + /* Take read_lock to update receive_room. + This avoids invalid free space information. + To limit the risk of introducing problems + only do it for raw tty */ + if (tty->raw) { + unsigned long flags_r; + spin_lock_irqsave(&tty->read_lock, + flags_r); + tty->receive_room = + N_TTY_BUF_SIZE - + tty->read_cnt - 1; + spin_unlock_irqrestore(&tty->read_lock, + flags_r); + } + if (!tty->receive_room) { schedule_delayed_work(&tty->buf.work, 1); break; -- 1.7.0.5 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-16 20:47 TTY loosing data with u_serial gadget Stefan Bigler @ 2011-03-17 0:04 ` Greg KH 2011-03-18 16:35 ` Stefan Bigler 0 siblings, 1 reply; 19+ messages in thread From: Greg KH @ 2011-03-17 0:04 UTC (permalink / raw) To: Stefan Bigler; +Cc: linux-kernel On Wed, Mar 16, 2011 at 09:47:50PM +0100, Stefan Bigler wrote: > Hi all > > I'm facing a problem with TTY loosing data using u_serial gadget. > > I have a MPC8247 based system running 2.6.33. Oh, that's an old kernel, lots of work on the tty layer has happened since then that would fix issues like this. If you are stuck using this kernel, please go ask your vendor for support for it. If not, can you try out 2.6.38 and let us, and the linux-usb@vger.kernel.org list know if you still have problems with it? thanks, greg k-h ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-17 0:04 ` Greg KH @ 2011-03-18 16:35 ` Stefan Bigler 2011-03-18 17:08 ` Felipe Balbi 2011-03-18 18:07 ` Alan Cox 0 siblings, 2 replies; 19+ messages in thread From: Stefan Bigler @ 2011-03-18 16:35 UTC (permalink / raw) To: Greg KH; +Cc: linux-kernel, linux-usb Hi Greg Thanks for your quick answer. Meanwhile we ported our board to 2.6.38. We recognize the same problem again. I had also a look at the relevant fixes, a lot is done but I could not find the required protection of the attribute receive_room. I added a printk in case of failure were receive_room shows more space that is really available in the queue and also used. The printk is attached below and also the log. Regards Stefan Log: flush_to_ldisc, receive_room to small count=201, receive_room=287, real_room=0 flush_to_ldisc, receive_room to small count=225, receive_room=1031, real_room=0 flush_to_ldisc, receive_room to small count=33, receive_room=4095, real_room=0 diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index d8210ca..fa9ca62 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -442,6 +442,21 @@ static void flush_to_ldisc(struct work_struct *work) line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; + + /* add printk in case of inconsistante size */ + if (tty->raw) { + unsigned long flags_r; + int real_room; + spin_lock_irqsave(&tty->read_lock, flags_r); + real_room = N_TTY_BUF_SIZE -tty->read_cnt - 1; + if (real_room < min(count, tty->receive_room)) { + printk(KERN_ERR "flush_to_ldisc, receive_room to small count=%d," + " receive_room=%d, real_room=%d\n", + count, tty->receive_room, real_room); + } + spin_unlock_irqrestore(&tty->read_lock, flags_r); + } + if (!tty->receive_room || seen_tail) { schedule_delayed_work(&tty->buf.work, 1); break; Am 03/17/2011 01:04 AM, wrote Greg KH: > On Wed, Mar 16, 2011 at 09:47:50PM +0100, Stefan Bigler wrote: >> Hi all >> >> I'm facing a problem with TTY loosing data using u_serial gadget. >> >> I have a MPC8247 based system running 2.6.33. > Oh, that's an old kernel, lots of work on the tty layer has happened > since then that would fix issues like this. > > If you are stuck using this kernel, please go ask your vendor for > support for it. > > If not, can you try out 2.6.38 and let us, and the > linux-usb@vger.kernel.org list know if you still have problems with it? > > thanks, > > greg k-h ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-18 16:35 ` Stefan Bigler @ 2011-03-18 17:08 ` Felipe Balbi 2011-03-18 18:06 ` Alan Cox 2011-03-18 21:46 ` Stefan Bigler 2011-03-18 18:07 ` Alan Cox 1 sibling, 2 replies; 19+ messages in thread From: Felipe Balbi @ 2011-03-18 17:08 UTC (permalink / raw) To: Stefan Bigler; +Cc: Greg KH, linux-kernel, linux-usb Hi, (please don't top-post) On Fri, Mar 18, 2011 at 05:35:56PM +0100, Stefan Bigler wrote: > Thanks for your quick answer. Meanwhile we ported our board to 2.6.38. > We recognize the same problem again. > I had also a look at the relevant fixes, a lot is done but I could not find > the required protection of the attribute receive_room. > > I added a printk in case of failure were receive_room shows more > space that is really available in the queue and also used. > > The printk is attached below and also the log. > > Regards > Stefan > > > Log: > flush_to_ldisc, receive_room to small count=201, receive_room=287, > real_room=0 > flush_to_ldisc, receive_room to small count=225, receive_room=1031, > real_room=0 > flush_to_ldisc, receive_room to small count=33, receive_room=4095, > real_room=0 I had the same problem while I was working at Nokia developing the N900. I even sent a patch (twice) with Alan Cox on the loop but nothing has happened. The patch might still be floating on the archives but essentially it would make ->receive_buf() return the amount of received bytes (as IMHO it should be done). -- balbi ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-18 17:08 ` Felipe Balbi @ 2011-03-18 18:06 ` Alan Cox 2011-03-21 9:32 ` Felipe Balbi 2011-03-18 21:46 ` Stefan Bigler 1 sibling, 1 reply; 19+ messages in thread From: Alan Cox @ 2011-03-18 18:06 UTC (permalink / raw) To: balbi; +Cc: Stefan Bigler, Greg KH, linux-kernel, linux-usb > I had the same problem while I was working at Nokia developing the N900. > I even sent a patch (twice) with Alan Cox on the loop but nothing has > happened. http://lkml.indiana.edu/hypermail/linux/kernel/0910.0/02347.html is I assume the thread you are talking about - which ground to a halt without actually figuring out what you were seeing and what was really going on. http://permalink.gmane.org/gmane.linux.usb.general/28976 would be the second generation one which was somewhat longer after I ceased to be tty maintainer so I can't really say it never got upstream. I still think its the right thing to do, so perhaps it's worth reposting it, especially if it fixes this case too. Alan ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-18 18:06 ` Alan Cox @ 2011-03-21 9:32 ` Felipe Balbi 2011-03-22 8:53 ` Felipe Balbi 0 siblings, 1 reply; 19+ messages in thread From: Felipe Balbi @ 2011-03-21 9:32 UTC (permalink / raw) To: Alan Cox; +Cc: balbi, Stefan Bigler, Greg KH, linux-kernel, linux-usb On Fri, Mar 18, 2011 at 06:06:57PM +0000, Alan Cox wrote: > > I had the same problem while I was working at Nokia developing the N900. > > I even sent a patch (twice) with Alan Cox on the loop but nothing has > > happened. > > http://lkml.indiana.edu/hypermail/linux/kernel/0910.0/02347.html > > is I assume the thread you are talking about - which ground to a halt > without actually figuring out what you were seeing and what was really > going on. > > http://permalink.gmane.org/gmane.linux.usb.general/28976 > > would be the second generation one which was somewhat longer after I > ceased to be tty maintainer so I can't really say it never got upstream. > > I still think its the right thing to do, so perhaps it's worth reposting > it, especially if it fixes this case too. Ok, I'll update that patch and re-send. I'll compile test on x86 and boot test on an ARM box, so it would be cool to have Acks from several people before applying, just in case. -- balbi ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-21 9:32 ` Felipe Balbi @ 2011-03-22 8:53 ` Felipe Balbi 2011-03-22 11:04 ` Alan Cox 2011-03-24 12:07 ` Toby Gray 0 siblings, 2 replies; 19+ messages in thread From: Felipe Balbi @ 2011-03-22 8:53 UTC (permalink / raw) To: Felipe Balbi; +Cc: Alan Cox, Stefan Bigler, Greg KH, linux-kernel, linux-usb [-- Attachment #1: Type: text/plain, Size: 1341 bytes --] On Mon, Mar 21, 2011 at 11:32:57AM +0200, Felipe Balbi wrote: > On Fri, Mar 18, 2011 at 06:06:57PM +0000, Alan Cox wrote: > > > I had the same problem while I was working at Nokia developing the N900. > > > I even sent a patch (twice) with Alan Cox on the loop but nothing has > > > happened. > > > > http://lkml.indiana.edu/hypermail/linux/kernel/0910.0/02347.html > > > > is I assume the thread you are talking about - which ground to a halt > > without actually figuring out what you were seeing and what was really > > going on. > > > > http://permalink.gmane.org/gmane.linux.usb.general/28976 > > > > would be the second generation one which was somewhat longer after I > > ceased to be tty maintainer so I can't really say it never got upstream. > > > > I still think its the right thing to do, so perhaps it's worth reposting > > it, especially if it fixes this case too. > > Ok, I'll update that patch and re-send. I'll compile test on x86 and > boot test on an ARM box, so it would be cool to have Acks from several > people before applying, just in case. Patch attached, please give it a good round of test as I don't have how to exercise all line disciplines. I ran 100 randconfigs over night and no warnings or erros on that area, at least not that I could see (so many warning while compiling the kernel :-( ) -- balbi [-- Attachment #2: 0001-ty-make-receive_buf-return-the-amout-of-bytes-receive.diff --] [-- Type: text/x-diff, Size: 25087 bytes --] >From 3d099a44deb26c10e0e51be0432d6dcc107d3358 Mon Sep 17 00:00:00 2001 From: Felipe Balbi <balbi@ti.com> Date: Mon, 21 Mar 2011 12:25:08 +0200 Subject: [PATCH] ty: make receive_buf() return the amout of bytes received Organization: Texas Instruments\n it makes it simpler to keep track of the amount of bytes received and simplifies how flush_to_ldisc counts the remaining bytes. It also fixes a bug of lost bytes on n_tty when flushing too many bytes via the USB serial gadget driver. Signed-off-by: Felipe Balbi <balbi@ti.com> --- drivers/bluetooth/hci_ldisc.c | 12 +++++-- drivers/input/serio/serport.c | 10 +++++- drivers/isdn/gigaset/ser-gigaset.c | 8 +++-- drivers/misc/ti-st/st_core.c | 6 ++- drivers/net/caif/caif_serial.c | 6 ++- drivers/net/can/slcan.c | 9 ++++-- drivers/net/hamradio/6pack.c | 8 +++-- drivers/net/hamradio/mkiss.c | 11 ++++-- drivers/net/irda/irtty-sir.c | 16 ++++++---- drivers/net/ppp_async.c | 6 ++- drivers/net/ppp_synctty.c | 6 ++- drivers/net/slip.c | 11 ++++-- drivers/net/wan/x25_asy.c | 7 +++- drivers/tty/n_gsm.c | 6 ++- drivers/tty/n_hdlc.c | 18 ++++++----- drivers/tty/n_r3964.c | 10 ++++-- drivers/tty/n_tty.c | 54 ++++++++--------------------------- include/linux/tty_ldisc.h | 9 +++-- 18 files changed, 114 insertions(+), 99 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 3c6cabc..72c542b 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -357,22 +357,26 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) * * Return Value: None */ -static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) +static unsigned int 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; + return -ENODEV; if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) - return; + return -EINVAL; spin_lock(&hu->rx_lock); - hu->proto->recv(hu, (void *) data, count); + 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) diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 8755f5f..f369896 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -120,17 +120,21 @@ static void serport_ldisc_close(struct tty_struct *tty) * 'interrupt' routine. */ -static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +static unsigned int 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)) + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) { + ret = -EINVAL; goto out; + } for (i = 0; i < count; i++) { switch (fp[i]) { @@ -152,6 +156,8 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c out: spin_unlock_irqrestore(&serport->lock, flags); + + return ret == 0 ? count : ret; } /* diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 0ef09d0..14e57b4 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -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 void +static unsigned int gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -683,12 +683,12 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, struct inbuf_t *inbuf; if (!cs) - return; + return -ENODEV; inbuf = cs->inbuf; if (!inbuf) { dev_err(cs->dev, "%s: no inbuf\n", __func__); cs_put(cs); - return; + return -EINVAL; } tail = inbuf->tail; @@ -725,6 +725,8 @@ 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; } /* diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index f9aad06..5d23d89 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -866,8 +866,8 @@ static void st_tty_close(struct tty_struct *tty) pr_debug("%s: done ", __func__); } -static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, - char *tty_flags, int count) +static unsigned int st_tty_receive(struct tty_struct *tty, + const unsigned char *data, char *tty_flags, int count) { #ifdef VERBOSE @@ -881,6 +881,8 @@ static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, */ st_recv(tty->disc_data, data, count); pr_debug("done %s", __func__); + + return count; } /* wake-up function called in from the TTY layer diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 3df0c0f..73c7e03 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -167,8 +167,8 @@ static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) #endif -static void ldisc_receive(struct tty_struct *tty, const u8 *data, - char *flags, int count) +static unsigned int ldisc_receive(struct tty_struct *tty, + const u8 *data, char *flags, int count) { struct sk_buff *skb = NULL; struct ser_device *ser; @@ -215,6 +215,8 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data, } else ++ser->dev->stats.rx_dropped; update_tty_status(ser); + + return count; } static int handle_tx(struct ser_device *ser) diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index b423965..c600954 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -425,16 +425,17 @@ static void slc_setup(struct net_device *dev) * in parallel */ -static void slcan_receive_buf(struct tty_struct *tty, +static unsigned int 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; + return -ENODEV; /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -443,6 +444,8 @@ static void slcan_receive_buf(struct tty_struct *tty, } slcan_unesc(sl, *cp++); } + + return count; } /************************************ diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 3e5d0b6..9920896 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -456,7 +456,7 @@ out: * a block of 6pack data has been received, which can now be decapsulated * and sent on to some IP layer for further processing. */ -static void sixpack_receive_buf(struct tty_struct *tty, +static unsigned int sixpack_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct sixpack *sp; @@ -464,11 +464,11 @@ static void sixpack_receive_buf(struct tty_struct *tty, int count1; if (!count) - return; + return 0; sp = sp_get(tty); if (!sp) - return; + return -ENODEV; memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf)); @@ -487,6 +487,8 @@ static void sixpack_receive_buf(struct tty_struct *tty, sp_put(sp); tty_unthrottle(tty); + + return count1; } /* diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 4c62839..0e4f235 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -923,13 +923,14 @@ 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 void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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; + return -ENODEV; /* * Argh! mtu change time! - costs us the packet part received @@ -939,7 +940,7 @@ static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, ax_changedmtu(ax); /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp != NULL && *fp++) { if (!test_and_set_bit(AXF_ERROR, &ax->flags)) ax->dev->stats.rx_errors++; @@ -952,6 +953,8 @@ static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, mkiss_put(ax); tty_unthrottle(tty); + + return count; } /* diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index ee1dde5..b4e6480 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -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 void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); + IRDA_ASSERT(priv != NULL, return -ENODEV;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EINVAL;); if (unlikely(count==0)) /* yes, this happens */ - return; + return 0; dev = priv->dev; if (!dev) { IRDA_WARNING("%s(), not ready yet!\n", __func__); - return; + return -ENODEV; } for (i = 0; i < count; i++) { @@ -242,11 +242,13 @@ static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, if (fp && *fp++) { IRDA_DEBUG(0, "Framing or parity error!\n"); sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */ - return; + return -EINVAL; } } sirdev_receive(dev, cp, count); + + return count; } /* diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index a1b82c9..53872d7 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -340,7 +340,7 @@ 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 void +static unsigned int ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -348,7 +348,7 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, unsigned long flags; if (!ap) - return; + return -ENODEV; spin_lock_irqsave(&ap->recv_lock, flags); ppp_async_input(ap, buf, cflags, count); spin_unlock_irqrestore(&ap->recv_lock, flags); @@ -356,6 +356,8 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, tasklet_schedule(&ap->tsk); ap_put(ap); tty_unthrottle(tty); + + return count; } static void diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 4e6b72f..01122cd 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -381,7 +381,7 @@ 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 void +static unsigned int ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -389,7 +389,7 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, unsigned long flags; if (!ap) - return; + return -ENODEV; spin_lock_irqsave(&ap->recv_lock, flags); ppp_sync_input(ap, buf, cflags, count); spin_unlock_irqrestore(&ap->recv_lock, flags); @@ -397,6 +397,8 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, tasklet_schedule(&ap->tsk); sp_put(ap); tty_unthrottle(tty); + + return count; } static void diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 86cbb9e..86718d3 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -670,16 +670,17 @@ static void sl_setup(struct net_device *dev) * in parallel */ -static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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; + return -ENODEV; /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -693,6 +694,8 @@ static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, #endif slip_unesc(sl, *cp++); } + + return count; } /************************************ diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 24297b2..40398bf 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -517,17 +517,18 @@ static int x25_asy_close(struct net_device *dev) * and sent on to some IP layer for further processing. */ -static void x25_asy_receive_buf(struct tty_struct *tty, +static unsigned int 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 (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -536,6 +537,8 @@ static void x25_asy_receive_buf(struct tty_struct *tty, } x25_asy_unesc(sl, *cp++); } + + return count; } /* diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index aa2e5d3..a606331 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2139,8 +2139,8 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) gsm->tty = NULL; } -static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int gsmld_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct gsm_mux *gsm = tty->disc_data; const unsigned char *dp; @@ -2174,6 +2174,8 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, } /* FASYNC if needed ? */ /* If clogged call tty_throttle(tty); */ + + return count; } /** diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 52fc0c9..517e376 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -189,8 +189,8 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait); static int n_hdlc_tty_open(struct tty_struct *tty); static void n_hdlc_tty_close(struct tty_struct *tty); -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp, - char *fp, int count); +static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 *cp, char *fp, int count); static void n_hdlc_tty_wakeup(struct tty_struct *tty); #define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) @@ -510,8 +510,8 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty) * Called by tty low level driver when receive data is available. Data is * interpreted as one HDLC frame. */ -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, - char *flags, int count) +static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 *data, char *flags, int count) { register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); register struct n_hdlc_buf *buf; @@ -522,20 +522,20 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, /* This can happen if stuff comes in on the backup tty */ if (!n_hdlc || tty != n_hdlc->tty) - return; + return -ENODEV; /* verify line is using HDLC discipline */ if (n_hdlc->magic != HDLC_MAGIC) { printk("%s(%d) line not using HDLC discipline\n", __FILE__,__LINE__); - return; + return -EINVAL; } if ( count>maxframe ) { if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) rx count>maxframesize, data discarded\n", __FILE__,__LINE__); - return; + return -EINVAL; } /* get a free HDLC buffer */ @@ -551,7 +551,7 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) no more rx buffers, data discarded\n", __FILE__,__LINE__); - return; + return -EINVAL; } /* copy received data to HDLC buffer */ @@ -566,6 +566,8 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, if (n_hdlc->tty->fasync != NULL) kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN); + return count; + } /* end of n_hdlc_tty_receive() */ /** diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 88dda0c..ea725c5 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -140,8 +140,8 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file, static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old); static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait); -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count); +static unsigned int r3964_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count); static struct tty_ldisc_ops tty_ldisc_N_R3964 = { .owner = THIS_MODULE, @@ -1240,8 +1240,8 @@ static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, return result; } -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int r3964_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct r3964_info *pInfo = tty->disc_data; const unsigned char *p; @@ -1258,6 +1258,8 @@ static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, } } + + return count; } MODULE_LICENSE("GPL"); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 428f4fe..8e0f113 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -81,32 +81,6 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set__room - receive space - * @tty: terminal - * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". - */ - -static void n_tty_set_room(struct tty_struct *tty) -{ - /* tty->read_cnt is not read locked ? */ - int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; - - /* - * If we are doing input canonicalization, and there are no - * pending newlines, let characters through without limit, so - * that erase characters will be handled. Other excess - * characters will be beeped. - */ - if (left <= 0) - left = tty->icanon && !tty->canon_data; - tty->receive_room = left; -} - static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) { if (tty->read_cnt < N_TTY_BUF_SIZE) { @@ -178,7 +152,6 @@ static void reset_buffer_flags(struct tty_struct *tty) tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); - n_tty_set_room(tty); check_unthrottle(tty); } @@ -1354,17 +1327,19 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * calls one at a time and in order (or using flush_to_ldisc) */ -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int n_tty_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { const unsigned char *p; char *f, flags = TTY_NORMAL; int i; char buf[64]; unsigned long cpuflags; + int left; + int ret = 0; if (!tty->read_buf) - return; + return 0; if (tty->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); @@ -1374,6 +1349,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; + ret += i; cp += i; count -= i; @@ -1385,6 +1361,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->read_cnt += i; spin_unlock_irqrestore(&tty->read_lock, cpuflags); } else { + ret = count; for (i = count, p = cp, f = fp; i; i--, p++) { if (f) flags = *f++; @@ -1412,8 +1389,6 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->ops->flush_chars(tty); } - n_tty_set_room(tty); - if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); @@ -1426,8 +1401,12 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) + left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + + if (left < TTY_THRESHOLD_THROTTLE) tty_throttle(tty); + + return ret; } int is_ignored(int sig) @@ -1471,7 +1450,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; tty->real_raw = 1; - n_tty_set_room(tty); return; } if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || @@ -1524,7 +1502,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) else tty->real_raw = 0; } - n_tty_set_room(tty); /* The termios change make the tty ready for I/O */ wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); @@ -1806,8 +1783,6 @@ do_it_again: retval = -ERESTARTSYS; break; } - /* FIXME: does n_tty_set_room need locking ? */ - n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; } @@ -1879,10 +1854,8 @@ do_it_again: * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { - n_tty_set_room(tty); + if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) check_unthrottle(tty); - } if (b - buf >= minimum) break; @@ -1904,7 +1877,6 @@ do_it_again: } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; - n_tty_set_room(tty); return retval; } diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index ff7dc08..5b07792 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -76,7 +76,7 @@ * tty device. It is solely the responsibility of the line * discipline to handle poll requests. * - * void (*receive_buf)(struct tty_struct *, const unsigned char *cp, + * unsigned int (*receive_buf)(struct tty_struct *, const unsigned char *cp, * char *fp, int count); * * This function is called by the low-level tty driver to send @@ -84,7 +84,8 @@ * processing. <cp> is a pointer to the buffer of input * character received by the device. <fp> is a pointer to a * pointer of flag bytes which indicate whether a character was - * received with a parity error, etc. + * received with a parity error, etc. Returns the amount of bytes + * received. * * void (*write_wakeup)(struct tty_struct *); * @@ -140,8 +141,8 @@ struct tty_ldisc_ops { /* * The following routines are called from below. */ - void (*receive_buf)(struct tty_struct *, const unsigned char *cp, - char *fp, int count); + unsigned int (*receive_buf)(struct tty_struct *, + const unsigned char *cp, char *fp, int count); void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int, struct pps_event_time *); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-22 8:53 ` Felipe Balbi @ 2011-03-22 11:04 ` Alan Cox 2011-03-22 17:01 ` Greg KH 2011-03-24 12:07 ` Toby Gray 1 sibling, 1 reply; 19+ messages in thread From: Alan Cox @ 2011-03-22 11:04 UTC (permalink / raw) To: balbi; +Cc: Stefan Bigler, Greg KH, linux-kernel, linux-usb > Patch attached, please give it a good round of test as I don't have how > to exercise all line disciplines. I ran 100 randconfigs over night and > no warnings or erros on that area, at least not that I could see (so > many warning while compiling the kernel :-( ) n_tty is I believe the only ldisc which assets throttling - the others actually prefer to lose data on an overflow not get further blockages. Greg - can we get this into -next once -rc1 is out and see who screams ? ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-22 11:04 ` Alan Cox @ 2011-03-22 17:01 ` Greg KH 0 siblings, 0 replies; 19+ messages in thread From: Greg KH @ 2011-03-22 17:01 UTC (permalink / raw) To: Alan Cox; +Cc: balbi, Stefan Bigler, linux-kernel, linux-usb On Tue, Mar 22, 2011 at 11:04:12AM +0000, Alan Cox wrote: > > Patch attached, please give it a good round of test as I don't have how > > to exercise all line disciplines. I ran 100 randconfigs over night and > > no warnings or erros on that area, at least not that I could see (so > > many warning while compiling the kernel :-( ) > > n_tty is I believe the only ldisc which assets throttling - the others > actually prefer to lose data on an overflow not get further blockages. > > > Greg - can we get this into -next once -rc1 is out and see who screams ? Yes, I will do that. thanks, greg k-h ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-22 8:53 ` Felipe Balbi 2011-03-22 11:04 ` Alan Cox @ 2011-03-24 12:07 ` Toby Gray 2011-03-24 12:37 ` Felipe Balbi 1 sibling, 1 reply; 19+ messages in thread From: Toby Gray @ 2011-03-24 12:07 UTC (permalink / raw) To: balbi; +Cc: Alan Cox, Stefan Bigler, Greg KH, linux-kernel, linux-usb On 22/03/2011 08:53, Felipe Balbi wrote: > Patch attached, please give it a good round of test as I don't have how > to exercise all line disciplines. I ran 100 randconfigs over night and > no warnings or erros on that area, at least not that I could see (so > many warning while compiling the kernel :-( ) Is this patch missing the changes to tty_buffer.c that were in http://permalink.gmane.org/gmane.linux.usb.general/28976 Without the changes to tty_buffer.c to use the value returned from the receive_buf call then doesn't this patch not work correctly? Also with this patch, does the receive_room member of tty_struct have any use? As far as I can tell it's also referenced in paste_selection in drivers/tty/vt/selection.c. It's the case that past_selection uses receive_buf so shouldn't it be updated to use the new return value semantics for receive_buf? Without modifying tty_buffer.c to not make use of receive_room I can't get console terminals to work with this patch. Although I have to admit that I've been applying the patch to 2.6.35.3 as that's the kernel my development board is currently using, but I can't see any immediate reason why the most recent kernel would be any different. However I'm still fairly new to the interactions between the various bits of tty code and drivers, so I could just be missing an important change that's in 2.6.38+. Regards, Toby ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-24 12:07 ` Toby Gray @ 2011-03-24 12:37 ` Felipe Balbi 2011-03-24 12:51 ` Toby Gray 0 siblings, 1 reply; 19+ messages in thread From: Felipe Balbi @ 2011-03-24 12:37 UTC (permalink / raw) To: Toby Gray Cc: balbi, Alan Cox, Stefan Bigler, Greg KH, linux-kernel, linux-usb [-- Attachment #1: Type: text/plain, Size: 1685 bytes --] Hi, On Thu, Mar 24, 2011 at 12:07:56PM +0000, Toby Gray wrote: > On 22/03/2011 08:53, Felipe Balbi wrote: > >Patch attached, please give it a good round of test as I don't have how > >to exercise all line disciplines. I ran 100 randconfigs over night and > >no warnings or erros on that area, at least not that I could see (so > >many warning while compiling the kernel :-( ) > > Is this patch missing the changes to tty_buffer.c that were in > http://permalink.gmane.org/gmane.linux.usb.general/28976 > > Without the changes to tty_buffer.c to use the value returned from > the receive_buf call then doesn't this patch not work correctly? > > Also with this patch, does the receive_room member of tty_struct have > any use? As far as I can tell it's also referenced in paste_selection > in drivers/tty/vt/selection.c. It's the case that past_selection uses > receive_buf so shouldn't it be updated to use the new return value > semantics for receive_buf? > > Without modifying tty_buffer.c to not make use of receive_room I > can't get console terminals to work with this patch. Although I have > to admit that I've been applying the patch to 2.6.35.3 as that's the > kernel my development board is currently using, but I can't see any > immediate reason why the most recent kernel would be any different. > However I'm still fairly new to the interactions between the various > bits of tty code and drivers, so I could just be missing an important > change that's in 2.6.38+. you are right, thanks for noticing that. Attached is an updated patch. I left removal of receive_room out of this patch to prevent too invasive change, that can be done on a separate patch. -- balbi [-- Attachment #2: 0001-tty-make-receive_buf-return-the-amout-of-bytes-receiv.diff --] [-- Type: text/x-diff, Size: 26738 bytes --] >From 7f6e7b8bc6eed9ee1347d7555540801435a3cc43 Mon Sep 17 00:00:00 2001 From: Felipe Balbi <balbi@ti.com> Date: Mon, 21 Mar 2011 12:25:08 +0200 Subject: [PATCH] tty: make receive_buf() return the amout of bytes received Organization: Texas Instruments\n it makes it simpler to keep track of the amount of bytes received and simplifies how flush_to_ldisc counts the remaining bytes. It also fixes a bug of lost bytes on n_tty when flushing too many bytes via the USB serial gadget driver. Signed-off-by: Felipe Balbi <balbi@ti.com> --- drivers/bluetooth/hci_ldisc.c | 12 +++++-- drivers/input/serio/serport.c | 10 +++++- drivers/isdn/gigaset/ser-gigaset.c | 8 +++-- drivers/misc/ti-st/st_core.c | 6 ++- drivers/net/caif/caif_serial.c | 6 ++- drivers/net/can/slcan.c | 9 ++++-- drivers/net/hamradio/6pack.c | 8 +++-- drivers/net/hamradio/mkiss.c | 11 ++++-- drivers/net/irda/irtty-sir.c | 16 ++++++---- drivers/net/ppp_async.c | 6 ++- drivers/net/ppp_synctty.c | 6 ++- drivers/net/slip.c | 11 ++++-- drivers/net/wan/x25_asy.c | 7 +++- drivers/tty/n_gsm.c | 6 ++- drivers/tty/n_hdlc.c | 18 ++++++----- drivers/tty/n_r3964.c | 10 ++++-- drivers/tty/n_tty.c | 54 ++++++++--------------------------- drivers/tty/tty_buffer.c | 7 ++-- drivers/tty/vt/selection.c | 3 +- include/linux/tty_ldisc.h | 9 +++-- 20 files changed, 118 insertions(+), 105 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 3c6cabc..72c542b 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -357,22 +357,26 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) * * Return Value: None */ -static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) +static unsigned int 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; + return -ENODEV; if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) - return; + return -EINVAL; spin_lock(&hu->rx_lock); - hu->proto->recv(hu, (void *) data, count); + 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) diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 8755f5f..f369896 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -120,17 +120,21 @@ static void serport_ldisc_close(struct tty_struct *tty) * 'interrupt' routine. */ -static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +static unsigned int 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)) + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) { + ret = -EINVAL; goto out; + } for (i = 0; i < count; i++) { switch (fp[i]) { @@ -152,6 +156,8 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c out: spin_unlock_irqrestore(&serport->lock, flags); + + return ret == 0 ? count : ret; } /* diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 0ef09d0..14e57b4 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -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 void +static unsigned int gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -683,12 +683,12 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, struct inbuf_t *inbuf; if (!cs) - return; + return -ENODEV; inbuf = cs->inbuf; if (!inbuf) { dev_err(cs->dev, "%s: no inbuf\n", __func__); cs_put(cs); - return; + return -EINVAL; } tail = inbuf->tail; @@ -725,6 +725,8 @@ 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; } /* diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index f9aad06..5d23d89 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -866,8 +866,8 @@ static void st_tty_close(struct tty_struct *tty) pr_debug("%s: done ", __func__); } -static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, - char *tty_flags, int count) +static unsigned int st_tty_receive(struct tty_struct *tty, + const unsigned char *data, char *tty_flags, int count) { #ifdef VERBOSE @@ -881,6 +881,8 @@ static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, */ st_recv(tty->disc_data, data, count); pr_debug("done %s", __func__); + + return count; } /* wake-up function called in from the TTY layer diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 3df0c0f..73c7e03 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -167,8 +167,8 @@ static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) #endif -static void ldisc_receive(struct tty_struct *tty, const u8 *data, - char *flags, int count) +static unsigned int ldisc_receive(struct tty_struct *tty, + const u8 *data, char *flags, int count) { struct sk_buff *skb = NULL; struct ser_device *ser; @@ -215,6 +215,8 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data, } else ++ser->dev->stats.rx_dropped; update_tty_status(ser); + + return count; } static int handle_tx(struct ser_device *ser) diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index b423965..c600954 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -425,16 +425,17 @@ static void slc_setup(struct net_device *dev) * in parallel */ -static void slcan_receive_buf(struct tty_struct *tty, +static unsigned int 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; + return -ENODEV; /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -443,6 +444,8 @@ static void slcan_receive_buf(struct tty_struct *tty, } slcan_unesc(sl, *cp++); } + + return count; } /************************************ diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 3e5d0b6..9920896 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -456,7 +456,7 @@ out: * a block of 6pack data has been received, which can now be decapsulated * and sent on to some IP layer for further processing. */ -static void sixpack_receive_buf(struct tty_struct *tty, +static unsigned int sixpack_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct sixpack *sp; @@ -464,11 +464,11 @@ static void sixpack_receive_buf(struct tty_struct *tty, int count1; if (!count) - return; + return 0; sp = sp_get(tty); if (!sp) - return; + return -ENODEV; memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf)); @@ -487,6 +487,8 @@ static void sixpack_receive_buf(struct tty_struct *tty, sp_put(sp); tty_unthrottle(tty); + + return count1; } /* diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 4c62839..0e4f235 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -923,13 +923,14 @@ 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 void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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; + return -ENODEV; /* * Argh! mtu change time! - costs us the packet part received @@ -939,7 +940,7 @@ static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, ax_changedmtu(ax); /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp != NULL && *fp++) { if (!test_and_set_bit(AXF_ERROR, &ax->flags)) ax->dev->stats.rx_errors++; @@ -952,6 +953,8 @@ static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, mkiss_put(ax); tty_unthrottle(tty); + + return count; } /* diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index ee1dde5..b4e6480 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -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 void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); + IRDA_ASSERT(priv != NULL, return -ENODEV;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EINVAL;); if (unlikely(count==0)) /* yes, this happens */ - return; + return 0; dev = priv->dev; if (!dev) { IRDA_WARNING("%s(), not ready yet!\n", __func__); - return; + return -ENODEV; } for (i = 0; i < count; i++) { @@ -242,11 +242,13 @@ static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, if (fp && *fp++) { IRDA_DEBUG(0, "Framing or parity error!\n"); sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */ - return; + return -EINVAL; } } sirdev_receive(dev, cp, count); + + return count; } /* diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index a1b82c9..53872d7 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -340,7 +340,7 @@ 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 void +static unsigned int ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -348,7 +348,7 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, unsigned long flags; if (!ap) - return; + return -ENODEV; spin_lock_irqsave(&ap->recv_lock, flags); ppp_async_input(ap, buf, cflags, count); spin_unlock_irqrestore(&ap->recv_lock, flags); @@ -356,6 +356,8 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, tasklet_schedule(&ap->tsk); ap_put(ap); tty_unthrottle(tty); + + return count; } static void diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 4e6b72f..01122cd 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -381,7 +381,7 @@ 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 void +static unsigned int ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -389,7 +389,7 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, unsigned long flags; if (!ap) - return; + return -ENODEV; spin_lock_irqsave(&ap->recv_lock, flags); ppp_sync_input(ap, buf, cflags, count); spin_unlock_irqrestore(&ap->recv_lock, flags); @@ -397,6 +397,8 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, tasklet_schedule(&ap->tsk); sp_put(ap); tty_unthrottle(tty); + + return count; } static void diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 86cbb9e..86718d3 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -670,16 +670,17 @@ static void sl_setup(struct net_device *dev) * in parallel */ -static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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; + return -ENODEV; /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -693,6 +694,8 @@ static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, #endif slip_unesc(sl, *cp++); } + + return count; } /************************************ diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 24297b2..40398bf 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -517,17 +517,18 @@ static int x25_asy_close(struct net_device *dev) * and sent on to some IP layer for further processing. */ -static void x25_asy_receive_buf(struct tty_struct *tty, +static unsigned int 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 (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -536,6 +537,8 @@ static void x25_asy_receive_buf(struct tty_struct *tty, } x25_asy_unesc(sl, *cp++); } + + return count; } /* diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index aa2e5d3..a606331 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2139,8 +2139,8 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) gsm->tty = NULL; } -static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int gsmld_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct gsm_mux *gsm = tty->disc_data; const unsigned char *dp; @@ -2174,6 +2174,8 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, } /* FASYNC if needed ? */ /* If clogged call tty_throttle(tty); */ + + return count; } /** diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 52fc0c9..517e376 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -189,8 +189,8 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait); static int n_hdlc_tty_open(struct tty_struct *tty); static void n_hdlc_tty_close(struct tty_struct *tty); -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp, - char *fp, int count); +static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 *cp, char *fp, int count); static void n_hdlc_tty_wakeup(struct tty_struct *tty); #define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) @@ -510,8 +510,8 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty) * Called by tty low level driver when receive data is available. Data is * interpreted as one HDLC frame. */ -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, - char *flags, int count) +static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 *data, char *flags, int count) { register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); register struct n_hdlc_buf *buf; @@ -522,20 +522,20 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, /* This can happen if stuff comes in on the backup tty */ if (!n_hdlc || tty != n_hdlc->tty) - return; + return -ENODEV; /* verify line is using HDLC discipline */ if (n_hdlc->magic != HDLC_MAGIC) { printk("%s(%d) line not using HDLC discipline\n", __FILE__,__LINE__); - return; + return -EINVAL; } if ( count>maxframe ) { if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) rx count>maxframesize, data discarded\n", __FILE__,__LINE__); - return; + return -EINVAL; } /* get a free HDLC buffer */ @@ -551,7 +551,7 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) no more rx buffers, data discarded\n", __FILE__,__LINE__); - return; + return -EINVAL; } /* copy received data to HDLC buffer */ @@ -566,6 +566,8 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, if (n_hdlc->tty->fasync != NULL) kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN); + return count; + } /* end of n_hdlc_tty_receive() */ /** diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 88dda0c..ea725c5 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -140,8 +140,8 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file, static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old); static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait); -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count); +static unsigned int r3964_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count); static struct tty_ldisc_ops tty_ldisc_N_R3964 = { .owner = THIS_MODULE, @@ -1240,8 +1240,8 @@ static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, return result; } -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int r3964_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct r3964_info *pInfo = tty->disc_data; const unsigned char *p; @@ -1258,6 +1258,8 @@ static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, } } + + return count; } MODULE_LICENSE("GPL"); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 428f4fe..8e0f113 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -81,32 +81,6 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set__room - receive space - * @tty: terminal - * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". - */ - -static void n_tty_set_room(struct tty_struct *tty) -{ - /* tty->read_cnt is not read locked ? */ - int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; - - /* - * If we are doing input canonicalization, and there are no - * pending newlines, let characters through without limit, so - * that erase characters will be handled. Other excess - * characters will be beeped. - */ - if (left <= 0) - left = tty->icanon && !tty->canon_data; - tty->receive_room = left; -} - static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) { if (tty->read_cnt < N_TTY_BUF_SIZE) { @@ -178,7 +152,6 @@ static void reset_buffer_flags(struct tty_struct *tty) tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); - n_tty_set_room(tty); check_unthrottle(tty); } @@ -1354,17 +1327,19 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * calls one at a time and in order (or using flush_to_ldisc) */ -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int n_tty_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { const unsigned char *p; char *f, flags = TTY_NORMAL; int i; char buf[64]; unsigned long cpuflags; + int left; + int ret = 0; if (!tty->read_buf) - return; + return 0; if (tty->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); @@ -1374,6 +1349,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; + ret += i; cp += i; count -= i; @@ -1385,6 +1361,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->read_cnt += i; spin_unlock_irqrestore(&tty->read_lock, cpuflags); } else { + ret = count; for (i = count, p = cp, f = fp; i; i--, p++) { if (f) flags = *f++; @@ -1412,8 +1389,6 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->ops->flush_chars(tty); } - n_tty_set_room(tty); - if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); @@ -1426,8 +1401,12 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) + left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + + if (left < TTY_THRESHOLD_THROTTLE) tty_throttle(tty); + + return ret; } int is_ignored(int sig) @@ -1471,7 +1450,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; tty->real_raw = 1; - n_tty_set_room(tty); return; } if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || @@ -1524,7 +1502,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) else tty->real_raw = 0; } - n_tty_set_room(tty); /* The termios change make the tty ready for I/O */ wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); @@ -1806,8 +1783,6 @@ do_it_again: retval = -ERESTARTSYS; break; } - /* FIXME: does n_tty_set_room need locking ? */ - n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; } @@ -1879,10 +1854,8 @@ do_it_again: * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { - n_tty_set_room(tty); + if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) check_unthrottle(tty); - } if (b - buf >= minimum) break; @@ -1904,7 +1877,6 @@ do_it_again: } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; - n_tty_set_room(tty); return retval; } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index d8210ca..2f119e2 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -416,6 +416,7 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_buffer *head, *tail = tty->buf.tail; int seen_tail = 0; while ((head = tty->buf.head) != NULL) { + int copied; int count; char *char_buf; unsigned char *flag_buf; @@ -446,15 +447,13 @@ static void flush_to_ldisc(struct work_struct *work) schedule_delayed_work(&tty->buf.work, 1); break; } - if (count > tty->receive_room) - count = tty->receive_room; char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; - head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->ops->receive_buf(tty, char_buf, + copied = disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); + head->read += copied; } clear_bit(TTY_FLUSHING, &tty->flags); } diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index c956ed6..c912cb9 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -335,8 +335,7 @@ int paste_selection(struct tty_struct *tty) continue; } count = sel_buffer_lth - pasted; - count = min(count, tty->receive_room); - tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, + count = tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); pasted += count; } diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index ff7dc08..5b07792 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -76,7 +76,7 @@ * tty device. It is solely the responsibility of the line * discipline to handle poll requests. * - * void (*receive_buf)(struct tty_struct *, const unsigned char *cp, + * unsigned int (*receive_buf)(struct tty_struct *, const unsigned char *cp, * char *fp, int count); * * This function is called by the low-level tty driver to send @@ -84,7 +84,8 @@ * processing. <cp> is a pointer to the buffer of input * character received by the device. <fp> is a pointer to a * pointer of flag bytes which indicate whether a character was - * received with a parity error, etc. + * received with a parity error, etc. Returns the amount of bytes + * received. * * void (*write_wakeup)(struct tty_struct *); * @@ -140,8 +141,8 @@ struct tty_ldisc_ops { /* * The following routines are called from below. */ - void (*receive_buf)(struct tty_struct *, const unsigned char *cp, - char *fp, int count); + unsigned int (*receive_buf)(struct tty_struct *, + const unsigned char *cp, char *fp, int count); void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int, struct pps_event_time *); -- 1.7.4.1.343.ga91df ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-24 12:37 ` Felipe Balbi @ 2011-03-24 12:51 ` Toby Gray 2011-03-24 13:00 ` Felipe Balbi 0 siblings, 1 reply; 19+ messages in thread From: Toby Gray @ 2011-03-24 12:51 UTC (permalink / raw) To: balbi; +Cc: Alan Cox, Stefan Bigler, Greg KH, linux-kernel, linux-usb On 24/03/2011 12:37, Felipe Balbi wrote: > Hi, > > On Thu, Mar 24, 2011 at 12:07:56PM +0000, Toby Gray wrote: >> Is this patch missing the changes to tty_buffer.c that were in >> http://permalink.gmane.org/gmane.linux.usb.general/28976 >> >> Without the changes to tty_buffer.c to use the value returned from >> the receive_buf call then doesn't this patch not work correctly? >> <snip> > you are right, thanks for noticing that. Attached is an updated patch. > I left removal of receive_room out of this patch to prevent too invasive > change, that can be done on a separate patch. I've just tried removing receive_room myself and noticed that it is still used in flush_to_ldisc to decide if it needs to schedule work to be done later: if (!tty->receive_room || seen_tail) { schedule_work(&tty->buf.work); break; } If receive_room is no longer being updated then isn't this the wrong thing to do? Shouldn't it check if some data was copied after calling receive_buf, and if there wasn't any then it should schedule the work and break? The other query I've got is about the return value from receive_buf. I noticed that you've modified some drivers (such as bluetooth/hci_ldisc.c) so that they can return error values, such as -ENODEV. Won't this cause things to go wrong when flush_to_ldisc and paste_selection use the return value without checking for it being negative? Thank you for your quick reply to my first query, it's appreciated. Regards, Toby ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-24 12:51 ` Toby Gray @ 2011-03-24 13:00 ` Felipe Balbi 2011-03-24 15:40 ` Stefan Bigler 0 siblings, 1 reply; 19+ messages in thread From: Felipe Balbi @ 2011-03-24 13:00 UTC (permalink / raw) To: Toby Gray Cc: balbi, Alan Cox, Stefan Bigler, Greg KH, linux-kernel, linux-usb On Thu, Mar 24, 2011 at 12:51:45PM +0000, Toby Gray wrote: > On 24/03/2011 12:37, Felipe Balbi wrote: > >Hi, > > > >On Thu, Mar 24, 2011 at 12:07:56PM +0000, Toby Gray wrote: > >>Is this patch missing the changes to tty_buffer.c that were in > >>http://permalink.gmane.org/gmane.linux.usb.general/28976 > >> > >>Without the changes to tty_buffer.c to use the value returned from > >>the receive_buf call then doesn't this patch not work correctly? > >> > <snip> > >you are right, thanks for noticing that. Attached is an updated patch. > >I left removal of receive_room out of this patch to prevent too invasive > >change, that can be done on a separate patch. > > I've just tried removing receive_room myself and noticed that it is > still used in flush_to_ldisc to decide if it needs to schedule work > to be done later: > > if (!tty->receive_room || seen_tail) { > schedule_work(&tty->buf.work); > break; > } > > If receive_room is no longer being updated then isn't this the wrong > thing to do? Shouldn't it check if some data was copied after calling > receive_buf, and if there wasn't any then it should schedule the work > and break? > > The other query I've got is about the return value from receive_buf. > I noticed that you've modified some drivers (such as > bluetooth/hci_ldisc.c) so that they can return error values, such as > -ENODEV. Won't this cause things to go wrong when flush_to_ldisc and > paste_selection use the return value without checking for it being > negative? > > Thank you for your quick reply to my first query, it's appreciated. Can you try this: diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 2f119e2..f0b9fb6 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -443,17 +443,19 @@ static void flush_to_ldisc(struct work_struct *work) line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; - if (!tty->receive_room || seen_tail) { - schedule_delayed_work(&tty->buf.work, 1); - break; - } char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; spin_unlock_irqrestore(&tty->buf.lock, flags); copied = disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); + head->read += copied; + + if (copied == 0 || seen_tail) { + schedule_delayed_work(&tty->buf.work, 1); + break; + } } clear_bit(TTY_FLUSHING, &tty->flags); } if I read the code correctly, when we have no space to receive bytes, then we schedule work, that should be the same. -- balbi ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-24 13:00 ` Felipe Balbi @ 2011-03-24 15:40 ` Stefan Bigler 2011-03-24 16:15 ` Toby Gray 0 siblings, 1 reply; 19+ messages in thread From: Stefan Bigler @ 2011-03-24 15:40 UTC (permalink / raw) To: balbi; +Cc: Toby Gray, Alan Cox, Greg KH, linux-kernel, linux-usb Can you try this: > diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c > index 2f119e2..f0b9fb6 100644 > --- a/drivers/tty/tty_buffer.c > +++ b/drivers/tty/tty_buffer.c I tried it and the console is working again. There is still an problem on heavy load transfer from host to gadget. I attached the patch to fix also this problem. diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 8e0f113..95d0a9c 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1359,6 +1359,7 @@ static unsigned int n_tty_receive_buf(struct tty_struct *tty, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; + ret += i; spin_unlock_irqrestore(&tty->read_lock, cpuflags); } else { ret = count; With this patch I was able to transfer gigabytes without lost data. ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-24 15:40 ` Stefan Bigler @ 2011-03-24 16:15 ` Toby Gray 2011-03-25 11:02 ` Felipe Balbi 0 siblings, 1 reply; 19+ messages in thread From: Toby Gray @ 2011-03-24 16:15 UTC (permalink / raw) To: stefan.bigler; +Cc: balbi, Alan Cox, Greg KH, linux-kernel, linux-usb On 24/03/2011 15:40, Stefan Bigler wrote: > Can you try this: >> diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c >> index 2f119e2..f0b9fb6 100644 >> --- a/drivers/tty/tty_buffer.c >> +++ b/drivers/tty/tty_buffer.c > > I tried it and the console is working again. > There is still an problem on heavy load transfer from host to gadget. > I attached the patch to fix also this problem. > > diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c > index 8e0f113..95d0a9c 100644 > --- a/drivers/tty/n_tty.c > +++ b/drivers/tty/n_tty.c > @@ -1359,6 +1359,7 @@ static unsigned int n_tty_receive_buf(struct > tty_struct *tty, > memcpy(tty->read_buf + tty->read_head, cp, i); > tty->read_head = (tty->read_head + i) & > (N_TTY_BUF_SIZE-1); > tty->read_cnt += i; > + ret += i; > spin_unlock_irqrestore(&tty->read_lock, cpuflags); > } else { > ret = count; > > With this patch I was able to transfer gigabytes without lost data. I can confirm that this combined with the last two patches from Felipe Balbi fix all the issues I've had as well and seems stable and without loss under fast data transfers. Regards, Toby ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-24 16:15 ` Toby Gray @ 2011-03-25 11:02 ` Felipe Balbi 0 siblings, 0 replies; 19+ messages in thread From: Felipe Balbi @ 2011-03-25 11:02 UTC (permalink / raw) To: Toby Gray Cc: stefan.bigler, balbi, Alan Cox, Greg KH, linux-kernel, linux-usb [-- Attachment #1: Type: text/plain, Size: 1393 bytes --] On Thu, Mar 24, 2011 at 04:15:02PM +0000, Toby Gray wrote: > On 24/03/2011 15:40, Stefan Bigler wrote: > >Can you try this: > >>diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c > >>index 2f119e2..f0b9fb6 100644 > >>--- a/drivers/tty/tty_buffer.c > >>+++ b/drivers/tty/tty_buffer.c > > > >I tried it and the console is working again. > >There is still an problem on heavy load transfer from host to gadget. > >I attached the patch to fix also this problem. > > > >diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c > >index 8e0f113..95d0a9c 100644 > >--- a/drivers/tty/n_tty.c > >+++ b/drivers/tty/n_tty.c > >@@ -1359,6 +1359,7 @@ static unsigned int n_tty_receive_buf(struct > >tty_struct *tty, > > memcpy(tty->read_buf + tty->read_head, cp, i); > > tty->read_head = (tty->read_head + i) & > >(N_TTY_BUF_SIZE-1); > > tty->read_cnt += i; > >+ ret += i; > > spin_unlock_irqrestore(&tty->read_lock, cpuflags); > > } else { > > ret = count; > > > >With this patch I was able to transfer gigabytes without lost data. > > I can confirm that this combined with the last two patches from > Felipe Balbi fix all the issues I've had as well and seems stable and > without loss under fast data transfers. Great guys, thanks a lot. Here's the final patch with your Tested-bys -- balbi [-- Attachment #2: 0001-tty-make-receive_buf-return-the-amout-of-bytes-receiv.diff --] [-- Type: text/x-diff, Size: 27235 bytes --] >From 706cb57d502b6c04b865f3a822ae1369e1e55dfa Mon Sep 17 00:00:00 2001 From: Felipe Balbi <balbi@ti.com> Date: Mon, 21 Mar 2011 12:25:08 +0200 Subject: [PATCH] tty: make receive_buf() return the amout of bytes received Organization: Texas Instruments\n it makes it simpler to keep track of the amount of bytes received and simplifies how flush_to_ldisc counts the remaining bytes. It also fixes a bug of lost bytes on n_tty when flushing too many bytes via the USB serial gadget driver. Tested-by: Stefan Bigler <stefan.bigler@keymile.com> Tested-by: Toby Gray <toby.gray@realvnc.com> Signed-off-by: Felipe Balbi <balbi@ti.com> --- drivers/bluetooth/hci_ldisc.c | 12 +++++-- drivers/input/serio/serport.c | 10 +++++- drivers/isdn/gigaset/ser-gigaset.c | 8 +++-- drivers/misc/ti-st/st_core.c | 6 ++- drivers/net/caif/caif_serial.c | 6 ++- drivers/net/can/slcan.c | 9 ++++-- drivers/net/hamradio/6pack.c | 8 +++-- drivers/net/hamradio/mkiss.c | 11 ++++-- drivers/net/irda/irtty-sir.c | 16 ++++++---- drivers/net/ppp_async.c | 6 ++- drivers/net/ppp_synctty.c | 6 ++- drivers/net/slip.c | 11 ++++-- drivers/net/wan/x25_asy.c | 7 +++- drivers/tty/n_gsm.c | 6 ++- drivers/tty/n_hdlc.c | 18 ++++++----- drivers/tty/n_r3964.c | 10 ++++-- drivers/tty/n_tty.c | 55 +++++++++-------------------------- drivers/tty/tty_buffer.c | 17 ++++++----- drivers/tty/vt/selection.c | 3 +- include/linux/tty_ldisc.h | 9 +++-- 20 files changed, 125 insertions(+), 109 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 3c6cabc..72c542b 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -357,22 +357,26 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) * * Return Value: None */ -static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) +static unsigned int 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; + return -ENODEV; if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) - return; + return -EINVAL; spin_lock(&hu->rx_lock); - hu->proto->recv(hu, (void *) data, count); + 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) diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 8755f5f..f369896 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -120,17 +120,21 @@ static void serport_ldisc_close(struct tty_struct *tty) * 'interrupt' routine. */ -static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +static unsigned int 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)) + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) { + ret = -EINVAL; goto out; + } for (i = 0; i < count; i++) { switch (fp[i]) { @@ -152,6 +156,8 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c out: spin_unlock_irqrestore(&serport->lock, flags); + + return ret == 0 ? count : ret; } /* diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 0ef09d0..14e57b4 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -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 void +static unsigned int gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -683,12 +683,12 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, struct inbuf_t *inbuf; if (!cs) - return; + return -ENODEV; inbuf = cs->inbuf; if (!inbuf) { dev_err(cs->dev, "%s: no inbuf\n", __func__); cs_put(cs); - return; + return -EINVAL; } tail = inbuf->tail; @@ -725,6 +725,8 @@ 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; } /* diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index f9aad06..5d23d89 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -866,8 +866,8 @@ static void st_tty_close(struct tty_struct *tty) pr_debug("%s: done ", __func__); } -static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, - char *tty_flags, int count) +static unsigned int st_tty_receive(struct tty_struct *tty, + const unsigned char *data, char *tty_flags, int count) { #ifdef VERBOSE @@ -881,6 +881,8 @@ static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, */ st_recv(tty->disc_data, data, count); pr_debug("done %s", __func__); + + return count; } /* wake-up function called in from the TTY layer diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 3df0c0f..73c7e03 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -167,8 +167,8 @@ static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) #endif -static void ldisc_receive(struct tty_struct *tty, const u8 *data, - char *flags, int count) +static unsigned int ldisc_receive(struct tty_struct *tty, + const u8 *data, char *flags, int count) { struct sk_buff *skb = NULL; struct ser_device *ser; @@ -215,6 +215,8 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data, } else ++ser->dev->stats.rx_dropped; update_tty_status(ser); + + return count; } static int handle_tx(struct ser_device *ser) diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index b423965..c600954 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -425,16 +425,17 @@ static void slc_setup(struct net_device *dev) * in parallel */ -static void slcan_receive_buf(struct tty_struct *tty, +static unsigned int 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; + return -ENODEV; /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -443,6 +444,8 @@ static void slcan_receive_buf(struct tty_struct *tty, } slcan_unesc(sl, *cp++); } + + return count; } /************************************ diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 3e5d0b6..9920896 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -456,7 +456,7 @@ out: * a block of 6pack data has been received, which can now be decapsulated * and sent on to some IP layer for further processing. */ -static void sixpack_receive_buf(struct tty_struct *tty, +static unsigned int sixpack_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct sixpack *sp; @@ -464,11 +464,11 @@ static void sixpack_receive_buf(struct tty_struct *tty, int count1; if (!count) - return; + return 0; sp = sp_get(tty); if (!sp) - return; + return -ENODEV; memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf)); @@ -487,6 +487,8 @@ static void sixpack_receive_buf(struct tty_struct *tty, sp_put(sp); tty_unthrottle(tty); + + return count1; } /* diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 4c62839..0e4f235 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -923,13 +923,14 @@ 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 void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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; + return -ENODEV; /* * Argh! mtu change time! - costs us the packet part received @@ -939,7 +940,7 @@ static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, ax_changedmtu(ax); /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp != NULL && *fp++) { if (!test_and_set_bit(AXF_ERROR, &ax->flags)) ax->dev->stats.rx_errors++; @@ -952,6 +953,8 @@ static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, mkiss_put(ax); tty_unthrottle(tty); + + return count; } /* diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index ee1dde5..b4e6480 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -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 void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); + IRDA_ASSERT(priv != NULL, return -ENODEV;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EINVAL;); if (unlikely(count==0)) /* yes, this happens */ - return; + return 0; dev = priv->dev; if (!dev) { IRDA_WARNING("%s(), not ready yet!\n", __func__); - return; + return -ENODEV; } for (i = 0; i < count; i++) { @@ -242,11 +242,13 @@ static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, if (fp && *fp++) { IRDA_DEBUG(0, "Framing or parity error!\n"); sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */ - return; + return -EINVAL; } } sirdev_receive(dev, cp, count); + + return count; } /* diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index a1b82c9..53872d7 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -340,7 +340,7 @@ 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 void +static unsigned int ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -348,7 +348,7 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, unsigned long flags; if (!ap) - return; + return -ENODEV; spin_lock_irqsave(&ap->recv_lock, flags); ppp_async_input(ap, buf, cflags, count); spin_unlock_irqrestore(&ap->recv_lock, flags); @@ -356,6 +356,8 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, tasklet_schedule(&ap->tsk); ap_put(ap); tty_unthrottle(tty); + + return count; } static void diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 4e6b72f..01122cd 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -381,7 +381,7 @@ 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 void +static unsigned int ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -389,7 +389,7 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, unsigned long flags; if (!ap) - return; + return -ENODEV; spin_lock_irqsave(&ap->recv_lock, flags); ppp_sync_input(ap, buf, cflags, count); spin_unlock_irqrestore(&ap->recv_lock, flags); @@ -397,6 +397,8 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, tasklet_schedule(&ap->tsk); sp_put(ap); tty_unthrottle(tty); + + return count; } static void diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 86cbb9e..86718d3 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -670,16 +670,17 @@ static void sl_setup(struct net_device *dev) * in parallel */ -static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int 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; + return -ENODEV; /* Read the characters out of the buffer */ - while (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -693,6 +694,8 @@ static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, #endif slip_unesc(sl, *cp++); } + + return count; } /************************************ diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 24297b2..40398bf 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -517,17 +517,18 @@ static int x25_asy_close(struct net_device *dev) * and sent on to some IP layer for further processing. */ -static void x25_asy_receive_buf(struct tty_struct *tty, +static unsigned int 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 (count--) { + while (bytes--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -536,6 +537,8 @@ static void x25_asy_receive_buf(struct tty_struct *tty, } x25_asy_unesc(sl, *cp++); } + + return count; } /* diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index aa2e5d3..a606331 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2139,8 +2139,8 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) gsm->tty = NULL; } -static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int gsmld_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct gsm_mux *gsm = tty->disc_data; const unsigned char *dp; @@ -2174,6 +2174,8 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, } /* FASYNC if needed ? */ /* If clogged call tty_throttle(tty); */ + + return count; } /** diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 52fc0c9..517e376 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -189,8 +189,8 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait); static int n_hdlc_tty_open(struct tty_struct *tty); static void n_hdlc_tty_close(struct tty_struct *tty); -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp, - char *fp, int count); +static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 *cp, char *fp, int count); static void n_hdlc_tty_wakeup(struct tty_struct *tty); #define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) @@ -510,8 +510,8 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty) * Called by tty low level driver when receive data is available. Data is * interpreted as one HDLC frame. */ -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, - char *flags, int count) +static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 *data, char *flags, int count) { register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); register struct n_hdlc_buf *buf; @@ -522,20 +522,20 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, /* This can happen if stuff comes in on the backup tty */ if (!n_hdlc || tty != n_hdlc->tty) - return; + return -ENODEV; /* verify line is using HDLC discipline */ if (n_hdlc->magic != HDLC_MAGIC) { printk("%s(%d) line not using HDLC discipline\n", __FILE__,__LINE__); - return; + return -EINVAL; } if ( count>maxframe ) { if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) rx count>maxframesize, data discarded\n", __FILE__,__LINE__); - return; + return -EINVAL; } /* get a free HDLC buffer */ @@ -551,7 +551,7 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) no more rx buffers, data discarded\n", __FILE__,__LINE__); - return; + return -EINVAL; } /* copy received data to HDLC buffer */ @@ -566,6 +566,8 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, if (n_hdlc->tty->fasync != NULL) kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN); + return count; + } /* end of n_hdlc_tty_receive() */ /** diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 88dda0c..ea725c5 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -140,8 +140,8 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file, static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old); static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait); -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count); +static unsigned int r3964_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count); static struct tty_ldisc_ops tty_ldisc_N_R3964 = { .owner = THIS_MODULE, @@ -1240,8 +1240,8 @@ static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, return result; } -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int r3964_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct r3964_info *pInfo = tty->disc_data; const unsigned char *p; @@ -1258,6 +1258,8 @@ static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, } } + + return count; } MODULE_LICENSE("GPL"); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 428f4fe..95d0a9c 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -81,32 +81,6 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set__room - receive space - * @tty: terminal - * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". - */ - -static void n_tty_set_room(struct tty_struct *tty) -{ - /* tty->read_cnt is not read locked ? */ - int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; - - /* - * If we are doing input canonicalization, and there are no - * pending newlines, let characters through without limit, so - * that erase characters will be handled. Other excess - * characters will be beeped. - */ - if (left <= 0) - left = tty->icanon && !tty->canon_data; - tty->receive_room = left; -} - static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) { if (tty->read_cnt < N_TTY_BUF_SIZE) { @@ -178,7 +152,6 @@ static void reset_buffer_flags(struct tty_struct *tty) tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); - n_tty_set_room(tty); check_unthrottle(tty); } @@ -1354,17 +1327,19 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * calls one at a time and in order (or using flush_to_ldisc) */ -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int n_tty_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { const unsigned char *p; char *f, flags = TTY_NORMAL; int i; char buf[64]; unsigned long cpuflags; + int left; + int ret = 0; if (!tty->read_buf) - return; + return 0; if (tty->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); @@ -1374,6 +1349,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; + ret += i; cp += i; count -= i; @@ -1383,8 +1359,10 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; + ret += i; spin_unlock_irqrestore(&tty->read_lock, cpuflags); } else { + ret = count; for (i = count, p = cp, f = fp; i; i--, p++) { if (f) flags = *f++; @@ -1412,8 +1390,6 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->ops->flush_chars(tty); } - n_tty_set_room(tty); - if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); @@ -1426,8 +1402,12 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) + left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + + if (left < TTY_THRESHOLD_THROTTLE) tty_throttle(tty); + + return ret; } int is_ignored(int sig) @@ -1471,7 +1451,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; tty->real_raw = 1; - n_tty_set_room(tty); return; } if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || @@ -1524,7 +1503,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) else tty->real_raw = 0; } - n_tty_set_room(tty); /* The termios change make the tty ready for I/O */ wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); @@ -1806,8 +1784,6 @@ do_it_again: retval = -ERESTARTSYS; break; } - /* FIXME: does n_tty_set_room need locking ? */ - n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; } @@ -1879,10 +1855,8 @@ do_it_again: * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { - n_tty_set_room(tty); + if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) check_unthrottle(tty); - } if (b - buf >= minimum) break; @@ -1904,7 +1878,6 @@ do_it_again: } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; - n_tty_set_room(tty); return retval; } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index d8210ca..f0b9fb6 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -416,6 +416,7 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_buffer *head, *tail = tty->buf.tail; int seen_tail = 0; while ((head = tty->buf.head) != NULL) { + int copied; int count; char *char_buf; unsigned char *flag_buf; @@ -442,19 +443,19 @@ static void flush_to_ldisc(struct work_struct *work) line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; - if (!tty->receive_room || seen_tail) { - schedule_delayed_work(&tty->buf.work, 1); - break; - } - if (count > tty->receive_room) - count = tty->receive_room; char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; - head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->ops->receive_buf(tty, char_buf, + copied = disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); + + head->read += copied; + + if (copied == 0 || seen_tail) { + schedule_delayed_work(&tty->buf.work, 1); + break; + } } clear_bit(TTY_FLUSHING, &tty->flags); } diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index c956ed6..c912cb9 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -335,8 +335,7 @@ int paste_selection(struct tty_struct *tty) continue; } count = sel_buffer_lth - pasted; - count = min(count, tty->receive_room); - tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, + count = tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); pasted += count; } diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index ff7dc08..5b07792 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -76,7 +76,7 @@ * tty device. It is solely the responsibility of the line * discipline to handle poll requests. * - * void (*receive_buf)(struct tty_struct *, const unsigned char *cp, + * unsigned int (*receive_buf)(struct tty_struct *, const unsigned char *cp, * char *fp, int count); * * This function is called by the low-level tty driver to send @@ -84,7 +84,8 @@ * processing. <cp> is a pointer to the buffer of input * character received by the device. <fp> is a pointer to a * pointer of flag bytes which indicate whether a character was - * received with a parity error, etc. + * received with a parity error, etc. Returns the amount of bytes + * received. * * void (*write_wakeup)(struct tty_struct *); * @@ -140,8 +141,8 @@ struct tty_ldisc_ops { /* * The following routines are called from below. */ - void (*receive_buf)(struct tty_struct *, const unsigned char *cp, - char *fp, int count); + unsigned int (*receive_buf)(struct tty_struct *, + const unsigned char *cp, char *fp, int count); void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int, struct pps_event_time *); -- 1.7.4.1.343.ga91df ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-18 17:08 ` Felipe Balbi 2011-03-18 18:06 ` Alan Cox @ 2011-03-18 21:46 ` Stefan Bigler 1 sibling, 0 replies; 19+ messages in thread From: Stefan Bigler @ 2011-03-18 21:46 UTC (permalink / raw) To: balbi; +Cc: Greg KH, linux-kernel, linux-usb Hi Returning the size of queued data is a reasonable solution for this problem. Stefan ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-18 16:35 ` Stefan Bigler 2011-03-18 17:08 ` Felipe Balbi @ 2011-03-18 18:07 ` Alan Cox 2011-03-18 21:15 ` Stefan Bigler 1 sibling, 1 reply; 19+ messages in thread From: Alan Cox @ 2011-03-18 18:07 UTC (permalink / raw) To: stefan.bigler; +Cc: Greg KH, linux-kernel, linux-usb > I had also a look at the relevant fixes, a lot is done but I could not find > the required protection of the attribute receive_room. receive_room isn't protected because it may only be shrunk by the amount of data sent to the ldisc or less. The ldisc is at liberty to grow the value as it sees fit. In essence if you get a value from receive_room it's a guarantee you may send that many bytes, it is not a precise instantaneous perfect answer to the question "exactly what number of bytes could fit at this precise moment". Which does of course mean you should never see the case where receive_room is bigger than the actual space available in tty raw mode. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: TTY loosing data with u_serial gadget 2011-03-18 18:07 ` Alan Cox @ 2011-03-18 21:15 ` Stefan Bigler 0 siblings, 0 replies; 19+ messages in thread From: Stefan Bigler @ 2011-03-18 21:15 UTC (permalink / raw) To: Alan Cox; +Cc: Greg KH, linux-kernel, linux-usb > In essence if you get a value from receive_room it's a guarantee you may > send that many bytes, it is not a precise instantaneous perfect answer to > the question "exactly what number of bytes could fit at this precise > moment". > Hi I have no problem with having more space available than guarantied by the value of receive_room. But what I see is that the value can be less! Data will be lost. My test showed: data to queue=201 receive_room (guarantied space) =287 real space in queue =0 In the last mail I attached the code for printing, the read_lock was only added to show a constistant view of all current values. Stefan ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2011-03-25 11:02 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-03-16 20:47 TTY loosing data with u_serial gadget Stefan Bigler 2011-03-17 0:04 ` Greg KH 2011-03-18 16:35 ` Stefan Bigler 2011-03-18 17:08 ` Felipe Balbi 2011-03-18 18:06 ` Alan Cox 2011-03-21 9:32 ` Felipe Balbi 2011-03-22 8:53 ` Felipe Balbi 2011-03-22 11:04 ` Alan Cox 2011-03-22 17:01 ` Greg KH 2011-03-24 12:07 ` Toby Gray 2011-03-24 12:37 ` Felipe Balbi 2011-03-24 12:51 ` Toby Gray 2011-03-24 13:00 ` Felipe Balbi 2011-03-24 15:40 ` Stefan Bigler 2011-03-24 16:15 ` Toby Gray 2011-03-25 11:02 ` Felipe Balbi 2011-03-18 21:46 ` Stefan Bigler 2011-03-18 18:07 ` Alan Cox 2011-03-18 21:15 ` Stefan Bigler
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox