Re: Is tty->receive_room no longer usable w/ SMP?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2014-02-13, Peter Hurley <peter@xxxxxxxxxxxxxxxxxx> wrote:
> On 02/13/2014 12:38 AM, Grant Edwards wrote:

>> That's the part I don't understand:
>>
>> How are drivers that use tty_insert_char() supposed to know when to
>> stop calling it?  tty_insert_char() doesn't return a status, so you
>> don't know even after the fact if the byte you just passed was
>> accepted. Do you?
>
> What tree are you looking at?

Rats.  I wasn't looking at tty_insert_char(), I was looking at
uart_insert_char() since that's what all the other drivers use.

> include/linux/tty_flip.h:tty_insert_flip_char() takes 1 byte and 1 flag
> and returns how many bytes were consumed (ie., 0 or 1). If it returned
> 0, the data was not accepted.

Right.

>> Similarly, how are drivers that use tty_insert_flip_string_flags()
>> supposed to know how much data they can pass to it?
>
> Drivers pass _all_ of the rx data to tty_insert_flip_string_flags(),

Except they don't.

They call tty_buffer_request_room() to decide how much data to pull
out of the rx FIFO/buffer.  Then they read only that much rx data and
pass it to tty_insert_flip_string_flags() ignoring the return value,
because the rx data has passed the point of no return. Once you pull
data out of the rx FIFO/buffer, you've got to do something with it.
If you can't transfer it to the line discipline layer, things get ugly
quickly.

> which returns how much was accepted. Eg.,
>
>          c = tty_insert_flip_string_flags(&my_port->port, rx_data, flags, n_rx);
>          if (c < 0) {
>                  /* actual error - none of the data was taken up */
>          }
>          if (c < n_rx) {
>                  /* tty buffer did not take up all the data */
>          }

That's not how tty_insert_flip_string_flags() is used by in-tree
drivers (at least not in 3.14rc2).

>> Are they expected to keep reading data from the rx FIFO and passing it
>> to tty_insert_flip_string_flags() until it returns a value smaller
>> than the requested transfer size and then buffer the "overflow" data
>> that has already been read from the rx FIFO but not accepted by
>> tty_insert_flip_string_flags()?  Then at some point in the future you
>> retry the old, buffered data, and once that's all been transferred you
>> start reading from the rx FIFO again? That would work, but it's a
>> little ugly.
>
> While this approach is possible, there is another way to look at
> this problem:
>
> The tty buffers are very deep (at least 64K+, as high as 128K).
> If data can't be taken in but the sender has not yet been throttled,
> then some error is occurring.

Not necessarily.

With modern UARTs, the way you throttle the sender is by not reading
data from the rx FIFO when there's nowhere to put that data.  When the
rx FIFO starts getting too full, the UART will send an XOFF or assirt
or de-assert RTS or DTR or whatever.

In order for that mechanism to work, you need to know when to stop
reading the rx FIFO.  That's what we had been using tty->receive_room
for, and now it appears I should be using tty_buffer_request_room()
for.

> Rather than buffering data outside the tty buffers (because
> eventually that will run out too causing a fifo overflow),

Not if flow control is enabled.  When you end up with data buffered in
the driver, the driver stops reading the rx FIFO, the rx FIFO fills
up, and the UART throttles the sender.  Once the buffered data has
been transferred, you start read the rx FIFO again and the UART
unthrottles the sender.  If flow control _isn't_ enable, and the
application isn't reading receive data, then there's going to be an
overflow error and data is lost no matter what approach you take. It
makes a lot more sense to let the UART do the overflow error detection
and discard the data than to do that in driver code.

> just drop the current rx and signal a condition to insert a
> TTY_OVERRUN at the beginning of the next rx (look at
> drivers/staging/fwserial/fwserial.c:fwtty_rx().

That seems like writing driver code to emulate functions that already
exist in the UART hardware.  I'd much rather just use the UART for
that.

> but don't use tty_buffer_space_avail() throttling part; that's only
> for very high speeds where the tty buffers can overflow even before
> the input processing worker runs).

The whole point of knowing when to stop reading the rx FIFO is to
_prevent_ data loss/overrun by allowing the UART to throttle the
sender before we get to the point where we've got nowhere to put data
we've read from the rx FIFO.

>> I must be missing something.
>
> tty_buffer_request_room() returns a buffer suitable for data + flags;
> you're just misreading the code.

Great!

That solves my problem.  I can call tty_buffer_request_room(), read
the indicated number of chars or char/flag pairs and then depend on
being able to call _either_ tty_insert_flip_string() or
tty_insert_flip_string_flags() to transfer the data I read.

When the application stops reading data and buffers fill up,
tty_buffer_request_room() will tell me to stop reading data from the
UART and everything will work the way it should.

> At least build against it; that's automatable.

Good point.  That would catch the obvious stuff with very little work.

> I don't mean to be critical but I can't really see that development
> model being sustainable.

We've been doing it for 20+ years (though I've only been involved for
the last 15).  We can't beat the Chinese on the price of boards, so we
had better beat them on support.

> There's no realistic way to single-source a driver across hundreds of
> kernel versions.

Maybe not hundreds.  Our current drivers only support 2.6.24 and
later.  We can still support previous driver versions if required for
customers running older 2.6 kernels.  We officially stopped supporting
2.4 several years ago.

> What was still being "maintained" for your driver for a 2.4 kernel
> (since any other active development there stopped years ago)?

We hadn't supported 2.4 for several years at that point -- I was just
illustrating how reluctant many customers are to upgrade kernels and
the infeasibility of depending on in-tree drivers to support customers
like that.

> One way to manage an in-kernel driver is to only have it support a
> minimum/common feature set. The advantage is you get automatic
> patches when the core changes -- and I don't just mean tty core but
> also locking, workqueues, etc -- which you can then pull into the
> out-of-tree driver (still adhering to the license requirements, of
> course). You just maintain the actual hardware changes/bug fixes.

A couple of our products do have in-tree drivers, but they've diverged
so far from the out-of-tree drivers that they aren't particularly
useful resources for maintaining the out-of-tree drivers.  It's
actually been more useful over the years to look at changes made to
other in-tree drivers.

> You have to deal with superseding the in-tree driver with the out-of-tree
> driver when both are present, but that's not insurmountable.

That doesn't seem to be an issue.  It's only been a problem a couple
times in the past couple decades when a customer has built a custom
configured kernel and configured the in-tree driver as built-in rather
than a module.

> [ For my own edification, why is the driver not a serial mini-port? ]

I don't know what a serial mini-port is.  We recently transitioned two
of our drivers from being tty drivers to being serial-core drivers.
Is mini-port something different that the serial core?

For another driver, it's still a tty driver because the serial-core
API just didn't fit the device well enough to make it work.  One issue
I recall is that our driver for that device needs to be able to cause
specific error returns for write() calls that are made when the
hardware is unavailable (and I think there are different errors
depending on why it's unavailable).

-- 
Grant Edwards               grant.b.edwards        Yow! BARBARA STANWYCK makes
                                  at               me nervous!!
                              gmail.com            

--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux