diff mbox

[2/2] hso: fix deadlock when receiving bursts of data

Message ID 1404723967-24245-2-git-send-email-olivier@sobrie.be
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Olivier Sobrie July 7, 2014, 9:06 a.m. UTC
When the module sends bursts of data, sometimes a deadlock happens in
the hso driver when the tty buffer doesn't get the chance to be flushed
quickly enough.

To avoid this, first, we remove the endless while loop in
put_rx_bufdata() which is the root cause of the deadlock.
Secondly, when there is no room anymore in the tty buffer, we set up a
timer of 100 msecs to give a chance to the upper layer to flush the tty
buffer and make room for new data.

Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
 drivers/net/usb/hso.c |   51 +++++++++++++++++++++++++++++++++----------------
 1 file changed, 35 insertions(+), 16 deletions(-)

Comments

David Laight July 7, 2014, 9:13 a.m. UTC | #1
From: Olivier Sobrie
> When the module sends bursts of data, sometimes a deadlock happens in
> the hso driver when the tty buffer doesn't get the chance to be flushed
> quickly enough.
> 
> To avoid this, first, we remove the endless while loop in
> put_rx_bufdata() which is the root cause of the deadlock.
> Secondly, when there is no room anymore in the tty buffer, we set up a
> timer of 100 msecs to give a chance to the upper layer to flush the tty
> buffer and make room for new data.

What is the timer for?
You need to get the sending code woken up by the urb completion.

	David



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Olivier Sobrie July 7, 2014, 10:42 a.m. UTC | #2
Hi David,

On Mon, Jul 07, 2014 at 09:13:53AM +0000, David Laight wrote:
> From: Olivier Sobrie
> > When the module sends bursts of data, sometimes a deadlock happens in
> > the hso driver when the tty buffer doesn't get the chance to be flushed
> > quickly enough.
> > 
> > To avoid this, first, we remove the endless while loop in
> > put_rx_bufdata() which is the root cause of the deadlock.
> > Secondly, when there is no room anymore in the tty buffer, we set up a
> > timer of 100 msecs to give a chance to the upper layer to flush the tty
> > buffer and make room for new data.
> 
> What is the timer for?
> You need to get the sending code woken up by the urb completion.

In put_rxbuf_data() (which can be called under irq disabled),
tty_flip_buffer_push() is called and schedules a push of the tty buffer.
When the buffer is full, I give some time to the above layer in order
to flush it.
The timer is used to recall put_rxbuf_data_and_resubmit_bulk_urb()
later in order to read the remaining data stored in
"urb->transfer_buffer" and then to resubmit the urb to receive more data
from the gsm module.
I don't understand what you mean by "getting the sending code woken up".
Calling tty_port_tty_wakeup()?? We are in the receive path...

Thanks,
David Laight July 7, 2014, 12:55 p.m. UTC | #3
From: Olivier Sobrie 
> Hi David,
> 
> On Mon, Jul 07, 2014 at 09:13:53AM +0000, David Laight wrote:
> > From: Olivier Sobrie
> > > When the module sends bursts of data, sometimes a deadlock happens in
> > > the hso driver when the tty buffer doesn't get the chance to be flushed
> > > quickly enough.
> > >
> > > To avoid this, first, we remove the endless while loop in
> > > put_rx_bufdata() which is the root cause of the deadlock.
> > > Secondly, when there is no room anymore in the tty buffer, we set up a
> > > timer of 100 msecs to give a chance to the upper layer to flush the tty
> > > buffer and make room for new data.
> >
> > What is the timer for?
> > You need to get the sending code woken up by the urb completion.
> 
> In put_rxbuf_data() (which can be called under irq disabled),
> tty_flip_buffer_push() is called and schedules a push of the tty buffer.
> When the buffer is full, I give some time to the above layer in order
> to flush it.
> The timer is used to recall put_rxbuf_data_and_resubmit_bulk_urb()
> later in order to read the remaining data stored in
> "urb->transfer_buffer" and then to resubmit the urb to receive more data
> from the gsm module.
> I don't understand what you mean by "getting the sending code woken up".
> Calling tty_port_tty_wakeup()?? We are in the receive path...

