Kworker 线程被 SCHED_RR 用户空间线程阻塞
Kworker threads getting blocked by SCHED_RR userspace threads
我们有一个使用内核 3.14.17、PREEMPT RT 的 Linux 系统。是单核系统。
对于延迟问题,我们的应用程序将其某些线程的调度类型设置为 SCHED_RR。但是,这会导致内核中的 kworker 被阻塞,因为它们在模式 SCHED_OTHER 下仅 运行。这可能会导致一种优先级反转,因为低优先级 SCHED_RR 线程会阻止更高优先级 SHED_RR 接收来自驱动程序的数据。
被阻止的是 TTY 驱动程序。它在函数 tty_flip_buffer_push 中使用了一个工作队列。可能有更多电话,但这是我们确定的电话。
有什么方法可以轻松解决此问题 - RT 应用程序依赖于 kworker?我们希望我们不必自己破解 driver/kernel。 RT 内核中是否有针对此类内容的任何内核配置选项?我们可以吗,
- 为 kworker 设置 SCHED_RR 优先级?
- 禁用特定驱动程序的工作队列?
如果我们必须破解驱动程序,我们可能会给它自己的工作队列,使用 SCHED_RR kworker。
当然,任何其他解决方案也很有趣。如果有一些新功能,我们可以升级到更高版本的内核。
此行为的根本原因是 tty_flip_buffer_push()
在kernel/drivers/tty/tty_buffer.c:518
中,
tty_flip_buffer_push 安排一个异步任务。这很快就会由 kworker 线程异步执行。
但是,如果任何实时线程在系统上执行并保持繁忙,那么 kworker 线程很快执行的机会就会非常小。最终,一旦 RT 线程放弃 CPU 或 RT-throttling 被触发,它最终可能会为 kworker 线程提供执行的机会。
旧内核支持 TTY 子系统中的 low_latency
标志。
在 Linux 内核 v3.15 之前 tty_flip_buffer_push()
支持 tty 端口的 low_latency
标志。
如果 low_latency
标志由 UART 驱动程序设置如下(通常在其 .startup()
函数中),
t->uport.state->port.tty->low_latency = 1;
然后 tty_flip_buffer_push()
在当前函数调用本身的上下文中执行同步复制。因此它会自动继承当前任务的优先级,即不会因异步调度工作任务而导致优先级反转。
Note: If the serial driver sets the low_latency
flag, it must avoid calling tty_flip_buffer_push()
within an ISR(interrupt context). With the low_latency
flag set, tty_flip_buffer_push()
does NOT use separate workqueue, but directly calls the functions. So if called within an interrupt context, the ISR will take longer to execute. This will increase latency of other parts of the kernel/system. Also under certain conditions (dpeending on how much data is available in the serial buffer) tty_flip_buffer_push()
may attempt to sleep (acquire a mutex). Calling sleep within an ISR in the Linux kernel causes a kernel bug.
Linux 内核中的工作队列实现已迁移到 CMWQ,
不再可能确定性地获得独立的执行上下文
(即单独的线程)用于各个工作队列。
系统中的所有工作队列都由系统中的 kworker/*
个线程支持。
注意:此部分已过时!!
保留以下内容作为旧版本 Linux 内核的参考。
low-latency/real-time UART/TTY 的自定义:
1。为 TTY 层创建和使用个人工作队列。
在 tty_init() 中创建一个新的工作队列。
使用 create_workqueue()
创建的工作队列将为系统上的每个 CPU 分配 1 个工作线程。
struct workqueue_struct *create_workqueue(const char *name);
改用create_singlethread_workqueue()
,创建一个只有一个 kworker 进程的工作队列
struct workqueue_struct *create_singlethread_workqueue(const char *name);
2。使用私有工作队列。
在上面的私有工作队列而不是内核的全局全局工作队列上排队翻转缓冲区工作。
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
在 tty_flip_buffer_push() 调用的函数中将 schedule_work()
替换为 queue_work()
。
3。调整私有工作队列的执行优先级。
启动时,TTY 层工作队列使用的 kworker 线程可以通过创建它时使用的字符串 name
来识别。根据系统设计的要求,在此线程上使用 chrt
设置适当的更高 RT 优先级。
我们有一个使用内核 3.14.17、PREEMPT RT 的 Linux 系统。是单核系统。
对于延迟问题,我们的应用程序将其某些线程的调度类型设置为 SCHED_RR。但是,这会导致内核中的 kworker 被阻塞,因为它们在模式 SCHED_OTHER 下仅 运行。这可能会导致一种优先级反转,因为低优先级 SCHED_RR 线程会阻止更高优先级 SHED_RR 接收来自驱动程序的数据。
被阻止的是 TTY 驱动程序。它在函数 tty_flip_buffer_push 中使用了一个工作队列。可能有更多电话,但这是我们确定的电话。
有什么方法可以轻松解决此问题 - RT 应用程序依赖于 kworker?我们希望我们不必自己破解 driver/kernel。 RT 内核中是否有针对此类内容的任何内核配置选项?我们可以吗,
- 为 kworker 设置 SCHED_RR 优先级?
- 禁用特定驱动程序的工作队列?
如果我们必须破解驱动程序,我们可能会给它自己的工作队列,使用 SCHED_RR kworker。
当然,任何其他解决方案也很有趣。如果有一些新功能,我们可以升级到更高版本的内核。
此行为的根本原因是 tty_flip_buffer_push()
在kernel/drivers/tty/tty_buffer.c:518
中,
tty_flip_buffer_push 安排一个异步任务。这很快就会由 kworker 线程异步执行。
但是,如果任何实时线程在系统上执行并保持繁忙,那么 kworker 线程很快执行的机会就会非常小。最终,一旦 RT 线程放弃 CPU 或 RT-throttling 被触发,它最终可能会为 kworker 线程提供执行的机会。
旧内核支持 TTY 子系统中的 low_latency
标志。
在 Linux 内核 v3.15 之前 tty_flip_buffer_push()
支持 tty 端口的 low_latency
标志。
如果 low_latency
标志由 UART 驱动程序设置如下(通常在其 .startup()
函数中),
t->uport.state->port.tty->low_latency = 1;
然后 tty_flip_buffer_push()
在当前函数调用本身的上下文中执行同步复制。因此它会自动继承当前任务的优先级,即不会因异步调度工作任务而导致优先级反转。
Note: If the serial driver sets the
low_latency
flag, it must avoid callingtty_flip_buffer_push()
within an ISR(interrupt context). With thelow_latency
flag set,tty_flip_buffer_push()
does NOT use separate workqueue, but directly calls the functions. So if called within an interrupt context, the ISR will take longer to execute. This will increase latency of other parts of the kernel/system. Also under certain conditions (dpeending on how much data is available in the serial buffer)tty_flip_buffer_push()
may attempt to sleep (acquire a mutex). Calling sleep within an ISR in the Linux kernel causes a kernel bug.
Linux 内核中的工作队列实现已迁移到 CMWQ,
不再可能确定性地获得独立的执行上下文
(即单独的线程)用于各个工作队列。
系统中的所有工作队列都由系统中的 kworker/*
个线程支持。
注意:此部分已过时!!
保留以下内容作为旧版本 Linux 内核的参考。
low-latency/real-time UART/TTY 的自定义:
1。为 TTY 层创建和使用个人工作队列。
在 tty_init() 中创建一个新的工作队列。
使用 create_workqueue()
创建的工作队列将为系统上的每个 CPU 分配 1 个工作线程。
struct workqueue_struct *create_workqueue(const char *name);
改用create_singlethread_workqueue()
,创建一个只有一个 kworker 进程的工作队列
struct workqueue_struct *create_singlethread_workqueue(const char *name);
2。使用私有工作队列。
在上面的私有工作队列而不是内核的全局全局工作队列上排队翻转缓冲区工作。
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
在 tty_flip_buffer_push() 调用的函数中将 schedule_work()
替换为 queue_work()
。
3。调整私有工作队列的执行优先级。
启动时,TTY 层工作队列使用的 kworker 线程可以通过创建它时使用的字符串 name
来识别。根据系统设计的要求,在此线程上使用 chrt
设置适当的更高 RT 优先级。