Kernel Module 6 Workqueue - FrankBau/meta-marsboard-bsp GitHub Wiki
A workqueue is used to put so called work items into a queue and execute them a little later in a safe kernel process context.
Typically work items are created in an interrupt handler where there is no process context and only a very limited subset of kernel functions is allowed.
Let's define our own "class" struct my_work_struct
based upon struct work_struct
and a workqueue.
Since we have to use plain "C", subclassing involves some nasty type casting, see below.
#include <linux/workqueue.h>
#include <linux/slab.h>
...
struct my_work_struct {
struct work_struct work; // "base class" members, do not touch
int my_irq_counter; // my data: number of irq
unsigned long my_jiffies; // my data: time of irq
};
// the workqueue
static struct workqueue_struct *my_workqueue;
Let's also define a callback function which will be called by the kernel later when it deques the queued work items:
// called by the kernel to dequeue my work item
static void my_workqueue_function( struct work_struct *work )
{
struct my_work_struct *my_work = (struct my_work_struct *)work; // downcasting to struct my_work_struct
pr_info("my_workqueue_function, my_irq_counter=%4d, my_jiffies=%lu\n", my_work->my_irq_counter, my_work->my_jiffies );
kfree(work); // free the memory allocated for my work item
return;
}
Work items are created in the interrupt handler, filled with my data and enqueued in the workqueue:
static irqreturn_t irq_handler( int irq, void *dev_id )
{
// allocate memory for my work item
struct my_work_struct *my_work = (struct my_work_struct *)kmalloc(sizeof(struct my_work_struct), GFP_KERNEL);
pr_info( " irq_handler at jiffies=%lu\n", jiffies );
if (!my_work) {
pr_err("failed to kmalloc my_work");
}
else {
INIT_WORK( (struct work_struct *)my_work, &my_workqueue_function );
my_work->my_irq_counter = irq_counter;
my_work->my_jiffies = jiffies;
queue_work( my_workqueue, (struct work_struct *)my_work ); // enqueue my work item
}
irq_counter++;
return IRQ_HANDLED;
}
Finally, the workqueue must be created in the init function and destroyed in the exit function of the module.
static int __init my_init(void)
{
...
my_workqueue = create_workqueue("my_queue");
...
}
static void __exit my_exit(void)
{
...
flush_workqueue( my_workqueue );
destroy_workqueue( my_workqueue );
...
}
The call to flush_workqueue
will block until all remaining items in the queue are processed which allows to free all allocated resources.
Make sure that no new work items will be enqueued when flush_workqueue
is called.
Delayed Work
Sometimes it is desirable to delay the processing of work items by some additional amount of time (jiffies). In order to do such delayed work, replace in the above code:
struct work_struct
bystruct delayed_work
INIT_WORK
byINIT_DELAYED_WORK
queue_work
byqueue_delayed_work
Alternatives / Related Concepts
Kernel thread deamon kthreadd
, see function kthread_create
.