CSE 522S: Studio 10

Kernel Synchronization


"Well, after them!" said Gimli. "Dwarves too can go swiftly, and they do not tire sooner than Orcs. But it will be a long chase: they have a long start."

"Yes," said Aragorn, "we shall all need the endurance of Dwarves. But come! With hope or without hope we will follow the trail of our enemies. And woe to them, if we prove the swifter! We will make such a chase as shall be accounted a marvel among the Three Kindreds; Elves, Dwarves, and Men. Forth the Three Hunters!"

The Two Towers, Chapter 1, Book III

Like many userspace programs, the Linux kernel is designed to run concurrently with itself. In this case, kernel code must be careful not to create race conditions and concurrency bugs by accessing shared data without protection.

In this studio, you will:

  1. Create a race condition in a kernel module
  2. Use kernel synchronization to resolve the race condition

Please complete the required exercises below, as well as any optional enrichment exercises that you wish to complete.

As you work through these exercises, please record your answers, and when finished email your results to eng-cse522@email.wustl.edu with the phrase Kernel Synchronization in the subject line.

Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.


Required Exercises

  1. As the answer to the first exercise, list the names of the people who worked together on this studio.

  2. Create a kernel module that creates a team of threads, one on each processor, that call a thread function and return. (I.e. your threads should not do the periodic or repeating behavior from lab 1.) You can use your solution to Lab1 ras a template for this.

  3. Now we will create a race condition in the kernel, so that we can later solve it. Declare a global int called race with the volatile qualifier, as so:

    volatile int race = 0;

    The volatile qualifier tells the compiler that this variable will be modified outside of the current execution context, and has the effect of disabling certain optimizations.

    Within the thread function for each of your threads, write a for loop that increments the variable race one million (1,000,000) times, as so:

    #define iters 1000000

    for( i=0; i<iters; i++){

    race++;

    }

    In your module's exit function, print out the value of race. Leave the answer to this exercise blank.

  4. Execute your code three times, what is the value of race? If you get 4,000,000 consistently then you have not successfully created a race condition. Make sure your variable is declared as volatile and that your threads are executing simultaneously.

  5. Make a new copy of your kernel module and replace your global integer with a global atomic_t type, which is defined in include/linux/types.h. This is an opaque type that is only accessed through special mutators and accessors. Initialize this new type with the function atomic_set(), increment it with the function atomic_add(), and access it's value with the function atomic_read(). The function prototypes are found in include/asm-generic/atomic.h

  6. Run your code three times. What is the new value of race? If you get anything other than 4,000,000 you have not successfully resolved your race condition.

  7. Have your threads print a statement to the system log before they start their for loop and when they finish. Use this as a crude timestamp to determine how long it takes your code to execute. How long does it take?

  8. Make another copy of your original kernel module. This time, rather than modifying your global integer to be atomic, we will use a mutex to protect it. Use the macro DEFINE_MUTEX(mutex_name) to statically declare a mutex at the global scope and use the functions mutex_lock(&mutex_name) and mutex_unlock(&mutex_name) to protect access to the race variable.

  9. Again, use kernel print statements as a crude timestamp for your thread functions. How long does it take your mutex code to complete?

  10. Given the time difference between using atomic variables versus mutexes, why are mutexes useful? Give an example where you might prefer a mutex over an atomic variable.

Things to turn in