如何给work回调函数传递用户参数
背景
Linux驱动开发中,经常会用到work queue
,该数据结构管理的是一个个的work_struct
结构体:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
最近在做pcie驱动开发,有个需求是1个pcie端口传输5块采集卡的数据,每块卡的收发是独立且随机的,所以需要用work queue
,它有一个内置的内核线程,可以轮询每块板卡的状态。
每块板卡都有一个work_struct,用于work queue启动work时 分辨板卡是哪个槽位(slot)的。
struct board{
int slot;
work_struct work;
}
INIT_WORK(&work, user_func); // 驱动初始化时绑定用户函数到work item
schedule_work(&work); // 驱动ISR用work queue的内核线程启动该work
work
结构体中有个回调函数字段func
,类型work_func_t
:
typedef void (*work_func_t)(struct work_struct *work);
问题
发现一个问题,该回调函数只有1个入参,即work_struct
结构体自身,而没有预留一个用户自定义参数,这样当用户想要传递槽位号时,就没办法了。
错误的思路
根据以往经验,work
结构体应该像device结构体有个void *
型的driver_data
字段一样,有个自己的void * work_data
字段,但是看来看去,只有atomic_long_t data
字段像,于是打算这样:
board board;
INIT_WORK(&board->work, user_func);
work.data = slot; // 初始化work后立即将槽位号信息储存到data字段
void user_func(struct work_struct *work) {
int slot = (int)work->data; // 提取槽位号
transfer(slot); // 传输特定槽位的板卡数据
}
但是搜遍内核代码,也没看到有利用work_struct
的data
字段的。仔细查看相关头文件,原来这个data字段最开始确实是用于传递用户数据的,但是后来就变成work queue管理work item内部状态的flag字段。
解决方法
灵机一动想到可以用container_of宏来获取外围的board结构体实例,然后再通过board实例得到槽位号
void user_func(struct work_struct *work) {
int slot = container_of(work, struct board, work); // 提取槽位号,第一个work是实例指针,第二个work是work_struct在board中的字段名
transfer(slot); // 传输特定槽位的板卡数据
}
后来去网上一搜,原来这是标准做法,自己重新发明了一次*????
总结
回调函数传递用户自定义参数的2种模式:
- 回调函数自己预留void *指针
- 将用户参数跟回调函数的某个参数一起嵌入到用户自定义结构体中
模式2的好处是,如果回调函数不需要处理多种数据,则用户不需要像模式1那样给void *参数传递NULL指针,更*些,扩展性也更好。
下一篇: 22个HTML5 技巧二