Этот вопрос был задан на linux-serial, пятница, 19 августа 2011 г..
Нет, низкая задержка в целом небезопасна.
Однако в частном случае 3.10.5 low_latency
безопасен.
Комментарии выше tty_flip_buffer_push
гласят:
«Эта функция не должна вызываться из контекста IRQ, если установлено значение port->low_latency».
Однако код (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
вызывается из обработчика "нижней половины" на следующем тактовом импульсе.
Глядя немного глубже, и комментарий, и тест, кажется, находятся в оригинальном коммите Алана Кокса e0495736
для tty_buffer.c
. Этот коммит был переписан более ранним кодом, поэтому кажется, что когда-то не было теста. Тот, кто добавил тест и сделал flush_to_ldisc
безопасным для прерываний, не удосужился исправить комментарий.
Поэтому всегда верьте коду, а не комментариям.
Однако в том же коде версии 3.12-rc* (по состоянию на 23 октября 2013 г.) похоже, что проблема возникла снова, когда были удалены spin_lock_irqsave в flush_to_ldisc и добавлены mutex_locks. То есть установка UPF_LOW_LATENCY во флагах serial_struct и вызов ioctl TIOCSSERIAL снова вызовет «атомарное планирование».
Последнее обновление от сопровождающего:
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
, если только вы не уверены, что никогда не собираетесь менять версию ядра с версии, которая его поддерживает.
Обновление: 18 февраля 2014 г., ядро 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
person
Jonathan Ben-Avraham
schedule
19.10.2013