world leader in high performance signal processing
Trace: » workqueues

Kernel Events

This is really all about the work queue.

Work Queues can be used when a kernel system or device driver wants to schedule a deferred task and get on with the work in hand knowing that the deferred task will be processed as soon at possible.

This detection of deferred work could possibly have happened during an interrupt routine, The function that needed to be executed may not have to be completed immediately but could be deferred until after the interrupt routine has completed.

There is a special kernel events task set up for each CPU in a system.

The command ps ax will show these tasks

   PID TTY      STAT   TIME COMMAND
  1 ?        S      0:04 init [5]
  2 ?        SN     0:00 [ksoftirqd/0]
  3 ?        S<     0:06 [events/0] < this is the event processor
  [... the rest are missing  ...]

The events thread is created by this code from kernel/workqueue.c

void init_workqueues(void)
 
{
        hotcpu_notifier(workqueue_cpu_callback, 0);
        keventd_wq = create_workqueue("events");
        BUG_ON(!keventd_wq);
}

This code is called from the kernel startup program init/main.c ( Yes the kernel does have a main or at least a main.c )

static void __init do_basic_setup(void)
{
        /* drivers will send hotplug events */
        init_workqueues();
 
        [... rest missing ...]
 
 

The create_workqueue code creates a worker_thread for each CPU. This thread sets up a workqueue and a waitqueue and then sleeps waiting for work to arrive on the workqueue.

The workqueue is a list of functions that need to be executed. They are stored in a structure containing a pointer to the function to be executed and some optional data to pass to the function.

struct work_struct {
        unsigned long pending;
        struct list_head entry;
        void (*func)(void *);
        void *data;
        void *wq_data;
        struct timer_list timer;
};

The process of declaring or initializing work consists of setting up such a structure. A function and possibly some data are provided and the whole thing is given a name.

The process of scheduling work is the action of adding this structure to a workqueue and waking up the waitqueue.

Event Code Examples

Some code examples help explain this.

from drivers/char/tty_io.c Associate a function and some data with a work queue name

  //this element is inside the tty structure
 
  struct work_struct hangup_work;
 
  // when setting up a tty device this is done
  INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);

Then when the tty driver detects a hangup the following is used.

   // kick off the hangup task
   schedule_work(&tty->hangup_work);

A nice touch is this fact that you can wait for the hangup task to complete its execution

   // wait for the hangup task to complete
   flush_scheduled_work();
 

You can also schedule the work to occur at the next system tick. This is useful when you expect a whole list of events to occur and feel safe in letting a bunch of events build up before servicing the lot of them.

   // schedule the work but do it later
   schedule_delayed_work(&tty->flip.work, 1);
 

The scheduled work can also be cancelled.

In the tty driver this is done when the device is to be closed and any characters received are to be discarded.

   // OK I've changed my mind 
   cancel_delayed_work(&tty->flip.work);