Sorry, it isn't at all clear from the general description whether you
are referring to the transmit or receive path.

'flush' can mean all sorts of things ...

In either case a 100ms delay to data doesn't seem right at all.

	David



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Olivier Sobrie July 7, 2014, 1:38 p.m. UTC | #4
On Mon, Jul 07, 2014 at 12:55:44PM +0000, David Laight wrote:
> From: Olivier Sobrie 
> > Hi David,
> > 
> > On Mon, Jul 07, 2014 at 09:13:53AM +0000, David Laight wrote:
> > > From: Olivier Sobrie
> > > > When the module sends bursts of data, sometimes a deadlock happens in
> > > > the hso driver when the tty buffer doesn't get the chance to be flushed
> > > > quickly enough.
> > > >
> > > > To avoid this, first, we remove the endless while loop in
> > > > put_rx_bufdata() which is the root cause of the deadlock.
> > > > Secondly, when there is no room anymore in the tty buffer, we set up a
> > > > timer of 100 msecs to give a chance to the upper layer to flush the tty
> > > > buffer and make room for new data.
> > >
> > > What is the timer for?
> > > You need to get the sending code woken up by the urb completion.
> > 
> > In put_rxbuf_data() (which can be called under irq disabled),
> > tty_flip_buffer_push() is called and schedules a push of the tty buffer.
> > When the buffer is full, I give some time to the above layer in order
> > to flush it.
> > The timer is used to recall put_rxbuf_data_and_resubmit_bulk_urb()
> > later in order to read the remaining data stored in
> > "urb->transfer_buffer" and then to resubmit the urb to receive more data
> > from the gsm module.
> > I don't understand what you mean by "getting the sending code woken up".
> > Calling tty_port_tty_wakeup()?? We are in the receive path...
> 
> Sorry, it isn't at all clear from the general description whether you
> are referring to the transmit or receive path.
> 
> 'flush' can mean all sorts of things ...
> 
> In either case a 100ms delay to data doesn't seem right at all.

An option to avoid the delay, is to replace the timer by a workqueue
and to schedule work in put_rxbuf_data_and_resubmit_bulk_urb()
when put_rxbuf_data() returns something greater than 0.
If you have better ideas, let me know.

Thanks,
Dan Williams July 7, 2014, 4:41 p.m. UTC | #5
On Mon, 2014-07-07 at 11:06 +0200, Olivier Sobrie wrote:
> When the module sends bursts of data, sometimes a deadlock happens in
> the hso driver when the tty buffer doesn't get the chance to be flushed
> quickly enough.
> 
> To avoid this, first, we remove the endless while loop in
> put_rx_bufdata() which is the root cause of the deadlock.
> Secondly, when there is no room anymore in the tty buffer, we set up a
> timer of 100 msecs to give a chance to the upper layer to flush the tty
> buffer and make room for new data.

I assume this problem happens when using PPP for data transfer, instead
of using the network port that the modules expose?  Or maybe the NMEA
port?  I can't imagine that AT commands would make this happen...

Dan

> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> ---
>  drivers/net/usb/hso.c |   51 +++++++++++++++++++++++++++++++++----------------
>  1 file changed, 35 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
> index 9ca2b41..1dff74f 100644
> --- a/drivers/net/usb/hso.c
> +++ b/drivers/net/usb/hso.c
> @@ -106,6 +106,8 @@
>  
>  #define MAX_RX_URBS			2
>  
> +#define UNTHROTTLE_TIMEOUT		100 /* msecs */
> +
>  /*****************************************************************************/
>  /* Debugging functions                                                       */
>  /*****************************************************************************/
> @@ -261,6 +263,7 @@ struct hso_serial {
>  	u16  curr_rx_urb_offset;
>  	u8   rx_urb_filled[MAX_RX_URBS];
>  	struct tasklet_struct unthrottle_tasklet;
> +	struct timer_list unthrottle_timer;
>  };
>  
>  struct hso_device {
> @@ -1161,13 +1164,18 @@ static void put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial *serial)
>  	while (serial->rx_urb_filled[serial->curr_rx_urb_idx]) {
>  		curr_urb = serial->rx_urb[serial->curr_rx_urb_idx];
>  		count = put_rxbuf_data(curr_urb, serial);
> -		if (count == -1)
> -			return;
>  		if (count == 0) {
>  			serial->curr_rx_urb_idx++;
>  			if (serial->curr_rx_urb_idx >= serial->num_rx_urbs)
>  				serial->curr_rx_urb_idx = 0;
>  			hso_resubmit_rx_bulk_urb(serial, curr_urb);
> +		} else if (count > 0) {
> +			mod_timer(&serial->unthrottle_timer,
> +				  jiffies
> +					+ msecs_to_jiffies(UNTHROTTLE_TIMEOUT));
> +			break;
> +		} else {
> +			break;
>  		}
>  	}
>  }
> @@ -1251,6 +1259,11 @@ static	void hso_unthrottle(struct tty_struct *tty)
>  	tasklet_hi_schedule(&serial->unthrottle_tasklet);
>  }
>  
> +static void hso_unthrottle_schedule(unsigned long data)
> +{
> +	tasklet_schedule((struct tasklet_struct *)data);
> +}
> +
>  /* open the requested serial port */
>  static int hso_serial_open(struct tty_struct *tty, struct file *filp)
>  {
> @@ -1286,6 +1299,10 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
>  		tasklet_init(&serial->unthrottle_tasklet,
>  			     (void (*)(unsigned long))hso_unthrottle_tasklet,
>  			     (unsigned long)serial);
> +		serial->unthrottle_timer.function = hso_unthrottle_schedule;
> +		serial->unthrottle_timer.data =
> +				(unsigned long)&serial->unthrottle_tasklet;
> +		init_timer(&serial->unthrottle_timer);
>  		result = hso_start_serial_device(serial->parent, GFP_KERNEL);
>  		if (result) {
>  			hso_stop_serial_device(serial->parent);
> @@ -1333,6 +1350,7 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)
>  		tty_port_tty_set(&serial->port, NULL);
>  		if (!usb_gone)
>  			hso_stop_serial_device(serial->parent);
> +		del_timer_sync(&serial->unthrottle_timer);
>  		tasklet_kill(&serial->unthrottle_tasklet);
>  	}
>  
> @@ -2012,22 +2030,23 @@ static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
>  
>  	tty = tty_port_tty_get(&serial->port);
>  
> +	if (tty && test_bit(TTY_THROTTLED, &tty->flags)) {
> +		tty_kref_put(tty);
> +		return -1;
> +	}
> +
>  	/* Push data to tty */
> -	write_length_remaining = urb->actual_length -
> -		serial->curr_rx_urb_offset;
>  	D1("data to push to tty");
> -	while (write_length_remaining) {
> -		if (tty && test_bit(TTY_THROTTLED, &tty->flags)) {
> -			tty_kref_put(tty);
> -			return -1;
> -		}
> -		curr_write_len = tty_insert_flip_string(&serial->port,
> -			urb->transfer_buffer + serial->curr_rx_urb_offset,
> -			write_length_remaining);
> -		serial->curr_rx_urb_offset += curr_write_len;
> -		write_length_remaining -= curr_write_len;
> -		tty_flip_buffer_push(&serial->port);
> -	}
> +	write_length_remaining = urb->actual_length -
> +					serial->curr_rx_urb_offset;
> +	curr_write_len =
> +		tty_insert_flip_string(&serial->port,
> +				       urb->transfer_buffer
> +						+ serial->curr_rx_urb_offset,
> +				       write_length_remaining);
> +	serial->curr_rx_urb_offset += curr_write_len;
> +	write_length_remaining -= curr_write_len;
> +	tty_flip_buffer_push(&serial->port);
>  	tty_kref_put(tty);
>  
>  	if (write_length_remaining == 0) {


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Olivier Sobrie July 7, 2014, 6:50 p.m. UTC | #6
Hi Dan,

On Mon, Jul 07, 2014 at 11:41:20AM -0500, Dan Williams wrote:
> On Mon, 2014-07-07 at 11:06 +0200, Olivier Sobrie wrote:
> > When the module sends bursts of data, sometimes a deadlock happens in
> > the hso driver when the tty buffer doesn't get the chance to be flushed
> > quickly enough.
> > 
> > To avoid this, first, we remove the endless while loop in
> > put_rx_bufdata() which is the root cause of the deadlock.
> > Secondly, when there is no room anymore in the tty buffer, we set up a
> > timer of 100 msecs to give a chance to the upper layer to flush the tty
> > buffer and make room for new data.
> 
> I assume this problem happens when using PPP for data transfer, instead
> of using the network port that the modules expose?  Or maybe the NMEA
> port?  I can't imagine that AT commands would make this happen...

Yes it happens when using ppp for data transfer.
The transfer of a large file sometimes triggers the problem.
David Miller July 8, 2014, 11:16 p.m. UTC | #7
From: Olivier Sobrie <olivier@sobrie.be>
Date: Mon,  7 Jul 2014 11:06:07 +0200

> When the module sends bursts of data, sometimes a deadlock happens in
> the hso driver when the tty buffer doesn't get the chance to be flushed
> quickly enough.
> 
> To avoid this, first, we remove the endless while loop in
> put_rx_bufdata() which is the root cause of the deadlock.
> Secondly, when there is no room anymore in the tty buffer, we set up a
> timer of 100 msecs to give a chance to the upper layer to flush the tty
> buffer and make room for new data.
> 
> Signed-off-by: Olivier Sobrie <olivier@sobrie.be>

I agree with the feedback you've been given in that adding a delay
like this is really not a reasonable solution.

Why is it so difficult to make the event which places the data there
trigger to necessary calls to pull the data out of the URB transfer
buffer?

This should be totally and completely event based.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Olivier Sobrie July 10, 2014, 2:28 p.m. UTC | #8
Hi David,

On Tue, Jul 08, 2014 at 04:16:33PM -0700, David Miller wrote:
> From: Olivier Sobrie <olivier@sobrie.be>
> Date: Mon,  7 Jul 2014 11:06:07 +0200
> 
> > When the module sends bursts of data, sometimes a deadlock happens in
> > the hso driver when the tty buffer doesn't get the chance to be flushed
> > quickly enough.
> > 
> > To avoid this, first, we remove the endless while loop in
> > put_rx_bufdata() which is the root cause of the deadlock.
> > Secondly, when there is no room anymore in the tty buffer, we set up a
> > timer of 100 msecs to give a chance to the upper layer to flush the tty
> > buffer and make room for new data.
> > 
> > Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
> 
> I agree with the feedback you've been given in that adding a delay
> like this is really not a reasonable solution.
> 
> Why is it so difficult to make the event which places the data there
> trigger to necessary calls to pull the data out of the URB transfer
> buffer?
> 
> This should be totally and completely event based.

The function put_rxbuf_data() is called from the urb completion handler.
It puts the data of the urb transfer in the tty buffer with
tty_insert_flip_string_flags() and schedules a work queue in order to
push the data to the ldisc.
Problem is that we are in a urb completion handler so we can't wait
until there is room in the tty buffer.
An option I see is: If tty_insert_flip_string_flags() returns 0, start a
workqueue that will insert the remaining data in the tty buffer and then
restart the urb. But I'm not convinced that it is a good solution.
I should miss something...

In put_rxbuf_data(), when tty_insert_flip_string_flags() returns 0, would
it be correct to set the TTY_THROTTLED flag? I assume not...

I'll have a look in other drivers how such cases are handled.

Thanks,
David Laight July 10, 2014, 2:37 p.m. UTC | #9
From: Olivier Sobrie
...
> The function put_rxbuf_data() is called from the urb completion handler.
> It puts the data of the urb transfer in the tty buffer with
> tty_insert_flip_string_flags() and schedules a work queue in order to
> push the data to the ldisc.
> Problem is that we are in a urb completion handler so we can't wait
> until there is room in the tty buffer.

Surely you can just keep the urb?
Resubmit it later when all the data has been transferred.

	David



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alan Cox July 10, 2014, 3:50 p.m. UTC | #10
On Thu, 10 Jul 2014 14:37:37 +0000
David Laight <David.Laight@ACULAB.COM> wrote:

> From: Olivier Sobrie
> ...
> > The function put_rxbuf_data() is called from the urb completion handler.
> > It puts the data of the urb transfer in the tty buffer with
> > tty_insert_flip_string_flags() and schedules a work queue in order to
> > push the data to the ldisc.
> > Problem is that we are in a urb completion handler so we can't wait
> > until there is room in the tty buffer.

The tty provides the input queue, if the queue is full then just chuck
the data in the bitbucket.  hso is trying to be far too clever.

If hso is fast enough that the buffering isn't sufficient on the tty side
then we need to fix the tty buffer size.

Arguably what we need are tty fastpaths for non N_TTY but thats rather
more work. There's no fundamental reason that hso can't throw the buffer
at buffer straight at the PPP ldisc and straight into the network stack -
just that 

1. The tty mid layer glue is standing in the way
2. The change of line discipline code has to lock against it

Alan
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Laight July 10, 2014, 3:55 p.m. UTC | #11
From: One Thousand Gnomes
> On Thu, 10 Jul 2014 14:37:37 +0000
> David Laight <David.Laight@ACULAB.COM> wrote:
> 
> > From: Olivier Sobrie
> > ...
> > > The function put_rxbuf_data() is called from the urb completion handler.
> > > It puts the data of the urb transfer in the tty buffer with
> > > tty_insert_flip_string_flags() and schedules a work queue in order to
> > > push the data to the ldisc.
> > > Problem is that we are in a urb completion handler so we can't wait
> > > until there is room in the tty buffer.
> 
> The tty provides the input queue, if the queue is full then just chuck
> the data in the bitbucket.  hso is trying to be far too clever.
> 
> If hso is fast enough that the buffering isn't sufficient on the tty side
> then we need to fix the tty buffer size.

You really want to apply flow control back over the 'serial' link.
That may just cause data discards earlier on the local system.
But it is possible that not resubmitting the receive urb will cause the
modem to flow control back to the sender.
In which case there is some chance that data won't be lost.

	David

> 
> Arguably what we need are tty fastpaths for non N_TTY but thats rather
> more work. There's no fundamental reason that hso can't throw the buffer
> at buffer straight at the PPP ldisc and straight into the network stack -
> just that
> 
> 1. The tty mid layer glue is standing in the way
> 2. The change of line discipline code has to lock against it
> 
> Alan


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alan Cox July 10, 2014, 9:20 p.m. UTC | #12
> You really want to apply flow control back over the 'serial' link.
> That may just cause data discards earlier on the local system.
> But it is possible that not resubmitting the receive urb will cause the
> modem to flow control back to the sender.
> In which case there is some chance that data won't be lost.

If you are doing PPP and you can't keep up the sooner you chuck data the
better. Flow control actually works against performance and good network
behaviour. It's counter intuitive but TCP/IP works best if any
performance bottlenecks are immediately visible and not covered over.

Alan
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Olivier Sobrie July 11, 2014, 9:18 a.m. UTC | #13
Hi Alan and Davids,

On Thu, Jul 10, 2014 at 04:50:03PM +0100, One Thousand Gnomes wrote:
> On Thu, 10 Jul 2014 14:37:37 +0000
> David Laight <David.Laight@ACULAB.COM> wrote:
> 
> > From: Olivier Sobrie
> > ...
> > > The function put_rxbuf_data() is called from the urb completion handler.
> > > It puts the data of the urb transfer in the tty buffer with
> > > tty_insert_flip_string_flags() and schedules a work queue in order to
> > > push the data to the ldisc.
> > > Problem is that we are in a urb completion handler so we can't wait
> > > until there is room in the tty buffer.
> 
> The tty provides the input queue, if the queue is full then just chuck
> the data in the bitbucket.  hso is trying to be far too clever.
> 
> If hso is fast enough that the buffering isn't sufficient on the tty side
> then we need to fix the tty buffer size.

Ok I'll adapt the patch to drop the data that can't be put in the tty
buffer. I test this and resend a new patch.

Thanks for your help!
David Laight July 11, 2014, 9:28 a.m. UTC | #14
From: Olivier Sobrie Olivier Sobrie
> Hi Alan and Davids,
> 
> On Thu, Jul 10, 2014 at 04:50:03PM +0100, One Thousand Gnomes wrote:
> > On Thu, 10 Jul 2014 14:37:37 +0000
> > David Laight <David.Laight@ACULAB.COM> wrote:
> >
> > > From: Olivier Sobrie
> > > ...
> > > > The function put_rxbuf_data() is called from the urb completion handler.
> > > > It puts the data of the urb transfer in the tty buffer with
> > > > tty_insert_flip_string_flags() and schedules a work queue in order to
> > > > push the data to the ldisc.
> > > > Problem is that we are in a urb completion handler so we can't wait
> > > > until there is room in the tty buffer.
> >
> > The tty provides the input queue, if the queue is full then just chuck
> > the data in the bitbucket.  hso is trying to be far too clever.
> >
> > If hso is fast enough that the buffering isn't sufficient on the tty side
> > then we need to fix the tty buffer size.
> 
> Ok I'll adapt the patch to drop the data that can't be put in the tty
> buffer. I test this and resend a new patch.

If you are going to drop data, then ideally you want to discard entire ppp
packets. Depending on exactly how the interface works it might be that
urb are likely to contain complete packets.

So discarding entire urb might work better than discarding a few bytes.
(But don't even think of scanning the data stream in the usb driver - except
for experiments.)

	David



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Olivier Sobrie July 11, 2014, 12:05 p.m. UTC | #15
On Fri, Jul 11, 2014 at 09:28:47AM +0000, David Laight wrote:
> From: Olivier Sobrie Olivier Sobrie
> > Hi Alan and Davids,
> > 
> > On Thu, Jul 10, 2014 at 04:50:03PM +0100, One Thousand Gnomes wrote:
> > > On Thu, 10 Jul 2014 14:37:37 +0000
> > > David Laight <David.Laight@ACULAB.COM> wrote:
> > >
> > > > From: Olivier Sobrie
> > > > ...
> > > > > The function put_rxbuf_data() is called from the urb completion handler.
> > > > > It puts the data of the urb transfer in the tty buffer with
> > > > > tty_insert_flip_string_flags() and schedules a work queue in order to
> > > > > push the data to the ldisc.
> > > > > Problem is that we are in a urb completion handler so we can't wait
> > > > > until there is room in the tty buffer.
> > >
> > > The tty provides the input queue, if the queue is full then just chuck
> > > the data in the bitbucket.  hso is trying to be far too clever.
> > >
> > > If hso is fast enough that the buffering isn't sufficient on the tty side
> > > then we need to fix the tty buffer size.
> > 
> > Ok I'll adapt the patch to drop the data that can't be put in the tty
> > buffer. I test this and resend a new patch.
> 
> If you are going to drop data, then ideally you want to discard entire ppp
> packets. Depending on exactly how the interface works it might be that
> urb are likely to contain complete packets.

Indeed, urbs contains complete ppp packets. I see that the first and last
byte are equal to 0x7e.

> 
> So discarding entire urb might work better than discarding a few bytes.
> (But don't even think of scanning the data stream in the usb driver - except
> for experiments.)

I will check with tty_buffer_request_room() that there is enough room to
receive the frame before inserting the data in the tty buffer with
tty_insert_flip_string().
diff mbox

Patch

diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 9ca2b41..1dff74f 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -106,6 +106,8 @@ 
 
 #define MAX_RX_URBS			2
 
+#define UNTHROTTLE_TIMEOUT		100 /* msecs */
+
 /*****************************************************************************/
 /* Debugging functions                                                       */
 /*****************************************************************************/
@@ -261,6 +263,7 @@  struct hso_serial {
 	u16  curr_rx_urb_offset;
 	u8   rx_urb_filled[MAX_RX_URBS];
 	struct tasklet_struct unthrottle_tasklet;
+	struct timer_list unthrottle_timer;
 };
 
 struct hso_device {
@@ -1161,13 +1164,18 @@  static void put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial *serial)
 	while (serial->rx_urb_filled[serial->curr_rx_urb_idx]) {
 		curr_urb = serial->rx_urb[serial->curr_rx_urb_idx];
 		count = put_rxbuf_data(curr_urb, serial);
-		if (count == -1)
-			return;
 		if (count == 0) {
 			serial->curr_rx_urb_idx++;
 			if (serial->curr_rx_urb_idx >= serial->num_rx_urbs)
 				serial->curr_rx_urb_idx = 0;
 			hso_resubmit_rx_bulk_urb(serial, curr_urb);
+		} else if (count > 0) {
+			mod_timer(&serial->unthrottle_timer,
+				  jiffies
+					+ msecs_to_jiffies(UNTHROTTLE_TIMEOUT));
+			break;
+		} else {
+			break;
 		}
 	}
 }
