Tuesday, September 20, 2011

Workqueues-1 Introduction


workqueues, like tasklets, are useful to schedule a task that for future. One of the
main areas where it is used is in scheduling the bottom half of an interrupt service routine.

One of the major differences between a tasklet and a workqueue, though both are used for similar purposes, is that a tasklet works in interrupt context where as a workqueue executes in a process context.
We will look into the differences between tasklet and workqueues in detail in another post.

In this post let us look at how to create a work queue and how to schedule it.
The work_queue are maintained in a structure work_struct, defined in workqueue.h.
The main members of the structure are

atomic_long_t data  : The data that will be used by the function that is scheduled by this workqueue.
work_func_t func : The function that has is scheduled by the workqueue.

The workqueues can be implemented in two ways
1. Using the default event threads  :The kernel provides default threads called event threads, one per processor, which can be used to schedule the deferred functions of the workqueues. This is simpler as we need not create a new thread and only need to queue up the function for execution. It is advantageous when the amount of work to be done by the deferred function is less
and is not a very strict constraints on the performance.
2. Creating a separate worker thread for the workqueue. Instead of using the default event threads, we can also create separate threads for the functions that have to be deferred as part of the workqueue. Generally separate worker threads are used only when amount of work the function to be deferred is huge which might affect the other workqueues scheduled on the default event thread.

Let us firs look at creating a workqueue using the default event thread.

Creation :

Static creation :
DECLARE_WORK(name, void (*func)(void *))
name: The name of the "work_struct" structure that has to be created.
func: The function to be scheduled in this workqueue.

Creating in runtime
INIT_WORK(struct work_struct *work, void (*func)(void *))

work: The work_struct structure that has to be created.
func: The function to be scheduled in this workqueue.

The prototype of function that has to be scheduled is
void func(void *data) 

As the workqueue executes in a process context this function is allowed sleep as well as use semaphores.

Scheduling
schedule_work(&work);

The work gets scheduled as soon as the above call to schedule_work is made, and the function gets executed when the events thread wakes up on the processor on which it is scheduled.

The scheduling can be delayed using the function
schedule_delayed_work(&work, delay);

Where the delay is mentioned in the timer ticks (jiffies).

Flushing all the works :

void flush_scheduled_work(void); 

The above call makes sure that all the functions that have been scheduled are executed and returns only after that. One of the applications of the function is while removing the module.
Before removing a module it should be made sure that all the functions deferred for execution by the module are finished which can be achieved by using the above function.

A workqueue can also be canceled using

int cancel_delayed_work(struct work_struct *work); 

The next post we will look at creation of workqueue using the static creation.

0 comments:

Post a Comment