5

在 Linux 串口上使用 low_latency tty 模式是否安全?tty_flip_buffer_push函数被记录为“如果设置了 port-> low_latency,则不能从 IRQ 上下文中调用它”。然而,无论标志是否设置,许多低级串行端口驱动程序都从 ISR 调用它。例如,mpc52xx 驱动程序在每次从其 FIFO 读取后无条件地调用翻转缓冲区。

ISR 中低延迟翻转缓冲区的结果是线路规则驱动程序进入 IRQ 上下文中。我的目标是获得一毫秒或更短的延迟,从高速 mpc52xx 串行端口读取。设置 low_latency 可以实现延迟目标,但它也违反了 tty_flip_buffer_push 记录的先决条件。

4

2 回答 2

6

这个问题是在2011 年 8 月 19 日星期五在 linux-serial 上提出的。

不,低延迟通常是不安全的。

但是,在 3.10.5 的特定情况下low_latency是安全的。

上面的评论tty_flip_buffer_push写道:

“如果设置了 port->low_latency,则不得从 IRQ 上下文调用此函数。”

但是,代码(3.10.5,drivers/tty/tty_buffer.c)与此相矛盾:

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);

spin_lock_irqsave/的使用spin_unlock_irqrestore使这段代码可以安全地从中断上下文中调用。

有一个测试low_latency,如果设置了,flush_to_ldisc则直接调用。这会立即将翻转缓冲区刷新到行规程,但代价是使中断处理时间更长。该flush_to_ldisc例程也被编码为在中断上下文中使用是安全的。我猜早期版本是不安全的。

如果low_latency未设置,则调用schedule_work。调用schedule_work是在中断上下文中从“上半部分”调用“下半部分”处理程序的经典方法。这会导致flush_to_ldisc在下一个时钟滴答时从“下半部分”处理程序中调用。

再深入一点,评论和测试似乎都在 Alan Cox 的原始e0495736提交中tty_buffer.c。这次提交是对早期代码的重写,所以似乎有一次没有测试。添加测试并修复flush_to_ldisc中断安全的人都没有费心修复评论。

所以,永远相信代码,而不是评论。

但是,在 3.12-rc*(截至 2013 年 10 月 23 日)中的相同代码中,当 flush_to_ldisc 中的 spin_lock_irqsave 被删除并添加 mutex_locks 时,问题似乎再次被打开。也就是说,在 serial_struct 标志中设置 UPF_LOW_LATENCY 并调用 TIOCSERIAL ioctl 将再次导致“原子调度”。

维护者的最新更新是:

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

low_latency因此,除非您确定永远不会从支持它的版本更改内核,否则您似乎不应该依赖它。


更新:2014 年 2 月 18 日,内核 3.13.2

斯坦尼斯瓦夫·格鲁斯卡写道:

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
于 2013-10-19T20:07:37.193 回答
2

已将补丁发布到 LKML 以解决该问题。它删除了处理 low_latency 的通用代码,但保留了低级驱动程序使用的参数。

http://www.kernelhub.org/?p=2&msg=419071

我尝试使用串行控制台在 Linux 3.12 上强制 low_latency。内核非常不稳定。如果启用了抢占,它会在使用几分钟后挂起。

所以现在的答案是远离。

于 2014-02-28T22:19:19.343 回答