@@ -1251,6 +1259,11 @@  static	void hso_unthrottle(struct tty_struct *tty)
 	tasklet_hi_schedule(&serial->unthrottle_tasklet);
 }
 
+static void hso_unthrottle_schedule(unsigned long data)
+{
+	tasklet_schedule((struct tasklet_struct *)data);
+}
+
 /* open the requested serial port */
 static int hso_serial_open(struct tty_struct *tty, struct file *filp)
 {
@@ -1286,6 +1299,10 @@  static int hso_serial_open(struct tty_struct *tty, struct file *filp)
 		tasklet_init(&serial->unthrottle_tasklet,
 			     (void (*)(unsigned long))hso_unthrottle_tasklet,
 			     (unsigned long)serial);
+		serial->unthrottle_timer.function = hso_unthrottle_schedule;
+		serial->unthrottle_timer.data =
+				(unsigned long)&serial->unthrottle_tasklet;
+		init_timer(&serial->unthrottle_timer);
 		result = hso_start_serial_device(serial->parent, GFP_KERNEL);
 		if (result) {
 			hso_stop_serial_device(serial->parent);
@@ -1333,6 +1350,7 @@  static void hso_serial_close(struct tty_struct *tty, struct file *filp)
 		tty_port_tty_set(&serial->port, NULL);
 		if (!usb_gone)
 			hso_stop_serial_device(serial->parent);
+		del_timer_sync(&serial->unthrottle_timer);
 		tasklet_kill(&serial->unthrottle_tasklet);
 	}
 
@@ -2012,22 +2030,23 @@  static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
 
 	tty = tty_port_tty_get(&serial->port);
 
+	if (tty && test_bit(TTY_THROTTLED, &tty->flags)) {
+		tty_kref_put(tty);
+		return -1;
+	}
+
 	/* Push data to tty */
-	write_length_remaining = urb->actual_length -
-		serial->curr_rx_urb_offset;
 	D1("data to push to tty");
-	while (write_length_remaining) {
-		if (tty && test_bit(TTY_THROTTLED, &tty->flags)) {
-			tty_kref_put(tty);
-			return -1;
-		}
-		curr_write_len = tty_insert_flip_string(&serial->port,
-			urb->transfer_buffer + serial->curr_rx_urb_offset,
-			write_length_remaining);
-		serial->curr_rx_urb_offset += curr_write_len;
-		write_length_remaining -= curr_write_len;
-		tty_flip_buffer_push(&serial->port);
-	}
+	write_length_remaining = urb->actual_length -
+					serial->curr_rx_urb_offset;
+	curr_write_len =
+		tty_insert_flip_string(&serial->port,
+				       urb->transfer_buffer
+						+ serial->curr_rx_urb_offset,
+				       write_length_remaining);
+	serial->curr_rx_urb_offset += curr_write_len;
+	write_length_remaining -= curr_write_len;
+	tty_flip_buffer_push(&serial->port);
 	tty_kref_put(tty);
 
 	if (write_length_remaining == 0) {