What is a spinlock in Linux?
A spin lock is a way to protect a shared resource from being modified by two or more processes simultaneously. The first process that tries to modify the resource "acquires" the lock and continues on its way, doing what it needed to with the resource. Any other processes that subsequently try to acquire the lock get stopped; they are said to "spin in place" waiting on the lock to be released by the first process, thus the name spin lock.
The Linux kernel uses spin locks for many things, such as when sending data to a particular peripheral. Most hardware peripherals aren't designed to handle multiple simultaneous state updates. If two different modifications have to happen, one has to strictly follow the other, they can't overlap. A spin lock provides the necessary protection, ensuring that the modifications happen one at a time.
Spin locks are a problem because spinning blocks that thread's CPU core from doing any other work. While the Linux kernel does provide multitasking services to user space programs running under it, that general-purpose multitasking facility doesn't extend to kernel code.
This situation is changing, and has been for most of Linux's existence. Up through Linux 2.0, the kernel was almost purely a single-tasking program: whenever the CPU was running kernel code, only one CPU core was used, because there was a single spin lock protecting all shared resources, called the Big Kernel Lock (BKL). Beginning with Linux 2.2, the BKL is slowly being broken up into many independent locks that each protect a more focused class of resource. Today, with kernel 2.6, the BKL still exists, but it's only used by really old code that can't be readily moved to some more granular lock. It is now quite possible for a multicore box to have every CPU running useful kernel code.
There's a limit to the utility of breaking up the BKL because the Linux kernel lacks general multitasking. If a CPU core gets blocked spinning on a kernel spin lock, it can't be retasked, to go do something else until the lock is released. It just sits and spins until the lock is released.
Spin locks can effectively turn a monster 16-core box into a single-core box, if the workload is such that every core is always waiting for a single spin lock. This is the main limit to the scalability of the Linux kernel: doubling CPU cores from 2 to 4 probably will nearly double the speed of a Linux box, but doubling it from 16 to 32 probably won't, with most workloads.
A spin lock is when a process continually polls for a lock to be removed. It is considered bad because the process is consuming cycles (usually) needlessly. It is not Linux-specific, but a general programming pattern. And while it is generally considered a bad practice, it is, in fact, the correct solution; there are cases where the cost of using the scheduler is higher (in terms of CPU cycles) than the cost of the few cycles that the spinlock is expected to last.
Example of a spinlock:
#!/bin/sh
#wait for some program to clear a lock before doing stuff
while [ -f /var/run/example.lock ]; do
sleep 1
done
#do stuff
There is frequently a way to avoid a spin lock. For this particular example, there is a Linux tool called inotifywait (it's not usually installed by default). If it was written in C, you would simply use the inotify API that Linux provides.
The same example, using inotifywait shows how to accomplish the same thing without a spin lock:
#/bin/sh
inotifywait -e delete_self /var/run/example.lock
#do stuff
When a thread tries to acquire a lock, three things can happen if it fails - it can try and block, it can try and continue, it can try then go to sleep telling the OS to wake it up when some event happens.
Now a try and continue uses a lot less time than a try and block. Let's say for the moment that a "try and continue" will take a unit of time and a "try and block" will take a hundred.
Now let us for the moment assume that on average a thread will take 4 units of time holding the lock. It's wasteful to wait 100 units. So instead, you write a loop of "try and continues". On the fourth attempt, you will usually acquire the lock. This is a spin lock. It's called that because the thread keeps spinning in place till it gets the lock.
An added safety measure is to limit the number of times the loop runs. So for example, you make a for-loop run, say, six times, if it fails then you "try and block".
If you know that a thread will always hold the lock for, say, 200 units, then you are wasting the computer time for each try and continue.
So in the end, a spin lock can be very efficient or wasteful. It is wasteful when the "typical" time to hold a lock is higher than the time it takes to "try and block". It is efficient when the typical time to hold a lock is much lower than the time to 'try and block".
Ps: The book to read on threads is "A Thread Primer", if you can still find it.