Linux Device Driver Tutorial Part 20 – Tasklet | Static Method

This is the Series on Linux Device Driver. The aim of this series is to provide the easy and practical examples that anyone can understand. This is the Linux Device Driver Tutorial Part 20 – Tasklet Static Method Tutorial.

Prerequisites

This is the continuation of Interrupts in Linux Kernel. So I’d suggest you to know some ideas about Linux Interrupts. You can find the some useful tutorials about Interrupts and Bottom Halves below.

  1. Interrupts in Linux Kernel 
  2. Interrupts Example Program
  3. Workqueue Example – Static Method
  4. Workqueue Example – Dynamic Method
  5. Workqueue Example – Own Workqueue

Bottom Half

When Interrupt triggers, Interrupt Handler should be execute very quickly and it should not run for more time (it should not perform time-consuming tasks). If we have the interrupt handler which is doing more tasks then we need to divide into two halves.

  1. Top Half
  2. Bottom Half

Top Half is nothing but our interrupt handler. If our interrupt handler is doing less task, then top half is more than enough. No need of bottom half in that situation. But if our we have more work when interrupt hits, then we need bottom half. The bottom half runs in the future, at a more convenient time, with all interrupts enabled. So, The job of bottom halves is to perform any interrupt-related work not performed by the interrupt handler.

There are 4 bottom half mechanisms are available in Linux:

  1. Work-queue
  2. Threaded IRQs
  3. Softirqs
  4. Tasklets

In this tutorial, we will see Tasklets in Linux Kernel.

Tasklets in Linux Kernel

Tasklets are used to queue up work to be done at a later time. Tasklets can be run in parallel, but the same tasklet cannot be run on multiple CPUs at the same time. Also each tasklet will run only on the CPU that schedules it, to optimize cache usage. Since the thread that queued up the tasklet must complete before it can run the tasklet, race conditions are naturally avoided. However, this arrangement can be suboptimal, as other potentially idle CPUs cannot be used to run the tasklet. Therefore workqueues can, and should be used instead, and workqueues were already discussed here.

In short, a tasklet is something like a very small thread that has neither stack, not context of its own. Such “threads” work quickly and completely.

Points To Remember

Before using Tasklets, you should consider these below points.

  • Tasklets are atomic, so we cannot use sleep() and such synchronization primitives as mutexessemaphores, etc. from them. But we can use spinlock.
  • A tasklet only runs on the same core (CPU) that schedules it.
  • Different tasklets can be running in parallel. But at the same time, a tasklet cannot be called concurrently with itself, as it runs on one CPU only.
  • Tasklets are executed by the principle of non-preemptive scheduling, one by one, in turn. We can schedule them with two different priorities: normal and high.

We can create tasklet in Two ways.

  1. Static Method
  2. Dynamic Method

In this tutorial we will see static method.

Tasklet Structure

This is the important data structure for the tasklet.


Here,

next – The next tasklet in line for scheduling.

state – This state denotes Tasklet’s State. TASKLET_STATE_SCHED (Scheduled) or TASKLET_STATE_RUN (Running).

count – It holds a nonzero value if the tasklet is disabled and 0 if it is enabled.

func –  This is the main function of the tasklet. Pointer to the function that needs to scheduled for execution at a later time.

data –  Data to be passed to the function “func”.

Create Tasklet

The below macros used to create a tasklet.

DECLARE_TASKLET

This macro used to create the tasklet structure and assigns the parameters to that structure.

If we are using this macro then tasklet will be in enabled state.

DECLARE_TASKLET(name, func, data);

name – name of the structure to be create.

func – This is the main function of the tasklet. Pointer to the function that needs to scheduled for execution at a later time.

data – Data to be passed to the function “func”.

Example

Now we will see how the macro is working. When I call the macro like above, first it creates tasklet structure with the name of tasklet. Then it assigns the parameter to that structure. It will be looks like below.

DECLARE_TASKLET_DISABLED

The tasklet can be declared and set at disabled state, which means that tasklet can be scheduled, but will not run until the tasklet is specifically enabled. You need to use tasklet_enable to enable.

