Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164// SPDX-License-Identifier: GPL-2.0#include <linux/export.h>#include <linux/lockref.h>
#if USE_CMPXCHG_LOCKREF
/* * Note that the "cmpxchg()" reloads the "old" value for the * failure case. */#define CMPXCHG_LOOP(CODE, SUCCESS) do { \ int retry = 100; \ struct lockref old; \ BUILD_BUG_ON(sizeof(old) != 8); \ old.lock_count = READ_ONCE(lockref->lock_count); \ while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) { \ struct lockref new = old; \ CODE \ if (likely(try_cmpxchg64_relaxed(&lockref->lock_count, \ &old.lock_count, \ new.lock_count))) { \ SUCCESS; \ } \ if (!--retry) \ break; \ } \} while (0)
#else
#define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
#endif
/** * lockref_get - Increments reference count unconditionally * @lockref: pointer to lockref structure * * This operation is only valid if you already hold a reference * to the object, so you know the count cannot be zero. */void lockref_get(struct lockref *lockref){ CMPXCHG_LOOP( new.count++; , return; );
spin_lock(&lockref->lock); lockref->count++; spin_unlock(&lockref->lock);}EXPORT_SYMBOL(lockref_get);
/** * lockref_get_not_zero - Increments count unless the count is 0 or dead * @lockref: pointer to lockref structure * Return: 1 if count updated successfully or 0 if count was zero */bool lockref_get_not_zero(struct lockref *lockref){ bool retval = false;
CMPXCHG_LOOP( new.count++; if (old.count <= 0) return false; , return true; );
spin_lock(&lockref->lock); if (lockref->count > 0) { lockref->count++; retval = true; } spin_unlock(&lockref->lock); return retval;}EXPORT_SYMBOL(lockref_get_not_zero);
/** * lockref_put_return - Decrement reference count if possible * @lockref: pointer to lockref structure * * Decrement the reference count and return the new value. * If the lockref was dead or locked, return -1. */int lockref_put_return(struct lockref *lockref){ CMPXCHG_LOOP( new.count--; if (old.count <= 0) return -1; , return new.count; ); return -1;}EXPORT_SYMBOL(lockref_put_return);
/** * lockref_put_or_lock - decrements count unless count <= 1 before decrement * @lockref: pointer to lockref structure * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken */bool lockref_put_or_lock(struct lockref *lockref){ CMPXCHG_LOOP( new.count--; if (old.count <= 1) break; , return true; );
spin_lock(&lockref->lock); if (lockref->count <= 1) return false; lockref->count--; spin_unlock(&lockref->lock); return true;}EXPORT_SYMBOL(lockref_put_or_lock);
/** * lockref_mark_dead - mark lockref dead * @lockref: pointer to lockref structure */void lockref_mark_dead(struct lockref *lockref){ assert_spin_locked(&lockref->lock); lockref->count = -128;}EXPORT_SYMBOL(lockref_mark_dead);
/** * lockref_get_not_dead - Increments count unless the ref is dead * @lockref: pointer to lockref structure * Return: 1 if count updated successfully or 0 if lockref was dead */bool lockref_get_not_dead(struct lockref *lockref){ bool retval = false;
CMPXCHG_LOOP( new.count++; if (old.count < 0) return false; , return true; );
spin_lock(&lockref->lock); if (lockref->count >= 0) { lockref->count++; retval = true; } spin_unlock(&lockref->lock); return retval;}EXPORT_SYMBOL(lockref_get_not_dead);