内核传感器驱动轮询优化:高负载下维持200Hz事件输出
传感器驱动轮询稳定性优化问题
我有一个传感器驱动,未采用中断处理方式,而是借助workqueue每5ms轮询陀螺仪和加速度计。驱动核心轮询代码如下:
static void sensor_delaywork_func(struct work_struct *work) { struct delayed_work *delaywork = container_of(work, struct delayed_work, work); struct sensor_private_data *sensor = container_of(delaywork, struct sensor_private_data, delaywork); struct i2c_client *client = sensor->client; int result; mutex_lock(&sensor->sensor_mutex); result = sensor->ops->report(client); if (result < 0) dev_err(&client->dev, "%s: Get data failed\n", __func__); mutex_unlock(&sensor->sensor_mutex); if ((!sensor->pdata->irq_enable) && (sensor->stop_work == 0)) schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); }
当前问题:高负载下kworker/*线程会被抢占、CPU分配减少,导致生成的输入事件从每秒200个降至170-180个。使用chrt工具为kworker/*线程设置更高(实时)优先级可缓解此问题,但希望在内核层面正确改造驱动,确保任何情况下都能稳定输出200个事件/秒。已知高优先级tasklet无法睡眠不适用,自定义高优先级内核线程被视为hack方案,寻求合理优化建议。
可行优化方案
1. 创建实时优先级的自定义工作队列
内核允许创建带特定调度属性的工作队列,替代默认系统队列,让轮询任务运行在高优先级kworker线程中,避免被普通任务抢占。
实现步骤:
- 驱动初始化时,通过
alloc_workqueue_attrs设置调度策略为SCHED_FIFO或SCHED_RR,并指定合理的实时优先级(如50,避免过高影响系统关键进程)。 - 调用
alloc_workqueue创建带WQ_HIGHPRI | WQ_RESCUER标志的工作队列,绑定上述属性。 - 将延迟工作提交到该自定义队列,而非默认队列。
- 驱动卸载时销毁队列。
示例代码片段:
struct workqueue_struct *sensor_wq; struct workqueue_attrs *attrs; // 初始化工作队列属性 attrs = alloc_workqueue_attrs(); if (!attrs) { // 错误处理 } attrs->sched_policy = SCHED_FIFO; attrs->sched_priority = 50; // 创建实时工作队列 sensor_wq = alloc_workqueue("sensor_poll_wq", WQ_HIGHPRI | WQ_RESCUER, 1, attrs); free_workqueue_attrs(attrs); if (!sensor_wq) { // 错误处理 } // 提交延迟工作到自定义队列 queue_delayed_work(sensor_wq, &sensor->delaywork, msecs_to_jiffies(5));
2. 加入轮询时间补偿逻辑
即使使用高优先级队列,极端情况仍可能出现微小延迟,可通过计算实际耗时动态调整下一次延迟,弥补误差:
修改原函数的调度部分:
unsigned long actual_delay; ktime_t start, end; start = ktime_get(); // 原有数据采集逻辑 mutex_lock(&sensor->sensor_mutex); result = sensor->ops->report(client); if (result < 0) dev_err(&client->dev, "%s: Get data failed\n", __func__); mutex_unlock(&sensor->sensor_mutex); end = ktime_get(); // 计算实际耗时,用目标5ms减去实际耗时得到下一次延迟 actual_delay = msecs_to_jiffies(5) - ktime_to_jiffies(ktime_sub(end, start)); // 确保延迟不为负 actual_delay = max(actual_delay, 0UL); if ((!sensor->pdata->irq_enable) && (sensor->stop_work == 0)) // 指定当前CPU运行,减少跨CPU调度开销 schedule_delayed_work_on(smp_processor_id(), &sensor->delaywork, actual_delay);
3. 优化数据采集路径耗时
检查sensor->ops->report函数的执行效率,减少不必要的延迟:
- 缩短临界区持有时间,将非核心逻辑移出
mutex_lock/mutex_unlock范围。 - 若传感器支持批量读取,调整I2C传输方式,减少单次操作耗时。
- 移除高负载下不必要的调试打印或冗余逻辑。
4. 高精度定时器(hrtimer)替代delayed_work
对时间精度要求极高时,hrtimer比delayed_work提供更精准的定时。由于hrtimer回调运行在软中断上下文,若需睡眠(如I2C操作),可在回调中调度高优先级工作队列任务:
示例思路:
static enum hrtimer_restart sensor_hrtimer_handler(struct hrtimer *timer) { struct sensor_private_data *sensor = container_of(timer, struct sensor_private_data, hrtimer); ktime_t now = ktime_get(); // 调度高优先级工作队列执行数据采集 queue_work(sensor_wq, &sensor->work); // 调整定时器,确保5ms间隔 hrtimer_forward(timer, now, ms_to_ktime(5)); return HRTIMER_RESTART; } // 初始化时启动高精度定时器 hrtimer_init(&sensor->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); sensor->hrtimer.function = sensor_hrtimer_handler; hrtimer_start(&sensor->hrtimer, ms_to_ktime(5), HRTIMER_MODE_REL);
方案优先级推荐
- 自定义实时工作队列:实现简单、兼容性好,是解决抢占问题的核心方案,优先采用。
- 时间补偿逻辑:作为辅助优化,进一步提升极端负载下的稳定性。
- 高精度定时器:仅在对时间精度有极致要求的场景下使用。
内容的提问来源于stack exchange,提问作者pulse
相关产品推荐
相关产品推荐

