Readings from the Anthology,
Linux Source:
Spinlock.h

Things to Consider

1. Kernel is written in C

2. C does not support Interfaces

3. C systems programming is extremely convoluted

4. likely(..) is a gcc macro for __builtin_expect((..),1)

4a. likely helps the compiler in making intelligent branching decisions

5. ## is a tag used for compiler concatenation


Introduction

Someone reading this might wonder how could they get their hands on such a wonderful anthology. Good news for the reader, this anthology is free and is easily available on the Internet. This code reading experience is powered by the 2.6.33.20 kernel from www.kernel.org.

After successfully obtaining the source, simply run tar xvf on the downloaded file, and presto. Now this anthology does not come with a handy index, or perhaps it does. Luckily linux provides a remedy to this problem. Perhaps the solution is find? However, no one really knows how find works except for the man. Thus here is a fun solution that avoids manning find, and at the same time utilizes it.

find . | grep spinlock
... //achival stuff, cool other things.
./arch/arm/include/asm/spinlock.h
./kernel/spinlock.c
./lib/rwsem-spinlock.c
./lib/bust_spinlocks.c
./lib/spinlock_debug.c
./Documentation/spinlocks.txt
./include/linux/spinlock_api_up.h
./include/linux/bit_spinlock.h
./include/linux/spinlock_types.h
./include/linux/rwsem-spinlock.h
./include/linux/spinlock.h
./include/linux/spinlock_api_smp.h
./include/linux/spinlock_up.h
./include/linux/spinlock_types_up.h
./include/asm-generic/spinlock.h

The header files are found within the .../include/ directory


Into the overview

So with the source obtained, untarred and all the necessary files found... One can now successfully open up spinklock.h and see what the hoopla is all about. Here the reader is greeted with a silly amount of #defines and #includes followed by syntactical sugar that is the __naming_convention. Thus, the user scrolls down until something familiar appears and that is of course is the elusive

#include <linux/preempt.h>

Here the reader can finally find solace in the fact that there is a preempt call, and the kernel does use it in the implementation of the spinlock. Further down there is some definitions for the spinlock omitting debug sections.

#define raw_spin_is_locked(lock)    arch_spin_is_locked(&(lock)->raw_lock)
#define raw_spin_lock(lock) _raw_spin_lock(lock)

#define raw_spin_lock_irqsave(lock, flags)	         \
	do {						 \
		typecheck(unsigned long, flags);         \
		flags = _raw_spin_lock_irqsave(lock);    \
	} while (0)

#define spin_lock_init(_lock)				 \
	do {	   					 \
		spinlock_check(_lock);		         \
		raw_spin_lock_init(&(_lock)->rlock);  	 \
	} while (0)

static inline void spin_lock(spinlock_t *lock)
{
	raw_spin_lock(&lock->rlock);
}

Following those definitions are the definitions for spinlock lock break

static inline void spin_unlock(spinlock_t *lock)
{
	raw_spin_unlock(&lock->rlock);
}

static inline void spin_unlock_irq(spinlock_t *lock)
{
	raw_spin_unlock_irq(&lock->rlock);
}

There are more definitions for specific implementations of spinlock which are perhaps out of the scope of this introduction. Now the reader is left with some questions. Rest assured dear reader, some questions may in fact be answered as in the later sections. If not, I apologize in advance.


But what about raw_spinlock_t?

Perhaps not the most important question in the world, but there is an arsenal of characters waiting to be used as locks. Also, there are various variables waiting to usurp the noble raw_lock;

typedef struct raw_spinlock {
	arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
	unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
	unsigned int magic, owner_cpu;
	void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

#define SPINLOCK_MAGIC      0xdead4ead

That is more or less the gist of the main lock structure, with plenty of compiler ifs. One of the reasons for this section aside from informing the reader of the structure in place is to point out the variable named magic, and the location of SPINLOCK_MAGIC at 0xdead4ead. Also to point out that arch_spinlock_t is defined in spinlock_types_up.h as follows:

typedef struct {
	volatile unsigned int slock;
} arch_spinlock_t;


Magne... err Locks, how do they work?

Welcome to the final section in this brief tour de spinlock. Long awaited is code that actually defines behavior of locks; here to assist that process is spinlock.c.

#define BUILD_LOCK_OPS(op, locktype)                                    \
	void __lockfunc __raw_##op##_lock(locktype##_t *lock)           \
{                                                                       \
	for (;;) {                                                      \
		preempt_disable();                                      \
		if (likely(do_raw_##op##_trylock(lock)))                \
		break;							\
		preempt_enable();                                       \
		if (!(lock)->break_lock)                                \
		(lock)->break_lock = 1;					\
		while (!raw_##op##_can_lock(lock) && (lock)->break_lock)\
		arch_##op##_relax(&lock->raw_lock);			\
	}                                                               \
	(lock)->break_lock = 0;                                         \
}                                                                       \

Here we finally encounter the awesomeness that is macros and double pound signs. Starting of is the generic definition for operations that may use this function. Speaking of functions, this one features an infinite for loop that breaks if branching on a specific lock is likely. However, during each likely test there is a call to preempt_disable() followed by another call to re-enable preemption. Then, the break_lock is set to true followed by another loop that relaxes the lock which should eventually result in a break call, and thus the exit of the for loop and this function.

Following is the version which calls interrupt request save, which is more or less same as above except with the addition of local_irq_save and local_irq_restor calls.

unsigned long __lockfunc __raw_##op##_lock_irqsave(locktype##_t *lock)  \
{                                                                       \
	unsigned long flags;                                            \
	for (;;) {                                                      \
		preempt_disable();                                      \
		local_irq_save(flags);                                  \
		if (likely(do_raw_##op##_trylock(lock)))                \
			break;                                          \
		local_irq_restore(flags);                               \
		preempt_enable();                                       \
		if (!(lock)->break_lock)                                \
			(lock)->break_lock = 1;                         \
		while (!raw_##op##_can_lock(lock) && (lock)->break_lock)\
			arch_##op##_relax(&lock->raw_lock);             \
	}                                                               \
	(lock)->break_lock = 0;                                         \
	return flags;                                                   \
}

Full Circle

At this point hopefully some of the questions the reader had have been lifted away, for everything else sadly I cannot offer any more guidance without spending more time studying the art of #defining everything.

Above is a brief introduction to linux spinlocks and their implementation. This introduction scraped the surface of spinlocks while using only 3 files as opposed to the dozen or so provided by grep, along with numerous links to other header files which will undoubtedly link to other header files, and perhaps if the adventurer is lucky, there will be a header file with no links to other header files.


Some irrelevant notes

While it may seem a great idea, writing http code in vim is miserable... Actually it is never a great idea.

This article should be treated as is with no explicit warranty or guarantee of accuracy. This article disclaims all claims of fire damage, and this entry does not condone the use of blink tag