Whenever you write kernel code, you should ask yourself these questions:
. Is the data global? Can a thread of execution other than the current one access it?
. Is the data shared between process context and interrupt context? Is it shared
between two different interrupt handlers?
. If a process is preempted while accessing this data, can the newly scheduled process
access the same data?
. Can the current process sleep (block) on anything? If it does, in what state does that
leave any shared data?
. What prevents the data from being freed out from under me?
. What happens if this function is called again on another processor?
. Given the proceeding points, how am I going to ensure that my code is safe from
concurrency?
In short, nearly all global and shared data in the kernel requires some form of the
synchronization methods, discussed in the next chapter.
Prevention of deadlock scenarios is important.Although it is difficult to prove that code
is free of deadlocks, you can write deadlock-free code.A few simple rules go a long way:
. Implement lock ordering. Nested locks must always be obtained in the same order.
This prevents the deadly embrace deadlock. Document the lock ordering so others
will follow it.
. Prevent starvation.Ask yourself, does this code always finish? If foo does not occur, will
bar wait forever?
. Do not double acquire the same lock.
. Design for simplicity. Complexity in your locking scheme invites deadlocks.