与 Linux 串行端口一起使用低延迟模式是否安全?

jos*_*oli 5 powerpc tty linux-device-driver

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

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

Jon*_*ham 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);
Run Code Online (Sandbox Code Playgroud)

使用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 并调用 TIOCSSERIAL 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
Run Code Online (Sandbox Code Playgroud)

所以,看起来你不应该依赖,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
Run Code Online (Sandbox Code Playgroud)