Concurrency issues in Linux Device Drivers and protection from concurrency

Concurrency was not a big issue till kernel support was introduced for SMP systems . Before there were only few sources of concurrency like the interrupt handling context , but with introduction of kernel support for SMP systems it also drastically increased the chances of  code being vulnerable for concurrency issues .SMP (Symmetric Multi Processing ) systems involve architecture with multiple processors , this introduces the possibility of the same code snippet being run in more than one processors at a given point of time . Codes running in kernel context are pre-emtible too , it may lose the processor at any point of time and the process scheduled next may run in your driver .There are surprisingly many ways where the Device Driver or a program running in kernel context becomes vulnerable for concurrency related issues , In the computing world where one in a million probable event too can happen every second bugs related to concurrency are difficult to identify even for expert kernel developers .

Reasons for concurrency

Consider this small code snippet in your driver

if(!dptr->data[block]) { //1.check whether memory is already allocated 
    dptr->data[block] = kmalloc(block_size , GFP_KERNEL); //2.allocate memory if its not allocated 
    if(!dptr->data[block] ) //3.check whether the memory allocation was successful 
         end the program ; 4.//exit if memory allocation fails 

Consider the case wherein processes A and B both try to access the driver . When both A and B arrives at the condition to check for memory allocation (line 1 ) , both predicts the memory to be not to be allocated and moves ahead executing the lines to follow allocating the memory .

The second process to allocate memory by executing kmalloc() wipes out the memory allocated by the first process . The second process gets the inconsistent view of the variable .

Such piece of code which is vulnerable for concurrency related bugs should be protected , such snippet of code is called as critical region of code . Critical region code should be protected from concurrent access , access to the critical region of code should be controlled and managed .

There are few rules following which the concurency related issues can be minimized and solved .Reduce the use of shared resources

  1. Hardware resources are shared and likewise many structures and variables too are accessible for any no.of.threads of execution , unless there is a strong reason its better not to make use of shared resoures ( like global variables ) .If there are no shared resources there wont be any concurrency issues , but sharing is often required and all varialbles in the driver by default is accessible all threads of execution .

  2. Use kernel’s locking primitives to manage and control access to critical region, if critical section of code is secured or prevented from multiple processes to execute simultaneously the issue can be solved .Kernel Provides different primitives to handle concurrency related issues since the triggering factor for all concurrency related issues are not same . Concurrency in the driver may be purely due to access from user space or code executed by asynchronous events like interrupt handling or kernel timer mechanisms . 

Ways of protecting critical section of code from concurrent access and kernel primitives and API’s that can be used in device drivers to protect the critical section of code will be discussed in the next post . Till then have great time coding ……….. 🙂

Linux Device Drivers-4 : The V.F.S Connection

 To make use of a character device driver , the associated character device file (usually located under /dev) is to be used .Using the following command gives you the list of all character device files in /dev

 ls -l /dev | grep “^c”


 For any user-level application to access the underlying hardware , it should make use of the device driver corresponding to the device . But communication with the device driver can only be made by performing file operations on the device file corresponding to the device (usually located under /dev).

Therefore for any user-level application to get services out of a hardware , it should perform file operations on the corresponding device files .

 The character device driver and the its corresponding device file are connected togetby V.F.S(Virtual File system) . The functions implemented inside the driver will perform her the low-level file access into the underlying hardware .

 From Linux kernel 2.6 onwards there can exist multiple drivers under same MAJOR.NO

 The connection of the device file with the driver is done by means of its <MAJOR.NO,MINOR.NO> pair .This connection is established by using the following to API’s in the driver .These calls registers a <MAJOR,MINOR> pair for the driver. Later any device file created by assigning the same major , minor number will be linked to the corresponding driver .

 int register_chrdev_region(dev_t first,unsigned int no_of_minors,char *name);

 This call associates the driver with the <Major,MINOR> pair stored in the variable first.The variable no_of_minors specifies the no.of.minor numbers to be registered .

 Sometimes the Major number requested may be already allocated and may not be free.So its better to dynamically allocate the MAJOR number and let kernel search a free MAJOR number and assign the same .The following function is used to achieve the purpose .

 int alloc_chrdev_region(dev_t *first,unsigned int first_minor,unsigned int no_of_minors,char *name);

 When any file operations are performed on a device file , it is passed onto the driver my V.F.S .

To achieve this purpose the corresponding functions to be invoked by the driver(functions written inside the driver code) when different file operations* performed on the device file have to be registered with the V.F.S.

This registration is first done by initializing the structure file_operations and then handing the structure

to V.F.S by using the function cdev_add which is declared under <linux/cdev.h>


Linux Device Drivers-1:Introduction to Device Drivers in Linux

The programs written for the LINUX kernel , that is the kernel programs cannot use headers and function from any library .they cannot make use C headers and their library functions.

Then the obvious question would be that which are  the HEADERS and FUNCTIONS does DEVICE DRIVER and KERNEL PROGRAMS in LINUX make use of??

Kernel programs and device drivers make use of headers which they obtain from the kernel source tree

issuing the command would get you into the path where these kernel headers reside

“cd /lib/modules/$(uname -r)/build/include/linux”
The kernel and device driver programs make use of these headers and functions within them .No External Libraries can be linked , not even C library or its headers found in /usr/include can be used to write device drivers or kernel programs.

After kernel programs or device drivers are written they are compiled and linked to the executable version of these files called as Kernel Modules with .ko extension.A Makefile is created for  these files which are to be compiled and linked.The ‘make’ program is made use of to achieve this purpose.

Later these kernel modules are loaded on top of the kernel dynamically using ‘insmod’ command .lsmod command gives you the list of all kernel modules on top of the kernel.Linux kernel is dynamic , addition and removal of kernel modules can done without the need for restarting the system .The running module can be removed by using ‘rmmod’ command.

By default all the kernel messages from printk function goes to /var/log/syslog file.This can viewed by using ‘dmesg’ command.
/proc/devices contain the list of all configured devices