There are a number of atomic operations defined in include/asm/atomic.h: these are guaranteed to be seen atomically from all CPUs in the system, thus avoiding races. If your shared data consists of a single counter, say, these operations might be simpler than using spinlocks (although for anything non-trivial using spinlocks is clearer).
Note that the atomic operations do in general not act as memory
barriers. Instead you can insert a memory barrier before or
after atomic_inc() or
atomic_dec() by inserting
smp_mb__before_atomic_inc(),
smp_mb__after_atomic_inc(),
smp_mb__before_atomic_dec() or
smp_mb__after_atomic_dec()
respectively. The advantage of using those macros instead of
smp_mb() is, that they are cheaper on some
platforms.