DECLARE_TASKLET_DISABLED(name, func, data);

name – name of the structure to be create.

func – This is the main function of the tasklet. Pointer to the function that needs to scheduled for execution at a later time.

data – Data to be passed to the function “func”.

Enable and Disable Tasklet

tasklet_enable

This used to enable the tasklet.

void tasklet_enable(struct);

t – pointer to the tasklet struct

tasklet_disable

This used to disable the tasklet wait for the completion of tasklet’s operation.

void tasklet_disable(struct tasklet_struct *t);

t – pointer to the tasklet struct

tasklet_disable_nosync

This used to disables immediately.

void tasklet_disable_nosync(struct tasklet_struct *t);

t – pointer to the tasklet struct

NOTE : If the tasklet has been disabled, we can still add it to the queue for scheduling, but it will not be executed on the CPU until it is enabled again. Moreover, if the tasklet has been disabled several times, it should be enabled exactly the same number of times, there is the count field in the structure for this purpose.

Schedule Tasklet

When we schedule the tasklet, then that tasklet is placed into one queue out of two, depending on the priority. Queues are organized as singly-linked lists. At that, each CPU has its own queues.

There are two priorities.

  1. Normal Priority
  2. High Priority

tasklet_schedule

Schedule a tasklet with normal priority. If a tasklet has previously been scheduled (but not yet run), the
new schedule will be silently discarded.

void tasklet_schedule (struct tasklet_struct *t);

t – pointer to the tasklet struct

Example

tasklet_hi_schedule

Schedule a tasklet with high priority. If a tasklet has previously been scheduled (but not yet run), the
new schedule will be silently discarded.

void tasklet_hi_schedule (struct tasklet_struct *t);

t – pointer to the tasklet struct

tasklet_hi_schedule_first

This version avoids touching any other tasklets. Needed for kmemcheck in order not to take any page faults while enqueueing this tasklet. Consider VERY carefully whether you really need this or tasklet_hi_schedule().

void tasklet_hi_schedule_first(struct tasklet_struct *t);

t – pointer to the tasklet struct

Kill Tasklet

Finally, after a tasklet has been created, it’s possible to delete a tasklet through these below functions.

tasklet_kill

This will wait for its completion, and then kill it.

void tasklet_kill( struct tasklet_struct *t );

t – pointer to the tasklet struct

Example

tasklet_kill_immediate

This is used only when a given CPU is in the dead state.

void tasklet_kill_immediate( struct tasklet_struct *t, unsigned int cpu );

t – pointer to the tasklet struct

cpu – cpu num

Programming

Driver Source Code

In that source code, When we read the /dev/etx_device interrupt will hit (To understand interrupts in Linux go to this tutorial). Whenever interrupt hits, I’m scheduling the task to the tasklet. I’m not going to do any job in both interrupt handler and tasklet function,  since it is a tutorial post. But in real tasklet, this function can be used to carry out any operations that need to be scheduled.

NOTE: In this source code many unwanted functions will be there (which is not related to the Tasklet). Because I’m just maintaining the source code throughout these Device driver series.

MakeFile

Building and Testing Driver

  • Build the driver by using Makefile (sudo make)
  • Load the driver using sudo insmod driver.ko
  • To trigger interrupt read device file (sudo cat /dev/etx_device)
  • Now see the Dmesg (dmesg)

[email protected]: dmesg

[ 8592.698763] Major = 246 Minor = 0
[ 8592.703380] Device Driver Insert…Done!!!
[ 8601.716673] Device File Opened…!!!
[ 8601.716697] Read function
[ 8601.716727] Shared IRQ: Interrupt Occurred
[ 8601.716732] Executing Tasklet Function : arg = 1
[ 8601.716741] Device File Closed…!!!                                            [8603.916741] Device Driver Remove…Done!!!

  • We can able to see the print “Shared IRQ: Interrupt Occurred“ and “Executing Tasklet Function : arg = 1
  • Unload the module using sudo rmmod driver

In our next tutorial we will discuss Tasklet using Dynamic Method.

Download our new Android app. You can learn all Embedded Tutorials from your Android Phone easily.

Click Here to Download App!