Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is low latency mode safe to use with Linux serial ports?

Is it safe to use the low_latency tty mode with Linux serial ports? The tty_flip_buffer_push function is documented that it "must not be called from IRQ context if port->low_latency is set." Nevertheless, many low-level serial port drivers call it from an ISR whether or not the flag is set. For example, the mpc52xx driver calls flip buffer unconditionally after each read from its FIFO.

A consequence of the low latency flip buffer in the ISR is that the line discipline driver is entered within the IRQ context. My goal is to get latency of one millisecond or less, reading from a high speed mpc52xx serial port. Setting low_latency acheives the latency goal, but it also violates the documented precondition for tty_flip_buffer_push.

like image 645
joshuanapoli Avatar asked Mar 19 '13 16:03

joshuanapoli


People also ask

What is Linux serial port?

A serial port connection allows a user to control systems without a display. Here's how you can access and use serial consoles in Linux. One of the indispensable elements when working on embedded Linux projects is accessing your device via the serial console interface.

What does Setserial command do in Linux?

setserial allows you (or a shell script) to talk to the serial software. But there's also another program, tt/stty/, that also deals with the serial port and is used for setting the port speed, etc.


1 Answers

This question was asked on linux-serial on Fri, 19 Aug 2011.

No, low latency is not safe in general.

However, in the particular case of 3.10.5 low_latency is safe.

The comments above tty_flip_buffer_push read:

"This function must not be called from IRQ context if port->low_latency is set."

However, the code (3.10.5, drivers/tty/tty_buffer.c) contradicts this:

void tty_flip_buffer_push(struct tty_port *port)
{
    struct tty_bufhead *buf = &port->buf;
    unsigned long flags;

    spin_lock_irqsave(&buf->lock, flags);
    if (buf->tail != NULL)
            buf->tail->commit = buf->tail->used;
    spin_unlock_irqrestore(&buf->lock, flags);

    if (port->low_latency)
            flush_to_ldisc(&buf->work);
    else
            schedule_work(&buf->work);
}
EXPORT_SYMBOL(tty_flip_buffer_push);

The use of spin_lock_irqsave/spin_unlock_irqrestore makes this code safe to call from interrupt context.

There is a test for low_latency and if it is set, flush_to_ldisc is called directly. This flushes the flip buffer to the line discipline immediately, at the cost of making the interrupt processing longer. The flush_to_ldisc routine is also coded to be safe for use in interrupt context. I guess that an earlier version was unsafe.

If low_latency is not set, then schedule_work is called. Calling schedule_work is the classic way to invoke the "bottom half" handler from the "top half" in interrupt context. This causes flush_to_ldisc to be called from the "bottom half" handler at the next clock tick.

Looking a little deeper, both the comment and the test seem to be in Alan Cox's original e0495736 commit of tty_buffer.c. This commit was a re-write of earlier code, so it seems that at one time there wasn't a test. Whoever added the test and fixed flush_to_ldisc to be interrupt-safe did not bother to fix the comment.

So, always believe the code, not the comments.

However, in the same code in 3.12-rc* (as of October 23, 2013) it looks like the problem was opened again when the spin_lock_irqsave's in flush_to_ldisc were removed and mutex_locks were added. That is, setting UPF_LOW_LATENCY in the serial_struct flags and calling the TIOCSSERIAL ioctl will again cause "scheduling while atomic".

The latest update from the maintainer is:

On 10/19/2013 07:16 PM, Jonathan Ben Avraham wrote:
> Hi Peter,
> "tty_flip_buffer_push" is called from IRQ handlers in most drivers/tty/serial UART drivers.
> 
> "tty_flip_buffer_push" calls "flush_to_ldisc" if low_latency is set.
> "flush_to_ldisc" calls "mutex_lock" in 3.12-rc5, which cannot be used in interrupt context.
> 
> Does this mean that setting "low_latency" cannot be used safely in 3.12-rc5?

Yes, I broke low_latency.

Part of the problem is that the 3.11- use of low_latency was unsafe; too many shared
data areas were simply accessed without appropriate safeguards.

I'm working on fixing it but probably won't make it for 3.12 final.

Regards,
Peter Hurley

So, it looks like you should not depend on low_latency unless you are sure that you are never going to change your kernel from a version that supports it.


Update: February 18, 2014, kernel 3.13.2

Stanislaw Gruszka wrote:

Hi,

setserial has low_latency option which should minimize receive latency
(scheduler delay). AFAICT it is used if someone talk to external device
via RS-485/RS-232 and need to have quick requests and responses . On
kernel this feature was implemented by direct tty processing from
interrupt context:

void tty_flip_buffer_push(struct tty_port *port)
{
    struct tty_bufhead *buf = &port->buf;

    buf->tail->commit = buf->tail->used;

    if (port->low_latency)
            flush_to_ldisc(&buf->work);
    else
            schedule_work(&buf->work);
} 

But after 3.12 tty locking changes, calling flush_to_ldisc() from
interrupt context is a bug (we got scheduling while atomic bug report
here: https://bugzilla.redhat.com/show_bug.cgi?id=1065087 )

I'm not sure how this should be solved. After Peter get rid all of those
race condition in tty layer, we probably don't want go back to use
spin_lock's there. Maybe we can create WQ_HIGHPRI workqueue and schedule
flush_to_ldisc() work there. Or perhaps users that need to low latency,
should switch to thread irq and prioritize serial irq to meat
retirements. Anyway setserial low_latency is now broken and all who use
this feature in the past can not do this any longer on 3.12+ kernels.

Thoughts ?

Stanislaw
like image 71
Jonathan Ben-Avraham Avatar answered Sep 23 '22 20:09

Jonathan Ben-Avraham