It _is_ complicated, but the fix is so simple that it's embarrassing. To
add upgradable read locks of a sort to the list of spinlocks you can do
this:
#define get_upgrade_lock(rw) \
asm volatile("\n1:\t" \
"lock ; btsl $31,%0\n\t" \
"jc 2f\n" \
".section .text.lock,\"ax\"\n" \
"2:\tcmp $0,%0\n\t" \
"js 2b\n\t" \
"jmp 1b\n" \
".previous" \
:"=m" (__dummy_lock(&(rw)->lock)))
#define do_upgrade_lock(rw) \
do { /* nothing */ \
} while ((rw)->lock & 0x7fffffff)
#define update_unlock(rw) \
write_unlock(rw)
Now, admittedly the above may not be optimal for all conditions (it won't
let in any new readers while somebody has a upgrade lock: that is often
the behaviour you want, but if reading is _far_ more common than writing
then it is sometimes advantageous to allow normal readers in even after
somebody got a update lock). But it sure is simple.
Usage is something like:
get_upgrade_lock(&rw_lock)
.. read the lock freely here - there may be other readers concurrent ..
if (needs_to_write) {
do_upgrade_lock(&rw_lock);
.. now we can write ..
}
update_unlock(&rw_lock);
(ie you don't _have_ to upgrade the lock if you don't want to, but you can
still unlock with the same generic unlock version).
NOTE NOTE NOTE! The above has not been compiled, much less tested. It
might not work. As usual, it should be fairly close to working, and it
might work as-is, but be careful, and be ready to debug it. (And you'd
need to add the irq-safe versions too).
If the above does work for people (or it works after a few fixes) and
somebody decides that he wants to use upgrade locks, send me a patch back
with the (possibly fixed) code.
Linus