diff options
Diffstat (limited to 'arch/mips/lib/dec_and_lock.c')
-rw-r--r-- | arch/mips/lib/dec_and_lock.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/arch/mips/lib/dec_and_lock.c b/arch/mips/lib/dec_and_lock.c new file mode 100644 index 00000000000..e44e9579bd3 --- /dev/null +++ b/arch/mips/lib/dec_and_lock.c @@ -0,0 +1,55 @@ +/* + * MIPS version of atomic_dec_and_lock() using cmpxchg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/spinlock.h> +#include <asm/atomic.h> +#include <asm/system.h> + +/* + * This is an implementation of the notion of "decrement a + * reference count, and return locked if it decremented to zero". + * + * This implementation can be used on any architecture that + * has a cmpxchg, and where atomic->value is an int holding + * the value of the atomic (i.e. the high bits aren't used + * for a lock or anything like that). + * + * N.B. ATOMIC_DEC_AND_LOCK gets defined in include/linux/spinlock.h + * if spinlocks are empty and thus atomic_dec_and_lock is defined + * to be atomic_dec_and_test - in that case we don't need it + * defined here as well. + */ + +#ifndef ATOMIC_DEC_AND_LOCK +int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + int counter; + int newcount; + + for (;;) { + counter = atomic_read(atomic); + newcount = counter - 1; + if (!newcount) + break; /* do it the slow way */ + + newcount = cmpxchg(&atomic->counter, counter, newcount); + if (newcount == counter) + return 0; + } + + spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + spin_unlock(lock); + return 0; +} + +EXPORT_SYMBOL(_atomic_dec_and_lock); +#endif /* ATOMIC_DEC_AND_LOCK */ |